veza/docs/STORYBOOK_CONTRACT.md

4.9 KiB
Raw Blame History

Storybook Contract (Veza Web)

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)
  • QueryClientProvider (React Query, retry: false, staleTime: Infinity)
  • ToastProvider
  • AudioProvider
  • AuthProvider (mock logged-in user by default)
  • 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 storys parameters:

export const MyStory: StoryObj<typeof MyPage> = {
  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:
parameters: {
  msw: {
    handlers: [
      http.get('*/api/v1/your/resource', () => HttpResponse.json({ data: [] })),
    ],
  },
},

You must not:

  • Rely on a real backend when running or building Storybook.
  • Add stories that trigger unhandled network requests; they will fail under the current strict MSW setup.

3. Hierarchy and tags

Stories are grouped in the sidebar as follows:

Segment Usage
App/Pages Full-page components (e.g. Dashboard, Login, TrackDetail).
App/Layouts Global layout components (Header, Sidebar, DashboardLayout).
Components/UI Atomic UI (Button, Alert, Input).
Components/Features Feature-specific and complex components (Playlists, Chat, Studio, etc.).
Docs/Failures Error states and error boundaries (ErrorBoundary, NotFoundPage, ServerErrorPage).

Set the meta title in your .stories.tsx accordingly (e.g. title: 'App/Pages/Dashboard/DashboardPage', title: 'Components/UI/Button').

Autodocs are enabled globally; each component gets a generated docs page.


4. Verification

  • Audit: From apps/web, build Storybook, serve the static build on port 6007, then run:

    npm run build-storybook
    npx serve -s storybook-static -p 6007 &
    npm run test:storybook
    

    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.

  • Local run: Use npm run storybook (port 6006). Ensure no console errors when opening the stories you changed.


5. Summary

Rule Action
Providers Use only StorybookDecorator; do not import app providers in stories.
Routes Use parameters.router.initialEntries for route-dependent stories.
API Mock every request in handlers.ts or per-story parameters.msw.handlers.
Hierarchy Use App/Pages, App/Layouts, Components/UI, Components/Features, Docs/Failures.
Strict MSW onUnhandledRequest: 'error' is on; add mocks for any new API used by a story.

This contract keeps Storybook deterministic, free of network leaks, and safe for future changes.