docs(13-03): complete form input components plan summary
- Input, Textarea, Select, FormField components ported from go-backend - TDD gates: 4 RED/GREEN commits, all 13 new tests passing - Full go test ./... is green (all packages)
This commit is contained in:
parent
52fb77d4f8
commit
569c6c7853
1 changed files with 117 additions and 0 deletions
117
.planning/phases/13-design-system-foundation/13-03-SUMMARY.md
Normal file
117
.planning/phases/13-design-system-foundation/13-03-SUMMARY.md
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
---
|
||||
phase: 13-design-system-foundation
|
||||
plan: "03"
|
||||
subsystem: backend/internal/web/ui
|
||||
tags: [form-components, input, textarea, select, form-field, design-system, tdd]
|
||||
dependency_graph:
|
||||
requires: [13-01, 13-02]
|
||||
provides: [input-component, textarea-component, select-component, form-field-component]
|
||||
affects:
|
||||
- backend/internal/web/ui/input.css
|
||||
- backend/internal/web/ui/input.templ
|
||||
- backend/internal/web/ui/textarea.css
|
||||
- backend/internal/web/ui/textarea.templ
|
||||
- backend/internal/web/ui/select.css
|
||||
- backend/internal/web/ui/select.templ
|
||||
- backend/internal/web/ui/select_helpers.go
|
||||
- backend/internal/web/ui/form-field.css
|
||||
- backend/internal/web/ui/form_field.templ
|
||||
- backend/internal/web/ui/ui_test.go
|
||||
- backend/tailwind.input.css
|
||||
tech_stack:
|
||||
added: []
|
||||
patterns: [verbatim-port, tdd-red-green, inline-js-htmx-reinit, nil-guard-optional-regions]
|
||||
key_files:
|
||||
created:
|
||||
- backend/internal/web/ui/input.css
|
||||
- backend/internal/web/ui/input.templ
|
||||
- backend/internal/web/ui/textarea.css
|
||||
- backend/internal/web/ui/textarea.templ
|
||||
- backend/internal/web/ui/select.css
|
||||
- backend/internal/web/ui/select.templ
|
||||
- backend/internal/web/ui/select_helpers.go
|
||||
- backend/internal/web/ui/form-field.css
|
||||
- backend/internal/web/ui/form_field.templ
|
||||
modified:
|
||||
- backend/internal/web/ui/ui_test.go
|
||||
- backend/tailwind.input.css
|
||||
decisions:
|
||||
- "InputProps and TextareaProps include Disabled and Required as explicit bool fields (D-CA03) — conditional attribute rendering via templ if blocks rather than Attrs pass-through"
|
||||
- "select_helpers.go ported verbatim from go-backend with no naming changes (package-private helpers, not exported)"
|
||||
- "tailwind.input.css updated to include all four new CSS imports in order: input, textarea, select, form-field"
|
||||
metrics:
|
||||
duration: "~12 minutes"
|
||||
completed_date: "2026-05-16"
|
||||
tasks: 2
|
||||
files: 11
|
||||
---
|
||||
|
||||
# Phase 13 Plan 03: Form Input Components — Input, Textarea, Select, FormField Summary
|
||||
|
||||
Four form-input component types ported from go-backend with matching CSS, typed Props structs, and full test coverage: Input (class ui-input, inputType/inputID helpers), Textarea (class ui-textarea, textareaRows default 4, resize vertical), Select (ui-select-control, inline JS with __uiSelectInitAll and htmx:afterSwap re-init), FormField (conditional label/hint/error regions).
|
||||
|
||||
## Tasks Completed
|
||||
|
||||
| Task | Name | Commit | Key Files |
|
||||
|------|------|--------|-----------|
|
||||
| 1 (RED) | Failing tests for Input and Textarea | ace9f5b | ui_test.go (7 tests) |
|
||||
| 1 (GREEN) | Port input.templ/input.css and textarea.templ/textarea.css | 9556b20 | input.css, input.templ, textarea.css, textarea.templ, tailwind.input.css |
|
||||
| 2 (RED) | Failing tests for Select and FormField | 50e3fb0 | ui_test.go (6 tests) |
|
||||
| 2 (GREEN) | Port select + form-field components with CSS and helpers | 52fb77d | select.css, select.templ, select_helpers.go, form-field.css, form_field.templ, tailwind.input.css |
|
||||
|
||||
## What Was Built
|
||||
|
||||
**Task 1 — Input and Textarea (TDD):**
|
||||
- `input.css`: `.ui-input` with `appearance: none`, `background: var(--color-surface-default)`, `border-radius: 0.75rem`, `min-height: 44px`, `padding: 0.75rem 0.95rem`, `width: 100%`, plus placeholder and focus rules.
|
||||
- `input.templ`: `InputProps` struct with `ID`, `Name`, `Value`, `Placeholder`, `Type`, `Disabled bool`, `Required bool`, `Attrs templ.Attributes`. Uses `inputID(props.ID, props.Name)` and `inputType(props.Type)` helpers. Conditional `disabled`/`required` attributes.
|
||||
- `textarea.css`: `.ui-textarea` with same base styling as input plus `min-height: 7rem` and `resize: vertical`.
|
||||
- `textarea.templ`: `TextareaProps` struct with `ID`, `Name`, `Value`, `Placeholder`, `Rows int`, `Disabled bool`, `Required bool`, `Attrs templ.Attributes`. Uses `inputID()` and `textareaRows()` helpers (defaults to 4 rows).
|
||||
- `tailwind.input.css`: Added `@import` for `input.css` and `textarea.css`.
|
||||
- 7 tests passing: `TestInput_DefaultType`, `TestInput_EmailType`, `TestInput_IDFallback`, `TestInput_ExplicitID`, `TestTextarea_RendersClass`, `TestTextarea_DefaultRows`, `TestTextarea_ExplicitRows`.
|
||||
|
||||
**Task 2 — Select and FormField (TDD):**
|
||||
- `select_helpers.go`: 9 helper functions ported verbatim from go-backend: `selectPlaceholder`, `selectNativeID`, `selectMenuID`, `selectBoolData`, `selectSelectedValues`, `selectOptionSelected`, `selectSelectedLabels`, `selectSelectedLabel`, `selectMenuOptionClass`, `selectIsDisabled`. Imports `"strings"` and `"github.com/a-h/templ"`.
|
||||
- `select.templ`: `SelectOption` struct (`Value`, `Label`, `Disabled bool`) and `SelectProps` struct (`ID`, `Name`, `Placeholder`, `Value`, `Values []string`, `Multiple bool`, `Options []SelectOption`, `Attrs`). Full custom dropdown with hidden native `<select>`, visible `ui-select-control` button, and dropdown `ui-select-menu`. Inline `<script>` block with `window.__uiSelectInitAll` and `document.addEventListener("htmx:afterSwap", ...)` re-init listener (Pitfall 6 compliance).
|
||||
- `select.css`: `.ui-select` (relative positioned wrapper), `.ui-select-control` (min-height 44px, border-radius 0.75rem), `.ui-select-menu` (absolute positioned, max-height 16rem, overflow-y auto), option states (hover, selected, disabled).
|
||||
- `form_field.templ`: `FormFieldProps` struct with `Label string`, `For string`, `Field templ.Component`, `Error string`, `Hint string`. Conditional label, field, hint, and error rendering with nil/empty guards.
|
||||
- `form-field.css`: `.ui-form-field` (grid layout), `.ui-form-label` (font-weight 600), `.ui-form-hint` (muted color), `.ui-form-error` (danger foreground color).
|
||||
- `tailwind.input.css`: Added `@import` for `select.css` and `form-field.css`.
|
||||
- 6 tests passing: `TestSelect_RendersControl`, `TestSelect_HasInlineScript`, `TestSelect_HasHtmxListener`, `TestFormField_RendersLabel`, `TestFormField_RendersError`, `TestFormField_NoErrorWhenEmpty`.
|
||||
|
||||
## Verification Results
|
||||
|
||||
- `just generate`: succeeds (no templ compile errors)
|
||||
- `go test ./internal/web/ui/... -count=1`: all 29 tests pass
|
||||
- `go test ./... -count=1`: all packages pass (auth, db, files, jobs, web, web/ui, templates)
|
||||
- `grep -l 'ui-input\|ui-textarea\|ui-select\|ui-form-field' backend/internal/web/ui/*.css`: lists 4 files
|
||||
- `grep '__uiSelectInitAll' backend/internal/web/ui/select.templ`: matches (inline JS present)
|
||||
- `grep 'htmx:afterSwap' backend/internal/web/ui/select.templ`: matches (re-init listener present)
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
None — plan executed exactly as written. `Disabled` and `Required` bool fields were added to `InputProps` and `TextareaProps` as specified by D-CA03 (plan action step 3 and 5). This was within the plan's explicit instructions.
|
||||
|
||||
## Known Stubs
|
||||
|
||||
None — all components are fully implemented with correct CSS selectors and templ rendering logic. 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-03-01 (inline script read-only DOM), T-13-03-02 (templ auto-escapes FormField Error), and T-13-03-03 (static Disabled/Required attributes) are all satisfied:
|
||||
- The inline select script reads only DOM data attributes — no user-controlled string is eval'd
|
||||
- `FormFieldProps` strings (Label, Hint, Error) are server-controlled and templ auto-escapes them
|
||||
- Disabled/Required are Go bool fields rendered as static HTML attributes
|
||||
|
||||
## TDD Gate Compliance
|
||||
|
||||
- Task 1 RED gate: commit `ace9f5b` — 7 failing tests (undefined: Input, undefined: InputProps, undefined: Textarea, undefined: TextareaProps)
|
||||
- Task 1 GREEN gate: commit `9556b20` — all 7 TestInput/TestTextarea tests pass; full suite green
|
||||
- Task 2 RED gate: commit `50e3fb0` — 6 failing tests (undefined: Select, undefined: SelectProps, undefined: FormField, undefined: FormFieldProps)
|
||||
- Task 2 GREEN gate: commit `52fb77d` — all 6 TestSelect/TestFormField tests pass; full suite green
|
||||
- REFACTOR gate: Not needed — implementation was clean on first pass
|
||||
|
||||
## Self-Check: PASSED
|
||||
Loading…
Reference in a new issue