docs(09-md3-components): create phase plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
---
|
||||
phase: 09-md3-components
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- src/components/ui/TextFieldMD3.tsx
|
||||
- src/components/ui/TextFieldMD3.test.tsx
|
||||
- src/styles/md3-buttons.ts
|
||||
- src/components/ui/FieldRenderer.tsx
|
||||
- src/components/ui/FieldRenderer.test.tsx
|
||||
autonomous: true
|
||||
requirements: [COMP-01, COMP-02, DEBT-01]
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "TextFieldMD3 renders an outlined input with a floating label that floats on focus"
|
||||
- "TextFieldMD3 floating label floats when the field has a value (not just on focus)"
|
||||
- "TextFieldMD3 preserves htmlFor/id pairing so getByLabelText queries work"
|
||||
- "MD3 button class constants exist for filled, outlined, and text variants"
|
||||
- "FieldRenderer text-branch tooltip button uses aria-label instead of sr-only span"
|
||||
artifacts:
|
||||
- path: "src/components/ui/TextFieldMD3.tsx"
|
||||
provides: "MD3 outlined text field with CSS floating label"
|
||||
exports: ["TextFieldMD3"]
|
||||
- path: "src/components/ui/TextFieldMD3.test.tsx"
|
||||
provides: "Unit tests for floating label behavior"
|
||||
min_lines: 30
|
||||
- path: "src/styles/md3-buttons.ts"
|
||||
provides: "MD3 button class constants"
|
||||
exports: ["MD3_BTN_FILLED", "MD3_BTN_OUTLINED", "MD3_BTN_TEXT"]
|
||||
- path: "src/components/ui/FieldRenderer.test.tsx"
|
||||
provides: "Tests for DEBT-01 aria-label consistency"
|
||||
min_lines: 20
|
||||
key_links:
|
||||
- from: "src/components/ui/TextFieldMD3.tsx"
|
||||
to: "react-hook-form"
|
||||
via: "registration prop spread onto input"
|
||||
pattern: "\\.\\.\\.(registration|register)"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Create MD3 UI primitives: TextFieldMD3 component with CSS-only floating label, MD3 button class constants, and fix FieldRenderer DEBT-01 aria-label inconsistency.
|
||||
|
||||
Purpose: Establish the reusable building blocks that Plan 03 will wire into all wizard steps. TextFieldMD3 is the most complex new component and needs its own test coverage before integration.
|
||||
Output: TextFieldMD3.tsx + tests, md3-buttons.ts constants, FieldRenderer aria-label fix + tests.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/09-md3-components/09-RESEARCH.md
|
||||
|
||||
@src/index.css
|
||||
@src/components/ui/FieldRenderer.tsx
|
||||
@src/components/ui/PasswordField.tsx
|
||||
|
||||
<interfaces>
|
||||
<!-- FieldRenderer and PasswordField both use react-hook-form registration -->
|
||||
|
||||
From src/components/ui/FieldRenderer.tsx:
|
||||
```typescript
|
||||
interface FieldRendererProps {
|
||||
field: FieldDef;
|
||||
register: UseFormRegister<any>;
|
||||
error?: FieldError;
|
||||
}
|
||||
```
|
||||
|
||||
From src/components/ui/PasswordField.tsx:
|
||||
```typescript
|
||||
interface PasswordFieldProps {
|
||||
id: string;
|
||||
label: string;
|
||||
error?: FieldError;
|
||||
registration: UseFormRegisterReturn;
|
||||
placeholder?: string;
|
||||
helpText?: string;
|
||||
tooltipText?: string;
|
||||
}
|
||||
```
|
||||
|
||||
From src/schemas/registry.ts (FieldDef shape):
|
||||
```typescript
|
||||
interface FieldDef {
|
||||
key: string;
|
||||
label: string;
|
||||
required?: boolean;
|
||||
inputType?: 'text' | 'password' | 'select';
|
||||
options?: { value: string; label: string }[];
|
||||
placeholder?: string;
|
||||
helpText?: string;
|
||||
tooltipText?: string;
|
||||
}
|
||||
```
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Create TextFieldMD3 component with floating label + MD3 button constants</name>
|
||||
<files>src/components/ui/TextFieldMD3.tsx, src/components/ui/TextFieldMD3.test.tsx, src/styles/md3-buttons.ts</files>
|
||||
<behavior>
|
||||
- Test 1: TextFieldMD3 renders an input with the given id and a label with matching htmlFor
|
||||
- Test 2: screen.getByLabelText(label) finds the input (htmlFor/id pairing works)
|
||||
- Test 3: Input receives registration props (can be queried after typing)
|
||||
- Test 4: Error message renders with role="alert" when error prop is passed
|
||||
- Test 5: Help text renders when helpText prop is passed and no error
|
||||
- Test 6: Required asterisk renders when required=true
|
||||
- Test 7: Suffix slot renders (for PasswordField show/hide button)
|
||||
- Test 8: Input has placeholder=" " for CSS floating label trick
|
||||
</behavior>
|
||||
<action>
|
||||
Create `src/components/ui/TextFieldMD3.tsx` implementing the CSS-only floating label pattern from the research doc (Pattern 1). Key details:
|
||||
- Interface: `{ id, label, error?, registration, type?, helpText?, required?, suffix? }` where registration is `UseFormRegisterReturn`
|
||||
- Input gets `placeholder=" "` for `:placeholder-shown` CSS detection
|
||||
- Label is absolutely positioned, uses Tailwind `peer` utilities to float on focus and when input has value
|
||||
- Use `peer-[:not(:placeholder-shown)]:` arbitrary variant for has-value state (Tailwind v4 supports this)
|
||||
- All colors use semantic tokens: `border-outline`, `focus:border-primary`, `text-on-surface-container/60`, `text-error`
|
||||
- Error state: `border-error` + error message with `role="alert"`
|
||||
- Suffix slot: absolutely positioned right side (for password show/hide toggle)
|
||||
- Spread `{...registration}` onto the input element
|
||||
|
||||
Create `src/styles/md3-buttons.ts` exporting three constants:
|
||||
- `MD3_BTN_FILLED`: `px-6 py-2.5 rounded-full bg-primary text-on-primary text-sm font-medium hover:opacity-90 active:opacity-80 transition-opacity disabled:opacity-40 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50`
|
||||
- `MD3_BTN_OUTLINED`: `px-6 py-2.5 rounded-full border border-outline text-on-surface text-sm font-medium hover:bg-primary/8 active:bg-primary/12 transition-colors disabled:opacity-40 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50`
|
||||
- `MD3_BTN_TEXT`: `px-4 py-2.5 rounded-full text-primary text-sm font-medium hover:bg-primary/8 active:bg-primary/12 transition-colors disabled:opacity-40 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50`
|
||||
|
||||
Write tests FIRST (RED), then implement (GREEN).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>npx vitest run src/components/ui/TextFieldMD3.test.tsx --reporter=dot</automated>
|
||||
</verify>
|
||||
<done>TextFieldMD3 component renders floating label with proper htmlFor/id pairing, getByLabelText works, error/helpText/suffix/required all render correctly. MD3 button constants exported. All tests pass.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Fix DEBT-01 aria-label inconsistency in FieldRenderer + add tests</name>
|
||||
<files>src/components/ui/FieldRenderer.tsx, src/components/ui/FieldRenderer.test.tsx</files>
|
||||
<behavior>
|
||||
- Test 1: Text-branch tooltip button has aria-label="More info about {field.label}" (not sr-only span)
|
||||
- Test 2: Select-branch tooltip button has aria-label="More info about {field.label}"
|
||||
- Test 3: Both branches' tooltip buttons are findable via getByRole('button', { name: /more info about/i })
|
||||
</behavior>
|
||||
<action>
|
||||
Create `src/components/ui/FieldRenderer.test.tsx` with tests for DEBT-01 aria-label consistency. Tests render FieldRenderer with a mock field that has tooltipText, and verify the tooltip button uses `aria-label` attribute (not sr-only span) for both text-branch and select-branch.
|
||||
|
||||
Then fix FieldRenderer.tsx text-branch (lines 91-99): Replace the `<span className="sr-only">...</span><span aria-hidden="true">...</span>` pattern with a direct `aria-label={...}` on the button and plain `i` icon content, matching the select-branch pattern (lines 49-54).
|
||||
|
||||
The fix is exactly:
|
||||
```tsx
|
||||
// Before (text branch, ~line 91):
|
||||
<button type="button" onClick={...} className="...">
|
||||
<span className="sr-only">More info about {field.label}</span>
|
||||
<span aria-hidden="true">ⓘ</span>
|
||||
</button>
|
||||
|
||||
// After:
|
||||
<button
|
||||
type="button"
|
||||
onClick={...}
|
||||
aria-label={`More info about ${field.label}`}
|
||||
className="..."
|
||||
>
|
||||
ⓘ
|
||||
</button>
|
||||
```
|
||||
|
||||
Also apply the same fix to PasswordField.tsx (lines 27-31) which has the identical sr-only pattern.
|
||||
|
||||
Run full test suite after to confirm no regressions.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>npx vitest run src/components/ui/FieldRenderer.test.tsx --reporter=dot && npx vitest run --reporter=dot</automated>
|
||||
</verify>
|
||||
<done>Both text-branch and select-branch tooltip buttons use aria-label attribute consistently. PasswordField tooltip also fixed. All 166+ existing tests still pass. DEBT-01 resolved.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
- `npx vitest run --reporter=dot` — all existing tests pass (no regressions)
|
||||
- `npx vitest run src/components/ui/TextFieldMD3.test.tsx` — new component tests pass
|
||||
- `npx vitest run src/components/ui/FieldRenderer.test.tsx` — DEBT-01 tests pass
|
||||
- `src/styles/md3-buttons.ts` exports MD3_BTN_FILLED, MD3_BTN_OUTLINED, MD3_BTN_TEXT
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- TextFieldMD3 component exists with floating label, proper htmlFor/id, and all tests passing
|
||||
- MD3 button constants exported from src/styles/md3-buttons.ts
|
||||
- FieldRenderer and PasswordField tooltip buttons use consistent aria-label (DEBT-01 complete)
|
||||
- Zero test regressions across the full suite
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/09-md3-components/09-01-SUMMARY.md`
|
||||
</output>
|
||||
@@ -0,0 +1,157 @@
|
||||
---
|
||||
phase: 09-md3-components
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- src/components/wizard/StepIndicator.tsx
|
||||
- src/components/wizard/StepIndicator.test.tsx
|
||||
autonomous: true
|
||||
requirements: [COMP-04]
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "Step indicator shows numbered circles for each wizard step"
|
||||
- "Completed steps display a checkmark and are clickable buttons"
|
||||
- "Current step is visually highlighted with primary color"
|
||||
- "Future steps appear muted"
|
||||
- "Connector lines link steps — filled for completed, muted for future"
|
||||
- "Clicking step 0 dispatches SET_REMOTE_PARAMS({}) then SET_STEP(0)"
|
||||
artifacts:
|
||||
- path: "src/components/wizard/StepIndicator.tsx"
|
||||
provides: "Rebuilt MD3 step indicator with circles and connectors"
|
||||
exports: ["StepIndicator"]
|
||||
min_lines: 40
|
||||
key_links:
|
||||
- from: "src/components/wizard/StepIndicator.tsx"
|
||||
to: "src/store/context.tsx"
|
||||
via: "useWizard() hook for state.currentStep and dispatch"
|
||||
pattern: "useWizard"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Rebuild StepIndicator from text breadcrumbs to MD3 visual step indicator with numbered circles, connector lines, checkmarks on completed steps, and proper semantic tokens.
|
||||
|
||||
Purpose: Replace the current text-based breadcrumb (using inline styles and hardcoded colors) with an MD3-compliant visual stepper that is dark-mode aware and accessible.
|
||||
Output: Rebuilt StepIndicator.tsx + updated tests.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/09-md3-components/09-RESEARCH.md
|
||||
|
||||
@src/components/wizard/StepIndicator.tsx
|
||||
@src/components/wizard/StepIndicator.test.tsx
|
||||
|
||||
<interfaces>
|
||||
From src/store/context.tsx:
|
||||
```typescript
|
||||
function useWizard(): { state: WizardState; dispatch: Dispatch<WizardAction> }
|
||||
```
|
||||
|
||||
From src/store/types.ts:
|
||||
```typescript
|
||||
type WizardAction =
|
||||
| { type: 'SET_STEP'; payload: number }
|
||||
| { type: 'SET_REMOTE_PARAMS'; payload: Record<string, unknown> }
|
||||
| ...
|
||||
```
|
||||
|
||||
Current StepIndicator test selectors (MUST remain compatible):
|
||||
- `screen.getByText(/Backend/)` — finds step label
|
||||
- `screen.getAllByRole('button')` — finds completed step buttons
|
||||
- `buttons.find(b => b.textContent?.includes('Backend'))` — locates specific step button
|
||||
- `screen.findByText(/Deployment/)` — finds step label async
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Rebuild StepIndicator with MD3 circles and connectors</name>
|
||||
<files>src/components/wizard/StepIndicator.tsx, src/components/wizard/StepIndicator.test.tsx</files>
|
||||
<action>
|
||||
Rebuild `src/components/wizard/StepIndicator.tsx` from the text breadcrumb to a visual MD3 step indicator. Keep the existing `handleStepClick` logic EXACTLY as-is (SET_REMOTE_PARAMS({}) before SET_STEP when going to step 0). Remove ALL inline `style={{}}` props.
|
||||
|
||||
Structure (from research Pattern 4):
|
||||
- Wrap in `<nav aria-label="Wizard steps"><ol className="flex items-center w-full">...</ol></nav>`
|
||||
- Each step is an `<li>` containing a circle + label + optional connector
|
||||
- Completed steps: `<button>` with checkmark icon, `bg-primary text-on-primary`, `aria-label="Go to step N: Label"`
|
||||
- Active step: `<span>` with number, `border-2 border-primary bg-primary/10 text-primary font-bold`
|
||||
- Future steps: `<span>` with number, `border-2 border-outline text-on-surface-container/40`
|
||||
- All circles: `w-8 h-8 rounded-full flex items-center justify-center text-sm`
|
||||
- Labels below circles: `text-xs mt-1 text-center`
|
||||
- Connector lines between steps: `flex-1 h-0.5 mx-2`, `bg-primary` if completed else `bg-outline`
|
||||
- Connector is `aria-hidden="true"`
|
||||
|
||||
CRITICAL for test compatibility: Completed step buttons MUST include the label text in their content so `b.textContent?.includes('Backend')` still works. Structure the button as:
|
||||
```tsx
|
||||
<button ...>
|
||||
<span aria-hidden="true">✓</span>
|
||||
<span className="sr-only">{label}</span>
|
||||
</button>
|
||||
```
|
||||
AND render the label as a visible `<span>` adjacent to the button (inside the same flex container). The test checks `b.textContent?.includes('Backend')` on the button element itself, so the label text must be INSIDE the button. Use:
|
||||
```tsx
|
||||
<button ... aria-label={`Go to step ${i + 1}: ${label}`}>
|
||||
✓ {label}
|
||||
</button>
|
||||
```
|
||||
This way `textContent` includes "Backend" and `aria-label` provides accessible name. The visual label below the circle can be separate.
|
||||
|
||||
Actually, looking at the test more carefully: the label text appears as a visible `<span>` below the circle in the new design. But the button `textContent` needs to include the label for the test. Solution: render the label text INSIDE the button element so textContent includes it, and use CSS to visually position it below the circle icon.
|
||||
|
||||
Simplest approach that preserves tests: make the completed step button contain BOTH the checkmark and the label text:
|
||||
```tsx
|
||||
<button type="button" onClick={() => handleStepClick(i)}
|
||||
className="flex flex-col items-center gap-1 shrink-0 group focus-visible:outline-none">
|
||||
<span className="w-8 h-8 rounded-full bg-primary text-on-primary flex items-center justify-center text-sm font-medium group-focus-visible:ring-2 group-focus-visible:ring-primary/50">
|
||||
✓
|
||||
</span>
|
||||
<span className="text-xs text-primary">{label}</span>
|
||||
</button>
|
||||
```
|
||||
This way `button.textContent` is `"✓Backend"` which includes "Backend".
|
||||
|
||||
For active and future steps (non-clickable), use a `<div>` wrapper with the circle `<span>` and label `<span>` inside.
|
||||
|
||||
Update `StepIndicator.test.tsx` only if needed. The existing 5 tests should pass with this structure since:
|
||||
- `screen.getByText(/Backend/)` will match the label span
|
||||
- `screen.getAllByRole('button')` finds completed step buttons
|
||||
- `buttons.find(b => b.textContent?.includes('Backend'))` works because label is inside button
|
||||
- Dispatch behavior is unchanged
|
||||
|
||||
Run the full test suite after rebuild to catch any regressions.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>npx vitest run src/components/wizard/StepIndicator.test.tsx --reporter=dot && npx vitest run --reporter=dot</automated>
|
||||
</verify>
|
||||
<done>StepIndicator renders numbered circles connected by lines, completed steps show checkmarks and are clickable, current step is highlighted, future steps are muted. All inline styles removed. All 5 existing WIZD-03 tests pass. No regressions in full suite.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
- `npx vitest run src/components/wizard/StepIndicator.test.tsx` — all 5 WIZD-03 tests pass
|
||||
- `npx vitest run --reporter=dot` — full suite green
|
||||
- No inline `style={{}}` props remain in StepIndicator.tsx
|
||||
- No hardcoded color values (`#999`, etc.) remain
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- StepIndicator uses MD3 circles with numbered/checkmark states
|
||||
- Connector lines between steps show progress visually
|
||||
- All semantic tokens used (no hardcoded colors)
|
||||
- All existing tests pass without modification (or with minimal test selector updates if button textContent changed)
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/09-md3-components/09-02-SUMMARY.md`
|
||||
</output>
|
||||
@@ -0,0 +1,272 @@
|
||||
---
|
||||
phase: 09-md3-components
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: ["09-01"]
|
||||
files_modified:
|
||||
- src/components/ui/FieldRenderer.tsx
|
||||
- src/components/ui/PasswordField.tsx
|
||||
- src/components/ui/BackendCard.tsx
|
||||
- src/components/wizard/OutputBlock.tsx
|
||||
- src/components/wizard/BackendSelectionStep.tsx
|
||||
- src/components/wizard/RemoteConfigStep.tsx
|
||||
- src/components/wizard/DeploymentStep.tsx
|
||||
- src/components/wizard/ReviewStep.tsx
|
||||
- src/index.css
|
||||
autonomous: false
|
||||
requirements: [COMP-01, COMP-02, COMP-03]
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "All text inputs in the wizard render with floating labels that animate on focus and when the field has content"
|
||||
- "Next and Download All buttons use MD3 filled style (rounded-full, bg-primary)"
|
||||
- "Back, Copy, and Download buttons use MD3 outlined style (rounded-full, border)"
|
||||
- "BackendCard uses rounded-xl shape and shadow elevation"
|
||||
- "OutputBlock pre blocks use rounded-xl and subtle shadow"
|
||||
- "All existing tests pass with no regressions"
|
||||
artifacts:
|
||||
- path: "src/components/ui/FieldRenderer.tsx"
|
||||
provides: "Text-branch using TextFieldMD3 component"
|
||||
contains: "TextFieldMD3"
|
||||
- path: "src/components/ui/PasswordField.tsx"
|
||||
provides: "Password field using TextFieldMD3 layout"
|
||||
contains: "TextFieldMD3"
|
||||
- path: "src/components/ui/BackendCard.tsx"
|
||||
provides: "MD3 elevation and shape"
|
||||
contains: "rounded-xl"
|
||||
- path: "src/components/wizard/BackendSelectionStep.tsx"
|
||||
provides: "MD3 filled button for Next"
|
||||
contains: "MD3_BTN_FILLED"
|
||||
key_links:
|
||||
- from: "src/components/ui/FieldRenderer.tsx"
|
||||
to: "src/components/ui/TextFieldMD3.tsx"
|
||||
via: "import and render TextFieldMD3"
|
||||
pattern: "import.*TextFieldMD3"
|
||||
- from: "src/components/wizard/BackendSelectionStep.tsx"
|
||||
to: "src/styles/md3-buttons.ts"
|
||||
via: "import button constants"
|
||||
pattern: "import.*MD3_BTN"
|
||||
- from: "src/components/wizard/ReviewStep.tsx"
|
||||
to: "src/styles/md3-buttons.ts"
|
||||
via: "import button constants"
|
||||
pattern: "import.*MD3_BTN"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Wire TextFieldMD3 into FieldRenderer and PasswordField, apply MD3 button styles across all wizard steps, and add MD3 elevation to BackendCard and OutputBlock.
|
||||
|
||||
Purpose: This is the integration plan that connects the primitives from Plan 01 into the actual wizard UI, completing COMP-01 (floating labels everywhere), COMP-02 (button hierarchy everywhere), and COMP-03 (elevation on cards and code blocks).
|
||||
Output: All wizard components updated with MD3 styling.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/09-md3-components/09-RESEARCH.md
|
||||
@.planning/phases/09-md3-components/09-01-SUMMARY.md
|
||||
|
||||
@src/components/ui/FieldRenderer.tsx
|
||||
@src/components/ui/PasswordField.tsx
|
||||
@src/components/ui/BackendCard.tsx
|
||||
@src/components/wizard/OutputBlock.tsx
|
||||
@src/components/wizard/BackendSelectionStep.tsx
|
||||
@src/components/wizard/RemoteConfigStep.tsx
|
||||
@src/components/wizard/DeploymentStep.tsx
|
||||
@src/components/wizard/ReviewStep.tsx
|
||||
|
||||
<interfaces>
|
||||
<!-- TextFieldMD3 and button constants created in Plan 01 -->
|
||||
|
||||
From src/components/ui/TextFieldMD3.tsx (created in 09-01):
|
||||
```typescript
|
||||
interface TextFieldMD3Props {
|
||||
id: string;
|
||||
label: string;
|
||||
error?: FieldError;
|
||||
registration: UseFormRegisterReturn;
|
||||
type?: 'text' | 'password';
|
||||
helpText?: string;
|
||||
required?: boolean;
|
||||
suffix?: React.ReactNode;
|
||||
}
|
||||
export function TextFieldMD3(props: TextFieldMD3Props): JSX.Element;
|
||||
```
|
||||
|
||||
From src/styles/md3-buttons.ts (created in 09-01):
|
||||
```typescript
|
||||
export const MD3_BTN_FILLED: string; // filled primary action
|
||||
export const MD3_BTN_OUTLINED: string; // outlined secondary action
|
||||
export const MD3_BTN_TEXT: string; // text tertiary action
|
||||
```
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Integrate TextFieldMD3 into FieldRenderer and PasswordField</name>
|
||||
<files>src/components/ui/FieldRenderer.tsx, src/components/ui/PasswordField.tsx</files>
|
||||
<action>
|
||||
**FieldRenderer.tsx — text branch (default, lines 82-119):**
|
||||
Replace the entire text-branch return block with a `<TextFieldMD3>` render. Import TextFieldMD3 at top. The text branch currently renders label + input + helpText + error manually. Replace with:
|
||||
```tsx
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
{field.tooltipText && /* keep tooltip button + tooltip display exactly as-is */}
|
||||
<TextFieldMD3
|
||||
id={field.key}
|
||||
label={field.label}
|
||||
error={error}
|
||||
registration={register(field.key)}
|
||||
helpText={field.helpText}
|
||||
required={field.required}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
```
|
||||
CRITICAL: The tooltip button (lines 88-99) must remain OUTSIDE TextFieldMD3, rendered above it. TextFieldMD3 handles the label, input, helpText, and error display. The tooltip is a separate concern that stays in FieldRenderer.
|
||||
|
||||
Actually, re-examining the layout: the current text branch has tooltip button inline with label. Since TextFieldMD3 includes its own label (floating), the tooltip must be rendered separately. Place the tooltip button + tooltip content ABOVE the TextFieldMD3 component in the flex column. This preserves existing tooltip behavior.
|
||||
|
||||
Keep the select branch (lines 40-79) UNCHANGED — select fields do NOT get floating labels.
|
||||
|
||||
**PasswordField.tsx:**
|
||||
Replace the manual label + input layout with TextFieldMD3, passing the show/hide toggle as `suffix`:
|
||||
```tsx
|
||||
import { TextFieldMD3 } from './TextFieldMD3';
|
||||
|
||||
export function PasswordField({ id, label, error, registration, helpText, tooltipText }: PasswordFieldProps) {
|
||||
const [show, setShow] = useState(false);
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
|
||||
const toggleButton = (
|
||||
<button type="button" onClick={() => setShow(v => !v)}
|
||||
aria-label={show ? 'Hide' : 'Show'}
|
||||
className="text-on-surface-container/50 hover:text-on-surface-container text-sm">
|
||||
{show ? 'Hide' : 'Show'}
|
||||
</button>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
{tooltipText && (
|
||||
<button type="button" onClick={() => setShowTooltip(v => !v)}
|
||||
aria-label={`More info about ${label}`}
|
||||
className="text-primary hover:text-primary text-xs leading-none self-start">
|
||||
ⓘ
|
||||
</button>
|
||||
)}
|
||||
{tooltipText && showTooltip && (
|
||||
<p className="text-xs text-primary bg-primary/10 border border-primary/30 rounded px-2 py-1.5">
|
||||
{tooltipText}
|
||||
</p>
|
||||
)}
|
||||
<TextFieldMD3
|
||||
id={id}
|
||||
label={label}
|
||||
error={error}
|
||||
registration={registration}
|
||||
type={show ? 'text' : 'password'}
|
||||
helpText={helpText}
|
||||
suffix={toggleButton}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Remove the `placeholder` prop from PasswordFieldProps interface since TextFieldMD3 uses `placeholder=" "` internally.
|
||||
|
||||
Run full test suite after — ALL `getByLabelText` queries must still work because TextFieldMD3 preserves htmlFor/id pairing.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>npx vitest run --reporter=dot</automated>
|
||||
</verify>
|
||||
<done>FieldRenderer text-branch renders TextFieldMD3 with floating label. PasswordField uses TextFieldMD3 with suffix for show/hide toggle. All existing tests pass (getByLabelText queries work). No regressions.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Apply MD3 button styles and elevation across all wizard steps</name>
|
||||
<files>src/components/wizard/BackendSelectionStep.tsx, src/components/wizard/RemoteConfigStep.tsx, src/components/wizard/DeploymentStep.tsx, src/components/wizard/ReviewStep.tsx, src/components/wizard/OutputBlock.tsx, src/components/ui/BackendCard.tsx, src/index.css</files>
|
||||
<action>
|
||||
**Button styling (COMP-02):** Import `{ MD3_BTN_FILLED, MD3_BTN_OUTLINED }` from `../../styles/md3-buttons` in each wizard step file. Apply className replacements:
|
||||
|
||||
BackendSelectionStep.tsx: "Next" button gets `className={MD3_BTN_FILLED}`. No Back button here.
|
||||
RemoteConfigStep.tsx: "Back" button gets `className={MD3_BTN_OUTLINED}`, "Next" (submit) button gets `className={MD3_BTN_FILLED}`.
|
||||
DeploymentStep.tsx: "Back" button gets `className={MD3_BTN_OUTLINED}`, "Next" button gets `className={MD3_BTN_FILLED}`.
|
||||
ReviewStep.tsx: "Back" button gets `className={MD3_BTN_OUTLINED}`, "Download All (ZIP)" button gets `className={MD3_BTN_FILLED}`.
|
||||
OutputBlock.tsx: "Copy" button gets `className={MD3_BTN_OUTLINED + ' text-xs !px-3 !py-1'}` (keep smaller size). "Download" button gets same. Import from `../../styles/md3-buttons`. Note: OutputBlock buttons are small utility buttons — keep `text-xs` and reduce padding with overrides. Alternatively, create a size-reduced variant inline: `className={\`\${MD3_BTN_OUTLINED} !text-xs !px-3 !py-1.5\`}`.
|
||||
|
||||
CRITICAL: Do NOT change button text content — only className. Tests use `getByRole('button', { name: /next/i })` etc.
|
||||
|
||||
**BackendCard elevation (COMP-03):**
|
||||
Update BackendCard.tsx className:
|
||||
- Change `rounded-lg` to `rounded-xl` (MD3 medium shape, 12px)
|
||||
- Add `shadow hover:shadow-md` to unselected state
|
||||
- Selected state: add `shadow-md`
|
||||
- Add `transition-all` (already present — keep)
|
||||
|
||||
**OutputBlock elevation (COMP-03):**
|
||||
Update OutputBlock.tsx `<pre>` className:
|
||||
- Change `rounded` to `rounded-xl`
|
||||
- Add `shadow-sm`
|
||||
|
||||
**Optional: Add elevation shadow token to index.css:**
|
||||
If standard Tailwind `shadow` is insufficient, add to `@theme` block:
|
||||
```css
|
||||
--shadow-elevation-1: 0 1px 4px 0 rgb(0 0 0 / 0.37);
|
||||
```
|
||||
Only add this if the default `shadow` utility doesn't provide enough visual lift. Use your judgment.
|
||||
|
||||
Run full test suite after all changes.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>npx vitest run --reporter=dot</automated>
|
||||
</verify>
|
||||
<done>All Next/Download buttons use MD3 filled (rounded-full, bg-primary). All Back/Copy buttons use MD3 outlined (rounded-full, border). BackendCard has rounded-xl and shadow. OutputBlock pre has rounded-xl and shadow-sm. All tests pass.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<name>Task 3: Visual verification of complete MD3 component set</name>
|
||||
<what-built>Complete MD3 component migration: floating label text fields, MD3 button hierarchy (filled/outlined), card elevation, and rebuilt step indicator</what-built>
|
||||
<how-to-verify>
|
||||
1. Run `npm run dev` and open http://localhost:5173
|
||||
2. **Step Indicator:** Verify numbered circles with connector lines at top. Step 1 should be highlighted, future steps muted.
|
||||
3. **Backend Selection:** Click a backend card — verify rounded corners (rounded-xl) and shadow elevation. Verify "Next" button is pill-shaped (rounded-full) with primary fill color.
|
||||
4. **Remote Config:** Verify text inputs have floating labels that animate up on focus and stay floated when field has value. Verify "Back" button is pill-shaped outlined, "Next" is pill-shaped filled.
|
||||
5. **Password fields:** Verify floating label works with show/hide toggle button visible on right side.
|
||||
6. **Deployment Step:** Verify Back/Next button styles match MD3 hierarchy.
|
||||
7. **Review Step:** Verify "Back" is outlined, "Download All" is filled. Verify OutputBlock code areas have rounded corners and subtle shadow. Verify Copy/Download buttons are outlined style.
|
||||
8. **Navigate back** to step 1 — verify step indicator shows checkmark on completed step, and the step is clickable.
|
||||
9. **Toggle dark mode** — verify all new components look correct in dark theme (no hardcoded colors, shadows work).
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" or describe issues</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
- `npx vitest run` — full suite passes (166+ tests, zero regressions)
|
||||
- All text inputs render floating labels
|
||||
- Buttons follow MD3 filled/outlined/text hierarchy
|
||||
- Cards and code blocks have MD3 elevation
|
||||
- Dark mode renders correctly
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- COMP-01: All text inputs (FieldRenderer text-branch + PasswordField) render as MD3 outlined fields with floating labels
|
||||
- COMP-02: Buttons across all wizard steps follow filled/outlined/text hierarchy
|
||||
- COMP-03: BackendCard has rounded-xl + shadow, OutputBlock pre has rounded-xl + shadow-sm
|
||||
- Zero test regressions
|
||||
- User visually approves the complete MD3 component set
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/09-md3-components/09-03-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user