Storybook中Dialog组件渲染函数报错:缺失args属性
问题背景
原本Dialog组件的Story代码为:
const meta: Meta<typeof Dialog> = { component: Dialog, title: "Feedback/Dialog/Dialog", } export default meta type Story = StoryObj<typeof Dialog>
后续修改为使用satisfies关键字的结构:
const meta = { component: Dialog, title: "Feedback/Dialog/Dialog", } satisfies Meta<typeof Dialog> export default meta type Story = StoryObj<typeof meta>
修改后,使用自定义render函数的Story开始报错,提示必须包含args属性,报错信息如下:
Property 'args' is missing in type '{ render: () => JSX.Element; }' but required in type '{ args: { "aria-describedby"?: string | undefined; "aria-labelledby"?: string | undefined; children?: ReactNode; classes?: Partial
| undefined; ... 291 more ...; inlist?: any; }; }'.
完整报错代码:
import { Meta, StoryObj } from "@storybook/react" import { Box } from "@mui/material" import { FC, useState } from "react" import { Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from "components/Dialog" import { Button } from "components/Button" import { Typography } from "components/Typography" const meta = { component: Dialog, title: "Feedback/Dialog/Dialog", } satisfies Meta<typeof Dialog> export default meta type Story = StoryObj<typeof meta> type DialogWrapperComponentProps = { title?: boolean actions?: boolean } const DialogWrapperComponent: FC<DialogWrapperComponentProps> = ({ title = false, actions = false }) => { const [isModalOpen, setIsModalOpen] = useState(false) const handleChange = () => { setIsModalOpen(!isModalOpen) } return ( <Box> <Box style={{ textAlign: "center" }}> <Button variant="contained" onClick={handleChange}> Открыть модальное окно </Button> </Box> <Dialog open={isModalOpen} onClose={handleChange}> {title && <DialogTitle>Фильтр</DialogTitle>} <DialogContent sx={{ paddingTop: title ? "0" : "64px" }}> <DialogContentText>Lorem ipsum</DialogContentText> <Box style={{ width: "376px", height: "200px", backgroundColor: "red", color: "white" }}>content</Box> <Box style={{ width: "376px", height: "200px", backgroundColor: "red", color: "white" }}>content</Box> </DialogContent> {actions && ( <DialogActions> <Button sx={{ width: "180px", borderWidth: "1px", borderStyle: "solid", borderColor: "neutral.800" }} disabled> <Typography variant="titleSmall">Сбросить</Typography> </Button> <Button sx={{ width: "180px" }} variant="contained"> <Typography variant="titleSmall">Применить</Typography> </Button> </DialogActions> )} </Dialog> </Box> ) } export const Default: Story = { render: () => <DialogWrapperComponent title actions />, } export const WithoutActions: Story = { render: () => <DialogWrapperComponent title />, } export const WithoutTitle: Story = { render: () => <DialogWrapperComponent actions />, } export const WithoutActionsAndTitle: Story = { render: () => <DialogWrapperComponent />, }
原因分析
问题出在type Story = StoryObj<typeof meta>这一行:当使用StoryObj<typeof meta>时,TypeScript会根据meta中指定的component(即Dialog组件),强制要求所有Story必须包含该组件对应的args属性,即便你使用自定义render函数完全控制了组件渲染流程。
而原写法StoryObj<typeof Dialog>虽然也关联了Dialog组件,但Storybook的类型系统允许在提供render函数时忽略args属性。
解决方案
有三种便捷的解决方式:
方法一:恢复Story类型关联原组件
保留satisfies的meta写法,同时将Story类型改回关联typeof Dialog:
const meta = { component: Dialog, title: "Feedback/Dialog/Dialog", } satisfies Meta<typeof Dialog> export default meta // 改为关联Dialog组件而非meta type Story = StoryObj<typeof Dialog>
这种方式既保留了satisfies带来的类型校验能力,又不会强制要求Story必须包含args属性。
方法二:给每个Story添加空args属性
如果不想修改Story类型,可以直接给每个Story添加空的args对象:
export const Default: Story = { render: () => <DialogWrapperComponent title actions />, args: {}, } export const WithoutActions: Story = { render: () => <DialogWrapperComponent title />, args: {}, } // 其余Story同理添加空args
方法三:在meta中定义默认空args
在meta里添加默认的空args,所有子Story会自动继承这个配置,无需单独添加:
const meta = { component: Dialog, title: "Feedback/Dialog/Dialog", args: {}, // 添加默认空args } satisfies Meta<typeof Dialog> export default meta type Story = StoryObj<typeof meta>
内容的提问来源于stack exchange,提问作者Alice Alikaeva

