Create atomic component library and Storybook stories

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-08 14:48:35 +00:00
parent 20ce8ec563
commit 7111ca899c
15 changed files with 569 additions and 14 deletions

View File

@@ -0,0 +1,46 @@
import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
import ConfirmDialog from './ConfirmDialog';
const meta = {
title: 'Admin/ConfirmDialog',
component: ConfirmDialog,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
onConfirm: { action: 'confirm' },
onCancel: { action: 'cancel' },
},
} satisfies Meta<typeof ConfirmDialog>;
export default meta;
type Story = StoryObj<typeof meta>;
// Stories based on features.json storybookStories.ConfirmDialog
export const Default: Story = {
name: 'Default',
args: {
open: true,
title: 'Confirm Action',
message: 'Are you sure you want to proceed?',
confirmLabel: 'Confirm',
cancelLabel: 'Cancel',
onConfirm: fn(),
onCancel: fn(),
},
};
export const DeleteWarning: Story = {
name: 'Delete Warning',
args: {
open: true,
title: 'Delete Item',
message: 'This action cannot be undone. Are you sure you want to delete this item?',
confirmLabel: 'Delete',
cancelLabel: 'Cancel',
onConfirm: fn(),
onCancel: fn(),
},
};

View File

@@ -1,13 +1,13 @@
'use client';
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
} from '@mui/material';
import Button from '../atoms/Button';
type ConfirmDialogProps = {
open: boolean;
@@ -35,10 +35,8 @@ export default function ConfirmDialog({
<DialogContentText>{message}</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={onCancel}>{cancelLabel}</Button>
<Button onClick={onConfirm} color="error" variant="contained">
{confirmLabel}
</Button>
<Button onClick={onCancel} text={cancelLabel} />
<Button onClick={onConfirm} color="error" variant="contained" text={confirmLabel} />
</DialogActions>
</Dialog>
);

View File

@@ -0,0 +1,66 @@
import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
import DataGrid from './DataGrid';
const meta = {
title: 'Admin/DataGrid',
component: DataGrid,
parameters: {
layout: 'padded',
},
tags: ['autodocs'],
argTypes: {
onEdit: { action: 'edit' },
onDelete: { action: 'delete' },
},
} satisfies Meta<typeof DataGrid>;
export default meta;
type Story = StoryObj<typeof meta>;
// Stories based on features.json storybookStories.DataGrid
export const Default: Story = {
name: 'Default',
args: {
columns: [
{ name: 'id', label: 'ID' },
{ name: 'name', label: 'Name' },
{ name: 'email', label: 'Email' },
],
rows: [
{ id: 1, name: 'John Doe', email: 'john@example.com' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' },
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com' },
],
primaryKey: 'id',
},
};
export const WithActions: Story = {
name: 'With Edit/Delete Actions',
args: {
columns: [
{ name: 'id', label: 'ID' },
{ name: 'name', label: 'Name' },
{ name: 'status', label: 'Status' },
],
rows: [
{ id: 1, name: 'Active User', status: 'active' },
{ id: 2, name: 'Pending User', status: 'pending' },
],
onEdit: fn(),
onDelete: fn(),
primaryKey: 'id',
},
};
export const Empty: Story = {
name: 'Empty State',
args: {
columns: [
{ name: 'id', label: 'ID' },
{ name: 'name', label: 'Name' },
],
rows: [],
},
};

View File

@@ -1,9 +1,7 @@
'use client';
import DeleteIcon from '@mui/icons-material/Delete';
import EditIcon from '@mui/icons-material/Edit';
import {
IconButton,
IconButton as MuiIconButton,
Paper,
Table,
TableBody,
@@ -13,6 +11,7 @@ import {
TableRow,
Tooltip,
} from '@mui/material';
import IconButton from '../atoms/IconButton';
type DataGridProps = {
columns: Array<{ name: string; label?: string }>;
@@ -54,16 +53,12 @@ export default function DataGrid({ columns, rows, onEdit, onDelete, primaryKey =
<TableCell>
{onEdit && (
<Tooltip title="Edit">
<IconButton size="small" onClick={() => onEdit(row)}>
<EditIcon fontSize="small" />
</IconButton>
<IconButton size="small" onClick={() => onEdit(row)} icon="Edit" />
</Tooltip>
)}
{onDelete && (
<Tooltip title="Delete">
<IconButton size="small" color="error" onClick={() => onDelete(row)}>
<DeleteIcon fontSize="small" />
</IconButton>
<IconButton size="small" color="error" onClick={() => onDelete(row)} icon="Delete" />
</Tooltip>
)}
</TableCell>

View File

@@ -0,0 +1,65 @@
import type { Meta, StoryObj } from '@storybook/react';
import Button from './Button';
const meta = {
title: 'Atoms/Button',
component: Button,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['text', 'outlined', 'contained'],
},
color: {
control: 'select',
options: ['default', 'primary', 'secondary', 'error', 'warning', 'info', 'success'],
},
size: {
control: 'select',
options: ['small', 'medium', 'large'],
},
},
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
// Stories based on features.json storybookStories
export const Primary: Story = {
name: 'Primary Button',
args: {
variant: 'contained',
color: 'primary',
text: 'Click Me',
},
};
export const Secondary: Story = {
name: 'Secondary Button',
args: {
variant: 'outlined',
color: 'secondary',
text: 'Cancel',
},
};
export const WithIcon: Story = {
name: 'With Icon',
args: {
variant: 'contained',
startIcon: 'Add',
text: 'Add Item',
},
};
export const Loading: Story = {
name: 'Loading State',
args: {
variant: 'contained',
disabled: true,
text: 'Loading...',
},
};

View File

@@ -0,0 +1,35 @@
'use client';
import { Button as MuiButton, ButtonProps as MuiButtonProps } from '@mui/material';
import * as Icons from '@mui/icons-material';
export type ButtonProps = Omit<MuiButtonProps, 'startIcon' | 'endIcon'> & {
text?: string;
startIcon?: keyof typeof Icons;
endIcon?: keyof typeof Icons;
};
/**
* Atomic Button component
* Wraps Material-UI Button with icon support from features.json
*/
export default function Button({
text,
children,
startIcon,
endIcon,
...props
}: ButtonProps) {
const StartIconComponent = startIcon ? Icons[startIcon] : null;
const EndIconComponent = endIcon ? Icons[endIcon] : null;
return (
<MuiButton
{...props}
startIcon={StartIconComponent ? <StartIconComponent /> : undefined}
endIcon={EndIconComponent ? <EndIconComponent /> : undefined}
>
{text || children}
</MuiButton>
);
}

View File

@@ -0,0 +1,53 @@
import type { Meta, StoryObj } from '@storybook/react';
import Icon from './Icon';
const meta = {
title: 'Atoms/Icon',
component: Icon,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
color: {
control: 'select',
options: ['inherit', 'primary', 'secondary', 'action', 'disabled', 'error'],
},
fontSize: {
control: 'select',
options: ['small', 'medium', 'large', 'inherit'],
},
},
} satisfies Meta<typeof Icon>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Storage: Story = {
args: {
name: 'Storage',
color: 'primary',
},
};
export const Code: Story = {
args: {
name: 'Code',
fontSize: 'large',
},
};
export const AccountTree: Story = {
args: {
name: 'AccountTree',
color: 'secondary',
},
};
export const Speed: Story = {
args: {
name: 'Speed',
color: 'error',
fontSize: 'large',
},
};

View File

@@ -0,0 +1,23 @@
'use client';
import { SvgIconProps } from '@mui/material';
import * as Icons from '@mui/icons-material';
export type IconProps = SvgIconProps & {
name: keyof typeof Icons;
};
/**
* Atomic Icon component
* Renders Material-UI icons by name from features.json
*/
export default function Icon({ name, ...props }: IconProps) {
const IconComponent = Icons[name];
if (!IconComponent) {
console.warn(`Icon "${name}" not found in Material Icons`);
return null;
}
return <IconComponent {...props} />;
}

View File

@@ -0,0 +1,52 @@
import type { Meta, StoryObj } from '@storybook/react';
import IconButton from './IconButton';
const meta = {
title: 'Atoms/IconButton',
component: IconButton,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
color: {
control: 'select',
options: ['default', 'primary', 'secondary', 'error', 'warning', 'info', 'success'],
},
size: {
control: 'select',
options: ['small', 'medium', 'large'],
},
},
} satisfies Meta<typeof IconButton>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Edit: Story = {
args: {
icon: 'Edit',
color: 'primary',
},
};
export const Delete: Story = {
args: {
icon: 'Delete',
color: 'error',
},
};
export const Add: Story = {
args: {
icon: 'Add',
color: 'primary',
},
};
export const Settings: Story = {
args: {
icon: 'Settings',
size: 'large',
},
};

View File

@@ -0,0 +1,27 @@
'use client';
import { IconButton as MuiIconButton, IconButtonProps as MuiIconButtonProps } from '@mui/material';
import * as Icons from '@mui/icons-material';
export type IconButtonProps = Omit<MuiIconButtonProps, 'children'> & {
icon: keyof typeof Icons;
};
/**
* Atomic IconButton component
* Wraps Material-UI IconButton with icon name from features.json
*/
export default function IconButton({ icon, ...props }: IconButtonProps) {
const IconComponent = Icons[icon];
if (!IconComponent) {
console.warn(`Icon "${icon}" not found in Material Icons`);
return null;
}
return (
<MuiIconButton {...props}>
<IconComponent />
</MuiIconButton>
);
}

View File

@@ -0,0 +1,64 @@
import type { Meta, StoryObj } from '@storybook/react';
import TextField from './TextField';
const meta = {
title: 'Atoms/TextField',
component: TextField,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['standard', 'outlined', 'filled'],
},
type: {
control: 'select',
options: ['text', 'email', 'password', 'number', 'tel', 'url'],
},
size: {
control: 'select',
options: ['small', 'medium'],
},
},
} satisfies Meta<typeof TextField>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
label: 'Text Field',
placeholder: 'Enter text...',
variant: 'outlined',
},
};
export const Email: Story = {
args: {
label: 'Email',
type: 'email',
placeholder: 'user@example.com',
variant: 'outlined',
},
};
export const WithError: Story = {
args: {
label: 'Name',
error: true,
helperText: 'This field is required',
variant: 'outlined',
},
};
export const Multiline: Story = {
args: {
label: 'Description',
multiline: true,
rows: 4,
placeholder: 'Enter description...',
variant: 'outlined',
},
};

View File

@@ -0,0 +1,15 @@
'use client';
import { TextField as MuiTextField, TextFieldProps as MuiTextFieldProps } from '@mui/material';
export type TextFieldProps = MuiTextFieldProps & {
// Additional props from features.json
};
/**
* Atomic TextField component
* Wraps Material-UI TextField with features.json configuration
*/
export default function TextField(props: TextFieldProps) {
return <MuiTextField {...props} />;
}

View File

@@ -0,0 +1,53 @@
import type { Meta, StoryObj } from '@storybook/react';
import Typography from './Typography';
const meta = {
title: 'Atoms/Typography',
component: Typography,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'subtitle1', 'subtitle2', 'body1', 'body2', 'caption', 'button', 'overline'],
},
align: {
control: 'select',
options: ['left', 'center', 'right', 'justify'],
},
},
} satisfies Meta<typeof Typography>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Heading1: Story = {
args: {
variant: 'h1',
text: 'Heading 1',
},
};
export const Heading4: Story = {
args: {
variant: 'h4',
text: 'Heading 4',
},
};
export const Body: Story = {
args: {
variant: 'body1',
text: 'This is body text with regular weight and size.',
},
};
export const Caption: Story = {
args: {
variant: 'caption',
color: 'text.secondary',
text: 'Caption text - smaller and secondary color',
},
};

View File

@@ -0,0 +1,16 @@
'use client';
import { Typography as MuiTypography, TypographyProps as MuiTypographyProps } from '@mui/material';
export type TypographyProps = Omit<MuiTypographyProps, 'children'> & {
text?: string;
children?: React.ReactNode;
};
/**
* Atomic Typography component
* Wraps Material-UI Typography with text prop from features.json
*/
export default function Typography({ text, children, ...props }: TypographyProps) {
return <MuiTypography {...props}>{text || children}</MuiTypography>;
}

View File

@@ -0,0 +1,47 @@
// Atomic component library - exported from features.json componentProps
export { default as Button } from './Button';
export { default as TextField } from './TextField';
export { default as Typography } from './Typography';
export { default as IconButton } from './IconButton';
export { default as Icon } from './Icon';
// Re-export commonly used Material-UI components for consistency
export {
Box,
Grid,
Paper,
Card,
CardContent,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Select,
MenuItem,
FormControl,
InputLabel,
Checkbox,
FormControlLabel,
AppBar,
Toolbar,
Drawer,
Alert,
CircularProgress,
Pagination,
Tabs,
Tab,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
List,
ListItem,
ListItemText,
ListItemIcon,
Accordion,
AccordionSummary,
AccordionDetails,
Chip,
} from '@mui/material';