StorybookはUI開発には欠かせないツールとなっていきました。
導入をしたこともなければ、ほとんど開発したこともなかったのでインプットしておきたいと思い学習を開始しました。
【こんな方にオススメ!】
・Storybookについて知りたい方
・Storybookの導入を検討している方
Storybookの基本的な書き方を理解して、携わるプロジェクトでの導入を検討してみてください!
Storybookってなに?
UI開発のためのツールです。
コーディングしたものをアプリケーション上で確認するのが一般的だと思いますが、作成したパーツをアプリケーションを操作することなく確認できるツールです。
開発環境構築
StotybookのDocsを見ながら導入を進めていきます。
npx storybook init
installが進むと下記のようなテキストが表示されます。
Would you like to send crash reports to Storybook?
Storybook now collects completely anonymous telemetry regarding usage.
This information is used to shape Storybook's roadmap and prioritize features.
You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL https://storybook.js.org/telemetry
クラッシュしたときになどにレポートを送信することに対する有無なので「y」と打ち込んで次に進みましょう。
次にPlease choose a project type from the following list
と聞かれるのでどんな形式でStorybookを作成するのか聞かれるので矢印でreact-scripts
を選択してEnterを押します。
installが終わると警告が出ました…なんで?
どんな警告が出たのか?
24 high severity vulnerabilities
と表示され続いて修正を実行するように促されます。
To address issues that do not require attention, run:
npm audit fix
To address all issues (including breaking changes), run:
npm audit fix --force
一番最後にDo you want to run the 'npm7' migration on your project?
とnpm7に移行を勧められるので大人しく従ってみました。
するとnpm run storybook
がコマンドラインに表示されるので実行してみます。
Error: Cannot find module 'react/package.json'
errorが出ました…調べてみます。
導入方法が間違っていました…Storybookの公式にもあるようにstorybook init
は空のプロジェクト用に作成されていないとのことでした…。
ということでReactのアプリを作成してからStorybookを導入する手順で仕切り直したいと思います!
Reactのドキュメントは新しいものがあるそうなので一応共有しておきます。
Reactプロジェクトを作る
npx create-react-app ./
すでにディレクトリを用意している場合はnpx create-react-app my-app
ではなくnpx create-react-app ./
とすることで現在のディレクトリにReactのプロジェクトを作れます!
構築が終わったのでnpm start
を実行するとhttp://localhost:3000/
で馴染みのある画面が表示されました。
npx storybook init
を実行します。
再びnpm7へ移行することをすすめすすめられたり、セキュリティーの警告を出したりしました。
ただ、npm7へ移行したらStorybookのwelcomページが表示されたのでいったんよしとします。
現時点では警告を解消する場合は手動での構築が必要そうですが、個人開発なので何か問題が起こらないかぎりはこのまま進めたいと思います。
Storybookが良いと感じている理由
余談になりますが、Storybookがなぜ良いと感じているのかを伝えておきます。
デザインパーツの管理は非常に難しいです。
サイト改善によって同じようなパーツや新しいパーツが生み出され続けます。
それらを競合なく、誰でも把握できる状態をこれまで作れませんでした…。
打開策としてGulpでFrontNoteを使いデザインカタログを作成れるので運用し始めましたが、リソースが減るにつれて手が回らなくなり、使われなくなってしまいました。
FrontNoteはscssファイルの中に記述し、コマンドを走らせることで生成してくれます。
FrontNoteに表示させたい部分はバッククォートで囲むことで表示できます。
/*
#styleguide
button
```
<button class="button">これはボタンです</button>
```
*/
FrontNoteが運用にのらなかったのは使いにくかったのだと思います。
複数のパターンがあればどれだけ定義が必要になります。
増えれば触れるほど記述量が増えるので、運用が大変でした。
また検索性も悪かったので確認する際の手間も問題でした…。
Storybookは変更できる値を定義しておけばUIで状態や見た目を変更することが可能&ビジュアルリグレッションテストもできますし、検索もできます。
ツールバーにはさまざまな機能もあるので使い勝手が良いです。
Storybookの記述方法を確認していく
srcフォルダの下にstoriesがあるので記述はそちらに追加していきます。
ドキュメントに沿って作成した場合はcssファイルとjsxファイルを別で作成し、jsxファイルにimportする形で作成されています。
Cardコンポーネントを作る
jsxファイルとstoriesファイルを作成してStorybookに表示できるところまで実装したいと思います。
import React from 'react';
function Card({ children }) {
return <div class='card'>{children}</div>;
}
export default Card;
import React from 'react';
import Card from './Card';
export default {
title: 'Example/Card',
component: Card
};
export const SampleCard = () => <Card>Sample Card!!</Card>;
なぜか表示されないな…と思ったらtitleの記述が誤っておりました…
表示してほしいヶ所に表示されない場合は一度確認してください。
export default {
- title: 'Card',
+ title: 'Example/Card',
component: Card
};
export const SampleCard = () => <Card>Sample Card!!</Card>;
+ export const DemoCard = () => <Card>Demo Card!!</Card>;
stories.jsx
に新しく定義することで左側のストーリーに追加されます。
引数を設定する
UI上でテキストの変更ができるようにしてみます。
import React from 'react';
import Card from './Card';
export default {
title: 'Example/Card',
component: Card
};
- export const SampleCard = () => <Card>Sample Card!!</Card>;
+ const Template = (args) => <Card>{args.text}</Card>;
+ export const SampleCard = Template.bind({});
+ SampleCard.args = {
+ text: 'Sample Card!!'
+ };
Controlsにテキストを入力できる項目が表示され、テキストを打ち込むとCanvasに反映されることが確認できると思います。
UIでデザインの切り替えができるようにしてみる
まずCardコンポーネントに引数を追加します。
- function Card({ children }) {
+ function Card({ foundation, children }) {
+ const card = foundation ? 'card-foundation' : 'card';
return (
<div class={card}>
<div class='card-inner'>{children}</div>
</div>
);
}
CSSを追加します。
.card-foundation {
border: 1px solid #ccc;
border-radius: 2px;
background-color: #f7f3e8;
}
最後にstories.jsxにUI操作のための表示が追加されるように設定します。
const Template = (args) => <Card>{args.text}</Card>;
export const SampleCard = Template.bind({});
SampleCard.args = {
text: 'Sample Card!!',
+ foundation: false,
};
すると画像の通り切り替えボタンが表示されます。
true
にすると背景色が切り替えられるようになりました。
UIで複数の選択しから選んで表示を切り替えられるようにしてみる
padding
やfont-size
など複数の値から大きさを選びたい場合もあると思います。
そんな場合の実装をしていきます。
Cardコンポーネントで引数の受け取りとstories.jsx
にて値の設定、CSSを追加します。
- function Card({ foundation, children }) {
+ function Card({ foundation, children, innerSize }) {
+ const card = foundation ? 'card-foundation' : 'card';
return (
<div class={card}>
- <div class='card-inner'>{children}</div>
+ <div className={`card-inner-${innerSize}`}>{children}</div>
</div>
);
}
const Template = (args) => <Card>{args.text}</Card>;
export const SampleCard = Template.bind({});
SampleCard.args = {
text: 'Sample Card!!',
foundation: false,
+ innerSize: 'medium'
};
.card-inner-small {
padding: 4px 12px;
}
.card-inner-medium {
padding: 8px 16px;
}
.card-inner-large {
padding: 16px 24px;
}
画面を確認してみます。
innerSize
はラジオボタンで変更したかったのに想像と違っています。
どうやらpropsのtypeを指定する方が良いらしいので、typeをCrad.jsx
に明記します。
import React from 'react';
+ import PropTypes from 'prop-types';
import './card.css';
function Card({ foundation, children, innerSize }) {
const card = foundation ? 'card-foundation' : 'card';
return (
<div class={card}>
<div class={`card-inner-${innerSize}`}>{children}</div>
</div>
);
}
export default Card;
+ Card.propTypes = {
+ foundation: PropTypes.bool,
+ innerSize: PropTypes.oneOf(['small', 'medium', 'large']),
+ text: PropTypes.string
+ };
画面を確認するとラジオボタンで切り替えられるようになりました!
まとめ
今回はStorybookの導入と基礎的な記述について学んでみました。
今回はCSSファイルをimportする形式でデザインを作成しましたが、本来であればstyled-components
やCSS in JS
、Emotion
などと併用して利用することがほとんどだと思います。
公式サイトを見ると効率的な記述方法なども見受けられました。
知識がまだまだ浅いのは否めませんが、もう少し複雑な要件でコンポーネントを作成するなどでStorybookの知識を高めていきます!
さらにビジュアルリグレッションテストなども可能です。
レガシーなサイトには簡単に導入できないかもしれませんが、自動テストも視野に入れて設計できるとすごく良いと考えています!