Skip to content

Commit 9218ac5

Browse files
committed
refactor: clean up Storybook stories and improve prop documentation
1 parent 11fa867 commit 9218ac5

26 files changed

+398
-587
lines changed

.storybook/locales/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ storyI18n.init({
1717
resources,
1818
});
1919

20-
export const t: i18n['t'] = ((...args) => {
20+
export const storyT: i18n['t'] = ((...args) => {
2121
return storyI18n.t(...args);
2222
}) as i18n['t'];
2323

2424
export const useStoryT = () => {
2525
const context = useContext(ReactEasyContext);
2626
const lang = context.lang;
2727

28-
// eslint-disable-next-line @tiny-codes/react-hooks/exhaustive-deps
29-
return useMemo(() => ((...args: unknown) => t(...args)) as typeof t, [lang]);
28+
// eslint-disable-next-line @tiny-codes/react-hooks/exhaustive-deps, @typescript-eslint/no-explicit-any
29+
return useMemo(() => ((...args: any) => storyT(...args)) as typeof storyT, [lang]);
3030
};
3131

3232
export default storyI18n;

.storybook/preview.tsx

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { type ComponentType, type PropsWithChildren, useMemo } from 'react';
2-
import { DocsContainer, type DocsContainerProps } from '@storybook/addon-docs/blocks';
2+
import { type Control, DocsContainer, type DocsContainerProps } from '@storybook/addon-docs/blocks';
33
import type { Preview, ReactRenderer } from '@storybook/react-vite';
44
import { App as AntdApp, ConfigProvider as AntdConfigProvider, theme } from 'antd';
55
import enUS from 'antd/es/locale/en_US';
@@ -8,6 +8,7 @@ import type { StoryContext, StoryContextForEnhancers } from 'storybook/internal/
88
import { themes } from 'storybook/theming';
99
import ConfigProvider from '../src/components/ConfigProvider';
1010
import storyI18n from './locales';
11+
import { inferControlFromDocgenType, standardizeJsDocDefaultValue } from './utils/jsdoc';
1112
import './preview.css';
1213

1314
const isPreferDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
@@ -97,33 +98,37 @@ function stripExampleBlock(input = '') {
9798
);
9899
}
99100

101+
/** Enhances the argTypes of a story based on JSDoc comments. */
100102
function jsdocArgTypesEnhancer(context: StoryContextForEnhancers) {
101103
const component = context.component;
102104
const docProps = component?.__docgenInfo?.props;
103105
if (!docProps) return context.argTypes;
104106

105-
const next = { ...(context.argTypes || {}) };
107+
const newArgTypes = { ...(context.argTypes || {}) };
106108

107109
// eslint-disable-next-line @typescript-eslint/no-explicit-any
108-
Object.entries(docProps).forEach(([propName, propInfo]: [string, any]) => {
109-
next[propName] = {
110-
...(next[propName] || {}),
111-
// control: ?
110+
Object.entries(docProps).forEach(([name, docProp]: [string, any]) => {
111+
const inferred = inferControlFromDocgenType(docProp?.type);
112+
const argType = newArgTypes[name];
113+
114+
newArgTypes[name] = {
115+
...argType,
116+
control: { type: argType?.control ?? inferred.control } as Control,
117+
options: argType?.options ?? inferred.options,
112118
// The handwritten description will not be overwritten.
113-
description: next[propName]?.description ?? propInfo?.description ?? '',
119+
description: argType?.description ?? docProp?.description ?? '',
114120
table: {
115-
...(next[propName]?.table || {}),
116-
type: next[propName]?.table?.type ?? {
117-
summary: propInfo?.type?.name || '',
121+
...(argType?.table || {}),
122+
defaultValue: {
123+
...docProp?.defaultValue,
124+
summary:
125+
docProp?.defaultValue?.summary ?? standardizeJsDocDefaultValue(String(docProp.defaultValue?.value ?? '-')),
118126
},
119-
defaultValue:
120-
next[propName]?.table?.defaultValue ??
121-
(propInfo?.defaultValue?.value ? { summary: String(propInfo.defaultValue.value) } : undefined),
122127
},
123128
};
124129
});
125-
console.log(next);
126-
return next;
130+
console.log('newArgTypes', newArgTypes);
131+
return newArgTypes;
127132
}
128133

129134
export default preview;

.storybook/stories/BreakLines.stories.tsx

Lines changed: 3 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,52 +11,11 @@ const meta: Meta<typeof BreakLines> = {
1111
tagName: 'div',
1212
className: '',
1313
} satisfies Partial<BreakLinesProps>,
14-
argTypes: {
15-
// value: {
16-
// control: 'text',
17-
// description: `- **EN:** Text content
18-
// - **CN:** 文本内容`,
19-
// table: {
20-
// defaultValue: { summary: '第一行\\n第二行\\n第三行(demo)' },
21-
// },
22-
// },
23-
// enabled: {
24-
// control: 'boolean',
25-
// description: `- **EN:** Whether the line break conversion is enabled.
26-
// - **CN:** 是否启用换行转换。`,
27-
// table: {
28-
// defaultValue: { summary: 'true' },
29-
// },
30-
// },
31-
// EOL: {
32-
// control: 'text',
33-
// description: `- **EN:** The end-of-line character to use for splitting lines.
34-
// - **CN:** 用于分割行的行结束字符。`,
35-
// table: {
36-
// defaultValue: { summary: '\\n' },
37-
// },
38-
// },
39-
// tagName: {
40-
// control: 'select',
41-
// options: [false, 'span', 'div', 'i', 'pre'],
42-
// description: `- **EN:** The HTML tag to use for rendering the content.
43-
// - **CN:** 用于渲染内容的 HTML 标签。`,
44-
// table: {
45-
// defaultValue: { summary: 'false(组件默认) / div(demo)' },
46-
// },
47-
// },
48-
// className: {
49-
// control: 'text',
50-
// description: `- **EN:** The CSS class name of the dom node, if \`tagName\` is set to false, this property is invalid.
51-
// - **CN:** dom 节点的 css 类名,如果 \`tagName\` 设置为 false,则此属性无效。`,
52-
// table: {
53-
// defaultValue: { summary: '""(demo)' },
54-
// },
55-
// },
56-
},
14+
argTypes: {},
5715
};
16+
type BreakLinesStoryArgs = BreakLinesProps;
5817

5918
export default meta;
60-
type Story = StoryObj<typeof BreakLines>;
19+
type Story = StoryObj<BreakLinesStoryArgs>;
6120

6221
export const Playground: Story = {};

.storybook/stories/ColumnSetting.stories.tsx

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import type { Meta, StoryObj } from '@storybook/react-vite';
33
import { Table } from 'antd';
44
import type { ColumnsType } from 'antd/es/table';
55
import ColumnSetting from '../../src/components/ColumnSetting';
6-
import type { ColumnSettingItem } from '../../src/components/ColumnSetting';
7-
import { useStoryT } from '../locales';
6+
import type { ColumnSettingItem, ColumnSettingProps } from '../../src/components/ColumnSetting';
7+
import { storyT, useStoryT } from '../locales';
88

99
interface User {
1010
id: number;
@@ -13,11 +13,7 @@ interface User {
1313
city: string;
1414
role: string;
1515
}
16-
17-
interface ColumnSettingStoryArgs {
18-
storageKey: string;
19-
useStorage: boolean;
20-
}
16+
type ColumnSettingStoryArgs = ColumnSettingProps;
2117

2218
const buildBaseColumns = (t: ReturnType<typeof useStoryT>): ColumnSettingItem<User>[] => [
2319
{ title: 'ID', dataIndex: 'id', key: 'id', width: 80, disabled: true },
@@ -29,24 +25,12 @@ const buildBaseColumns = (t: ReturnType<typeof useStoryT>): ColumnSettingItem<Us
2925

3026
const meta: Meta<ColumnSettingStoryArgs> = {
3127
title: 'Components/ColumnSetting',
28+
component: ColumnSetting,
3229
args: {
33-
useStorage: false,
3430
storageKey: 'storybook:column-setting',
31+
columns: buildBaseColumns(storyT),
3532
},
36-
argTypes: {
37-
useStorage: {
38-
control: 'boolean',
39-
description: `- **EN:** Whether to enable local storage persistence in this demo.
40-
- **CN:** 当前示例中是否启用本地存储持久化。`,
41-
table: { defaultValue: { summary: 'false(demo)' } },
42-
},
43-
storageKey: {
44-
control: 'text',
45-
description: `- **EN:** Local storage key for persisting column settings.
46-
- **CN:** 用于持久化列设置的本地存储键。`,
47-
table: { defaultValue: { summary: 'storybook:column-setting(demo)' } },
48-
},
49-
},
33+
argTypes: {},
5034
};
5135

5236
export default meta;
@@ -87,7 +71,7 @@ export const Playground: Story = {
8771
<div style={{ marginBottom: 12, display: 'flex', justifyContent: 'flex-end' }}>
8872
<ColumnSetting<ColumnSettingItem<User>>
8973
columns={columns}
90-
storageKey={args.useStorage ? args.storageKey : undefined}
74+
storageKey={args.storageKey}
9175
onChange={setColumns}
9276
triggerProps={{ children: t('storybook.stories.ColumnSetting.trigger') }}
9377
/>

.storybook/stories/ConfigProvider.stories.tsx

Lines changed: 8 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,21 @@
11
import type { Meta, StoryObj } from '@storybook/react-vite';
22
import { App as AntdApp, Space, Typography } from 'antd';
3-
import ConfigProvider from '../../src/components/ConfigProvider';
3+
import ConfigProvider, { type ConfigProviderProps } from '../../src/components/ConfigProvider';
44
import ConfirmAction from '../../src/components/ConfirmAction';
55

6-
interface ConfigProviderStoryArgs {
7-
lang: 'en' | 'zh-CN';
8-
defaultTitle: string;
9-
defaultContent: string;
10-
}
6+
type ConfigProviderStoryArgs = ConfigProviderProps;
117

128
const meta: Meta<ConfigProviderStoryArgs> = {
139
title: 'Components/ConfigProvider',
10+
component: ConfigProvider,
1411
args: {
1512
lang: 'zh-CN',
16-
defaultTitle: '全局确认标题',
17-
defaultContent: '这是由 ConfigProvider 注入的全局默认内容。',
18-
},
19-
argTypes: {
20-
lang: {
21-
control: 'radio',
22-
options: ['en', 'zh-CN'],
23-
description: `- **EN:** Language of the component for global configuration.
24-
- **CN:** 组件语言(全局配置)。`,
25-
table: { defaultValue: { summary: 'en(组件默认) / zh-CN(demo)' } },
26-
},
27-
defaultTitle: {
28-
control: 'text',
29-
description: `- **EN:** Global default title for \`ConfirmAction\` in this demo.
30-
- **CN:** 当前示例中 \`ConfirmAction\` 的全局默认标题。`,
31-
table: { defaultValue: { summary: '全局确认标题(demo)' } },
32-
},
33-
defaultContent: {
34-
control: 'text',
35-
description: `- **EN:** Global default content for \`ConfirmAction\` in this demo.
36-
- **CN:** 当前示例中 \`ConfirmAction\` 的全局默认内容。`,
37-
table: { defaultValue: { summary: '这是由 ConfigProvider 注入的全局默认内容。(demo)' } },
13+
ConfirmAction: {
14+
title: '全局确认标题',
15+
content: '这是由 ConfigProvider 注入的全局默认内容。',
3816
},
3917
},
18+
argTypes: {},
4019
};
4120

4221
export default meta;
@@ -45,13 +24,7 @@ type Story = StoryObj<ConfigProviderStoryArgs>;
4524
export const Playground: Story = {
4625
render: (args: ConfigProviderStoryArgs) => (
4726
<AntdApp>
48-
<ConfigProvider
49-
lang={args.lang}
50-
ConfirmAction={{
51-
title: args.defaultTitle,
52-
content: args.defaultContent,
53-
}}
54-
>
27+
<ConfigProvider {...args}>
5528
<Space direction="vertical" size={12}>
5629
<Typography.Text>下面按钮未传 title/content,将使用全局默认值:</Typography.Text>
5730
<ConfirmAction.Button onOk={async () => Promise.resolve()}>打开确认框</ConfirmAction.Button>

.storybook/stories/ConfirmAction.stories.tsx

Lines changed: 19 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
import type { Meta, StoryObj } from '@storybook/react-vite';
2-
import { App as AntdApp, message } from 'antd';
2+
import type { ButtonProps, SwitchProps } from 'antd';
3+
import { App as AntdApp } from 'antd';
4+
import type { LinkProps } from 'antd/es/typography/Link';
5+
import type { ConfirmActionProps } from '../../src/components/ConfirmAction';
36
import ConfirmAction from '../../src/components/ConfirmAction';
47

5-
type TriggerType = 'button' | 'switch' | 'link';
8+
type TriggerType = 'Button' | 'Switch' | 'Link';
69

7-
interface ConfirmActionStoryArgs {
10+
type ConfirmActionStoryArgs = ConfirmActionProps<object, never> & {
811
triggerType: TriggerType;
9-
triggerText: string;
10-
title: string;
11-
content: string;
12-
danger: boolean;
13-
okText: string;
14-
cancelText: string;
15-
}
12+
};
1613

1714
const meta: Meta<ConfirmActionStoryArgs> = {
1815
title: 'Components/ConfirmAction',
1916
args: {
20-
triggerType: 'button',
21-
triggerText: '执行操作',
17+
triggerType: 'Button',
18+
triggerProps: {
19+
children: '执行操作',
20+
},
2221
title: '确认执行该操作?',
2322
content: '这是一个可交互的 ConfirmAction demo。',
2423
danger: false,
@@ -28,46 +27,10 @@ const meta: Meta<ConfirmActionStoryArgs> = {
2827
argTypes: {
2928
triggerType: {
3029
control: 'radio',
31-
options: ['button', 'switch', 'link'],
30+
options: ['Button', 'Switch', 'Link'],
3231
description: `- **EN:** Demo-only option to switch trigger component type.
3332
- **CN:** 示例专用:切换触发器组件类型。`,
34-
table: { defaultValue: { summary: 'button(demo)' } },
35-
},
36-
triggerText: {
37-
control: 'text',
38-
description: `- **EN:** Custom trigger content.
39-
- **CN:** 自定义触发器内容。`,
40-
table: { defaultValue: { summary: '执行操作(demo)' } },
41-
},
42-
title: {
43-
control: 'text',
44-
description: `- **EN:** Confirm box title.
45-
- **CN:** 确认框标题。`,
46-
table: { defaultValue: { summary: '确认执行该操作?(demo)' } },
47-
},
48-
content: {
49-
control: 'text',
50-
description: `- **EN:** Confirm box content text.
51-
- **CN:** 确认框内容文本。`,
52-
table: { defaultValue: { summary: '这是一个可交互的 ConfirmAction demo。(demo)' } },
53-
},
54-
danger: {
55-
control: 'boolean',
56-
description: `- **EN:** Whether to display in red danger mode.
57-
- **CN:** 是否显示为红色危险模式。`,
58-
table: { defaultValue: { summary: 'false' } },
59-
},
60-
okText: {
61-
control: 'text',
62-
description: `- **EN:** Confirm button text.
63-
- **CN:** 确认按钮文本。`,
64-
table: { defaultValue: { summary: '确认(demo)' } },
65-
},
66-
cancelText: {
67-
control: 'text',
68-
description: `- **EN:** Cancel button text.
69-
- **CN:** 取消按钮文本。`,
70-
table: { defaultValue: { summary: '取消(demo)' } },
33+
table: { defaultValue: { summary: '"Button"' } },
7134
},
7235
},
7336
};
@@ -77,36 +40,27 @@ type Story = StoryObj<ConfirmActionStoryArgs>;
7740

7841
export const Playground: Story = {
7942
render: (args: ConfirmActionStoryArgs) => {
80-
const commonProps = {
81-
title: args.title,
82-
content: args.content,
83-
danger: args.danger,
84-
okText: args.okText,
85-
cancelText: args.cancelText,
86-
onOk: async () => {
87-
message.success('操作成功');
88-
},
89-
};
43+
const { triggerType, ...props } = args;
9044

91-
if (args.triggerType === 'switch') {
45+
if (triggerType === 'Switch') {
9246
return (
9347
<AntdApp>
94-
<ConfirmAction.Switch {...commonProps} />
48+
<ConfirmAction.Switch {...(props as ConfirmActionProps<SwitchProps, 'onChange'>)} />
9549
</AntdApp>
9650
);
9751
}
9852

99-
if (args.triggerType === 'link') {
53+
if (triggerType === 'Link') {
10054
return (
10155
<AntdApp>
102-
<ConfirmAction.Link {...commonProps}>{args.triggerText}</ConfirmAction.Link>
56+
<ConfirmAction.Link {...(props as ConfirmActionProps<LinkProps, 'onClick'>)} />
10357
</AntdApp>
10458
);
10559
}
10660

10761
return (
10862
<AntdApp>
109-
<ConfirmAction.Button {...commonProps}>{args.triggerText}</ConfirmAction.Button>
63+
<ConfirmAction.Button {...(props as ConfirmActionProps<ButtonProps, 'onClick'>)} />
11064
</AntdApp>
11165
);
11266
},

0 commit comments

Comments
 (0)