この記事は Angular + Storybook でコンポーネントガイドを作ろう を参考に、インストールのコマンドや設定をメモしたものです。
Storybook
https://storybook.js.org/
環境
- macOS High Sierra
- yarn v1.6
- node v8.10
- @storybook/angular v3.4.3
- @angular/cli v1.7.4
Storybook のインストール
npm / yarn どちらでもインストールできます。両方試した結果 yarn のほうがスムーズだったため、こちらを使う事にしました。
私の環境には yarn がインストールされていなかったため homebrew で入れました。
$ brew install yarn
一発目はエラーで失敗したのでリトライ。
xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools),
missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
Error: Failure while executing: git config --local --replace-all homebrew.private true
$ xcode-select --install
$ xcode-select: note: install requested for command line developer tools
$ brew install yarn
$ which yarn
/usr/local/bin/yarn
まず Angular のプロジェクトを作成します。
$ ng new learn-storybook
$ cd learn-storybook
Storybook のコマンドラインツールをインストールします。
$ yarn add @storybook/cli
【参考】 node 9系だと
SyntaxError: Unexpected token ( in JSON at position 0
というエラーでコマンドが完了しなかったので、node 8 (npm 5.6) を使う事にしました。
Storybook の Angular プラグイン的なものをインストールします。
$ yarn add @storybook/angular babel-core
Storybook を作成します。
$ yarn run getstorybook
# デモページで使われるアドオンもインストール
$ yarn add @storybook/addon-notes
$ yarn add @storybook/addon-links
$ yarn add @storybook/addon-actions
package.json を開くと storybook / build-storybook という2つのスクリプトが追加されています。Storybook の設定は .storybook ディレクトリに配置されるため、このディレクトリが読み込まれるよう設定します。
- "storybook": "start-storybook -p 6006",
+ "storybook": "start-storybook -p 9001 -c .storybook",
"build-storybook": "build-storybook"
Storybook のファイルが ng serve の実行を妨げないように設定を追加します。
// src/tsconfig.app.json
{
+ "exclude": [
+ "stories",
+ "**/*.stories.ts"
+ ]
}
これでインストールは完了です。
Storybook を起動してデモページを開いてみましょう。
$ yarn run storybook
ブラウザで http://localhost:9001 を開き Welcome to storybook が表示されていれば成功です。表示が確認できたら Ctrl + C で終了します。
Storybook にコンポーネントを追加
デモページにはあらかじめいくつかのコンポーネントが表示されています。自分で作ったコンポーネントを追加してみましょう。
まず適当なコンポーネントを作成します。
$ ng generate hello-button
// src/app/hello-button/hello-button.component.ts
import {
+ Input,
Component,
+ EventEmitter,
OnInit,
+ Output,
} from '@angular/core';
@Component({
selector: 'app-hello-button',
templateUrl: './hello-button.component.html',
styleUrls: ['./hello-button.component.css']
})
export class HelloButtonComponent implements OnInit {
+ @Input() name: string;
+ @Output() hello = new EventEmitter<string>();
constructor() { }
ngOnInit() {
}
+ click() {
+ this.hello.emit(`Hello ${this.name}`);
+ }
}
// src/app/hello-button/hello-button.component.html
- <p>
- hello-button works!
- </p>
+ <button type="button" (click)="click()">
+ Hello {{name}}!
+ </button>
こんな感じのコンポーネントです。
<hello-button name="foo"></hello-button>
-
<button>Hello foo!</button>
のように展開される - クリックすると hello イベントが発火し
Hello foo!
を受け取る事ができる
ストーリーの作成とコンポーネント追加
Storybook のソースコードは index.stories.ts に記述されています。
ここに hello-button コンポーネントを追加してみます。
// src/stories/index.stories.ts
+ import { HelloButtonComponent } from '../app/hello-button/hello-button.component';
+ storiesOf('Hello Button', module)
+ .add('with name', () => ({
+ component: HelloButtonComponent,
+ props: {
+ name: 'foo',
+ },
+ }))
;
storiesOf は Storybook に新しいストーリー Hello Button
を追加し、add はストーリーにコンポーネントを追加します。props には @Input
に値を渡すための key/value を記述します。
再び Storybook を起動してみましょう。
Hello Button という見出しが追加され、展開すると with name というタイトルが現れるはずです。
$ yarn run storybook
コンポーネントのイベントを拾う
hello-button コンポーネントは hello イベントを発火する機能を持っています。
Storybook で発火したイベントを拾ってみましょう。
// src/stories/index.stories.ts
storiesOf('Hello Button', module)
.add('with name', () => ({
...
}))
+ .add('with action', () => ({
+ component: HelloButtonComponent,
+ props: {
+ name: 'bar',
+ hello: action('clicked!')
+ },
+ }))
ソースコードを保存するとブラウザが自動でリロードし、Hello Button の見出しの下に with action というタイトルが現れます。
ボタンをクリックするとページ下部の ACTION LOGGER タブに clicked! が表示されます。
ACTION LOGGER への出力
Storybook にはいろいろなアドオンが存在します。Angular のデモページはデフォルトで actions / links / notes の3つのアドオンが読み込まれています。
// .storybook/addons.js
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';
import '@storybook/addon-notes/register';
// src/stories/index.stories.ts
import { withNotes } from '@storybook/addon-notes';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
actions アドオンをインポートすると action 関数を利用できます。
action 関数は受け取った引数を ACTION LOGGER タブに出力します。
さきほどソースコードに追加したこの部分ですね。
props: {
hello: action('clicked!')
}
NOTE への出力
notes アドオンを利用すると NOTE タブに任意の HTML を出力する事ができます。
// src/stories/index.stories.ts
storiesOf('Hello Button', module)
.add('with name', () => ({
...
}))
+ .add('with note', withNotes(`
+ <h3>Notes</h3>
+ <span>sample text</span>
+ `)(() => ({
+ component: HelloButtonComponent,
+ props: {
+ name: 'hoge',
+ },
+ })
+ ))
アドオンの追加
デフォルト以外のアドオンを読み込んでみましょう。
Knobs を利用すると Storybook 上で入力したテキストをリアルタイムにコンポーネントに反映させる事ができます。
$ yarn add @storybook/addon-knobs
// .storybook/addons.js
+ import '@storybook/addon-knobs/register';
// src/stories/index.stories.ts
+ import { text, withKnobs } from '@storybook/addon-knobs/angular';
storiesOf('Hello Button', module)
+ .addDecorator(withKnobs)
.add('with name', () => ({
...
}))
.add('with name', () => ({
component: HelloButtonComponent,
props: {
- name: 'foo',
+ name: text('name', 'foo')
},
}))
ブラウザをリロードすると追加の Knobs アドオンが有効になります。
他にもいろいろなアドオンが Storybook 公式ページで紹介されています。
Storybook Addon Gallery
https://storybook.js.org/addons/addon-gallery/
静的ページの出力
build-storybook コマンドを使うと Storybook を静的ページとして出力する事ができます。出力先はルートディレクトリ直下の storybook-static です。
$ yarn run build-storybook
$ open storybook-static/index.html
ディレクトリ構成
Storybook は既存の Angular プロジェクトに後付けで追加する事もできます。Storybook によって追加されるディレクトリ・ファイルは以下に示す ●印 の部分です。
├── .storybook ●
│ ├── addons.js ● // アドオン
│ └── config.js ● // Storybook の読み込み
├── node_modules
├── package.json
├── src
│ ├── app
│ ├── stories ●
│ │ └── index.stories.ts ● // Storybook を記述するファイル
├── storybook-static ● // build-storybook で出力される静的ページ
│ ├── favicon.ico ●
│ ├── iframe.html ●
│ ├── index.html ●
│ └── static ●
おわり
複数プロジェクトで利用するオリジナルのコンポーネントを開発しているのですが、使い方を説明するために demo.html にサンプルを載せて GitHub wiki にドキュメントを書いて...という事をせっせと行っていました。これを便利に代行してくれるのが Storybook ではないかと思います。
まだ Angular 未対応のアドオンもちらほらあるようですが、demo.html みたいな泥臭い作業をせっせとこなすより、新しいものをワクワクしながら使いたいですね!