この記事は「株式会社オープンストリーム "小ネタ" 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
したときに次のアドオンが自動的に導入されています。
...
"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
に使いたいアドオンを読み込みます。
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
の値をセットします。
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
で認識できる形に注釈を入れるのですが…
<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 に説明を挿入できませんでした。
また、props
, methods
などに付けたアノテーション以外に説明文を追加する方法は見つかりませんでした。
Controls addon
ドキュメントでだいぶ出鼻をくじかれた形になりますが、他のアドオンを使ってみましょう。
こちらも @storybook/addon-essentials
を導入すると使えるようになります。
Story でコンポーネントを呼び出すときに、コンポーネントに props があってそれが Story から args として渡されていれば、自動的にそれを Storybook で変更できるようにするアドオンになります。
例としてVueコンポーネントにはBoolean, Stringの props
を2つ追加します。
<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が表示されます。
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',
}
Controls を手動で定義する
ただし、自動的に作ってくれるとはいえ Storybook 上でVueの props
の形式に対して不適切なコントロールが作られることがあります。
例えばボタンをダークモード向けに暗くする設定を colorMode
というpropに仕込むとします。
is-light
, is-dark
, is-black
の 3 種類の値をとるpropの場合でも、Controls addon のデフォルトではテキストボックスが表示されてしまいます(先ほどの例も colorMode
がテキストボックスになってます)
これをテキストボックスではなく 3 つの選択肢に制限したい時や、テキストボックスよりももっと良いコントロールを使いたい場合は Story ファイルに Controls addon の設定を argTypes
に記述します。
コントロールの種類は types
で指定します。 こちら に載っています。
import CustomButtons from './CustomButtons.vue'
export default {
title: 'Button',
argTypes: {
colorMode: {
control: {
type: 'inline-radio',
options: ['light', 'dark', 'black']
}
}
}
}
...
Controls を使用する想定でない Story の場合
Controls addon を導入した場合、Controls を表示できる設定になっていないと次のメッセージが表示されます。
This story is not configured to handle controls. Learn how to add controls »
後で Controls を使う想定であればそのままにしておきますが、Controls を使いたくない Story の場合は Story ファイルで Template を bind した後 hideNoControlsWarning: true
を parameters に設定することでこの表示を消すことができます。
(Controls タブを消すことはできません)
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 },
};
また、 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/