Storybook: разработка компонентов

Storybook — инструмент для разработки UI-компонентов в изоляции от приложения: каждый компонент отображается в отдельной «истории» (story) со всеми возможными состояниями и вариантами.

Зачем нужно

Разработка компонента внутри приложения требует воспроизвести определённое состояние (пустая корзина, ошибка загрузки, длинный текст), что занимает время. Storybook позволяет разрабатывать компонент отдельно, сразу видя все его состояния. Это ускоряет разработку, служит живой документацией для дизайнеров и разработчиков, и упрощает визуальное тестирование.

Где используется

  • Дизайн-системы и компонентные библиотеки (Material UI, Chakra UI используют Storybook)
  • Командная разработка: дизайнеры просматривают компоненты без запуска приложения
  • Visual regression testing через Chromatic или Percy
  • Документация API компонентов (Controls addon)

Структура Story

src/
  components/
    Button/
      Button.jsx         ← сам компонент
      Button.stories.jsx ← истории (stories)
      Button.test.jsx    ← тесты
      Button.module.css

Button.stories.jsx (CSF format)

// Component Story Format (CSF) — стандарт Storybook
import Button from './Button';

// Meta — настройки для всех историй компонента
export default {
  title: 'UI/Button',       // категория/имя в сайдбаре
  component: Button,
  // Автогенерация Controls из TypeScript типов
  argTypes: {
    variant: {
      control: 'select',
      options: ['primary', 'secondary', 'danger'],
    },
    disabled: { control: 'boolean' },
    onClick: { action: 'clicked' },  // логирует клики
  },
};

// Каждый именованный экспорт = отдельная история
export const Primary = {
  args: {
    children: 'Сохранить',
    variant: 'primary',
  },
};

export const Secondary = {
  args: {
    children: 'Отмена',
    variant: 'secondary',
  },
};

export const Disabled = {
  args: {
    children: 'Недоступно',
    variant: 'primary',
    disabled: true,
  },
};

export const LongText = {
  args: {
    children: 'Очень длинный текст кнопки для проверки переноса',
    variant: 'primary',
  },
};

Decorator — глобальный контекст

// .storybook/preview.js
import { ThemeProvider } from '../src/providers/ThemeProvider';

export const decorators = [
  // Оборачивает каждую story в ThemeProvider
  (Story) => (
    <ThemeProvider>
      <Story />
    </ThemeProvider>
  ),
];

export const parameters = {
  // Настройки backgrounds, viewport и т.д.
  backgrounds: {
    default: 'light',
    values: [
      { name: 'light', value: '#ffffff' },
      { name: 'dark', value: '#1a1a1a' },
    ],
  },
};

Play function — интерактивные тесты

import { userEvent, within } from '@storybook/testing-library';

export const WithInteraction = {
  args: { children: 'Открыть' },
  // play выполняется после рендера — автоматизирует взаимодействие
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    const button = canvas.getByRole('button');
    await userEvent.click(button);
  },
};

Частые ошибки

  • Stories без крайних случаев — пишут только «happy path»; добавляйте истории для пустых данных, ошибок, длинных строк.
  • Дублируют логику в stories — story должна только передавать props; не добавляйте в story бизнес-логику.
  • Не настраивают Decorator для провайдеров — компоненты с ThemeContext или Router падают в Storybook без соответствующих обёрток в preview.js.

Связанные темы

Ресурсы