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

Blocks: Support subcomponents in ArgTypes and Controls, remove ArgsTable block #25614

Merged
merged 13 commits into from
Jan 18, 2024
Merged
15 changes: 11 additions & 4 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@
- [Description Doc block properties](#description-doc-block-properties)
- [Story Doc block properties](#story-doc-block-properties)
- [Manager API expandAll and collapseAll methods](#manager-api-expandall-and-collapseall-methods)
- [Source Doc block properties](#source-doc-block-properties)
- [Canvas Doc block properties](#canvas-doc-block-properties)
- [`ArgsTable` Doc block removed](#argstable-doc-block-removed)
- [`Source` Doc block properties](#source-doc-block-properties)
- [`Canvas` Doc block properties](#canvas-doc-block-properties)
- [`Primary` Doc block properties](#primary-doc-block-properties)
- [`createChannel` from `@storybook/postmessage` and `@storybook/channel-websocket`](#createchannel-from-storybookpostmessage-and-storybookchannel-websocket)
- [StoryStore and methods deprecated](#storystore-and-methods-deprecated)
Expand Down Expand Up @@ -1002,11 +1003,17 @@ api.collapseAll(); // becomes api.emit(STORIES_COLLAPSE_ALL)
api.expandAll(); // becomes api.emit(STORIES_EXPAND_ALL)
```

#### Source Doc block properties
#### `ArgsTable` Doc block removed

The `ArgsTable` doc block has been removed in favor of `ArgTypes` and `Controls`. [More info](#argstable-block).

With this removal we've reintroduced `subcomponents` support to `ArgTypes`, `Controls`, and autodocs. We've also undeprecated `subcomponents`, by popular demand.

#### `Source` Doc block properties

`id` and `ids` are now removed in favor of the `of` property. [More info](#doc-blocks).

#### Canvas Doc block properties
#### `Canvas` Doc block properties

The following properties were removed from the Canvas Doc block:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { global as globalThis } from '@storybook/global';

export default {
component: globalThis.Components.Button,
// FIXME: remove array subcomponents in 7.0?
subcomponents: {
Pre: globalThis.Components.Pre,
},
Expand Down
13 changes: 1 addition & 12 deletions code/lib/preview-api/src/modules/store/csf/processCSFFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import type {
NormalizedComponentAnnotations,
} from '@storybook/types';
import { isExportStory } from '@storybook/csf';
import { deprecate, logger } from '@storybook/client-logger';
import { logger } from '@storybook/client-logger';

import dedent from 'ts-dedent';
import { normalizeStory } from './normalizeStory';
import { normalizeComponentAnnotations } from './normalizeComponentAnnotations';

Expand Down Expand Up @@ -39,15 +38,6 @@ const checkDisallowedParameters = (parameters?: Parameters) => {
checkStorySort(parameters);
};

const checkSubcomponents = (meta: ModuleExports) => {
if (meta.subcomponents) {
deprecate(dedent`The \`subcomponents\` annotation is deprecated.

Please refer to the migration guide: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#argstable-block'
`);
}
};

// Given the raw exports of a CSF file, check and normalize it.
export function processCSFFile<TRenderer extends Renderer>(
moduleExports: ModuleExports,
Expand All @@ -63,7 +53,6 @@ export function processCSFFile<TRenderer extends Renderer>(
importPath
);
checkDisallowedParameters(meta.parameters);
checkSubcomponents(meta);

const csfFile: CSFFile<TRenderer> = { meta, stories: {}, moduleExports };

Expand Down
3 changes: 3 additions & 0 deletions code/ui/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ const config: StorybookConfig = {
],
build: {
test: {
// we have stories for the blocks here, we can't exclude them
disableBlocks: false,
// some stories in blocks (ArgTypes, Controls) depends on argTypes inference
disableDocgen: false,
},
},
framework: {
Expand Down
73 changes: 71 additions & 2 deletions code/ui/blocks/src/blocks/ArgTypes.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ import type { Meta, StoryObj } from '@storybook/react';

import { ArgTypes } from './ArgTypes';
import * as ExampleStories from '../examples/ArgTypesParameters.stories';
import * as SubcomponentsExampleStories from '../examples/ArgTypesWithSubcomponentsParameters.stories';
import { within } from '@storybook/test';
import type { PlayFunctionContext } from '@storybook/csf';

const meta: Meta<typeof ArgTypes> = {
title: 'Blocks/ArgTypes',
component: ArgTypes,
parameters: {
relativeCsfPaths: ['../examples/ArgTypesParameters.stories'],
relativeCsfPaths: [
'../examples/ArgTypesParameters.stories',
'../examples/ArgTypesWithSubcomponentsParameters.stories',
],
docsStyles: true,
},
};
Expand Down Expand Up @@ -46,7 +52,6 @@ export const OfUndefined: Story = {
decorators: [(s) => (window?.navigator.userAgent.match(/StorybookTestRunner/) ? <div /> : s())],
};

// NOTE: this will throw with no of prop
export const OfStoryUnattached: Story = {
parameters: { attached: false },
args: {
Expand Down Expand Up @@ -92,3 +97,67 @@ export const SortParameter: Story = {
of: ExampleStories.Sort,
},
};

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

const findSubcomponentTabs = async (
canvas: ReturnType<typeof within>,
step: PlayFunctionContext['step']
) => {
let subcomponentATab: HTMLElement;
let subcomponentBTab: HTMLElement;
await step('should have tabs for the subcomponents', async () => {
subcomponentATab = await canvas.findByText('SubcomponentA');
subcomponentBTab = await canvas.findByText('SubcomponentB');
});
return { subcomponentATab, subcomponentBTab };
};

export const SubcomponentsOfMeta: Story = {
args: {
of: SubcomponentsExampleStories.default,
},
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
await findSubcomponentTabs(canvas, step);
},
};

export const SubcomponentsOfStory: Story = {
...SubcomponentsOfMeta,
args: {
of: SubcomponentsExampleStories.NoParameters,
},
};

export const SubcomponentsIncludeProp: Story = {
args: {
of: SubcomponentsExampleStories.NoParameters,
include: ['a', 'f'],
},
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
const { subcomponentBTab } = await findSubcomponentTabs(canvas, step);
await subcomponentBTab.click();
},
};

export const SubcomponentsExcludeProp: Story = {
...SubcomponentsIncludeProp,
args: {
of: SubcomponentsExampleStories.NoParameters,
exclude: ['a', 'c', 'f', 'g'],
},
};

export const SubcomponentsSortProp: Story = {
...SubcomponentsIncludeProp,
args: {
of: SubcomponentsExampleStories.NoParameters,
sort: 'alpha',
},
};
34 changes: 26 additions & 8 deletions code/ui/blocks/src/blocks/ArgTypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import { filterArgTypes } from '@storybook/preview-api';
import type { ArgTypesExtractor } from '@storybook/docs-tools';
import React from 'react';

import { mapValues } from 'lodash';
import type { SortType } from '../components';
import { ArgsTable as PureArgsTable, ArgsTableError } from '../components';
import { ArgsTable as PureArgsTable, ArgsTableError, TabbedArgsTable } from '../components';
import { useOf } from './useOf';
import { getComponentName } from './utils';

type ArgTypesParameters = {
include?: PropDescriptor;
Expand All @@ -31,7 +33,7 @@ function extractComponentArgTypes(
return extractArgTypes(component);
}

function getArgTypesFromResolved(resolved: ReturnType<typeof useOf>, props: ArgTypesProps) {
function getArgTypesFromResolved(resolved: ReturnType<typeof useOf>) {
if (resolved.type === 'component') {
const {
component,
Expand All @@ -40,22 +42,23 @@ function getArgTypesFromResolved(resolved: ReturnType<typeof useOf>, props: ArgT
return {
argTypes: extractComponentArgTypes(component, parameters),
parameters,
component,
};
}

if (resolved.type === 'meta') {
const {
preparedMeta: { argTypes, parameters },
preparedMeta: { argTypes, parameters, component, subcomponents },
} = resolved;
return { argTypes, parameters };
return { argTypes, parameters, component, subcomponents };
}

// 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 },
story: { argTypes, parameters, component, subcomponents },
} = resolved;
return { argTypes, parameters };
return { argTypes, parameters, component, subcomponents };
}

export const ArgTypes: FC<ArgTypesProps> = (props) => {
Expand All @@ -64,7 +67,7 @@ export const ArgTypes: FC<ArgTypesProps> = (props) => {
throw new Error('Unexpected `of={undefined}`, did you mistype a CSF file reference?');
}
const resolved = useOf(of || 'meta');
const { argTypes, parameters } = getArgTypesFromResolved(resolved, props);
const { argTypes, parameters, component, subcomponents } = getArgTypesFromResolved(resolved);
const argTypesParameters = parameters.docs?.argTypes || ({} as ArgTypesParameters);

const include = props.include ?? argTypesParameters.include;
Expand All @@ -73,5 +76,20 @@ export const ArgTypes: FC<ArgTypesProps> = (props) => {

const filteredArgTypes = filterArgTypes(argTypes, include, exclude);

return <PureArgsTable rows={filteredArgTypes} sort={sort} />;
const hasSubcomponents = Boolean(subcomponents) && Object.keys(subcomponents).length > 0;

if (!hasSubcomponents) {
return <PureArgsTable rows={filteredArgTypes} sort={sort} />;
}

const mainComponentName = getComponentName(component);
const subcomponentTabs = mapValues(subcomponents, (comp) => ({
rows: filterArgTypes(extractComponentArgTypes(comp, parameters), include, exclude),
sort,
}));
const tabs = {
[mainComponentName]: { rows: filteredArgTypes, sort },
...subcomponentTabs,
};
return <TabbedArgsTable tabs={tabs} sort={sort} />;
};
Loading