1. TOP
  2. BLOG
  3. DEVELOP
  4. React Native(Expo)でStorybook v6をつかってみた

React Native(Expo)でStorybook v6をつかってみた

2021.01.19


 

はじめに

こんにちは、すずきです。

今回とりあげたのは、11月の社内勉強会でも紹介したStorybookというフロントエンド開発で最も有名なUI開発ツールです。

Githubのスターが55kを超えるような超有名ツールみたいなのですが、恥ずかしながら今まで全く知りませんでした。

私の場合はアプリのUI開発にあたるのですが、開発フローのなかでちょっとした手間が結構あるんですよね。

例えば、以下のようなことです。

  • APIより先にUIができてしまったときの表示確認。
  • デザイナーやディレクターとのUI微修正のやりとり。
  • プロジェクト内のコンポーネントがアプリ内のどれにあたるのかわからない。

プロダクトと切り離したUI開発ができるツールとかないんかな。。。と探していたところで見つけたのがこのStorybookでした。

React Nativeの関連資料を探してみたところ、記事がほとんど見つからず、あっても前バージョンのStorybook v5に関するものでした。そこで今回は、React NativeのプロジェクトにStorybook v6を導入し、実務でも使えるものなのかを検証してみることにしました。

インストール

プロジェクト作成の前にNode.jsとyarnがインストールされていることを確認します。以下のコマンドでプロジェクトを作成することができます。

$ expo init my-project

コマンドを実行するとtempleteを選ぶことができるので、今回はblank (TypeScript)を選択します。

? Choose a template: › - Use arrow-keys. Return to submit.
    ----- Managed workflow -----
    blank                 a minimal app as clean as an empty canvas
❯   blank (TypeScript)    same as blank but with TypeScript configuration
    tabs (TypeScript)     several example screens and tabs using react-navigation and TypeScript
    ----- Bare workflow -----
    minimal               bare and minimal, just the essentials to get you started
    minimal (TypeScript)  same as minimal but with TypeScript configuration

cd my-projectでフォルダに移動し、以下のコマンドでStorybookを導入することができます。

npx -p @storybook/cli sb init --type react

*Create React Appではnpx sb initコマンドでv6が導入されましたが、expoではv5が導入されてしまうので注意が必要です。

以下のコマンドを実行することでStorybookが起動します。

yarn storybook

スクリーンショット 2020-09-11 20.52.43

Storybookの関連ファイルとフォルダ

 ここまでのコマンド実行でプロジェクト直下に.storybookとstoriesというフォルダが作成されていると思います。

*Create React Appだとsrc/storiesというディレクトリ構成ですが、Expoの場合だとプロジェクト直下にstoriesフォルダが作成されます。

スクリーンショット 2020-09-11 20.52.43

.storybookフォルダ内のファイル構成はv6から、main.jsとpreview.jsの2ファイルになりました。

main.jsではstoryのパスや適用するアドオンを定義し、preview.jsでは全ストーリーに対してグローバルにアドオンを適用したりすることができます。

main.js
module.exports = {
  "stories": [
    "../stories/**/*.stories.mdx",
    "../stories/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-knobs"
  ]
}
preview.js
export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
}

Storyの作成

今回はButtonコンポーネントを使って簡単なStoryを作成します。

stories直下にcomponentsフォルダを作成し、componentsフォルダの中でさらにコンポーネント単位でフォルダを作成します。

例えば、今回のケースでは、stories/components/ButtonNativeの中にButton.tsx, Button.stories.tsxの2つファイルを作成します。

Button.tsx
import React from 'react';
import { Button, ScrollView, View } from 'react-native';

const ButtonNative = (props) => {
  const { color, title, age, ...rest } = props;
  return (
    <ScrollView style={{ height: 1000 }}>
      <View style={{ width: 300 }}>
        <Button
          buttonStyle={{ width: 100 }}
          color={color}
          title={title}
          {...rest}
        />
      </View>
    </ScrollView>
  );
};
export default ButtonNative;

当たり前ですが、上記のようにScrollViewなどのReact Naviveコンポーネントにも対応しています。

Button.stories.tsx
import React from 'react';
import ButtonNative from './Button';

export default {
  title: 'native/ButtonNative',
  compoent: ButtonNative,
};

const Template = (args) => <ButtonNative {...args} />;

export const PrimaryA = Template.bind({});
PrimaryA.args = {
  color: 'blue',
  title: 'Primary Args',
};

export const LongPrimaryA = Template.bind({});
LongPrimaryA.args = {
  ...PrimaryA.args,
  title: 'Long Primary Args',
};

export const SecondaryA = Template.bind({});
SecondaryA.args = {
  color: 'red',
  title: 'Secondary Args',
};

数箇所にわけて説明します。

export default {
  title: 'native/ButtonNative',
  compoent: ButtonNative,
};

titleはStorybook内のサイドバーでの表示名を表します。

スクリーンショット 2020-09-11 20.52.43

componentにはimportしたtsxファイル名を指定します。

const Template = (args) => <ButtonNative {...args}

こちらで各ストーリーのテンプレートを作成します。作成していくストーリーには、全てこのテンプレートを使用します。

export const PrimaryA = Template.bind({});
PrimaryA.args = {
  color: 'blue',
  title: 'Primary Args',
};

PrimaryAというストーリーを作成しました。

作成したTempleteをbindし、colorとtitleにそれぞれ引数を与えます。すると、Storybook内に作成したストーリーのコンポーネントが表示されます。

スクリーンショット 2020-09-11 20.52.43

Button.stories.tsxにさらに2つのストーリーを追加します。

PrimaryAのcolorはそのままでtitleのみを変更したり、

export const LongPrimaryA = Template.bind({});
LongPrimaryA.args = {
  ...PrimaryA.args,
  title: 'Long Primary Args',
};

スクリーンショット 2020-09-11 20.52.43

colorとtitleを両方とも変更したりなど、自由にstoryを作ることができます。

export const SecondaryA = Template.bind({});
SecondaryA.args = {
  color: 'red',
  title: 'Secondary Args',
};

スクリーンショット 2020-09-11 20.52.43

アドオンの導入

Storybookには便利なアドオン(拡張機能)がいろいろ揃っています。

今回はaddon-knobsというアドオンを導入します。

アドオンのインストールには以下のコマンドを用い、インストール後はmain.jsに使用するアドオンを追記してください。

yarn add -D ~
main.js
module.exports = {
  stories: [
    '../stories/**/*.stories.mdx',
    '../stories/**/*.stories.@(js|jsx|ts|tsx)',
  ],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-knobs',
  ],
};

addon-knobs

こちらのアドオンでは、Storybook下部のknobsタブ内でpropsを手動で変更することができます。

以下では、Buttonのtitle, color, disabledを変更できるようにしました。

*addon-knobsはv6の書き方に対応していないようでしたので、v5の書き方で記載しました。

Button.stories.tsx
export const Knobs = () => (
  <ButtonNative
    title={text('Name', 'ボタン')}
    color={color('Color', 'red')}
    disabled={boolean('Disabled', false)}
  />
);

以下のようにカラーマップから自由に色を変更したり、チェックボックスからDisabled状態に切り替えることができます。

スクリーンショット 2020-09-11 20.52.43

スクリーンショット 2020-09-11 20.52.43

React Nativeプロジェクトにおけるアドオン

Storybookには、addon-knobsの他にも、addon-viewport, addon-console, addon-a11y, addon-docsなどの便利なアドオンが揃っています。しかし、React Nativeでは、なんとこれらのアドオンのほとんどには対応していません…

スクリーンショット 2020-09-11 20.52.43

せめて市販端末の画面サイズでコンポーネント表示を確認できる、addon-viewportには対応してほしかったのですが…

(以下はCreate React Appで導入した際の画面です)

スクリーンショット 2020-09-11 20.52.43

キミスカアプリの一部コンポーネントでStoryをつくってみた

Storyの作り方がわかったところで、キミスカアプリの一部のコンポーネントで試してみました。

それっぽい表示にはなったのですが、実際の端末サイズでないので、「コンポーネントの実際の表示確認」という目的には一歩足りていないですね。

Input(メールアドレス)

未入力 + 未チェック

スクリーンショット 2021-01-18 18.46.56

未入力 + チェック

スクリーンショット 2021-01-18 18.46.18

バリデーションエラー

スクリーンショット 2021-01-18 18.45.02

CheckBox(業種選択)

スクリーンショット 2021-01-18 18.26.19

Modal(企業からの注目度)

スクリーンショット 2021-01-18 18.50.11

おわりに

ほとんどのアドオンに対応していないことから、現状では、実際のReact Native + ExpoプロジェクトにStorybookを導入するのは難しそうです。

今後の公式の対応に期待したいと思います。