最初に
私の作業の備忘録として記載されています。
StoryBookは色々なフレームワークに対応していますが、
htmlで使用する際の情報が少ないので試行錯誤しながら知り得た情報を記載していきます。
今回作成したいもの
StoryBook for htmlで実際に触れるComponentを作成し、
デザインガイドラインとして運用したいです。
使用する技術
- フレームワーク
- Component開発言語
- html
- scriptが必要な時は、Vanillajsで記載
- storyの開発にはTypeScriptを使用
- スタイル
- デプロイ環境
- CI/CD
- github Action
開発環境構築
以下のような手順を考えています。
- StoryBook for htmlプロジェクトの作成
- StoryBookをTypeScriptで記載できるように修正
- StoryBookでSassを使用できるようする
- StoryBookでVanillajsを実行できるようにする
- VanillajsはTypeScriptで開発できるようにしたい。
- Chromaticにdeploy
- githubのPRが作成された際にChromaticにdeployできるようにgithub Action作成
StoryBook for htmlプロジェクトの作成
公式のページを参照
https://storybook.js.org/docs/html/get-started/install
mkdir <project名>
cd <project名>
npx storybook init --type html
StoryBookをTypeScriptで記載できるように修正
現在のままでは、storyなどがjavascriptで記載されています。
javascriptでは開発したくないので、TypeScriptで開発できるように修正します。
(StoryBookはデフォルトでTypeScriptをサポートしてますので、tsconfig.jsonなど用意しなくても良いです。)
デフォルトのButtonComponentを例にしますが、変更点は以下です。
stories/Button.js
をstories/Button.ts
に変更し、以下の内容に書き換える。
import "./button.css";
export enum ButtonSize {
small = "small",
medium = "medium",
large = "large",
}
export type ButtonProps = {
primary?: boolean;
size?: ButtonSize;
label: string;
backgroundColor?: string;
onClick: () => void;
};
export const createButton = ({
primary = false,
size = ButtonSize.medium,
backgroundColor,
label,
onClick,
}: ButtonProps) => {
const btn = document.createElement("button");
btn.type = "button";
btn.innerText = label;
btn.addEventListener("click", onClick);
const mode = primary
? "storybook-button--primary"
: "storybook-button--secondary";
btn.className = ["storybook-button", `storybook-button--${size}`, mode].join(
" "
);
if (backgroundColor) btn.style.backgroundColor = backgroundColor;
return btn;
};
あまり特出して補足するところはないですが、
ButtonProps
を作成して、createButton
の引数を定義すると使いやすくなります。
次にstoryもTypeScript化します。
stories/Button.stories.js
をstories/Button.stories.ts
に変更し、以下の内容に書き換えます。
import { ButtonProps, ButtonSize, createButton } from "./Button";
import { Meta, StoryFn } from "@storybook/html";
export default {
title: "Example/Button",
argTypes: {
backgroundColor: { control: "color" },
label: { control: "text" },
onClick: { action: "onClick" },
primary: { control: "boolean" },
size: {
control: { type: "select" },
options: ButtonSize,
},
},
} as Meta<ButtonProps>;
const Template: StoryFn<ButtonProps> = ({ label, ...args }) => {
return createButton({ label, ...args });
};
export const Primary = Template.bind({});
Primary.args = {
primary: true,
label: "Button",
};
export const Secondary = Template.bind({});
Secondary.args = {
label: "Button",
};
export const Large = Template.bind({});
Large.args = {
size: ButtonSize.large,
label: "Button",
};
export const Small = Template.bind({});
Small.args = {
size: ButtonSize.small,
label: "Button",
};
補足としては2点あります。
- argTypesに型を当てるために、Metaという型を当てています。
- Templateの型はStoryFnでTemplateの型を定義することができます。
また、Template.bind({})
がanyになってしまう方で、修正したいという方は
tsconfig.json
を別途用意して、"strictBindCallApply": true
を追記すると型が指定されます。
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Node",
"target": "ES2020",
"jsx": "react",
"strictNullChecks": true,
"strictFunctionTypes": true,
"sourceMap": true,
"strictBindCallApply": true
},
"exclude": ["node_modules", "**/node_modules/*"]
}
StoryBookでSassを使用できるようにする
StoryBookでSassを使用してスタイルを記述したいと考えています。
Sassの最新版を使用するためには、StoryBookの中で使用されているwebpack5にしないといけません。
※StoryBookがデフォルトで使用しているのはwebpack4なので、webpack4でsassを使用するためには、この後インストールするライブラリのversionを色々と調整しないといけないのでめんどくさくなります。
webpack5の導入
npm uninstall @storybook/builder-webpack4 @storybook/manager-webpack4
npm i -D @storybook/builder-webpack5 @storybook/manager-webpack5
webpack5のbuilderをinstallします。
uninstallは必要ないですが、ゴミは削除しておきます。
.storybook/main.js
に設定を追加します。
module.exports = {
stories: [
"../stories/**/*.stories.mdx",
"../stories/**/*.stories.@(js|jsx|ts|tsx)",
],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
core: {
builder: "webpack5", // ここにwebpack5を使うよ!という内容を記述
},
framework: "@storybook/html",
};
coreのbuilderに"webpack5"と記載します。
sassの導入(必要なライブラリも追加)
各種必要なライブラリの追加
npm i -D sass css-loader sass-loader style-loader @storybook/preset-scss
.storybook/main.js
のaddonsに@storybook/preset-scss
を追加
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
"@storybook/preset-scss", // 追加
],
sassの適用
これは好みですが、一つ一つのファイルにcssを当てるのめんどくさいので、
大元のファイルのcssを反映させたいと思います。
まず、すべてのcssを取りまとめるcssファイルを作成します。
私の場合だと、stories/styles/main.scss
を作成し、以下のように記述
@import "./components/button.scss";
@import "./components/header.scss";
@import "./components/page.scss";
この例ではstories/styles/components/
のディレクトリを作成し、そこに各のComponentのcssを記述し、importさせています。
そして、.storybook/preview.js
にmain.scss
を適用します。
import "../stories/styles/main.scss";
これで、すべてのstoriesに対してmain.scss
に記載されたcssが適用されます。
この作業でディレクトリ構成は以下になります。
※不要そうなものは削除してます。
./
├── package-lock.json
├── package.json
├── stories
│ ├── Button.stories.ts
│ ├── Button.ts
│ ├── Header.stories.ts
│ ├── Header.ts
│ ├── Introduction.stories.mdx
│ ├── Page.stories.ts
│ ├── Page.ts
│ └── styles
│ ├── components
│ │ ├── button.scss
│ │ ├── header.scss
│ │ └── page.scss
│ └── main.scss
└── tsconfig.json
StoryBookでVanillajsを実行できるようにする
StoryBookでVanillajsを実行したいのですが、一々scriptタグで囲んで文字列で入れるのめんどくさいです。
文字列だと、editorの補完が効かないので開発効率も落ちるでしょう。
どんな内容がいいのかは開発したい内容によると思いますが、
以下のstorybook-addon-run-scriptを使用して、scriptを適用させます。
必要なものをインストール
npm i -D storybook-addon-run-script raw-loader
-
storybook-addon-run-script
- StoryBookの実行前にscriptを埋め込みを行なってくれる
-
raw-loader
- jsファイルをスクリプトとしてではなく、文字列として読み取ることができます。
- scriptはStringでないと読み込めないので、特定のファイル(.runscript.js)をStringとして読み込みます。
scriptの用意
適当にstories/scripts/page.runscript.js
を作成します。
setTimeout(() => {
const title = document.getElementById("title");
if (title) {
title.innerText = "チェンジタイトル";
}
}, 5000);
function alertTest() {
alert("アラートです。");
}
storiesにscriptを読み込ませる
stories/Page.stories.ts
に先ほど作成したスクリプトを読み込ませます。
import { within, userEvent } from "@storybook/testing-library";
import { createPage } from "./Page";
import { StoryFn, Meta } from "@storybook/html";
import { withRunScript } from "storybook-addon-run-script/html";
import runScript from "!!raw-loader!./scripts/page.runscript.js"; // !!raw-loader!を追記
export default {
title: "Example/Page",
parameters: {
layout: "fullscreen",
},
decorators: [withRunScript(runScript)], // decoratorsにwithRunScript(runScript)を追記
} as Meta;
const Template: StoryFn<any> = () => createPage();
export const LoggedOut = Template.bind({});
export const LoggedIn = Template.bind({});
LoggedIn.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
const loginButton = await canvas.getByRole("button", { name: /Log in/i });
await userEvent.click(loginButton);
};
注意点としてスクリプトファイルを読み込む際には、!!raw-loader!
を追記してください。
これを行うことで、runScriptはStringとして扱われます。
これで、StoryBookを読み込み直すと、5秒後にtitleが変更されます。
また、Pageに<button onclick=alertTest();>alertTest</button>
みたいなhtmlを埋め込むと
scriptで定義したalertTest関数が実行され、alertが表示されます。
typescriptでscriptを開発したい人
scriptをtypescriptで開発したい人には、.runstories.tsでスクリプトを記載して、
tsc コマンドでjsに変換することで実現できます。
ただのts -> jsへのコンパイルなので詳細は省略します。
Chromaticにdeploy
ChromaticとはStoryBookを簡単にdeployして、
公開したり、特定のユーザに見せて開発状況を共有したりできます。
Chromaticへのログインおよびプロジェクト作成
とりあえず、Chromaticのドキュメント通りにSignUpして、プロジェクト作成してください。
ログインする際は、私の場合はgithubと連携する必要があったので
githubアカウントを使用しています。
Chromaticをインストール
ドキュメント通りにChromaticをインストール
npm install --save-dev chromatic
ChromaticにStoryBookをデプロイ
npx chromatic --project-token <your-project-token>
project-tokenはプロジェクトを作成した際に出てくるtokenを使用してください。
※この時gitを使用してないとエラーになるみたいなので、プロジェクトをgit管理してください。
もちろんやってるよね??
あと、今の作業ブランチにCommitが一つもないのもエラーになるみたい??
Chromaticにログインして、以下のようにbuildが確認できればdeploy完了です。
最後に、chromaticコマンドを実行するとpackage.json
に以下のコマンドが追加されます。
"scripts": {
"chromatic": "npx chromatic --project-token=<your-project-token>"
},
ptoject-tokenが下手書きされていて、危ないかつ、後々githubAcrtionで使用するため
以下のように変更してます。
"scripts": {
"chromatic": "npx chromatic --project-token=${CHROMATIC_PROJECT_TOKEN}"
},
上記のため、今後ローカルからChromaticにdeployする場合は、
export CHROMATIC_PROJECT_TOKEN=<your-project-token>
npm run chromatic
となります。