|
| 1 | +--- |
| 2 | +name: storybook-story-generation |
| 3 | +description: Use after React components are built (Phase 4.5 of the Figma/Canva/screenshot pipeline) or whenever a project needs Storybook coverage — auto-generates .stories.tsx and .mdx docs from components via ts-morph AST parsing, with prop controls, variant stories, action args, and default args. Keywords: Storybook, generate stories, .stories.tsx, MDX docs, argTypes, controls, autodocs, CSF3, variant stories, action args, component documentation, story generation, ts-morph |
| 4 | +--- |
| 5 | + |
| 6 | +# Storybook Story Generation — AST-Based Stories + MDX Docs |
| 7 | + |
| 8 | +## Purpose |
| 9 | + |
| 10 | +Auto-generate Storybook 8 (CSF3) stories and MDX documentation from built React |
| 11 | +components. The generator parses each component's TypeScript source with a |
| 12 | +`ts-morph` AST — not regex — so it can reliably extract prop types, string-literal |
| 13 | +unions, default values, callback props, and JSDoc comments, then emit: |
| 14 | + |
| 15 | +- **`Component.stories.tsx`** — `Meta` with `tags: ['autodocs']`, prop-aware |
| 16 | + `argTypes`/controls, a `Default` story seeded with destructured defaults, one |
| 17 | + variant story per union value, `True`/`False` stories for booleans, action args |
| 18 | + for callbacks, and optional responsive-viewport stories. |
| 19 | +- **`Component.mdx`** — an autodocs page with `Meta`, `Canvas`, `Controls`, and |
| 20 | + `ArgTypes` blocks, plus the component's JSDoc description and a `Canvas` per |
| 21 | + variant. |
| 22 | + |
| 23 | +The skill wraps `scripts/generate-stories.sh` (a thin wrapper over |
| 24 | +`scripts/generate-stories.js`). It is a **non-blocking** step: a failure never |
| 25 | +fails the build. |
| 26 | + |
| 27 | +## When to Use |
| 28 | + |
| 29 | +- **Phase 4.5** of `/build-from-figma`, `/build-from-canva`, and |
| 30 | + `/build-from-screenshot` — immediately after components are built and unit tests |
| 31 | + pass, before/alongside visual QA (runs in parallel, non-blocking). |
| 32 | +- Backfilling Storybook coverage for an existing component library. |
| 33 | +- When the user asks to "generate Storybook stories", "add stories for these |
| 34 | + components", "create autodocs", or "document components in Storybook". |
| 35 | + |
| 36 | +**Trigger phrases:** |
| 37 | +- "Generate Storybook stories" |
| 38 | +- "Auto-generate stories for my components" |
| 39 | +- "Add MDX docs to Storybook" |
| 40 | +- "Create stories with controls and variants" |
| 41 | + |
| 42 | +**Do NOT use for:** hand-authored interaction/`play()` tests (those belong to the |
| 43 | +`react-testing-workflows` skill), or non-React output targets. |
| 44 | + |
| 45 | +## Inputs |
| 46 | + |
| 47 | +- **Required:** Built components under `src/components/**/*.tsx` (override with |
| 48 | + `--src-dir`). Components must be **exported** and named with an uppercase first |
| 49 | + letter (function, arrow-const, or default export). |
| 50 | +- **Recommended:** A props `interface ComponentNameProps` — the generator keys off |
| 51 | + this naming convention to build controls and variants. |
| 52 | +- **Optional:** `.claude/pipeline.config.json` → `storybook` (thresholds and |
| 53 | + toggles; see [Configuration](#configuration)). |
| 54 | +- **Optional:** `tsconfig.json` — used by `ts-morph` when present; otherwise a |
| 55 | + sensible in-memory compiler config is used. |
| 56 | + |
| 57 | +## Process |
| 58 | + |
| 59 | +### Step 1: Read configuration |
| 60 | + |
| 61 | +Load `.claude/pipeline.config.json` → `storybook` (falls back to defaults if the |
| 62 | +file is missing or `autoGenerate` is unset). Relevant keys: `skipPatterns`, |
| 63 | +`generateMdx`, `maxVariantsPerProp`, `includeResponsiveViewports`, `viewports` |
| 64 | +(widths resolved from `visualDiff.breakpoints`). |
| 65 | + |
| 66 | +### Step 2: Scan for components |
| 67 | + |
| 68 | +Recursively walk `src/components`, collecting `.tsx` files while excluding |
| 69 | +`*.test.tsx`, `*.stories.tsx`, `*.d.ts`, `index.tsx`, and the `node_modules`, |
| 70 | +`__tests__`, and `__mocks__` directories. Files matching a `skipPatterns` glob are |
| 71 | +skipped. Components that already have a `.stories.tsx` sibling are skipped unless |
| 72 | +`--force` is passed. |
| 73 | + |
| 74 | +### Step 3: Extract prop metadata (AST) |
| 75 | + |
| 76 | +For each component the generator: |
| 77 | +1. Finds the first exported uppercase component (function decl, exported |
| 78 | + `const`, or default export). |
| 79 | +2. Locates its `ComponentNameProps` interface. |
| 80 | +3. For each prop, records: name, optionality, control type, union options, |
| 81 | + JSDoc description, and whether it is a callback. |
| 82 | +4. Reads default values from the parameter destructuring pattern |
| 83 | + (`({ size = 'md' })` → `size: 'md'`). |
| 84 | + |
| 85 | +**Control mapping:** |
| 86 | + |
| 87 | +| Prop type | Control | Notes | |
| 88 | +|-----------|---------|-------| |
| 89 | +| `string` | `text` | | |
| 90 | +| `number` | `number` | | |
| 91 | +| `boolean` | `boolean` | also emits `True`/`False` variant stories | |
| 92 | +| `'a' \| 'b' \| 'c'` (string-literal union) | `select` | `options` populated; one variant story per value | |
| 93 | +| `on*` function (e.g. `onClick`) | action arg | `{ action: 'clicked' }` (past-tense of the event) | |
| 94 | +| other / complex | `text` | falls back to `text`; adjust manually if needed | |
| 95 | + |
| 96 | +### Step 4: Generate `.stories.tsx` |
| 97 | + |
| 98 | +Emit CSF3 with `title: 'Components/<path-relative-to-src>'`, `tags: ['autodocs']`, |
| 99 | +`argTypes`, a `Default` story (seeded with destructured defaults), variant stories |
| 100 | +(capped at `maxVariantsPerProp` per prop), and viewport stories when |
| 101 | +`includeResponsiveViewports` is enabled. |
| 102 | + |
| 103 | +```tsx |
| 104 | +import type { Meta, StoryObj } from '@storybook/react'; |
| 105 | +import { Badge } from './Badge'; |
| 106 | + |
| 107 | +const meta: Meta<typeof Badge> = { |
| 108 | + title: 'Components/ui/Badge', |
| 109 | + component: Badge, |
| 110 | + tags: ['autodocs'], |
| 111 | + argTypes: { |
| 112 | + variant: { control: 'select', options: ['info', 'success', 'warning', 'error'] }, |
| 113 | + onDismiss: { action: 'dismissed' }, |
| 114 | + }, |
| 115 | +}; |
| 116 | +export default meta; |
| 117 | +type Story = StoryObj<typeof Badge>; |
| 118 | + |
| 119 | +export const Default: Story = { args: { variant: 'info' } }; |
| 120 | +export const Info: Story = { args: { variant: 'info' } }; |
| 121 | +export const Success: Story = { args: { variant: 'success' } }; |
| 122 | +// …one per union value |
| 123 | +``` |
| 124 | + |
| 125 | +### Step 5: Generate `.mdx` (unless `--no-mdx` or `generateMdx: false`) |
| 126 | + |
| 127 | +Emit an autodocs page importing `@storybook/blocks`, referencing the sibling |
| 128 | +`.stories`, and embedding the component's JSDoc description, the `Default` |
| 129 | +`Canvas`, `Controls`, a `Canvas` per variant, and the full `ArgTypes` table. |
| 130 | + |
| 131 | +```mdx |
| 132 | +import { Meta, Canvas, Controls, ArgTypes } from '@storybook/blocks'; |
| 133 | +import * as Stories from './Badge.stories'; |
| 134 | + |
| 135 | +<Meta of={Stories} /> |
| 136 | + |
| 137 | +# Badge |
| 138 | + |
| 139 | +A status badge with semantic color variants. |
| 140 | + |
| 141 | +<Canvas of={Stories.Default} /> |
| 142 | +<Controls of={Stories.Default} /> |
| 143 | +<Canvas of={Stories.Info} /> |
| 144 | +<ArgTypes of={Stories} /> |
| 145 | +``` |
| 146 | + |
| 147 | +### Step 6: Run and report |
| 148 | + |
| 149 | +```bash |
| 150 | +./scripts/generate-stories.sh # generate stories + MDX for all components |
| 151 | +./scripts/generate-stories.sh --dry-run # report what would be written, write nothing |
| 152 | +./scripts/generate-stories.sh --force # overwrite existing stories |
| 153 | +./scripts/generate-stories.sh --no-mdx # stories only, skip MDX |
| 154 | +./scripts/generate-stories.sh --json # machine-readable { generated, skipped, files } |
| 155 | +``` |
| 156 | + |
| 157 | +Because this step is **non-blocking**, log the generated/skipped counts and |
| 158 | +continue the pipeline even if generation fails. |
| 159 | + |
| 160 | +## CLI Reference |
| 161 | + |
| 162 | +| Flag | Default | Effect | |
| 163 | +|------|---------|--------| |
| 164 | +| `--src-dir <path>` | `src/components` | Component source directory to scan | |
| 165 | +| `--config <path>` | `.claude/pipeline.config.json` | Pipeline config path | |
| 166 | +| `--force` | off | Overwrite existing `.stories.tsx` files | |
| 167 | +| `--dry-run` | off | Report only; write nothing | |
| 168 | +| `--no-mdx` | off | Skip `.mdx` generation | |
| 169 | +| `--json` | off | Emit JSON (suppresses human logs) | |
| 170 | + |
| 171 | +## Configuration |
| 172 | + |
| 173 | +`.claude/pipeline.config.json` → `storybook`: |
| 174 | + |
| 175 | +| Key | Default | Purpose | |
| 176 | +|-----|---------|---------| |
| 177 | +| `autoGenerate` | `true` | Enable/disable generation in the pipeline | |
| 178 | +| `includeResponsiveViewports` | `true` | Add a viewport story per breakpoint | |
| 179 | +| `viewports` | `["mobile","tablet","desktop"]` | Which breakpoints to emit (widths from `visualDiff.breakpoints`) | |
| 180 | +| `skipPatterns` | `["**/index.ts","**/*.test.*","**/*.stories.*"]` | Globs to exclude | |
| 181 | +| `generateMdx` | `true` | Emit `.mdx` docs alongside stories | |
| 182 | +| `maxVariantsPerProp` | `10` | Cap variant stories per prop (prevents explosion) | |
| 183 | + |
| 184 | +## Output |
| 185 | + |
| 186 | +| File | Purpose | |
| 187 | +|------|---------| |
| 188 | +| `src/components/**/Component.stories.tsx` | CSF3 stories with controls, variants, actions | |
| 189 | +| `src/components/**/Component.mdx` | Autodocs page (Meta/Canvas/Controls/ArgTypes) | |
| 190 | + |
| 191 | +## Common Mistakes |
| 192 | + |
| 193 | +- **No controls generated.** Props are declared as a `type` alias rather than an |
| 194 | + `interface ComponentNameProps`. The generator detects the type but does not yet |
| 195 | + expand its members — convert to an `interface` (or add controls manually). |
| 196 | +- **Component skipped.** The export is lowercase, not exported, or the file is a |
| 197 | + `*.test.tsx`/`index.tsx`. Ensure the component is exported with an uppercase name. |
| 198 | +- **Stories not overwritten.** Existing `.stories.tsx` are preserved by design; pass |
| 199 | + `--force` to regenerate. |
| 200 | +- **`children`/complex props show a text control.** Non-primitive props fall back to |
| 201 | + a `text` control — refine `argTypes` by hand where richer controls matter. |
| 202 | +- **Action name looks odd.** Action args derive a past-tense label from the event |
| 203 | + name (`onClick` → `clicked`, `onChange` → `changed`); rename the arg in the story |
| 204 | + if a different label reads better. |
| 205 | + |
| 206 | +## Integration |
| 207 | + |
| 208 | +- **figma-to-react-workflow skill** — invokes this skill as Phase 4.5, right after |
| 209 | + components are built and unit tests pass (non-blocking, in parallel with visual QA). |
| 210 | +- **react-testing-workflows skill** — owns hand-authored `play()`/interaction tests; |
| 211 | + this skill produces the base stories those can extend. |
| 212 | +- **parallel-orchestration skill** — runs the `storybook` phase (depends on |
| 213 | + `component-build`, `blocking: false`) alongside `visual-diff` and `dark-mode`. |
| 214 | +- **scripts/generate-stories.sh** — bash wrapper; forwards all flags to the Node |
| 215 | + generator. |
| 216 | +- **scripts/generate-stories.js** — the `ts-morph` AST generator that does the work. |
| 217 | +- **.claude/pipeline.config.json → storybook** — thresholds and toggles. |
| 218 | + |
| 219 | +--- |
| 220 | + |
| 221 | +**Skill Version:** 1.0.0 |
| 222 | +**Last Updated:** 2026-07-01 |
0 commit comments