Angular + Storybook でコンポーネントガイドを作ろう


Storybook v3.3.0でAngular対応がリリースされたので、さっそく試してみました1

Angular CLIプロジェクトへの導入方法

Angular CLIで作成したプロジェクトに導入するのであれば、コマンド一発です。

ng new my-ng-storybook-prj
cd my-ng-storybook-prj
yarn add @storybook/cli --dev
yarn run getstorybook

これで完了です。
package.jsonに storybookbuild-storybook scriptが定義されているので、こいつを叩くだけです。

yarn run storybook

localhost:6006 でstorybookが起動します。

ちゃんとHMRにも対応しています。

もちろんReactやVue版と同じく、yarn run build-storybook を実行することで、storybookを静的なサイトとして出力することも可能です。

現状、 getstorybook で作成される設定では、storyを .js で記述するようになっていますが、TypeScriptに変えたい!という場合はWebpackのRequire Contextを修正しておくとよいです。
どう考えても.tsの方が良いに決まっているので、PR出して修正済み

.storybook/config.js
// automatically import all files ending in *.stories.js
const req = require.context('../stories', true, /.stories.ts$/); //ここね
function loadStories() {
  req.keys().forEach((filename) => req(filename));
}

configure(loadStories, module);

Angular版Storyの記述方法

Storyを書く際は、@storybook/angularstoriesOf を使います。

storiesOf('Awesome Button', module).add('to Storybook', () => ({
  component: AwesomeButtonComponent,
  props: {
    name: 'Button',
    myClick: action('Clicked!'),
  },
}));

対象のComponentと、propsを指定するのが基本です。
propsは、対象が @Input であれば入力として、 @Output であればEventEmitterが発火したタイミングで呼び出されるコールバック関数を指定します。

単純なComponentであればこれでよいのですが、そのComponentを動作させるためのモジュール設定が必要な場合は、さらに moduleMetadata を指定できます。

storiesOf('Awesome Button', module).add('to Storybook', () => ({
    component: AwesomeButtonComponent,
    props: { /*...*/ },
    moduleMetadata: {
      imports: [],
      schemas: [],
      declarations: [],
      providers: []
    }
  }))

Angularの単体テストを書いたことがある人であれば、下記のようなテスト用モジュール設定には馴染みがあると思います。Storybookの moduleMetadata も役割は同じです。

TestBed.configureTestingModule({
  declarations: [ BannerComponent ],
  providers: [
    { provide: ComponentFixtureAutoDetect, useValue: true }
  ]
})

Storyの書き方の詳細については、 https://github.com/storybooks/storybook/tree/master/examples/angular-cli/src/stories に色々とサンプルがあるので、こちらも参考にするとよいでしょう。

css関係の諸注意

現状のStorybookはAngular CLIには特に依存しない作りになっています。このため、Componentのstyle周りについては、多少のworkaroundを施す必要があります2

.storybook/webpack.config.js
const genDefaultConfig = require('@storybook/angular/dist/server/config/defaults/webpack.config.js');

module.exports = (baseConfig, env) => {
  const config = genDefaultConfig(baseConfig, env);

  // Overwrite .css rule
  const cssRule = config.module.rules.find(rule => rule.test && rule.test.toString() === '/\\.css$/');
  if (cssRule) {
    cssRule.exclude = /\.component\.css$/;
  }

  // Add .scss rule
  config.module.rules.unshift({
    test: /\.scss$/,
    loaders: ['raw-loader', 'sass-loader'],
  });

  return config;
};

本当はAngular CLIのmodelを参照して、同じruleを適用するようにすべきなのでしょうが、ちょっと複雑になりそうなので今後の宿題にします。

おわりに

さくっとStorybook + Angularの使い方を見てきましたが如何でしょうか。導入するのは本当に簡単なので、ぜひAngularでComponentガイドを作ってみてほしいです。

あと、tsuyoshiwada/storybook-chrome-screenshot のAngular版があれば、CIでStorybookについてキャプチャをとって、reg-suit で画像スナップショットテストを回す、みたいなこともできそうなので、色々と夢が広がりますね。
折角なので、自分でPR作ったので、mergeしてもらいました。これで Angular + Storybook + Screenshot + Snapshot test という構成のプロジェクトがAngular CLIベース作れるようになったので、 Quramy/angular-sss-demo にデモレポジトリを作っています。

それでは、また。


  1. Angularアドカレのネタにしようかな、とも思ってたのですが、若干タイミングがズレてたのでCDK優先にしました 

  2. https://storybook.js.org/basics/guide-angular/#configure-style-rules