172 lines
3.9 KiB
Markdown
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
|
|
};
|
|
```
|