0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Storybookを使おう!

Posted at

はじめに

モダンなフロントエンド開発において、UIコンポーネントの開発と管理は重要な課題の一つです。今回は、その課題を解決するツール「Storybook」について、その特徴と実践的な使い方を紹介します。

Storybookとは

Storybookは、UIコンポーネントの開発環境として機能するオープンソースツールです。
以下のような特徴があります。

  • コンポーネントを独立した環境で開発・テストできる
  • インタラクティブなドキュメントとしても機能する
  • 様々なフレームワーク(React、Vue、Angular等)に対応
  • 豊富なアドオンによる拡張性

image.png

セットアップ方法

1. プロジェクトへの導入

# 既存プロジェクトに Storybook を追加
npx storybook init

2. 基本的な構成

Storybookをインストールすると、以下のようなファイル構造が生成されます。

.storybook/
  ├── main.js  # Storybook の設定ファイル
  └── preview.js  # Story のグローバル設定
src/
  └── stories/  # Story ファイル用ディレクトリ

full-mui-demo-1.gif

Storyファイルの作成単位

Storyファイルの作成対象単位を表にまとめてみましょう。

作成対象 説明
コンポーネント
(Component)
単一のUIコンポーネント
(Button, Input, Cardなど)
複合コンポーネント
(Composite)
複数のコンポーネントを組み合わせた機能
(Form, Dialog, Tableなど)
レイアウト
(Layout)
ページ構造を定義するコンポーネント
(Header, Sidebar, Gridなど)
ページ
(Page)
完全なページ単位のコンポーネント
(Login, Dashboard, Settingsなど)
テンプレート
(Template)
再利用可能なページの雛形
(ArticleTemplate, ProductTemplate など)
フック
(Hook)
カスタムフックの使用例
(useForm, useAuth などのカスタムフック)
ユーティリティ
(Utility)
共通の機能やヘルパー
(DataFormatter, ThemeProvider など)
パターン
(Pattern)
特定の UI パターン
(LoadingState, ErrorBoundary など)

これらの作成対象は、プロジェクトの規模や要件に応じて適切に選択します。
必ずしもすべての対象にStoryを作成する必要はありません。

Storyの書き方

Storyはコンポーネントの使用例を示すためのファイルです。以下は基本的な作成例です。

1. コンポーネント(Component)

// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  title: 'Components/Button',
  component: Button,
  tags: ['autodocs'],
};

export default meta;
type Story = StoryObj<typeof Button>;

export const Primary: Story = {
  args: {
    variant: 'primary',
    children: 'ボタン',
  },
};

2. 複合コンポーネント(Composite)

// LoginForm.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { LoginForm } from './LoginForm';

const meta: Meta<typeof LoginForm> = {
  title: 'Forms/LoginForm',
  component: LoginForm,
  parameters: {
    layout: 'centered',
  },
};

export default meta;
type Story = StoryObj<typeof LoginForm>;

export const Default: Story = {
  args: {
    onSubmit: (data) => console.log('Form submitted:', data),
  },
};

export const WithError: Story = {
  args: {
    error: 'Invalid credentials',
    onSubmit: (data) => console.log('Form submitted:', data),
  },
};

3. レイアウト(Layout)

// Sidebar.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Sidebar } from './Sidebar';

const meta: Meta<typeof Sidebar> = {
  title: 'Layout/Sidebar',
  component: Sidebar,
  parameters: {
    layout: 'fullscreen',
  },
};

export default meta;
type Story = StoryObj<typeof Sidebar>;

export const Default: Story = {
  args: {
    items: [
      { label: 'Dashboard', icon: 'home' },
      { label: 'Settings', icon: 'settings' },
    ],
  },
};

4. ページ(Page)

// DashboardPage.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { DashboardPage } from './DashboardPage';

const meta: Meta<typeof DashboardPage> = {
  title: 'Pages/Dashboard',
  component: DashboardPage,
  parameters: {
    layout: 'fullscreen',
    backgrounds: {
      default: 'light',
    },
  },
};

export default meta;
type Story = StoryObj<typeof DashboardPage>;

export const Default: Story = {
  args: {
    username: 'John Doe',
    stats: {
      totalUsers: 1234,
      activeUsers: 789,
    },
  },
};

5. テンプレート(Template)

// ArticleTemplate.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { ArticleTemplate } from './ArticleTemplate';

const meta: Meta<typeof ArticleTemplate> = {
  title: 'Templates/Article',
  component: ArticleTemplate,
  parameters: {
    layout: 'fullscreen',
  },
};

export default meta;
type Story = StoryObj<typeof ArticleTemplate>;

export const Default: Story = {
  args: {
    title: '記事タイトル',
    content: '記事の本文...',
    author: {
      name: '著者名',
      avatar: '/avatar.jpg',
    },
  },
};

6. フック(Hook)

// useCounter.stories.tsx
import { useCounter } from './useCounter';
import { Meta, StoryObj } from '@storybook/react';

// フックを表示するためのラッパーコンポーネント
const CounterDemo = () => {
  const { count, increment, decrement } = useCounter(0);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  );
};

const meta: Meta<typeof CounterDemo> = {
  title: 'Hooks/useCounter',
  component: CounterDemo,
};

export default meta;
type Story = StoryObj<typeof CounterDemo>;

export const Default: Story = {};

7. ユーティリティ(Utility)

// ThemeProvider.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { ThemeProvider } from './ThemeProvider';
import { Button } from '../components/Button';

const meta: Meta<typeof ThemeProvider> = {
  title: 'Utilities/ThemeProvider',
  component: ThemeProvider,
};

export default meta;
type Story = StoryObj<typeof ThemeProvider>;

export const Default: Story = {
  render: () => (
    <ThemeProvider theme="light">
      <Button>Themed Button</Button>
    </ThemeProvider>
  ),
};

8. パターン(Pattern)

// LoadingState.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { LoadingState } from './LoadingState';

const meta: Meta<typeof LoadingState> = {
  title: 'Patterns/LoadingState',
  component: LoadingState,
};

export default meta;
type Story = StoryObj<typeof LoadingState>;

export const Default: Story = {
  args: {
    isLoading: true,
    children: <div>コンテンツ</div>,
  },
};

export const WithCustomSpinner: Story = {
  args: {
    isLoading: true,
    spinner: <div>カスタムローディング...</div>,
    children: <div>コンテンツ</div>,
  },
};

アドオンの活用

Storybookの機能は、アドオンによって拡張できます。特に有用なアドオンを紹介します。

アドオン名 説明
Controls コンポーネントのプロパティをGUI上で動的に変更可能
Accessibility アクセシビリティチェックを自動実行
Viewport 様々な画面サイズでのレイアウト確認
Actions ユーザーイベントのモニタリング
Docs 自動ドキュメント生成
Interactions インタラクティブなテストの作成と実行

CI/CDへの統合

Storybookは、CIプロセスに組み込むことができます。
以下に主要なCIプラットフォームでの設定例を示します。

GitHub Actions

# .github/workflows/storybook.yml
name: Storybook Tests
on: push
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
      - run: npm ci
      - run: npm run build-storybook
      - run: npm run test-storybook

GitLab CI

# .gitlab-ci.yml
storybook:
  image: node:latest
  stage: test
  script:
    - npm ci
    - npm run build-storybook
    - npm run test-storybook
  artifacts:
    paths:
      - storybook-static/

まとめ

Storybookを導入することで、以下のメリットが得られます。
Storybookを使おう!

  • コンポーネントの開発効率の向上

  • 品質管理の効率化

  • ドキュメント作成の自動化

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?