format: Apply Prettier to existing codebase
Formatting-only changes across all component and story files. No logic or behaviour changes — only whitespace, line breaks, and trailing commas. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -78,12 +78,24 @@ export const AllColoursFilled: Story = {
|
||||
name: 'All Colours — Filled',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<Badge variant="filled" color="default">Default</Badge>
|
||||
<Badge variant="filled" color="brand">Brand</Badge>
|
||||
<Badge variant="filled" color="success">Success</Badge>
|
||||
<Badge variant="filled" color="warning">Warning</Badge>
|
||||
<Badge variant="filled" color="error">Error</Badge>
|
||||
<Badge variant="filled" color="info">Info</Badge>
|
||||
<Badge variant="filled" color="default">
|
||||
Default
|
||||
</Badge>
|
||||
<Badge variant="filled" color="brand">
|
||||
Brand
|
||||
</Badge>
|
||||
<Badge variant="filled" color="success">
|
||||
Success
|
||||
</Badge>
|
||||
<Badge variant="filled" color="warning">
|
||||
Warning
|
||||
</Badge>
|
||||
<Badge variant="filled" color="error">
|
||||
Error
|
||||
</Badge>
|
||||
<Badge variant="filled" color="info">
|
||||
Info
|
||||
</Badge>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -95,11 +107,21 @@ export const WithIcons: Story = {
|
||||
name: 'With Icons',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<Badge color="brand" icon={<StarIcon />}>Popular</Badge>
|
||||
<Badge color="success" icon={<CheckCircleIcon />}>Verified</Badge>
|
||||
<Badge color="warning" icon={<WarningAmberIcon />}>Limited</Badge>
|
||||
<Badge color="error" icon={<ErrorOutlineIcon />}>Sold out</Badge>
|
||||
<Badge color="info" icon={<InfoOutlinedIcon />}>New</Badge>
|
||||
<Badge color="brand" icon={<StarIcon />}>
|
||||
Popular
|
||||
</Badge>
|
||||
<Badge color="success" icon={<CheckCircleIcon />}>
|
||||
Verified
|
||||
</Badge>
|
||||
<Badge color="warning" icon={<WarningAmberIcon />}>
|
||||
Limited
|
||||
</Badge>
|
||||
<Badge color="error" icon={<ErrorOutlineIcon />}>
|
||||
Sold out
|
||||
</Badge>
|
||||
<Badge color="info" icon={<InfoOutlinedIcon />}>
|
||||
New
|
||||
</Badge>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -109,11 +131,21 @@ export const WithIconsFilled: Story = {
|
||||
name: 'With Icons — Filled',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<Badge variant="filled" color="brand" icon={<StarIcon />}>Popular</Badge>
|
||||
<Badge variant="filled" color="success" icon={<CheckCircleIcon />}>Included</Badge>
|
||||
<Badge variant="filled" color="warning" icon={<WarningAmberIcon />}>Attention</Badge>
|
||||
<Badge variant="filled" color="error" icon={<ErrorOutlineIcon />}>Unavailable</Badge>
|
||||
<Badge variant="filled" color="info" icon={<InfoOutlinedIcon />}>Updated</Badge>
|
||||
<Badge variant="filled" color="brand" icon={<StarIcon />}>
|
||||
Popular
|
||||
</Badge>
|
||||
<Badge variant="filled" color="success" icon={<CheckCircleIcon />}>
|
||||
Included
|
||||
</Badge>
|
||||
<Badge variant="filled" color="warning" icon={<WarningAmberIcon />}>
|
||||
Attention
|
||||
</Badge>
|
||||
<Badge variant="filled" color="error" icon={<ErrorOutlineIcon />}>
|
||||
Unavailable
|
||||
</Badge>
|
||||
<Badge variant="filled" color="info" icon={<InfoOutlinedIcon />}>
|
||||
Updated
|
||||
</Badge>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -124,9 +156,15 @@ export const WithIconsFilled: Story = {
|
||||
export const Sizes: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
<Badge size="small" color="brand" icon={<StarIcon />}>Small</Badge>
|
||||
<Badge size="medium" color="brand" icon={<StarIcon />}>Medium</Badge>
|
||||
<Badge size="large" color="brand" icon={<StarIcon />}>Large</Badge>
|
||||
<Badge size="small" color="brand" icon={<StarIcon />}>
|
||||
Small
|
||||
</Badge>
|
||||
<Badge size="medium" color="brand" icon={<StarIcon />}>
|
||||
Medium
|
||||
</Badge>
|
||||
<Badge size="large" color="brand" icon={<StarIcon />}>
|
||||
Large
|
||||
</Badge>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -146,7 +184,9 @@ export const InPriceCard: Story = {
|
||||
<Typography variant="overline" color="text.secondary">
|
||||
Essential
|
||||
</Typography>
|
||||
<Badge size="small" color="default">Standard</Badge>
|
||||
<Badge size="small" color="default">
|
||||
Standard
|
||||
</Badge>
|
||||
</Box>
|
||||
<Typography variant="display3" color="primary" sx={{ mb: 1 }}>
|
||||
$3,200
|
||||
@@ -162,7 +202,9 @@ export const InPriceCard: Story = {
|
||||
<Typography variant="overline" color="text.secondary">
|
||||
Premium
|
||||
</Typography>
|
||||
<Badge color="brand" icon={<StarIcon />}>Most popular</Badge>
|
||||
<Badge color="brand" icon={<StarIcon />}>
|
||||
Most popular
|
||||
</Badge>
|
||||
</Box>
|
||||
<Typography variant="display3" color="primary" sx={{ mb: 1 }}>
|
||||
$5,800
|
||||
@@ -178,7 +220,9 @@ export const InPriceCard: Story = {
|
||||
<Typography variant="overline" color="text.secondary">
|
||||
Bespoke
|
||||
</Typography>
|
||||
<Badge color="info" icon={<LocalOfferIcon />}>Best value</Badge>
|
||||
<Badge color="info" icon={<LocalOfferIcon />}>
|
||||
Best value
|
||||
</Badge>
|
||||
</Box>
|
||||
<Typography variant="display3" color="primary" sx={{ mb: 1 }}>
|
||||
$8,500
|
||||
@@ -202,11 +246,46 @@ export const ServiceStatus: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, maxWidth: 500 }}>
|
||||
{[
|
||||
{ service: 'Chapel ceremony', badge: <Badge color="success" icon={<CheckCircleIcon />}>Confirmed</Badge> },
|
||||
{ service: 'Floral arrangements', badge: <Badge color="warning" icon={<WarningAmberIcon />}>Pending</Badge> },
|
||||
{ service: 'Catering', badge: <Badge color="error" icon={<ErrorOutlineIcon />}>Unavailable</Badge> },
|
||||
{ service: 'Memorial printing', badge: <Badge color="info" icon={<NewReleasesIcon />}>New option</Badge> },
|
||||
{ service: 'Premium casket', badge: <Badge variant="filled" color="brand" icon={<VerifiedIcon />}>Included</Badge> },
|
||||
{
|
||||
service: 'Chapel ceremony',
|
||||
badge: (
|
||||
<Badge color="success" icon={<CheckCircleIcon />}>
|
||||
Confirmed
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
service: 'Floral arrangements',
|
||||
badge: (
|
||||
<Badge color="warning" icon={<WarningAmberIcon />}>
|
||||
Pending
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
service: 'Catering',
|
||||
badge: (
|
||||
<Badge color="error" icon={<ErrorOutlineIcon />}>
|
||||
Unavailable
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
service: 'Memorial printing',
|
||||
badge: (
|
||||
<Badge color="info" icon={<NewReleasesIcon />}>
|
||||
New option
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
service: 'Premium casket',
|
||||
badge: (
|
||||
<Badge variant="filled" color="brand" icon={<VerifiedIcon />}>
|
||||
Included
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
].map((item) => (
|
||||
<Card key={item.service} variant="outlined" padding="compact">
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
@@ -231,7 +310,14 @@ export const CompleteMatrix: Story = {
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||||
{(['soft', 'filled'] as const).map((variant) => (
|
||||
<div key={variant}>
|
||||
<div style={{ marginBottom: 8, fontWeight: 600, fontSize: 14, textTransform: 'capitalize' }}>
|
||||
<div
|
||||
style={{
|
||||
marginBottom: 8,
|
||||
fontWeight: 600,
|
||||
fontSize: 14,
|
||||
textTransform: 'capitalize',
|
||||
}}
|
||||
>
|
||||
{variant}
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
@@ -239,7 +325,13 @@ export const CompleteMatrix: Story = {
|
||||
<div key={size} style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
||||
<span style={{ width: 60, fontSize: 12, color: '#737373' }}>{size}</span>
|
||||
{colors.map((color) => (
|
||||
<Badge key={color} variant={variant} color={color} size={size} icon={<StarIcon />}>
|
||||
<Badge
|
||||
key={color}
|
||||
variant={variant}
|
||||
color={color}
|
||||
size={size}
|
||||
icon={<StarIcon />}
|
||||
>
|
||||
{color}
|
||||
</Badge>
|
||||
))}
|
||||
|
||||
@@ -87,7 +87,9 @@ export const FigmaMapping: Story = {
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
<Button variant="contained">Primary</Button>
|
||||
<Button variant="soft">Sec / Brand</Button>
|
||||
<Button variant="soft" color="secondary">Sec / Grey</Button>
|
||||
<Button variant="soft" color="secondary">
|
||||
Sec / Grey
|
||||
</Button>
|
||||
<Button variant="text">Ghost</Button>
|
||||
</div>
|
||||
),
|
||||
@@ -113,10 +115,18 @@ export const VariantsSecondary: Story = {
|
||||
name: 'Variants — Secondary',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
<Button variant="contained" color="secondary">Contained</Button>
|
||||
<Button variant="soft" color="secondary">Soft</Button>
|
||||
<Button variant="outlined" color="secondary">Outlined</Button>
|
||||
<Button variant="text" color="secondary">Text</Button>
|
||||
<Button variant="contained" color="secondary">
|
||||
Contained
|
||||
</Button>
|
||||
<Button variant="soft" color="secondary">
|
||||
Soft
|
||||
</Button>
|
||||
<Button variant="outlined" color="secondary">
|
||||
Outlined
|
||||
</Button>
|
||||
<Button variant="text" color="secondary">
|
||||
Text
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -140,10 +150,18 @@ export const AllSizesSoft: Story = {
|
||||
name: 'All Sizes — Soft',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
<Button variant="soft" size="xs">Extra small</Button>
|
||||
<Button variant="soft" size="small">Small</Button>
|
||||
<Button variant="soft" size="medium">Medium</Button>
|
||||
<Button variant="soft" size="large">Large</Button>
|
||||
<Button variant="soft" size="xs">
|
||||
Extra small
|
||||
</Button>
|
||||
<Button variant="soft" size="small">
|
||||
Small
|
||||
</Button>
|
||||
<Button variant="soft" size="medium">
|
||||
Medium
|
||||
</Button>
|
||||
<Button variant="soft" size="large">
|
||||
Large
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -180,10 +198,18 @@ export const IconsAllSizes: Story = {
|
||||
name: 'Icons — All Sizes',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
<Button size="xs" startIcon={<AddIcon />}>Add</Button>
|
||||
<Button size="small" startIcon={<AddIcon />}>Add</Button>
|
||||
<Button size="medium" startIcon={<AddIcon />}>Add</Button>
|
||||
<Button size="large" startIcon={<AddIcon />}>Add</Button>
|
||||
<Button size="xs" startIcon={<AddIcon />}>
|
||||
Add
|
||||
</Button>
|
||||
<Button size="small" startIcon={<AddIcon />}>
|
||||
Add
|
||||
</Button>
|
||||
<Button size="medium" startIcon={<AddIcon />}>
|
||||
Add
|
||||
</Button>
|
||||
<Button size="large" startIcon={<AddIcon />}>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -204,9 +230,15 @@ export const DisabledAllVariants: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
<Button disabled>Contained</Button>
|
||||
<Button disabled variant="soft">Soft</Button>
|
||||
<Button disabled variant="outlined">Outlined</Button>
|
||||
<Button disabled variant="text">Text</Button>
|
||||
<Button disabled variant="soft">
|
||||
Soft
|
||||
</Button>
|
||||
<Button disabled variant="outlined">
|
||||
Outlined
|
||||
</Button>
|
||||
<Button disabled variant="text">
|
||||
Text
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -225,9 +257,15 @@ export const LoadingAllVariants: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
<Button loading>Submitting...</Button>
|
||||
<Button loading variant="soft">Processing...</Button>
|
||||
<Button loading variant="outlined">Processing...</Button>
|
||||
<Button loading variant="text">Loading...</Button>
|
||||
<Button loading variant="soft">
|
||||
Processing...
|
||||
</Button>
|
||||
<Button loading variant="outlined">
|
||||
Processing...
|
||||
</Button>
|
||||
<Button loading variant="text">
|
||||
Loading...
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -293,9 +331,15 @@ export const TextButtonComparison: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 24, alignItems: 'center' }}>
|
||||
<Button variant="text">No underline</Button>
|
||||
<Button variant="text" underline>With underline</Button>
|
||||
<Button variant="text" color="secondary">Secondary</Button>
|
||||
<Button variant="text" color="secondary" underline>Secondary underlined</Button>
|
||||
<Button variant="text" underline>
|
||||
With underline
|
||||
</Button>
|
||||
<Button variant="text" color="secondary">
|
||||
Secondary
|
||||
</Button>
|
||||
<Button variant="text" color="secondary" underline>
|
||||
Secondary underlined
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -305,10 +349,18 @@ export const TextButtonSizes: Story = {
|
||||
name: 'Text Buttons — All Sizes',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
<Button variant="text" size="xs">Extra small</Button>
|
||||
<Button variant="text" size="small">Small</Button>
|
||||
<Button variant="text" size="medium">Medium</Button>
|
||||
<Button variant="text" size="large">Large</Button>
|
||||
<Button variant="text" size="xs">
|
||||
Extra small
|
||||
</Button>
|
||||
<Button variant="text" size="small">
|
||||
Small
|
||||
</Button>
|
||||
<Button variant="text" size="medium">
|
||||
Medium
|
||||
</Button>
|
||||
<Button variant="text" size="large">
|
||||
Large
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -348,15 +400,27 @@ export const CompleteMatrix: Story = {
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||||
{(['contained', 'soft', 'outlined', 'text'] as const).map((variant) => (
|
||||
<div key={variant}>
|
||||
<div style={{ marginBottom: 8, fontWeight: 600, fontSize: 14, textTransform: 'capitalize' }}>
|
||||
<div
|
||||
style={{ marginBottom: 8, fontWeight: 600, fontSize: 14, textTransform: 'capitalize' }}
|
||||
>
|
||||
{variant}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 12, alignItems: 'center', flexWrap: 'wrap' }}>
|
||||
<Button variant={variant} color="primary">Primary</Button>
|
||||
<Button variant={variant} color="secondary">Secondary</Button>
|
||||
<Button variant={variant} color="primary" startIcon={<AddIcon />}>With icon</Button>
|
||||
<Button variant={variant} color="primary" disabled>Disabled</Button>
|
||||
<Button variant={variant} color="primary" loading>Loading...</Button>
|
||||
<Button variant={variant} color="primary">
|
||||
Primary
|
||||
</Button>
|
||||
<Button variant={variant} color="secondary">
|
||||
Secondary
|
||||
</Button>
|
||||
<Button variant={variant} color="primary" startIcon={<AddIcon />}>
|
||||
With icon
|
||||
</Button>
|
||||
<Button variant={variant} color="primary" disabled>
|
||||
Disabled
|
||||
</Button>
|
||||
<Button variant={variant} color="primary" loading>
|
||||
Loading...
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -69,13 +69,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
{children}
|
||||
{loading && (
|
||||
<>
|
||||
<CircularProgress
|
||||
size={16}
|
||||
color="inherit"
|
||||
thickness={3}
|
||||
aria-hidden
|
||||
sx={{ ml: 1 }}
|
||||
/>
|
||||
<CircularProgress size={16} color="inherit" thickness={3} aria-hidden sx={{ ml: 1 }} />
|
||||
<span
|
||||
style={{
|
||||
position: 'absolute',
|
||||
|
||||
@@ -52,8 +52,8 @@ export const Default: Story = {
|
||||
Funeral package
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.secondary">
|
||||
A comprehensive service including chapel ceremony, transport, and
|
||||
preparation. Suitable for families seeking a traditional farewell.
|
||||
A comprehensive service including chapel ceremony, transport, and preparation. Suitable
|
||||
for families seeking a traditional farewell.
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
@@ -99,12 +99,7 @@ export const Variants: Story = {
|
||||
export const Interactive: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', gap: 24, maxWidth: 800 }}>
|
||||
<Card
|
||||
interactive
|
||||
sx={{ flex: 1 }}
|
||||
tabIndex={0}
|
||||
onClick={() => alert('Card clicked')}
|
||||
>
|
||||
<Card interactive sx={{ flex: 1 }} tabIndex={0} onClick={() => alert('Card clicked')}>
|
||||
<Typography variant="h5" gutterBottom>
|
||||
Elevated + Interactive
|
||||
</Typography>
|
||||
@@ -305,11 +300,15 @@ export const OnDifferentBackgrounds: Story = {
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
<Card variant="elevated">
|
||||
<Typography variant="labelLg">Elevated</Typography>
|
||||
<Typography variant="body2" color="text.secondary">Shadow defines edges</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Shadow defines edges
|
||||
</Typography>
|
||||
</Card>
|
||||
<Card variant="outlined">
|
||||
<Typography variant="labelLg">Outlined</Typography>
|
||||
<Typography variant="body2" color="text.secondary">Border defines edges</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Border defines edges
|
||||
</Typography>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
@@ -328,11 +327,15 @@ export const OnDifferentBackgrounds: Story = {
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
<Card variant="elevated">
|
||||
<Typography variant="labelLg">Elevated</Typography>
|
||||
<Typography variant="body2" color="text.secondary">White card + shadow on grey</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
White card + shadow on grey
|
||||
</Typography>
|
||||
</Card>
|
||||
<Card variant="outlined">
|
||||
<Typography variant="labelLg">Outlined</Typography>
|
||||
<Typography variant="body2" color="text.secondary">Contrast + border on grey</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Contrast + border on grey
|
||||
</Typography>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
@@ -395,8 +398,8 @@ export const PriceCardPreview: Story = {
|
||||
$3,200
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
|
||||
A respectful and simple service with chapel ceremony, transport, and
|
||||
professional preparation.
|
||||
A respectful and simple service with chapel ceremony, transport, and professional
|
||||
preparation.
|
||||
</Typography>
|
||||
<Button fullWidth size="large">
|
||||
Select this package
|
||||
@@ -432,8 +435,8 @@ export const WithImage: Story = {
|
||||
Parsons Chapel
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Our heritage-listed chapel seats up to 120 guests and features
|
||||
modern audio-visual facilities.
|
||||
Our heritage-listed chapel seats up to 120 guests and features modern audio-visual
|
||||
facilities.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Card>
|
||||
|
||||
@@ -58,9 +58,7 @@ export const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
||||
const muiVariant = variant === 'outlined' ? 'outlined' : undefined;
|
||||
|
||||
// Interactive cards need keyboard operability
|
||||
const interactiveProps = interactive
|
||||
? { tabIndex: 0 as const, role: 'button' as const }
|
||||
: {};
|
||||
const interactiveProps = interactive ? { tabIndex: 0 as const, role: 'button' as const } : {};
|
||||
|
||||
return (
|
||||
<MuiCard
|
||||
@@ -88,8 +86,7 @@ export const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
||||
// Focus-visible for keyboard accessibility on interactive cards
|
||||
interactive && {
|
||||
'&:focus-visible': {
|
||||
outline: (theme: Theme) =>
|
||||
`2px solid ${theme.palette.primary.main}`,
|
||||
outline: (theme: Theme) => `2px solid ${theme.palette.primary.main}`,
|
||||
outlineOffset: '2px',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -117,7 +117,12 @@ export const Clickable: Story = {
|
||||
<Chip label="Clickable default" onClick={() => {}} />
|
||||
<Chip label="Clickable primary" color="primary" onClick={() => {}} />
|
||||
<Chip label="Clickable outlined" variant="outlined" onClick={() => {}} />
|
||||
<Chip label="Clickable outlined primary" variant="outlined" color="primary" onClick={() => {}} />
|
||||
<Chip
|
||||
label="Clickable outlined primary"
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
onClick={() => {}}
|
||||
/>
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
@@ -143,14 +148,18 @@ export const Selected: Story = {
|
||||
render: () => (
|
||||
<Box sx={{ display: 'flex', gap: 2, flexDirection: 'column' }}>
|
||||
<Box>
|
||||
<Typography variant="label" sx={{ mb: 1 }}>Filled</Typography>
|
||||
<Typography variant="label" sx={{ mb: 1 }}>
|
||||
Filled
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 1.5 }}>
|
||||
<Chip label="Not selected" onClick={() => {}} />
|
||||
<Chip label="Selected" selected onClick={() => {}} />
|
||||
</Box>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="label" sx={{ mb: 1 }}>Outlined</Typography>
|
||||
<Typography variant="label" sx={{ mb: 1 }}>
|
||||
Outlined
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 1.5 }}>
|
||||
<Chip variant="outlined" label="Not selected" onClick={() => {}} />
|
||||
<Chip variant="outlined" label="Selected" selected onClick={() => {}} />
|
||||
@@ -253,12 +262,7 @@ export const RemovableTags: Story = {
|
||||
</Typography>
|
||||
) : (
|
||||
tags.map((tag) => (
|
||||
<Chip
|
||||
key={tag}
|
||||
label={tag}
|
||||
color="primary"
|
||||
onDelete={() => remove(tag)}
|
||||
/>
|
||||
<Chip key={tag} label={tag} color="primary" onDelete={() => remove(tag)} />
|
||||
))
|
||||
)}
|
||||
</Box>
|
||||
@@ -283,7 +287,9 @@ export const InServiceOption: Story = {
|
||||
<Card interactive>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start', mb: 1 }}>
|
||||
<Typography variant="h5">Chapel Ceremony</Typography>
|
||||
<Typography variant="display3" color="primary">$1,200</Typography>
|
||||
<Typography variant="display3" color="primary">
|
||||
$1,200
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||
Traditional chapel service with celebrant and music of your choosing.
|
||||
@@ -298,7 +304,9 @@ export const InServiceOption: Story = {
|
||||
<Card interactive>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start', mb: 1 }}>
|
||||
<Typography variant="h5">Graveside Service</Typography>
|
||||
<Typography variant="display3" color="primary">$900</Typography>
|
||||
<Typography variant="display3" color="primary">
|
||||
$900
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
||||
Intimate outdoor farewell at the burial site.
|
||||
@@ -337,8 +345,20 @@ export const CompleteMatrix: Story = {
|
||||
{colors.map((color) => (
|
||||
<React.Fragment key={color}>
|
||||
<Chip variant={variant} color={color} size={size} label={color} />
|
||||
<Chip variant={variant} color={color} size={size} label={`${color} + icon`} icon={<LocalOfferIcon />} />
|
||||
<Chip variant={variant} color={color} size={size} label={`${color} delete`} onDelete={() => {}} />
|
||||
<Chip
|
||||
variant={variant}
|
||||
color={color}
|
||||
size={size}
|
||||
label={`${color} + icon`}
|
||||
icon={<LocalOfferIcon />}
|
||||
/>
|
||||
<Chip
|
||||
variant={variant}
|
||||
color={color}
|
||||
size={size}
|
||||
label={`${color} delete`}
|
||||
onDelete={() => {}}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Box>
|
||||
@@ -348,7 +368,9 @@ export const CompleteMatrix: Story = {
|
||||
))}
|
||||
|
||||
<Box>
|
||||
<Typography variant="label" sx={{ mb: 1 }}>Selected state</Typography>
|
||||
<Typography variant="label" sx={{ mb: 1 }}>
|
||||
Selected state
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
||||
<Chip selected label="Filled selected" onClick={() => {}} />
|
||||
<Chip selected variant="outlined" label="Outlined selected" onClick={() => {}} />
|
||||
|
||||
@@ -44,16 +44,7 @@ export interface ChipProps extends MuiChipProps {
|
||||
* is being removed.
|
||||
*/
|
||||
export const Chip = React.forwardRef<HTMLDivElement, ChipProps>(
|
||||
(
|
||||
{
|
||||
selected = false,
|
||||
variant = 'filled',
|
||||
color,
|
||||
sx,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
({ selected = false, variant = 'filled', color, sx, ...props }, ref) => {
|
||||
// When selected, promote to primary colour unless explicitly set
|
||||
const resolvedColor = color ?? (selected ? 'primary' : 'default');
|
||||
|
||||
@@ -63,7 +54,8 @@ export const Chip = React.forwardRef<HTMLDivElement, ChipProps>(
|
||||
variant={variant}
|
||||
color={resolvedColor}
|
||||
sx={[
|
||||
selected && variant === 'outlined' && {
|
||||
selected &&
|
||||
variant === 'outlined' && {
|
||||
borderWidth: 2,
|
||||
borderColor: 'var(--fa-color-brand-500)',
|
||||
backgroundColor: 'var(--fa-color-brand-50)',
|
||||
|
||||
@@ -19,7 +19,13 @@ type Story = StoryObj<typeof Divider>;
|
||||
// ─── Default ────────────────────────────────────────────────────────────────
|
||||
|
||||
export const Default: Story = {
|
||||
decorators: [(Story) => <Box sx={{ width: 400 }}><Story /></Box>],
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<Box sx={{ width: 400 }}>
|
||||
<Story />
|
||||
</Box>
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
// ─── Variants ───────────────────────────────────────────────────────────────
|
||||
@@ -84,9 +90,7 @@ export const InContent: Story = {
|
||||
</Box>
|
||||
<Divider />
|
||||
<Box sx={{ fontWeight: 600, mt: 2, mb: 1 }}>Venue</Box>
|
||||
<Box sx={{ fontSize: 14, color: 'text.secondary', mb: 2 }}>
|
||||
West Chapel, Strathfield
|
||||
</Box>
|
||||
<Box sx={{ fontSize: 14, color: 'text.secondary', mb: 2 }}>West Chapel, Strathfield</Box>
|
||||
<Divider />
|
||||
<Box sx={{ fontWeight: 600, mt: 2, mb: 1 }}>Total</Box>
|
||||
<Box sx={{ fontSize: 14, color: 'text.primary' }}>$2,400</Box>
|
||||
|
||||
@@ -116,7 +116,17 @@ export const CommonUseCases: Story = {
|
||||
{/* Card actions toolbar */}
|
||||
<Box>
|
||||
<Box sx={{ fontSize: 12, color: 'text.secondary', mb: 1 }}>Card action toolbar</Box>
|
||||
<Box sx={{ display: 'flex', gap: 1, p: 1, border: '1px solid', borderColor: 'divider', borderRadius: 1, width: 'fit-content' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
gap: 1,
|
||||
p: 1,
|
||||
border: '1px solid',
|
||||
borderColor: 'divider',
|
||||
borderRadius: 1,
|
||||
width: 'fit-content',
|
||||
}}
|
||||
>
|
||||
<IconButton size="small" color="primary" aria-label="Favourite">
|
||||
<FavoriteBorderIcon />
|
||||
</IconButton>
|
||||
@@ -132,7 +142,18 @@ export const CommonUseCases: Story = {
|
||||
{/* Dialog close */}
|
||||
<Box>
|
||||
<Box sx={{ fontSize: 12, color: 'text.secondary', mb: 1 }}>Dialog close button</Box>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', p: 2, border: '1px solid', borderColor: 'divider', borderRadius: 1, width: 300 }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
p: 2,
|
||||
border: '1px solid',
|
||||
borderColor: 'divider',
|
||||
borderRadius: 1,
|
||||
width: 300,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ fontWeight: 600 }}>Confirm Selection</Box>
|
||||
<IconButton size="small" aria-label="Close dialog">
|
||||
<CloseIcon />
|
||||
@@ -143,7 +164,17 @@ export const CommonUseCases: Story = {
|
||||
{/* Navigation header */}
|
||||
<Box>
|
||||
<Box sx={{ fontSize: 12, color: 'text.secondary', mb: 1 }}>Mobile navigation toggle</Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, p: 1, backgroundColor: 'var(--fa-color-brand-50)', borderRadius: 1, width: 'fit-content' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 1,
|
||||
p: 1,
|
||||
backgroundColor: 'var(--fa-color-brand-50)',
|
||||
borderRadius: 1,
|
||||
width: 'fit-content',
|
||||
}}
|
||||
>
|
||||
<IconButton size="large" aria-label="Open menu">
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
|
||||
@@ -122,10 +122,7 @@ export const FigmaMapping: Story = {
|
||||
helperText="Input Label - Description"
|
||||
endIcon={<SearchIcon />}
|
||||
/>
|
||||
<Input
|
||||
placeholder="Select an option"
|
||||
endIcon={<SearchIcon />}
|
||||
/>
|
||||
<Input placeholder="Select an option" endIcon={<SearchIcon />} />
|
||||
<Input placeholder="Select an option" />
|
||||
</div>
|
||||
),
|
||||
@@ -222,20 +219,16 @@ export const SizeAlignment: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||||
<div style={{ display: 'flex', gap: 8, alignItems: 'flex-end' }}>
|
||||
<Input
|
||||
placeholder="Search arrangements..."
|
||||
endIcon={<SearchIcon />}
|
||||
size="medium"
|
||||
/>
|
||||
<Button size="large" sx={{ minWidth: 100, minHeight: 48 }}>Search</Button>
|
||||
<Input placeholder="Search arrangements..." endIcon={<SearchIcon />} size="medium" />
|
||||
<Button size="large" sx={{ minWidth: 100, minHeight: 48 }}>
|
||||
Search
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 8, alignItems: 'flex-end' }}>
|
||||
<Input
|
||||
placeholder="Quick search..."
|
||||
endIcon={<SearchIcon />}
|
||||
size="small"
|
||||
/>
|
||||
<Button size="medium" sx={{ minWidth: 100, minHeight: 40 }}>Search</Button>
|
||||
<Input placeholder="Quick search..." endIcon={<SearchIcon />} size="small" />
|
||||
<Button size="medium" sx={{ minWidth: 100, minHeight: 40 }}>
|
||||
Search
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
@@ -248,11 +241,7 @@ export const WithIcons: Story = {
|
||||
name: 'With Icons',
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||||
<Input
|
||||
label="Search"
|
||||
placeholder="Search services..."
|
||||
endIcon={<SearchIcon />}
|
||||
/>
|
||||
<Input label="Search" placeholder="Search services..." endIcon={<SearchIcon />} />
|
||||
<Input
|
||||
label="Email"
|
||||
placeholder="you@example.com"
|
||||
@@ -265,12 +254,7 @@ export const WithIcons: Story = {
|
||||
startIcon={<PhoneOutlinedIcon />}
|
||||
type="tel"
|
||||
/>
|
||||
<Input
|
||||
label="Amount"
|
||||
placeholder="0.00"
|
||||
startIcon={<AttachMoneyIcon />}
|
||||
type="number"
|
||||
/>
|
||||
<Input label="Amount" placeholder="0.00" startIcon={<AttachMoneyIcon />} type="number" />
|
||||
<Input
|
||||
label="Email verified"
|
||||
defaultValue="john@example.com"
|
||||
@@ -355,18 +339,22 @@ export const ValidationFlow: Story = {
|
||||
required
|
||||
startIcon={<EmailOutlinedIcon />}
|
||||
endIcon={
|
||||
showSuccess ? <CheckCircleOutlineIcon sx={{ color: 'success.main' }} /> :
|
||||
showError ? <ErrorOutlineIcon sx={{ color: 'error.main' }} /> :
|
||||
undefined
|
||||
showSuccess ? (
|
||||
<CheckCircleOutlineIcon sx={{ color: 'success.main' }} />
|
||||
) : showError ? (
|
||||
<ErrorOutlineIcon sx={{ color: 'error.main' }} />
|
||||
) : undefined
|
||||
}
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
error={showError}
|
||||
success={showSuccess}
|
||||
helperText={
|
||||
showError ? 'Please enter a valid email address' :
|
||||
showSuccess ? 'Looks good!' :
|
||||
'Required for arrangement confirmation'
|
||||
showError
|
||||
? 'Please enter a valid email address'
|
||||
: showSuccess
|
||||
? 'Looks good!'
|
||||
: 'Required for arrangement confirmation'
|
||||
}
|
||||
/>
|
||||
);
|
||||
@@ -387,9 +375,7 @@ export const ArrangementForm: Story = {
|
||||
],
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
|
||||
<div style={{ fontWeight: 700, fontSize: 20, marginBottom: 4 }}>
|
||||
Contact details
|
||||
</div>
|
||||
<div style={{ fontWeight: 700, fontSize: 20, marginBottom: 4 }}>Contact details</div>
|
||||
<Input
|
||||
label="Full name"
|
||||
placeholder="Enter your full name"
|
||||
@@ -443,7 +429,16 @@ export const CompleteMatrix: Story = {
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 32 }}>
|
||||
{(['medium', 'small'] as const).map((size) => (
|
||||
<div key={size}>
|
||||
<div style={{ marginBottom: 12, fontWeight: 600, fontSize: 14, textTransform: 'uppercase', letterSpacing: 1, color: '#737373' }}>
|
||||
<div
|
||||
style={{
|
||||
marginBottom: 12,
|
||||
fontWeight: 600,
|
||||
fontSize: 14,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 1,
|
||||
color: '#737373',
|
||||
}}
|
||||
>
|
||||
Size: {size} ({size === 'medium' ? '48px' : '40px'})
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
|
||||
|
||||
@@ -77,11 +77,15 @@ export const Input = React.forwardRef<HTMLDivElement, InputProps>(
|
||||
// Prefer convenience icon props; fall back to raw adornment props
|
||||
const resolvedStart = startIcon ? (
|
||||
<InputAdornment position="start">{startIcon}</InputAdornment>
|
||||
) : startAdornment;
|
||||
) : (
|
||||
startAdornment
|
||||
);
|
||||
|
||||
const resolvedEnd = endIcon ? (
|
||||
<InputAdornment position="end">{endIcon}</InputAdornment>
|
||||
) : endAdornment;
|
||||
) : (
|
||||
endAdornment
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
@@ -133,7 +137,8 @@ export const Input = React.forwardRef<HTMLDivElement, InputProps>(
|
||||
aria-describedby={helperId}
|
||||
sx={[
|
||||
// Success border + focus ring (not a native MUI state)
|
||||
success && !error && {
|
||||
success &&
|
||||
!error && {
|
||||
'& .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: 'success.main',
|
||||
},
|
||||
|
||||
@@ -32,15 +32,21 @@ export const UnderlineVariants: Story = {
|
||||
render: () => (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Link href="#" underline="hover">Hover (default)</Link>
|
||||
<Link href="#" underline="hover">
|
||||
Hover (default)
|
||||
</Link>
|
||||
<Box sx={{ fontSize: 11, color: 'text.secondary' }}>underline="hover"</Box>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Link href="#" underline="always">Always underlined</Link>
|
||||
<Link href="#" underline="always">
|
||||
Always underlined
|
||||
</Link>
|
||||
<Box sx={{ fontSize: 11, color: 'text.secondary' }}>underline="always"</Box>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Link href="#" underline="none">No underline</Link>
|
||||
<Link href="#" underline="none">
|
||||
No underline
|
||||
</Link>
|
||||
<Box sx={{ fontSize: 11, color: 'text.secondary' }}>underline="none"</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -58,13 +64,19 @@ export const ColourVariants: Story = {
|
||||
<Link href="#">Brand link (default — copper, 4.8:1 contrast)</Link>
|
||||
</Box>
|
||||
<Box>
|
||||
<Link href="#" color="text.secondary">Secondary link (neutral grey)</Link>
|
||||
<Link href="#" color="text.secondary">
|
||||
Secondary link (neutral grey)
|
||||
</Link>
|
||||
</Box>
|
||||
<Box>
|
||||
<Link href="#" color="text.primary">Primary text link (charcoal)</Link>
|
||||
<Link href="#" color="text.primary">
|
||||
Primary text link (charcoal)
|
||||
</Link>
|
||||
</Box>
|
||||
<Box>
|
||||
<Link href="#" color="error.main">Error link (red — for destructive actions)</Link>
|
||||
<Link href="#" color="error.main">
|
||||
Error link (red — for destructive actions)
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
),
|
||||
@@ -76,12 +88,10 @@ export const ColourVariants: Story = {
|
||||
export const Inline: Story = {
|
||||
render: () => (
|
||||
<Box sx={{ maxWidth: 500, lineHeight: 1.7 }}>
|
||||
If you need help planning a funeral, our{' '}
|
||||
<Link href="#">arrangement guide</Link> walks you through each
|
||||
step. You can also browse our{' '}
|
||||
<Link href="#">provider directory</Link> to find local funeral
|
||||
directors, or <Link href="#">contact us</Link> directly for
|
||||
personalised assistance.
|
||||
If you need help planning a funeral, our <Link href="#">arrangement guide</Link> walks you
|
||||
through each step. You can also browse our <Link href="#">provider directory</Link> to find
|
||||
local funeral directors, or <Link href="#">contact us</Link> directly for personalised
|
||||
assistance.
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
@@ -92,9 +102,15 @@ export const Inline: Story = {
|
||||
export const Navigation: Story = {
|
||||
render: () => (
|
||||
<Box sx={{ display: 'flex', gap: 3 }}>
|
||||
<Link href="#" underline="none" sx={{ fontWeight: 600 }}>FAQ</Link>
|
||||
<Link href="#" underline="none" sx={{ fontWeight: 600 }}>Contact Us</Link>
|
||||
<Link href="#" underline="none" sx={{ fontWeight: 600 }}>Log In</Link>
|
||||
<Link href="#" underline="none" sx={{ fontWeight: 600 }}>
|
||||
FAQ
|
||||
</Link>
|
||||
<Link href="#" underline="none" sx={{ fontWeight: 600 }}>
|
||||
Contact Us
|
||||
</Link>
|
||||
<Link href="#" underline="none" sx={{ fontWeight: 600 }}>
|
||||
Log In
|
||||
</Link>
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
@@ -106,10 +122,18 @@ export const FooterLinks: Story = {
|
||||
name: 'Footer Links',
|
||||
render: () => (
|
||||
<Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap' }}>
|
||||
<Link href="#" color="text.secondary" variant="body2">Privacy Policy</Link>
|
||||
<Link href="#" color="text.secondary" variant="body2">Terms of Service</Link>
|
||||
<Link href="#" color="text.secondary" variant="body2">Accessibility</Link>
|
||||
<Link href="#" color="text.secondary" variant="body2">Cookie Settings</Link>
|
||||
<Link href="#" color="text.secondary" variant="body2">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
<Link href="#" color="text.secondary" variant="body2">
|
||||
Terms of Service
|
||||
</Link>
|
||||
<Link href="#" color="text.secondary" variant="body2">
|
||||
Accessibility
|
||||
</Link>
|
||||
<Link href="#" color="text.secondary" variant="body2">
|
||||
Cookie Settings
|
||||
</Link>
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
@@ -121,7 +145,15 @@ export const OnDifferentBackgrounds: Story = {
|
||||
name: 'On Different Backgrounds',
|
||||
render: () => (
|
||||
<Box sx={{ display: 'flex', gap: 3 }}>
|
||||
<Box sx={{ p: 3, backgroundColor: 'background.default', borderRadius: 1, border: '1px solid', borderColor: 'divider' }}>
|
||||
<Box
|
||||
sx={{
|
||||
p: 3,
|
||||
backgroundColor: 'background.default',
|
||||
borderRadius: 1,
|
||||
border: '1px solid',
|
||||
borderColor: 'divider',
|
||||
}}
|
||||
>
|
||||
<Box sx={{ fontSize: 11, color: 'text.secondary', mb: 1 }}>White</Box>
|
||||
<Link href="#">Learn more</Link>
|
||||
</Box>
|
||||
|
||||
@@ -63,7 +63,9 @@ export const Group: Story = {
|
||||
name: 'Radio Group',
|
||||
render: () => (
|
||||
<FormControl>
|
||||
<Typography variant="label" sx={{ mb: 1 }}>Service type</Typography>
|
||||
<Typography variant="label" sx={{ mb: 1 }}>
|
||||
Service type
|
||||
</Typography>
|
||||
<RadioGroup defaultValue="chapel">
|
||||
<FormControlLabel value="chapel" control={<Radio />} label="Chapel ceremony" />
|
||||
<FormControlLabel value="graveside" control={<Radio />} label="Graveside service" />
|
||||
@@ -88,14 +90,31 @@ export const CardSelection: Story = {
|
||||
const [selected, setSelected] = useState('standard');
|
||||
|
||||
const options = [
|
||||
{ value: 'direct', label: 'Direct cremation', desc: 'Simple, dignified cremation with no service.', price: '$1,800' },
|
||||
{ value: 'standard', label: 'Standard service', desc: 'Traditional chapel ceremony with viewing.', price: '$4,200' },
|
||||
{ value: 'premium', label: 'Premium service', desc: 'Full service with personalised memorial.', price: '$7,500' },
|
||||
{
|
||||
value: 'direct',
|
||||
label: 'Direct cremation',
|
||||
desc: 'Simple, dignified cremation with no service.',
|
||||
price: '$1,800',
|
||||
},
|
||||
{
|
||||
value: 'standard',
|
||||
label: 'Standard service',
|
||||
desc: 'Traditional chapel ceremony with viewing.',
|
||||
price: '$4,200',
|
||||
},
|
||||
{
|
||||
value: 'premium',
|
||||
label: 'Premium service',
|
||||
desc: 'Full service with personalised memorial.',
|
||||
price: '$7,500',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Box sx={{ maxWidth: 420 }}>
|
||||
<Typography variant="h4" sx={{ mb: 2 }}>Choose a package</Typography>
|
||||
<Typography variant="h4" sx={{ mb: 2 }}>
|
||||
Choose a package
|
||||
</Typography>
|
||||
<RadioGroup value={selected} onChange={(e) => setSelected(e.target.value)}>
|
||||
{options.map((opt) => (
|
||||
<Card
|
||||
@@ -110,11 +129,21 @@ export const CardSelection: Story = {
|
||||
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1 }}>
|
||||
<Radio value={opt.value} sx={{ mt: -0.5 }} />
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography variant="label">{opt.label}</Typography>
|
||||
<Typography variant="labelLg" color="primary">{opt.price}</Typography>
|
||||
<Typography variant="labelLg" color="primary">
|
||||
{opt.price}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography variant="body2" color="text.secondary">{opt.desc}</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{opt.desc}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Card>
|
||||
@@ -135,7 +164,9 @@ export const PaymentMethod: Story = {
|
||||
name: 'Interactive — Payment Method',
|
||||
render: () => (
|
||||
<FormControl>
|
||||
<Typography variant="label" sx={{ mb: 1 }}>Payment method</Typography>
|
||||
<Typography variant="label" sx={{ mb: 1 }}>
|
||||
Payment method
|
||||
</Typography>
|
||||
<RadioGroup defaultValue="card" row>
|
||||
<FormControlLabel value="card" control={<Radio />} label="Credit card" />
|
||||
<FormControlLabel value="bank" control={<Radio />} label="Bank transfer" />
|
||||
|
||||
@@ -77,39 +77,84 @@ export const ServiceAddOns: Story = {
|
||||
};
|
||||
|
||||
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' },
|
||||
{
|
||||
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,
|
||||
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>
|
||||
<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={{ 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>
|
||||
<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>
|
||||
<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' }}>
|
||||
<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>
|
||||
<Typography variant="labelLg" color="primary">
|
||||
${total}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -16,22 +16,35 @@ const meta: Meta<typeof Typography> = {
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: [
|
||||
'displayHero', 'display1', 'display2', 'display3', 'displaySm',
|
||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
||||
'bodyLg', 'body1', 'body2', 'bodyXs',
|
||||
'labelLg', 'label', 'labelSm',
|
||||
'caption', 'captionSm',
|
||||
'overline', 'overlineSm',
|
||||
'displayHero',
|
||||
'display1',
|
||||
'display2',
|
||||
'display3',
|
||||
'displaySm',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'bodyLg',
|
||||
'body1',
|
||||
'body2',
|
||||
'bodyXs',
|
||||
'labelLg',
|
||||
'label',
|
||||
'labelSm',
|
||||
'caption',
|
||||
'captionSm',
|
||||
'overline',
|
||||
'overlineSm',
|
||||
],
|
||||
description: 'Typography variant — 21 variants across 6 categories',
|
||||
table: { defaultValue: { summary: 'body1' } },
|
||||
},
|
||||
color: {
|
||||
control: 'select',
|
||||
options: [
|
||||
'textPrimary', 'textSecondary', 'textDisabled',
|
||||
'primary', 'secondary', 'error',
|
||||
],
|
||||
options: ['textPrimary', 'textSecondary', 'textDisabled', 'primary', 'secondary', 'error'],
|
||||
},
|
||||
maxLines: { control: 'number' },
|
||||
gutterBottom: { control: 'boolean' },
|
||||
@@ -47,7 +60,8 @@ const SAMPLE = 'Discover, Explore, and Plan Funerals in Minutes, Not Hours';
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: 'Funeral Arranger helps families find transparent, affordable funeral services across Australia.',
|
||||
children:
|
||||
'Funeral Arranger helps families find transparent, affordable funeral services across Australia.',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -59,23 +73,33 @@ export const Display: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">displayHero — 80px</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
displayHero — 80px
|
||||
</Typography>
|
||||
<Typography variant="displayHero">{SAMPLE}</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">display1 — 64px</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
display1 — 64px
|
||||
</Typography>
|
||||
<Typography variant="display1">{SAMPLE}</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">display2 — 52px</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
display2 — 52px
|
||||
</Typography>
|
||||
<Typography variant="display2">{SAMPLE}</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">display3 — 40px</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
display3 — 40px
|
||||
</Typography>
|
||||
<Typography variant="display3">{SAMPLE}</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">displaySm — 32px</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
displaySm — 32px
|
||||
</Typography>
|
||||
<Typography variant="displaySm">{SAMPLE}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,27 +114,39 @@ export const Headings: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">h1 — 36px</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
h1 — 36px
|
||||
</Typography>
|
||||
<Typography variant="h1">{SAMPLE}</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">h2 — 30px</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
h2 — 30px
|
||||
</Typography>
|
||||
<Typography variant="h2">{SAMPLE}</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">h3 — 24px</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
h3 — 24px
|
||||
</Typography>
|
||||
<Typography variant="h3">{SAMPLE}</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">h4 — 20px</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
h4 — 20px
|
||||
</Typography>
|
||||
<Typography variant="h4">{SAMPLE}</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">h5 — 18px</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
h5 — 18px
|
||||
</Typography>
|
||||
<Typography variant="h5">{SAMPLE}</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">h6 — 16px</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
h6 — 16px
|
||||
</Typography>
|
||||
<Typography variant="h6">{SAMPLE}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
@@ -125,31 +161,39 @@ export const Body: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, maxWidth: 640 }}>
|
||||
<div>
|
||||
<Typography variant="overline" gutterBottom>bodyLg — 18px</Typography>
|
||||
<Typography variant="overline" gutterBottom>
|
||||
bodyLg — 18px
|
||||
</Typography>
|
||||
<Typography variant="bodyLg">
|
||||
Planning a funeral is one of the most difficult tasks a family faces. Funeral Arranger
|
||||
is here to help you navigate this process with care and transparency.
|
||||
Planning a funeral is one of the most difficult tasks a family faces. Funeral Arranger is
|
||||
here to help you navigate this process with care and transparency.
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="overline" gutterBottom>body1 (default) — 16px</Typography>
|
||||
<Typography variant="overline" gutterBottom>
|
||||
body1 (default) — 16px
|
||||
</Typography>
|
||||
<Typography variant="body1">
|
||||
Compare funeral directors in your area, view transparent pricing, and make informed
|
||||
decisions at your own pace. Every family deserves clarity during this time.
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="overline" gutterBottom>body2 (small) — 14px</Typography>
|
||||
<Typography variant="overline" gutterBottom>
|
||||
body2 (small) — 14px
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
Prices shown are indicative and may vary based on your specific requirements.
|
||||
Contact the funeral director directly for a detailed quote.
|
||||
Prices shown are indicative and may vary based on your specific requirements. Contact the
|
||||
funeral director directly for a detailed quote.
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="overline" gutterBottom>bodyXs — 12px</Typography>
|
||||
<Typography variant="overline" gutterBottom>
|
||||
bodyXs — 12px
|
||||
</Typography>
|
||||
<Typography variant="bodyXs">
|
||||
Terms and conditions apply. Funeral Arranger is a comparison service and does not
|
||||
directly provide funeral services. ABN 12 345 678 901.
|
||||
Terms and conditions apply. Funeral Arranger is a comparison service and does not directly
|
||||
provide funeral services. ABN 12 345 678 901.
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
@@ -164,32 +208,60 @@ export const UIText: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">labelLg — 16px medium</Typography>
|
||||
<Typography variant="labelLg" display="block">Form label or section label</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
labelLg — 16px medium
|
||||
</Typography>
|
||||
<Typography variant="labelLg" display="block">
|
||||
Form label or section label
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">label — 14px medium</Typography>
|
||||
<Typography variant="label" display="block">Default form label</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
label — 14px medium
|
||||
</Typography>
|
||||
<Typography variant="label" display="block">
|
||||
Default form label
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">labelSm — 12px medium</Typography>
|
||||
<Typography variant="labelSm" display="block">Compact label or tag text</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
labelSm — 12px medium
|
||||
</Typography>
|
||||
<Typography variant="labelSm" display="block">
|
||||
Compact label or tag text
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<Typography variant="captionSm" color="textSecondary">caption — 12px regular</Typography>
|
||||
<Typography variant="caption" display="block">Fine print, timestamps, metadata</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
caption — 12px regular
|
||||
</Typography>
|
||||
<Typography variant="caption" display="block">
|
||||
Fine print, timestamps, metadata
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">captionSm — 11px regular</Typography>
|
||||
<Typography variant="captionSm" display="block">Compact metadata, footnotes</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
captionSm — 11px regular
|
||||
</Typography>
|
||||
<Typography variant="captionSm" display="block">
|
||||
Compact metadata, footnotes
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<Typography variant="captionSm" color="textSecondary">overline — 12px semibold uppercase</Typography>
|
||||
<Typography variant="overline" display="block">Section overline</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
overline — 12px semibold uppercase
|
||||
</Typography>
|
||||
<Typography variant="overline" display="block">
|
||||
Section overline
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="captionSm" color="textSecondary">overlineSm — 11px semibold uppercase</Typography>
|
||||
<Typography variant="overlineSm" display="block">Compact overline</Typography>
|
||||
<Typography variant="captionSm" color="textSecondary">
|
||||
overlineSm — 11px semibold uppercase
|
||||
</Typography>
|
||||
<Typography variant="overlineSm" display="block">
|
||||
Compact overline
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
@@ -222,20 +294,25 @@ export const FontFamilies: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||||
<div>
|
||||
<Typography variant="overline" gutterBottom>Display font — Noto Serif SC (Regular 400)</Typography>
|
||||
<Typography variant="display3">
|
||||
Warm, trustworthy, and professional
|
||||
<Typography variant="overline" gutterBottom>
|
||||
Display font — Noto Serif SC (Regular 400)
|
||||
</Typography>
|
||||
<Typography variant="display3">Warm, trustworthy, and professional</Typography>
|
||||
<Typography variant="caption" color="textSecondary" sx={{ mt: 1 }}>
|
||||
Used exclusively for display variants (hero through sm). Regular weight — serif carries inherent visual weight at large sizes.
|
||||
Used exclusively for display variants (hero through sm). Regular weight — serif carries
|
||||
inherent visual weight at large sizes.
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="overline" gutterBottom>Body font — Montserrat</Typography>
|
||||
<Typography variant="h3" gutterBottom>Clean, modern, and highly readable</Typography>
|
||||
<Typography variant="overline" gutterBottom>
|
||||
Body font — Montserrat
|
||||
</Typography>
|
||||
<Typography variant="h3" gutterBottom>
|
||||
Clean, modern, and highly readable
|
||||
</Typography>
|
||||
<Typography>
|
||||
Used for all headings (h1–h6), body text, labels, captions, and UI elements.
|
||||
Headings use Bold (700), body uses Medium (500), captions use Regular (400).
|
||||
Used for all headings (h1–h6), body text, labels, captions, and UI elements. Headings use
|
||||
Bold (700), body uses Medium (500), captions use Regular (400).
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
@@ -249,18 +326,21 @@ export const MaxLines: Story = {
|
||||
render: () => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24, maxWidth: 400 }}>
|
||||
<div>
|
||||
<Typography variant="label" gutterBottom>maxLines=1</Typography>
|
||||
<Typography variant="label" gutterBottom>
|
||||
maxLines=1
|
||||
</Typography>
|
||||
<Typography maxLines={1}>
|
||||
H. Parsons Funeral Directors — trusted by Australian families for over 30 years,
|
||||
providing compassionate and transparent funeral services.
|
||||
H. Parsons Funeral Directors — trusted by Australian families for over 30 years, providing
|
||||
compassionate and transparent funeral services.
|
||||
</Typography>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="label" gutterBottom>maxLines=2</Typography>
|
||||
<Typography variant="label" gutterBottom>
|
||||
maxLines=2
|
||||
</Typography>
|
||||
<Typography maxLines={2}>
|
||||
H. Parsons Funeral Directors — trusted by Australian families for over 30 years,
|
||||
providing compassionate and transparent funeral services across metropolitan
|
||||
and regional areas.
|
||||
H. Parsons Funeral Directors — trusted by Australian families for over 30 years, providing
|
||||
compassionate and transparent funeral services across metropolitan and regional areas.
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
@@ -276,23 +356,27 @@ export const RealisticContent: Story = {
|
||||
<Typography variant="overline">Funeral planning</Typography>
|
||||
<Typography variant="display3">Compare funeral services in your area</Typography>
|
||||
<Typography variant="bodyLg" color="textSecondary">
|
||||
Transparent pricing and service comparison to help you make informed
|
||||
decisions during a difficult time.
|
||||
Transparent pricing and service comparison to help you make informed decisions during a
|
||||
difficult time.
|
||||
</Typography>
|
||||
<Typography variant="h2" sx={{ mt: 2 }}>How it works</Typography>
|
||||
<Typography>
|
||||
Enter your suburb or postcode to find funeral directors near you. Each
|
||||
listing includes a full price breakdown, service inclusions, and reviews
|
||||
from families who have used their services.
|
||||
<Typography variant="h2" sx={{ mt: 2 }}>
|
||||
How it works
|
||||
</Typography>
|
||||
<Typography variant="h3" sx={{ mt: 1 }}>Step 1: Browse packages</Typography>
|
||||
<Typography>
|
||||
Compare packages side by side. Each package clearly shows what is and
|
||||
isn't included, so there are no surprises.
|
||||
Enter your suburb or postcode to find funeral directors near you. Each listing includes a
|
||||
full price breakdown, service inclusions, and reviews from families who have used their
|
||||
services.
|
||||
</Typography>
|
||||
<Typography variant="h3" sx={{ mt: 1 }}>
|
||||
Step 1: Browse packages
|
||||
</Typography>
|
||||
<Typography>
|
||||
Compare packages side by side. Each package clearly shows what is and isn't included, so
|
||||
there are no surprises.
|
||||
</Typography>
|
||||
<Typography variant="caption" color="textSecondary" sx={{ mt: 2 }}>
|
||||
Prices are indicative and current as of March 2026. Contact the funeral
|
||||
director for a binding quote.
|
||||
Prices are indicative and current as of March 2026. Contact the funeral director for a
|
||||
binding quote.
|
||||
</Typography>
|
||||
</div>
|
||||
),
|
||||
@@ -333,7 +417,11 @@ export const CompleteScale: Story = {
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||
{variants.map(({ variant, label }) => (
|
||||
<div key={variant} style={{ display: 'flex', alignItems: 'baseline', gap: 16 }}>
|
||||
<Typography variant="captionSm" color="textSecondary" sx={{ width: 160, flexShrink: 0, textAlign: 'right' }}>
|
||||
<Typography
|
||||
variant="captionSm"
|
||||
color="textSecondary"
|
||||
sx={{ width: 160, flexShrink: 0, textAlign: 'right' }}
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
<Typography variant={variant}>{SAMPLE}</Typography>
|
||||
|
||||
@@ -150,7 +150,8 @@ export const ServiceAddOns: Story = {
|
||||
export const WithoutPrice: Story = {
|
||||
args: {
|
||||
name: 'Order of service booklet',
|
||||
description: 'Complimentary printed booklet with the service programme and a photo of your loved one.',
|
||||
description:
|
||||
'Complimentary printed booklet with the service programme and a photo of your loved one.',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -160,13 +161,7 @@ export const WithoutPrice: Story = {
|
||||
export const WithoutDescription: Story = {
|
||||
render: function Render() {
|
||||
const [checked, setChecked] = React.useState(false);
|
||||
return (
|
||||
<AddOnOption
|
||||
name="Include GST in pricing"
|
||||
checked={checked}
|
||||
onChange={setChecked}
|
||||
/>
|
||||
);
|
||||
return <AddOnOption name="Include GST in pricing" checked={checked} onChange={setChecked} />;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -176,7 +171,8 @@ export const WithoutDescription: Story = {
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
name: 'Catering',
|
||||
description: 'Not available at this venue. Please contact the venue directly for catering options.',
|
||||
description:
|
||||
'Not available at this venue. Please contact the venue directly for catering options.',
|
||||
price: 1200,
|
||||
disabled: true,
|
||||
},
|
||||
|
||||
@@ -54,7 +54,19 @@ export interface AddOnOptionProps {
|
||||
* ```
|
||||
*/
|
||||
export const AddOnOption = React.forwardRef<HTMLDivElement, AddOnOptionProps>(
|
||||
({ name, description, price, checked = false, onChange, disabled = false, maxDescriptionLines, sx }, ref) => {
|
||||
(
|
||||
{
|
||||
name,
|
||||
description,
|
||||
price,
|
||||
checked = false,
|
||||
onChange,
|
||||
disabled = false,
|
||||
maxDescriptionLines,
|
||||
sx,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const switchId = React.useId();
|
||||
const [expanded, setExpanded] = React.useState(false);
|
||||
const [isClamped, setIsClamped] = React.useState(false);
|
||||
@@ -129,10 +141,7 @@ export const AddOnOption = React.forwardRef<HTMLDivElement, AddOnOptionProps>(
|
||||
|
||||
{/* Price — tucks directly under heading */}
|
||||
{price != null && (
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="text.secondary"
|
||||
>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
${price.toLocaleString('en-AU')}
|
||||
</Typography>
|
||||
)}
|
||||
@@ -146,7 +155,8 @@ export const AddOnOption = React.forwardRef<HTMLDivElement, AddOnOptionProps>(
|
||||
color="text.secondary"
|
||||
sx={{
|
||||
mt: 0.5,
|
||||
...(maxDescriptionLines && !expanded && {
|
||||
...(maxDescriptionLines &&
|
||||
!expanded && {
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: maxDescriptionLines,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
|
||||
@@ -88,15 +88,54 @@ export const PackageContents: Story = {
|
||||
Essentials
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<LineItem name="Accommodation" price={1500} info="Refrigerated holding of the deceased prior to the funeral service." />
|
||||
<LineItem name="Death Registration Certificate" price={1500} info="Lodgement of death registration with NSW Registry of Births, Deaths & Marriages." />
|
||||
<LineItem name="Doctor Fee for Cremation" price={1500} info="Statutory medical referee fee required for all cremations in NSW." />
|
||||
<LineItem name="NSW Government Levy — Cremation" price={1500} info="NSW Government cremation levy as set by the Department of Health." />
|
||||
<LineItem name="Professional Mortuary Care" price={1500} info="Preparation and care of the deceased." />
|
||||
<LineItem name="Professional Service Fee" price={1500} info="Coordination of all funeral arrangements and services." />
|
||||
<LineItem name="Allowance for Coffin" price={1500} isAllowance info="Allowance amount — upgrade options available during arrangement." />
|
||||
<LineItem name="Allowance for Crematorium" price={1500} isAllowance info="Allowance for crematorium fees — varies by location." />
|
||||
<LineItem name="Allowance for Hearse" price={1500} isAllowance info="Allowance for hearse transfer — distance surcharges may apply." />
|
||||
<LineItem
|
||||
name="Accommodation"
|
||||
price={1500}
|
||||
info="Refrigerated holding of the deceased prior to the funeral service."
|
||||
/>
|
||||
<LineItem
|
||||
name="Death Registration Certificate"
|
||||
price={1500}
|
||||
info="Lodgement of death registration with NSW Registry of Births, Deaths & Marriages."
|
||||
/>
|
||||
<LineItem
|
||||
name="Doctor Fee for Cremation"
|
||||
price={1500}
|
||||
info="Statutory medical referee fee required for all cremations in NSW."
|
||||
/>
|
||||
<LineItem
|
||||
name="NSW Government Levy — Cremation"
|
||||
price={1500}
|
||||
info="NSW Government cremation levy as set by the Department of Health."
|
||||
/>
|
||||
<LineItem
|
||||
name="Professional Mortuary Care"
|
||||
price={1500}
|
||||
info="Preparation and care of the deceased."
|
||||
/>
|
||||
<LineItem
|
||||
name="Professional Service Fee"
|
||||
price={1500}
|
||||
info="Coordination of all funeral arrangements and services."
|
||||
/>
|
||||
<LineItem
|
||||
name="Allowance for Coffin"
|
||||
price={1500}
|
||||
isAllowance
|
||||
info="Allowance amount — upgrade options available during arrangement."
|
||||
/>
|
||||
<LineItem
|
||||
name="Allowance for Crematorium"
|
||||
price={1500}
|
||||
isAllowance
|
||||
info="Allowance for crematorium fees — varies by location."
|
||||
/>
|
||||
<LineItem
|
||||
name="Allowance for Hearse"
|
||||
price={1500}
|
||||
isAllowance
|
||||
info="Allowance for hearse transfer — distance surcharges may apply."
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ my: 3 }} />
|
||||
@@ -105,7 +144,10 @@ export const PackageContents: Story = {
|
||||
Complimentary Items
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<LineItem name="Dressing Fee" info="Dressing and preparation of the deceased — included at no charge." />
|
||||
<LineItem
|
||||
name="Dressing Fee"
|
||||
info="Dressing and preparation of the deceased — included at no charge."
|
||||
/>
|
||||
<LineItem name="Viewing Fee" info="One private family viewing — included at no charge." />
|
||||
</Box>
|
||||
|
||||
@@ -117,12 +159,38 @@ export const PackageContents: Story = {
|
||||
Extras
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<LineItem name="Allowance for Flowers" price={1500} isAllowance info="Seasonal floral arrangements for the service." />
|
||||
<LineItem name="Allowance for Master of Ceremonies" price={1500} isAllowance info="Professional celebrant or MC for the funeral service." />
|
||||
<LineItem name="After Business Hours Service Surcharge" price={1500} info="Additional fee for services held outside standard business hours." />
|
||||
<LineItem name="After Hours Prayers" price={1500} info="Evening prayer service at the funeral home." />
|
||||
<LineItem name="Coffin Bearing by Funeral Directors" price={1500} info="Professional pallbearing by funeral directors." />
|
||||
<LineItem name="Digital Recording" price={1500} info="Professional video recording of the funeral service." />
|
||||
<LineItem
|
||||
name="Allowance for Flowers"
|
||||
price={1500}
|
||||
isAllowance
|
||||
info="Seasonal floral arrangements for the service."
|
||||
/>
|
||||
<LineItem
|
||||
name="Allowance for Master of Ceremonies"
|
||||
price={1500}
|
||||
isAllowance
|
||||
info="Professional celebrant or MC for the funeral service."
|
||||
/>
|
||||
<LineItem
|
||||
name="After Business Hours Service Surcharge"
|
||||
price={1500}
|
||||
info="Additional fee for services held outside standard business hours."
|
||||
/>
|
||||
<LineItem
|
||||
name="After Hours Prayers"
|
||||
price={1500}
|
||||
info="Evening prayer service at the funeral home."
|
||||
/>
|
||||
<LineItem
|
||||
name="Coffin Bearing by Funeral Directors"
|
||||
price={1500}
|
||||
info="Professional pallbearing by funeral directors."
|
||||
/>
|
||||
<LineItem
|
||||
name="Digital Recording"
|
||||
price={1500}
|
||||
info="Professional video recording of the funeral service."
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
),
|
||||
|
||||
@@ -48,8 +48,9 @@ export const LineItem = React.forwardRef<HTMLDivElement, LineItemProps>(
|
||||
({ name, info, price, isAllowance = false, priceLabel, variant = 'default', sx }, ref) => {
|
||||
const isTotal = variant === 'total';
|
||||
|
||||
const formattedPrice = priceLabel
|
||||
?? (price != null ? `$${price.toLocaleString('en-AU')}${isAllowance ? '*' : ''}` : undefined);
|
||||
const formattedPrice =
|
||||
priceLabel ??
|
||||
(price != null ? `$${price.toLocaleString('en-AU')}${isAllowance ? '*' : ''}` : undefined);
|
||||
|
||||
return (
|
||||
<Box
|
||||
|
||||
@@ -68,7 +68,8 @@ export const Default: Story = {
|
||||
reviewCount: 127,
|
||||
capabilityLabel: 'Online Arrangement',
|
||||
capabilityColor: 'success',
|
||||
capabilityDescription: 'Complete your arrangement entirely online — no in-person visit required.',
|
||||
capabilityDescription:
|
||||
'Complete your arrangement entirely online — no in-person visit required.',
|
||||
startingPrice: 900,
|
||||
},
|
||||
};
|
||||
@@ -311,11 +312,7 @@ export const EdgeCases: Story = {
|
||||
onClick={() => {}}
|
||||
/>
|
||||
{/* Minimal — just name and location */}
|
||||
<ProviderCard
|
||||
name="Minimal Card"
|
||||
location="Hobart"
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<ProviderCard name="Minimal Card" location="Hobart" onClick={() => {}} />
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -144,12 +144,7 @@ export const ProviderCard = React.forwardRef<HTMLDivElement, ProviderCardProps>(
|
||||
>
|
||||
{/* Verified badge */}
|
||||
<Box sx={{ position: 'absolute', top: 12, right: 12 }}>
|
||||
<Badge
|
||||
variant="filled"
|
||||
color="brand"
|
||||
size="medium"
|
||||
icon={<VerifiedOutlinedIcon />}
|
||||
>
|
||||
<Badge variant="filled" color="brand" size="medium" icon={<VerifiedOutlinedIcon />}>
|
||||
Verified
|
||||
</Badge>
|
||||
</Box>
|
||||
@@ -219,9 +214,7 @@ export const ProviderCard = React.forwardRef<HTMLDivElement, ProviderCardProps>(
|
||||
>
|
||||
{/* Location */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
<LocationOnOutlinedIcon
|
||||
sx={{ fontSize: 14, color: 'text.secondary' }}
|
||||
/>
|
||||
<LocationOnOutlinedIcon sx={{ fontSize: 14, color: 'text.secondary' }} />
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{location}
|
||||
</Typography>
|
||||
@@ -233,10 +226,7 @@ export const ProviderCard = React.forwardRef<HTMLDivElement, ProviderCardProps>(
|
||||
sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}
|
||||
aria-label={`Rated ${rating} out of 5${reviewCount != null ? `, ${reviewCount} reviews` : ''}`}
|
||||
>
|
||||
<StarRoundedIcon
|
||||
sx={{ fontSize: 14, color: 'warning.main' }}
|
||||
aria-hidden
|
||||
/>
|
||||
<StarRoundedIcon sx={{ fontSize: 14, color: 'warning.main' }} aria-hidden />
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{rating}
|
||||
{reviewCount != null && ` (${reviewCount.toLocaleString('en-AU')})`}
|
||||
@@ -249,17 +239,8 @@ export const ProviderCard = React.forwardRef<HTMLDivElement, ProviderCardProps>(
|
||||
{capabilityLabel && (
|
||||
<Box>
|
||||
{capabilityDescription ? (
|
||||
<Tooltip
|
||||
title={capabilityDescription}
|
||||
arrow
|
||||
placement="top"
|
||||
enterTouchDelay={0}
|
||||
>
|
||||
<Badge
|
||||
color={capabilityColor}
|
||||
size="medium"
|
||||
sx={{ cursor: 'help' }}
|
||||
>
|
||||
<Tooltip title={capabilityDescription} arrow placement="top" enterTouchDelay={0}>
|
||||
<Badge color={capabilityColor} size="medium" sx={{ cursor: 'help' }}>
|
||||
{capabilityLabel}
|
||||
<InfoOutlinedIcon />
|
||||
</Badge>
|
||||
|
||||
@@ -2,7 +2,8 @@ import type { Meta, StoryObj } from '@storybook/react';
|
||||
import Box from '@mui/material/Box';
|
||||
import { ProviderCardCompact } from './ProviderCardCompact';
|
||||
|
||||
const DEMO_IMAGE = 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=400&h=300&fit=crop';
|
||||
const DEMO_IMAGE =
|
||||
'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=400&h=300&fit=crop';
|
||||
|
||||
const meta: Meta<typeof ProviderCardCompact> = {
|
||||
title: 'Molecules/ProviderCardCompact',
|
||||
|
||||
@@ -101,10 +101,7 @@ export const ProviderCardCompact = React.forwardRef<HTMLDivElement, ProviderCard
|
||||
|
||||
{/* Location */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
<LocationOnOutlinedIcon
|
||||
sx={{ fontSize: 16, color: 'text.secondary' }}
|
||||
aria-hidden
|
||||
/>
|
||||
<LocationOnOutlinedIcon sx={{ fontSize: 16, color: 'text.secondary' }} aria-hidden />
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{location}
|
||||
</Typography>
|
||||
@@ -118,7 +115,10 @@ export const ProviderCardCompact = React.forwardRef<HTMLDivElement, ProviderCard
|
||||
aria-hidden
|
||||
/>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{rating} Rating{reviewCount != null ? ` (${reviewCount} ${reviewCount === 1 ? 'Review' : 'Reviews'})` : ''}
|
||||
{rating} Rating
|
||||
{reviewCount != null
|
||||
? ` (${reviewCount} ${reviewCount === 1 ? 'Review' : 'Reviews'})`
|
||||
: ''}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -149,13 +149,7 @@ export const Loading: Story = {
|
||||
const [value, setValue] = React.useState('Parsons funeral');
|
||||
|
||||
return (
|
||||
<SearchBar
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
placeholder="Search..."
|
||||
showButton
|
||||
loading
|
||||
/>
|
||||
<SearchBar value={value} onChange={setValue} placeholder="Search..." showButton loading />
|
||||
);
|
||||
},
|
||||
};
|
||||
@@ -200,11 +194,7 @@ export const ProviderSearch: Story = {
|
||||
setResults([]);
|
||||
return;
|
||||
}
|
||||
setResults(
|
||||
providers.filter((p) =>
|
||||
p.toLowerCase().includes(query.toLowerCase()),
|
||||
),
|
||||
);
|
||||
setResults(providers.filter((p) => p.toLowerCase().includes(query.toLowerCase())));
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -252,17 +252,9 @@ export const EdgeCases: Story = {
|
||||
onClick={() => {}}
|
||||
/>
|
||||
{/* No description */}
|
||||
<ServiceOption
|
||||
name="Flowers"
|
||||
price={250}
|
||||
selected
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<ServiceOption name="Flowers" price={250} selected onClick={() => {}} />
|
||||
{/* No price, no description */}
|
||||
<ServiceOption
|
||||
name="Contact us for pricing"
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<ServiceOption name="Contact us for pricing" onClick={() => {}} />
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -55,7 +55,19 @@ export interface ServiceOptionProps {
|
||||
* ```
|
||||
*/
|
||||
export const ServiceOption = React.forwardRef<HTMLDivElement, ServiceOptionProps>(
|
||||
({ name, description, price, selected = false, disabled = false, onClick, maxDescriptionLines, sx }, ref) => {
|
||||
(
|
||||
{
|
||||
name,
|
||||
description,
|
||||
price,
|
||||
selected = false,
|
||||
disabled = false,
|
||||
onClick,
|
||||
maxDescriptionLines,
|
||||
sx,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const [expanded, setExpanded] = React.useState(false);
|
||||
const [isClamped, setIsClamped] = React.useState(false);
|
||||
const descRef = React.useRef<HTMLElement>(null);
|
||||
@@ -123,7 +135,8 @@ export const ServiceOption = React.forwardRef<HTMLDivElement, ServiceOptionProps
|
||||
color="text.secondary"
|
||||
sx={{
|
||||
mt: 0.5,
|
||||
...(maxDescriptionLines && !expanded && {
|
||||
...(maxDescriptionLines &&
|
||||
!expanded && {
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: maxDescriptionLines,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
|
||||
@@ -106,7 +106,15 @@ export const Interactive: Story = {
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
<StepIndicator steps={arrangementSteps} currentStep={step} />
|
||||
|
||||
<Box sx={{ p: 3, bgcolor: 'background.paper', borderRadius: 1, border: '1px solid', borderColor: 'divider' }}>
|
||||
<Box
|
||||
sx={{
|
||||
p: 3,
|
||||
bgcolor: 'background.paper',
|
||||
borderRadius: 1,
|
||||
border: '1px solid',
|
||||
borderColor: 'divider',
|
||||
}}
|
||||
>
|
||||
<Typography variant="h5" sx={{ mb: 1 }}>
|
||||
{arrangementSteps[step].label}
|
||||
</Typography>
|
||||
|
||||
@@ -161,12 +161,7 @@ export const EdgeCases: Story = {
|
||||
onClick={() => {}}
|
||||
/>
|
||||
{/* Minimal — just name, image, location */}
|
||||
<VenueCard
|
||||
name="Minimal Venue"
|
||||
imageUrl={VENUE_BEACH}
|
||||
location="Kiama"
|
||||
onClick={() => {}}
|
||||
/>
|
||||
<VenueCard name="Minimal Venue" imageUrl={VENUE_BEACH} location="Kiama" onClick={() => {}} />
|
||||
</Box>
|
||||
),
|
||||
};
|
||||
@@ -186,9 +181,7 @@ export const Responsive: Story = {
|
||||
<Box sx={{ display: 'flex', gap: 3, alignItems: 'start', flexWrap: 'wrap' }}>
|
||||
{[280, 340, 420].map((width) => (
|
||||
<Box key={width} sx={{ width }}>
|
||||
<Box sx={{ mb: 1, fontSize: 12, color: 'text.secondary' }}>
|
||||
{width}px
|
||||
</Box>
|
||||
<Box sx={{ mb: 1, fontSize: 12, color: 'text.secondary' }}>{width}px</Box>
|
||||
<VenueCard
|
||||
name="West Chapel"
|
||||
imageUrl={VENUE_CHAPEL}
|
||||
@@ -218,9 +211,7 @@ export const OnDifferentBackgrounds: Story = {
|
||||
render: () => (
|
||||
<Box sx={{ display: 'flex', gap: 3 }}>
|
||||
<Box sx={{ width: 360, p: 3, backgroundColor: 'background.default' }}>
|
||||
<Box sx={{ mb: 1, fontSize: 12, color: 'text.secondary' }}>
|
||||
White surface
|
||||
</Box>
|
||||
<Box sx={{ mb: 1, fontSize: 12, color: 'text.secondary' }}>White surface</Box>
|
||||
<VenueCard
|
||||
name="West Chapel"
|
||||
imageUrl={VENUE_CHAPEL}
|
||||
@@ -237,9 +228,7 @@ export const OnDifferentBackgrounds: Story = {
|
||||
backgroundColor: 'var(--fa-color-surface-subtle)',
|
||||
}}
|
||||
>
|
||||
<Box sx={{ mb: 1, fontSize: 12, color: 'text.secondary' }}>
|
||||
Grey surface (neutral.50)
|
||||
</Box>
|
||||
<Box sx={{ mb: 1, fontSize: 12, color: 'text.secondary' }}>Grey surface (neutral.50)</Box>
|
||||
<VenueCard
|
||||
name="Macquarie Park Gardens"
|
||||
imageUrl={VENUE_GARDEN}
|
||||
|
||||
@@ -112,9 +112,7 @@ export const VenueCard = React.forwardRef<HTMLDivElement, VenueCardProps>(
|
||||
>
|
||||
{/* Location */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
<LocationOnOutlinedIcon
|
||||
sx={{ fontSize: 14, color: 'text.secondary' }}
|
||||
/>
|
||||
<LocationOnOutlinedIcon sx={{ fontSize: 14, color: 'text.secondary' }} />
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{location}
|
||||
</Typography>
|
||||
@@ -126,10 +124,7 @@ export const VenueCard = React.forwardRef<HTMLDivElement, VenueCardProps>(
|
||||
sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}
|
||||
aria-label={`Capacity: ${capacity} guests`}
|
||||
>
|
||||
<PeopleOutlinedIcon
|
||||
sx={{ fontSize: 14, color: 'text.secondary' }}
|
||||
aria-hidden
|
||||
/>
|
||||
<PeopleOutlinedIcon sx={{ fontSize: 14, color: 'text.secondary' }} aria-hidden />
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{capacity} guests
|
||||
</Typography>
|
||||
@@ -143,12 +138,7 @@ export const VenueCard = React.forwardRef<HTMLDivElement, VenueCardProps>(
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
From
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h6"
|
||||
component="span"
|
||||
color="primary"
|
||||
sx={{ fontWeight: 600 }}
|
||||
>
|
||||
<Typography variant="h6" component="span" color="primary" sx={{ fontWeight: 600 }}>
|
||||
${price.toLocaleString('en-AU')}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
@@ -15,12 +15,7 @@ const FALogoInverse = () => (
|
||||
);
|
||||
|
||||
const FALogoNav = () => (
|
||||
<Box
|
||||
component="img"
|
||||
src="/brandlogo/logo-full.svg"
|
||||
alt="Funeral Arranger"
|
||||
sx={{ height: 28 }}
|
||||
/>
|
||||
<Box component="img" src="/brandlogo/logo-full.svg" alt="Funeral Arranger" sx={{ height: 28 }} />
|
||||
);
|
||||
|
||||
const defaultLinkGroups = [
|
||||
@@ -169,8 +164,8 @@ export const FullPage: Story = {
|
||||
Find a funeral director
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mb: 4, maxWidth: 600 }}>
|
||||
Compare trusted funeral directors in your area. View services,
|
||||
pricing, and reviews to find the right support for your family.
|
||||
Compare trusted funeral directors in your area. View services, pricing, and reviews to
|
||||
find the right support for your family.
|
||||
</Typography>
|
||||
{Array.from({ length: 4 }).map((_, i) => (
|
||||
<Box
|
||||
|
||||
@@ -64,19 +64,7 @@ export interface FooterProps {
|
||||
* ```
|
||||
*/
|
||||
export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
|
||||
(
|
||||
{
|
||||
logo,
|
||||
tagline,
|
||||
linkGroups = [],
|
||||
phone,
|
||||
email,
|
||||
copyright,
|
||||
legalLinks = [],
|
||||
sx,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
({ logo, tagline, linkGroups = [], phone, email, copyright, legalLinks = [], sx }, ref) => {
|
||||
const year = new Date().getFullYear();
|
||||
const copyrightText = copyright || `\u00A9 ${year} Funeral Arranger. All rights reserved.`;
|
||||
|
||||
@@ -143,10 +131,7 @@ export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
|
||||
<Typography variant="overlineSm" sx={overlineSx}>
|
||||
Email
|
||||
</Typography>
|
||||
<Link
|
||||
href={`mailto:${email}`}
|
||||
sx={contactLinkSx}
|
||||
>
|
||||
<Link href={`mailto:${email}`} sx={contactLinkSx}>
|
||||
{email}
|
||||
</Link>
|
||||
</Box>
|
||||
@@ -157,7 +142,15 @@ export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
|
||||
|
||||
{/* Link group columns */}
|
||||
{linkGroups.map((group) => (
|
||||
<Grid item xs={6} sm={4} md key={group.heading} component="nav" aria-label={group.heading}>
|
||||
<Grid
|
||||
item
|
||||
xs={6}
|
||||
sm={4}
|
||||
md
|
||||
key={group.heading}
|
||||
component="nav"
|
||||
aria-label={group.heading}
|
||||
>
|
||||
<Typography
|
||||
variant="label"
|
||||
sx={{
|
||||
@@ -170,7 +163,14 @@ export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
|
||||
</Typography>
|
||||
<Box
|
||||
component="ul"
|
||||
sx={{ listStyle: 'none', p: 0, m: 0, display: 'flex', flexDirection: 'column', gap: 1.5 }}
|
||||
sx={{
|
||||
listStyle: 'none',
|
||||
p: 0,
|
||||
m: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 1.5,
|
||||
}}
|
||||
>
|
||||
{group.links.map((link) => (
|
||||
<li key={link.label}>
|
||||
@@ -206,10 +206,7 @@ export const Footer = React.forwardRef<HTMLDivElement, FooterProps>(
|
||||
py: 3,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="captionSm"
|
||||
sx={{ color: 'var(--fa-color-brand-400)' }}
|
||||
>
|
||||
<Typography variant="captionSm" sx={{ color: 'var(--fa-color-brand-400)' }}>
|
||||
{copyrightText}
|
||||
</Typography>
|
||||
|
||||
|
||||
@@ -9,7 +9,12 @@ import { Typography } from '../../atoms/Typography';
|
||||
const funeralTypes = [
|
||||
{ id: 'cremation', label: 'Cremation', hasServiceOption: true },
|
||||
{ id: 'burial', label: 'Burial', hasServiceOption: true },
|
||||
{ id: 'water-burial', label: 'Water Burial', note: 'Available in QLD only', hasServiceOption: false },
|
||||
{
|
||||
id: 'water-burial',
|
||||
label: 'Water Burial',
|
||||
note: 'Available in QLD only',
|
||||
hasServiceOption: false,
|
||||
},
|
||||
];
|
||||
|
||||
const themeOptions = [
|
||||
@@ -152,8 +157,8 @@ export const InHeroDesktop: Story = {
|
||||
color="text.secondary"
|
||||
sx={{ textAlign: 'center', mb: 4, maxWidth: 440, mx: 'auto' }}
|
||||
>
|
||||
Whether you're thinking ahead or arranging for a loved one, find
|
||||
trusted local providers with transparent pricing.
|
||||
Whether you're thinking ahead or arranging for a loved one, find trusted local
|
||||
providers with transparent pricing.
|
||||
</Typography>
|
||||
<FuneralFinder
|
||||
funeralTypes={funeralTypes}
|
||||
@@ -199,7 +204,11 @@ export const InHeroMobile: Story = {
|
||||
]}
|
||||
/>
|
||||
<Box sx={{ bgcolor: 'var(--fa-color-brand-100)', px: 3, py: 4, textAlign: 'center' }}>
|
||||
<Typography variant="h3" component="h1" sx={{ mb: 1.5, color: 'var(--fa-color-brand-950)' }}>
|
||||
<Typography
|
||||
variant="h3"
|
||||
component="h1"
|
||||
sx={{ mb: 1.5, color: 'var(--fa-color-brand-950)' }}
|
||||
>
|
||||
Discover, Explore, and Plan Funerals in Minutes
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
@@ -210,7 +219,8 @@ export const InHeroMobile: Story = {
|
||||
sx={{
|
||||
height: 180,
|
||||
bgcolor: 'var(--fa-color-brand-200)',
|
||||
backgroundImage: 'url(https://images.unsplash.com/photo-1516733968668-dbdce39c0571?w=800&h=400&fit=crop)',
|
||||
backgroundImage:
|
||||
'url(https://images.unsplash.com/photo-1516733968668-dbdce39c0571?w=800&h=400&fit=crop)',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
|
||||
@@ -146,7 +146,11 @@ function ChoiceCard({
|
||||
</Typography>
|
||||
</Box>
|
||||
{description && (
|
||||
<Typography variant="caption" component="span" sx={{ display: 'block', mt: 0.5, color: 'text.secondary' }}>
|
||||
<Typography
|
||||
variant="caption"
|
||||
component="span"
|
||||
sx={{ display: 'block', mt: 0.5, color: 'text.secondary' }}
|
||||
>
|
||||
{description}
|
||||
</Typography>
|
||||
)}
|
||||
@@ -214,12 +218,20 @@ function TypeCard({
|
||||
</Typography>
|
||||
</Box>
|
||||
{description && (
|
||||
<Typography variant="caption" component="span" sx={{ display: 'block', mt: 0.25, color: 'text.secondary' }}>
|
||||
<Typography
|
||||
variant="caption"
|
||||
component="span"
|
||||
sx={{ display: 'block', mt: 0.25, color: 'text.secondary' }}
|
||||
>
|
||||
{description}
|
||||
</Typography>
|
||||
)}
|
||||
{note && (
|
||||
<Typography variant="captionSm" component="span" sx={{ display: 'block', mt: 0.5, color: 'text.secondary', fontWeight: 500 }}>
|
||||
<Typography
|
||||
variant="captionSm"
|
||||
component="span"
|
||||
sx={{ display: 'block', mt: 0.5, color: 'text.secondary', fontWeight: 500 }}
|
||||
>
|
||||
{note}
|
||||
</Typography>
|
||||
)}
|
||||
@@ -261,7 +273,13 @@ function CompletedRow({
|
||||
onClick={onChangeClick}
|
||||
underline="hover"
|
||||
aria-label={`Change ${question.toLowerCase()}`}
|
||||
sx={{ color: 'text.secondary', ml: 'auto', minHeight: 44, display: 'inline-flex', alignItems: 'center' }}
|
||||
sx={{
|
||||
color: 'text.secondary',
|
||||
ml: 'auto',
|
||||
minHeight: 44,
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
Change
|
||||
</Link>
|
||||
@@ -348,8 +366,10 @@ export const FuneralFinder = React.forwardRef<HTMLDivElement, FuneralFinderProps
|
||||
const typeSummary = [typeLabel, themeSuffix].filter(Boolean).join(', ');
|
||||
|
||||
const serviceLabel =
|
||||
servicePref === 'with-service' ? 'With a service'
|
||||
: servicePref === 'without-service' ? 'No service'
|
||||
servicePref === 'with-service'
|
||||
? 'With a service'
|
||||
: servicePref === 'without-service'
|
||||
? 'No service'
|
||||
: 'Flexible';
|
||||
|
||||
// ─── Handlers ───────────────────────────────────────────────────
|
||||
@@ -409,7 +429,7 @@ export const FuneralFinder = React.forwardRef<HTMLDivElement, FuneralFinderProps
|
||||
intent,
|
||||
planningFor: needsPlanningFor ? (planningFor ?? undefined) : undefined,
|
||||
funeralTypeId: isExploreAll ? null : (typeSelection ?? null),
|
||||
servicePreference: (showServiceStep && serviceAnswered) ? servicePref : 'either',
|
||||
servicePreference: showServiceStep && serviceAnswered ? servicePref : 'either',
|
||||
themes: selectedThemes,
|
||||
location: location.trim(),
|
||||
});
|
||||
@@ -448,16 +468,32 @@ export const FuneralFinder = React.forwardRef<HTMLDivElement, FuneralFinderProps
|
||||
|
||||
{/* ── Completed rows ─────────────────────────────────────── */}
|
||||
<Collapse in={intent !== null && activeStep !== 1} timeout={250}>
|
||||
<CompletedRow question="I'm here to" answer={intentLabel} onChangeClick={() => revertTo(1)} />
|
||||
<CompletedRow
|
||||
question="I'm here to"
|
||||
answer={intentLabel}
|
||||
onChangeClick={() => revertTo(1)}
|
||||
/>
|
||||
</Collapse>
|
||||
<Collapse in={needsPlanningFor && planningFor !== null && activeStep !== 2} timeout={250}>
|
||||
<CompletedRow question="Planning for" answer={planningForLabel} onChangeClick={() => revertTo(2)} />
|
||||
<CompletedRow
|
||||
question="Planning for"
|
||||
answer={planningForLabel}
|
||||
onChangeClick={() => revertTo(2)}
|
||||
/>
|
||||
</Collapse>
|
||||
<Collapse in={typeSelected && activeStep !== 3} timeout={250}>
|
||||
<CompletedRow question="Looking for" answer={typeSummary} onChangeClick={() => revertTo(3)} />
|
||||
<CompletedRow
|
||||
question="Looking for"
|
||||
answer={typeSummary}
|
||||
onChangeClick={() => revertTo(3)}
|
||||
/>
|
||||
</Collapse>
|
||||
<Collapse in={showServiceStep && serviceAnswered && activeStep !== 4} timeout={250}>
|
||||
<CompletedRow question="Service" answer={serviceLabel} onChangeClick={() => revertTo(4)} />
|
||||
<CompletedRow
|
||||
question="Service"
|
||||
answer={serviceLabel}
|
||||
onChangeClick={() => revertTo(4)}
|
||||
/>
|
||||
</Collapse>
|
||||
|
||||
{/* ── Step 1: Intent ─────────────────────────────────────── */}
|
||||
@@ -467,13 +503,22 @@ export const FuneralFinder = React.forwardRef<HTMLDivElement, FuneralFinderProps
|
||||
<Typography
|
||||
variant="caption"
|
||||
role="alert"
|
||||
sx={{ color: 'var(--fa-color-brand-600)', textAlign: 'center', display: 'block', mb: 1.5 }}
|
||||
sx={{
|
||||
color: 'var(--fa-color-brand-600)',
|
||||
textAlign: 'center',
|
||||
display: 'block',
|
||||
mb: 1.5,
|
||||
}}
|
||||
>
|
||||
Please let us know how we can help
|
||||
</Typography>
|
||||
)}
|
||||
<StepHeading>How can we help you today?</StepHeading>
|
||||
<Box role="radiogroup" aria-label="How can we help" sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
||||
<Box
|
||||
role="radiogroup"
|
||||
aria-label="How can we help"
|
||||
sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}
|
||||
>
|
||||
<ChoiceCard
|
||||
label="Arrange a funeral now"
|
||||
description="Someone has passed and I need to make arrangements"
|
||||
@@ -494,7 +539,11 @@ export const FuneralFinder = React.forwardRef<HTMLDivElement, FuneralFinderProps
|
||||
<Collapse in={activeStep === 2 && needsPlanningFor} timeout={250}>
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<StepHeading>Who are you planning for?</StepHeading>
|
||||
<Box role="radiogroup" aria-label="Who are you planning for" sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
||||
<Box
|
||||
role="radiogroup"
|
||||
aria-label="Who are you planning for"
|
||||
sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}
|
||||
>
|
||||
<ChoiceCard
|
||||
label="Myself"
|
||||
description="I want to plan my own funeral in advance"
|
||||
@@ -515,7 +564,11 @@ export const FuneralFinder = React.forwardRef<HTMLDivElement, FuneralFinderProps
|
||||
<Collapse in={activeStep === 3} timeout={250}>
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<StepHeading>What type of funeral are you considering?</StepHeading>
|
||||
<Box role="radiogroup" aria-label="Type of funeral" sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
||||
<Box
|
||||
role="radiogroup"
|
||||
aria-label="Type of funeral"
|
||||
sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}
|
||||
>
|
||||
{funeralTypes.map((ft) => (
|
||||
<TypeCard
|
||||
key={ft.id}
|
||||
@@ -543,11 +596,20 @@ export const FuneralFinder = React.forwardRef<HTMLDivElement, FuneralFinderProps
|
||||
<Typography variant="body2" component="span" sx={{ fontWeight: 600 }}>
|
||||
Any preferences?
|
||||
</Typography>
|
||||
<Typography variant="caption" component="span" color="text.secondary" sx={{ ml: 0.75 }}>
|
||||
<Typography
|
||||
variant="caption"
|
||||
component="span"
|
||||
color="text.secondary"
|
||||
sx={{ ml: 0.75 }}
|
||||
>
|
||||
(optional)
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box role="group" aria-label="Preferences" sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
|
||||
<Box
|
||||
role="group"
|
||||
aria-label="Preferences"
|
||||
sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}
|
||||
>
|
||||
{themeOptions.map((theme) => {
|
||||
const isSelected = selectedThemes.includes(theme.id);
|
||||
return (
|
||||
@@ -573,7 +635,11 @@ export const FuneralFinder = React.forwardRef<HTMLDivElement, FuneralFinderProps
|
||||
<Collapse in={activeStep === 4 && showServiceStep} timeout={250}>
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<StepHeading>Would you like a service?</StepHeading>
|
||||
<Box role="group" aria-label="Service preference" sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||
<Box
|
||||
role="group"
|
||||
aria-label="Service preference"
|
||||
sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}
|
||||
>
|
||||
{SERVICE_OPTIONS.map((opt) => (
|
||||
<Chip
|
||||
key={opt.value}
|
||||
@@ -583,7 +649,11 @@ export const FuneralFinder = React.forwardRef<HTMLDivElement, FuneralFinderProps
|
||||
onClick={() => selectService(opt.value)}
|
||||
clickable
|
||||
aria-pressed={serviceAnswered && servicePref === opt.value}
|
||||
sx={{ justifyContent: 'flex-start', height: 44, borderRadius: 'var(--fa-border-radius-md)' }}
|
||||
sx={{
|
||||
justifyContent: 'flex-start',
|
||||
height: 44,
|
||||
borderRadius: 'var(--fa-border-radius-md)',
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
@@ -41,12 +41,8 @@ export const BelowMasthead: Story = {
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Box sx={{ fontSize: '2rem', fontWeight: 700, mb: 1 }}>
|
||||
Funeral Arranger
|
||||
</Box>
|
||||
<Box sx={{ opacity: 0.8 }}>
|
||||
Find trusted funeral directors near you
|
||||
</Box>
|
||||
<Box sx={{ fontSize: '2rem', fontWeight: 700, mb: 1 }}>Funeral Arranger</Box>
|
||||
<Box sx={{ opacity: 0.8 }}>Find trusted funeral directors near you</Box>
|
||||
</Box>
|
||||
{/* Widget below masthead */}
|
||||
<Box sx={{ maxWidth: 560, mx: 'auto', mt: -4, px: 2, position: 'relative', zIndex: 1 }}>
|
||||
|
||||
@@ -103,7 +103,10 @@ function StepCircle({
|
||||
transition: 'background-color 200ms ease, color 200ms ease',
|
||||
...(usePrimary
|
||||
? { bgcolor: 'var(--fa-color-brand-500)', color: 'common.white' }
|
||||
: { bgcolor: 'var(--fa-color-brand-200, #EBDAC8)', color: 'var(--fa-color-brand-700, #8B4E0D)' }),
|
||||
: {
|
||||
bgcolor: 'var(--fa-color-brand-200, #EBDAC8)',
|
||||
color: 'var(--fa-color-brand-700, #8B4E0D)',
|
||||
}),
|
||||
// Connector line from bottom of this circle toward the next
|
||||
...(showConnector && {
|
||||
'&::after': {
|
||||
@@ -245,7 +248,8 @@ export const FuneralFinderV2 = React.forwardRef<HTMLDivElement, FuneralFinderV2P
|
||||
}
|
||||
: { lookingTo: false, planningFor: false, funeralType: false, location: false };
|
||||
|
||||
const hasErrors = submitted && (errs.lookingTo || errs.planningFor || errs.funeralType || errs.location);
|
||||
const hasErrors =
|
||||
submitted && (errs.lookingTo || errs.planningFor || errs.funeralType || errs.location);
|
||||
|
||||
// ─── Handlers ────────────────────────────────────────────────
|
||||
const handleLookingTo = (e: SelectChangeEvent<string>) => {
|
||||
@@ -311,11 +315,7 @@ export const FuneralFinderV2 = React.forwardRef<HTMLDivElement, FuneralFinderV2P
|
||||
>
|
||||
{heading}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="text.secondary"
|
||||
sx={{ textAlign: 'center', mb: 0 }}
|
||||
>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ textAlign: 'center', mb: 0 }}>
|
||||
{subheading}
|
||||
</Typography>
|
||||
<Divider sx={{ my: 3.5 }} />
|
||||
@@ -326,7 +326,10 @@ export const FuneralFinderV2 = React.forwardRef<HTMLDivElement, FuneralFinderV2P
|
||||
<Box sx={{ display: 'flex', gap: 2.5, alignItems: 'flex-end' }}>
|
||||
<StepCircle step={1} completed={!!lookingTo} active showConnector />
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="body1" sx={{ fontWeight: 600, mb: 1, color: 'var(--fa-color-brand-700)' }}>
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{ fontWeight: 600, mb: 1, color: 'var(--fa-color-brand-700)' }}
|
||||
>
|
||||
I’m looking to…
|
||||
</Typography>
|
||||
<Select
|
||||
@@ -352,7 +355,14 @@ export const FuneralFinderV2 = React.forwardRef<HTMLDivElement, FuneralFinderV2P
|
||||
<Box sx={{ display: 'flex', gap: 2.5, alignItems: 'flex-end' }}>
|
||||
<StepCircle step={2} completed={!!planningFor} showConnector />
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="body1" sx={{ fontWeight: 600, mb: 1, color: lookingTo ? 'var(--fa-color-brand-700)' : 'text.disabled' }}>
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
fontWeight: 600,
|
||||
mb: 1,
|
||||
color: lookingTo ? 'var(--fa-color-brand-700)' : 'text.disabled',
|
||||
}}
|
||||
>
|
||||
I’m planning for
|
||||
</Typography>
|
||||
<Select
|
||||
@@ -383,7 +393,14 @@ export const FuneralFinderV2 = React.forwardRef<HTMLDivElement, FuneralFinderV2P
|
||||
<Box sx={{ display: 'flex', gap: 2.5, alignItems: 'flex-end' }}>
|
||||
<StepCircle step={3} completed={!!funeralType} showConnector />
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="body1" sx={{ fontWeight: 600, mb: 1, color: step3Disabled ? 'text.disabled' : 'var(--fa-color-brand-700)' }}>
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
fontWeight: 600,
|
||||
mb: 1,
|
||||
color: step3Disabled ? 'text.disabled' : 'var(--fa-color-brand-700)',
|
||||
}}
|
||||
>
|
||||
Type of funeral
|
||||
</Typography>
|
||||
<Select
|
||||
@@ -410,7 +427,14 @@ export const FuneralFinderV2 = React.forwardRef<HTMLDivElement, FuneralFinderV2P
|
||||
<Box sx={{ display: 'flex', gap: 2.5, alignItems: 'flex-end' }}>
|
||||
<StepCircle step={4} completed={location.trim().length >= 3} />
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="body1" sx={{ fontWeight: 600, mb: 1, color: step4Disabled ? 'text.disabled' : 'var(--fa-color-brand-700)' }}>
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{
|
||||
fontWeight: 600,
|
||||
mb: 1,
|
||||
color: step4Disabled ? 'text.disabled' : 'var(--fa-color-brand-700)',
|
||||
}}
|
||||
>
|
||||
Looking for providers in
|
||||
</Typography>
|
||||
<Input
|
||||
|
||||
@@ -33,8 +33,7 @@ export const BelowMasthead: Story = {
|
||||
<Box>
|
||||
<Box
|
||||
sx={{
|
||||
background:
|
||||
'linear-gradient(160deg, #2C2E35 0%, #4C5B6B 60%, #6B3C13 100%)',
|
||||
background: 'linear-gradient(160deg, #2C2E35 0%, #4C5B6B 60%, #6B3C13 100%)',
|
||||
color: '#fff',
|
||||
py: 8,
|
||||
px: 4,
|
||||
|
||||
@@ -75,28 +75,15 @@ const FUNERAL_TYPE_OPTIONS: { value: FuneralType; label: string }[] = [
|
||||
|
||||
/** Hoisted outside component to avoid re-creation on render */
|
||||
const selectPlaceholder = (
|
||||
<span style={{ color: 'var(--fa-color-text-disabled)' }}>
|
||||
Select funeral type
|
||||
</span>
|
||||
<span style={{ color: 'var(--fa-color-text-disabled)' }}>Select funeral type</span>
|
||||
);
|
||||
|
||||
// ─── Sub-components ──────────────────────────────────────────────────────────
|
||||
|
||||
/** Uppercase section label — overline style */
|
||||
function SectionLabel({
|
||||
children,
|
||||
id,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
id?: string;
|
||||
}) {
|
||||
function SectionLabel({ children, id }: { children: React.ReactNode; id?: string }) {
|
||||
return (
|
||||
<Typography
|
||||
variant="overline"
|
||||
component="div"
|
||||
id={id}
|
||||
sx={{ color: 'text.secondary' }}
|
||||
>
|
||||
<Typography variant="overline" component="div" id={id} sx={{ color: 'text.secondary' }}>
|
||||
{children}
|
||||
</Typography>
|
||||
);
|
||||
@@ -138,8 +125,7 @@ const StatusCard = React.forwardRef<
|
||||
cursor: 'pointer',
|
||||
fontFamily: 'inherit',
|
||||
textAlign: 'center',
|
||||
transition:
|
||||
'border-color 200ms ease, background-color 200ms ease, transform 100ms ease',
|
||||
transition: 'border-color 200ms ease, background-color 200ms ease, transform 100ms ease',
|
||||
'&:hover': {
|
||||
borderColor: selected
|
||||
? 'var(--fa-color-border-brand, #BA834E)'
|
||||
@@ -164,9 +150,7 @@ const StatusCard = React.forwardRef<
|
||||
fontWeight: 600,
|
||||
display: 'block',
|
||||
mb: 0.75,
|
||||
color: selected
|
||||
? 'var(--fa-color-text-brand, #B0610F)'
|
||||
: 'text.primary',
|
||||
color: selected ? 'var(--fa-color-text-brand, #B0610F)' : 'text.primary',
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
@@ -253,16 +237,13 @@ const selectMenuProps = {
|
||||
* Required fields: status + location (min 3 chars).
|
||||
* Funeral type defaults to "show all" if not selected.
|
||||
*/
|
||||
export const FuneralFinderV3 = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
FuneralFinderV3Props
|
||||
>((props, ref) => {
|
||||
export const FuneralFinderV3 = React.forwardRef<HTMLDivElement, FuneralFinderV3Props>(
|
||||
(props, ref) => {
|
||||
const {
|
||||
onSearch,
|
||||
loading = false,
|
||||
heading = 'Find funeral directors near you',
|
||||
subheading =
|
||||
"Tell us what you need and we\u2019ll show options in your area.",
|
||||
subheading = 'Tell us what you need and we\u2019ll show options in your area.',
|
||||
sx,
|
||||
} = props;
|
||||
|
||||
@@ -309,18 +290,14 @@ export const FuneralFinderV3 = React.forwardRef<
|
||||
}, [location, errors.location]);
|
||||
|
||||
// ─── Radiogroup keyboard nav (WAI-ARIA pattern) ──────────
|
||||
const activeStatusIndex = status
|
||||
? STATUS_OPTIONS.findIndex((o) => o.key === status)
|
||||
: 0;
|
||||
const activeStatusIndex = status ? STATUS_OPTIONS.findIndex((o) => o.key === status) : 0;
|
||||
|
||||
const handleStatusKeyDown = (e: React.KeyboardEvent) => {
|
||||
const isNext = e.key === 'ArrowRight' || e.key === 'ArrowDown';
|
||||
const isPrev = e.key === 'ArrowLeft' || e.key === 'ArrowUp';
|
||||
if (!isNext && !isPrev) return;
|
||||
e.preventDefault();
|
||||
const current = cardRefs.current.indexOf(
|
||||
e.target as HTMLButtonElement,
|
||||
);
|
||||
const current = cardRefs.current.indexOf(e.target as HTMLButtonElement);
|
||||
if (current === -1) return;
|
||||
const next = isNext
|
||||
? Math.min(current + 1, STATUS_OPTIONS.length - 1)
|
||||
@@ -456,9 +433,7 @@ export const FuneralFinderV3 = React.forwardRef<
|
||||
onChange={handleFuneralType}
|
||||
displayEmpty
|
||||
renderValue={(v) =>
|
||||
v
|
||||
? FUNERAL_TYPE_OPTIONS.find((o) => o.value === v)?.label
|
||||
: selectPlaceholder
|
||||
v ? FUNERAL_TYPE_OPTIONS.find((o) => o.value === v)?.label : selectPlaceholder
|
||||
}
|
||||
MenuProps={selectMenuProps}
|
||||
sx={{
|
||||
@@ -563,7 +538,8 @@ export const FuneralFinderV3 = React.forwardRef<
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
FuneralFinderV3.displayName = 'FuneralFinderV3';
|
||||
export default FuneralFinderV3;
|
||||
|
||||
@@ -77,11 +77,7 @@ export const WithCTA: Story = {
|
||||
export const WithPageContent: Story = {
|
||||
render: () => (
|
||||
<Box>
|
||||
<Navigation
|
||||
logo={<FALogo />}
|
||||
items={defaultItems}
|
||||
ctaLabel="Start planning"
|
||||
/>
|
||||
<Navigation logo={<FALogo />} items={defaultItems} ctaLabel="Start planning" />
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: 'lg',
|
||||
@@ -94,8 +90,8 @@ export const WithPageContent: Story = {
|
||||
Find a funeral director
|
||||
</Typography>
|
||||
<Typography variant="body1" color="text.secondary" sx={{ mb: 4, maxWidth: 600 }}>
|
||||
Compare trusted funeral directors in your area. View services,
|
||||
pricing, and reviews to find the right support for your family.
|
||||
Compare trusted funeral directors in your area. View services, pricing, and reviews to
|
||||
find the right support for your family.
|
||||
</Typography>
|
||||
{Array.from({ length: 8 }).map((_, i) => (
|
||||
<Box
|
||||
|
||||
@@ -156,11 +156,7 @@ export const Navigation = React.forwardRef<HTMLDivElement, NavigationProps>(
|
||||
))}
|
||||
|
||||
{ctaLabel && (
|
||||
<Button
|
||||
variant="contained"
|
||||
size="medium"
|
||||
onClick={onCtaClick}
|
||||
>
|
||||
<Button variant="contained" size="medium" onClick={onCtaClick}>
|
||||
{ctaLabel}
|
||||
</Button>
|
||||
)}
|
||||
@@ -193,14 +189,8 @@ export const Navigation = React.forwardRef<HTMLDivElement, NavigationProps>(
|
||||
bgcolor: 'var(--fa-color-surface-subtle)',
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
{logo}
|
||||
</Box>
|
||||
<IconButton
|
||||
aria-label="Close menu"
|
||||
onClick={handleDrawerToggle}
|
||||
size="small"
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>{logo}</Box>
|
||||
<IconButton aria-label="Close menu" onClick={handleDrawerToggle} size="small">
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
@@ -10,44 +10,136 @@ import { Button } from '../../atoms/Button';
|
||||
import { Navigation } from '../Navigation';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
|
||||
const DEMO_IMAGE = 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=400&h=300&fit=crop';
|
||||
const DEMO_IMAGE =
|
||||
'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=400&h=300&fit=crop';
|
||||
|
||||
const essentials = [
|
||||
{ name: 'Accommodation', price: 1500, info: 'Refrigerated holding of the deceased prior to the funeral service.' },
|
||||
{ name: 'Death Registration Certificate', price: 1500, info: 'Lodgement of death registration with NSW Registry of Births, Deaths & Marriages.' },
|
||||
{ name: 'Doctor Fee for Cremation', price: 1500, info: 'Statutory medical referee fee required for all cremations in NSW.' },
|
||||
{ name: 'NSW Government Levy — Cremation', price: 1500, info: 'NSW Government cremation levy as set by the Department of Health.' },
|
||||
{ name: 'Professional Mortuary Care', price: 1500, info: 'Preparation and care of the deceased.' },
|
||||
{ name: 'Professional Service Fee', price: 1500, info: 'Coordination of all funeral arrangements and services.' },
|
||||
{ name: 'Allowance for Coffin', price: 1500, isAllowance: true, info: 'Allowance amount — upgrade options available during arrangement.' },
|
||||
{ name: 'Allowance for Crematorium', price: 1500, isAllowance: true, info: 'Allowance for crematorium fees — varies by location.' },
|
||||
{ name: 'Allowance for Hearse', price: 1500, isAllowance: true, info: 'Allowance for hearse transfer — distance surcharges may apply.' },
|
||||
{
|
||||
name: 'Accommodation',
|
||||
price: 1500,
|
||||
info: 'Refrigerated holding of the deceased prior to the funeral service.',
|
||||
},
|
||||
{
|
||||
name: 'Death Registration Certificate',
|
||||
price: 1500,
|
||||
info: 'Lodgement of death registration with NSW Registry of Births, Deaths & Marriages.',
|
||||
},
|
||||
{
|
||||
name: 'Doctor Fee for Cremation',
|
||||
price: 1500,
|
||||
info: 'Statutory medical referee fee required for all cremations in NSW.',
|
||||
},
|
||||
{
|
||||
name: 'NSW Government Levy — Cremation',
|
||||
price: 1500,
|
||||
info: 'NSW Government cremation levy as set by the Department of Health.',
|
||||
},
|
||||
{
|
||||
name: 'Professional Mortuary Care',
|
||||
price: 1500,
|
||||
info: 'Preparation and care of the deceased.',
|
||||
},
|
||||
{
|
||||
name: 'Professional Service Fee',
|
||||
price: 1500,
|
||||
info: 'Coordination of all funeral arrangements and services.',
|
||||
},
|
||||
{
|
||||
name: 'Allowance for Coffin',
|
||||
price: 1500,
|
||||
isAllowance: true,
|
||||
info: 'Allowance amount — upgrade options available during arrangement.',
|
||||
},
|
||||
{
|
||||
name: 'Allowance for Crematorium',
|
||||
price: 1500,
|
||||
isAllowance: true,
|
||||
info: 'Allowance for crematorium fees — varies by location.',
|
||||
},
|
||||
{
|
||||
name: 'Allowance for Hearse',
|
||||
price: 1500,
|
||||
isAllowance: true,
|
||||
info: 'Allowance for hearse transfer — distance surcharges may apply.',
|
||||
},
|
||||
];
|
||||
|
||||
const complimentary = [
|
||||
{ name: 'Dressing Fee', info: 'Dressing and preparation of the deceased — included at no charge.' },
|
||||
{
|
||||
name: 'Dressing Fee',
|
||||
info: 'Dressing and preparation of the deceased — included at no charge.',
|
||||
},
|
||||
{ name: 'Viewing Fee', info: 'One private family viewing — included at no charge.' },
|
||||
];
|
||||
|
||||
const extras = {
|
||||
heading: 'Extras',
|
||||
items: [
|
||||
{ name: 'Allowance for Flowers', price: 1500, isAllowance: true, info: 'Seasonal floral arrangements for the service.' },
|
||||
{ name: 'Allowance for Master of Ceremonies', price: 1500, isAllowance: true, info: 'Professional celebrant or MC for the funeral service.' },
|
||||
{ name: 'After Business Hours Service Surcharge', price: 1500, info: 'Additional fee for services held outside standard business hours.' },
|
||||
{ name: 'After Hours Prayers', price: 1500, info: 'Evening prayer service at the funeral home.' },
|
||||
{ name: 'Coffin Bearing by Funeral Directors', price: 1500, info: 'Professional pallbearing by funeral directors.' },
|
||||
{ name: 'Digital Recording', price: 1500, info: 'Professional video recording of the funeral service.' },
|
||||
{
|
||||
name: 'Allowance for Flowers',
|
||||
price: 1500,
|
||||
isAllowance: true,
|
||||
info: 'Seasonal floral arrangements for the service.',
|
||||
},
|
||||
{
|
||||
name: 'Allowance for Master of Ceremonies',
|
||||
price: 1500,
|
||||
isAllowance: true,
|
||||
info: 'Professional celebrant or MC for the funeral service.',
|
||||
},
|
||||
{
|
||||
name: 'After Business Hours Service Surcharge',
|
||||
price: 1500,
|
||||
info: 'Additional fee for services held outside standard business hours.',
|
||||
},
|
||||
{
|
||||
name: 'After Hours Prayers',
|
||||
price: 1500,
|
||||
info: 'Evening prayer service at the funeral home.',
|
||||
},
|
||||
{
|
||||
name: 'Coffin Bearing by Funeral Directors',
|
||||
price: 1500,
|
||||
info: 'Professional pallbearing by funeral directors.',
|
||||
},
|
||||
{
|
||||
name: 'Digital Recording',
|
||||
price: 1500,
|
||||
info: 'Professional video recording of the funeral service.',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const termsText = '* This package includes a funeral service at a chapel or a church with a funeral procession following to the crematorium. It includes many of the most commonly selected funeral options preselected for you. Many people choose this package for the extended funeral rituals — of course, you can tailor the funeral service to meet your needs and budget as you go through the selections.';
|
||||
const termsText =
|
||||
'* This package includes a funeral service at a chapel or a church with a funeral procession following to the crematorium. It includes many of the most commonly selected funeral options preselected for you. Many people choose this package for the extended funeral rituals — of course, you can tailor the funeral service to meet your needs and budget as you go through the selections.';
|
||||
|
||||
const packages = [
|
||||
{ id: 'everyday', name: 'Everyday Funeral Package', price: 900, description: 'Our most popular package with all essential services included. Suitable for a traditional chapel or church service.' },
|
||||
{ id: 'deluxe', name: 'Deluxe Funeral Package', price: 1200, description: 'An enhanced package with premium coffin and additional floral arrangements.' },
|
||||
{ id: 'essential', name: 'Essential Funeral Package', price: 600, description: 'A simple, dignified service covering all necessary arrangements.' },
|
||||
{ id: 'catholic', name: 'Catholic Service', price: 950, description: 'A service tailored for Catholic traditions including prayers and church ceremony.' },
|
||||
{
|
||||
id: 'everyday',
|
||||
name: 'Everyday Funeral Package',
|
||||
price: 900,
|
||||
description:
|
||||
'Our most popular package with all essential services included. Suitable for a traditional chapel or church service.',
|
||||
},
|
||||
{
|
||||
id: 'deluxe',
|
||||
name: 'Deluxe Funeral Package',
|
||||
price: 1200,
|
||||
description: 'An enhanced package with premium coffin and additional floral arrangements.',
|
||||
},
|
||||
{
|
||||
id: 'essential',
|
||||
name: 'Essential Funeral Package',
|
||||
price: 600,
|
||||
description: 'A simple, dignified service covering all necessary arrangements.',
|
||||
},
|
||||
{
|
||||
id: 'catholic',
|
||||
name: 'Catholic Service',
|
||||
price: 950,
|
||||
description:
|
||||
'A service tailored for Catholic traditions including prayers and church ceremony.',
|
||||
},
|
||||
];
|
||||
|
||||
const funeralTypes = ['All', 'Cremation', 'Burial', 'Memorial', 'Catholic', 'Direct Cremation'];
|
||||
@@ -101,9 +193,7 @@ export const CompareLoading: Story = {
|
||||
args: {
|
||||
name: 'Everyday Funeral Package',
|
||||
price: 900,
|
||||
sections: [
|
||||
{ heading: 'Essentials', items: essentials.slice(0, 4) },
|
||||
],
|
||||
sections: [{ heading: 'Essentials', items: essentials.slice(0, 4) }],
|
||||
total: 6000,
|
||||
onArrange: () => alert('Make Arrangement'),
|
||||
onCompare: () => {},
|
||||
|
||||
@@ -155,15 +155,14 @@ export const PackageDetail = React.forwardRef<HTMLDivElement, PackageDetailProps
|
||||
<Typography variant="h3" component="h2">
|
||||
{name}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{ mt: 0.5, color: 'primary.main', fontWeight: 600 }}
|
||||
>
|
||||
<Typography variant="h5" sx={{ mt: 0.5, color: 'primary.main', fontWeight: 600 }}>
|
||||
${price.toLocaleString('en-AU')}
|
||||
</Typography>
|
||||
|
||||
{/* CTA buttons */}
|
||||
<Box sx={{ display: 'flex', flexDirection: { xs: 'column', sm: 'row' }, gap: 1.5, mt: 2.5 }}>
|
||||
<Box
|
||||
sx={{ display: 'flex', flexDirection: { xs: 'column', sm: 'row' }, gap: 1.5, mt: 2.5 }}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
@@ -198,9 +197,7 @@ export const PackageDetail = React.forwardRef<HTMLDivElement, PackageDetailProps
|
||||
))}
|
||||
|
||||
{/* Total — separates included content from extras */}
|
||||
{total != null && (
|
||||
<LineItem name="Total" price={total} variant="total" />
|
||||
)}
|
||||
{total != null && <LineItem name="Total" price={total} variant="total" />}
|
||||
|
||||
{/* Extras — additional cost items after the total */}
|
||||
{extras && extras.items.length > 0 && (
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
export { PackageDetail, type PackageDetailProps, type PackageSection, type PackageLineItem } from './PackageDetail';
|
||||
export {
|
||||
PackageDetail,
|
||||
type PackageDetailProps,
|
||||
type PackageSection,
|
||||
type PackageLineItem,
|
||||
} from './PackageDetail';
|
||||
|
||||
@@ -37,10 +37,30 @@ const serviceTypes = [
|
||||
];
|
||||
|
||||
const coffinOptions = [
|
||||
{ id: 'eco', name: 'Eco Willow', price: 850, description: 'Handwoven natural willow. Biodegradable and sustainable.' },
|
||||
{ id: 'classic', name: 'Classic Maple', price: 1400, description: 'Solid maple with satin finish and brass handles.' },
|
||||
{ id: 'premium', name: 'Premium Oak', price: 2200, description: 'Quarter-sawn oak with high-gloss lacquer and gold-plated handles.' },
|
||||
{ id: 'simple', name: 'Simple Pine', price: 600, description: 'Unfinished pine. Can be personalised with paint, photos, or messages.' },
|
||||
{
|
||||
id: 'eco',
|
||||
name: 'Eco Willow',
|
||||
price: 850,
|
||||
description: 'Handwoven natural willow. Biodegradable and sustainable.',
|
||||
},
|
||||
{
|
||||
id: 'classic',
|
||||
name: 'Classic Maple',
|
||||
price: 1400,
|
||||
description: 'Solid maple with satin finish and brass handles.',
|
||||
},
|
||||
{
|
||||
id: 'premium',
|
||||
name: 'Premium Oak',
|
||||
price: 2200,
|
||||
description: 'Quarter-sawn oak with high-gloss lacquer and gold-plated handles.',
|
||||
},
|
||||
{
|
||||
id: 'simple',
|
||||
name: 'Simple Pine',
|
||||
price: 600,
|
||||
description: 'Unfinished pine. Can be personalised with paint, photos, or messages.',
|
||||
},
|
||||
];
|
||||
|
||||
const meta: Meta<typeof ServiceSelector> = {
|
||||
@@ -72,7 +92,7 @@ type Story = StoryObj<typeof ServiceSelector>;
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
heading: 'Choose a service type',
|
||||
subheading: 'Select the type of service you\'d like to arrange. Prices are starting estimates.',
|
||||
subheading: "Select the type of service you'd like to arrange. Prices are starting estimates.",
|
||||
items: serviceTypes,
|
||||
continueLabel: 'Continue',
|
||||
},
|
||||
@@ -180,7 +200,13 @@ export const WithDisabledOptions: Story = {
|
||||
const [selected, setSelected] = useState<string | undefined>();
|
||||
|
||||
const items = serviceTypes.map((item) =>
|
||||
item.id === 'memorial' ? { ...item, disabled: true, description: item.description + ' (Currently unavailable at this location.)' } : item,
|
||||
item.id === 'memorial'
|
||||
? {
|
||||
...item,
|
||||
disabled: true,
|
||||
description: item.description + ' (Currently unavailable at this location.)',
|
||||
}
|
||||
: item,
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -227,7 +253,11 @@ export const InArrangementFlow: Story = {
|
||||
maxDescriptionLines={2}
|
||||
/>
|
||||
|
||||
<Typography variant="captionSm" color="text.secondary" sx={{ mt: 3, textAlign: 'center', display: 'block' }}>
|
||||
<Typography
|
||||
variant="captionSm"
|
||||
color="text.secondary"
|
||||
sx={{ mt: 3, textAlign: 'center', display: 'block' }}
|
||||
>
|
||||
All prices are estimates and may vary based on your specific requirements.
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
@@ -92,13 +92,7 @@ export const ServiceSelector = React.forwardRef<HTMLDivElement, ServiceSelectorP
|
||||
const isContinueDisabled = continueDisabled ?? nothingSelected;
|
||||
|
||||
return (
|
||||
<Box
|
||||
ref={ref}
|
||||
sx={[
|
||||
{ width: '100%' },
|
||||
...(Array.isArray(sx) ? sx : [sx]),
|
||||
]}
|
||||
>
|
||||
<Box ref={ref} sx={[{ width: '100%' }, ...(Array.isArray(sx) ? sx : [sx])]}>
|
||||
{/* Header */}
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="h4" component="h2" sx={{ mb: subheading ? 1 : 0 }}>
|
||||
|
||||
@@ -8,7 +8,9 @@ const App = () => (
|
||||
<CssBaseline />
|
||||
<div style={{ padding: 32 }}>
|
||||
<h1>FA Design System</h1>
|
||||
<p>Run <code>npm run storybook</code> to view components.</p>
|
||||
<p>
|
||||
Run <code>npm run storybook</code> to view components.
|
||||
</p>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
@@ -16,5 +18,5 @@ const App = () => (
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
</React.StrictMode>,
|
||||
);
|
||||
|
||||
@@ -356,7 +356,8 @@ export const theme = createTheme({
|
||||
textTransform: 'none' as const,
|
||||
fontWeight: 600,
|
||||
letterSpacing: '0.02em',
|
||||
transition: 'background-color 150ms ease-in-out, border-color 150ms ease-in-out, box-shadow 150ms ease-in-out',
|
||||
transition:
|
||||
'background-color 150ms ease-in-out, border-color 150ms ease-in-out, box-shadow 150ms ease-in-out',
|
||||
'&:focus-visible': {
|
||||
outline: `2px solid ${t.ColorInteractiveFocus}`,
|
||||
outlineOffset: '2px',
|
||||
@@ -482,7 +483,8 @@ export const theme = createTheme({
|
||||
// Reserve 2px border on ALL cards (transparent for elevated, coloured for outlined).
|
||||
// Prevents layout shift when toggling selected state.
|
||||
border: '2px solid transparent',
|
||||
transition: 'box-shadow 150ms ease-in-out, border-color 150ms ease-in-out, background-color 150ms ease-in-out',
|
||||
transition:
|
||||
'box-shadow 150ms ease-in-out, border-color 150ms ease-in-out, background-color 150ms ease-in-out',
|
||||
},
|
||||
},
|
||||
variants: [
|
||||
@@ -512,7 +514,8 @@ export const theme = createTheme({
|
||||
transition: 'border-color 150ms ease-in-out',
|
||||
},
|
||||
// Hover — darker border (skip when focused, error, or disabled)
|
||||
'&:hover:not(.Mui-focused):not(.Mui-error):not(.Mui-disabled) .MuiOutlinedInput-notchedOutline': {
|
||||
'&:hover:not(.Mui-focused):not(.Mui-error):not(.Mui-disabled) .MuiOutlinedInput-notchedOutline':
|
||||
{
|
||||
borderColor: t.ColorNeutral400,
|
||||
},
|
||||
// Focus — brand gold border + double ring (white gap + brand ring)
|
||||
@@ -586,7 +589,8 @@ export const theme = createTheme({
|
||||
borderRadius: parseInt(t.ChipBorderRadiusDefault, 10),
|
||||
fontWeight: 500,
|
||||
letterSpacing: '0.01em',
|
||||
transition: 'background-color 150ms ease-in-out, border-color 150ms ease-in-out, box-shadow 150ms ease-in-out',
|
||||
transition:
|
||||
'background-color 150ms ease-in-out, border-color 150ms ease-in-out, box-shadow 150ms ease-in-out',
|
||||
'&:focus-visible': {
|
||||
outline: `2px solid ${t.ColorInteractiveFocus}`,
|
||||
outlineOffset: '2px',
|
||||
@@ -597,27 +601,39 @@ export const theme = createTheme({
|
||||
fontSize: t.ChipFontSizeMd,
|
||||
'& .MuiChip-label': { paddingLeft: t.ChipPaddingXMd, paddingRight: t.ChipPaddingXMd },
|
||||
'& .MuiChip-icon': { fontSize: t.ChipIconSizeMd, marginLeft: t.ChipPaddingXMd },
|
||||
'& .MuiChip-deleteIcon': { fontSize: t.ChipDeleteIconSizeMd, marginRight: t.ChipPaddingXMd },
|
||||
'& .MuiChip-deleteIcon': {
|
||||
fontSize: t.ChipDeleteIconSizeMd,
|
||||
marginRight: t.ChipPaddingXMd,
|
||||
},
|
||||
},
|
||||
sizeSmall: {
|
||||
height: parseInt(t.ChipHeightSm, 10),
|
||||
fontSize: t.ChipFontSizeSm,
|
||||
'& .MuiChip-label': { paddingLeft: t.ChipPaddingXMd, paddingRight: t.ChipPaddingXMd },
|
||||
'& .MuiChip-icon': { fontSize: t.ChipIconSizeSm, marginLeft: t.ChipPaddingXSm },
|
||||
'& .MuiChip-deleteIcon': { fontSize: t.ChipDeleteIconSizeSm, marginRight: t.ChipPaddingXSm },
|
||||
'& .MuiChip-deleteIcon': {
|
||||
fontSize: t.ChipDeleteIconSizeSm,
|
||||
marginRight: t.ChipPaddingXSm,
|
||||
},
|
||||
},
|
||||
filled: {
|
||||
'&.MuiChip-colorDefault': {
|
||||
backgroundColor: t.ColorNeutral200,
|
||||
color: t.ColorNeutral700,
|
||||
'&:hover': { backgroundColor: t.ColorNeutral300 },
|
||||
'& .MuiChip-deleteIcon': { color: t.ColorNeutral500, '&:hover': { color: t.ColorNeutral700 } },
|
||||
'& .MuiChip-deleteIcon': {
|
||||
color: t.ColorNeutral500,
|
||||
'&:hover': { color: t.ColorNeutral700 },
|
||||
},
|
||||
},
|
||||
'&.MuiChip-colorPrimary': {
|
||||
backgroundColor: t.ColorBrand200,
|
||||
color: t.ColorBrand700,
|
||||
'&:hover': { backgroundColor: t.ColorBrand300 },
|
||||
'& .MuiChip-deleteIcon': { color: t.ColorBrand400, '&:hover': { color: t.ColorBrand700 } },
|
||||
'& .MuiChip-deleteIcon': {
|
||||
color: t.ColorBrand400,
|
||||
'&:hover': { color: t.ColorBrand700 },
|
||||
},
|
||||
},
|
||||
},
|
||||
outlined: {
|
||||
@@ -625,13 +641,19 @@ export const theme = createTheme({
|
||||
borderColor: t.ColorNeutral300,
|
||||
color: t.ColorNeutral700,
|
||||
'&:hover': { backgroundColor: t.ColorNeutral100, borderColor: t.ColorNeutral400 },
|
||||
'& .MuiChip-deleteIcon': { color: t.ColorNeutral400, '&:hover': { color: t.ColorNeutral700 } },
|
||||
'& .MuiChip-deleteIcon': {
|
||||
color: t.ColorNeutral400,
|
||||
'&:hover': { color: t.ColorNeutral700 },
|
||||
},
|
||||
},
|
||||
'&.MuiChip-colorPrimary': {
|
||||
borderColor: t.ColorBrand400,
|
||||
color: t.ColorBrand700,
|
||||
'&:hover': { backgroundColor: t.ColorBrand100, borderColor: t.ColorBrand500 },
|
||||
'& .MuiChip-deleteIcon': { color: t.ColorBrand400, '&:hover': { color: t.ColorBrand700 } },
|
||||
'& .MuiChip-deleteIcon': {
|
||||
color: t.ColorBrand400,
|
||||
'&:hover': { color: t.ColorBrand700 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user