# storybook
### intro
- storybook解決了甚麼: codebase長大時共用的 component變得很chaotic,storybook提供一個元件型錄 for 展示/紀錄/測試 元件的 props & 不同的樣貌
![](https://i.imgur.com/QHlz5ny.png)
- 甚麼是story
- 以JS角度: 一個function (A story is a function that describes how to render a component)
- <b>Default export => component
- named exports => the stories</b>
- 以storybook介面角度: 一個component (A story is a component with a set of arguments that define how the component should render)
- jest 測試: 除了提供一個介面整理元件外,storybook的另一個好處是有一個類似sandbox的地方可以測試元件 [待研究](https://storybook.js.org/docs/react/writing-tests/introduction)
### write a sotry (step by step)
####
#### 0. 建立環境設定檔
```javascript=
module.exports = {
stories: ['../src/**/*.stories.js'], // 入口,所有stories檔案都在這個目錄下
addons: [ // storybook的擴充功能 類似套件
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
],
webpackFinal: async (config, { configType }) => {
// 設定 path alias
config.resolve.alias = {
...config.resolve.alias,
'@components': path.resolve(__dirname, '../src/components'),
'@module': path.resolve(__dirname, '../src/module'),
'@assets': path.resolve(__dirname, '../src/assets'),
'@utils': path.resolve(__dirname, '../src/utils'),
};
config.module.rules.push({
test: /\.(scss)$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
},
{
loader: require.resolve('sass-loader'),
options: {
implementation: require('sass'),
// 設定全域CSS檔案 (或環境設定檔也可用這個方法+)
// 詳https://webpack.js.org/loaders/sass-loader/#additionaldata
additionalData: ` @use "src/assets/scss/utils/variables";
@use "src/assets/scss/utils/mixin";`,
},
},
],
include: path.resolve(__dirname, '../'),
});
return config;
},
framework: '@storybook/react',
core: {
builder: '@storybook/builder-webpack5',
},
};
```
不贅述細節,詳情請洽 `/packages/ui/.storybook` OR [storybook文件](https://storybook.js.org/docs/react/configure/overview#configure-story-rendering) OR 再視需求看webpack文件
#### 1. 依設定檔的入口路徑建立檔案 & import component & 他的scss檔案
檔名要以 `*.stories.@(j|t)sx?.` 結尾 (除非有自定義設定檔)
For example, "Badge.stories.js" or "Badge.stories.tsx"
#### 2. default export (定義元件)
- title: 這個變數會影響storybook的目錄架構,可以用來group components
`ROOTS(全大寫) / Components / Buttons`
`ROOTS(全大寫) / Components / ActivityTitle`
=> storybook官方放建議 stories 的架構要跟專案的架構一樣
=> [可以自動產生title](https://storybook.js.org/docs/react/configure/overview#configure-story-loading)
=> [當component只有一個story的時候,也是由title控制,避免多包一層](#情境d-元件只有一個story-gt-name-hoisting)
- *component: 主角
- <b>args</b>: 物件object,會出現在storybook的控制介面,可以控制的參數/props, 可以在三種層級定義:
- Story args [最常用的情況](#3-named-export-定義元件的stories): 寫在 <b>name export</b>
- component args: <b>寫在default export</b>
- argTypes: 設定參數(如control, description, defaultValue etc)
- args: 賦值
<b>可以把argTypes想像成propTypes, args想像成props</b>
- global args (在preview.js export args object),使用情境可能是環境變數,如之後要分開首頁&路標頁的元件可能就可以在那邊定義
```
// preview.js
// 所有stories都要有一個theme的props
export const argTypes = { theme: { control: 'select', options: ['homepage', 'sign'] } };
// 設定theme的預設值為light
export const args = { theme: 'homepage' };
```
#### 3. named export (定義元件的stories)
- bind => 複製一份template (JS 的 this 會指向剛剛assign的const)
```
💡 Template.bind({}) is a standard JavaScript technique for making a copy of a function.
We copy the Template so each exported story can set its own properties on it.
```
- args => react 元件的props
#### <span style="color:blue">< RECAP step 1~3 > </span>
1. 建立檔案在 main.js 指定的entry目錄
2. default export 一個object
```
{
title: Components/Button
component: Button,
}
```
3. 很多個named export => 元件的故事
```
// 3-1. 建立一個Template元件 等等可以複製用的
const Template = (args) => <Button {...args} />;
// 3-2. 複製 Template
export const ButtonLogin = Template.bind({});
// 3-3. 寫 Story args (上面提到寫arg的方法有三種)
ButtonLogin.args = {
text: '登入',
btnType: 'a',
btnSize: 'sm',
btnShape: 'oval',
btnColor: 'tertiary',
};
```
#### 4. [proptypes](https://github.com/facebook/prop-types)寫好會自動設定好storybook的[argstable](https://storybook.js.org/docs/react/writing-docs/doc-block-argstable) (storybook的args控制介面)
* 多用 oneOf, shape...之類的去定義寫死的enum type
* instead of `PropTypes.string`, use `PropTypes.oneOf(['homepage', 'sign'])`
#### 5. 依需求去調整
##### 情境a. prop加上 description
##### 情境b. 改寫story名稱
##### 情境c. 新增props專門for storybook展示用
##### 情境d. 元件只有一個story => name hoisting
##### 情境e. [打造展示頁](https://storybook.js.org/docs/react/writing-stories/build-pages-with-storybook)
##### 情境f. [多個元件的故事](https://storybook.js.org/docs/react/writing-stories/stories-for-multiple-components)