Atomic Design结合Storybook开发时Modal动态插入Atoms的实现问题
Atomic Design 模态框(Modal)分子组件动态传入原子组件解决方案
现有实现问题梳理
- 节点渲染逻辑错误:如果传入的
test是函数类型组件,直接写{test}不会触发渲染,需要执行函数或作为组件调用 - 结构位置错误:自定义内容放在了header容器标签外部,样式会出现错位
- Prop命名语义模糊:
test命名无法明确对应区域,扩展多区域配置时维护成本极高 - 区域配置不完整:仅支持header区域自定义,无法覆盖body、footer的灵活配置需求
修正后组件实现
采用具名Prop插槽思路,分别开放header、body、footer三个可配置区域,支持传入任意React节点(包括原子组件、组合元素、纯文本等):
export default function ModalMolecule({ header, body, footer, ...props }) { return ( <> <div className="justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 z-50 outline-none focus:outline-none"> <div className="relative w-auto my-6 mx-auto max-w-3xl"> {/*content*/} <div className="border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-white outline-none focus:outline-none"> {/*header:仅传入内容时渲染容器*/} {header && ( <div className="flex items-start justify-between p-5 border-b border-solid border-blueGray-200 rounded-t"> {header} </div> )} {/*body*/} {body && ( <div className="relative p-6 flex-auto"> {body} </div> )} {/*footer*/} {footer && ( <div className="flex items-center justify-end p-6 border-t border-solid border-blueGray-200 rounded-b"> {footer} </div> )} </div> </div> </div> <div className="opacity-25 fixed inset-0 z-40 bg-black"></div> </> ); }
调用方式说明
普通场景(无内部状态依赖)
直接传入JSX节点即可,无需包裹函数:
import ButtonAtom from '../atoms/Button'; // 场景1:Header仅渲染标题 <ModalMolecule header={<h3>弹窗标题</h3>} body={<div>弹窗内容区域</div>} footer={<ButtonAtom type="primary">确认</ButtonAtom>} /> // 场景2:Header放置3个按钮原子 <ModalMolecule header={ <> <ButtonAtom type="text">返回</ButtonAtom> <h3>编辑页面</h3> <ButtonAtom type="text">帮助</ButtonAtom> </> } body={<div>编辑表单内容</div>} footer={ <> <ButtonAtom>取消</ButtonAtom> <ButtonAtom type="primary">保存</ButtonAtom> </> } />
特殊场景(需要使用Modal内部状态)
如果自定义内容需要用到Modal内部的关闭、可见状态等参数,可修改组件支持渲染函数入参:
// 组件内部渲染逻辑调整,注入内部状态 {typeof header === 'function' ? header({ close: props.onClose, visible: props.visible }) : header} // 调用时传入渲染函数 <ModalMolecule header={({close}) => ( <> <h3>带关闭按钮的标题</h3> <ButtonAtom onClick={close}>关闭</ButtonAtom> </> )} />
Storybook 配置优化
可针对不同使用场景单独配置Storybook示例,覆盖所有适配需求:
import ButtonAtom from '../atoms/Button'; export default { title: "Molecules/Modal", component: ModalMolecule, }; const Template = (args) => <ModalMolecule {...args} />; // 基础弹窗示例 export const Basic = Template.bind({}); Basic.args = { header: <h3>基础弹窗</h3>, body: <div>这是基础弹窗的内容</div>, footer: <ButtonAtom type="primary">确认</ButtonAtom> }; // Header带多按钮示例 export const HeaderWithMultiButtons = Template.bind({}); HeaderWithMultiButtons.args = { header: <> <ButtonAtom type="text">返回</ButtonAtom> <h3>编辑弹窗</h3> <ButtonAtom type="text">导出</ButtonAtom> </>, body: <div>编辑表单内容区域</div>, footer: <> <ButtonAtom>取消</ButtonAtom> <ButtonAtom type="primary">保存</ButtonAtom> </> };
扩展优化建议
- 增加默认值:未传入header时默认渲染带关闭按钮的通用头部,减少重复代码
- TS类型约束:给Props增加类型定义,限制传入值为ReactNode类型,避免非法值报错
- 尺寸适配:新增size Prop控制弹窗宽度,覆盖不同场景的尺寸需求
内容的提问来源于stack exchange,提问作者jjlittle
相关产品推荐
相关产品推荐

