From 723f3daa9a2bfe7a5ef544dc7e84484e3b9822c4 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Wed, 18 Jan 2023 15:19:47 +1100 Subject: [PATCH 1/6] Revert "Revert "Created `ArgTypes` component and stories"" This reverts commit 580ee45df380ac4ae625ec2f85ca92e2cc28fba2. --- .../ui/blocks/src/blocks/ArgTypes.stories.tsx | 106 ++++++++++++++++++ code/ui/blocks/src/blocks/ArgTypes.tsx | 81 +++++++++++++ code/ui/blocks/src/blocks/index.ts | 1 + .../ui/blocks/src/examples/Button.stories.tsx | 16 +++ 4 files changed, 204 insertions(+) create mode 100644 code/ui/blocks/src/blocks/ArgTypes.stories.tsx create mode 100644 code/ui/blocks/src/blocks/ArgTypes.tsx diff --git a/code/ui/blocks/src/blocks/ArgTypes.stories.tsx b/code/ui/blocks/src/blocks/ArgTypes.stories.tsx new file mode 100644 index 000000000000..11aa7ac2ec1a --- /dev/null +++ b/code/ui/blocks/src/blocks/ArgTypes.stories.tsx @@ -0,0 +1,106 @@ +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; + +import { ArgTypes } from './ArgTypes'; +import * as ButtonStories from '../examples/Button.stories'; + +const meta: Meta = { + title: 'Blocks/ArgTypes', + component: ArgTypes, + parameters: { + relativeCsfPaths: ['../examples/Button.stories', '../blocks/ArgTypes.stories'], + }, +}; +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; + +export const OfComponent: Story = { + args: { + of: ButtonStories.default.component, + }, +}; + +export const OfMeta: Story = { + args: { + of: ButtonStories.default, + }, +}; + +export const OfStory: Story = { + args: { + of: ButtonStories.Primary, + }, +}; + +// NOTE: this will throw with no of prop +export const OfStoryUnattached: Story = { + parameters: { attached: false }, + args: { + of: ButtonStories.Primary, + }, +}; + +export const Simple = { + render: () =>
Story for reference
, + argTypes: { + a: { type: { name: 'string' }, name: 'A', description: 'a' }, + b: { type: { name: 'string', required: true }, name: 'B', description: 'b' }, + }, +}; + +export const IncludeProp: Story = { + args: { + of: Simple, + include: ['A'], + }, +}; + +export const SimpleInclude = { + ...Simple, + parameters: { docs: { argTypes: { include: ['A'] } } }, +}; + +export const IncludeParameter: Story = { + args: { + of: SimpleInclude, + }, +}; + +export const ExcludeProp: Story = { + args: { + of: Simple, + exclude: ['A'], + }, +}; + +export const SimpleExclude = { + ...Simple, + parameters: { docs: { argTypes: { exclude: ['A'] } } }, +}; + +export const ExcludeParameter: Story = { + args: { + of: SimpleExclude, + }, +}; + +export const SortProp: Story = { + args: { + of: Simple, + sort: 'requiredFirst', + }, +}; + +export const SimpleSort = { + ...Simple, + parameters: { docs: { argTypes: { sort: 'requiredFirst' } } }, +}; + +export const SortParameter: Story = { + args: { + of: SimpleSort, + }, +}; diff --git a/code/ui/blocks/src/blocks/ArgTypes.tsx b/code/ui/blocks/src/blocks/ArgTypes.tsx new file mode 100644 index 000000000000..5aedfd504f62 --- /dev/null +++ b/code/ui/blocks/src/blocks/ArgTypes.tsx @@ -0,0 +1,81 @@ +/* eslint-disable react/destructuring-assignment */ +import type { Parameters, Renderer, StrictArgTypes } from '@storybook/csf'; +import type { ModuleExports } from '@storybook/types'; +import type { FC } from 'react'; +import type { PropDescriptor } from '@storybook/preview-api'; +import { combineParameters, filterArgTypes } from '@storybook/preview-api'; +import type { ArgTypesExtractor } from '@storybook/docs-tools'; +import React from 'react'; + +import type { SortType } from '../components'; +import { ArgsTable as PureArgsTable, ArgsTableError } from '../components'; +import { useOf } from './useOf'; + +type ArgTypesParameters = { + include?: PropDescriptor; + exclude?: PropDescriptor; + sort?: SortType; +}; + +type ArgTypesProps = ArgTypesParameters & { + of: Renderer['component'] | ModuleExports; +}; + +// TODO: generalize +function extractComponentArgTypes( + component: Renderer['component'], + parameters: Parameters +): StrictArgTypes { + const { extractArgTypes }: { extractArgTypes: ArgTypesExtractor } = parameters.docs || {}; + if (!extractArgTypes) { + throw new Error(ArgsTableError.ARGS_UNSUPPORTED); + } + return extractArgTypes(component); +} + +function getArgTypesFromResolved(resolved: ReturnType, props: ArgTypesProps) { + if (resolved.type === 'component') { + const { + component, + projectAnnotations: { parameters }, + } = resolved; + return { + argTypes: extractComponentArgTypes(component, parameters), + parameters, + }; + } + + if (resolved.type === 'meta') { + const { + preparedMeta: { component, argTypes, parameters }, + } = resolved; + const componentArgTypes = component && extractComponentArgTypes(component, parameters); + const metaArgTypes = filterArgTypes(argTypes, props.include, props.exclude); + return { + argTypes: combineParameters(componentArgTypes, metaArgTypes) as StrictArgTypes, + parameters, + }; + } + + // In the case of the story, the enhanceArgs argTypeEnhancer has already added the extracted + // arg types from the component to the prepared story. + const { + story: { argTypes, parameters }, + } = resolved; + return { argTypes, parameters }; +} + +export const ArgTypes: FC = (props) => { + const { of } = props; + const resolved = useOf(of || 'meta'); + const { argTypes, parameters } = getArgTypesFromResolved(resolved, props); + const argTypesParameters = parameters.docs?.argTypes || ({} as ArgTypesParameters); + + const include = props.include ?? argTypesParameters.include; + const exclude = props.exclude ?? argTypesParameters.exclude; + const sort = props.sort ?? argTypesParameters.sort; + + const filteredArgTypes = filterArgTypes(argTypes, include, exclude); + + return ; +}; diff --git a/code/ui/blocks/src/blocks/index.ts b/code/ui/blocks/src/blocks/index.ts index 0fa8da8c4ea5..105c08191b4c 100644 --- a/code/ui/blocks/src/blocks/index.ts +++ b/code/ui/blocks/src/blocks/index.ts @@ -1,6 +1,7 @@ export { ColorPalette, ColorItem, IconGallery, IconItem, Typeset } from '../components'; export * from './Anchor'; +export * from './ArgTypes'; export * from './ArgsTable'; // eslint-disable-next-line import/export export * from './Canvas'; diff --git a/code/ui/blocks/src/examples/Button.stories.tsx b/code/ui/blocks/src/examples/Button.stories.tsx index d8067da6873a..a993e0834bb5 100644 --- a/code/ui/blocks/src/examples/Button.stories.tsx +++ b/code/ui/blocks/src/examples/Button.stories.tsx @@ -12,6 +12,13 @@ const meta = { tags: ['autodocs'], argTypes: { backgroundColor: { control: 'color' }, + // @ts-expect-error Meta type is trying to force us to use real props as args + extraMetaArgType: { + type: { name: 'string' }, + name: 'Extra Meta', + description: 'An extra argtype added at the meta level', + table: { defaultValue: { summary: "'a default value'" } }, + }, }, parameters: { // Stop *this* story from being stacked in Chromatic @@ -36,6 +43,15 @@ export const Primary: Story = { primary: true, label: 'Button', }, + argTypes: { + // @ts-expect-error Story type is trying to force us to use real props as args + extraStoryArgType: { + type: { name: 'string' }, + name: 'Extra Story', + description: 'An extra argtype added at the story level', + table: { defaultValue: { summary: "'a default value'" } }, + }, + }, }; export const Secondary: Story = { From 9044052f7b6c420f1cc1f58f4fac255b8c61b11a Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 19 Jan 2023 14:46:24 +1100 Subject: [PATCH 2/6] Simplify argType extraction from meta --- code/ui/blocks/src/blocks/ArgTypes.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/code/ui/blocks/src/blocks/ArgTypes.tsx b/code/ui/blocks/src/blocks/ArgTypes.tsx index 5aedfd504f62..df6891be826a 100644 --- a/code/ui/blocks/src/blocks/ArgTypes.tsx +++ b/code/ui/blocks/src/blocks/ArgTypes.tsx @@ -47,14 +47,9 @@ function getArgTypesFromResolved(resolved: ReturnType, props: ArgT if (resolved.type === 'meta') { const { - preparedMeta: { component, argTypes, parameters }, + preparedMeta: { argTypes, parameters }, } = resolved; - const componentArgTypes = component && extractComponentArgTypes(component, parameters); - const metaArgTypes = filterArgTypes(argTypes, props.include, props.exclude); - return { - argTypes: combineParameters(componentArgTypes, metaArgTypes) as StrictArgTypes, - parameters, - }; + return { argTypes, parameters }; } // In the case of the story, the enhanceArgs argTypeEnhancer has already added the extracted From 01eb8f42fef2ac7454f8c43498ff2d2fd7a6ccfd Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 19 Jan 2023 14:46:53 +1100 Subject: [PATCH 3/6] Drop unused --- code/ui/blocks/src/blocks/ArgTypes.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ui/blocks/src/blocks/ArgTypes.tsx b/code/ui/blocks/src/blocks/ArgTypes.tsx index df6891be826a..d7443489a107 100644 --- a/code/ui/blocks/src/blocks/ArgTypes.tsx +++ b/code/ui/blocks/src/blocks/ArgTypes.tsx @@ -3,7 +3,7 @@ import type { Parameters, Renderer, StrictArgTypes } from '@storybook/csf'; import type { ModuleExports } from '@storybook/types'; import type { FC } from 'react'; import type { PropDescriptor } from '@storybook/preview-api'; -import { combineParameters, filterArgTypes } from '@storybook/preview-api'; +import { filterArgTypes } from '@storybook/preview-api'; import type { ArgTypesExtractor } from '@storybook/docs-tools'; import React from 'react'; From c6fd9b959a7e8db96dc275367fe0aaa529d26c1c Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 19 Jan 2023 15:05:08 +1100 Subject: [PATCH 4/6] Refactor reference stories into their own file --- .../ui/blocks/src/blocks/ArgTypes.stories.tsx | 54 +++++------------- .../examples/ArgTypesParameters.stories.tsx | 55 +++++++++++++++++++ .../src/examples/ArgTypesParameters.tsx | 5 ++ .../ui/blocks/src/examples/Button.stories.tsx | 16 ------ 4 files changed, 75 insertions(+), 55 deletions(-) create mode 100644 code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx create mode 100644 code/ui/blocks/src/examples/ArgTypesParameters.tsx diff --git a/code/ui/blocks/src/blocks/ArgTypes.stories.tsx b/code/ui/blocks/src/blocks/ArgTypes.stories.tsx index 11aa7ac2ec1a..5248a56a8e3c 100644 --- a/code/ui/blocks/src/blocks/ArgTypes.stories.tsx +++ b/code/ui/blocks/src/blocks/ArgTypes.stories.tsx @@ -1,14 +1,13 @@ -import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { ArgTypes } from './ArgTypes'; -import * as ButtonStories from '../examples/Button.stories'; +import * as ExampleStories from '../examples/ArgTypesParameters.stories'; const meta: Meta = { title: 'Blocks/ArgTypes', component: ArgTypes, parameters: { - relativeCsfPaths: ['../examples/Button.stories', '../blocks/ArgTypes.stories'], + relativeCsfPaths: ['../examples/ArgTypesParameters.stories'], }, }; export default meta; @@ -19,19 +18,19 @@ export const Default: Story = {}; export const OfComponent: Story = { args: { - of: ButtonStories.default.component, + of: ExampleStories.default.component, }, }; export const OfMeta: Story = { args: { - of: ButtonStories.default, + of: ExampleStories.default, }, }; export const OfStory: Story = { args: { - of: ButtonStories.Primary, + of: ExampleStories.NoParameters, }, }; @@ -39,68 +38,45 @@ export const OfStory: Story = { export const OfStoryUnattached: Story = { parameters: { attached: false }, args: { - of: ButtonStories.Primary, - }, -}; - -export const Simple = { - render: () =>
Story for reference
, - argTypes: { - a: { type: { name: 'string' }, name: 'A', description: 'a' }, - b: { type: { name: 'string', required: true }, name: 'B', description: 'b' }, + of: ExampleStories.NoParameters, }, }; export const IncludeProp: Story = { args: { - of: Simple, - include: ['A'], + of: ExampleStories.NoParameters, + include: ['a'], }, }; -export const SimpleInclude = { - ...Simple, - parameters: { docs: { argTypes: { include: ['A'] } } }, -}; - export const IncludeParameter: Story = { args: { - of: SimpleInclude, + of: ExampleStories.Include, }, }; export const ExcludeProp: Story = { args: { - of: Simple, - exclude: ['A'], + of: ExampleStories.NoParameters, + exclude: ['a'], }, }; -export const SimpleExclude = { - ...Simple, - parameters: { docs: { argTypes: { exclude: ['A'] } } }, -}; - export const ExcludeParameter: Story = { args: { - of: SimpleExclude, + of: ExampleStories.Exclude, }, }; export const SortProp: Story = { args: { - of: Simple, - sort: 'requiredFirst', + of: ExampleStories.NoParameters, + sort: 'alpha', }, }; -export const SimpleSort = { - ...Simple, - parameters: { docs: { argTypes: { sort: 'requiredFirst' } } }, -}; - export const SortParameter: Story = { args: { - of: SimpleSort, + of: ExampleStories.Sort, }, }; diff --git a/code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx b/code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx new file mode 100644 index 000000000000..283e0c208f1a --- /dev/null +++ b/code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx @@ -0,0 +1,55 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { ArgTypesParameters } from './ArgTypesParameters'; + +/** + * Reference stories to be used by the ArgTypes stories + */ +const meta = { + title: 'Example/Stories for ArgTypes', + component: ArgTypesParameters, + args: { b: 'b' }, + argTypes: { + // @ts-expect-error Meta type is trying to force us to use real props as args + extraMetaArgType: { + type: { name: 'string' }, + name: 'Extra Meta', + description: 'An extra argtype added at the meta level', + table: { defaultValue: { summary: "'a default value'" } }, + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +/** + * This is the primary mode for the button + * + * _this description was written as a comment above the story_ + */ +export const NoParameters: Story = { + argTypes: { + // @ts-expect-error Story type is trying to force us to use real props as args + extraStoryArgType: { + type: { name: 'string' }, + name: 'Extra Story', + description: 'An extra argtype added at the story level', + table: { defaultValue: { summary: "'a default value'" } }, + }, + }, +}; + +export const Include = { + ...NoParameters, + parameters: { docs: { argTypes: { include: ['a'] } } }, +}; + +export const Exclude = { + ...NoParameters, + parameters: { docs: { argTypes: { exclude: ['a'] } } }, +}; + +export const Sort = { + ...NoParameters, + parameters: { docs: { argTypes: { sort: 'alpha' } } }, +}; diff --git a/code/ui/blocks/src/examples/ArgTypesParameters.tsx b/code/ui/blocks/src/examples/ArgTypesParameters.tsx new file mode 100644 index 000000000000..5e2522a21a1f --- /dev/null +++ b/code/ui/blocks/src/examples/ArgTypesParameters.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +type PropTypes = { a?: string; b: string }; + +export const ArgTypesParameters = ({ a = 'a', b }: PropTypes) =>
Example story
; diff --git a/code/ui/blocks/src/examples/Button.stories.tsx b/code/ui/blocks/src/examples/Button.stories.tsx index a993e0834bb5..d8067da6873a 100644 --- a/code/ui/blocks/src/examples/Button.stories.tsx +++ b/code/ui/blocks/src/examples/Button.stories.tsx @@ -12,13 +12,6 @@ const meta = { tags: ['autodocs'], argTypes: { backgroundColor: { control: 'color' }, - // @ts-expect-error Meta type is trying to force us to use real props as args - extraMetaArgType: { - type: { name: 'string' }, - name: 'Extra Meta', - description: 'An extra argtype added at the meta level', - table: { defaultValue: { summary: "'a default value'" } }, - }, }, parameters: { // Stop *this* story from being stacked in Chromatic @@ -43,15 +36,6 @@ export const Primary: Story = { primary: true, label: 'Button', }, - argTypes: { - // @ts-expect-error Story type is trying to force us to use real props as args - extraStoryArgType: { - type: { name: 'string' }, - name: 'Extra Story', - description: 'An extra argtype added at the story level', - table: { defaultValue: { summary: "'a default value'" } }, - }, - }, }; export const Secondary: Story = { From 9aadeade4c9af8e50aab2dc5d875aa8745519dce Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 19 Jan 2023 19:36:34 +1100 Subject: [PATCH 5/6] Update code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx Co-authored-by: Jeppe Reinhold --- code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx b/code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx index 283e0c208f1a..dfb6162c4262 100644 --- a/code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx +++ b/code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx @@ -22,11 +22,6 @@ const meta = { export default meta; type Story = StoryObj; -/** - * This is the primary mode for the button - * - * _this description was written as a comment above the story_ - */ export const NoParameters: Story = { argTypes: { // @ts-expect-error Story type is trying to force us to use real props as args From 4fec6d403645ca2ea5e94c18e7d792f4a2ecf548 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 19 Jan 2023 20:46:29 +1100 Subject: [PATCH 6/6] Small fix --- code/ui/blocks/src/components/Story.stories.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/ui/blocks/src/components/Story.stories.tsx b/code/ui/blocks/src/components/Story.stories.tsx index de3428db67af..e157a665b7c1 100644 --- a/code/ui/blocks/src/components/Story.stories.tsx +++ b/code/ui/blocks/src/components/Story.stories.tsx @@ -74,8 +74,7 @@ export const ForceInitialArgs = { // test that it ignores updated args by emitting an arg update and assert that it isn't reflected in the DOM play: async ({ args, canvasElement, loaded }: PlayFunctionContext) => { const docsContext = loaded.docsContext as DocsContextProps; - const resolved = docsContext.resolveModuleExport(args.storyExport); - if (resolved.type !== 'story') throw new Error('Bad export, pass a story!'); + const resolved = docsContext.resolveOf(args.storyExport, ['story']); await within(canvasElement).findByText(/Button/);