Chapter 07

[アドオン編] Controls addon

Controls addon について

Controls addon は、 essentials addons の一つで、コンポーネントに対するパラメータ(≒props)をGUI上で動的に切り替える 仕組みを提供します。

@storybook/addon-knobs と聞けばピンとくる方もいるかもしれません。 controls addonaddon-knobs の正当進化系であり、今まで以上に遥かに柔軟なパラメータ制御を行えるようになります。

Controls addon を体感する

essentials addons が有効になった状態で、 Storybook を立ち上げ、 Button コンポーネントのストーリーを開き、 アドオンパネルの Controls タブを開いてみましょう。

Controls タブ上に、 Button コンポーネントで定義された props が全てフォームに表示され、それを書き換えることで描画されているコンポーネントがリアクティブに書き換わっていることが確認できます。

このように、コンポーネントに対するパラメータに応じたデザイン、ふるまいの変化を、ソースコードを変更せずに柔軟に検証することができるのが Controls addon の真髄です。

Controls addon の原理を知る

おさらいすると、 Button コンポーネントは以下の props を受け取ります。

props名 役割
label String ボタンに表示するテキスト
primary Boolean 青を基調とした配色にする
secondary Boolean 灰を基調とした配色にする
size String ボタンサイズを "large" "medium" "small" から指定する
backgroundColor String 背景色を上書きする

ストーリーファイルでは、以下のように label primary secondary を、 args で定義することで、各ストーリーの初期値を指定しています。

src/components/Button.stories.js
export const Primary = Template.bind({})
Primary.args = {
  primary: true,
  secondary: false,
  label: 'プライマリーボタン'
}

export const Secondary = Template.bind({})
Secondary.args = {
  primary: false,
  scondary: true,
  label: 'セカンダリーボタン'
}

ではそれ以外の、 size backgroundColor に関する情報はどこから取ってきているのでしょうか?

実はそのために、デフォルトエクスポート内で component を指定していました。

src/components/Button.stories.js
export default {
  title: 'Button',
  component: Button
}

裏側では、 vue-docgen-api によって、 Vue ファイルからそのメタデータを抽出し、 vue-docgen-loader によって Webpack 経由で読み込むことで、コンポーネントのメタデータをコンポーネント内に注入しています。

__docgenInfo という属性で注入されているので、内容を出力してみましょう

src/components/Button.stories.js
console.log(JSON.stringify(Button.__docgenInfo, null, 2))

以下のように、 props だけでなく、 events についても抽出されていることがわかります。

{
  "exportName": "default",
  "displayName": "Button",
  "description": "",
  "tags": {},
  "props": [
    {
      "name": "label",
      "type": {
        "name": "string"
      },
      "required": true
    },
    {
      "name": "primary",
      "type": {
        "name": "boolean"
      },
      "defaultValue": {
        "func": false,
        "value": "false"
      }
    },
    {
      "name": "secondary",
      "type": {
        "name": "boolean"
      },
      "defaultValue": {
        "func": false,
        "value": "false"
      }
    },
    {
      "name": "size",
      "type": {
        "name": "string"
      },
      "defaultValue": {
        "func": false,
        "value": "\"medium\""
      }
    },
    {
      "name": "backgroundColor",
      "type": {
        "name": "string"
      },
      "required": false
    }
  ],
  "events": [
    {
      "name": "onClick"
    }
  ]
}

パラメータの型を定義します

先程の例では、 size backgroundColor を変更しましたが、どちらもテキストボックスで文字列を入力する形式でした。

お気づきかと思われますが、 size は、 small medium large のいずれかの値のみを取ることを想定しているため、自由にテキストボックスに入力する形式は正確ではありません。(backgroudnColor も同様)

事実、入力中もリアクティブに再描画されているため、入力が中途半端な状態でのコンソールエラーも発生してしまいます。

そのようなケースのために、 Controls addon では、以下のようなコントロールUIが提供されています。

  • 真偽値
  • 数値
  • JSON
  • ラジオボタン
  • チェックボックス
  • セレクトボックス
  • テキストボックス
  • マルチラインテキストボックス
  • カラーピッカー
  • カレンダー

上記のどのコントロールUIを使うかを指定する場合は、 export default するメタデータの中に、 argTypes 属性を定義し、パラメータ(≒props) の型情報を記述します。

Button コンポーネントの場合は、以下のようになります。

src/components/Button.stories.js
export default {
  title: 'Button',
  component: Button,
  argTypes: {
    size: {
      control: {
        type: 'inline-radio',
        options: ['small', 'medium', 'large']
      }
    },
    backgroundColor: {
      control: 'color'
    }
  }
}

コードから察せる通り、 sizesmall medium large から一つ選択できるようになり、backgroundColor はカラーピッカーから色を選択できるようになりました。

このあたりは手動でやる必要が出てくるので、やや億劫ではありますが、 props が取りうる値をより具体化することで、ドキュメントとしての実用性を高めることができます。