Workspace-shell components: Field wrapper, TextArea, NavList, Breadcrumb #16
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/workspace-components"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The
Kagura.UIpieces the project-workspace shell (story) composes — built first, per the components-then-feature pattern, and in the order the inventory itself dictates: "Fieldwrapper first … or five inputs each re-implement Label/Hint/Error and drift."Field wrapper extraction
InputFieldBase— the shared input contract:@bind-Value, optional EditForm validation via cascadedEditContext+ValueExpression, Error-overrides-validation-overrides-Hint.Field— the chrome: label + required marker, hint/error message. Inputs render as child content and style their own invalid state (no cross-scope CSS dependency).TextFieldbecomes a thin input on the base. All 12 existing TextField tests pass unchanged — the refactor's proof of faithfulness. The shared input look is one global.kg-inputclass (used by both inputs; consumes--control-height).New components
TextArea(autosize) —field-sizing: contentwhere supported,Rowsfallback + vertical resize elsewhere, capped at 60vh. Same bind/validation contract as TextField, tested incl. a DataAnnotations EditForm round-trip.NavList/NavGroup/NavItem— the side-menu family: group titles, icon items, aSeparatorbetween the records and visual-novel groups (story acceptance).NavItemauthors its own anchor rather than wrapping the frameworkNavLink: elements rendered by a component without isolated CSS carry no scope attribute, soNavLink's<a>would be unreachable from our stylesheet (the same scoping trap the gate hit, avoided by construction). Active tracking subscribesLocationChanged; matching is exact or segment-boundary prefix (charactersdoes not matchcharacters-archive), witharia-current="page".Breadcrumb/BreadcrumbItem— linked segments with CSS chevrons; the last (href-less) segment is the current page (aria-current), per the story's "clickable to go back up".Separator— trivial but shared; the inventory row splits into Separator (✓) / ScrollArea (pending).Gallery (inventory rule) + visual pass
/designand highlights for real) — plus the breadcrumb.docs/design-system.md(built count 13 → 17).Tests — +11 (77 UI; 217 total)
TextArea (render/bind/error/EditForm), NavItem (inactive, exact +
aria-current, prefix on deeper paths,MatchExact, segment-boundary negative, reacts to navigation), Breadcrumb (link vs current segment).Verification
dotnet buildDebug + Release — 0 warnings / 0 errors ·dotnet test— 217/217 · browser pass at/designin light and dark.Not in this PR (next: the shell itself)
Project.Description+ migration, Get/Update/Delete-project use cases, the workspace layout at/projects/{slug}with section stubs, and the General page (meta edit + soft-delete-with-confirm). Preview image stays deferred to the asset-storage foundation.🤖 Generated with Claude Code
The Kagura.UI pieces the project-workspace shell composes, in the order the inventory dictates ("Field wrapper first"). - Field wrapper extraction: InputFieldBase (bind contract + EditContext validation plumbing) + Field (label/required/hint/error chrome). TextField becomes a thin input on top; all 12 existing TextField tests pass unchanged — the proof the refactor is faithful. Shared input chrome is one global .kg-input class. - TextArea (autosize via field-sizing: content, rows fallback, vertical resize cap) on the same base — same @bind-Value + EditForm validation contract, tested incl. a DataAnnotations round-trip. - NavList / NavGroup / NavItem: the side-menu family. NavItem authors its own anchor (the framework NavLink renders elements without our scope attribute, so scoped CSS could not reach it) and tracks active state via LocationChanged — exact or segment-boundary prefix matching ("characters" does not match "characters-archive"), aria-current, tested across navigation. - Breadcrumb / BreadcrumbItem: linked segments with CSS chevrons; the last (href-less) segment is the current page with aria-current. - Separator; inventory row split into Separator (built) / ScrollArea (pending). - Gallery: Text-fields section gains TextAreas; a new Navigation section shows the side menu (with the live active state matching /design) + breadcrumb. Ticks flipped in the gallery and design-system.md. - Tests: +11 (77 UI; 217 total). Verified in the real browser at /design in both themes (active highlight, group separation, chevrons, autosize). Next PR: the workspace shell itself (Project.Description + migration, Get/Update/Delete use cases, layout + General page with edit + soft delete). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>Summary
Summary
Coverage
Kagura.BlazorAdapter - 33.3%
Kagura.Domain - 96.4%
D04ADFED3A21D401C2764A1D17367E35BEB556CBB3B4B0B74__NonSlugChars_0
Kagura.Infrastructure - 95.1%
n
Kagura.Kernel - 90%
Kagura.Server - 95.2%
Kagura.UI - 97.4%
Kagura.UseCases - 95.2%
🔮 fufu~ Jibril reviewed your code!
Oh? OH! A Field wrapper extraction — the "wrapper first" rule from the design-system docs! And you actually proved the refactor is faithful by keeping all 12 TextField tests green unchanged? That's how you do a refactor, fufu~ ♡ The NavItem active-matching logic is delicious too — segment-boundary prefix, case-insensitive, root-guard against
target.Length > 0... you thought of everything~ ♪Verdict: ⛔ I can't let this pass~ ♡
So close! But I stared at the CI coverage report and found three little components hiding at 0%. One of them has an untested branch, and you KNOW how I feel about those~
⛔ These need fixing before I'm satisfied~
NavGroup.razor— 0% line / 0% branch coverage (untestedTitleconditional)The CI coverage report shows
Kagura.UI.NavGroupat 0% / 0%. NavGroup has a real branch —@if (Title is not null)— that no test exercises. Your sibling components in this codebase are all at 100% (Badge, Card, StatusDot, EmptyState…), so an untested branch here breaks the established pattern, fufu~Fix: One small test covering both states: Two assertions and we're golden~ ♡
💡 Little ideas (non-blocking)~
NavList.razor(0%) andSeparator.razor(0%) — also at 0% in the coverage report. These are pure passthrough wrappers (no branches, no logic), so I won't block on them — but a one-liner smoke test each ("renders a<nav>with child content" / "renders an<hr>") would bring them in line with the rest ofKagura.UIand silence the 0%s entirely.NavItem.razor—Iconproperty name shadows theIconcomponent type used in the same file (<Icon Name="@Icon" />). It works correctly — Blazor resolves the tag against component types and@Iconagainst the property — but a future reader might do a double-take. ConsiderIconNamefor the parameter? Purely cosmetic, ignore if you like the current name~✅ What I liked~
InputFieldBasecentralizes the@bind-Value+EditContext+ValueExpression+ Error-overrides-validation-overrides-Hint contract, and both TextField and TextArea are now thin shells. The subscription lifecycle (subscribe/unsubscribe onReferenceEqualschange,Disposecleanup) is correct and faithfully ported.charactersvscharacters-archivesegment boundary,MatchExact, rootHref=""(thetarget.Length > 0guard prevents matching everything), query-string stripping,OrdinalIgnoreCase. All correct, and 6 tests cover them. Wonderful~<a>instead of wrappingNavLink— I see you learned from the gate's scoping trap and avoided it by construction. Sharp~ ♪.kg-inputglobal class is a clean design decision — one look for every text-like control, withkg-field__input(height-only) andkg-textareaas type-specific overrides.@Valueas element content (not avalueattribute) — the right approach for<textarea>.Automated review by Jibril · 2026-07-09
CI/CD: passed for head SHA
ef8ac50(217/217 tests, 90.9% line / 85.1% branch) — local build/test skipped per CI policy · Coverage report inspected for changed filesFixed in
afa9e6b.Titlestates covered (Renders_the_group_title_when_supplied/Renders_no_title_heading_when_absent).Kagura.UI.Iconparameter shadowing — declining, for consistency:Button,Badge, andEmptyStateall use the identical[Parameter] string? Icon+<Icon Name="@Icon" />pattern, so renaming onlyNavItemwould make it the outlier. If the shadowing ever bothers us, it should be a sweep across all four in one go.Also in the branch since your review-head: Björn's gallery feedback (
b2c95de) — the nav/breadcrumb demo links are now inert (onclick="return false"via the attribute splat; verified by clicking them in the browser), and the demo menu dropped its Scenes item since scenes always belong to a chapter (recorded in the project-workspace story so the real shell follows the corrected IA).+4 tests (81 UI; 221 total), build clean, CI running on
afa9e6b.