5.0 KiB
ChicoryTV SPA Component Primitives Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Implement issue #80 by recreating the design-system primitive components as typed React components in the SPA and adding a gallery view for visual review across all three themes.
Architecture: Components live under web/src/components/ and are grouped by domain with a barrel export. Visual styling lives in one shared CSS file using existing ChicoryTV design tokens imported by web/src/designSystem.ts; gallery composition lives in App.tsx and exercises every primitive.
Tech Stack: React 19, TypeScript, Vite, Vitest, Testing Library, lucide-react, existing design-system/styles.css tokens.
Task 1: Baseline and Failing Component Tests
Files:
-
Modify:
web/src/App.test.tsx -
Step 1: Run baseline tests
Run: npm test -- --run from web/.
Expected: existing SPA scaffold tests pass before new work.
- Step 2: Write failing tests
Add tests that import the upcoming component barrel, render the gallery, and verify key API behavior:
import { Button, Checkbox, Input, ProgressBar, Switch, Tabs, Tooltip } from './components';
it('exports typed primitives with expected interactions', () => {
const onSwitch = vi.fn();
const onCheckbox = vi.fn();
const onTab = vi.fn();
render(
<>
<Button variant="primary" loading>Saving</Button>
<Switch checked={false} onChange={onSwitch} label="Show disabled" />
<Checkbox indeterminate onChange={onCheckbox} label="Select all" />
<Input label="Channel number" value="5.1" error="Already in use" onChange={() => {}} />
<ProgressBar value={62} showLabel />
<Tabs value="streaming" onChange={onTab} tabs={[{ value: 'streaming', label: 'Streaming' }]} />
<Tooltip label="Reset playout"><button type="button">Reset</button></Tooltip>
</>
);
expect(screen.getByRole('button', { name: 'Saving' })).toBeDisabled();
fireEvent.click(screen.getByRole('switch', { name: 'Show disabled' }));
expect(onSwitch).toHaveBeenCalledWith(true);
expect(screen.getByRole('checkbox', { name: 'Select all' })).toHaveAttribute('aria-checked', 'mixed');
expect(screen.getByText('Already in use')).toBeInTheDocument();
expect(screen.getByText('62%')).toBeInTheDocument();
fireEvent.click(screen.getByRole('tab', { name: 'Streaming' }));
expect(onTab).toHaveBeenCalledWith('streaming');
fireEvent.mouseEnter(screen.getByText('Reset'));
expect(screen.getByRole('tooltip')).toHaveTextContent('Reset playout');
});
- Step 3: Verify red
Run: npm test -- --run App.test.tsx from web/.
Expected: failure because ./components does not exist yet.
Task 2: Implement Component Library
Files:
-
Create:
web/src/components/index.ts -
Create:
web/src/components/components.css -
Create:
web/src/components/forms.tsx -
Create:
web/src/components/dataDisplay.tsx -
Create:
web/src/components/feedback.tsx -
Create:
web/src/components/navigation.tsx -
Step 1: Implement typed exports
Implement these exports with props matching design-system/components/**/*.d.ts: Button, IconButton, Input, Select, Switch, Checkbox, Badge, StatusDot, Tag, ChannelLogo, Card, Stat, ProgressBar, Toast, Tooltip, Spinner, NavItem, NavSection, Tabs.
- Step 2: Implement token-driven CSS
Use components.css classes for variants, sizes, focus, hover, disabled, active, live pulse, indeterminate progress, and theme-aware colors through --ctv-* and semantic aliases.
- Step 3: Verify green
Run: npm test -- --run App.test.tsx from web/.
Expected: new component behavior tests pass.
Task 3: Add Component Gallery
Files:
-
Modify:
web/src/App.tsx -
Modify:
web/src/shell.css -
Modify:
web/src/App.test.tsx -
Step 1: Add failing gallery assertions
Assert the SPA renders "Component gallery", "Forms", "Data display", "Feedback", and "Navigation", and includes one visible sample of each primitive family.
- Step 2: Verify red
Run: npm test -- --run App.test.tsx from web/.
Expected: failure because the gallery is not implemented.
- Step 3: Implement gallery
Compose the new primitives in App.tsx, matching the specimen cards' sample content and retaining the three-theme switcher.
- Step 4: Verify green
Run: npm test -- --run App.test.tsx from web/.
Expected: App tests pass.
Task 4: Final Verification and Issue Completion
Files:
-
Inspect all changed files with
git diff. -
Step 1: Run verification
Run from web/:
npm test -- --run
npm run typecheck
npm run build
- Step 2: Complete done workflow
Use the done skill for issue #80: read the issue, verify acceptance criteria, check docs impact, run git status, commit and push if files changed, comment on Gitea, and close the issue if complete.