veza/apps/web/docs/STORYBOOK_CONTRACT.md

172 lines
3.9 KiB
Markdown

# Storybook Contract — Veza Design System
## Purpose
This document defines the mandatory structure and conventions for all Storybook stories in the Veza frontend. It is referenced by `.cursorrules` and must be followed by all contributors and AI assistants.
## Story Requirements
### 1. Feature Components
Every **Feature component** (components in `src/features/`) must have the following story variants:
| Story Name | Description | Required |
|---|---|---|
| `Default` | Component with typical data | Yes |
| `Loading` | Component in loading state (using Skeleton components) | Yes |
| `Error` | Component displaying an error fallback | Yes |
| `Empty` | Component with no data (empty list/state) | Yes, if applicable |
### 2. UI Components
Every **UI component** (components in `src/components/ui/`) must have:
| Story Name | Description | Required |
|---|---|---|
| `Default` | Default variant/state | Yes |
| `Variants` | All visual variants (size, color, etc.) | If applicable |
| `Disabled` | Disabled state | If applicable |
| `WithIcon` | With icon prop | If applicable |
### 3. Layout Components
Layout components (`src/components/layout/`) should demonstrate:
- Responsive behavior (mobile, tablet, desktop)
- Light and dark modes
## Decorators
### Global Decorator
All stories automatically receive the global `StorybookDecorator` configured in `.storybook/preview.tsx`. This provides:
- Theme provider (dark/light mode)
- i18n provider
- Router context (MemoryRouter)
- Toast provider
- MSW request interception
### Do NOT Import Application Providers
Never import application-level providers directly in stories:
```tsx
// BAD — do not do this
import { AuthProvider } from '@/providers/AuthProvider';
export const MyStory = () => (
<AuthProvider>
<MyComponent />
</AuthProvider>
);
// GOOD — the global decorator handles providers
export const MyStory = () => <MyComponent />;
```
## Data Mocking
### Use MSW Handlers
All API data should be mocked via MSW handlers in `src/mocks/handlers.ts`:
```tsx
// In the story file
import type { Meta, StoryObj } from '@storybook/react';
import { http, HttpResponse } from 'msw';
const meta: Meta<typeof MyComponent> = {
component: MyComponent,
parameters: {
msw: {
handlers: [
http.get('*/api/v1/my-endpoint', () => {
return HttpResponse.json({
success: true,
data: { items: [] },
});
}),
],
},
},
};
```
### Loading States
Use `delay: 'infinite'` in MSW handlers to simulate loading:
```tsx
export const Loading: StoryObj<typeof MyComponent> = {
parameters: {
msw: {
handlers: [
http.get('*/api/v1/my-endpoint', async () => {
await new Promise(() => {}); // Never resolves
}),
],
},
},
};
```
### Error States
Return error responses from MSW handlers:
```tsx
export const Error: StoryObj<typeof MyComponent> = {
parameters: {
msw: {
handlers: [
http.get('*/api/v1/my-endpoint', () => {
return HttpResponse.json(
{ success: false, error: { message: 'Server error' } },
{ status: 500 },
);
}),
],
},
},
};
```
## File Naming Convention
- Story files: `ComponentName.stories.tsx`
- Co-located with their component file
- Example: `src/components/ui/button.tsx``src/components/ui/button.stories.tsx`
## Story File Template
```tsx
import type { Meta, StoryObj } from '@storybook/react';
import { MyComponent } from './MyComponent';
const meta: Meta<typeof MyComponent> = {
title: 'Features/MyFeature/MyComponent',
component: MyComponent,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof MyComponent>;
export const Default: Story = {
args: {
// Default props
},
};
export const Loading: Story = {
// Loading state configuration
};
export const Error: Story = {
// Error state configuration
};
export const Empty: Story = {
// Empty state configuration
};
```