文中有部分名词可能不准确,见谅
本文主要介绍storybook+shadcn/ui+tailwindcss的storybook代码编写。
相关lib版本
“storybook”: “^8.2.4”,
“tailwindcss”: “^3.4.1”,
一、简介
首先,使用storybook开发一个组件需要两个文件,一个是组件*.tsx
一个是story*.stories.ts
/*.stories.tsx
,css使用了tailwind,所以不单独进行css文件的编写。
在这里关于story文件*.stories.ts
/*.stories.tsx
,如果有引入组件的需求,请务必使用tsx或者jsx,否则无法识别。
二、开始
以开发一个列表为例,此列表的列表项左侧是icon+title,并且支持点击选中和点击事件。
分成两个组件开发。一个是<ListItem>
,一个是<List>
。
先看ListItem的开发
1.ListItem
1)组件
下方是ListItem的组件代码,icon可以是导入的icon组件或者本地图片放在img标签内,这点随意。
注意,因为是tsx,所以该组件需要声明类型。
//ListItem.tsx
import React from 'react';
interface List{
title: string;
icon: string;
}
interface ListItemProps {
list: List;
onClick: () => void;
selected?: boolean;
}
const ListItem: React.FC<ListItemProps> = ({
list,
onClick,
selected = false,
}) => {
return(
<div
className={`flex items-center justify-between p-4 text-sm ${selected?'border-l-4 border-l-black bg-gray-500':''}`}
onClick={onClick}
>
<div className="flex items-center">
<div className="relative mr-4 size-10">
<img src={list.icon} alt=""/>
</div>
<div className="font-semibold">{list.title}</div>
</div>
</div>
)
}
这个组件接收三个参数,分别是list对象、onClick函数和select状态。根据这三个,在story文件中定义不同的story。
2)story
//ListItem.stories.tsx
import { Meta, StoryObj } from '@storybook/react';
import ListItem from './ListItem';
const meta: Meta<typeof ListItem> = {
title: 'List/ListItem',
component: ListItem,
argTypes: {
onClick: { action: 'onClick' },
selected: { control: 'boolean' },
},
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof ListItem>;
编写story文件,导入Meta生成meta,定义组件的属性。对于onClick和selected,放在argTypes
中,因为需要在文档中实现这两个属性的交互。StoryObj用于生成推断类型,在生成story实例时进行类型声明
//ListItem.stories.tsx
const List = {
title: "tmp1";
icon: "img1";
}
export const Default: Story = {
args: {
network: List,
selected: false,
},
};
export const Selected: Story = {
args: {
network: List,
selected: true,
},
};
上方的代码生成了两个story实例。在args中定义属性。args也可以在meta定义时声明,这样就可以得到所有story实例通用的属性。类似继承和重写。
2.List
1)组件
//List.tsx
import React from 'react';
import ListItem from './ListItem';
interface List{
title: string;
icon: string;
}
interface ListProps {
lists: List[];
onSelect: () => void;
selected:string
}
const List: React.FC<ListProps> = ({
lists,
onSelect,
selected
}) => {
return (
<div className="divide-y">
{lists.map((list) => (
<ListItem
key={list.title}
list={list}
onClick={onSelect}
selected={list.title === selected}
/>
))}
</div>
);
};
export default List;
2)story
//List.stories.tsx
import { Meta, StoryObj } from '@storybook/react';
import List from './List';
const meta: Meta<typeof List> = {
title: 'List/List',
component: List,
argTypes: {
onSelect: { action: 'onSelect' },
selected: { control: 'text' },
},
};
export default meta;
type Story = StoryObj<typeof List>;
const lists: List[] = [
{
title: 'tmp1',
icon: 'img1',
},
{
title: 'tmp2',
icon: 'img2',
},
];
export const Default: Story = {
args: {
list2,
selected: 'tmp2',
},
};