Storybook で作る WebComponents UI カタログ

最初（自分用ってことも兼ねて）WebComponents のエコシステムをまとめてみようと思っていたのですが、正直 [Polymer Project](https://www.polymer-project.org/) を見ればいいじゃないか!!と思ったので、急遽内容を変更しました :frowning2:

個人的に WebComponents は Web 標準としてもっと利用されていてもいいはずなのに...と思ってはいるのですが、先日の [HTML5 Conference 2018](https://events.html5j.org/conference/2018/11/) で aggre さんのセッションを聞いていた際の、「customElements.define したことある人〜?」 の質問で会場に居た方でほとんど手が上がっていなかったことに、ちょっぴり切ない気持ちになってしまったことを思い出しました。

Polymer Project の[日本語化のプロジェクト](https://polymer-project-org-dot-polymer-japan.appspot.com/)も行われているようですし（自分も手伝わせてもらえるならちょっと :writing_hand: お手伝いしたい!! ）、WebComponents の Advent Calender ももっと盛り上がりを見せて欲しい!! との想いを込めて投稿させていただきます。

## Storybook

<img width="300" alt="Storybookのロゴ" src="https://qiita-image-store.s3.amazonaws.com/0/166794/e36d04f7-77d8-3aca-8294-91be93caf3aa.png">


[Storybook](https://storybook.js.org/) はコンポーネントのUI コンポーネントカタログ（スタイルガイド)を生成できる、豊富な機能を備えたフレームワークです。多人数で開発を行うプロジェクトでは導入されているケースも多いのではないでしょうか?
先日 v4 がリリースされ、気がつけばかなりの種類のフレームワークやライブラリに対応するようになりました。
もともと React のエコシステムの一環として担っていたものがかなり大きなプロジェクトになりました。

[Quick Start Guide](https://storybook.js.org/basics/quick-start-guide/) には記載はないのですが、実は（実験段階の） Polymer プラグインが存在していたりします。
今回はそのプラグインを利用して、Storybook に lit-element で作成した WebComponents を載せてみたいと思います。

今回のソースコードですが、簡潔に[こちら](https://github.com/doutori/storybook-webcomponents/tree/master)においております。


## Storybook のプロジェクト作成

まず、公式のドキュメント通りに作成していきます。
`type` オプションに `polymer` を指定します。

```
npx -p @storybook/cli sb init --type polymer
```

プロジェクトを作成したのちに `npm run storybook` で Storybook を起動するとわかるのですが、 `lit-html` がない!! と怒られます。
今回は `lit-element` でコンポーネントを作成するので一緒に入れておきます。

```
npm i lit-html @polymer/lit-element -D
```

これでもう storybook の準備が整いました。

## コンポーネントの作成

シンプルに、ボタンを押したら値のインクリメントを行うコンポーネントを作ってみます。

```js:countup-button.js
import { LitElement, html } from '@polymer/lit-element';

class CountupButton extends LitElement {
  static get properties() {
    return {
      val: { type: Number },
      increment: { type: Number },
    };
  }

  constructor() {
    super();
    this.increment = 1;
    this.val = 0;
  }

  render() {
    return html`
        <style> button{
            -webkit-appearance: none;
            padding: 5px 20px;
            border: none;
            background: #9f0;
        }
        </style>
        <button @click="${this.clickHandler}">+${this.increment}</button>
        <p>${this.val}</p>`;
  }

  clickHandler() {
    this.val += this.increment
  }
}

customElements.define('countup-button', CountupButton);
```

## Storybook にコンポーネントのカタログを追加する

index.stories.js にカタログを登録していきます。
だいたい1つのカタログに対して、1つのコンポーネントを追加していくとは思うのですが、便宜的に2つ並べています。

```js:stories/index.story.js
import { storiesOf } from '@storybook/polymer';
import '../countup-button'

storiesOf('countup-button', module).add(
  'default',
  () => `<countup-button></countup-button>
         <countup-button increment="3" />`
);
```

Storybook 上でコンポーネントの動作が確認できますね!
<img width="600" alt="CountUp コンポーネントのサンプル" src="https://qiita-image-store.s3.amazonaws.com/0/166794/ad7e778c-fd51-f528-8f86-b460e0c951b9.gif">


## Storybook の Addon

便利な Addon があり、 Storybook の機能を拡張させることができます。
どのフレームワークがどの Addon に対応しているかの [一覧](https://github.com/storybooks/storybook/blob/master/ADDONS_SUPPORT.md) があるので、ここから利用できる Addon を探していきます。

Addon を利用する場合は、.storybook ディレクトリ直下の `addons.js` に、対応する Addon の `register` を import させます。

```js:.storybook/addons.js
import '@storybook/addon-knobs/register';
```

#### storybook-addon-knobs

今回、例の1つとして `knobs` を導入してみます。
Props で渡す Value の値を動的に書き換えることができ、 Storybook 上で簡単に Props の検証を行うことができます。

```js:stories/index.story.js
import { storiesOf } from '@storybook/polymer';
import { withKnobs, number } from '@storybook/addon-knobs';

import '../countup-button'

storiesOf('countup-button', module)
  .addDecorator(withKnobs) // Decorator追加
  .add('add props',
  () => {
    const num = number('increment value', 5);
    return `<countup-button increment="${num}" />`
  }
);
```

Props の値を書き換えると、それに伴ってインクリメントがされるようになりました。
`KNOBS` タブから自由に値を書き換えることができます。便利ですね! :relaxed: 

![knobs.gif](https://qiita-image-store.s3.amazonaws.com/0/166794/adbdf0e9-3a9f-2e33-2921-fe19cf89133b.gif)

これ以外にも様々な Addon が開発されています。
プロジェクトにあった様々な Addon を導入すると効率があがることでしょう。

## さいごに

Storybook は一昔前に比べかなり機能の幅が増え、柔軟なカタログを作ることが可能になりました。取り入れていないプロジェクトは是非取り入れてみてはどうでしょうか?
また、 Learn Storybook の [翻訳コミッター](https://www.learnstorybook.com/react/en/contribute/) などコントリビュートを募集しているようなので、ちょっとこの正月とか気が向いたらやってみようかな??