Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7.2 KiB
7.2 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 09-md3-components | 02 | execute | 1 |
|
true |
|
|
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.
<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>
@.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
From src/store/context.tsx: ```typescript function useWizard(): { state: WizardState; dispatch: Dispatch } ```From src/store/types.ts:
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 labelscreen.getAllByRole('button')— finds completed step buttonsbuttons.find(b => b.textContent?.includes('Backend'))— locates specific step buttonscreen.findByText(/Deployment/)— finds step label async
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.
<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>