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