title: Storybook超入門(+Next.js+TypeScript)
Storybookとは?
Reactのコンポーネントを1つのアプリケーションのように扱えるツール
これを利用することでNext.jsのアプリからコンポーネントを切り離すことができ。
あらゆるコンポーネントのパターンをシミュレーションして設計、開発できる。
コンポーネントとはウェブアプリの画面を構成する最小単位です。
目的
最小限のStorybookはどのようなものか調べる、
0からStorybook使ってコンポーネントを作ってみる。
そして、作ったコンポーネントをどのように使っていくか見ていく。
バージョン
Storybook 6.4.9
Storybookを導入しても
(UIコンポーネントの管理ツールとも言えます。)
上記図のUIコンポーネント部分をNext.jsから切り離して、Storybookの支配下に置きます。
そうすることで、コンポーネント同士のスパゲティ化を防ぐ効果が生まれます。
Next.js部分はビジネスロジックやDB等のデータアクセス部分に集中します。
表示する部分を作る時にStorybookからUIコンポーネントをimportします。
Storybookの導入で画面表示のコンポーネントを切り離して独立させることができます。
Next.jsとUIコンポーネントを個別に管理することで、スパゲティ化を防ぐ効果があります。
そうすることで全体の構成が自然ときれいになります。
方法
ReactアプリにStorybookをインストールをします。
Storybookはほぼ独立しているので、どの段階でもReactのフレームワークにインストール出来ます。
npx sb init
(Storybookのインストーラーコマンド)は現フレームワーク環境を考慮してインストールしてくれるようです。
(Create React App, Vue, Angular その他)
準備
StorybookをNext.jsにインストールする前の準備を行います。
※これは自分が0から構築したアプリです、
皆さんは自分のアプリにgitのブランチを適当に切って入れてみてください。
自分のアプリが用意できない場合は下記の詳細を利用してみてください。
詳細
今回使用するもの
Next.js(Reactのフレームワーク)
git
その他便利なツール
手順
最初にNext.jsをインストールする。
create-next-appを最新バージョンにする
npm i -g create-next-app
next.jsのインストール。
npx create-next-app storybooksample
npx create-next-app@latest <アプリネーム> --ts
npx create-next-app@latest storybooksample --ts
npx create-next-app@latest . --ts
オプションの説明
@latest 最新のNext.jsでインストールする。これをつけないとローカルに古いバージョンが合った場合そちらが優先される(らしい)
--ts TypeScriptでインストールする
<アプリネーム>を「.」 ドットにするとその場所にインストールされる。
基本方針
srcフォルダにNext.jsのソースを入れる
componentsフォルダにコンポーネントのソースを入れる。
基本方針の解説
レゴブロックで例えると
レゴブロックそのものをcomponentsフォルダに入れ
レゴブロックで組み上げた作品をsrcフォルダに入れる。
レゴブロック同士を組み合わせた中間ブロックは
中間ブロックは作らない派
レゴブロックと考える派
後で考える派
etc
小さなアプリは中間ブロックとか考えないで作るとか、
全部レゴブロックとして考えるとか
色々考えられるので今回はやらない
srcフォルダの中にNext.jsのソース全部入れる。(コンポーネント以外のソース)
追加
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
上記設定を追加した場合の設定例
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
root直下にsrcフォルダを作り
その下にpagesディレクトリを移動する。
その下にstylesディレクトリを移動する。
publicはその位置のままで触らない。
src\pages\index.tsxファイルを最小に変更
余分なimport文とreturn文の中身を適当な文字列に変える
import type { NextPage } from "next";
const Home: NextPage = () => {
return <>適当な文字列</>;
};
export default Home;
ここで一旦ローカルを立ち上げてみる
npm run dev
文字列を確認
gitを使用する
最小限のコミット戦略
何かを作ってソースを追加する時新しいブランチを作っておく。
新しい機能やライブラリを追加する時は新しいブランチを切る。
ひとつのプロジェクトから枝分かれをさせ、
別の作業を行う事を「ブランチを切る」と言います。
ブランチとは、「支流、枝、分岐する」等といった意味があります。
mainには完成(完動、動作確認済み、エラー除去済み)したソースだけを
他のブランチからマージしていく。
コミットまだしていないのなら
今までの変更部分は新しいブランチにそのまま反映される。
新しいブランチを作って移動したら変更部分が消えるということはない。
ただブランチ間を移動するだけならgit stash等は必要無い。
git stashは作業部分を一時的に退避し保存するコマンド
(コミット間を移動する場合にはgit stash等が必要。)
gitのcheckoutとbranchをまとめて行うには
git checkout -b
今回の新しいブランチ名は
featue/storybookinit
実際のコマンド
git checkout -b feature/storybook
gitで適当にコミットをしていく。
ESLintはデフォルトでインストール済み
バージョン確認
./node_modules/.bin/eslint --version
yarn lint
を実行して
✔ No ESLint warnings or errors
などのメッセージを確認
prettierを追加
Basic Features: ESLint | Next.js
https://nextjs.org/docs/basic-features/eslint# prettier
yarn add -D prettier eslint-config-prettier
ESLintとprettierを連携させる
.eslintrc.jsonがあるので
設定例
`.eslintrc.json
{
"extends": ["next", "prettier"]
}
prettier設定ファイル
touch .prettierrc.json
```.prettierrc.json
{
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"quoteProps": "as-needed",
"jsxSingleQuote": false,
"trailingComma": "es5",
"arrowParens": "always",
"endOfLine": "lf",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"requirePragma": false,
"insertPragma": false,
"proseWrap": "preserve",
"vueIndentScriptAndStyle": false
}
"scripts": {
"format": "prettier --write ."
},
Storybookは基本的に独立しているので
Next.jsアプリのどの段階でいれても
本体にほぼ影響を与えない。
しかし、できるだけ初期段階で組み込んだほうがいい
ここから作ったNext.jsアプリにStorybookを追加していく。
rootで
Add Storybook:
npx sb init
eslint-pluginを導入するか聞いてくるので
Nを押す。
(Next.jsには最初から入っている。)
インストールが成功しているかどうかの確認
yarn storybook
確認できたら一旦止めて、
最小限のstorybookを自分の手で作るため、
storiesの中のファイルをいったんすべて消す。
.storybook
の中身はNext.jsアプリとの設定情報なので消さない。
Storybookで最小限のコンポーネントを作る。
基本的な考え方
Next.jsとコンポーネント
を分けて考える。
コンポーネントをレゴブロックの最小単位として考え
componentsフォルダからレゴブロックを取り出してNext.jsアプリを組み立てる。
componentsフォルダの中身を5段階くらいに分けて組み上げる設計思想がAtomic Design
現段階では1段階で十分なので採用しない。
ここまでが準備段階
ここから0からstoryファイルを作ってコンポーネントを作っていく。
componentsフォルダを作る
componentsフォルダの中に
// Reactを使うためにimport命令
import React from 'react';
type Props = {
children: string;
};
// Buttonコンポーネント(中身は普通のJavaScript関数)
// { children }を受け取り
// 受け取る{ children }は
const Button: React.VFC<Props> = ({ children }: Props) => {
// tsxファイルは返り値にHTMLのButtonタグを返す。
// 受け取った引数{children}によってボタンに表示する文言が決定されます。
// {children}はreactでよく使われる引数です。
return <button>{children}</button>;
};
// export default は JavaScriptの基本です
export default Button;
// Reactを使うためにimport命令
import React from 'react';
// コンポーネントのストーリーが、引数をpropsとして受け取る単純なコンポーネントである場合。
import { ComponentMeta } from '@storybook/react';
// コンポーネントファイルを読み込む
import Button from './Button';
export default {
// ブラウザで表示するタイトル
// titleは自由に変えられます。
// titleを変えるとブラウザ画面上にエラーが出ます、その場合はtitleをもう一度クリックし直すと新しい設定値でコンポーネントを呼び出してくれエラーが直ります。
title: "Button",
// .storybook\main.jsの"stories"設定とマッチするファイルを探します。
// Buttonの場合はButton.tsxファイルを探しています。
component: Button,
// TypeScriptの型設定
// ComponentMetaは コンポーネントのストーリーが、引数をプロップとして受け取る単純なコンポーネントで使用します。
} as ComponentMeta<typeof Button>;
// storyです
// このコンポーネントにいろんな引数を与えて
// 色んなパターンの表示を見ることができます。
// storyはそれぞれ独立しているので、このように中身が同じアロー関数を与えても大丈夫です。(通常は色々なパターンの引数を与えシミュレートします。)
export const Default1 = () => <Button>Click me</Button>;
export const Default2 = () => <Button>Click me</Button>;
export const Default3 = () => <Button>Click me</Button>;
この2つのファイルを作る
(公式)ストーリーの書き方
How to write stories
https://storybook.js.org/docs/react/writing-stories/introduction
ファイルの中身がわからない時は
1文字、1行単位でソースを弄ってブラウザでどう動くかを実感する。
import
, from
, export default
等がわからない時はJavascriptを勉強してきましょう。
現時点(2021年12月)で最強のJavaScript無料入門書
JavaScript Primer - 迷わないための入門書 # jsprimer
https://jsprimer.net/
日本語訳
Storyの書き方
Storyとは引数を与えてコンポーネントの状態を返す関数のことです。
Storybookでは引数をargsを呼んでいます。
Reactではprops
Storyをどこに置くか?
基本的にコンポーネントファイルと一緒に置きます。
ストーリーファイルは開発専用であり、プロダクションバンドルには含まれません。
コンポーネントストーリーのフォーマット
Component Story Format (CSF)
ストーリー読み込みの設定
デフォルトでは、.storybook/main.jsのグロブ(パターンマッチ文字列)に基づいて、プロジェクト内の拡張子が.stories.*のすべてのファイルにマッチしたストーリーを読み込みます。これはストーリーファイルと、それを記述するコンポーネントを一緒に配置することを意図しています。
•
└── components
├── Button.js
└── Button.stories.js
コンポーネントファイルとストーリーズファイルを一緒に置く
https://storybook.js.org/docs/react/configure/overview# configure-story-loading
Next.jsでコンポーネントを使う
これから作ったコンポーネントを実際に使ってみます。
Storybookで作ったコンポーネントをNext.jsで使う方法です。
Storybookの本質
ここまでの説明はReactアプリ(Next.js+TypeScript)からコンポーネントを独立する方法だけを説明してきました。
分離したからこそ、ありとあらゆることを試すことが出来るようになります。
この色々なことが出来るのがStorybookの本当の強みです。
これでようやくスタート地点に立つことができたわけです。
Storybookで何が出来るか?
基本的にアドオンを追加していきます。
essentialというアドオンが公式が推奨している最も基本的なアドオンとなります。
essentialアドオンは6つの基礎的なアドオンの集合体です。
sb init
を実行して Storybook をプロジェクトに組み込んだ場合、
Essentials アドオンはすでにインストールされています。
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials"
],
.storybook\main.jsを見ると"addons"の項目にすでに設定されています。
6つのアドオン
Essential addons
https://storybook.js.org/docs/react/essentials/introduction
Docs
Controls
Actions
Viewport
Backgrounds
Toolbars & globals
Doc Blocks
https://storybook.js.org/docs/react/writing-docs/doc-blocks# gatsby-focus-wrapper
Storybook のドキュメントページを構成するブロックです。
Storybook Docs は、サポートされているフレームワークのコンポーネントの args テーブルを自動的に生成します。これらのテーブルはコンポーネントの引数を一覧表示し、controlsと統合して、現在レンダリングされているストーリーの引数を変更することもできます。
Controls
https://storybook.js.org/docs/react/essentials/controls
Actions
https://storybook.js.org/docs/react/essentials/actions
Viewport
https://storybook.js.org/docs/react/essentials/viewport
Backgrounds
https://storybook.js.org/docs/react/essentials/backgrounds
Toolbars & globals
https://storybook.js.org/docs/react/essentials/toolbars-and-globals
2段階目 argsを色々いじってみる
import React from "react";
function Button({ children, disabled }) {
return (
<button
style={{
padding: 12,
fontSize: 12,
backgroundColor: disabled ? "lightgray" : "green",
color: disabled ? "gray" : "white",
}}
>
{children}
</button>
);
}
export default Button;
import React from "react";
import Button from "./Button";
export default {
title: "Components / Button",
component: "Button",
};
export const Default = () => <Button>Click me</Button>;
export const Disabled = () => <Button disabled>Don't Click me</Button>;
オリジナル
(公式)Storybookのチュートリアル一覧(4種類)
Storybook Tutorials
https://storybook.js.org/tutorials/
(公式)Storybookのチュートリアルその1
Storybook for React tutorial | Storybook Tutorials
https://storybook.js.org/tutorials/intro-to-storybook/react/en/get-started/
一番シンプルなStorybook(動画でシンプルなstorybookを学習します:英語)
Learn storybook in 8 minutes - YouTube
https://www.youtube.com/watch?v=U7lW6qAsvrg