Настройка Storybook для документации UI-компонентов
Storybook — среда разработки UI-компонентов в изоляции. Каждый компонент описывается Stories — отдельными состояниями: кнопка по умолчанию, кнопка disabled, кнопка loading, кнопка с иконкой. Разработчик видит все состояния сразу, дизайнер может проверять без запуска приложения, QA тестирует каждое состояние независимо.
Поддерживает React, Vue, Angular, Svelte, Web Components.
Установка в существующий проект
npx storybook@latest init
# Storybook определит фреймворк автоматически
Что создаётся:
.storybook/
main.ts — конфигурация аддонов, фреймворка, статических файлов
preview.ts — глобальные декораторы, параметры, тема
stories/ — примеры от Storybook (можно удалить)
Конфигурация для React + Vite + TypeScript
// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite'
const config: StorybookConfig = {
stories: ['../src/**/*.stories.@(ts|tsx)'],
addons: [
'@storybook/addon-essentials', // docs, controls, actions, viewport
'@storybook/addon-a11y', // проверка доступности
'@storybook/addon-interactions', // тестирование взаимодействий
'@chromatic-com/storybook', // visual regression testing
],
framework: {
name: '@storybook/react-vite',
options: {},
},
staticDirs: ['../public'],
docs: {
autodocs: 'tag', // генерировать документацию для компонентов с тегом 'autodocs'
},
}
export default config
// .storybook/preview.ts
import type { Preview } from '@storybook/react'
import '../src/styles/globals.css' // подключить глобальные стили
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
viewport: {
defaultViewport: 'responsive',
},
backgrounds: {
default: 'light',
values: [
{ name: 'light', value: '#ffffff' },
{ name: 'dark', value: '#0f172a' },
{ name: 'gray', value: '#f8fafc' },
],
},
},
// Глобальный декоратор — обернуть все stories в провайдеры
decorators: [
(Story) => (
<ThemeProvider>
<Story />
</ThemeProvider>
),
],
}
export default preview
Написание Stories (CSF 3)
Component Story Format 3 — актуальный формат:
// src/components/Button/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react'
import { fn } from '@storybook/test'
import { Button } from './Button'
const meta = {
title: 'UI/Button',
component: Button,
tags: ['autodocs'], // автоматически генерировать страницу документации
parameters: {
layout: 'centered', // 'centered' | 'fullscreen' | 'padded'
},
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'destructive', 'ghost'],
},
size: {
control: 'radio',
options: ['sm', 'md', 'lg'],
},
disabled: { control: 'boolean' },
loading: { control: 'boolean' },
},
args: {
onClick: fn(), // автоматически логируется в панели Actions
},
} satisfies Meta<typeof Button>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
children: 'Нажать',
variant: 'primary',
size: 'md',
},
}
export const Destructive: Story = {
args: {
children: 'Удалить',
variant: 'destructive',
size: 'md',
},
}
export const Loading: Story = {
args: {
children: 'Сохраняется...',
loading: true,
variant: 'primary',
},
}
export const AllVariants: Story = {
render: () => (
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
{(['primary', 'secondary', 'destructive', 'ghost'] as const).map((v) => (
<Button key={v} variant={v}>{v}</Button>
))}
</div>
),
}
Stories для сложных компонентов
// src/components/DataTable/DataTable.stories.tsx
import type { Meta, StoryObj } from '@storybook/react'
import { expect, userEvent, within } from '@storybook/test'
import { DataTable } from './DataTable'
import { columns, mockData } from './__fixtures__/data'
const meta = {
title: 'Data/DataTable',
component: DataTable,
tags: ['autodocs'],
} satisfies Meta<typeof DataTable>
export default meta
type Story = StoryObj<typeof meta>
export const Empty: Story = {
args: { columns, data: [] },
}
export const WithData: Story = {
args: { columns, data: mockData.slice(0, 5) },
}
export const Sortable: Story = {
args: { columns, data: mockData, sortable: true },
// Тест взаимодействия — запускается через "Interactions" панель
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const nameHeader = canvas.getByText('Имя')
await userEvent.click(nameHeader)
// Проверить, что данные отсортировались
const firstRow = canvas.getAllByRole('row')[1]
await expect(firstRow).toBeInTheDocument()
},
}
MDX-документация
{/* src/components/Button/Button.mdx */}
import { Canvas, Controls, Meta, Story } from '@storybook/blocks'
import * as ButtonStories from './Button.stories'
<Meta of={ButtonStories} />
# Button
Основная кнопка интерфейса. Использует Radix UI Slot для полиморфизма.
## Варианты
<Canvas of={ButtonStories.AllVariants} />
## API
<Controls />
## Доступность
Кнопка использует нативный `<button>` элемент. При `loading=true` добавляется
`aria-busy="true"` и `aria-label` для screen readers.
Интеграция с Tailwind CSS
// .storybook/preview.ts
import '../src/styles/globals.css' // должен содержать @import 'tailwindcss'
Этого достаточно. Storybook через Vite подхватит конфигурацию Tailwind автоматически.
Запуск и сборка
npm run storybook # dev-сервер на :6006
npm run build-storybook # статическая сборка в storybook-static/
Статическую сборку можно деплоить на любой хостинг (GitHub Pages, Netlify, Chromatic).
Что входит в работу
Установка и конфигурация Storybook под проект (Vite, TypeScript, CSS-фреймворк), настройка глобальных декораторов (провайдеры темы, роутер), написание Stories для существующих компонентов, настройка Controls и Actions, опционально — подключение addon-a11y и addon-interactions.
Срок: 0.5 дня на базовую настройку + 1–3 часа на каждые 10 компонентов.







