目次
はじめに
こんにちは、Webエンジニアの柴田です。
前回 StorybookからReact Cosmosに移行した話を執筆しました。
StorybookからReact Cosmosに移行した際に、実はReact Cosmosのキャッチアップに苦労しました。また日本語の記事もあまりありませんでしたので、他の方のお役に立てばということで、今回チュートリアルの記事を執筆することにしました。
本記事ではReact Cosmosのチュートリアルと題して、React CosmosのインストールからReact Cosmosの設定、React Cosmosでのコンポーネントのストーリーの作成方法、React Cosmosの便利な機能をご紹介します。
環境
React 17.0.2
React Cosmos 5.7.2
React Cosmos インストール
# Using npm
npm i -D react-cosmos
# or Yarn
yarn add --dev react-cosmos
package.jsonに起動スクリプトを記入
"scripts": {
"cosmos": "cosmos",
"cosmos:export": "cosmos-export"
}
React Cosmos 起動コマンド
# Using npm
npm run cosmos
# or Yarn
yarn cosmos
起動に成功すると下記のスクショの画面が表示されます。
設定
・必要な設定としては、cosmos.config.jsonの設定 + webpackの設定 + babelの設定
が必要になります。
・Reactを使用している場合とNext.jsを導入している場合、その他で設定方法が異なってきます。
・設定等でトラブルがあれば、下記をご確認頂けたらと思います。
https://github.com/react-cosmos/react-cosmos/blob/main/docs/README.md#troubleshooting
cosmos.config.json
cosmos.config.json
の定義方法は2パターンに分かれます。
- ルートディレクトリ直下に
cosmos.config.json
を作成した場合
CLIで実行するときにファイルパスを定義しなくても、起動時にReact Cosmosが読み取ってくれます。デフォルトではこちらになります。
- カスタムディレクトリに
cosmos.config.json
を作成した場合
CLIで実行するときに--config={cosmos.config.jsonのファイルパス}
を引数に渡してあげる必要があります。
cosmos.config.jsonの中身
// (例)
// cosmos.config.json
{
"webpack":{
"configPath": "./webpack.config.js"
},
"globalImports": [
// (例)
"./common/reset.css",
],
"staticPath": "public",
"publicUrl": "./",
}
webpack
optional
- cosmosの仕様上、デフォルトではReact Cosmosが定義しているwebpackが読み込まれます。
- 自前もしくは指定のwebpackがある場合は、cosmos.config.jsonのconfigPathにて、webpackのconfigのファイルパスを定義する必要があります。定義しないとReact Cosmosが定義しているwebpackが読み込まれます。
- TypeScript(JavaScript)やCSS、image、Fontなどをカスタムにバンドル設定したい場合は自前でwebpackを設定する必要があります。この場合は必須の設定になります。
globalImports
optional
- cosmosに読み込ませたいcss定義のためのものです。
例: reset.css, base.css, common.css etc...
staticPath
optional
- React Cosmos内部で静的アセットを読み込むためのものです。
publicUrl
optional
- ネストされたファイルのパスを相対パスで参照できるようにするためのものです。
Create React Appの設定
・webpack.configPath
をreact-scripts/config/webpack.config
に設定します。
・staticPath
をsrc
に設定します。
・watchDirs
をsrc
に制限して、該当ディレクトリ以外のファイル変更を無視します。
・globalImports
に カスタムcssや自前で用意したcssを追加します。
// cosmos.config.json
{
"staticPath": "src",
"watchDirs": ["src"],
"globalImports": ["src/index.css"],
"webpack": {
"configPath": "react-scripts/config/webpack.config"
}
}
Next.jsを導入している場合の設定
cosmos.config.jsonの設定
・html-webpack-plugin
をdevDependenciesにインストールします。(必須)
・staticPath
をpublic
に設定します。
・globalImports
にstyles/globals.css
を追加します。
その他カスタムcssや自前で用意したcssがあれば、追加します。
// cosmos.config.json
{
"globalImports": ["styles/globals.css"],
"staticPath": "public"
}
babelの設定
・ルートディレクトリ直下に .babelrc
を作成し、"next/babel"
を presets
に追加します。
// .babelrc
{
"presets": ["next/babel"],
"plugins": []
}
Webpackの設定
・(S)CSS、Image、Font、TypeScript 用のローダー設定を記述します。
・その他プラグイン、オプション周りの設定を記述します。
// webpack.config.js
const webpack = require('webpack');
const path = require('path');
/**
* (S)CSS 用のローダー設定。
*/
const cssRules = {
test: /\.s?css/,
use: [
{ loader: require.resolve('style-loader') },
{ loader: require.resolve('css-loader'),
options: {
url: false,
importLoaders: 1,
sourceMap: true
}},
],
exclude: /node_modules/,
};
/**
* Image 画像用のローダー設定
*/
const imageRules = {
test: /\.(jpg|png|gif|svg|eot)$/,
loader: require.resolve('file-loader'),
options: {
name: '[name].[ext]'
}
}
/**
* Font用のローダー設定
*/
const fontRules = {
test: /\.(woff|woff2|ttf)$/,
loader: require.resolve('file-loader'),
options: {
name: '[name].[ext]',
}
}
/**
* TypeScript 用のローダー設定。
*/
const tsRules = {
exclude: /node_modules/,
test: /\.tsx?$/,
use: [
{
loader: require.resolve('ts-loader'),
options: {
transpileOnly: true,
compilerOptions: { jsx: 'react', module: 'commonjs' }
}
},
{
loader: require.resolve('babel-loader'),
options: {
configFile: path.resolve(__dirname, `.babelrc`)
}
}
]
};
/**
* Webpackの設定をカスタマイズ
*/
const config = {
mode: 'development',
target: 'web',
optimization: {
minimize: false
},
module: {
rules: [
tsRules, imageRules, cssRules
]
},
resolve: {
extensions: ['.ts', '.tsx', '.js'],
},
plugins: [
new webpack.ProvidePlugin({
React:'react',
})
]
}
module.exports = config;
ReactやNext.jsなどのフレームワークを使用していない場合
・html-webpack-plugin
をdevDependenciesにインストールします。(必須)
・staticPath
を public
に設定します。
・publicUrl
を "./"
に設定します。
cosmos.config.json
{
"globalImports": [],
"staticPath": "public",
"publicUrl": "./",
"webpack": {
"configPath": "./webpack.config.js"
}
}
・ ルートディレクトリ直下に .babelrc
を作成し、"presets"
に "@babel/env"
, "@babel/react"
を追加します。
// .babelrc
{
"presets": ["@babel/env", "@babel/react"],
"plugins": []
}
・Webpackの設定
必須になります。
記述方法はNext.jsでの webpack.config.js
のファイルを参考にして頂けたらと思います。
Fixtureファイルの作成方法
React Cosmosではコンポーネントのストーリーを作成するとき、Fixtureファイルを作成する必要があります。
Fixtureファイル形式は下記の2パターンになります。
xxx.fixture.{js,jsx,ts,tsx}
/__fixtures__/xxx.{js,jsx,ts,tsx}
例)
Text.fixture.tsx
/__fixtures__/Text.tsx
コンポーネントのFixtureファイルの書き方
主要なパターンを3つ紹介します。
1. Propsを必要としないコンポーネントの場合
// Text.fixture.tsx
export default <Text>Hoge!!!</Text>
2. 可変Propsを渡す場合
Fixtureファイル内でもReact Hooksを使用できます。
useStateを使い、可変Propsを渡すことでシュミレーションできます。
// CounterButton.fixture.tsx
export default () => {
const [count, setCount] = useState(0);
const handleCounter = useCallback(() => {
setCount((prev) => prev + 1);
}, []);
return <CounterButton count={count} onClick={handleCounter}/>;
};
3. コンポーネントの複数のパターンを一つのFixtureファイルにまとめたい場合
コンポーネントのパターンをオブジェクト形式でまとめて、出力するだけです。
// AlertBox.fixture.tsx
import AlertBox from './AlertBox';
export default {
Info: <AlertBox type="info" message="ログインしました" />,
Success: <AlertBox type="success" message="ログイン成功しました" />,
Warning: <AlertBox type="warning" message="パスワード変更してください" />,
Error: <AlertBox type="error" message="ただいま大変混み合っています。" />,
};
Control Panelについて
React Cosmos UI上でコンポーネントのPropsの値を動的に操作・変更して、コンポーネントの挙動を確認できる機能になります。StorybookのAddonであるControls機能に似ています。
useValue
useValueは動作的にはReactのuseStateと変わらず、useStateとの違いはControl Panel名をカスタムできるAPIです。
書き方としては第一引数にControl Panel名、第二引数にはデフォルト値を定義します。
import React from 'react';
import { useValue } from 'react-cosmos/fixture';
import Modal, { BoxWithTitle } from './Modal';
export default () => {
const [isActive, setIsActive] = useValue('モーダルを表示するかどうか', { defaultValue: false });
return (
<div id="parent">
<Modal
isActive={isActive}
>
<BoxWithTitle title="タイトル">
<p>説明</p>
<p>コンテンツ</p>
<p>文章</p>
</BoxWithTitle>
</Modal>
</div>
);
};
上記のコードで実装すると、第一引数で定義したControl Panel名がそのまま反映されます。(下記の赤枠を参照)
赤枠の拡大画像
useSelect
useSelectはreact cosmosのUI上でコンポーネントのPropsの値をプルダウン形式で変更できるAPIです。
書き方としては、useSelectの第一引数にControl Panel名、第二引数のoptionsにプルダウンの選択肢を定義します。
import { useSelect } from 'react-cosmos/fixture';
import DisplayNumberSelector from './DisplayNumberSelector';
export default () => {
const [displayedNumber] = useSelect('表示数', {
options: ['48', '120'],
});
return (
<DisplayNumberSelector
displayedNumber={displayedNumber}
onClickCallBack={() => {}} // mock
/>
);
};
useSelectの見た目は下記のスクリーンショットの赤枠部分を参照。
赤枠の拡大画像
おわりに
React Cosmos チュートリアルでした。
React Cosmosの導入検討している方のご参考になれば幸いです。
参考
・https://github.com/react-cosmos/react-cosmos
・Cosmosの関連記事で執筆したStorybookからReact Cosmosに移行した話
https://techlab.q-co.jp/articles/48/