@@ -0,0 +1,135 @@
|
||||
# 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:
|
||||
|
||||
```tsx
|
||||
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/`:
|
||||
|
||||
```bash
|
||||
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.
|
||||
Reference in New Issue
Block a user