Initial commit: FA Design System source files
Copy of the Funeral Arranger design system components, theme, tokens, and Storybook config from the original Parsons project. Pre-upgrade baseline with React 18, MUI v5, Storybook 8. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
179
src/components/atoms/Switch/Switch.stories.tsx
Normal file
179
src/components/atoms/Switch/Switch.stories.tsx
Normal file
@@ -0,0 +1,179 @@
|
||||
import { useState } from 'react';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { Switch } from './Switch';
|
||||
import { Typography } from '../Typography';
|
||||
import { Card } from '../Card';
|
||||
import Box from '@mui/material/Box';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import FormGroup from '@mui/material/FormGroup';
|
||||
|
||||
const meta: Meta<typeof Switch> = {
|
||||
title: 'Atoms/Switch',
|
||||
component: Switch,
|
||||
tags: ['autodocs'],
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
design: {
|
||||
type: 'figma',
|
||||
url: 'https://www.figma.com/design/XUDUrw4yMkEexBCCYHXUvT/Parsons?node-id=2322-42538',
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
checked: {
|
||||
control: 'boolean',
|
||||
description: 'Whether the switch is on',
|
||||
},
|
||||
disabled: {
|
||||
control: 'boolean',
|
||||
description: 'Disable the switch',
|
||||
table: { defaultValue: { summary: 'false' } },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Switch>;
|
||||
|
||||
// ─── Default ────────────────────────────────────────────────────────────────
|
||||
|
||||
/** Default switch — unchecked */
|
||||
export const Default: Story = {
|
||||
args: {},
|
||||
};
|
||||
|
||||
// ─── States ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/** All visual states */
|
||||
export const States: Story = {
|
||||
render: () => (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
<FormControlLabel control={<Switch />} label="Unchecked" />
|
||||
<FormControlLabel control={<Switch defaultChecked />} label="Checked" />
|
||||
<FormControlLabel control={<Switch disabled />} label="Disabled unchecked" />
|
||||
<FormControlLabel control={<Switch disabled defaultChecked />} label="Disabled checked" />
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
|
||||
// ─── Interactive: Service Add-ons ───────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Realistic arrangement form pattern — toggle add-on services.
|
||||
*/
|
||||
export const ServiceAddOns: Story = {
|
||||
name: 'Interactive — Service Add-ons',
|
||||
render: () => {
|
||||
const AddOnDemo = () => {
|
||||
const [addOns, setAddOns] = useState({
|
||||
catering: true,
|
||||
flowers: true,
|
||||
music: false,
|
||||
memorial: false,
|
||||
guestBook: false,
|
||||
});
|
||||
|
||||
const toggle = (key: keyof typeof addOns) => {
|
||||
setAddOns((prev) => ({ ...prev, [key]: !prev[key] }));
|
||||
};
|
||||
|
||||
const items = [
|
||||
{
|
||||
key: 'catering' as const,
|
||||
label: 'Catering',
|
||||
desc: 'Light refreshments after the service',
|
||||
price: '$450',
|
||||
},
|
||||
{
|
||||
key: 'flowers' as const,
|
||||
label: 'Floral arrangements',
|
||||
desc: 'Seasonal flowers for the chapel',
|
||||
price: '$280',
|
||||
},
|
||||
{
|
||||
key: 'music' as const,
|
||||
label: 'Live music',
|
||||
desc: 'Organist or solo musician',
|
||||
price: '$350',
|
||||
},
|
||||
{
|
||||
key: 'memorial' as const,
|
||||
label: 'Memorial video',
|
||||
desc: 'Photo slideshow with music',
|
||||
price: '$200',
|
||||
},
|
||||
{
|
||||
key: 'guestBook' as const,
|
||||
label: 'Guest book',
|
||||
desc: 'Leather-bound memorial guest book',
|
||||
price: '$85',
|
||||
},
|
||||
];
|
||||
|
||||
const total = items.reduce(
|
||||
(sum, item) => (addOns[item.key] ? sum + parseInt(item.price.replace('$', ''), 10) : sum),
|
||||
0,
|
||||
);
|
||||
|
||||
return (
|
||||
<Box sx={{ maxWidth: 420 }}>
|
||||
<Typography variant="h4" sx={{ mb: 2 }}>
|
||||
Service add-ons
|
||||
</Typography>
|
||||
<FormGroup>
|
||||
{items.map((item) => (
|
||||
<Card key={item.key} variant="outlined" padding="compact" sx={{ mb: 1 }}>
|
||||
<Box
|
||||
sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}
|
||||
>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="label">{item.label}</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{item.desc}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Typography variant="label" color="text.secondary">
|
||||
{item.price}
|
||||
</Typography>
|
||||
<Switch checked={addOns[item.key]} onChange={() => toggle(item.key)} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Card>
|
||||
))}
|
||||
</FormGroup>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
mt: 2,
|
||||
pt: 2,
|
||||
borderTop: 1,
|
||||
borderColor: 'divider',
|
||||
}}
|
||||
>
|
||||
<Typography variant="labelLg">Total add-ons</Typography>
|
||||
<Typography variant="labelLg" color="primary">
|
||||
${total}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
return <AddOnDemo />;
|
||||
},
|
||||
};
|
||||
|
||||
// ─── With Labels ────────────────────────────────────────────────────────────
|
||||
|
||||
/** FormControlLabel pairing — the recommended usage pattern */
|
||||
export const WithLabels: Story = {
|
||||
name: 'With Labels',
|
||||
render: () => (
|
||||
<FormGroup>
|
||||
<FormControlLabel control={<Switch defaultChecked />} label="Email notifications" />
|
||||
<FormControlLabel control={<Switch />} label="SMS notifications" />
|
||||
<FormControlLabel control={<Switch defaultChecked />} label="Save arrangement progress" />
|
||||
</FormGroup>
|
||||
),
|
||||
};
|
||||
Reference in New Issue
Block a user