Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Created ArgTypes component and stories #20664

Merged
merged 10 commits into from
Jan 20, 2023
82 changes: 82 additions & 0 deletions code/ui/blocks/src/blocks/ArgTypes.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { Meta, StoryObj } from '@storybook/react';

import { ArgTypes } from './ArgTypes';
import * as ExampleStories from '../examples/ArgTypesParameters.stories';

const meta: Meta<typeof ArgTypes> = {
title: 'Blocks/ArgTypes',
tmeasday marked this conversation as resolved.
Show resolved Hide resolved
component: ArgTypes,
parameters: {
relativeCsfPaths: ['../examples/ArgTypesParameters.stories'],
},
};
export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {};

export const OfComponent: Story = {
args: {
of: ExampleStories.default.component,
},
};

export const OfMeta: Story = {
args: {
of: ExampleStories.default,
},
};

export const OfStory: Story = {
args: {
of: ExampleStories.NoParameters,
},
};

// NOTE: this will throw with no of prop
export const OfStoryUnattached: Story = {
parameters: { attached: false },
args: {
of: ExampleStories.NoParameters,
},
};

export const IncludeProp: Story = {
args: {
of: ExampleStories.NoParameters,
include: ['a'],
},
};

export const IncludeParameter: Story = {
args: {
of: ExampleStories.Include,
},
};

export const ExcludeProp: Story = {
tmeasday marked this conversation as resolved.
Show resolved Hide resolved
args: {
of: ExampleStories.NoParameters,
exclude: ['a'],
},
};

export const ExcludeParameter: Story = {
args: {
of: ExampleStories.Exclude,
},
};

export const SortProp: Story = {
tmeasday marked this conversation as resolved.
Show resolved Hide resolved
args: {
of: ExampleStories.NoParameters,
sort: 'alpha',
},
};

export const SortParameter: Story = {
args: {
of: ExampleStories.Sort,
},
};
76 changes: 76 additions & 0 deletions code/ui/blocks/src/blocks/ArgTypes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* 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 { 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<typeof useOf>, props: ArgTypesProps) {
if (resolved.type === 'component') {
const {
component,
projectAnnotations: { parameters },
} = resolved;
return {
argTypes: extractComponentArgTypes(component, parameters),
parameters,
};
}

if (resolved.type === 'meta') {
const {
preparedMeta: { argTypes, parameters },
} = resolved;
return { argTypes, 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<ArgTypesProps> = (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 <PureArgsTable rows={filteredArgTypes} sort={sort} />;
};
1 change: 1 addition & 0 deletions code/ui/blocks/src/blocks/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
3 changes: 1 addition & 2 deletions code/ui/blocks/src/components/Story.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<WebRenderer>) => {
const docsContext = loaded.docsContext as DocsContextProps;
const resolved = docsContext.resolveOf(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/);

Expand Down
50 changes: 50 additions & 0 deletions code/ui/blocks/src/examples/ArgTypesParameters.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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<typeof ArgTypesParameters>;

export default meta;
type Story = StoryObj<typeof meta>;

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' } } },
};
5 changes: 5 additions & 0 deletions code/ui/blocks/src/examples/ArgTypesParameters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';

type PropTypes = { a?: string; b: string };

export const ArgTypesParameters = ({ a = 'a', b }: PropTypes) => <div>Example story</div>;