wheatandcatの開発ブログ

React Nativeで開発しているペペロミア & memoirの技術系記事を投稿してます

@storybook/react-native v6.5にバージョンアップ

@storybook/react-nativeが v6.5にバージョンアップしたので移行した。 結構書き方が変わったので紹介

PR

github.com

Storybook v5 → v6で変わった内容

Component Story Format (CSF) がサポートされた

Storybookのファイルの書きからが以下のように変更。

■ 既存

import React from 'react';
import { storiesOf } from '@storybook/react-native';
import { View } from 'react-native';
import Text from './';

storiesOf('atoms', module).add('Text', () => (
  <View>
    <Text>default</Text>
  </View>
));

■ 新規

import React from 'react';
import { View } from 'react-native';
import Text from '.';

const Story = () => (
  <View>
    <Text>default</Text>
  </View>
);

export default {
  title: 'atoms/Text',
  component: Story,
};

export const Default = {};

上記みたいな感じでstoriesOfは使わず、exportすることでstorybookの表示が行える。

Storybook v6.5への移行作業

以下の記事を参考に移行作業をした。

storybook.js.org

まず、.storybookのフォルダを作成して以下のexampleのリポジトリを参考にファイルを配置

github.com

package.jsonに以下を追加

package.json

  "scripts": {
      ...(略)
    "start-server": "react-native-storybook-server",
    "storybook-generate": "sb-rn-get-stories",
    "storybook-watch": "sb-rn-watcher",
    "storybook": "sb-rn-get-stories && cross-env STORYBOOK_ENABLED='true' expo start",
    "storybook:ios": "sb-rn-get-stories && cross-env STORYBOOK_ENABLED='true' expo start --ios",
    "storybook:android": "sb-rn-get-stories && cross-env STORYBOOK_ENABLED='true' expo start --android",

次に既存のstoryファイルを移行。 storybookがマイグレーション用のコマンドを用意してくれているので、以下のコマンドを実行で自動的変換できる。

$ npx storybook@next migrate storiesof-to-csf --glob="src/**/*.stories.tsx"

※上記のコマンドで変換されるのは、CSF1のフォーマットなので注意。現状の最新はCSF3なので、そちらに変換したい場合は、CSF1に変更後に手動で修正する必要がある(CSF1でも動作はするので、今回はこのまま実装)

memoirには既存で以下のように、1つのstoryファイルに複数のComponentのstoryを表示しているファイルがあった。以下のようなファイルはコマンドではうまく変換できなかった。

src/components/organisms/Memoir/stories.tsx

import React from 'react';
import { storiesOf } from '@storybook/react-native';

()

storiesOf('organisms/Memoir/DateCards', module)
  .add('default', () => (
    <View style={styles.root}>
      <DateCards {...props()} />
    </View>
  ))
  .add('loading', () => (
    <View style={styles.root}>
      <DateCards {...props()} loading />
    </View>
  ));

storiesOf('organisms/Memoir', module).add('Card', () => (
  <Card
    {...item()}
    user={{
      id: 'test',
      displayName: 'suzuki',
      image: 'https://placehold.jp/150x150.png',
    }}
  />
));

storiesOf('organisms/Memoir/ScreenShot', module).add('default', () => (
  <View style={styles.root}>
    <ScreenShot {...screenShotProps()} />
  </View>
));

CSFは1ファイル、1Componentのフォーマットになっているので、以下のスクリプトを作成して、既存のstories.tsxをrename

scripts/storybookRename/main.js

const fs = require('fs');
const path = require('path');

const dirPath = 'src/components';

const traverseDirectory = (dir, callback) => {
  fs.readdirSync(dir).forEach((file) => {
    const fullPath = path.resolve(dir, file);
    if (fs.lstatSync(fullPath).isDirectory()) {
      traverseDirectory(fullPath, callback);
    } else {
      callback(fullPath);
    }
  });
};

traverseDirectory(dirPath, (filePath) => {
  const dirname = path.dirname(filePath);
  const basename = path.basename(filePath);

  if (basename === 'stories.tsx') {
    const newBasename = `${path.basename(dirname)}.${basename}`;
    const newPath = path.resolve(dirname, newBasename);

    fs.renameSync(filePath, newPath);

    console.log(`Renamed: ${filePath} --> ${newPath}`);
  }
});

上記のスクリプトを実行して、stories.tsxText.stories.tsxにrenameしてフォーマットをCSFに合わせていった。 CSFの変換完了後は以下のコマンドで実行。

まず、以下のコマンドでstorybook-serverを起動。

$ yarn start-server

次に以下のコマンドでstorybookを起動。

$ yarn storybook:ios

これで以下の通りに起動ができた。

@storybook/react-nativeはv5で大分更新が止まっていたのでweb版に移行も検討していた。 ただ、web版だとNative固有のコードが動かせないので困るなーと思っていたので更新がきてよかったです。