組件庫開發(fā)—Storybook生成UI文檔

簡介

Storybook是一個UI組件的開發(fā)環(huán)境披蕉。

使用

初始化StoryBook環(huán)境

$ npx -p @storybook/cli sb init

storybook自動檢測開發(fā)環(huán)境重父,安裝依賴纲仍。

執(zhí)行以上命令行會進(jìn)行以下操作:

1. 自動生成以下目錄結(jié)構(gòu):

├─.storybook // Storybook 全局配置文件
    ├─ main.js // 入口文件
    └─ preview.js // 頁面展示恰响、全局資源配置
└─stories // 示例代碼
    └─assets

2. 更新pkg#run-scripts:

...
  "scripts": {
    "storybook": "start-storybook -p 6006 -h 0.0.0.0", // 啟動本地服務(wù)以預(yù)覽
    "build-storybook": "build-storybook"  // 構(gòu)建
  },
...

核心文件

main.js

module.exports = {
  "stories": [  // 組件Stories目錄所在 —— Storybook會載入配置路徑下的指定文件渲染展示
    "../stories/**/*.stories.mdx",
    "../stories/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [  // Storybook所用插件 —— Storybook功能增強
    "@storybook/addon-links",
    "@storybook/addon-essentials"
  ],
  "framework": "@storybook/vue3" // Storybook所用框架 —— Vue環(huán)境支持
}

該文件定義StoryBook與編譯相關(guān)的配置治专。

preview.js

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  }
}

該文件引入全局依賴竞穷,定義StoryBook渲染相關(guān)的配置吴藻。

簡單示例

入口配置

更新.storybook/main.js,將組件所在目錄注冊到入口文件聲明中:

module.exports = {
  "stories": [  // 組件Stories目錄所在 —— Storybook會載入配置路徑下的指定文件渲染展示
    "../packages/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  ...
}

組件Story編寫

import SubmitForm from "./index"; // 引入組件
import { SchemaType, RuleTrigger } from "./src/schemas/baseSchema";

const caseSchema = [ // 示例數(shù)據(jù)
  {
    key: "moduleName",
    name: "title",
    type: SchemaType.Text,
    label: "欄目名稱",
    placeholder: "請輸入欄目名稱",
    attrs: {
      //
    },
    rules: [
      {
        required: true,
        message: "欄目名稱必填~",
        trigger: RuleTrigger.Blur,
      },
    ],
  },
  ...
];

export default {
  title: "ui組件/SubmitForm", // 展示標(biāo)題:使用路徑定義命名空間 —— 分組懈凹、分類
  component: SubmitForm,
};

const Template = (args: any) => ({ // 渲染組件
  components: { SubmitForm },
  setup() {
    return {
      ...args,
    };
  },
  template: '<submit-form :schema="schema"></submit-form>',
});

export const 基本應(yīng)用 = Template.bind({}); // 組件應(yīng)用示例

(基本應(yīng)用 as any).args = {
  schema: caseSchema,
  ref: "submitFormRef",
};

其中蜀变,

默認(rèn)導(dǎo)出的是組件的元數(shù)據(jù),包含歸屬組件介评、組件所屬StoryBook文檔分類库北、組件參數(shù)交互式聲明...

更多配置參見:

ArgsTable配置

Contorl配置

命名導(dǎo)出的Story ( export const 基本應(yīng)用 = Template.bind({}); ) 是一個函數(shù),變量名為StoryBook文檔展示的標(biāo)題们陆,另一種導(dǎo)出方式參考下文寒瓦。

全局依賴配置

因為示例代碼中依賴element-plus,通過上述展現(xiàn)的頁面沒有樣式坪仇,所以杂腰,StoryBook渲染需要額外引入element-plus主題:

// .storybook/preview.js
import "element-plus/lib/theme-chalk/index.css";

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  }
}

啟動本地服務(wù)

啟動服務(wù)

更新pkg#storybook:

  // package.json
  "scripts": {
    "storybook": "start-storybook -p 6006 -h 0.0.0.0",
    "build-storybook": "build-storybook"
  },

命令行執(zhí)行:

$ npm run storybook

效果展示

image

默認(rèn)參數(shù)欄只展示兩項,如需更多參數(shù)信息椅文,修改 preview.js 文件:

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
  controls: {
    expanded: true // 展開所有參數(shù)信息
  }
}

image

在Stories中使用第三方UI庫

以ElementPlus為例:

全局配置

如果 babel.config 沒有配置按需加載喂很,可直接編輯.storybook/preview.js

// .storybook/preview.js
import elementPlus from 'element-plus';
import { app } from '@storybook/vue3'

app.use(elementPlus);
export const decorators = [
  (story) => ({
    components: { story, elementPlus },
    template: '<elementPlus><story/></elementPlus>'
  })
];
import "element-plus/lib/theme-chalk/index.css";

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  }
}

Notes:配置按需加載后,import elementPlus from 'element-plus';導(dǎo)入elementPlus報錯:elementPlus is not defined —— 全局加載皆刺、按需加載不能在同一項目中使用少辣。

按需加載

在需要使用ElementPlus的Stories中直接引入即可:

// SubmitForm.stories.ts
import { ElButton } from 'element-plus';
import SubmitForm from "./index";
import { SchemaType, RuleTrigger } from "./src/schemas/baseSchema";

const caseSchema = [
  {
    key: "moduleName",
    name: "title",
    type: SchemaType.Text,
    label: "欄目名稱",
    placeholder: "請輸入欄目名稱",
    attrs: {
      //
    },
    rules: [
      {
        required: true,
        message: "欄目名稱必填~",
        trigger: RuleTrigger.Blur,
      },
    ],
  },
  ...
];

export default {
  title: "ui組件/SubmitForm",
  component: SubmitForm,
};

const Template = (args: any) => ({
  components: { SubmitForm, ElButton },
  setup() {
    return {
      ...args,
    };
  },
  template: '<submit-form :schema="schema" ref="submitFormRef"></submit-form><el-button @click="submit">提交</el-button>',
});
export const 基本應(yīng)用 = Template.bind({});
(基本應(yīng)用 as any).args = {
  schema: caseSchema,
};

補充已有示例交互

// SubmitForm.stories.ts
import { ElButton } from "element-plus";
import { ref } from "vue";
import SubmitForm from "./index";
import { SchemaType, RuleTrigger } from "./src/schemas/baseSchema";

const caseSchema = [
  {
    key: "moduleName",
    name: "title",
    type: SchemaType.Text,
    label: "欄目名稱",
    placeholder: "請輸入欄目名稱",
    attrs: {
      //
    },
    rules: [
      {
        required: true,
        message: "欄目名稱必填~",
        trigger: RuleTrigger.Blur,
      },
    ],
  },
  ...
];

export default {
  title: "ui組件/SubmitForm",
  component: SubmitForm,
};
const Template = (args: any) => ({
  components: { SubmitForm, ElButton },
  setup() {
    const { refName } = args;
    const submitFormRef = ref();
    function submit() {
      console.log(submitFormRef.value.values);
    }
    function onRuntimeChange(name: string, value: any) {
      console.log(name, " = ", value);
    }
    return {
      submit,
      onRuntimeChange,
      [refName]: submitFormRef,
      ...args,
    };
  },
  template: `
      <submit-form :schema="schema" :ref="refName" @runtimeChange="onRuntimeChange"></submit-form>
      <el-button @click="submit">提交</el-button>
    `,
});
export const 基本應(yīng)用 = Template.bind({});

(基本應(yīng)用 as any).args = {
  refName: "submitFormRef",
  schema: caseSchema,
};

這里做了兩件事:

  • 增加提交按鈕

  • 增加數(shù)據(jù)提交交互

配置參數(shù)文檔

默認(rèn)文檔展示

默認(rèn)查看到的文檔是兩欄展示:

image

更新 .storybook/preview.js 文件:

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
  controls: {
    expanded: true
  }
}

參數(shù)的所有配置都展示:

image

參數(shù)配置

通過配置argTypes可以補充參數(shù)信息:

// SubmitForm.stories.ts
...
export default {
  title: "ui組件/SubmitForm",
  component: SubmitForm,
  argTypes: {
    refName: {
      description: '表單組件引用',
      type: {
        required: true,
      },
      table: {
        defaultValue: {
          summary: 'defaultNameRef',
        }
      },
      control: {
        type: 'text'
      }
    },
    schema: {
      type: {
        required: true,
      },
      table: {
        type: {
          summary: '渲染表單所需JSON結(jié)構(gòu)',
          detail: 'JSON結(jié)構(gòu)包含表單渲染、交互所需要的必要字段羡蛾,也包含表單的校驗規(guī)則',
        },
        defaultValue: {
          summary: '[]',
          detail: `[
              {
                key: "moduleName",
                name: "title",
                type: SchemaType.Text,
                label: "欄目名稱",
                placeholder: "請輸入欄目名稱",
                attrs: {
                  //
                },
                rules: [
                  {
                    required: true,
                    message: "欄目名稱必填~",
                    trigger: RuleTrigger.Blur,
                  },
                ],
              }
            ]
          `
        }
      }
    },
    runtimeChange: {
      description: '實時監(jiān)聽表單的更新',
      table: {
        category: 'Events',
      },
    }
  }
};
...

更多相關(guān)配置見:

ArgsTable配置

Contorl配置

StoryBook功能模塊

一個Story是以函數(shù)形式描述如何渲染組件的方式漓帅。

args提供動態(tài)參數(shù),提供省時的便利:

  • 參數(shù)可在Controls面板實時編輯林说,可檢測組件在不同參數(shù)下的狀態(tài)煎殷。

  • 事件可在Actions面板查看日志輸出。

    • 需要配置actions

自定義Stories展示名稱

命名模塊

export const 自定義名稱 = () => ({
  components: { Button },
  template: '<Button primary label="Button" />',
});

【推薦】storyName屬性設(shè)定

export const Primary = () => ({
  components: { Button },
  template: '<Button primary label="Button" />',
});
Primary.storyName = '自定義名稱';

Args

提供交互動態(tài)修改Props腿箩、Slots豪直、styles、inputs...的方式珠移,允許在Storybook交互界面中實時編輯弓乙,不必改動底層組件代碼。

通過Storybook交互界面指定Args

界面直接修改

通過URL設(shè)定Args

?path=/story/avatar--default&args=style:rounded;size:100

由于安全策略(XSS攻擊)和特殊值钧惧,傳入URL需要處理暇韧,見詳情

全局依賴

// .storybook/preview.js
import { app } from '@storybook/vue3';
import Vuex from 'vuex';

//?? Storybook Vue app being extended and registering the library
app.use(Vuex);

export const decorators = [
  (story) => ({
    components: { story },
    template: '<div style="margin: 3em;"><story /></div>',
  }),
];

靜態(tài)資源訪問

// .storybook/main.js

module.exports = {
  stories: [],
  addons: [],
  staticDirs: ['../public'],
};

// .storybook/main.js

module.exports = {
  staticDirs: [
     { 
        from: '../my-custom-assets/images', 
        to: '/assets' 
     }
  ],
};

查看更多配置

定制化主題

修改Logo

安裝依賴:

npm i -D @storybook/addons @storybook/theming

修改pkg#scripts

// pkg#scripts
"storybook": "start-storybook -p 6006 -s public",
"build-storybook": "build-storybook -s public",

新建.storybook/manager.js文件:

import { addons } from "@storybook/addons";
import theme from "./themes/theme";

addons.setConfig({
  theme: theme
})

創(chuàng)建./storybook/themes/theme.js:

// .storybook/themes/theme.js
import { create } from '@storybook/theming';

export default create({
  base: 'light',
  brandTitle: 'Custom StoryBook', // logo不展示時浓瞪,替代文本alt
  brandImage: '/logo.png',
});

Notes:brandImage和brandTitle同時配置的情況下懈玻,只有一項起作用,優(yōu)先級brandImage > brandTitle

Notes:自定義主題時乾颁,base配置是必填的。

// package.json
{
  ...
  "scripts": {
    "replace": "rimraf storybook-static/favicon.ico && cpr .storybook/themes/favicon.ico storybook-static/favicon.ico",
    "storybook": "start-storybook -p 6006 -h 0.0.0.0",
    "build-storybook": "build-storybook && npm run replace"
  },
}

Notes:打包的話,需要用本地圖標(biāo)替換storybook包內(nèi)的默認(rèn)圖標(biāo)廊敌。

這里使用了Cli參數(shù)-s指定靜態(tài)文件訪問地址,更推薦在main.js中配置:

// .storybook/main.js

module.exports = {
  stories: [],
  addons: [],
  staticDirs: ['/public'],
};

修改站點Title湿右、favicon

新增.storybook/manager-head.html:

<link rel="shortcut icon" href="/favicon.ico">
<script>
  var observer = new MutationObserver(function(mutations) {
    if (document.title.match(/Storybook$/)) {
      document.title = "M.UI | GameCenter";
    }
  }).observe(document.querySelector("title"), {
    childList: true,
    subtree: true,
    characterData: true
  });
</script>

參見更多配置

組件樣式—Scss

組件樣式需要storybook-addon的支持

npm i -D @storybook/preset-scss

修改.storybook/main.js

module.exports = {
  "stories": [
    "../packages/**/*.stories.@(js|jsx|ts|tsx)",
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/preset-scss"
  ],
  "framework": "@storybook/vue3"
}

Rem支持

替換@storybook/preset-scss為@storybook/addon-postcss:

npm uninstall -D @storybook/preset-scss
npm i -D @storybook/addon-postcss
# 修改webpack內(nèi)核為版本5
# 初始化環(huán)境時修改
npx -y sb init --builder webpack5
# 初始化時未設(shè)定,后續(xù)修改
npm i -D @storybook/builder-webpack5
npm i -D @storybook/manager-webpack5
// 修改.storybook/main.js
module.exports = {
  "stories": [
    "../packages/**/*.stories.@(js|jsx|ts|tsx)",
  ],
  "addons": [
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    {
      name: '@storybook/addon-postcss',
      options: {
        postcssLoaderOptions: {
          implementation: require('postcss'),
        },
        sassLoaderOptions: {
          implementation: require('sass'),
        }
      },
    }
  ],
  core: {
    builder: 'webpack5',
  },
  webpackFinal: (config) => {
    config.module.rules.push({
      test: /\.scss$/,
      use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
    });
    return config
  },
  "framework": "@storybook/vue3"
}

在preview.js中引入flexible.js罚勾,并定義視窗:

import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';
import '../assets/js/flexible.all';
import './assets/stylesheets/sb.scss';
import '../assets/stylesheets/reset.scss';

export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
  viewport: {
    viewports: INITIAL_VIEWPORTS, // newViewports would be an ViewportMap. (see below for examples)
    defaultViewport: 'iphone6',
  },
  controls: {
    expanded: true
  }
}

詳細(xì)內(nèi)容參見文章地址

同類對比

Decorator自定義畫布毅人、文檔樣式

Decorator通過包裹Story來增強其表現(xiàn)形式。
作用域:全局 > Component > Story(按執(zhí)行順序排列)

// js示例
export default {
  title: 'YourComponent',
  component: YourComponent,
  decorators: [() => ({ template: '<div style="margin: 3em;"><story/></div>' })],
};
// mdx示例
<Meta
  title="YourComponent"
  component={YourComponent}
  decorators={[
    () => ({
      template: '<div style="margin: 3em;"><story /></div>',
    }),
  ]}
/>

Decorator可在.storybook/preview.js尖殃、組件聲明丈莺、Story聲明中定義,最終會合并送丰,執(zhí)行順序為 Global > Component > Story场刑,可通過定義ClassName的命名空間來定制樣式。

更多配置: https://storybook.js.org/docs/vue/writing-stories/decorators#wrap-stories-with-extra-markup

畫布蚪战、文檔的分離

默認(rèn)文檔中包含畫布信息牵现,若不想文檔中渲染Story,需要寫.stories.js和.stories.mdx兩個文件
其中邀桑,.stories.js中默認(rèn)導(dǎo)出不添加任何元數(shù)據(jù)瞎疼,轉(zhuǎn)移至.stories.mdx中,示例如下:

// *.stories.ts
export default {}
const Template = (args: any) => ({
  components: {
    Drawer
  },
  props: Object.keys(args),
  methods: {
    onClose: action('onClose'),
    onStretched: action('onStretched')
  },
  setup () {
    const state = reactive({
      ...args
    })
    function toogleVisible () {
      state.visible = true
    }
    return {
      state,
      toogleVisible
    }
  },
  template: `
    <Drawer v-bind="state" v-model:visible="state.visible" @close="onClose" @stretched="onStretched">
    </Drawer>
    <button @click="toogleVisible">切換可見性</button>
  `
})
export const Primary = Template.bind({}) as any
(Primary as any).args = {
  visible: false,
  title: '示例'
}
Primary.parameters = { docs: { disable: true } };


// *.stories.mdx
import { Meta, Story } from '@storybook/addon-docs'
import { Primary } from './Drawer.stories.ts';
import Drawer from './index'
export const argsType = {
  visible: {
    description: '是否顯示Dialog壁畸,支持.sync修飾符',
    type: {
      required: true
    },
    table: {
      type: {
        summary: 'Boolean'
      },
      defaultValue: {
        summary: false
      }
      // category: 'Boolean' // 參數(shù)分組
      // subcategory: 'Button colors', // 子分組
    },
    control: 'boolean'
  },
  ...
}
# Drawer

## 基本用法
<Story
  name="Drawer"
   decorators={[
    () => ({
      template: '<div id="custom-root" style="background: red;"><story /></div>',
    })
  ]}
  story={Primary} />

## 自定義內(nèi)容

## 參數(shù)文檔

<Meta 
  title="組件/Basic/Drawer" 
  component={Drawer} 
  argTypes={{
    ...argsType
  }}
/>

parameters = { docs: { disable: true } }可在docs中禁止渲染Story
https://storybook.js.org/docs/vue/api/mdx#documentation-only-mdx
https://github.com/storybookjs/storybook/blob/master/addons/docs/docs/recipes.md#docspage
https://github.com/storybookjs/storybook/blob/master/addons/docs/docs/mdx.md#documentation-only-mdx

Notes:

  • mdx文件中jsx和markdown語法之間要用空行分隔贼急;
  • jsx定義對象,尤其是空對象捏萍,不能有多余的空行太抓;
export const argTypes = {

}
# 這樣會報錯

正確寫法:

export const argTypes = {}

# 這樣才能正確解析

側(cè)邊欄忽略子節(jié)點

若不想要側(cè)邊欄創(chuàng)建子節(jié)點,可以定義Story.storyName與export default的組件title保持一致:

// 示例
export default {
  title: '組件/Basic/Drawer'
}
export const Primary = Template.bind({}) as any
Primary.storyName = 'Drawer';

調(diào)整docs優(yōu)先展示權(quán)

默認(rèn)優(yōu)先展示stories令杈,可以通過優(yōu)先展示docs

parameters = {
  docs: {
    disable: true
  },
  viewMode: 'docs'
}

https://github.com/storybookjs/storybook/blob/761501bf1344c5bab34e881702cc1938557b611c/addons/docs/docs/recipes.md#controlling-a-storys-view-mode

隱藏Canvas

  previewTabs: {
    canvas: {
        hidden: true,
    }
  },

可以隱藏當(dāng)前Stories的Canvas面板

修改Logo跳轉(zhuǎn)地址

// .storybook/themes/theme.js
import { create } from '@storybook/theming';

export default create({
  base: 'light',
  brandTitle: 'StoryBook',
  brandUrl: '/?path=/docs/快速開始--primary',
  brandImage: '/logo.png',
});

關(guān)閉Addons

showPanel無論設(shè)置在哪一層級走敌,都是全局的

parameters = {
  docs: {
    disable: true
  },
  controls: {
    disable: true
  },
  options: {
    showPanel: false
  }
}

MDX寫法

動機

上述的寫法為CSF,component story format逗噩,是 Storybook 官方推薦的一種基于 ES module 的 stories 編寫方法掉丽,由一個 export default 和一個或多個 export 組成。

它是Storybook 提供了一些封裝過后的組件供使用异雁,讓我們能夠較為快速的生成 stories捶障。

代價是靈活度會相對的沒有高,當(dāng)然纲刀,如果只是簡單的展示組件及其接收參數(shù)项炼,那其實已經(jīng)完全足夠了。

可如果在展示組件之余,還想要編寫一個額外的文檔锭部,比如介紹一下組件封裝的背景驱闷,用到的技術(shù)等,CSF 就不是那么好用了空免。

基于這樣的需求,Storybook 也支持使用 MDX 格式編寫 stories盆耽。

MDX蹋砚,如同 TSX,就是一種能夠在 Markdown 文檔中寫 JSX 的格式摄杂。使用 MDX 格式編寫 stories坝咐,文字部分內(nèi)容的編寫會更加靈活,沒有了官方預(yù)置的內(nèi)容析恢,真正的所寫即所得墨坚。

MDX示例

import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs';
import { action } from '@storybook/addon-actions'
import { reactive } from 'vue'
import Dialog from './index'

# Dialog

export const argsType = {
  visible: {
    description: '是否顯示Dialog,支持.sync修飾符',
    type: {
      required: true
    },
    table: {
      type: {
        summary: 'Boolean'
      },
      defaultValue: {
        summary: false
      }
      // category: 'Boolean' // 參數(shù)分組
      // subcategory: 'Button colors', // 子分組
    },
    control: 'boolean'
  },
  showCancel: {
    description: '展示獨立的關(guān)閉按鈕X',
    table: {
      type: {
        summary: 'Boolean'
      },
      defaultValue: {
        summary: true
      }
    },
    control: 'boolean'
  },
  title: {
    description: 'Dialog的標(biāo)題映挂,也可通過具名slot(見下表)傳入',
    table: {
      defaultValue: {
        summary: '示例Dialog'
      }
    },
    control: 'text'
  },
  rawHtml: {
    description: 'dialog主體內(nèi)容',
    table: {
      type: {
        summary: 'string / htmlString'
      },
      defaultValue: {
        summary: 'DJ小能手',
        detail: '<span style="color: red;">DJ</span>小能手'
      }
    },
    control: 'text'
  },
  confirm: {
    description: '確認(rèn)相關(guān)的配置項目',
    mapping: {
      label: '確定按鈕的文本內(nèi)容',
      handler: '確認(rèn)的回調(diào)'
    },
    options: ['label', 'handler'],
    table: {
      type: {
        summary: 'Object',
        detail: `
          confirm.label: 確定按鈕的文本內(nèi)容;
          confirm.handler: 確認(rèn)的回調(diào);
        `
      }
    },
    control: 'object'
  },
  cancel: {
    description: '取消相關(guān)的配置項目',
    table: {
      type: {
        summary: 'Object',
        detail: `
        cancel.label: 取消按鈕的文本內(nèi)容;
        cancel.handler: 取消的回調(diào);`
      }
    },
    control: 'object'
  },
  header: {
    description: 'Dialog標(biāo)題區(qū)的內(nèi)容',
    table: {
      type: {
        summary: 'Vnode'
      },
      defaultValue: ''
    },
    control: 'text',
    category: 'Slots'
  },
  default: {
    description: 'Dialog的內(nèi)容',
    table: {
      type: {
        summary: 'Vnode'
      },
      defaultValue: ''
    },
    control: 'text',
    category: 'Slots'
  },
  'update:visible': {
    table: {
      disable: true
    }
  }
}

export const actionData = {
  updateVisible: action('update:visible')
}

## 參數(shù)文檔

<Meta
  title="組件/Basic/Dialog"
  component={Dialog}
  argTypes={{
    ...argsType
  }}
/>

<ArgsTable story="基本用法" />

## 基本用法

export const argsData = {
  visible: false,
  showCancel: true,
  confirm: {
    label: '確定',
    handler () {
      console.log('確定')
    }
  },
  cancel: {
    handler () {
      console.log('X')
    }
  }
}

export const HTMLTemplate = `
  <Dialog v-bind="state" v-model:visible="state.visible" @update:visible="updateVisible">
    <template v-slot:header v-if="state.header">
      <header v-html="state.header"></header>
    </template>
    <template v-slot:default v-if="state.default">
      <main v-html="state.default"></main>
    </template>
  </Dialog>
  <button @click="toggleVisible">切換可見性</button>
`

export const ConstructorFactory = (args) => ({
  components: { Dialog },
  props: Object.keys(args),
  setup () {
    const state = reactive({
      ...args
    })
    function toggleVisible () {
      state.visible = true
    }
    return {
      state,
      toggleVisible
    }
  },
  methods: {
    ...actionData
  },
  template: HTMLTemplate
})

<Canvas
  mdxSource={HTMLTemplate}
>
  <Story
    name="基本用法"
    args={{...argsData}}
  >
    {
      ConstructorFactory.bind({})
    }
  </Story>
</Canvas>

內(nèi)置組件

Meta

聲明本 MDX 文件渲染的頁面的標(biāo)題泽篮,對應(yīng)的組件等。作用和 CSF 寫法中的 export default 一致柑船;

ArgsTable

自定義arguments類型帽撑,用于Props、Slots鞍时、Events展示與交互亏拉。

屬性 示例 屬性說明 屬性值 屬性值示例
of <ArgsTable of={ComponentObj} /> 自動解析組件的Props、Slots逆巍、Events等聲明及塘,依據(jù)Storybook內(nèi)置類型聲明輸出Storybook文檔 import導(dǎo)入的組件對象 import Dialog from './index'的"Dialog"
story <ArgsTable story="StoryNameString" /> 自定義arguments,需要使用story屬性承接arguments類型聲明锐极。 Story標(biāo)簽的name屬性值 <Story name="storyName">的"storyName"

Canvas

生成一個 Canvas 畫板笙僚,用于展示我們自己編寫的組件。

畫布會提供一些便捷的功能灵再,比如展示當(dāng)前組件的源代碼等味咳;

Canvas默認(rèn)將Stroybook的Template函數(shù)作為源碼輸出,若想自定義源碼輸出檬嘀,將源碼作為屬性mdxSource的值即可槽驶。

點擊鏈接查看更多屬性配置

Story

生成一個 story。

Notes:并不是一定要把 story 寫在 <Canvas />組件中鸳兽,Canvas 組件只是能夠提供一些其他的功能(展示源碼掂铐、復(fù)制代碼之類的)。直接編寫 story 的話,組件也能正常渲染全陨。

image

區(qū)別在于爆班,寫在<Canvas />中,Canvas會提供便捷的功能:工具欄辱姨、源代碼查看...

vscode語法提示插件

名稱: MDX

ID: silvenon.mdx
說明: Provides syntax highlighting and bracket matching for MDX (JSX in Markdown) files.
版本: 0.1.0
發(fā)布者: Matija Marohni?
VS Marketplace 鏈接: https://marketplace.visualstudio.com/items?itemName=silvenon.mdx

名稱: MDX Preview

ID: xyc.vscode-mdx-preview
說明: MDX Preview
版本: 0.3.3
發(fā)布者: Xiaoyi Chen
VS Marketplace 鏈接: https://marketplace.visualstudio.com/items?itemName=xyc.vscode-mdx-preview

名稱: Vue Storybook Snippets

ID: megrax.vue-storybook-snippets
說明: Storybook Snippets for Vue
版本: 0.0.7
發(fā)布者: Megrax
VS Marketplace 鏈接: https://marketplace.visualstudio.com/items?itemName=Megrax.vue-storybook-snippets

參考文檔:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末柿菩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子雨涛,更是在濱河造成了極大的恐慌枢舶,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件替久,死亡現(xiàn)場離奇詭異凉泄,居然都是意外死亡,警方通過查閱死者的電腦和手機蚯根,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門后众,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人颅拦,你說我怎么就攤上這事蒂誉。” “怎么了距帅?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵拗盒,是天一觀的道長。 經(jīng)常有香客問我锥债,道長陡蝇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任哮肚,我火速辦了婚禮登夫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘允趟。我一直安慰自己恼策,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布潮剪。 她就那樣靜靜地躺著涣楷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抗碰。 梳的紋絲不亂的頭發(fā)上狮斗,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音弧蝇,去河邊找鬼碳褒。 笑死折砸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的沙峻。 我是一名探鬼主播睦授,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼摔寨!你這毒婦竟也來了去枷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤是复,失蹤者是張志新(化名)和其女友劉穎删顶,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體佑笋,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年斑鼻,在試婚紗的時候發(fā)現(xiàn)自己被綠了蒋纬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡坚弱,死狀恐怖蜀备,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荒叶,我是刑警寧澤碾阁,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站些楣,受9級特大地震影響脂凶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜愁茁,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一蚕钦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鹅很,春花似錦嘶居、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至菠齿,卻和暖如春佑吝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绳匀。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工迹蛤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留民珍,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓盗飒,卻偏偏與公主長得像嚷量,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子逆趣,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容