lit-html を Storybook 上で表示させる

乱立しているフロントエンド界隈の共通ライブラリの作成について、 Web Components による問題解決を夢見て、最近は lit-html を触っています。

以前の記事で取り上げましたが、 React などのライブラリはたくさんのツールが存在しています。
Custom Elements (Web Components) については、必要ないからかツールはあまりありませんが、 Storybook は役に立つツールとなるのではないかと思います。

Custom Elements は v1 、 Storybook は v3.4.1 で確認しました。

サンプルプロジェクトはこちら

lit-html 専用の Storybook 機能は存在していません。代わりに、 Custom Elements を使うという点で似ている Polymer 用のパッケージを使ってみます。(上手くストーリーを記述すれば React 用のパッケージでも動くのではないかと思います。)

まずは、必要なパッケージをインストールします。
Polymer 関連のパッケージは使うわけではありませんが、 Polymer 用のパッケージの依存関係として入れる必要はあります。

$ yarn add -D @storybook/polymer babel-core webpack polymer-webpack-loader babel-runtime
$ yarn add lit-html

次に、 Storybook の設定ファイルを作成します。
‘.storybook/config.js’ に以下のように記述します。これは ‘stories/index.js’ だけをストーリーとして読み込むようにしただけの簡単な設定です。

.storybook/config.jslink
1
2
3
4
5
6
7
import { configure } from '@storybook/polymer';

function loadStories() {
require('../stories/index.js');
}

configure(loadStories, module);

自作の Custom Element を表示させる前に、試しに通常の <a> タグをストーリーとして表示させてみましょう。

stories/index.js
import {storiesOf} from '@storybook/polymer';

storiesOf('anchor', module).add(
'simple',
() => '<a href="http://example.com/">Foo</a>'
)

‘package.json’ の “scripts” に "storybook": "start-storybook -p 9001" を追加して yarn run storybook すると、これが Storybook 上の Polymer のお陰かはともかくとして、 Storybook のページにはリンクテキストとして表示されたかと思います。

ここから lit-html (というよりも Custom Elements そのもの)をここに表示させるには、 ストーリーの中で importrequire などをして Custom Elements を登録する処理を実行させた後で、先ほどの <a> タグのように標示させたいタグを記述するだけです。
この記事では、サンプルとして <lhs-button> カスタム要素を ‘src/lhs-button.js’ に定義し、使っています。

stories/index.js
import '../src/lhs-button';

storiesOf('lhs-button', module).add(
'simple',
() => '<lhs-button>a</lhs-button>'
).add(
'wide with stylesheet',
() => '<style>lhs-button { width: 100px; }</style><lhs-button>a</lhs-button>'
).

これで、 “lhs-button” の下に “simple” と “wide with stylesheet” の 2 つの表示パターンが加えられ、内容を確認できます。

もし複雑な処理を加えたものを表示させたい場合は、以下のような書き方で実現できます。

stories/index.js
import {html, render} from 'lit-html';

storiesOf('Dynamic', module).add(
'wrap',
() => {
const root = document.createElement('div');
render(html`<lhs-button link="http://google.com/">a</lhs-button>`, root);
const button = root.querySelector("lhs-button");
// do something here.
return root;
}
);