diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b55eaa..424ba64 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [ main, develop ] + branches: [ main, develop, "release/**" ] pull_request: - branches: [ main, develop ] + branches: [ main, develop, "release/**" ] jobs: build-and-test: @@ -134,6 +134,24 @@ jobs: dotnet build -c Debug + # Every direct target must install and compile together; this catches missing + # dependencies and template compile errors that per-template tests can't see. + ALL=$(python3 - "$GITHUB_WORKSPACE" <<'PY' + import os, re, sys + root = sys.argv[1] + reg = open(os.path.join(root, "src/ShellUI.Templates/ComponentRegistry.cs")).read().split("GetComponentContent")[0] + names = [n for n, c in re.findall(r'\{ "([a-z0-9-]+)", (\w+)\.Metadata \}', reg) + if not re.search(r"IsAvailable\s*=\s*false", open(os.path.join(root, f"src/ShellUI.Templates/Templates/{c}.cs")).read())] + print(" ".join(names)) + PY + ) + cd "$TMPDIR/app" + dotnet new blazor -o AllApp --no-restore + cd AllApp + shellui init --tailwind standalone --yes + shellui add $ALL + dotnet build -c Debug + # Pure-NuGet install path — `dotnet add package ShellUI.Components` without # the CLI. Uses a one-off NuGet.config that whitelists ONLY the local feed, # so a missing local package can't silently fall back to nuget.org and pull diff --git a/Directory.Build.props b/Directory.Build.props index d37bcbd..9a0221f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,8 +2,8 @@ - 0.3.0 - rc.2 + 0.4.0 + alpha.1 diff --git a/SHELLUI-FIXES.md b/SHELLUI-FIXES.md deleted file mode 100644 index c9d305a..0000000 --- a/SHELLUI-FIXES.md +++ /dev/null @@ -1,32 +0,0 @@ -# ShellUI fixes - -Problems hit while adding ShellUI 0.3.0-rc.1 components to `src/FDMS.UI` and how each was handled. The fixes now live in the current `0.4.0-alpha.1` source templates; they are not all present in the published `0.3.0-rc.1` package. The legacy `sidebar-js` module remains only for projects that still contain an old generated provider. - -## Edits to generated components - -1. `Tabs.razor` **does not compile.** The template emits `private string _effectiveValue = ";` (an unterminated string). Fixed to `= "";`. -2. `Select.razor` **depends on an icon font nothing loads.** It draws the chevron with `expand_more`, which needs the Material Symbols font, so without it the arrow would render as the plain text "expand_more". Replaced with an inline SVG chevron. -3. `SidebarProvider.razor` **never detects mobile.** The old template imported `./shellui-sidebar.js`, which resolves against the page URL and fails when the component is compiled into a Razor class library. New CLI installs use the host-loaded `shellui.js` and call `ShellUI.initSidebar`; no library-specific path is required. The legacy `shellui-sidebar.js` module remains available only for projects that still use the old generated provider. -4. `SidebarInset.razor` **lets wide content push the page sideways.** The `
` is a flex child without `min-w-0`, so a wide table stretches it past the viewport instead of scrolling inside its own container. Added `min-w-0`. -5. `Skeleton.razor` **uses `ClassName` while the component API and demo use `Class`.** Calls such as `` therefore pass `Class` through unmatched attributes instead of merging it with the skeleton classes, which can remove the pulse/background styling. The component now exposes `Class`, retains `ClassName` for compatibility, and renders `ChildContent` like the ShellUI demo. - - - -## Customised from the dashboard-02 template - -- `Components/Layout/DashboardLayout02.razor`: breadcrumb labels, redirect to the account page while a password change is pending, `min-w-0` on the content area. -- `Components/UI/AppSidebar.razor`: FDMS navigation, admin-only Users link, account and sign-out entries. - - - -## Things to know when using it - -- **Class overrides do not merge.** `Shell.Cn` joins class strings and does not resolve conflicts like tailwind-merge does. Passing `Class="max-w-2xl"` to something that already sets `max-w-lg` or `sm:max-w-sm` is a coin toss. Use the important modifier, for example `sm:max-w-2xl!` (see `InvoiceDetailSheet.razor`). -- **Styles and scripts come from the library.** `FDMS.UI` builds `wwwroot/app.css` with the Tailwind CLI (`Build/ShellUI.targets`, needs Node). Hosts must reference `_content/FDMS.UI/app.css` and `_content/FDMS.UI/shellui.js` and must not copy the CSS into their own `wwwroot`. -- **Tailwind only scans** `FDMS.UI` **by default.** Classes used in a host's own markup (for example `FDMS.Web/Components`) are not generated unless the host is listed with `@source` in `wwwroot/input.css`. -- **Theme flash.** `ThemeToggle` assumes dark when nothing is stored, and it only touches the `dark` class after the first render. The host needs the small inline script in `FDMS.Web/Components/App.razor` that sets a default and adds `dark` before Blazor starts. -- **No prerender with browser storage.** The signed-in session lives in browser storage, so `FDMS.Web` renders `Routes` and `HeadOutlet` with `prerender: false`. `ThemeToggle` and `SidebarProvider` also need JS interop and skip it during prerender. -- `data-table` pulls in `System.Linq.Dynamic.Core`. It filters and sorts by property name, so columns need a real `PropertyName`. -- **Icons.** The `ShellIcons.Blazor` package (Lucide names, for example ``) is used for icons rather than inline SVGs. -- **Run the CLI from** `src/FDMS.UI`, next to `shellui.json`. - diff --git a/ShellUI.Tests/TemplateCompileTests.cs b/ShellUI.Tests/TemplateCompileTests.cs index 748f411..227a011 100644 --- a/ShellUI.Tests/TemplateCompileTests.cs +++ b/ShellUI.Tests/TemplateCompileTests.cs @@ -90,7 +90,6 @@ public void EveryRazorTemplate_CodeBlockParses() var failures = new List(); foreach (var (name, metadata) in ComponentRegistry.Components) { - if (!metadata.IsAvailable) continue; if (!metadata.FilePath.EndsWith(".razor", System.StringComparison.OrdinalIgnoreCase)) continue; var content = ComponentRegistry.GetComponentContent(name); @@ -117,6 +116,32 @@ public void EveryRazorTemplate_CodeBlockParses() string.Join("\n ", failures)); } + [Fact] + public void EveryRegistryEntry_HasContent() + { + var empty = ComponentRegistry.Components.Keys + .Where(name => string.IsNullOrWhiteSpace(ComponentRegistry.GetComponentContent(name))) + .ToList(); + Assert.True(empty.Count == 0, "Registry entries with empty content:\n " + string.Join("\n ", empty)); + } + + [Fact] + public void EveryHiddenEntry_IsReachableFromAnInstallableTarget() + { + var reachable = new HashSet(); + var stack = new Stack(ComponentRegistry.Components.Where(c => c.Value.IsAvailable).Select(c => c.Key)); + while (stack.Count > 0) + { + var name = stack.Pop(); + if (!reachable.Add(name)) continue; + foreach (var dep in ComponentRegistry.Components[name].Dependencies ?? new List()) + stack.Push(dep); + } + // sidebar-js: legacy, kept for projects that still load shellui-sidebar.js. + var orphans = ComponentRegistry.Components.Keys.Where(k => !reachable.Contains(k) && k != "sidebar-js").ToList(); + Assert.True(orphans.Count == 0, "Hidden entries no installable target depends on:\n " + string.Join("\n ", orphans)); + } + /// Strips Razor markup directives so the remaining text can be best-effort /// parsed as C#. Not a real Razor parser — just enough to surface useful /// diagnostics when ExtractCodeBlock fails. diff --git a/ShellUI.Tests/TemplateSyncTests.cs b/ShellUI.Tests/TemplateSyncTests.cs index fbed9ee..3c64fc1 100644 --- a/ShellUI.Tests/TemplateSyncTests.cs +++ b/ShellUI.Tests/TemplateSyncTests.cs @@ -53,6 +53,37 @@ public class TemplateSyncTests [InlineData("chat-input", "ChatInput.razor")] [InlineData("chat", "Chat.razor")] [InlineData("command", "Command.razor")] + [InlineData("select", "Select.razor")] + [InlineData("select-trigger", "SelectTrigger.razor")] + [InlineData("select-content", "SelectContent.razor")] + [InlineData("select-item", "SelectItem.razor")] + [InlineData("context-menu", "ContextMenu.razor")] + [InlineData("context-menu-trigger", "ContextMenuTrigger.razor")] + [InlineData("context-menu-content", "ContextMenuContent.razor")] + [InlineData("context-menu-option", "ContextMenuOption.razor")] + [InlineData("navigation-menu", "NavigationMenu.razor")] + [InlineData("nav-list", "NavList.razor")] + [InlineData("nav-item", "NavItem.razor")] + [InlineData("nav-trigger", "NavTrigger.razor")] + [InlineData("nav-content", "NavContent.razor")] + [InlineData("carousel", "Carousel.razor")] + [InlineData("carousel-list", "CarouselList.razor")] + [InlineData("carousel-slide", "CarouselSlide.razor")] + [InlineData("dropdown", "Dropdown.razor")] + [InlineData("dropdown-trigger", "DropdownTrigger.razor")] + [InlineData("dropdown-content", "DropdownContent.razor")] + [InlineData("dropdown-item", "DropdownItem.razor")] + [InlineData("popover", "Popover.razor")] + [InlineData("popover-trigger", "PopoverTrigger.razor")] + [InlineData("popover-content", "PopoverContent.razor")] + [InlineData("hover-card", "HoverCard.razor")] + [InlineData("hover-card-trigger", "HoverCardTrigger.razor")] + [InlineData("hover-card-content", "HoverCardContent.razor")] + [InlineData("tabs", "Tabs.razor")] + [InlineData("accordion", "Accordion.razor")] + [InlineData("accordion-item", "AccordionItem.razor")] + [InlineData("accordion-trigger", "AccordionTrigger.razor")] + [InlineData("accordion-content", "AccordionContent.razor")] public void TemplateCodeBlock_MatchesLiveLibrary(string templateName, string razorFileName) { if (AllowedDrift.ContainsKey(templateName)) return; diff --git a/shellui-integration-notes.md b/shellui-integration-notes.md deleted file mode 100644 index 8ed77a4..0000000 --- a/shellui-integration-notes.md +++ /dev/null @@ -1,297 +0,0 @@ -# ShellUI + ShellIcons integration notes - -> [!NOTE] -> These are dated consumer-project notes captured against published `0.3.0-rc.1`. They are not the current product documentation. Current source facts and workflows are maintained in `README.md` and `docs/`. - -Notes captured while wiring the `SageEvolutionApi.Dashboard` Blazor Server project on `feat/dashboard`. Written for whoever picks this up next — either to reproduce, or to hand upstream to `shellui-dev`. - -Ground truth used: -- Local ShellUI source: `C:\Users\Shewatipa\source\repos\Shell\shellui\shellui\shellui` (`0.4.0-alpha.1` in `Directory.Build.props`) -- Published, installed, and used in this project: - - **`ShellUI.CLI` 0.3.0-rc.1** (`dotnet tool install -g ShellUI.CLI --version 0.3.0-rc.1`) - - **Component templates 0.3.0-rc.1** (delivered by the CLI as `.razor` sources into `Components/UI/`) - - **`ShellIcons.Blazor` 0.1.0-alpha** (NuGet) - ---- - -## Install-channel gotcha (this bit us first) - -`dotnet tool install -g ShellUI.CLI` — no version — grabs stable-latest, which is **0.2.1**. All the fixes in `docs/RELEASE_NOTES.md` under the 0.3.0-rc.1 heading are trapped behind that: `App.razor` isn't patched with `@rendermode` / theme bootstrap / `shellui.js`, `input.css` is written as a single `@import "tailwindcss";` line so every `bg-background` class resolves to nothing, `data-table-models` fails to install, `ThemeToggle` uses `eval` and crashes during prerender. - -**Do this instead:** -```powershell -dotnet tool install -g ShellUI.CLI --version 0.3.0-rc.1 -# or upgrade an existing 0.2.1 -dotnet tool update -g ShellUI.CLI --version 0.3.0-rc.1 -``` -Then `shellui --version` should report `0.3.0-rc.1+…`. - -**Upstream ask.** Either (a) mark 0.3.0-rc.1 as the recommended install in the README quickstart (right now it says `dotnet tool install -g ShellUI.CLI` with no channel), or (b) publish 0.3.0 stable so plain-install lands on the fixes. Also worth: an `--allow-prerelease` reminder printed by `shellui --version` when a newer prerelease exists on NuGet. - ---- - -## Bugs still shipping in 0.3.0-rc.1 CLI - -After re-installing at 0.3.0-rc.1 and re-running `shellui init --yes --tailwind npm` + `shellui add `, three real bugs remained. - -### 1. `accordion-type` sub-dep not registered (same class as the 0.2.1 `data-table-models` bug) - -**Symptom.** `shellui add accordion` reports `Failed: accordion-type`. `Accordion.razor` compiles-in `[Parameter] public AccordionType Type { get; set; } = AccordionType.Single;` but `AccordionType.cs` never lands, so the project doesn't compile. - -**Verified in source.** `src/ShellUI.Templates/Templates/AccordionTypeTemplate.cs` exists with `Name = "accordion-type"`, `FilePath = "AccordionType.cs"`, `IsAvailable = false`, and `AccordionTemplate.cs:16` declares `Dependencies = new List { "accordion-type", "accordion-item" }`. So the template is there and the dep is declared — the wiring inside `ComponentRegistry.cs` is presumably what's missing (same shape as `data-table-models` and `chart-styles` were before). The RELEASE_NOTES fix that landed `data-table-models` didn't cover this case. - -**Workaround.** Hand-write `Components/UI/AccordionType.cs`: -```csharp -namespace SageEvolutionApi.Dashboard.Components.UI; -public enum AccordionType { Single, Multiple } -``` - -**Upstream ask.** Add a test in `NuGetDepsAndSuggestionsTests.cs` that walks *every* component's `Dependencies` list and asserts each name resolves via `ComponentRegistry.GetMetadata(...)`. That catches this bug class once and for all. - -### 2. `Tabs.razor` template ships with a mangled string literal - -**Symptom.** `Components/UI/Tabs.razor(18,38): error RZ1000: Unterminated string literal.` - -**Cause.** The installed Tabs.razor line 18 reads `private string _effectiveValue = ";` — three chars where four (`"";`) should be. The template source (`src/ShellUI.Templates/Templates/TabsTemplate.cs:35`) declares it as `private string _effectiveValue = "";` inside a `@"...""..."` verbatim block, so one of the two escape-doubled `""` gets clipped by the CLI's placeholder substitution or by the verbatim-decoder before write-out. `TemplateCompileTests` (per RELEASE_NOTES) should have caught this — it seems the compile pass is running against the raw template source, not against the CLI's *output* after placeholder substitution. - -**Workaround.** -```razor -private string _effectiveValue = ""; -``` - -**Upstream ask.** Add a `TemplateOutputCompileTest` variant that (a) runs each template through the placeholder substitution the CLI uses, then (b) `CSharpSyntaxTree.ParseText` on the substituted result. That's the surface consumers actually see. - -### 3. `Select.razor` renders "expand_more" as literal text - -**Symptom.** The Select dropdown shows the raw string `expand_more` in the corner where a chevron should be. - -**Cause.** Line 12: `expand_more`. This depends on Google Material Symbols being loaded via `` — which ShellUI never adds to `App.razor` and doesn't list as a required host resource. Every other ShellUI component uses inline SVG for its chrome (see DataTable's sort arrow, ThemeToggle's sun/moon, Sheet's close X, Accordion's chevron), so this is the odd one out. - -**Workaround.** -```razor - - - -``` - -**Upstream ask.** Replace the Material Symbols span with the inline chevron SVG — matches every other component in the library and drops a hidden network dependency. - ---- - -## 0.2.1 branch — fix candidates (backport from 0.3.0-rc.1) - -Each of these was hit on a real `dotnet tool install -g ShellUI.CLI` (stable-latest = 0.2.1) + `dotnet new blazor --interactivity Server --empty` + `shellui init --yes --tailwind npm` + `shellui add …` run in this project on 2026-09-04. All are fixed in `main` / 0.3.0-rc.1. If you want to cut a `hotfix/v0.2.1.1` branch off the `v0.2.1` tag, this section is the punch list — each item lists the symptom I saw, a minimal repro, the file(s) to touch in the source, and the pattern the fix in `main` took so you can cherry-forward it. - -Reference for every "the fix pattern in main is…" line below: `docs/RELEASE_NOTES.md` §"🐛 Critical fixes" under the 0.3.0-rc.1 heading. - -### B1. `input.css` written as one-line `@import "tailwindcss";` - -**Symptom.** Every ShellUI component renders unstyled. `bg-background`, `text-foreground`, `text-muted-foreground`, `border-border`, `bg-card` etc. all resolve to nothing because Tailwind v4 needs the tokens declared under `@theme inline`, and none of them are there. - -**Repro.** After `shellui init` on 0.2.1, `wc -l wwwroot/input.css` → `1`. - -**Files.** `src/ShellUI.CLI/Services/ThemeService.cs` (writes `input.css`). - -**Fix pattern.** `main` writes ~193 lines: `:root {…light HSL vars…}`, `.dark {…dark HSL vars…}`, `@theme inline {…Tailwind color-token mapping…}`, `@custom-variant dark`, `@layer base {…}`, and Loading-component keyframes. Read the current `input.css` template out of `main`'s `ThemeService` and drop it verbatim. - -**Consumer-side test to pin the fix.** After init, assert the file contains at minimum: `@theme inline`, `--background`, and `.dark { --background`. Anything less is a broken install. - -### B2. `App.razor` not patched with `@rendermode`, theme bootstrap, or `shellui.js` - -**Symptom.** No JS interop reaches any component (ThemeToggle silently no-ops, InputOTP focus-forward doesn't work, Sonner toasts don't appear). `` renders SSR-only markup so the head never hydrates. On dark-preference machines, the page flashes light before the theme applies (FOUC). - -**Repro.** After `shellui init` on 0.2.1, open `Components/App.razor` — the file the template produced is identical to `dotnet new blazor`'s default: no `@rendermode`, no `