Phase 2: Build 9 presentational components with full test coverage

New components (all with .tsx + .test.tsx, 90%+ coverage):
- CalendarDay: day cell with event badges
- CalendarEvent: colored event chip with dismiss option
- DaysBreakdown: table of days by type
- ProgressChart: progress bar toward age-4 deadline
- TipCard: optimization tip card with severity badges
- VacationDaysInput: number input with min/max validation
- ParentProfileInput: dual selects for salary type & employment status
- PlanningGoalSelector: radio buttons for planning goal
- BreakPeriodsInput: list manager with inline add/edit form

All components follow existing patterns from Header/Footer:
- Typed props interfaces (exported)
- Semantic HTML with data-testid & ARIA attributes
- No business logic, purely presentational
- Comprehensive test coverage including edge cases

npm test: 22 files, 167 tests passing
npx tsc --noEmit: clean

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 17:50:23 +02:00
parent faebcced8a
commit 425e29ea21
19 changed files with 2048 additions and 0 deletions
@@ -0,0 +1,55 @@
import { ReactNode } from 'react'
export interface PlanningGoalSelectorProps {
value?: string
onChange: (goal: string) => void
}
export function PlanningGoalSelector({ value, onChange }: PlanningGoalSelectorProps): ReactNode {
return (
<div data-testid="planning-goal-selector" className="planning-goal-selector">
<fieldset className="planning-goal-selector__fieldset">
<legend className="planning-goal-selector__legend">Planning Goal</legend>
<div className="planning-goal-selector__option">
<input
type="radio"
id="goal-maximize-contiguous"
name="planning-goal"
value="maximize_contiguous"
checked={value === 'maximize_contiguous'}
onChange={(e) => onChange(e.target.value)}
data-testid="goal-maximize-contiguous"
/>
<label htmlFor="goal-maximize-contiguous">Maximize Contiguous</label>
</div>
<div className="planning-goal-selector__option">
<input
type="radio"
id="goal-minimize-stress"
name="planning-goal"
value="minimize_stress"
checked={value === 'minimize_stress'}
onChange={(e) => onChange(e.target.value)}
data-testid="goal-minimize-stress"
/>
<label htmlFor="goal-minimize-stress">Minimize Stress</label>
</div>
<div className="planning-goal-selector__option">
<input
type="radio"
id="goal-balance"
name="planning-goal"
value="balance"
checked={value === 'balance'}
onChange={(e) => onChange(e.target.value)}
data-testid="goal-balance"
/>
<label htmlFor="goal-balance">Balance</label>
</div>
</fieldset>
</div>
)
}