🥤

Storybook Controlsを使ってみよう!動的にコンポーネントを変更する方法とは?

2024/09/24に公開

はじめに

Storybook Controlsとは

Storybook Controlsは、Storybookというフロントエンド開発ツールの機能の1つです。簡単に言えば、UIコンポーネントの「つまみ」のようなものです。これを使うと、開発者やデザイナーが、コードを書き換えることなく、コンポーネントの見た目や動きを簡単に調整できます。

https://storybook.js.org/docs/essentials/controls

例えば、ボタンのコンポーネントを作ったとしましょう。Controlsを使えば、そのボタンの色、サイズ、テキストなどを、まるでおもちゃで遊ぶように簡単に変更できます。

Storybook Controlsを使う利点

「へー、便利そうだけど、どういう利点があるの?」と思われるかもしれません。実は、Storybook Controlsには開発プロセスを効率化する様々なメリットがあります。

  1. 開発スピードの向上: コードの変更→ビルド→確認、というサイクルを繰り返さなくても、即座に変更を確認できます。

  2. コミュニケーションの円滑化: 開発者とデザイナー、あるいは顧客との間で、コンポーネントの調整をリアルタイムで行えます。

  3. 品質の向上: さまざまなパターンを簡単に試せるので、エッジケースの発見や、より堅牢なコンポーネント設計につながります。

  4. ドキュメンテーションの充実: Controlsは、そのままコンポーネントの使い方を示すドキュメントとしても機能します。

これから、このStorybook Controlsについて、基本的な使い方から応用テクニックまで、じっくりと見ていきましょう。

Storybook Controlsの基本

さて、Storybook Controlsの魅力が少し分かってきたところで、実際にどんなものなのか、もう少し詳しく見ていきましょう。

Controlsの概要

Storybook Controlsは、コンポーネントの「props」(プロパティ)を動的に変更できるインターフェースです。これを使うと、開発者やデザイナーは、コードを直接編集することなく、コンポーネントの様々な状態や設定を簡単に確認できます。

例えるなら、テレビのリモコンのようなものです。チャンネルや音量を変えるのに、テレビの裏側の配線をいじる必要はありませんよね。同じように、Controlsを使えば、コードをいじらなくてもコンポーネントの見た目や動作を変更できます。

Controlsのタイプ

Storybook Controlsには、様々なタイプがあります。主なものを見ていきましょう。

  1. テキスト (Text): 文字列を入力するためのシンプルな入力欄です。ボタンのラベルや見出しのテキストを変更するのに使えます。

  2. 数値 (Number): 数値を入力するための欄です。スライダーとしても表示できます。例えば、余白やフォントサイズの調整に便利です。

  3. ブール値 (Boolean): オン/オフを切り替えるためのスイッチです。例えば、ボタンを無効化するかどうかの設定に使えます。

  4. オプション選択 (Select): プルダウンメニューから選択肢を選ぶためのコントロールです。例えば、ボタンのサイズ(小/中/大)を選ぶのに使えます。

  5. カラーピッカー (Color): 色を選択するためのツールです。背景色やテキスト色の変更に便利です。

  6. オブジェクト (Object): 複雑なデータ構造を入力するための領域です。JSONエディタのように使えます。

これらのControlsを組み合わせることで、コンポーネントを制御できます。

Controlsの設定方法

では、実際にControlsを設定する方法を見ていきましょう。基本的な手順は以下の通りです。

  1. まず、Storybookのストーリーファイル(例:Button.stories.js)を開きます。
  2. コンポーネントの「args」(引数)を定義します。これがControlsの基になります。
  3. 必要に応じて、特定のpropsに対してControlsの型を指定します。

簡単な例を見てみましょう。

// Button.stories.js
import { Button } from './Button';

export default {
  title: 'Components/Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
    size: {
      control: { type: 'select', options: ['small', 'medium', 'large'] }
    },
  },
};

const Template = (args) => <Button {...args} />;

export const Primary = Template.bind({});
Primary.args = {
  primary: true,
  label: 'Button',
};

このコードでは、backgroundColorsizeにControlsを設定しています。backgroundColorはカラーピッカーになり、sizeはプルダウンメニューになります。

Storybookを起動すると、これらのControlsがUIに表示され、簡単に値を変更できるようになります。

Storybook Controlsの詳細な使い方

基本を押さえたところで、もう一歩進んだStorybook Controlsの使い方を見ていきましょう。ここでは、より複雑なシナリオや、Controlsのカスタマイズ方法について書いています。

カスタムControlsの作成

時には、標準のControlsだけでは足りないこともあります。そんなときは、カスタムControlsを作成することができます。

例えば、特定の形式の日付を入力するためのカスタムControlを作ってみましょう。

// MyComponent.stories.js
import { MyComponent } from './MyComponent';

export default {
  title: 'Components/MyComponent',
  component: MyComponent,
  argTypes: {
    date: {
      control: {
        type: 'date',
      },
      transform: (value) => {
        const date = new Date(value);
        return date.toISOString().split('T')[0]; // YYYY-MM-DD形式に変換
      },
    },
  },
};

export const Default = (args) => <MyComponent {...args} />;
Default.args = {
  date: new Date().toISOString(),
};

この例では、日付を「YYYY-MM-DD」形式で扱うカスタムControlを作成しています。Storybookの画面では日付選択UIが表示され、選択した日付が指定の形式で出力されます。 transform 関数を使用することで、選択された日付を希望の形式に変換しています。

動的なControlsの実装

Controlsの内容を動的に変更したいケースもあるでしょう。例えば、あるControlの値に応じて、別のControlの選択肢を変更したい場合などです。

これを実現するには、Storybookの「装飾子(Decorator)」機能を使います。

// DynamicControls.stories.js
import { useState, useEffect } from 'react';
import { DynamicControls } from './DynamicControls';

export default {
  title: 'Components/DynamicControls',
  component: DynamicControls,
};

const withDynamicSelect = (Story, context) => {
  const [options, setOptions] = useState(['Option A', 'Option B']);

  useEffect(() => {
    if (context.args.enableExtraOptions) {
      if (!options.includes('Option C')) {
        setOptions([...options, 'Option C', 'Option D']);
      }
    } else {
      if (options.includes('Option C')) {
        setOptions(options.filter(opt => !['Option C', 'Option D'].includes(opt)));
      }
    }
  }, [context.args.enableExtraOptions]);

  return (
    <Story
      args={{
        ...context.args,
        options,
      }}
    />
  );
};

export const Default = {
  decorators: [withDynamicSelect],
  args: {
    enableExtraOptions: false,
    selectedOption: 'Option A',
  },
  argTypes: {
    enableExtraOptions: { control: 'boolean' },
    selectedOption: {
      control: 'select',
      options: ['Option A', 'Option B', 'Option C', 'Option D'],
    },
  },
};

この例では、enableExtraOptions の値に応じて、選択肢の options が動的に変更されます。enableExtraOptions が true になると、「Option C」と「Option D」が選択肢に追加され、false になるとこれらが削除されます。

ControlsとActionの連携

最後に、ControlsとStorybookのもう一つの強力な機能である「Actions」を組み合わせる方法を見てみましょう。Actionsを使うと、コンポーネントの挙動をより詳細に観察できます。

// InteractiveButton.stories.js
import { InteractiveButton } from './InteractiveButton';

export default {
  title: 'Components/InteractiveButton',
  component: InteractiveButton,
  argTypes: {
    backgroundColor: { control: 'color' },
    onClick: { action: 'clicked' },
  },
};

const Template = (args) => <InteractiveButton {...args} />;

export const Default = Template.bind({});
Default.args = {
  label: 'Click me!',
};

この例では、ボタンの背景色を変更するControlと、クリック時のActionを設定しています。Storybookの画面では、色選択コントロールを使って背景色を変更でき、同時にボタンをクリックしたときの挙動も確認できます。

まとめ

以上、Storybook Controlsの基本から実際の使い方まで見てきました。Controlsを活用することで、コンポーネントの開発とテストがより効率的になり、チーム間のコミュニケーションも円滑になります。

実際のプロジェクトでこれらの技術を適用し、自身のワークフローに合わせて調整していくことをお勧めします。

Discussion