Storybook для документирования компонентов

Storybook — это инструмент для разработки и документирования пользовательских интерфейсов. Он позволяет изолированно создавать, тестировать и демонстрировать компоненты, что особенно удобно при работе с современными фреймворками, такими как Lit. Storybook строится вокруг концепции историй (stories) — отдельных примеров использования компонентов с конкретными состояниями и данными.


Установка и настройка

Для интеграции Storybook с проектом на Lit необходимо выполнить установку основных пакетов:

npm install @storybook/web-components @storybook/addon-essentials --save-dev

Инициализация Storybook происходит через команду:

npx sb init

В процессе инициализации выбирается вариант для Web Components, что позволяет корректно работать с Lit-компонентами. После этого создается структура каталогов, включая:

  • stories — каталог с историями компонентов.
  • .storybook — конфигурационные файлы Storybook, такие как main.js и preview.js.

В файле main.js необходимо указать поддержку web-компонентов:

module.exports = {
  stories: ['../src/**/*.stories.@(js|ts)'],
  addons: ['@storybook/addon-essentials'],
  framework: '@storybook/web-components',
};

Создание истории для Lit-компонента

Предположим, есть простой компонент на Lit:

import { LitElement, html, css } from 'lit';

export class MyButton extends LitElement {
  static properties = {
    label: { type: String },
    disabled: { type: Boolean },
  };

  static styles = css`
    button {
      padding: 8px 16px;
      font-size: 16px;
    }
    button[disabled] {
      background-color: #ccc;
      cursor: not-allowed;
    }
  `;

  render() {
    return html`<button ?disabled=${this.disabled}>${this.label}</button>`;
  }
}

customElements.define('my-button', MyButton);

Для него создается файл истории my-button.stories.js:

import './my-button.js';

export default {
  title: 'Components/MyButton',
  component: 'my-button',
  argTypes: {
    label: { control: 'text' },
    disabled: { control: 'boolean' },
  },
};

const Template = ({ label, disabled }) => {
  const el = document.createElement('my-button');
  el.label = label;
  el.disabled = disabled;
  return el;
};

export const Default = Template.bind({});
Default.args = {
  label: 'Click Me',
  disabled: false,
};

export const Disabled = Template.bind({});
Disabled.args = {
  label: 'Cannot Click',
  disabled: true,
};

Ключевой момент — использование argTypes и args, которые позволяют динамически изменять свойства компонента через интерфейс Storybook.


Настройка документации и аддонов

Storybook поддерживает аддоны, которые улучшают визуализацию и документацию компонентов:

  1. Docs — автоматическая генерация документации на основе историй и комментариев JSDoc.
  2. Controls — интерактивное управление свойствами компонента.
  3. Actions — логирование событий, таких как клики или изменения состояния.
  4. Accessibility — проверка компонентов на соответствие стандартам доступности.

Пример подключения аддонов в main.js:

module.exports = {
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-a11y',
    '@storybook/addon-actions',
  ],
};

Использование Actions для события кнопки:

import { action } from '@storybook/addon-actions';

const Template = ({ label, disabled }) => {
  const el = document.createElement('my-button');
  el.label = label;
  el.disabled = disabled;
  el.addEventListener('click', action('button-click'));
  return el;
};

Структура историй и организация компонентов

Истории удобно группировать по категориям, например:

Components/
  ├─ Buttons/
  │   ├─ my-button.stories.js
  │   └─ icon-button.stories.js
  ├─ Inputs/
  │   └─ text-input.stories.js

Использование иерархии title позволяет создавать древовидное отображение в боковой панели Storybook:

export default {
  title: 'Components/Buttons/MyButton',
  component: 'my-button',
};

Каждая история должна демонстрировать отдельное состояние компонента. Например, для кнопки:

  • Default — обычная кнопка.
  • Disabled — недоступная кнопка.
  • Loading — кнопка с индикатором загрузки.

Автоматизация документации

Storybook Docs автоматически формирует превью и описание компонентов, если они имеют JSDoc-комментарии:

/**
 * Компонент кнопки.
 * 
 * @prop {string} label - Текст кнопки
 * @prop {boolean} disabled - Отключает кнопку
 */

Документация включает:

  • Список всех свойств (props) и их типы.
  • Демо всех историй.
  • Возможность интерактивно менять свойства через Controls.

Интеграция с LitElement особенностей

Lit-компоненты поддерживают:

  • Reactive properties — свойства автоматически обновляют DOM при изменении.
  • CSS encapsulation — стили компонента изолированы от внешнего контекста.
  • Slots — возможность вставки вложенного контента.

Storybook полностью поддерживает эти возможности. Например, для компонента с slot:

import { LitElement, html } from 'lit';

export class CardComponent extends LitElement {
  render() {
    return html`
      <div class="card">
        <slot></slot>
      </div>
    `;
  }
}

customElements.define('card-component', CardComponent);

История:

export const WithContent = () => {
  const el = document.createElement('card-component');
  el.innerHTML = '<p>Содержимое карточки</p>';
  return el;
};

Настройка среды разработки

Storybook предоставляет:

  • Live Reload — автоматическое обновление при изменении компонента.
  • Hot Module Replacement — быстрый ререндер без полной перезагрузки.
  • Preview iframe — изолированное отображение компонентов, исключающее внешние стили проекта.

Запуск:

npm run storybook

После чего компоненты доступны по адресу http://localhost:6006.


Тонкости и рекомендации

  • Использовать Template + args вместо ручного создания компонентов, чтобы Storybook автоматически управлял состояниями.
  • Документировать все свойства через JSDoc для полной совместимости с Docs.
  • Создавать истории для каждого ключевого состояния компонента, включая ошибки и пустые состояния.
  • Использовать аддоны для контроля доступности и логирования действий, особенно при работе с интерактивными компонентами.

Storybook в связке с Lit позволяет строить полный каталог UI-компонентов, который легко поддерживать и расширять, обеспечивая высокую модульность и прозрачность интерфейсов.