LoginSignup
3

More than 1 year has 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
What you can do with signing up
3