はじめに
Qiita初投稿なので最初に自己紹介。
都内のWEB系企業でアプリ開発をしているスズキと申します。
商社で約4年間営業として働いていたのですが、2020年7月のコロナ真っ只中にエンジニア転職しました。
記事投稿のようなアウトプットはこれまで面倒でやっていなかったのですが、知識の定着や同じ問題が起こったときの見返しメモとして有用だと思い、このたび始めた次第です。
月2くらいのペースで投稿していけたらと考えています。
ということで本題に入ります!
今回とりあげたのは、Storybookというフロントエンド開発で最も有名なUI開発ツールです。
Githubのスターが55kを超えるような超有名ツールみたいなのですが、恥ずかしながら今まで全く知りませんでした。
私の場合はアプリのUI開発にあたるのですが、開発フローのなかでちょっとした手間が結構あるんですよね。
例えば、以下のようなことです。
・APIより先にUIができてしまったときの表示確認。
・デザイナーやディレクターとのUI微修正のやりとり。
・プロジェクト内のコンポーネントがアプリ内のどれにあたるのかわからない。
プロダクトと切り離したUI開発ができるツールとかないんかな。。。と探していたところで見つけたのがこのStorybookでした。
React Native(Expo)で早速検証したかったのですが、Storybook v6の導入に関する情報がほとんどなかったので、手始めにReact(Create React App)で検証することにしました。
インストール
プロジェクト作成の前にNode.jsとyarnがインストールされていることを確認します。
以下のコマンドでプロジェクトを作成することができます。
npx create-react-app my-app --template typescript
cd my-app
でフォルダに移動し、以下のコマンドでStorybookを導入することができます。
npx sb init
*Qiitaの様々な記事でnpx -p @storybook/cli sb init
のコマンドが使われていますが、こちらはv5までの導入方法になります。今回はv6の導入記事なので、npx sb init
を実行してください。
以下のコマンドを実行することでStorybookが起動します。
yarn storybook
Storybookの関連ファイルとフォルダ
ここまでのコマンド実行でプロジェクト直下に.storybook、src直下にstoriesというフォルダが作成されていると思います。
.storybookフォルダ内のファイル構成はv6から、main.jsとpreview.jsの2ファイルになりました。
main.jsではstoryのパスや適用するアドオンを定義し、preview.jsでは全ストーリーに対してグローバルにアドオンを適用したりすることができます。
main.js
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/preset-create-react-app"
]
}
preview.js
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
}
Storyの作成
今回はbuttonコンポーネントを使って簡単なStoryを作成します。
src直下にcomponentsフォルダを作成し、componentsフォルダの中でさらにコンポーネント単位でフォルダを作成します。
例えば、今回のケースでは、src/components/Button
の中にButton.tsx
, Button.css
, Button.stories.tsx
の3つファイルを作成します。
Button.tsx
import React from 'react'
import './Button.css'
type Props = {
variant: string
}
const Button: React.FC<Props> = (props) => {
const {variant, children, ...rest} = props
return (
<button className={`button ${variant}`} {...rest}>
{children}
</button>
)
}
export default Button
Button.css
.button {
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
border-radius: 4px;
cursor: pointer;
}
.primary {
background-color: #008cba;
}
.secondary {
background-color: #e7e7e7;
color: black;
}
Button.stories.tsx
import React from 'react'
import Button from './Button'
export default {
title: 'form/Button',
compoent: Button,
}
const Template = args => <Button {...args}/>
export const PrimaryA = Template.bind({})
PrimaryA.args = {
variant: 'primary',
children: 'Primary Args'
}
3箇所にわけて説明します。
export default {
title: 'form/Button',
compoent: Button,
}
titleはStorybook内のサイドバーでの表示名を表します。
componentにはimportしたtsxファイル名を指定します。
const Template = args => <Button {...args}/>
こちらで各ストーリーのテンプレートを作成します。作成していくストーリーには、全てこのテンプレートを使用します。
export const PrimaryA = Template.bind({})
PrimaryA.args = {
variant: 'primary',
children: 'Primary Args'
}
PrimaryAというストーリーを作成しました。
作成したTempleteをbindし、variantとchildrenにそれぞれ引数を与えます。
すると、Storybook内に作成したストーリーのコンポーネントが表示されます。
Button.stories.tsxにさらに2つのストーリーを追加します。
export const LongPrimaryA = Template.bind({})
LongPrimaryA.args = {
...PrimaryA.args,
children: 'Long Primary Args'
}
export const SecondaryA = Template.bind({})
SecondaryA.args = {
variant: 'secondary',
children: 'Secondary Args'
}
さきほどのコンポーネントと同様に、Storybookへ反映されます。
各種アドオンの紹介
Storybookには便利なアドオン(拡張機能)がいろいろ揃っています。
何種類かアドオンをいれて確かめてみたので、いくつか紹介します。
アドオンのインストールには以下のコマンドを用い、インストール後はmain.jsに使用するアドオンを追記してください。
*@storybook/addon-docs
を使用する際に、@storybook/addon-controls
がないとエラーがでました。
yarn add -D ~
main.js
"addons": [
"@storybook/addon-links",
"@storybook/addon-actions",
"@storybook/preset-create-react-app",
"@storybook/addon-viewport",
"@storybook/addon-docs",
"@storybook/addon-essentials",
"@storybook/addon-controls",
"@storybook/addon-knobs",
"@storybook/addon-a11y"
]
addon-viewport
デフォルトではSmall, Medium, Large, Tabletの4つから画面サイズを選ぶことができるのですが、以下のようにINITIAL_VIEWPORTSをインポートすると、登録されている市販端末の画面サイズでコンポーネントの表示を確認することができます。
import {addParameters} from '@storybook/react'
import {INITIAL_VIEWPORTS} from '@storybook/addon-viewport'
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
}
addParameters({
viewport: {
viewports: INITIAL_VIEWPORTS,
}
})
addon-console
ストーリーでonClickイベントに指定したconsole.logをStorybook内で出力することができます。
Button.stories.tsx
PrimaryA.args = {
variant: 'primary',
children: 'Primary Args',
onClick: () => console.log('ButtonClicked')
}
preview.js
import {addDecorator} from '@storybook/react'
import {withConsole} from '@storybook/addon-console'
addDecorator((storyFn, context) => withConsole()(storyFn)(context))
addon-docs
Storybook内のdocsタブで、コンポーネントに関するドキュメントをまとめることができます。
addon-knobs
こちらのアドオンでは、Storybook下部のknobsタブ内でpropsを手動で変更することができます。
以下では、Buttonのdisabledとchildrenを変更できるようにしました。
preview.js
import {addDecorator} from '@storybook/react'
import {withKnobs} from '@storybook/addon-knobs'
addDecorator(withKnobs)
stories.tsxファイルがv6の書き方だとうまくいかず、v5の書き方だとうまくいきました。
うまく行かなかった理由については調査中です。。。
Button.stories.tsx
export const Knobs = () => (
<Button variant='primary' disabled={boolean('Disabled', false)}>
{text('Label', 'Button Label')}
</Button>
)
addon-a11y
コンポーネントのアクセシビリティを確認することができます。
https://ja.reactjs.org/docs/accessibility.html
preview.js
import {addDecorator} from '@storybook/react'
import {withA11y} from '@storybook/addon-a11y'
addDecorator(withA11y)
おわりに
初めての記事作成非常に疲れました。。。
よくいわれることですが、アウトプットしてみると知識の穴がめちゃくちゃ見つかりますね。
記事内容であやしい部分があったら都度修正していきます。
それでは!