From 2805cbbdd918506dc53e76bc946cae35c2c3faf2 Mon Sep 17 00:00:00 2001 From: Lee Chase Date: Sun, 21 Feb 2021 13:42:44 +0000 Subject: [PATCH] feat: v3 improve story source for CvButton (#1128) * feat: v3 improve story source for CvButton * chore: storybook package bump Co-authored-by: Lee Chase --- .../storybook-6.2.0-alpha.29.tgz | Bin 0 -> 1264 bytes packages/v3/.storybook/_styles.scss | 4 +- packages/v3/.storybook/preview.js | 7 +- packages/v3/package.json | 2 +- .../components/CvButton/CvButton.stories.js | 76 +++++---- .../CvButton/CvButtonSet.stories.js | 23 ++- .../CvButton/CvIconButton.stories.js | 51 ++++-- packages/v3/src/components/CvSvg/_CvSvg.vue | 2 +- .../__tests__/controls-from-props.spec.js} | 4 +- .../__tests__/story-source-code.js | 148 ++++++++++++++++++ .../controls-from-props.js} | 0 .../v3/src/global/storybook-utils/index.js | 3 + .../storybook-utils/story-source-code.js | 50 ++++++ packages/v3/yarn.lock | 43 +++-- 14 files changed, 341 insertions(+), 72 deletions(-) create mode 100644 .yarn-offline-mirror/storybook-6.2.0-alpha.29.tgz rename packages/v3/src/global/{__tests__/storybook-utils.spec.js => storybook-utils/__tests__/controls-from-props.spec.js} (93%) create mode 100644 packages/v3/src/global/storybook-utils/__tests__/story-source-code.js rename packages/v3/src/global/{storybook-utils.js => storybook-utils/controls-from-props.js} (100%) create mode 100644 packages/v3/src/global/storybook-utils/index.js create mode 100644 packages/v3/src/global/storybook-utils/story-source-code.js diff --git a/.yarn-offline-mirror/storybook-6.2.0-alpha.29.tgz b/.yarn-offline-mirror/storybook-6.2.0-alpha.29.tgz new file mode 100644 index 0000000000000000000000000000000000000000..81501f6c537e21e9bf084b3ca55c49774564ee8c GIT binary patch literal 1264 zcmVm(jn+0swZLd=AF1;6dWQpwT%=665zNa9`9a&Iw;tm}zbRIo~Se7;D z_u)%=lwP;ng-0i+1FPE|3@o&>x>j#s;rBtJ`9vyN5`m%oRh%Xn*na;b5-llA-3TTu zp}C}RgpFfO8}i~_u*I^3qvRNRR`(2ElavVpZk`zMVo9@%zq+ec2IDU^ZAQh4Ny&H) zOu~{18sEc0ki4X+4s$^%@Hr$)A{JDKk^{-_p`b#d1CL9>a+WWEK!VH~T9!+Mk$hgR ziJ)kl0+Es@j36GQJgHVRFG;ER<}9NUj!I0W6Sf|mCV`SwG7I=E{D_*w33*Cic^ObPuX03X`NP4#Tk=J9a5&C+jr~xQ;quO@KCCM^~XNwjAT%7QS*J8rh)up2SWcMy0!xc+z@7g|Hd6TBk0&6wmUjpyV2Oc ziU2JF+ly|%zX032f!D4#(!u$87C2!DexSM2*~E2F=X%4*)yVZO;T-)uA1AhpGa5l7 z9~5B=xX8j7|I8ow(7Bu3fm1r!X)40Bo4qLF5jvCUyX`t6=7b4$_Yh)_dxD z7Xi{ZQ^$)8q(vn-Z?FO3*q%%jmu6pK_<;%#hW_j(a4*LZjQz>TLFKuFq1opXr{Th^ zh7;SJ>M*jW_N7yM`G^u|%B%?wuE&m2DLxy2!^rhLmB-Ncq5zvZrWr(A*K0R)bg%zy6W|L-A>e=sX4%$VgS&EMfBpHfW|^lili zJ$mv=mR#J&oZp#A#%im2qC7eN>9FAc7qVIZ?H6|dp8wq(U%&r%Px>dHuK(_7|FHhQ zi@etWIyqU<&U5H&S9?cS>UVhc#Jij785ljodP=flNsQi)O?OIVB3MzjCgH{zFEVap zaYyghT%;1af5A3<%z*7*Ya_hW(gLTR!dRANA)lM(0R1u1ru=pq<0Dsm zK;ccjA?R!}+48sV2GxhR*fv9!?}CxB>NB>-_Xc)X=l$+2ax-)vm8!s8)w>Dp)gb_e zVdAefGuz%hh?Eu-aq|SP{X3lfBjXEmXY1cSmWDi^v&9a8Bng!YurWw>4p_D^C25U0 zNMk~~aev+)%q=oF?I#K8k&|A(YoQ98r2p($uiMc+91g0l$!7l#9D6i%j8*yo9ADr6 zt!{7d+5PYIWN_I3zlS`Ay?tB#R{)>=_#?0uq`>z-%<)P8Vzalc*J~p$R=2X=-Zrym af6>1@EV+Xm{{defaultSlot}}`; const Template = (args, { argTypes }) => { + const newArgs = { ...args, icon: icons[args.icon] }; return { props: Object.keys(argTypes), components: { CvButton }, - template: `{{label}}`, - setup(props) { - const icon = shallowRef(icons[props.icon]); - - if (icon.value === undefined) { - // assigning an icon seem sto need a kick - setTimeout(() => { - console.log(props.icon); - icon.value === icons[props.icon]; - console.dir(icon.value); - }, 1); - } - - return { icon }; + setup() { + return { newArgs, onClick: action('click') }; }, + template, }; }; export const Primary = Template.bind({}); Primary.args = { kind: 'primary', - label: 'primary', - banana: 'banana', + defaultSlot: 'Primary', }; +Primary.parameters = storyParametersObject( + Primary.parameters, + template, + Primary.args, + 'v-bind="newArgs"' +); export const Secondary = Template.bind({}); Secondary.args = { kind: 'secondary', - label: 'Secondary', + defaultSlot: 'Secondary', }; +Secondary.parameters = storyParametersObject( + Secondary.parameters, + template, + Secondary.args, + 'v-bind="newArgs"' +); export const Field = Template.bind({}); Field.args = { - label: 'Field size', + defaultSlot: 'Field size', size: 'field', }; +Field.parameters = storyParametersObject( + Field.parameters, + template, + Field.args, + 'v-bind="newArgs"' +); export const Small = Template.bind({}); Small.args = { - label: 'sm', + defaultSlot: 'sm', size: 'sm', }; +Small.parameters = storyParametersObject( + Small.parameters, + template, + Small.args, + 'v-bind="newArgs"' +); export const Large = Template.bind({}); Large.args = { - label: 'Large size', - size: 'large', + defaultSlot: 'Large size', + size: 'lg', }; +Large.parameters = storyParametersObject( + Large.parameters, + template, + Large.args, + 'v-bind="newArgs"' +); diff --git a/packages/v3/src/components/CvButton/CvButtonSet.stories.js b/packages/v3/src/components/CvButton/CvButtonSet.stories.js index 8a59352b7..faa2a4b7b 100644 --- a/packages/v3/src/components/CvButton/CvButtonSet.stories.js +++ b/packages/v3/src/components/CvButton/CvButtonSet.stories.js @@ -1,21 +1,32 @@ import { CvButtonSet, CvButton } from './'; +import { storyParametersObject } from '../../global/storybook-utils'; + export default { title: 'Components/CvButtonSet', component: CvButtonSet, }; +const template = ` + + One + Two + Three +`; const Template = (args, { argTypes }) => { return { props: Object.keys(argTypes), components: { CvButtonSet, CvButton }, - template: ` - - One - Two - Three - `, + template, + setup() { + return { args }; + }, }; }; export const Default = Template.bind({}); +Default.parameters = storyParametersObject( + Default.parameters, + template, + Default.args +); diff --git a/packages/v3/src/components/CvButton/CvIconButton.stories.js b/packages/v3/src/components/CvButton/CvIconButton.stories.js index 6fa4162db..f821d44e8 100644 --- a/packages/v3/src/components/CvButton/CvIconButton.stories.js +++ b/packages/v3/src/components/CvButton/CvIconButton.stories.js @@ -1,7 +1,10 @@ -import { computed } from 'vue'; +import { action } from '@storybook/addon-actions'; import { CvIconButton } from './'; import { buttonKinds, buttonSizes } from './consts.js'; -import { storybookControlsFromProps } from '../../global/storybook-utils'; +import { + storybookControlsFromProps, + storyParametersObject, +} from '../../global/storybook-utils'; import { props as commonCvButtonProps } from './CvButtonCommon'; import { @@ -45,21 +48,17 @@ export default { */ size: { control: { type: 'select', options: buttonSizes } }, }, - parameters: { - actions: { - handles: ['mouseover', 'mousedown .bx--btn', 'click'], - }, - }, }; +const template = ``; const Template = (args, { argTypes }) => { + const newArgs = { ...args, icon: icons[args.icon] }; return { props: Object.keys(argTypes), components: { CvIconButton }, - template: ``, - setup(props) { - const theIcon = computed(() => icons[props.icon]); - return { theIcon }; + template, + setup() { + return { newArgs, onClick: action('click') }; }, }; }; @@ -70,6 +69,12 @@ Primary.args = { label: 'primary', icon: 'Bee20', }; +Primary.parameters = storyParametersObject( + Primary.parameters, + template, + Primary.args, + 'v-bind="newArgs"' +); export const Secondary = Template.bind({}); Secondary.args = { @@ -77,6 +82,12 @@ Secondary.args = { label: 'Secondary', icon: 'Bee20', }; +Secondary.parameters = storyParametersObject( + Secondary.parameters, + template, + Secondary.args, + 'v-bind="newArgs"' +); export const Field = Template.bind({}); Field.args = { @@ -84,6 +95,12 @@ Field.args = { size: 'field', icon: 'Bee20', }; +Field.parameters = storyParametersObject( + Field.parameters, + template, + Field.args, + 'v-bind="newArgs"' +); export const Small = Template.bind({}); Small.args = { @@ -91,6 +108,12 @@ Small.args = { size: 'sm', icon: 'Bee20', }; +Small.parameters = storyParametersObject( + Small.parameters, + template, + Small.args, + 'v-bind="newArgs"' +); export const Large = Template.bind({}); Large.args = { @@ -98,3 +121,9 @@ Large.args = { size: 'lg', icon: 'Bee20', }; +Large.parameters = storyParametersObject( + Large.parameters, + template, + Large.args, + 'v-bind="newArgs"' +); diff --git a/packages/v3/src/components/CvSvg/_CvSvg.vue b/packages/v3/src/components/CvSvg/_CvSvg.vue index ee5f3a553..c377fa60b 100644 --- a/packages/v3/src/components/CvSvg/_CvSvg.vue +++ b/packages/v3/src/components/CvSvg/_CvSvg.vue @@ -11,7 +11,7 @@ export default { if (!val || typeof val === 'string') { return true; } - const result = val.render !== undefined; + const result = val.render !== undefined || val.setup !== undefined; if (!result) { console.error( 'Expected a Vue icon component, SVG, SVG Symbol or SVG file' diff --git a/packages/v3/src/global/__tests__/storybook-utils.spec.js b/packages/v3/src/global/storybook-utils/__tests__/controls-from-props.spec.js similarity index 93% rename from packages/v3/src/global/__tests__/storybook-utils.spec.js rename to packages/v3/src/global/storybook-utils/__tests__/controls-from-props.spec.js index 75149e807..00c942ae6 100644 --- a/packages/v3/src/global/__tests__/storybook-utils.spec.js +++ b/packages/v3/src/global/storybook-utils/__tests__/controls-from-props.spec.js @@ -1,4 +1,4 @@ -import { storybookControlsFromProps } from '../storybook-utils'; +import { storybookControlsFromProps } from '..'; const props = { boolean_0: true, @@ -40,7 +40,7 @@ const expectedResult = { errNotSure2: { control: { type: 'text', default: '' } }, }; -describe('global/storybook-utils', () => { +describe('global/storybook-utils/controls-from-props', () => { it('Should convert common props to storybook control types', () => { const controls = storybookControlsFromProps(props); diff --git a/packages/v3/src/global/storybook-utils/__tests__/story-source-code.js b/packages/v3/src/global/storybook-utils/__tests__/story-source-code.js new file mode 100644 index 000000000..7d5ba2390 --- /dev/null +++ b/packages/v3/src/global/storybook-utils/__tests__/story-source-code.js @@ -0,0 +1,148 @@ +import { storyParametersObject } from '..'; +import { storySourceCode } from '../story-source-code'; + +describe('global/storybook-utils/storyParametersObject', () => { + it('Should build properties from null', () => { + const parameters = storyParametersObject(null, '
', { + prop1: 'prop1', + prop2: 'prop2', + }); + + expect(parameters).toEqual({ + docs: { + source: { + code: '
', + }, + }, + }); + }); + + it('Should build properties from {}', () => { + const parameters = storyParametersObject({}, '
', { + prop1: 'prop1', + prop2: 'prop2', + }); + + expect(parameters).toEqual({ + docs: { + source: { + code: '
', + }, + }, + }); + }); + + it('Should build properties from { docs: {}}', () => { + const parameters = storyParametersObject( + { docs: {} }, + '
', + { + prop1: 'prop1', + prop2: 'prop2', + } + ); + + expect(parameters).toEqual({ + docs: { + source: { + code: '
', + }, + }, + }); + }); + + it('Should build properties from { docs: { source: {}}}', () => { + const parameters = storyParametersObject( + { docs: { source: {} } }, + '
', + { + prop1: 'prop1', + prop2: 'prop2', + } + ); + + expect(parameters).toEqual({ + docs: { + source: { + code: '
', + }, + }, + }); + }); + + it('Should build properties from with non-string props', () => { + const parameters = storyParametersObject(null, '
', { + prop1: true, + prop2: 2, + }); + + expect(parameters).toEqual({ + docs: { + source: { + code: '
', + }, + }, + }); + }); + + it('Should replace alternate v-bind', () => { + const parameters = storyParametersObject( + null, + '
', + { + prop1: true, + }, + 'v-bind="otherProps"' + ); + + expect(parameters).toEqual({ + docs: { + source: { + code: '
', + }, + }, + }); + }); + + it('Should deal with default values', () => { + const parameters = storyParametersObject(null, '
', { + prop1: false, + prop2: '', + prop3: 0, + }); + + expect(parameters).toEqual({ + docs: { + source: { + code: '
', + }, + }, + }); + }); + + it('Is OK with zero pros', () => { + const parameters = storyParametersObject( + null, + '
', + null + ); + + expect(parameters).toEqual({ + docs: { + source: { + code: '
', + }, + }, + }); + }); +}); + +describe('global/storybook-utils/storySourceCode', () => { + it('Works with a default parameter', () => { + const result = storySourceCode('
', { + prop1: 'prop1 ', + }); + + expect(result).toEqual('
'); + }); +}); diff --git a/packages/v3/src/global/storybook-utils.js b/packages/v3/src/global/storybook-utils/controls-from-props.js similarity index 100% rename from packages/v3/src/global/storybook-utils.js rename to packages/v3/src/global/storybook-utils/controls-from-props.js diff --git a/packages/v3/src/global/storybook-utils/index.js b/packages/v3/src/global/storybook-utils/index.js new file mode 100644 index 000000000..ffada0bff --- /dev/null +++ b/packages/v3/src/global/storybook-utils/index.js @@ -0,0 +1,3 @@ +export { storybookControlsFromProps } from './controls-from-props'; +export { storySourceCode } from './story-source-code'; +export { storyParametersObject } from './story-source-code'; diff --git a/packages/v3/src/global/storybook-utils/story-source-code.js b/packages/v3/src/global/storybook-utils/story-source-code.js new file mode 100644 index 000000000..dbd2bb190 --- /dev/null +++ b/packages/v3/src/global/storybook-utils/story-source-code.js @@ -0,0 +1,50 @@ +export const storySourceCode = ( + templateSource, + args, + replacing = 'v-bind="$props"' +) => { + const propToSource = (key, val) => { + const type = typeof val; + switch (type) { + case 'boolean': + return val ? key : ''; + case 'string': + return `${key}="${val}"`; + default: + return `:${key}="${val}"`; + } + }; + + const argsKeys = args ? Object.keys(args) : []; + return templateSource.replace( + replacing, + argsKeys + .map(key => propToSource(key, args[key])) + .filter(item => item !== '') + .join(' ') + ); +}; + +export const storyParametersObject = ( + parameters, + templateSource, + args, + replacing = 'v-bind="$props"' +) => { + const code = storySourceCode(templateSource, args, replacing); + + if (!parameters) { + parameters = { docs: { source: { code } } }; + } else { + if (!parameters.docs) { + parameters.docs = { source: { code } }; + } else { + if (!parameters.docs.source) { + parameters.docs.source = { code }; + } else { + parameters.docs.source.code = code; + } + } + } + return parameters; +}; diff --git a/packages/v3/yarn.lock b/packages/v3/yarn.lock index aa2ca9558..4b5588906 100644 --- a/packages/v3/yarn.lock +++ b/packages/v3/yarn.lock @@ -1803,15 +1803,15 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/cli@6.2.0-alpha.24": - version "6.2.0-alpha.24" - resolved "https://registry.yarnpkg.com/@storybook/cli/-/cli-6.2.0-alpha.24.tgz#38f0c63b77e852b58a8eee6c4bc394f873621d5d" - integrity sha512-pRf2wgOlcESAqh6HUJWDj37Z2gPVICyZxa8Ld5wb9qXdqGqpFxmX1ducC4Gy4Sh7SFsabyM310LozXWaz6NpoA== +"@storybook/cli@6.2.0-alpha.29": + version "6.2.0-alpha.29" + resolved "https://registry.yarnpkg.com/@storybook/cli/-/cli-6.2.0-alpha.29.tgz#16424c37688c6a1fe9e3c73ee2d0337aacda104a" + integrity sha512-8RozQRVjlXt+qW0XWLdec0sTg2bO1F8h3L+loXNdzp3hDM46IWUbZkUF2mF9E8mOPfJi6MYsC+oyi01LGtC8tA== dependencies: "@babel/core" "^7.12.10" "@babel/preset-env" "^7.12.11" - "@storybook/codemod" "6.2.0-alpha.24" - "@storybook/node-logger" "6.2.0-alpha.24" + "@storybook/codemod" "6.2.0-alpha.29" + "@storybook/node-logger" "6.2.0-alpha.29" "@storybook/semver" "^7.3.2" chalk "^4.1.0" commander "^6.2.1" @@ -1865,14 +1865,14 @@ core-js "^3.8.2" global "^4.4.0" -"@storybook/codemod@6.2.0-alpha.24": - version "6.2.0-alpha.24" - resolved "https://registry.yarnpkg.com/@storybook/codemod/-/codemod-6.2.0-alpha.24.tgz#45a6766d45761495de54482daeb4813744082ee0" - integrity sha512-w8mcRoUbW8bkuYos+47PBKyJGxu83XgtUw2t/MxiUn621f4SgfYuN+CBVgakKIkIY1H22U4qdU0kQyKaR39JEA== +"@storybook/codemod@6.2.0-alpha.29": + version "6.2.0-alpha.29" + resolved "https://registry.yarnpkg.com/@storybook/codemod/-/codemod-6.2.0-alpha.29.tgz#f8f9ec2f5f47ff5edddaa0f7adcccee93226843b" + integrity sha512-sdMNuM8OdMmUmnlgLxlzOSuHDUedvJi+vFveAOMjspdgH3Xqi+YGEKVlKWOh/TC4xufGBt/Y3YxndK/60vTq4Q== dependencies: "@mdx-js/mdx" "^1.6.22" "@storybook/csf" "0.0.1" - "@storybook/node-logger" "6.2.0-alpha.24" + "@storybook/node-logger" "6.2.0-alpha.29" core-js "^3.8.2" cross-spawn "^7.0.3" globby "^11.0.2" @@ -2042,6 +2042,17 @@ npmlog "^4.1.2" pretty-hrtime "^1.0.3" +"@storybook/node-logger@6.2.0-alpha.29": + version "6.2.0-alpha.29" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.2.0-alpha.29.tgz#fbf8547efa3ba16ddc2d3eea86e3b47299958f33" + integrity sha512-hgqLXVAfJmbs45OFuSmgMp+f/VqDfnBj1pOiSjNZxzb8MoWIc2P9MOm+KGt/sCfvSBNjgC1zc+k4xxj9cjzCow== + dependencies: + "@types/npmlog" "^4.1.2" + chalk "^4.1.0" + core-js "^3.8.2" + npmlog "^4.1.2" + pretty-hrtime "^1.0.3" + "@storybook/postinstall@6.2.0-alpha.24": version "6.2.0-alpha.24" resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.2.0-alpha.24.tgz#253eb7b1a6a1b85d233cb5746b9bd00748679bc4" @@ -13700,12 +13711,12 @@ store2@^2.12.0: resolved "https://registry.yarnpkg.com/store2/-/store2-2.12.0.tgz#e1f1b7e1a59b6083b2596a8d067f6ee88fd4d3cf" integrity sha512-7t+/wpKLanLzSnQPX8WAcuLCCeuSHoWdQuh9SB3xD0kNOM38DNf+0Oa+wmvxmYueRzkmh6IcdKFtvTa+ecgPDw== -storybook@^6.2.0-alpha.24: - version "6.2.0-alpha.24" - resolved "https://registry.yarnpkg.com/storybook/-/storybook-6.2.0-alpha.24.tgz#5029a7d5d3cc61cc31de6d3b704bb850498c0b5f" - integrity sha512-qkiRsnSaZdjAKBdPuDAxMpsHTlhJQUt9S175JQzBZ2V5LWlLZfGoSglW8iorsxiq2at8Qzi5XyrgXt0N8zRMnA== +storybook@^6.2.0-alpha.29: + version "6.2.0-alpha.29" + resolved "https://registry.yarnpkg.com/storybook/-/storybook-6.2.0-alpha.29.tgz#fb16f27472eaec32231dde62162ee9098c4aa6b1" + integrity sha512-aYzUcy3APcM3pEfgBRoehCxL1LRO/H+01o3dJsAC0P9Tu/R3AaKpMXkufAbQEObmR7luGUHhJ9YuLYwwsrlF+g== dependencies: - "@storybook/cli" "6.2.0-alpha.24" + "@storybook/cli" "6.2.0-alpha.29" stream-browserify@^2.0.1: version "2.0.2"