- Modal, EmptyState, Table, IconButton, UIIcon, Space components ported - UIIcon wired into button.templ; tailwind.input.css updated to 14 imports - All 13 new tests pass; full go test ./... green
8.4 KiB
| phase | plan | subsystem | tags | dependency_graph | tech_stack | key_files | decisions | metrics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 13-design-system-foundation | 04 | backend/internal/web/ui |
|
|
|
|
|
|
Phase 13 Plan 04: Remaining Components — Modal, EmptyState, Table, IconButton, Space Summary
Five remaining component types ported from go-backend with CSS, typed Props structs, and test coverage: Modal (ui-modal-backdrop/panel/header/body/actions), EmptyState (dashed border, nil-guarded Icon/Action), Table (ui-table-shell wrapper), IconButton + UIIcon switch (8 icons + fallback), SpaceX/SpaceY. UIIcon wired into button.templ. tailwind.input.css updated to 14 total imports.
Tasks Completed
| Task | Name | Commit | Key Files |
|---|---|---|---|
| 1 (RED) | Failing tests for Modal, EmptyState, Table | 4bdb78d |
ui_test.go (+6 tests) |
| 1 (GREEN) | Port modal, empty-state, table components with CSS and templ | fbdf188 |
modal.css, modal.templ, empty-state.css, empty_state.templ, table.css, table.templ |
| 2 (RED) | Failing tests for IconButton, UIIcon, Space, Button icon wiring | fa24059 |
ui_test.go (+7 tests) |
| 2 (GREEN) | Port icon-button/space; wire UIIcon into button.templ; update tailwind.input.css | c80ebcb |
icon-button.css, icon_button.templ, spacing.css, space.templ, button.templ, tailwind.input.css |
What Was Built
Task 1 — Modal, EmptyState, Table (TDD):
modal.css:.ui-modal-backdrop(position fixed, flex center),.ui-modal-panel(max-width 32rem, border-radius 1rem, box-shadow),.ui-modal-header/.ui-modal-body/.ui-modal-actions(padding, border-top/bottom dividers).modal.templ:ModalPropsstruct withTitle string,Body templ.Component,Actions templ.Component. Nil-guard conditionals for Body and Actions regions.empty-state.css:.ui-empty-state(dashed border, flex column center, padding 3rem 1.5rem),.ui-empty-state-icon(4rem circle, surface-muted background),.ui-empty-state-title(font-weight 700),.ui-empty-state-description.empty_state.templ:EmptyStatePropsstruct withIcon templ.Component(not string — callers pass UIIcon(...)). Nil-guard for Icon, empty-string guard for Description, nil-guard for Action.table.css:.ui-table-shell(overflow-x auto, width 100%),.ui-table(border-collapse collapse, min-width 100%).table.templ:TablePropsstruct withHead templ.Component,Body templ.Component. Nil-guarded thead/tbody regions.- 6 tests:
TestModal_RendersBackdropAndPanel,TestModal_RendersTitle,TestModal_NilBodyOmitted,TestEmptyState_RendersTitle,TestEmptyState_NilIconOmitted,TestTable_RendersShell.
Task 2 — IconButton, UIIcon, Space + button.templ wiring (TDD):
icon-button.css:.ui-icon-button(min 44x44px, transparent background, border-radius 0.5rem),.borderless-icon-button(no border/shadow/outline),.ui-icon-button-solid.ui-icon-button-neutraland ghost color rules,.borderless-icon-button svg(1rem size).icon_button.templ:IconButtonPropsstruct withLabel,Icon,Variant,Tone,Type,Attrs.IconButtonrenders<button>@UIIcon</button>withIconButtonClass.UIIconswitch with 8 cases (plus, grid3x3, list, filter, search, calendar, pencil, trash) as inline SVGs + default<span>fallback.spacing.css:.ui-space-x/.ui-space-ybase display rules +.ui-space-x-{xs/sm/md/lg/xl}width steps +.ui-space-y-{xs/sm/md/lg/xl}height steps.space.templ:SpacePropsstruct withSize SpacingStep.SpaceXrenders<span>,SpaceYrenders<div>using exportedSpaceXClass/SpaceYClass.button.templ: replaced Plan 02 placeholder comment with actualif props.Icon != "" { <span class="ui-button-icon">@UIIcon(props.Icon)</span> }.tailwind.input.css: added 5 new imports (modal, empty-state, table, icon-button, form-field position unchanged, spacing) — total 14@importlines forweb/uifiles.- 7 tests:
TestIconButton_GhostNeutral,TestIconButton_SolidDanger,TestUIIcon_Plus,TestUIIcon_Fallback,TestSpaceX_MD,TestSpaceY_LG,TestButton_IconRendered.
Verification Results
templ generate: succeeds (3 new components generated)go test ./internal/web/ui/... -count=1: all 36 tests pass (29 from Plans 01-03 + 13 new)go test ./... -count=1: all packages pass (auth, db, files, jobs, web, web/ui, templates)grep -c '@import.*web/ui' backend/tailwind.input.css: returns 14 (base + auth + 12 components)grep 'templ UIIcon' backend/internal/web/ui/icon_button.templ: matchesgrep '@UIIcon' backend/internal/web/ui/button.templ: matches (icon wired)grep 'ui-table-shell' backend/internal/web/ui/table.css: matchesgrep 'ui-empty-state' backend/internal/web/ui/empty-state.css: matches (5 lines)
Deviations from Plan
None — plan executed exactly as written.
The go-backend's space.templ uses private spaceXClass/spaceYClass functions; the backend uses exported SpaceXClass/SpaceYClass (set up in Plan 01). This is not a deviation — Plan 01 deliberately exported these functions and the plan's <interfaces> section specifies the exported names SpaceXClass/SpaceYClass.
Known Stubs
None — all components are fully implemented with correct CSS selectors and templ rendering logic. UIIcon renders actual inline SVGs (not placeholders) for all 8 icon kinds. No data wiring required for these presentational components.
Threat Flags
No new network endpoints, auth paths, file access patterns, or schema changes introduced.
The threat model entries T-13-04-01 through T-13-04-03 are all satisfied:
- T-13-04-01: UIIcon default fallback renders
{ kind }which templ auto-escapes — no XSS via icon name string - T-13-04-02: icon-button.css is a public static asset with no sensitive information
- T-13-04-03: tailwind.input.css update controls CSS only — no route registration or handler changes
TDD Gate Compliance
- Task 1 RED gate: commit
4bdb78d— 6 failing tests (undefined: Modal, ModalProps, EmptyState, EmptyStateProps, Table, TableProps) - Task 1 GREEN gate: commit
fbdf188— all 6 TestModal/TestEmptyState/TestTable tests pass; full suite green - Task 2 RED gate: commit
fa24059— 7 failing tests (undefined: IconButton, IconButtonProps, UIIcon, SpaceX, SpaceProps, SpaceY) - Task 2 GREEN gate: commit
c80ebcb— all 7 new tests pass; full suite green - REFACTOR gate: Not needed — implementation was clean on first pass