LoginSignup
4

More than 3 years have passed since last update.

posted at

updated at

AngularでStorybookを使う

はじめに

AngularでAtomic Designを実践してみようと思い、Storybookを使ってみました。
いろいろハマったので書き残しておこうと思います。
導入時には公式ドキュメントとこちらの記事が参考になりました。
https://qiita.com/Quramy/items/463ed80f0d6fcd9e3939

環境

試した環境です。

  • Angular: 6.0.3
  • storybook: 3.4.8

導入

下記コマンドを実行すればOKです。
プロジェクト内にstorybookで使うファイルが生成されているはずです。

npm i -g @storybook/cli
cd {対象プロジェクトのディレクトリ}
getstorybook

getstorybokで行われてることは下記に書かれています。
https://storybook.js.org/basics/guide-angular/#docs-content

注意
src/tsconfig.app.jsonのexcludeにstorybookに関するファイルを追加しないとangularのビルド時にエラーになります。

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "module": "es2015",
    "types": []
  },
  "exclude": [
    "src/test.ts",
    "**/*.spec.ts",
    "stories",            // 追加
    "**/**.stories.ts"    // 追加
  ]
}

storybookを起動する

下記コマンドのどちらかでstorybookが起動します。ブラウザでlocalhost:6006にアクセスすると確認できます。

yarn storybook
# or
npm run storybook

Cannot read property 'compilation' of undefined が発生した場合

Angular6だと、StoryBook 4.0-alphaを使わないといけないらしい。

Module build failed: Error: Cannot find module 'babel-core' が発生した場合

babel-coreをインストールすると解決します。

yarn add babel-core --dev

componentの依存関係を解決する

例えば、itemというcomponentでpriceというcomponentを利用していた場合、story上で依存関係を解決しないといけません。
storybookはangularのmoduleファイルをみないので、storyにitem componentの追加だけを行うとpriceというエレメントなんて知らないよと怒られてしまいます。
storyで依存を解決する方法は下記です。moduleファイルと似ているので、すんなり書けると思います。 

const itemStories = storiesOf('{story名}', module);
itemStories.addDecorator(
  moduleMetadata({
    declarations: [
       PriceComponent
    ],
    imports: [],
    providers: []
  })
);

Global styleとして読み込む方法

各ComponentでGlobalに定義しているstyleを参照したくなるケースがあるかと思います。
angularではangular.jsonに追加すればいいのですが、storybookはangular.jsonとは無縁なので、自分でGlobalで読み込めるようにしないといけません。

scssの場合は、storyファイル(src/stories/*.stories.ts)で下記のように対象ファイルをimportします。

import '!style-loader!css-loader!sass-loader!../styles.scss';

cssの場合はscssのsass-loaderが不要になるだけです。

import '!style-loader!css-loader!../styles.css';

knobsの利用

knobsはcomponentのInputになる値をstorybook上から動的に変更できるプラグインです。
デフォルトでは入っていないようなので、自分で追加します。

yarn add @storybook/addon-knobs --dev

.storybook/addons.jsに追加


import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';
import '@storybook/addon-notes/register';
import '@storybook/addon-knobs/register';  // 追加

storyにknobsを追加します。angularの場合はangular用のknobsをimportする必要があるみたいです。


import { withKnobs, text, number, color, object } from '@storybook/addon-knobs/angular';

const itemStories = storiesOf('{story名}', module);
txtContainerStories.addDecorator(withKnobs);

import時にsrcを起点としたパスでimportする

angularでscssやtsファイルで他のファイルをimportする際には、どのファイルからも下記のようにsrcを起点としたパスで書くことができます。(tsconfigの設定にもよる)


// e.g.
import { ButtonComponent } from 'src/app/shared/button/button.component.ts';

// e.g.
@import 'src/variables/colors.scss';

しかし、storybookでは、パスを解決してくれないので、エラーになってしまいます。
これも自分でどうにかしないといけないです。
やり方としては、storybookのwebpackの設定を自分で拡張するというやり方です。

  1. .storybook配下にwebpack.config.jsファイルを作成します。

    webpack.config.js
    const genDefaultConfig = require('@storybook/angular/dist/server/config/defaults/webpack.config.js');
    const path = require('path');
    
    module.exports = (baseConfig, env) => {
      const config = genDefaultConfig(baseConfig, env);
    
      return config;
    };
    
  2. aliasを設定して、srcのパスを解決させる設定を追加します。これでimport時にsrcから指定してもstorybookからも怒られません。

    webpack.config.js
    const genDefaultConfig = require('@storybook/angular/dist/server/config/defaults/webpack.config.js');
    const path = require('path');
    
    module.exports = (baseConfig, env) => {
      const config = genDefaultConfig(baseConfig, env);
    
      // 追加
      config.resolve = {
        ...config.resolve,
        alias: {
          src: path.resolve(__dirname, '../src')
        }
      };
    
      return config;
    };
    

今後、他にもwebpackの設定を自分で拡張していくケースがあると思うので、基本的にwebpack.config.jsは最初で用意しておくといいと思います。

静的ファイルの参照

assets配下にある画像やアイコンをcomponent等で参照することはよくあると思いますが、storybookでassetsファイルを読み込めるようにするには、起動やビルドじにoptionが必要でした。
package.jsonを開いて、scriptsを修正します。

package.json

{
  "name": "test-project",
  "version": "0.0.0",
  "scripts": {
    // 省略...
    "storybook": "start-storybook -p 6006 -s ./src",  // 修正
    "build-storybook": "build-storybook -s ./src"    // 修正
  },
  // 省略...
}

何をしているかというと、static-dirオプションというのを追加しています。
これにより、ビルド時などに吐き出されるディレクトリにassetsディレクトリが追加され、storybook上からも参照することが可能になります。
なぜ指定している値がassetsじゃなくてsrcになっているかというと、指定したディレクトリの中にあるファイルやディレクトリのみが展開され、指定したディレクトリ自体は展開されないためです。
angularで画像などを参照する際には、通常 assets/image/item.pngのようにすると思うのですが、staticーdirオプションにassetsを指定すると、image/item.pngでしか参照できません。
ただ、srcを指定すると無駄なファイルもたくさん吐き出されるので、良い方法をご存知の方は是非教えてください!

さいごに

いろいろハマりましたがコンポーネントを一覧化できるので、便利ですね!
あとatomic designの考えのもとにつくってみたんですが、再利用性も高くなるし、コードのメンテナンスもしやすくなるしでいいことだらけでした。最初は時間がかかってしまいますが、これからはatomic designを意識して開発していきたいなと思います。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
4