Cover image

Monorepo+TypeScript+React+Storybook環境をつくる

2019/08/24

Monorepo 環境で、create-react-app w/ TypeScript したパッケージに対して、Storybook を導入する手順です。CLI を使わずに手動で設定していますが、簡単にセットアップは完了できました。

セットアップ

  • Storybook w/React, Addons(viewport, actions, storyshots) をインストール
  • 型情報もインストール
$ yarn workspace client add -D @storybook/addon-actions @storybook/addon-storyshots @storybook/addon-viewport @storybook/addons @storybook/react @storybook/theming
$ yarn workspace client add -D @types/storybook__react @types/storybook__addon-actions

config

  • 子パッケージの ./.storybook/config.js に設定を記述すれば OK
  • /.stories.tsx?$/ にマッチするファイルすべてを対象にするように設定
  • viewport, actions のアドオンを追加
src/client/.storybook/config.js
import { configure, addParameters } from '@storybook/react';
import { create } from '@storybook/theming';

addParameters({
  options: {
    theme: create({
      base: 'light',
      brandTitle: 'Keiba',
    }),
  },
});

// automatically import all files ending in *.stories.js
const req = require.context('../src/components', true, /.stories.tsx?$/);
function loadStories() {
  req.keys().forEach(filename => req(filename));
}

configure(loadStories, module);
src/client/.storybook/addons.js
import '@storybook/addon-viewport/register';
import '@storybook/addon-actions/register';

storybook

  • コンポーネントを TypeScript で記述
  • Story も同様。
src/client/src/components/organisms/RaceListSmall/index.tsx
import React from 'react';
import format from 'date-fns/format';

import trackCode from '../../../constants/codes/track';
import gradeCode from '../../../constants/codes/grade';
import joukenCode from '../../../constants/codes/jouken';

interface Race {
  id: number; // Int!
  number: number; // Int!
  ryakushou6: string; // String!
  gradeCode: string;
  joukenCodeJy: string;
  trackCode: string;
  distance: number; // Int!
  tourokuTousuu: number;
  hassouTime: Date;
}

interface RaceItemProps {
  race: Race;
}

export const RaceItem: React.FC<RaceItemProps> = ({ race }) => (
  <tr>
    <td>{race.number}</td>
    <td>{race.ryakushou6}</td>
    <td>{gradeCode[race.gradeCode].ryaku || joukenCode[race.joukenCodeJy].ryaku}</td>
    <td>{`${trackCode[race.trackCode].course}${race.distance}`}</td>
    <td>{race.tourokuTousuu}</td>
    <td>{`${format(race.hassouTime, 'HH:mm')}`}</td>
  </tr>
);

interface RaceListPresenterProps {
  races: Race[];
}

export const RaceListPresenter: React.FC<RaceListPresenterProps> = ({ races }) => (
  <table className="bp3-html-table bp3-html-table-condensed bp3-html-table-striped bp3-interactive">
    <thead>
      <tr>
        <th>R</th>
        <th>レース</th>
        <th>クラス</th>
        <th>コース</th>
        <th></th>
        <th>発走</th>
      </tr>
    </thead>
    <tbody>
      {races.map(race => (
        <RaceItem key={race.id} race={race} />
      ))}
    </tbody>
  </table>
);
src/client/src/components/organisms/RaceListSmall/storybook/index.stories.tsx
import React from 'react';
import { storiesOf } from '@storybook/react';

import { RaceListPresenter } from '..';

const races = [
  {
    id: 1,
    number: 1,
    ryakushou6: '',
    joukenCodeJy: '703',
    gradeCode: ' ',
    trackCode: '24',
    distance: 1200,
    tourokuTousuu: 16,
    hassouTime: new Date(2019, 2, 1, 9, 50, 0),
  },
  ...
];

storiesOf('organisms/RaceListSmall', module).add('correct', () => (
  <RaceListPresenter races={races} />
));

start

  • package.json で yarn workspace [package-name] [command] を使って yarn workspace client start-storybook と記述
  • これで yarn storybook と単純な記述で起動できる
package.json
{
  "scripts": {
    "storybook": "yarn workspace client start-storybook"
  },
}
$ yarn storybook

表示結果

/contents/blog/2019-08-24-cra-typescript-storybook/storybook.png

Writings

blogsnippetcourse
Home©︎ suzukalight