Storybookの新しいアドオン addon-docs がいい感じ

[Storybook](https://storybook.js.org/)のv5.2 で入る予定の新しいアドオン `addon-docs` を試してみたら、なかなかいい感じだったので布教記事を書きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25588/5a734e82-e1ae-af4f-6c5f-74df31e08d2c.png)

実物はGitHub Pagesで公開しています。
https://y-takey.github.io/storybook_docs_example/
(リポジトリ: https://github.com/y-takey/storybook_docs_example )

`addon-docs` は (既存のアドオンである) `addon-info` をほぼ置き換える新しいアドオンで、従来のストーリー形式に加えて [MDX](https://mdxjs.com/)も使えることが一番の特徴だと思います。上の画像のようなドキュメントを手軽に書くことができます。
MDXを使えることが理由で、 [Styleguidist](https://react-styleguidist.js.org/) や [Docz](https://www.docz.site/) を選んだ人もいるんじゃないかと思いますが、これからは Storybook も負けていません。

---

※ 本記事は `Storybook` および `addon-docs` のバージョン 5.2.0-alpha.29 を元にしており、最新バージョンでは設定や使用方法が変わっている可能性がありますのでご注意下さい。

# 設定方法など

Storybook自体のインストール・設定は終わっている前提で、`addon-docs` のインストール・設定等は次のとおりです。

## Storybook 本体と既存アドオンのアップグレード

2019/06/23 時点では Storybook@5.2 はまだ正式リリースされていないため、`debug` を指定してアップグレードします。

```shell
yarn upgrade @storybook/react@debug @storybook/addons@debug @storybook/addon-actions@debug
```

他にも使用している公式アドオンがあれば同様にアップグレードしてください。

## `addon-docs` 等のインストール

```shell
yarn add -D @storybook/addon-docs@debug @storybook/source-loader@debug
```

`addon-docs` の他に、ソースも表示できるようにするために `@storybook/source-loader` もインストールしています。
ソース表示が不要な場合はインストールしなくても大丈夫です。

## `addon-docs` の設定など

```js:.storybook/config.js
import { load } from '@storybook/react';

load(require.context('../src', true, /\.stories\.[tj]sx?$/), module);
load(require.context('../src', true, /\.stories\.mdx$/), module);
```

ロード対象ファイルの正規表現は適宜変えて下さい。この例では TypeScript , mdx をロードするようにしています。
今回はMDXのみとするので書いていませんが、以下の設定を上の config.js に追加すると、従来のストーリにも自動で（ある程度よしなに）ドキュメントを追加してくれます。

```js:.storybook/config.js
import { addParameters } from '@storybook/react'
import { DocsPage } from '@storybook/addon-docs/blocks';

addParameters({
  docs: DocsPage,
});
```

```js:.storybook/presets.js
module.exports = [
  '@storybook/addon-docs/react/preset',
];
```

```js:.storybook/webpack.config
const path = require('path');

module.exports = ({ config }) => {
  config.module.rules.push({
    test: /\.stories\.[tj]sx?$/,
    use: [
      {
        loader: require.resolve('@storybook/source-loader'),
        options: { injectParameters: true },
      },
    ],
    include: [path.resolve(__dirname, '../src')],
    enforce: 'pre',
  }, {
    test: /\.(ts|tsx)$/,
    use: [
      {
        loader: require.resolve('awesome-typescript-loader'),
      },
      {
        loader: require.resolve('react-docgen-typescript-loader'),
      },
    ],
    include: [path.resolve(__dirname, '../src')],
  });
  config.resolve.extensions.push('.ts', '.tsx');

  return config;
};
```

ソースコードを表示するには `@storybook/source-loader` が必要となるため、 webpack.config に設定を追加します。(module.rules.push の1つ目の引数)
また TypeScript のソースコードから props のテーブルを表示するには `react-docgen-typescript-loader` が必要となるため、こちらもwebpack.config に設定を追加します。(module.rules.push の2つ目の引数)
ここでは `awesome-typescript-loader` を使っていますが、 `ts-loader` でも大丈夫です。ちなみに、TypeScriptまわりの設定については、 `@storybook/preset-typescript` を使えばwebpackの設定を自分でやる必要はありません。(内部では `ts-loader`, `react-docgen-typescript-loader` を追加してくます)

また、TypeScriptの他にも [prop-types](https://github.com/facebook/prop-types), [flow](https://flow.org/)でも props のテーブルを表示してくれます。(それぞれの対応する loader の設定が必要)

# MDX

ドキュメントは以下のような感じで書きます。(MDXのハイライトがないみたいで、ちょっと見にくいかもしれません。)

```jsx:Button.stories.mdx
import { action } from "@storybook/addon-actions";
import { Story, Meta, Preview, Props, Source, Description } from "@storybook/addon-docs/blocks";

import Button from "./Button";

<Meta title="hoge" parameters={{ component: Button }} />

# Button

何の変哲もない、ただのボタン。

<Source code={`import Button from "~/components/Button";`} />

<Preview withSource="open">
  <Story name="basic" >
    <Button label="this is src" onClick={action('clicked')} />
  </Story>
</Preview>

<Props of="." />
```

`@storybook/addon-docs/blocks` から import できるコンポーネントは以下のものがあります。

## Meta

### Props

| プロパティ   | 必須 | 説明  |
|:-----------------|:------------------|:----------------|
| title  | 必須 | サイドバーに表示されるタイトル。"&brvbar;"(縦棒)でセクショニングできて、"/"で階層化できる。(たぶん addon-info と同じ記法) |
| decorators | 任意(デフォはなし) | `addon-info` と同様なため詳細はそちらを参照。指定する場合は1つでもdecoratorの配列にする必要がある。 |
| parameters | 任意(デフォはなし) | `{ component, notes, info }` のオブジェクトでいずれも任意要素。後述の Props, Description 等のコンポーネントから参照される。  |

### 例

```jsx
<Meta
  title="セクションA|Components/Button"
  parameters={{
    component: Button,
    notes: "This is notes;",
    info: "This is info.",
  }}
/>
```

## Preview

### Props

| プロパティ   | 必須 | 説明  |
|:-----------------|:------------------|:----------------|
| withSource | 任意(デフォは"closed") | ソースも一緒に表示するかの設定で、"open" | "closed" | "none" のいずれかを指定可。 |

### 例

```jsx
<Preview withSource="open">
  <Story name="basic" >
    <Button label="this is src" onClick={action('clicked')} />
  </Story>
</Preview>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25588/94428c81-d6c6-ca72-8436-3f7a472b9eb7.png)

## Story

### Props

| プロパティ   | 必須 | 説明  |
|:-----------------|:------------------|:----------------|
| id | 任意 | 他の箇所でidを指定して参照できるようになる。 |
| name | 任意 | サイドメニューに表示される名称 |
| height | 任意 | 表示領域の高さ |

### 例

```jsx
<Story name="basic" >
  <Button label="this is src" onClick={action('clicked')} />
</Story>
```


## Source

### Props

| プロパティ   | 必須 | 説明  |
|:-----------------|:------------------|:----------------|
| code | 必須 | 表示するソースコード |
| language | 任意(デフォは"jsx") | ハイライト用に使われる言語。 |

### 例

```jsx
<Source code={`import Button from "~/components/Button";`} />
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25588/6e0ba2b7-042d-8d77-fd9a-4ea198b85a67.png)


## Props

### Props

| プロパティ   | 必須 | 説明  |
|:-----------------|:------------------|:----------------|
| of | 必須 | 対象のコンポーネント。"." でカレントコンポーネント(Metaコンポーネントの `parameters.component` )  |

### 例

```jsx
<Props of={Button} />
// <Props of="." /> と書いても同じ
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25588/f0faff23-d898-6556-c74f-08cadfc61d39.png)


## Description

### Props

| プロパティ   | 必須 | 説明  |
|:-----------|:---------|:---------|
| of | 任意 | 対象のコンポーネント。"." でカレントコンポーネント。 |
| markdown | 任意 | 表示するマークダウンテキスト。 
| type | 任意 | "info", "notes", "docgen", "auto" のいずれかを指定可能。"info", "notes" は Meta コンポーネントの parameters プロパティで指定した 同名の要素を使用する。"docgen" は各種のdocgen loader によって注入される 対象コンポーネントの `__docgenInfo.description` を使用する。 |

markdown が指定されている場合には type は無視される。

### 例

```jsx
<Description markdown="**BOLD**" />

<Description type="info" />
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25588/ee1b9ccf-419a-dc9d-8dd2-af65065ec3e0.png)

# その他

Storybook は React 以外にも Vue や Angular 等など色々サポートしていますが、 `addon-docs` が対応しているのは現時点では React, Vue だけです。( Vue は一部未実装の模様)
Angularも今後対応予定のようです。


---

説明は以上です。まだまだドキュメントがほとんどないため `addon-docs` のソースコードも読みながら調べた内容をまとめてみました。
正式リリースされる際にはきっとドキュメントも整備されていると思います。