# 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)