ReactのUI開発を促進するstorybookを触ってみた

storybookの超基本的な使い方について紹介できればと思います。

今回はコマンド一つでreactの環境を構築できるお馴染みのcreate-reacta-appを使っていこうと思います。

インストールしていない方は事前にしておいてください。

環境構築

ではまずは以下のコマンドを実行してください。

create-react-app storybook_paractice

プロジェクトフォルダに移動します。

cd storybook_paractice

storybookに必要なパッケージをインストールします。

 yarn add @storybook/cli

package.jsonの中身はこのようになっています。

{
  "name": "storybook-sample",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.4.1",
    "react-dom": "^16.4.1",
    "react-scripts": "1.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject",
    "storybook": "start-storybook -p 9009 -s public",
    "build-storybook": "build-storybook -s public"
  },
  "devDependencies": {
    "@storybook/react": "^3.4.8",
    "@storybook/addon-actions": "^3.4.8",
    "@storybook/addon-links": "^3.4.8",
    "@storybook/addons": "^3.4.8",
    "babel-core": "^6.26.3",
    "babel-runtime": "^6.26.0"
  }
}

その後プロジェクトフォルダの直下で以下のコマンドを実行します。

getstorybook

このコマンドを実行するとstoriesというフォルダができていると思います。

src
  ├── App.css
  ├── App.js
  ├── App.test.js
  ├── index.css
  ├── index.js
  ├── logo.svg
  ├── registerServiceWorker.js
   └── stories
      └── index.js

package.jsonのscriptの部分にも記述が追加されています。

"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject",
    "storybook": "start-storybook -p 9009 -s public", // 追加されている部分
    "build-storybook": "build-storybook -s public" // 追加されている部分
  },

では早速ローカルサーバーを起動してstorybookの画面をみてみましょう。

yarn storybook

このような画面が表示されると思います。

f:id:top_men:20180730195525p:plain

stories/index.jsコンポーネントの設定をしていきます。

import React from 'react';

import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';

import { Button, Welcome } from '@storybook/react/demo';

storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);

storiesOf('Button', module)
  .add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
  .add('with some emoji', () => (
    <Button onClick={action('clicked')}>
      <span role="img" aria-label="so cool">
        😀 😎 👍 💯
      </span>
    </Button>
  ));

storiseOf()関数の第一引数にタイトル(上のブラウザのサイドバーにあるWelcomeやButton)を、add関数でサブメニューのような階層を作ることができます。 ストーリーを構成するコンポーネントの配置場所は任意で良いです。 またストーリーを追加するごとにページが増えます。

コンポーネントの作成

実際にコンポーネントを作成してみましょう。

まずLabelコンポーネントを作成します。今回はstyled-componetsを使うのでインストールをしましょう。

yarn add styled-components

その後、Label.jsファイルを作成します。

import React, { Component } from 'react';
import styled from 'styled-components';

class Label extends Component {
  render(props){
    return(
      <LabelCom {...this.props}>Hello, storybook!</LabelCom>
    )
  }
}

const LabelCom = styled.div`
  color: #fff;
  width: 200px;
  margin: 0 auto;
  padding: 10px;
  text-align:center;
  background: ${(props) => {
    return props.type === "alert" ? "red" : "green"
  }};
`;

export default Label;

受け取るpropsによって背景色を変更しているだけです。

その後、stories/index.jsを編集します。

import React from 'react';

import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';

import { Button, Welcome } from '@storybook/react/demo';
import Label from '../Label'; // 追加

storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);

storiesOf('Button', module)
  .add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
  .add('with some emoji', () => (
    <Button onClick={action('clicked')}>
      <span role="img" aria-label="so cool">
        😀 😎 👍 💯
      </span>
    </Button>
  ));

storiesOf('Label', module)
  .add('default', () => <Label type="" />)
  .add('alert', () => <Label type="alert" />)

propsに文字列alertを渡した時と、何も渡さない時でストーリーを分けました。

またstorybookのプレビュー画面に独自のスタイルやスクリプトを読み込ませたいときは.storybook以下にpreview-head.htmlを作成します。 例えばフォント読み込んだり、bodyにスタイルを付与しています。

<link href="https://fonts.googleapis.com/earlyaccess/roundedmplus1c.css" rel="stylesheet" />
<style>
body {
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>

こちら公式サイトで説明されています。

https://storybook.js.org/configurations/add-custom-head-tags/

実際に画面を見るとdefault用とalert用のストーリーが確認できると思います。

アドオンの紹介

次にstorybookにはアドオン(拡張機能)があります。その中から一つinfoというものを今回は紹介します。

必要なモジュールをインストールします。

yarn add @storybook/addon-info

以下のコードを追加します。

stories/index.jswidthInfo、Appコンポーネントをインポートします。(省略していますがAppコンポーネントではpropsでtextを渡すようにしています。)

import { withInfo } from '@storybook/addon-info';
import App from '../App';

storiesOf('App', module)
  .add('info of App',
    withInfo(`
      マークダウン形式で記述することができます。

      ### タイトル

      ~~~js
      <Button>Click Here</Button>
      ~~~

    `)(() =>
      <App text="hello from storybook"/>
    )
  )

widthInfoメソッドの中でマークダウンを記述することができます。 infoという拡張機能は文字の通りコンポーネントの情報について記述することに特化したものになります。

ブラウザでみるとこのようになります。

f:id:top_men:20180801015846p:plain

まとめ

ButtonやSideBarなどパーツごとに分けるのか、それともページごとにコンポーネントを分けるのかなど、どのようなカテゴリー分けが一般的にはあるのか気になりました。コンポーネントがどのような役割をもっているかをこのようにガイドラインのような形で確認することができるので大規模な開発では積極的につかっていきたいです。またデザイナーがコンポーネントを記述できる環境であれば、デザイナーに任せてしまうことでエンジニアがより開発の方に集中できるようになるのではないかと思いました。