This document defines the development contract for Storybook in `apps/web`: how to write and maintain stories without breaking the deterministic, zero-leak setup.
## 1. Use the global decorator only
**All stories are wrapped by `StorybookDecorator`** (see `.storybook/decorators.tsx`). It provides:
-`I18nextProvider` (i18n)
-`ThemeProvider` (light/dark via Storybook backgrounds)
-`MemoryRouter` (initial route configurable via parameters)
**You must not:**
- Import or wrap stories with these providers yourself.
- Import app-level providers from `src/` (e.g. `AuthProvider`, `QueryClientProvider`) inside a story file.
**Why:** A single decorator avoids duplicate providers, inconsistent configs, and hooks running outside their context (`useAuth`, `useNavigate`, `useQueryClient`, `useToast`, etc.).
### Route-dependent stories
If the component under test uses `useParams()`, `useSearchParams()`, or a specific path, set the route in the story’s `parameters`:
```ts
export const MyStory: StoryObj<typeofMyPage> = {
parameters: {
router: {
initialEntries: ['/tracks/123'],
},
},
};
```
Do not add a local `MemoryRouter` unless you need a different base path for that story only; the global decorator already provides `MemoryRouter` and will use `parameters.router.initialEntries` when present.
---
## 2. Mock all API calls with MSW
Storybook runs with **MSW (Mock Service Worker)** and is configured with **`onUnhandledRequest: 'error'`**. Any request that is not handled by a mock will cause an error in the console and can fail the story/audit.
**You must:**
- Ensure every API call used by your story is covered by a handler in `src/mocks/handlers.ts` (or by story-level `parameters.msw.handlers` overrides).
- Use relative API base (e.g. `VITE_API_URL=/api/v1` in Storybook scripts) so requests are same-origin and intercepted by MSW.
**Adding a mock for a new feature:**
1. Open `apps/web/src/mocks/handlers.ts`.
2. Add an `http.get()` or `http.post()` (etc.) handler for the endpoint (e.g. `*/api/v1/your/resource`).
3. Return a `HttpResponse.json(...)` with a shape that matches what your app expects.
4. If a story needs a different response (e.g. 404, empty list), override in that story:
Or run `npm run test:storybook` after starting the server (it runs `node scripts/audit-storybook.js`). The audit must report **0 application errors** (console errors, page errors, unhandled network failures). On failure it **exits with code 1** so CI can fail the job.
- **CI:** The workflow `.github/workflows/storybook-audit.yml` runs on changes under `apps/web`, builds Storybook, serves it on 6007, and runs the audit; the job fails if any story has errors.