diff --git a/Directory.Build.props b/Directory.Build.props index 74e0b2d..853de07 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ - 0.3.3 + 0.3.4 diff --git a/NET10/BlazorInteractiveServer/wwwroot/input.css b/NET10/BlazorInteractiveServer/wwwroot/input.css index 250c8c0..597ebfb 100644 --- a/NET10/BlazorInteractiveServer/wwwroot/input.css +++ b/NET10/BlazorInteractiveServer/wwwroot/input.css @@ -3,6 +3,7 @@ @custom-variant dark (&:is(.dark *)); :root { + color-scheme: light; --background: oklch(0.9900 0 0); --foreground: oklch(0 0 0); --card: oklch(1 0 0); @@ -58,6 +59,7 @@ } .dark { + color-scheme: dark; --background: oklch(0 0 0); --foreground: oklch(1 0 0); --card: oklch(0.1400 0 0); diff --git a/README.md b/README.md index a871a99..a3c7925 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ | Channel | Version | Notes | |---|---|---| -| Latest stable (recommended) | `0.3.3` | Tailwind CSS `4.3.2` | +| Latest stable (recommended) | `0.3.4` | Tailwind CSS `4.3.2` | | Previous stable | `0.2.1` | Superseded by `0.3.0` | The CLI needs the .NET 10 SDK and works in Blazor projects on .NET 8, 9 and 10. The `ShellUI.Components` NuGet package targets .NET 10 only; on .NET 8 or 9, use the CLI. diff --git a/ShellUI.Tests/ColorSchemeTests.cs b/ShellUI.Tests/ColorSchemeTests.cs new file mode 100644 index 0000000..5a72c61 --- /dev/null +++ b/ShellUI.Tests/ColorSchemeTests.cs @@ -0,0 +1,42 @@ +using System.IO; +using ShellUI.CLI.Services; +using ShellUI.Templates; +using Xunit; + +namespace ShellUI.Tests; + +public class ColorSchemeTests +{ + [Fact] + public void InitTheme_SetsColorSchemeForBothThemes() + { + Assert.Contains("color-scheme: light;", CssTemplates.InputCss); + Assert.Contains("color-scheme: dark;", CssTemplates.InputCss); + } + + [Fact] + public void AppliedTheme_SetsColorSchemeForBothThemes() + { + var css = ThemeService.BuildThemeCss(new TweakcnTheme + { + Name = "t", + ThemeVars = new(), + LightVars = new() { ["background"] = "#fff" }, + DarkVars = new() { ["background"] = "#000" }, + }); + + Assert.Contains(":root {\n color-scheme: light;", css.Replace("\r\n", "\n")); + Assert.Contains(".dark {\n color-scheme: dark;", css.Replace("\r\n", "\n")); + } + + [Fact] + public void PackageTheme_SetsColorSchemeForBothThemes() + { + var root = new DirectoryInfo(System.AppContext.BaseDirectory); + while (root != null && !File.Exists(Path.Combine(root.FullName, "ShellUI.slnx"))) root = root.Parent; + var css = File.ReadAllText(Path.Combine(root!.FullName, "src", "ShellUI.Components", "wwwroot", "shellui-theme.css")); + + Assert.Contains("color-scheme: light;", css); + Assert.Contains("color-scheme: dark;", css); + } +} diff --git a/ShellUI.Tests/PackageBehaviorTests.cs b/ShellUI.Tests/PackageBehaviorTests.cs index 76a2068..5bce648 100644 --- a/ShellUI.Tests/PackageBehaviorTests.cs +++ b/ShellUI.Tests/PackageBehaviorTests.cs @@ -62,4 +62,14 @@ public void Dropdown_ClosesOnEscapeButNotOnOtherKeys() dropdown.Find("div.relative").KeyDown(new KeyboardEventArgs { Key = "Escape" }); Assert.Equal(new[] { false }, changes); } + + [Fact] + public void TimePicker_ShowsASetValue() + { + using var ctx = new BunitContext(); + + var picker = ctx.Render(p => p.Add(c => c.Value, new TimeSpan(9, 5, 0))); + + Assert.Contains("09:05", picker.Markup); + } } diff --git a/ShellUI.Tests/PackageRenderTests.cs b/ShellUI.Tests/PackageRenderTests.cs index 260d626..099c0c8 100644 --- a/ShellUI.Tests/PackageRenderTests.cs +++ b/ShellUI.Tests/PackageRenderTests.cs @@ -57,6 +57,78 @@ public void EveryPackageComponent_RendersWithoutParameterBindingErrors() Assert.True(offenders.Count == 0, "Package components that fail parameter binding:\n " + string.Join("\n ", offenders)); } + // A component that renders empty but throws once it has a value fails here (the TimePicker "HH:mm" bug). + [Fact] + public void ValueBearingComponents_RenderWithASampleValue() + { + var types = typeof(Shell).Assembly.GetExportedTypes() + .Where(t => typeof(IComponent).IsAssignableFrom(t) && !t.IsAbstract && t.Namespace == "ShellUI.Components") + .Select(t => t.IsGenericTypeDefinition ? Close(t) : t) + .OfType() + .ToList(); + + var tried = 0; + var offenders = new List(); + foreach (var type in types) + { + var values = SampleValues(type); + if (values.Count == 0 || !Renders(type, new Dictionary(), out _)) continue; + + tried++; + if (!Renders(type, values, out var error)) + offenders.Add($"{type.Name} ({string.Join(", ", values.Keys)}): {error}"); + } + + Assert.True(tried >= 10, $"Only {tried} components had a value to set; the sample types may need extending."); + Assert.True(offenders.Count == 0, "Package components that throw once a value is set:\n " + string.Join("\n ", offenders)); + } + + private static readonly System.Text.RegularExpressions.Regex ValueLike = new("Value|Date|Time|Selected"); + + private static Dictionary SampleValues(Type type) + { + var values = new Dictionary(); + foreach (var p in type.GetProperties()) + { + if (!p.IsDefined(typeof(ParameterAttribute), true) || !p.CanWrite || !ValueLike.IsMatch(p.Name)) continue; + var t = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType; + object? sample = + t == typeof(string) ? "b" : + t == typeof(DateTime) ? new DateTime(2026, 3, 14) : + t == typeof(DateOnly) ? new DateOnly(2026, 3, 14) : + t == typeof(TimeSpan) ? new TimeSpan(9, 5, 0) : + t == typeof(TimeOnly) ? new TimeOnly(9, 5) : + t == typeof(int) ? 42 : + t == typeof(double) ? 42.5 : + t == typeof(decimal) ? 42.5m : + t == typeof(bool) ? true : + t == typeof(List) ? new List { "a", "b" } : + t == typeof(IEnumerable) ? new List { "a", "b" } : + null; + if (sample != null) values[p.Name] = sample; + } + return values; + } + + private static bool Renders(Type type, Dictionary parameters, out string error) + { + using var ctx = new BunitContext(); + ctx.JSInterop.Mode = JSRuntimeMode.Loose; + ctx.Services.AddShellUISonner(); + ctx.Services.AddScoped(); + try + { + ctx.Render(p => p.Add(c => c.Type, type).Add(c => c.Parameters, parameters)); + error = ""; + return true; + } + catch (Exception ex) + { + error = Flatten(ex); + return false; + } + } + private static Type? Close(Type definition) { foreach (var candidate in new[] { typeof(Row), typeof(string), typeof(int) }) diff --git a/VERSIONING_STRATEGY.md b/VERSIONING_STRATEGY.md index 9bd198e..cea7c31 100644 --- a/VERSIONING_STRATEGY.md +++ b/VERSIONING_STRATEGY.md @@ -6,7 +6,7 @@ ShellUI uses one centralized version for the source templates, CLI tool, and pac | Scope | Value | |---|---| -| Version | `0.3.3` | +| Version | `0.3.4` | | Target framework | .NET 10 | | Tailwind version | `4.3.2` | @@ -18,15 +18,15 @@ The root `Directory.Build.props` supplies the version: ```xml - 0.3.3 + 0.3.4 ``` `Directory.Build.props` composes the package and assembly metadata: -- `Version` is `0.3.3`; with a suffix such as `rc.1` it becomes `0.3.3-rc.1`. -- `AssemblyVersion` and `FileVersion` use the numeric base `0.3.3`. +- `Version` is `0.3.4`; with a suffix such as `rc.1` it becomes `0.3.4-rc.1`. +- `AssemblyVersion` and `FileVersion` use the numeric base `0.3.4`. - `InformationalVersion` includes the prerelease suffix. - Component metadata reads the centralized properties when running from the repository. `VersionHelper` uses assembly metadata as a fallback when its repository search does not find the solution file. @@ -57,7 +57,7 @@ The CLI writes the computed version into `shellui.json` for each installed entry "InstalledComponents": [ { "Name": "button", - "Version": "0.3.3", + "Version": "0.3.4", "InstalledAt": "2026-01-01T00:00:00Z", "IsCustomized": false } diff --git a/docs/CLI_INSTALLATION.md b/docs/CLI_INSTALLATION.md index 5eacd81..d48ff2d 100644 --- a/docs/CLI_INSTALLATION.md +++ b/docs/CLI_INSTALLATION.md @@ -4,7 +4,7 @@ | Context | Version | |---|---| -| Latest stable (recommended) | `0.3.3`, needs the .NET 10 runtime | +| Latest stable (recommended) | `0.3.4`, needs the .NET 10 runtime | | Previous stable | `0.2.1` | | Tailwind | `4.3.2` | @@ -43,7 +43,7 @@ The manifest is `.config/dotnet-tools.json` and uses the installed package versi "isRoot": true, "tools": { "shellui.cli": { - "version": "0.3.3", + "version": "0.3.4", "commands": ["shellui"] } } diff --git a/docs/CLI_SYNTAX.md b/docs/CLI_SYNTAX.md index 14b5e1e..c633d29 100644 --- a/docs/CLI_SYNTAX.md +++ b/docs/CLI_SYNTAX.md @@ -1,6 +1,6 @@ # ShellUI CLI Syntax -This reference covers CLI `0.3.3`: it runs on .NET 10, sets up Blazor projects on .NET 8, 9 and 10, uses Tailwind CSS `4.3.2`, and has and 76 direct component targets. Older versions, such as `0.2.1`, lack some of these commands. +This reference covers CLI `0.3.4`: it runs on .NET 10, sets up Blazor projects on .NET 8, 9 and 10, uses Tailwind CSS `4.3.2`, and has and 76 direct component targets. Older versions, such as `0.2.1`, lack some of these commands. ## Command prefix @@ -205,7 +205,7 @@ Representative fields look like this: "InstalledComponents": [ { "Name": "button", - "Version": "0.3.3", + "Version": "0.3.4", "InstalledAt": "2026-01-01T00:00:00Z", "IsCustomized": false } diff --git a/docs/FAQ.md b/docs/FAQ.md index 649b6ff..f277a78 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -6,7 +6,7 @@ Answers for the current ShellUI source and the currently published packages. ### Which version should I use? -Use `0.3.3`. It uses Tailwind CSS `4.3.2` and has 76 direct component targets: +Use `0.3.4`. It uses Tailwind CSS `4.3.2` and has 76 direct component targets: ```bash dotnet tool install -g ShellUI.CLI @@ -145,7 +145,7 @@ A representative Tailwind and component record is: "InstalledComponents": [ { "Name": "button", - "Version": "0.3.3", + "Version": "0.3.4", "InstalledAt": "2026-01-01T00:00:00Z", "IsCustomized": false } diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index be06112..bb150f6 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -1,6 +1,6 @@ # ShellUI Quick Start -This quick start uses ShellUI `0.3.3`: Tailwind CSS `4.3.2` and 76 direct component targets. The CLI needs the .NET 10 SDK and works in Blazor projects on .NET 8, 9 and 10. +This quick start uses ShellUI `0.3.4`: Tailwind CSS `4.3.2` and 76 direct component targets. The CLI needs the .NET 10 SDK and works in Blazor projects on .NET 8, 9 and 10. ## Prerequisites diff --git a/docs/RELEASE_NOTES.md b/docs/RELEASE_NOTES.md index e424054..ceb5ab0 100644 --- a/docs/RELEASE_NOTES.md +++ b/docs/RELEASE_NOTES.md @@ -1,5 +1,21 @@ # ShellUI Release Notes +# ShellUI v0.3.4 + +> A patch release with a `TimePicker` fix for the `ShellUI.Components` NuGet package and dark-theme fixes for both the package and the CLI. + +## 🐛 Fixes + +- NuGet package: `TimePicker` threw a `FormatException` whenever `Value` was set, because it formatted the `TimeSpan` with a date pattern. It now shows the time, the same as the CLI template. +- Dark theme: the browser kept drawing its own controls light on a dark page (the `` calendar, number spinners, scrollbars, autofill and `