LoginSignup
3

More than 3 years have passed since last update.

Nuxt環境でStorybook 6.0をUIカタログからステップアップさせる: Docs(未完成), Controls

Posted at

この記事は「株式会社オープンストリーム "小ネタ" Advent Calendar 2020」の 8 日目の記事です。

前記事で Storybook6.0 の導入を開設しましたが、Storybook の機能をさらに使って UI カタログ以上の使い方をしましょう。

環境

  • Nuxt.js 2.14.9
  • Storybook 6.0.10
$ node -v
v14.5.0

$ yarn -v
1.22.5

Storybook にアドオンを追加する

sb init したときに次のアドオンが自動的に導入されています。

package.json
...
  "devDependencies": {
    "@storybook/addon-actions": "^6.1.10",
    "@storybook/addon-essentials": "^6.1.10",
    "@storybook/addon-links": "^6.1.10",
...

"devDependencies" にアドオンのパッケージが導入されていなければ、 yarn add -D で導入します。

yarn add -D @storybook/addon-essentials

パッケージを add した後は .storybook/main.js に使いたいアドオンを読み込みます。

.storybook/main.js
const path = require('path')

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

...

そのほかに、パッケージ add 後 .storybook/preview.js に記述する必要があるアドオンなどもありますので、README を読んでから導入しましょう。

Docs

@storybook/addon-essentials を導入すると使えるようになります。Storyで使っているコンポーネントについて props, methods などに説明を追加することができます。

Nuxt(Vue)の環境でMDXを書くことができなかった

Storybook のドキュメントの通り Writing Stories の次の Writing Docs を進めようとすると、create-nuxt-appの環境だと MDX を書く段階 で Vue コンポーネントを読み込んでくれないので別の方法で Storybook のドキュメントを作成します。

WARNING in ./components/buttons.stories.mdx 22:13-26
"export 'CustomButtons' was not found in './CustomButtons.vue'

VueコンポーネントからDocsを自動生成する

Story ファイルと Vue コンポーネントから自動生成する方法が別に用意されています。 vue-docgen-api (依存関係で導入される)を使った仕組みで、Storybook 5.3 から使えます。

その前に、Story ファイルでは Story でコンポーネントを呼び出すときに prop を受け入れる形に書き直す必要があります。
これをやらないと "Nothing found" と表示されたままになります。

Storyファイル内でTemplateを定義してからそのTemplateをbind, そのあとに args プロパティに props の値をセットします。

.storybook/main.js
import CustomButtons from './CustomButtons.vue'
export default {
  title: 'Button'
}

const Template = (args, { argTypes }) => ({
  props: Object.keys(argTypes),
  components: { CustomButtons },
  template: `<custom-buttons v-bind="$props" /> `
})

export const FirstTime = Template.bind({})
FirstTime.args = {
  isVisibleTutorButton: true,
  colorMode: 'light',
}

export const Normal = Template.bind({})
Normal.args = {
  isVisibleTutorButton: false,
  colorMode: 'light',
}

そして、Vue コンポーネント側に vue-docgen-api で認識できる形に注釈を入れるのですが…

components/CustomButtons.vue
<template>
  <div>
    <p class="introduction">好きな<strong>処理</strong>を選択してください</p>
    <div class="buttons">
      <button @click="onClick" class="button" v-if="isVisibleTutorButton">
        tutor
      </button>
      <button @click="onClick" class="button is-primary">かんたん</button>
      <button @click="onClick" class="button is-link">むずかしい</button>
    </div>
  </div>
</template>

<script lang="ts">
import Vue from "vue";
export default Vue.extend({
  props: {
    /**
     * チュートリアルのボタンを表示させるか
     */
    isVisibleTutorButton: {
      type: Boolean,
      default: true,
    },
  },
  methods: {
    /**
     * ボタンがクリックされたとき
     * @Event onClick
     * @type {Object}
     */
    onClick() {
      this.$emit("onButtonClick");
    },
  },
});
</script>

締め切り過ぎてまで 検証しても Description に説明を挿入できませんでした。

image.png

また、props, methods などに付けたアノテーション以外に説明文を追加する方法は見つかりませんでした。

Controls addon

ドキュメントでだいぶ出鼻をくじかれた形になりますが、他のアドオンを使ってみましょう。

こちらも @storybook/addon-essentials を導入すると使えるようになります。

Story でコンポーネントを呼び出すときに、コンポーネントに props があってそれが Story から args として渡されていれば、自動的にそれを Storybook で変更できるようにするアドオンになります。

例としてVueコンポーネントにはBoolean, Stringの props を2つ追加します。

components/CustomButtons.vue
<template>
  <div>
    <p class="introduction">好きな<strong>処理</strong>を選択してください</p>
    <div class="buttons">
      <button
        @click="onClick"
        class="button"
        :class="colorClass"
        v-if="isVisibleTutorButton"
      >
        tutor
      </button>
      <button @click="onClick" class="button is-primary">かんたん</button>
      <button @click="onClick" class="button is-link">むずかしい</button>
    </div>
  </div>
</template>

<script lang="ts">
import Vue from "vue";
export default Vue.extend({
  props: {
    isVisibleTutorButton: {
      type: Boolean,
      default: true,
    },
    colorMode: {
      type: String,
      default: "light",
      validator: function (value) {
        return ["light", "dark", "black"].indexOf(value) !== -1;
      },
    },
  },
  methods: {
    onClick() {
      this.$emit("onButtonClick");
    },
  },
  computed: {
    colorClass() {
      return "is-" + this.colorMode;
    },
  },
});
</script>

Storyファイルでその props に対して値を入れるとStorybookを開いたときにControlsが表示されます。

components/buttons.stories.ts
import CustomButtons from './CustomButtons.vue'
export default {
  title: 'Button'
}

const Template = (args, { argTypes }) => ({
  props: Object.keys(argTypes),
  components: { CustomButtons },
  template: `<custom-buttons v-bind="$props" /> `
})

export const FirstTime = Template.bind({})
FirstTime.args = {
  isVisibleTutorButton: true,
  colorMode: 'light',
}

export const Normal = Template.bind({})
Normal.args = {
  isVisibleTutorButton: false,
  colorMode: 'light',
}

image.png

Controls を手動で定義する

ただし、自動的に作ってくれるとはいえ Storybook 上でVueの props の形式に対して不適切なコントロールが作られることがあります。

例えばボタンをダークモード向けに暗くする設定を colorMode というpropに仕込むとします。

is-light, is-dark, is-black の 3 種類の値をとるpropの場合でも、Controls addon のデフォルトではテキストボックスが表示されてしまいます(先ほどの例も colorMode がテキストボックスになってます)

これをテキストボックスではなく 3 つの選択肢に制限したい時や、テキストボックスよりももっと良いコントロールを使いたい場合は Story ファイルに Controls addon の設定を argTypes に記述します。

コントロールの種類は types で指定します。 こちら に載っています。

components/buttons.stories.ts
import CustomButtons from './CustomButtons.vue'

export default {
  title: 'Button',
  argTypes: {
    colorMode: {
      control: {
        type: 'inline-radio',
        options: ['light', 'dark', 'black']
      }
    }
  }
}

...

image.png

Controls を使用する想定でない Story の場合

Controls addon を導入した場合、Controls を表示できる設定になっていないと次のメッセージが表示されます。

This story is not configured to handle controls. Learn how to add controls »

image.png

後で Controls を使う想定であればそのままにしておきますが、Controls を使いたくない Story の場合は Story ファイルで Template を bind した後 hideNoControlsWarning: true を parameters に設定することでこの表示を消すことができます。

(Controls タブを消すことはできません)

components/simple-buttons.stories.ts
import SimpleButton from "./SimpleButton.vue";

export default {
  title: "Simple Button",
};

const SimpleTemplate = (args, { argTypes }) => ({
  props: Object.keys(argTypes),
  components: { SimpleButton },
  template: `<simple-button /> `,
});

export const ControlsDisabled = SimpleTemplate.bind({});
ControlsDisabled.parameters = {
  controls: { hideNoControlsWarning: true },
};

image.png

また、 export default の部分で argTypes が設定されていると、この設定にかかわらず Controls が表示されます。

Story 単位で Controls を完全に無効にする設定ではなく、あくまでも Controls を使用する想定ではない Story であることを設定するものです。

参考

How to document components
https://storybook.js.org/docs/react/writing-docs/introduction

[アドオン編] Essential addons について| Vue と CSF によるモダンな Storybook 6 のはじめかた
https://zenn.dev/sa2knight/books/aca5d5e021dd10262bb9/viewer/1c420c

Storybook for Vue の Docs Addon の裏側: コンポーネントの自動ドキュメント生成について - log.pocka.io
https://log.pocka.io/posts/vue-docgen-loader/

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
3