From 5493caad3623ff2c52ea50f67b6ac48dc565326f Mon Sep 17 00:00:00 2001 From: pocka Date: Sat, 2 Feb 2019 01:17:13 +0900 Subject: [PATCH 1/3] [feat] Add docs in panel mode * Add feature to show docs in addon panel. * Add docsInPanel option. * Hide preview section when docs in panel. --- README.md | 1 + package.json | 8 ++- rollup.config.js | 28 ++++++++--- src/addon.ts | 8 +++ src/components/Wrapper/index.vue | 2 +- src/index.ts | 8 ++- src/lib.d.ts | 47 +++++++++++++++++ src/options.ts | 6 +++ src/register.tsx | 86 ++++++++++++++++++++++++++++++++ src/view/index.ts | 9 ++++ tsconfig.json | 1 + yarn.lock | 33 ++++++++++-- 12 files changed, 221 insertions(+), 16 deletions(-) create mode 100644 src/addon.ts create mode 100644 src/register.tsx diff --git a/README.md b/README.md index 9771f4a..ba0b31a 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,7 @@ This addon accepts [@storybook/addon-info](https://github.com/storybooks/storybo | `wrapperComponent` | `Component` | [default wrapper](src/components/Wrapper/index.vue) | Override docs component. | | `summary` | `string` | `''` | Summary for the story. Accepts Markdown. | | `components` | `{ [name: string]: Component }\|null` | `null` | Display info for these components. Same type as component's `components` property. If `null` or `false`, this addon tries to use `components` property in story component then outermost tag in `template`. | +| `docsInPanel` | `boolean` | `true` | Whether to show docs in addon panel. | | `useDocgen` | `boolean` | `true` | Whether to use result of vue-docgen-api. | In addition to addon options, we have a component option. diff --git a/package.json b/package.json index c9e7461..a1c00cf 100644 --- a/package.json +++ b/package.json @@ -38,11 +38,13 @@ "homepage": "https://github.com/pocka/storybook-addon-vue-info#readme", "devDependencies": { "@babel/core": "^7.2.2", + "@storybook/addons": "^4.1.11", "@storybook/vue": "^4.1.11", "@types/dedent": "^0.7.0", "@types/highlight.js": "^9.12.2", "@types/jest": "^22.2.3", "@types/marked": "^0.3.0", + "@types/react": "^16.7.22", "@types/storybook__vue": "^3.3.0", "autoprefixer": "^9.4.7", "babel-loader": "^8.0.5", @@ -65,12 +67,13 @@ "ts-jest": "^22.4.5", "tslint": "^5.10.0", "tslint-config-prettier": "^1.13.0", - "typescript": "^2.9.2", + "typescript": "3", "vue": "^2.5.16", "vue-loader": "^15.6.2" }, "peerDependencies": { "@storybook/vue": "*", + "react": "*", "vue": "^2.0.0" }, "prettier": { @@ -104,6 +107,7 @@ "marked": "^0.3.19", "querystring": "^0.2.0", "vue-docgen-api": "^3.3.4", - "vue-template-compiler": "^2.5.16" + "vue-template-compiler": "^2.5.16", + "vuera": "^0.2.3" } } diff --git a/rollup.config.js b/rollup.config.js index 4841197..5e46dfd 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -8,12 +8,7 @@ import resolve from 'rollup-plugin-node-resolve' import typescript from 'rollup-plugin-typescript2' import vue from 'rollup-plugin-vue' -export default { - input: './src/index.ts', - output: { - file: './lib/index.js', - format: 'es' - }, +const commonConfig = { plugins: [ resolve({ only: ['parse5'] @@ -42,5 +37,24 @@ export default { plugins: ['external-helpers'] }) ], - external: ['vue', 'dedent', 'marked', 'highlight.js', 'vue-template-compiler'] + external: ['vue', 'dedent', 'marked', 'highlight.js', 'vue-template-compiler', 'react', 'vuera', '@storybook/addons'] } + +export default [ + { + input: './src/index.ts', + output: { + file: './lib/index.js', + format: 'es' + }, + ...commonConfig + }, + { + input: './src/register.tsx', + output: { + file: './lib/register.js', + format: 'es' + }, + ...commonConfig + } +] diff --git a/src/addon.ts b/src/addon.ts new file mode 100644 index 0000000..1937feb --- /dev/null +++ b/src/addon.ts @@ -0,0 +1,8 @@ +export const AddonName = 'STORYBOOK_ADDON_VUE_INFO' + +export const PanelName = AddonName + '/panel' + +export const Events = { + ShowDocs: AddonName + '/show_docs' +} + diff --git a/src/components/Wrapper/index.vue b/src/components/Wrapper/index.vue index 7c1595b..29bf9d2 100644 --- a/src/components/Wrapper/index.vue +++ b/src/components/Wrapper/index.vue @@ -37,7 +37,7 @@ export default { :title="info.title" :subtitle="info.subtitle" /> - + + | AsyncComponent + [prop: string]: any + } + + export const VueWrapper: React.SFC +} + +declare module '@storybook/addons' { + import { SFC } from 'react' + + export interface Api { + onStory(callback: Function): () => void + } + + export interface Channel { + on(name: string, handler: Function): void + emit(name: string, payload: any): void + + removeListener(name: string, handler: Function): void + } + + interface Mod { + register(name: string, callback: (api: Api) => any): void + + addPanel( + name: string, + options: { + title: string + render: SFC<{ active: boolean }> + } + ): void + + getChannel(): Channel + } + + const addons: Mod + + export default addons +} diff --git a/src/options.ts b/src/options.ts index e8265bc..0c6287c 100644 --- a/src/options.ts +++ b/src/options.ts @@ -8,6 +8,7 @@ export const defaultOptions: InfoAddonOptions = { summary: '', components: null, wrapperComponent: DefaultWrapper, + docsInPanel: true, useDocgen: true } @@ -40,6 +41,11 @@ export interface InfoAddonOptions { */ wrapperComponent: AnyComponent + /** + * Whether to show docs in panel instead of wrapping story + */ + docsInPanel: boolean + /** * Whether to use component infomation generated by vue-docgen-api */ diff --git a/src/register.tsx b/src/register.tsx new file mode 100644 index 0000000..ae1db69 --- /dev/null +++ b/src/register.tsx @@ -0,0 +1,86 @@ +import * as React from 'react' +import Vue, { ComponentOptions } from 'vue' +import { VueWrapper } from 'vuera' + +import addons, { Api, Channel } from '@storybook/addons' + +import { AddonName, PanelName, Events } from './addon' +import { InfoAddonOptions } from './options' +import { StoryInfo } from './types/info' + +import Wrapper from './components/Wrapper/index.vue' + +interface Props { + channel: Channel + api: Api + active: boolean +} + +interface State { + /** Information of current story */ + info?: StoryInfo + /** Addon options */ + options?: InfoAddonOptions +} + +class Info extends React.Component { + public state: State = {} + + /** Callback to remove event handler */ + private stopListen?: () => void + + /** + * Callback for ShowDocs event + */ + private onShowDocs = ({ info, options }: State) => { + this.setState({ info, options }) + } + + public componentDidMount() { + const { channel, api } = this.props + + channel.on(Events.ShowDocs, this.onShowDocs) + + this.stopListen = api.onStory(() => { + // Clear panel when story changes + this.setState({ + info: undefined, + options: undefined + }) + }) + } + + public render() { + const { info, options } = this.state + const { active } = this.props + + if (!active || !info || !options) { + return null + } + + return ( +
+ +
+ ) + } + + public componentWillUnmount() { + if (this.stopListen) { + this.stopListen() + } + + const { channel } = this.props + + channel.removeListener(Events.ShowDocs, this.onShowDocs) + } +} + +addons.register(AddonName, api => { + addons.addPanel(PanelName, { + title: 'Info(Vue)', + render: ({ active }) => ( + + ) + }) +}) diff --git a/src/view/index.ts b/src/view/index.ts index d2fbde5..b620512 100644 --- a/src/view/index.ts +++ b/src/view/index.ts @@ -1,5 +1,8 @@ import Vue, { ComponentOptions } from 'vue' +import addons from '@storybook/addons' + +import { Events } from '../addon' import { InfoAddonOptions } from '../options' import { StoryInfo } from '../types/info' @@ -20,3 +23,9 @@ export function wrap( } } } + +export function transfer(info: StoryInfo, options: InfoAddonOptions): void { + const channel = addons.getChannel() + + channel.emit(Events.ShowDocs, { info, options }) +} diff --git a/tsconfig.json b/tsconfig.json index 6f19149..3a0c712 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "es2015", "es2015.promise" ], + "jsx": "react", "target": "es5", "strict": true, "module": "es2015", diff --git a/yarn.lock b/yarn.lock index 0997230..cb3c88a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -742,7 +742,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== -"@storybook/addons@4.1.11": +"@storybook/addons@4.1.11", "@storybook/addons@^4.1.11": version "4.1.11" resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-4.1.11.tgz#a0d537bd10d123ecee6cb1f5f149b148ce250e57" integrity sha512-n9oDs7GgJbiN5NYPkR3B3e5W0Tr6bIZvFfcJzgyP4dn50AUvS1IE1CEthezfn1L/nc2suw/8Oe30bOXOyTl/SQ== @@ -1001,11 +1001,24 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== +"@types/prop-types@*": + version "15.5.8" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.8.tgz#8ae4e0ea205fe95c3901a5a1df7f66495e3a56ce" + integrity sha512-3AQoUxQcQtLHsK25wtTWIoIpgYjH3vSDroZOUr7PpCHw/jLY1RB9z9E8dBT/OSmwStVgkRNvdh+ZHNiomRieaw== + "@types/q@^1.5.1": version "1.5.1" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18" integrity sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA== +"@types/react@^16.7.22": + version "16.7.22" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.22.tgz#5bc6d166d5ac34b835756f0b736c7b1af0043e81" + integrity sha512-j/3tVoY09kHcTfbia4l67ofQn9xvktUvlC/4QN0KuBHAXlbU/wuGKMb8WfEb/vIcWxsOxHv559uYprkFDFfP8Q== + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" + "@types/storybook__vue@^3.3.0": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/storybook__vue/-/storybook__vue-3.3.1.tgz#be15009df7333692e0df46e0b6e5e287729dfeec" @@ -3629,6 +3642,11 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" +csstype@^2.2.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.2.tgz#3043d5e065454579afc7478a18de41909c8a2f01" + integrity sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow== + cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" @@ -10096,10 +10114,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^2.9.2: - version "2.9.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" - integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w== +typescript@3: + version "3.3.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b" + integrity sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA== typescript@^3.2.2: version "3.2.4" @@ -10451,6 +10469,11 @@ vue@^2.5.21: resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.22.tgz#3bf88041af08b8539c37b268b70ca79245e9cc30" integrity sha512-pxY3ZHlXNJMFQbkjEgGVMaMMkSV1ONpz+4qB55kZuJzyJOhn6MSy/YZdzhdnumegNzVTL/Dn3Pp4UrVBYt1j/g== +vuera@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/vuera/-/vuera-0.2.3.tgz#f7f3535ab22cef149558cbcea7dab60c56c67c78" + integrity sha512-BzAILHtR4HG4Ke+k5qQGbrlgUhewcV+489XqhxJMHLu3sGB9gISxvTajC9KpJt4h4Boy0UslCLitf1gRRoMFJA== + w3c-hr-time@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" From d4266d79568d6bd82fca9bb0beee38931cac1594 Mon Sep 17 00:00:00 2001 From: pocka Date: Sat, 2 Feb 2019 01:18:50 +0900 Subject: [PATCH 2/3] [nits] Update examples for docs in panel mode --- example/.storybook/addons.js | 1 + example/stories/examples/index.js | 48 +++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 example/.storybook/addons.js diff --git a/example/.storybook/addons.js b/example/.storybook/addons.js new file mode 100644 index 0000000..5ae5961 --- /dev/null +++ b/example/.storybook/addons.js @@ -0,0 +1 @@ +import 'storybook-addon-vue-info/lib/register' diff --git a/example/stories/examples/index.js b/example/stories/examples/index.js index 8dffbea..1f45560 100644 --- a/example/stories/examples/index.js +++ b/example/stories/examples/index.js @@ -170,11 +170,45 @@ storiesOf('Examples/Advance usage', module) } })) ) + .add( + 'Show docs in preview area', + withInfo({ + summary: ` + To show docs in preview area, turn off \`docsInPanel\` option. + + \`\`\`js + withInfo({ + docsInPanel: false + }) + \`\`\` + + This will override descriptions generated by docgen. + + If you never use docsInPanel feature, + you can omit \`import 'storybook-addon-vue-info/register\` in \`.storybook/addons.js\`. + + ### ATTENTION! + + Docs in preview wraps story component, which might cause unexpected behaviors/bugs, + especially with other addons and Vue.js plugins. + + I recommend to use this only for documentation and + separate from interactive story such as one using addon-knobs + (otherwise you should set this option true). + `, + docsInPanel: false + })(() => ({ + components: { BaseButton }, + template: '', + })) + ) .add( 'Customize docs', withInfo({ summary: ` - To customize docs view, set your docs component to \`wrapperComponent\` option. + ## Customize docs in preview + + To customize docs in preview area, set your docs component to \`wrapperComponent\` option. This addon passes two props: @@ -185,8 +219,18 @@ storiesOf('Examples/Advance usage', module) For more detail, please look at source code of this example (\`example/components/customDocs/wrapper\`). + +
+ + ## Customize docs in addon panel + + Not supported. + + It's challenging to change components in addon panel. + To avoid plugin being complex and messy, customizing addon panel is not supported. `, - wrapperComponent: CustomWrapper + wrapperComponent: CustomWrapper, + docsInPanel: false })(() => ({ components: { BaseButton }, template: '' From 042c8be062cf18e700ba8ed0ba207e9ce00c0cb2 Mon Sep 17 00:00:00 2001 From: pocka Date: Sat, 2 Feb 2019 01:57:38 +0900 Subject: [PATCH 3/3] [refactor] Fix lint errors --- package.json | 2 +- src/addon.ts | 1 - src/index.ts | 2 +- src/lib.d.ts | 4 +++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index a1c00cf..3dd2616 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "homepage": "https://github.com/pocka/storybook-addon-vue-info#readme", "devDependencies": { "@babel/core": "^7.2.2", - "@storybook/addons": "^4.1.11", "@storybook/vue": "^4.1.11", "@types/dedent": "^0.7.0", "@types/highlight.js": "^9.12.2", @@ -102,6 +101,7 @@ ] }, "dependencies": { + "@storybook/addons": "^4.1.11", "dedent": "^0.7.0", "highlight.js": "^9.12.0", "marked": "^0.3.19", diff --git a/src/addon.ts b/src/addon.ts index 1937feb..5518d77 100644 --- a/src/addon.ts +++ b/src/addon.ts @@ -5,4 +5,3 @@ export const PanelName = AddonName + '/panel' export const Events = { ShowDocs: AddonName + '/show_docs' } - diff --git a/src/index.ts b/src/index.ts index 0deeb35..b22378d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ import { StoryDecorator, StoryFunction } from '@storybook/vue' import { extract } from './extract' import { defaultOptions, InfoAddonOptions } from './options' -import { wrap, transfer } from './view' +import { transfer, wrap } from './view' export * from './components' diff --git a/src/lib.d.ts b/src/lib.d.ts index 660406d..abb3360 100644 --- a/src/lib.d.ts +++ b/src/lib.d.ts @@ -1,3 +1,5 @@ +/* tslint:disable:ban-types */ + declare module 'vue-template-compiler' { export interface ASTElement { attrs?: any[] @@ -20,7 +22,7 @@ declare module 'vue-template-compiler' { declare module 'vuera' { import * as React from 'react' - import { Component, AsyncComponent } from 'vue' + import { AsyncComponent, Component } from 'vue' interface Props { component: