From 047d913960bd53aeff6eee431e3a4701748143cb Mon Sep 17 00:00:00 2001 From: Richie Date: Fri, 27 Mar 2026 16:42:16 +1100 Subject: [PATCH] format: Apply Prettier to existing codebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- src/components/atoms/Badge/Badge.stories.tsx | 150 ++++- .../atoms/Button/Button.stories.tsx | 128 +++- src/components/atoms/Button/Button.tsx | 8 +- src/components/atoms/Card/Card.stories.tsx | 35 +- src/components/atoms/Card/Card.tsx | 7 +- src/components/atoms/Chip/Chip.stories.tsx | 50 +- src/components/atoms/Chip/Chip.tsx | 22 +- .../atoms/Divider/Divider.stories.tsx | 12 +- .../atoms/IconButton/IconButton.stories.tsx | 37 +- src/components/atoms/Input/Input.stories.tsx | 69 +- src/components/atoms/Input/Input.tsx | 31 +- src/components/atoms/Link/Link.stories.tsx | 72 ++- src/components/atoms/Radio/Radio.stories.tsx | 49 +- .../atoms/Switch/Switch.stories.tsx | 71 ++- .../atoms/Typography/Typography.stories.tsx | 236 ++++--- .../AddOnOption/AddOnOption.stories.tsx | 14 +- .../molecules/AddOnOption/AddOnOption.tsx | 32 +- .../molecules/LineItem/LineItem.stories.tsx | 100 ++- .../molecules/LineItem/LineItem.tsx | 5 +- .../ProviderCard/ProviderCard.stories.tsx | 9 +- .../molecules/ProviderCard/ProviderCard.tsx | 29 +- .../ProviderCardCompact.stories.tsx | 3 +- .../ProviderCardCompact.tsx | 10 +- .../molecules/SearchBar/SearchBar.stories.tsx | 14 +- .../ServiceOption/ServiceOption.stories.tsx | 12 +- .../molecules/ServiceOption/ServiceOption.tsx | 27 +- .../StepIndicator/StepIndicator.stories.tsx | 10 +- .../molecules/VenueCard/VenueCard.stories.tsx | 19 +- .../molecules/VenueCard/VenueCard.tsx | 16 +- .../organisms/Footer/Footer.stories.tsx | 11 +- src/components/organisms/Footer/Footer.tsx | 43 +- .../FuneralFinder/FuneralFinder.stories.tsx | 20 +- .../organisms/FuneralFinder/FuneralFinder.tsx | 110 +++- .../FuneralFinder/FuneralFinderV2.stories.tsx | 8 +- .../FuneralFinder/FuneralFinderV2.tsx | 46 +- .../FuneralFinder/FuneralFinderV3.stories.tsx | 3 +- .../FuneralFinder/FuneralFinderV3.tsx | 588 +++++++++--------- .../Navigation/Navigation.stories.tsx | 10 +- .../organisms/Navigation/Navigation.tsx | 16 +- .../PackageDetail/PackageDetail.stories.tsx | 140 ++++- .../organisms/PackageDetail/PackageDetail.tsx | 13 +- .../organisms/PackageDetail/index.ts | 7 +- .../ServiceSelector.stories.tsx | 44 +- .../ServiceSelector/ServiceSelector.tsx | 8 +- src/main.tsx | 6 +- src/theme/index.ts | 46 +- 46 files changed, 1510 insertions(+), 886 deletions(-) diff --git a/src/components/atoms/Badge/Badge.stories.tsx b/src/components/atoms/Badge/Badge.stories.tsx index cc40d1a..5980dd7 100644 --- a/src/components/atoms/Badge/Badge.stories.tsx +++ b/src/components/atoms/Badge/Badge.stories.tsx @@ -78,12 +78,24 @@ export const AllColoursFilled: Story = { name: 'All Colours — Filled', render: () => (
- Default - Brand - Success - Warning - Error - Info + + Default + + + Brand + + + Success + + + Warning + + + Error + + + Info +
), }; @@ -95,11 +107,21 @@ export const WithIcons: Story = { name: 'With Icons', render: () => (
- }>Popular - }>Verified - }>Limited - }>Sold out - }>New + }> + Popular + + }> + Verified + + }> + Limited + + }> + Sold out + + }> + New +
), }; @@ -109,11 +131,21 @@ export const WithIconsFilled: Story = { name: 'With Icons — Filled', render: () => (
- }>Popular - }>Included - }>Attention - }>Unavailable - }>Updated + }> + Popular + + }> + Included + + }> + Attention + + }> + Unavailable + + }> + Updated +
), }; @@ -124,9 +156,15 @@ export const WithIconsFilled: Story = { export const Sizes: Story = { render: () => (
- }>Small - }>Medium - }>Large + }> + Small + + }> + Medium + + }> + Large +
), }; @@ -146,7 +184,9 @@ export const InPriceCard: Story = { Essential - Standard + + Standard + $3,200 @@ -162,7 +202,9 @@ export const InPriceCard: Story = { Premium - }>Most popular + }> + Most popular + $5,800 @@ -178,7 +220,9 @@ export const InPriceCard: Story = { Bespoke - }>Best value + }> + Best value + $8,500 @@ -202,11 +246,46 @@ export const ServiceStatus: Story = { render: () => (
{[ - { service: 'Chapel ceremony', badge: }>Confirmed }, - { service: 'Floral arrangements', badge: }>Pending }, - { service: 'Catering', badge: }>Unavailable }, - { service: 'Memorial printing', badge: }>New option }, - { service: 'Premium casket', badge: }>Included }, + { + service: 'Chapel ceremony', + badge: ( + }> + Confirmed + + ), + }, + { + service: 'Floral arrangements', + badge: ( + }> + Pending + + ), + }, + { + service: 'Catering', + badge: ( + }> + Unavailable + + ), + }, + { + service: 'Memorial printing', + badge: ( + }> + New option + + ), + }, + { + service: 'Premium casket', + badge: ( + }> + Included + + ), + }, ].map((item) => ( @@ -231,7 +310,14 @@ export const CompleteMatrix: Story = {
{(['soft', 'filled'] as const).map((variant) => (
-
+
{variant}
@@ -239,7 +325,13 @@ export const CompleteMatrix: Story = {
{size} {colors.map((color) => ( - }> + } + > {color} ))} diff --git a/src/components/atoms/Button/Button.stories.tsx b/src/components/atoms/Button/Button.stories.tsx index 83dc8b5..92c469e 100644 --- a/src/components/atoms/Button/Button.stories.tsx +++ b/src/components/atoms/Button/Button.stories.tsx @@ -87,7 +87,9 @@ export const FigmaMapping: Story = {
- +
), @@ -113,10 +115,18 @@ export const VariantsSecondary: Story = { name: 'Variants — Secondary', render: () => (
- - - - + + + +
), }; @@ -140,10 +150,18 @@ export const AllSizesSoft: Story = { name: 'All Sizes — Soft', render: () => (
- - - - + + + +
), }; @@ -180,10 +198,18 @@ export const IconsAllSizes: Story = { name: 'Icons — All Sizes', render: () => (
- - - - + + + +
), }; @@ -204,9 +230,15 @@ export const DisabledAllVariants: Story = { render: () => (
- - - + + +
), }; @@ -225,9 +257,15 @@ export const LoadingAllVariants: Story = { render: () => (
- - - + + +
), }; @@ -293,9 +331,15 @@ export const TextButtonComparison: Story = { render: () => (
- - - + + +
), }; @@ -305,10 +349,18 @@ export const TextButtonSizes: Story = { name: 'Text Buttons — All Sizes', render: () => (
- - - - + + + +
), }; @@ -348,15 +400,27 @@ export const CompleteMatrix: Story = {
{(['contained', 'soft', 'outlined', 'text'] as const).map((variant) => (
-
+
{variant}
- - - - - + + + + +
))} diff --git a/src/components/atoms/Button/Button.tsx b/src/components/atoms/Button/Button.tsx index 6612963..8559fdd 100644 --- a/src/components/atoms/Button/Button.tsx +++ b/src/components/atoms/Button/Button.tsx @@ -69,13 +69,7 @@ export const Button = React.forwardRef( {children} {loading && ( <> - + - A comprehensive service including chapel ceremony, transport, and - preparation. Suitable for families seeking a traditional farewell. + A comprehensive service including chapel ceremony, transport, and preparation. Suitable + for families seeking a traditional farewell. ), @@ -99,12 +99,7 @@ export const Variants: Story = { export const Interactive: Story = { render: () => (
- alert('Card clicked')} - > + alert('Card clicked')}> Elevated + Interactive @@ -305,11 +300,15 @@ export const OnDifferentBackgrounds: Story = {
Elevated - Shadow defines edges + + Shadow defines edges + Outlined - Border defines edges + + Border defines edges +
@@ -328,11 +327,15 @@ export const OnDifferentBackgrounds: Story = {
Elevated - White card + shadow on grey + + White card + shadow on grey + Outlined - Contrast + border on grey + + Contrast + border on grey +
@@ -395,8 +398,8 @@ export const PriceCardPreview: Story = { $3,200 - A respectful and simple service with chapel ceremony, transport, and - professional preparation. + A respectful and simple service with chapel ceremony, transport, and professional + preparation.
), @@ -222,20 +219,16 @@ export const SizeAlignment: Story = { render: () => (
- } - size="medium" - /> - + } size="medium" /> +
- } - size="small" - /> - + } size="small" /> +
), @@ -248,11 +241,7 @@ export const WithIcons: Story = { name: 'With Icons', render: () => (
- } - /> + } /> } type="tel" /> - } - type="number" - /> + } type="number" /> } endIcon={ - showSuccess ? : - showError ? : - undefined + showSuccess ? ( + + ) : showError ? ( + + ) : undefined } value={value} onChange={(e) => setValue(e.target.value)} error={showError} success={showSuccess} helperText={ - showError ? 'Please enter a valid email address' : - showSuccess ? 'Looks good!' : - 'Required for arrangement confirmation' + showError + ? 'Please enter a valid email address' + : showSuccess + ? 'Looks good!' + : 'Required for arrangement confirmation' } /> ); @@ -387,9 +375,7 @@ export const ArrangementForm: Story = { ], render: () => (
-
- Contact details -
+
Contact details
{(['medium', 'small'] as const).map((size) => (
-
+
Size: {size} ({size === 'medium' ? '48px' : '40px'})
diff --git a/src/components/atoms/Input/Input.tsx b/src/components/atoms/Input/Input.tsx index ed6ae76..3b99dee 100644 --- a/src/components/atoms/Input/Input.tsx +++ b/src/components/atoms/Input/Input.tsx @@ -77,11 +77,15 @@ export const Input = React.forwardRef( // Prefer convenience icon props; fall back to raw adornment props const resolvedStart = startIcon ? ( {startIcon} - ) : startAdornment; + ) : ( + startAdornment + ); const resolvedEnd = endIcon ? ( {endIcon} - ) : endAdornment; + ) : ( + endAdornment + ); return ( ( aria-describedby={helperId} sx={[ // Success border + focus ring (not a native MUI state) - success && !error && { - '& .MuiOutlinedInput-notchedOutline': { - borderColor: 'success.main', + success && + !error && { + '& .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]), ]} {...props} diff --git a/src/components/atoms/Link/Link.stories.tsx b/src/components/atoms/Link/Link.stories.tsx index 540f086..0a588e0 100644 --- a/src/components/atoms/Link/Link.stories.tsx +++ b/src/components/atoms/Link/Link.stories.tsx @@ -32,15 +32,21 @@ export const UnderlineVariants: Story = { render: () => ( - Hover (default) + + Hover (default) + underline="hover" - Always underlined + + Always underlined + underline="always" - No underline + + No underline + underline="none" @@ -58,13 +64,19 @@ export const ColourVariants: Story = { Brand link (default — copper, 4.8:1 contrast) - Secondary link (neutral grey) + + Secondary link (neutral grey) + - Primary text link (charcoal) + + Primary text link (charcoal) + - Error link (red — for destructive actions) + + Error link (red — for destructive actions) + ), @@ -76,12 +88,10 @@ export const ColourVariants: Story = { export const Inline: Story = { render: () => ( - If you need help planning a funeral, our{' '} - arrangement guide walks you through each - step. You can also browse our{' '} - provider directory to find local funeral - directors, or contact us directly for - personalised assistance. + If you need help planning a funeral, our arrangement guide walks you + through each step. You can also browse our provider directory to find + local funeral directors, or contact us directly for personalised + assistance. ), }; @@ -92,9 +102,15 @@ export const Inline: Story = { export const Navigation: Story = { render: () => ( - FAQ - Contact Us - Log In + + FAQ + + + Contact Us + + + Log In + ), }; @@ -106,10 +122,18 @@ export const FooterLinks: Story = { name: 'Footer Links', render: () => ( - Privacy Policy - Terms of Service - Accessibility - Cookie Settings + + Privacy Policy + + + Terms of Service + + + Accessibility + + + Cookie Settings + ), }; @@ -121,7 +145,15 @@ export const OnDifferentBackgrounds: Story = { name: 'On Different Backgrounds', render: () => ( - + White Learn more diff --git a/src/components/atoms/Radio/Radio.stories.tsx b/src/components/atoms/Radio/Radio.stories.tsx index e2fa186..d88139c 100644 --- a/src/components/atoms/Radio/Radio.stories.tsx +++ b/src/components/atoms/Radio/Radio.stories.tsx @@ -63,7 +63,9 @@ export const Group: Story = { name: 'Radio Group', render: () => ( - Service type + + Service type + } label="Chapel ceremony" /> } label="Graveside service" /> @@ -88,14 +90,31 @@ export const CardSelection: Story = { const [selected, setSelected] = useState('standard'); const options = [ - { value: 'direct', label: 'Direct cremation', desc: 'Simple, dignified cremation with no service.', price: '$1,800' }, - { value: 'standard', label: 'Standard service', desc: 'Traditional chapel ceremony with viewing.', price: '$4,200' }, - { value: 'premium', label: 'Premium service', desc: 'Full service with personalised memorial.', price: '$7,500' }, + { + value: 'direct', + label: 'Direct cremation', + desc: 'Simple, dignified cremation with no service.', + price: '$1,800', + }, + { + value: 'standard', + label: 'Standard service', + desc: 'Traditional chapel ceremony with viewing.', + price: '$4,200', + }, + { + value: 'premium', + label: 'Premium service', + desc: 'Full service with personalised memorial.', + price: '$7,500', + }, ]; return ( - Choose a package + + Choose a package + setSelected(e.target.value)}> {options.map((opt) => ( - + {opt.label} - {opt.price} + + {opt.price} + - {opt.desc} + + {opt.desc} + @@ -135,7 +164,9 @@ export const PaymentMethod: Story = { name: 'Interactive — Payment Method', render: () => ( - Payment method + + Payment method + } label="Credit card" /> } label="Bank transfer" /> diff --git a/src/components/atoms/Switch/Switch.stories.tsx b/src/components/atoms/Switch/Switch.stories.tsx index 6f16592..2e815e1 100644 --- a/src/components/atoms/Switch/Switch.stories.tsx +++ b/src/components/atoms/Switch/Switch.stories.tsx @@ -77,39 +77,84 @@ export const ServiceAddOns: Story = { }; const items = [ - { key: 'catering' as const, label: 'Catering', desc: 'Light refreshments after the service', price: '$450' }, - { key: 'flowers' as const, label: 'Floral arrangements', desc: 'Seasonal flowers for the chapel', price: '$280' }, - { key: 'music' as const, label: 'Live music', desc: 'Organist or solo musician', price: '$350' }, - { key: 'memorial' as const, label: 'Memorial video', desc: 'Photo slideshow with music', price: '$200' }, - { key: 'guestBook' as const, label: 'Guest book', desc: 'Leather-bound memorial guest book', price: '$85' }, + { + key: 'catering' as const, + label: 'Catering', + desc: 'Light refreshments after the service', + price: '$450', + }, + { + key: 'flowers' as const, + label: 'Floral arrangements', + desc: 'Seasonal flowers for the chapel', + price: '$280', + }, + { + key: 'music' as const, + label: 'Live music', + desc: 'Organist or solo musician', + price: '$350', + }, + { + key: 'memorial' as const, + label: 'Memorial video', + desc: 'Photo slideshow with music', + price: '$200', + }, + { + key: 'guestBook' as const, + label: 'Guest book', + desc: 'Leather-bound memorial guest book', + price: '$85', + }, ]; - const total = items.reduce((sum, item) => - addOns[item.key] ? sum + parseInt(item.price.replace('$', ''), 10) : sum, 0, + const total = items.reduce( + (sum, item) => (addOns[item.key] ? sum + parseInt(item.price.replace('$', ''), 10) : sum), + 0, ); return ( - Service add-ons + + Service add-ons + {items.map((item) => ( - + {item.label} - {item.desc} + + {item.desc} + - {item.price} + + {item.price} + toggle(item.key)} /> ))} - + Total add-ons - ${total} + + ${total} + ); diff --git a/src/components/atoms/Typography/Typography.stories.tsx b/src/components/atoms/Typography/Typography.stories.tsx index 8d4cd99..ebdc962 100644 --- a/src/components/atoms/Typography/Typography.stories.tsx +++ b/src/components/atoms/Typography/Typography.stories.tsx @@ -16,22 +16,35 @@ const meta: Meta = { variant: { control: 'select', options: [ - 'displayHero', 'display1', 'display2', 'display3', 'displaySm', - 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', - 'bodyLg', 'body1', 'body2', 'bodyXs', - 'labelLg', 'label', 'labelSm', - 'caption', 'captionSm', - 'overline', 'overlineSm', + 'displayHero', + 'display1', + 'display2', + 'display3', + 'displaySm', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'bodyLg', + 'body1', + 'body2', + 'bodyXs', + 'labelLg', + 'label', + 'labelSm', + 'caption', + 'captionSm', + 'overline', + 'overlineSm', ], description: 'Typography variant — 21 variants across 6 categories', table: { defaultValue: { summary: 'body1' } }, }, color: { control: 'select', - options: [ - 'textPrimary', 'textSecondary', 'textDisabled', - 'primary', 'secondary', 'error', - ], + options: ['textPrimary', 'textSecondary', 'textDisabled', 'primary', 'secondary', 'error'], }, maxLines: { control: 'number' }, gutterBottom: { control: 'boolean' }, @@ -47,7 +60,8 @@ const SAMPLE = 'Discover, Explore, and Plan Funerals in Minutes, Not Hours'; export const Default: Story = { args: { - children: 'Funeral Arranger helps families find transparent, affordable funeral services across Australia.', + children: + 'Funeral Arranger helps families find transparent, affordable funeral services across Australia.', }, }; @@ -59,23 +73,33 @@ export const Display: Story = { render: () => (
- displayHero — 80px + + displayHero — 80px + {SAMPLE}
- display1 — 64px + + display1 — 64px + {SAMPLE}
- display2 — 52px + + display2 — 52px + {SAMPLE}
- display3 — 40px + + display3 — 40px + {SAMPLE}
- displaySm — 32px + + displaySm — 32px + {SAMPLE}
@@ -90,27 +114,39 @@ export const Headings: Story = { render: () => (
- h1 — 36px + + h1 — 36px + {SAMPLE}
- h2 — 30px + + h2 — 30px + {SAMPLE}
- h3 — 24px + + h3 — 24px + {SAMPLE}
- h4 — 20px + + h4 — 20px + {SAMPLE}
- h5 — 18px + + h5 — 18px + {SAMPLE}
- h6 — 16px + + h6 — 16px + {SAMPLE}
@@ -125,31 +161,39 @@ export const Body: Story = { render: () => (
- bodyLg — 18px + + bodyLg — 18px + - Planning a funeral is one of the most difficult tasks a family faces. Funeral Arranger - is here to help you navigate this process with care and transparency. + Planning a funeral is one of the most difficult tasks a family faces. Funeral Arranger is + here to help you navigate this process with care and transparency.
- body1 (default) — 16px + + body1 (default) — 16px + Compare funeral directors in your area, view transparent pricing, and make informed decisions at your own pace. Every family deserves clarity during this time.
- body2 (small) — 14px + + body2 (small) — 14px + - Prices shown are indicative and may vary based on your specific requirements. - Contact the funeral director directly for a detailed quote. + Prices shown are indicative and may vary based on your specific requirements. Contact the + funeral director directly for a detailed quote.
- bodyXs — 12px + + bodyXs — 12px + - Terms and conditions apply. Funeral Arranger is a comparison service and does not - directly provide funeral services. ABN 12 345 678 901. + Terms and conditions apply. Funeral Arranger is a comparison service and does not directly + provide funeral services. ABN 12 345 678 901.
@@ -164,32 +208,60 @@ export const UIText: Story = { render: () => (
- labelLg — 16px medium - Form label or section label + + labelLg — 16px medium + + + Form label or section label +
- label — 14px medium - Default form label + + label — 14px medium + + + Default form label +
- labelSm — 12px medium - Compact label or tag text + + labelSm — 12px medium + + + Compact label or tag text +
- caption — 12px regular - Fine print, timestamps, metadata + + caption — 12px regular + + + Fine print, timestamps, metadata +
- captionSm — 11px regular - Compact metadata, footnotes + + captionSm — 11px regular + + + Compact metadata, footnotes +
- overline — 12px semibold uppercase - Section overline + + overline — 12px semibold uppercase + + + Section overline +
- overlineSm — 11px semibold uppercase - Compact overline + + overlineSm — 11px semibold uppercase + + + Compact overline +
), @@ -222,20 +294,25 @@ export const FontFamilies: Story = { render: () => (
- Display font — Noto Serif SC (Regular 400) - - Warm, trustworthy, and professional + + Display font — Noto Serif SC (Regular 400) + Warm, trustworthy, and professional - 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.
- Body font — Montserrat - Clean, modern, and highly readable + + Body font — Montserrat + + + Clean, modern, and highly readable + - Used for all headings (h1–h6), body text, labels, captions, and UI elements. - Headings use Bold (700), body uses Medium (500), captions use Regular (400). + Used for all headings (h1–h6), body text, labels, captions, and UI elements. Headings use + Bold (700), body uses Medium (500), captions use Regular (400).
@@ -249,18 +326,21 @@ export const MaxLines: Story = { render: () => (
- maxLines=1 + + maxLines=1 + - H. Parsons Funeral Directors — trusted by Australian families for over 30 years, - providing compassionate and transparent funeral services. + H. Parsons Funeral Directors — trusted by Australian families for over 30 years, providing + compassionate and transparent funeral services.
- maxLines=2 + + maxLines=2 + - H. Parsons Funeral Directors — trusted by Australian families for over 30 years, - providing compassionate and transparent funeral services across metropolitan - and regional areas. + H. Parsons Funeral Directors — trusted by Australian families for over 30 years, providing + compassionate and transparent funeral services across metropolitan and regional areas.
@@ -276,23 +356,27 @@ export const RealisticContent: Story = { Funeral planning Compare funeral services in your area - Transparent pricing and service comparison to help you make informed - decisions during a difficult time. + Transparent pricing and service comparison to help you make informed decisions during a + difficult time. - 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. + + How it works - Step 1: Browse packages - Compare packages side by side. Each package clearly shows what is and - isn't included, so there are no surprises. + Enter your suburb or postcode to find funeral directors near you. Each listing includes a + full price breakdown, service inclusions, and reviews from families who have used their + services. + + + Step 1: Browse packages + + + Compare packages side by side. Each package clearly shows what is and isn't included, so + there are no surprises. - Prices are indicative and current as of March 2026. Contact the funeral - director for a binding quote. + Prices are indicative and current as of March 2026. Contact the funeral director for a + binding quote.
), @@ -333,7 +417,11 @@ export const CompleteScale: Story = {
{variants.map(({ variant, label }) => (
- + {label} {SAMPLE} diff --git a/src/components/molecules/AddOnOption/AddOnOption.stories.tsx b/src/components/molecules/AddOnOption/AddOnOption.stories.tsx index 4183714..aa67d85 100644 --- a/src/components/molecules/AddOnOption/AddOnOption.stories.tsx +++ b/src/components/molecules/AddOnOption/AddOnOption.stories.tsx @@ -150,7 +150,8 @@ export const ServiceAddOns: Story = { export const WithoutPrice: Story = { args: { name: 'Order of service booklet', - description: 'Complimentary printed booklet with the service programme and a photo of your loved one.', + description: + 'Complimentary printed booklet with the service programme and a photo of your loved one.', }, }; @@ -160,13 +161,7 @@ export const WithoutPrice: Story = { export const WithoutDescription: Story = { render: function Render() { const [checked, setChecked] = React.useState(false); - return ( - - ); + return ; }, }; @@ -176,7 +171,8 @@ export const WithoutDescription: Story = { export const Disabled: Story = { args: { name: 'Catering', - description: 'Not available at this venue. Please contact the venue directly for catering options.', + description: + 'Not available at this venue. Please contact the venue directly for catering options.', price: 1200, disabled: true, }, diff --git a/src/components/molecules/AddOnOption/AddOnOption.tsx b/src/components/molecules/AddOnOption/AddOnOption.tsx index 0d28088..2b0940c 100644 --- a/src/components/molecules/AddOnOption/AddOnOption.tsx +++ b/src/components/molecules/AddOnOption/AddOnOption.tsx @@ -54,7 +54,19 @@ export interface AddOnOptionProps { * ``` */ export const AddOnOption = React.forwardRef( - ({ name, description, price, checked = false, onChange, disabled = false, maxDescriptionLines, sx }, ref) => { + ( + { + name, + description, + price, + checked = false, + onChange, + disabled = false, + maxDescriptionLines, + sx, + }, + ref, + ) => { const switchId = React.useId(); const [expanded, setExpanded] = React.useState(false); const [isClamped, setIsClamped] = React.useState(false); @@ -129,10 +141,7 @@ export const AddOnOption = React.forwardRef( {/* Price — tucks directly under heading */} {price != null && ( - + ${price.toLocaleString('en-AU')} )} @@ -146,12 +155,13 @@ export const AddOnOption = React.forwardRef( color="text.secondary" sx={{ mt: 0.5, - ...(maxDescriptionLines && !expanded && { - display: '-webkit-box', - WebkitLineClamp: maxDescriptionLines, - WebkitBoxOrient: 'vertical', - overflow: 'hidden', - }), + ...(maxDescriptionLines && + !expanded && { + display: '-webkit-box', + WebkitLineClamp: maxDescriptionLines, + WebkitBoxOrient: 'vertical', + overflow: 'hidden', + }), }} > {description} diff --git a/src/components/molecules/LineItem/LineItem.stories.tsx b/src/components/molecules/LineItem/LineItem.stories.tsx index 7fb3e70..e9623b6 100644 --- a/src/components/molecules/LineItem/LineItem.stories.tsx +++ b/src/components/molecules/LineItem/LineItem.stories.tsx @@ -88,15 +88,54 @@ export const PackageContents: Story = { Essentials - - - - - - - - - + + + + + + + + + @@ -105,7 +144,10 @@ export const PackageContents: Story = { Complimentary Items - + @@ -117,12 +159,38 @@ export const PackageContents: Story = { Extras - - - - - - + + + + + + ), diff --git a/src/components/molecules/LineItem/LineItem.tsx b/src/components/molecules/LineItem/LineItem.tsx index 302c62e..432fc29 100644 --- a/src/components/molecules/LineItem/LineItem.tsx +++ b/src/components/molecules/LineItem/LineItem.tsx @@ -48,8 +48,9 @@ export const LineItem = React.forwardRef( ({ name, info, price, isAllowance = false, priceLabel, variant = 'default', sx }, ref) => { const isTotal = variant === 'total'; - const formattedPrice = priceLabel - ?? (price != null ? `$${price.toLocaleString('en-AU')}${isAllowance ? '*' : ''}` : undefined); + const formattedPrice = + priceLabel ?? + (price != null ? `$${price.toLocaleString('en-AU')}${isAllowance ? '*' : ''}` : undefined); return ( {}} /> {/* Minimal — just name and location */} - {}} - /> + {}} /> ), }; diff --git a/src/components/molecules/ProviderCard/ProviderCard.tsx b/src/components/molecules/ProviderCard/ProviderCard.tsx index f47607e..bfa8093 100644 --- a/src/components/molecules/ProviderCard/ProviderCard.tsx +++ b/src/components/molecules/ProviderCard/ProviderCard.tsx @@ -144,12 +144,7 @@ export const ProviderCard = React.forwardRef( > {/* Verified badge */} - } - > + }> Verified @@ -219,9 +214,7 @@ export const ProviderCard = React.forwardRef( > {/* Location */} - + {location} @@ -233,10 +226,7 @@ export const ProviderCard = React.forwardRef( sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }} aria-label={`Rated ${rating} out of 5${reviewCount != null ? `, ${reviewCount} reviews` : ''}`} > - + {rating} {reviewCount != null && ` (${reviewCount.toLocaleString('en-AU')})`} @@ -249,17 +239,8 @@ export const ProviderCard = React.forwardRef( {capabilityLabel && ( {capabilityDescription ? ( - - + + {capabilityLabel} diff --git a/src/components/molecules/ProviderCardCompact/ProviderCardCompact.stories.tsx b/src/components/molecules/ProviderCardCompact/ProviderCardCompact.stories.tsx index bdfa271..3930453 100644 --- a/src/components/molecules/ProviderCardCompact/ProviderCardCompact.stories.tsx +++ b/src/components/molecules/ProviderCardCompact/ProviderCardCompact.stories.tsx @@ -2,7 +2,8 @@ import type { Meta, StoryObj } from '@storybook/react'; import Box from '@mui/material/Box'; import { ProviderCardCompact } from './ProviderCardCompact'; -const DEMO_IMAGE = 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=400&h=300&fit=crop'; +const DEMO_IMAGE = + 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=400&h=300&fit=crop'; const meta: Meta = { title: 'Molecules/ProviderCardCompact', diff --git a/src/components/molecules/ProviderCardCompact/ProviderCardCompact.tsx b/src/components/molecules/ProviderCardCompact/ProviderCardCompact.tsx index 8806380..923f017 100644 --- a/src/components/molecules/ProviderCardCompact/ProviderCardCompact.tsx +++ b/src/components/molecules/ProviderCardCompact/ProviderCardCompact.tsx @@ -101,10 +101,7 @@ export const ProviderCardCompact = React.forwardRef - + {location} @@ -118,7 +115,10 @@ export const ProviderCardCompact = React.forwardRef - {rating} Rating{reviewCount != null ? ` (${reviewCount} ${reviewCount === 1 ? 'Review' : 'Reviews'})` : ''} + {rating} Rating + {reviewCount != null + ? ` (${reviewCount} ${reviewCount === 1 ? 'Review' : 'Reviews'})` + : ''} )} diff --git a/src/components/molecules/SearchBar/SearchBar.stories.tsx b/src/components/molecules/SearchBar/SearchBar.stories.tsx index 956f9b9..af015ea 100644 --- a/src/components/molecules/SearchBar/SearchBar.stories.tsx +++ b/src/components/molecules/SearchBar/SearchBar.stories.tsx @@ -149,13 +149,7 @@ export const Loading: Story = { const [value, setValue] = React.useState('Parsons funeral'); return ( - + ); }, }; @@ -200,11 +194,7 @@ export const ProviderSearch: Story = { setResults([]); return; } - setResults( - providers.filter((p) => - p.toLowerCase().includes(query.toLowerCase()), - ), - ); + setResults(providers.filter((p) => p.toLowerCase().includes(query.toLowerCase()))); }; return ( diff --git a/src/components/molecules/ServiceOption/ServiceOption.stories.tsx b/src/components/molecules/ServiceOption/ServiceOption.stories.tsx index 8d9f6c5..038de86 100644 --- a/src/components/molecules/ServiceOption/ServiceOption.stories.tsx +++ b/src/components/molecules/ServiceOption/ServiceOption.stories.tsx @@ -252,17 +252,9 @@ export const EdgeCases: Story = { onClick={() => {}} /> {/* No description */} - {}} - /> + {}} /> {/* No price, no description */} - {}} - /> + {}} /> ), }; diff --git a/src/components/molecules/ServiceOption/ServiceOption.tsx b/src/components/molecules/ServiceOption/ServiceOption.tsx index 3bce4a8..f184e84 100644 --- a/src/components/molecules/ServiceOption/ServiceOption.tsx +++ b/src/components/molecules/ServiceOption/ServiceOption.tsx @@ -55,7 +55,19 @@ export interface ServiceOptionProps { * ``` */ export const ServiceOption = React.forwardRef( - ({ name, description, price, selected = false, disabled = false, onClick, maxDescriptionLines, sx }, ref) => { + ( + { + name, + description, + price, + selected = false, + disabled = false, + onClick, + maxDescriptionLines, + sx, + }, + ref, + ) => { const [expanded, setExpanded] = React.useState(false); const [isClamped, setIsClamped] = React.useState(false); const descRef = React.useRef(null); @@ -123,12 +135,13 @@ export const ServiceOption = React.forwardRef {description} diff --git a/src/components/molecules/StepIndicator/StepIndicator.stories.tsx b/src/components/molecules/StepIndicator/StepIndicator.stories.tsx index f09c845..b375a10 100644 --- a/src/components/molecules/StepIndicator/StepIndicator.stories.tsx +++ b/src/components/molecules/StepIndicator/StepIndicator.stories.tsx @@ -106,7 +106,15 @@ export const Interactive: Story = { - + {arrangementSteps[step].label} diff --git a/src/components/molecules/VenueCard/VenueCard.stories.tsx b/src/components/molecules/VenueCard/VenueCard.stories.tsx index 3f0af08..c49e373 100644 --- a/src/components/molecules/VenueCard/VenueCard.stories.tsx +++ b/src/components/molecules/VenueCard/VenueCard.stories.tsx @@ -161,12 +161,7 @@ export const EdgeCases: Story = { onClick={() => {}} /> {/* Minimal — just name, image, location */} - {}} - /> + {}} /> ), }; @@ -186,9 +181,7 @@ export const Responsive: Story = { {[280, 340, 420].map((width) => ( - - {width}px - + {width}px ( - - White surface - + White surface - - Grey surface (neutral.50) - + Grey surface (neutral.50) ( > {/* Location */} - + {location} @@ -126,10 +124,7 @@ export const VenueCard = React.forwardRef( sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }} aria-label={`Capacity: ${capacity} guests`} > - + {capacity} guests @@ -143,12 +138,7 @@ export const VenueCard = React.forwardRef( From - + ${price.toLocaleString('en-AU')} diff --git a/src/components/organisms/Footer/Footer.stories.tsx b/src/components/organisms/Footer/Footer.stories.tsx index 02e6eaa..78af0cf 100644 --- a/src/components/organisms/Footer/Footer.stories.tsx +++ b/src/components/organisms/Footer/Footer.stories.tsx @@ -15,12 +15,7 @@ const FALogoInverse = () => ( ); const FALogoNav = () => ( - + ); const defaultLinkGroups = [ @@ -169,8 +164,8 @@ export const FullPage: Story = { Find a funeral director - Compare trusted funeral directors in your area. View services, - pricing, and reviews to find the right support for your family. + Compare trusted funeral directors in your area. View services, pricing, and reviews to + find the right support for your family. {Array.from({ length: 4 }).map((_, i) => ( ( - ( - { - logo, - tagline, - linkGroups = [], - phone, - email, - copyright, - legalLinks = [], - sx, - }, - ref, - ) => { + ({ logo, tagline, linkGroups = [], phone, email, copyright, legalLinks = [], sx }, ref) => { const year = new Date().getFullYear(); const copyrightText = copyright || `\u00A9 ${year} Funeral Arranger. All rights reserved.`; @@ -143,10 +131,7 @@ export const Footer = React.forwardRef( Email - + {email} @@ -157,7 +142,15 @@ export const Footer = React.forwardRef( {/* Link group columns */} {linkGroups.map((group) => ( - + ( {group.links.map((link) => (
  • @@ -206,10 +206,7 @@ export const Footer = React.forwardRef( py: 3, }} > - + {copyrightText} diff --git a/src/components/organisms/FuneralFinder/FuneralFinder.stories.tsx b/src/components/organisms/FuneralFinder/FuneralFinder.stories.tsx index 7d9d4fa..23b7eb6 100644 --- a/src/components/organisms/FuneralFinder/FuneralFinder.stories.tsx +++ b/src/components/organisms/FuneralFinder/FuneralFinder.stories.tsx @@ -9,7 +9,12 @@ import { Typography } from '../../atoms/Typography'; const funeralTypes = [ { id: 'cremation', label: 'Cremation', hasServiceOption: true }, { id: 'burial', label: 'Burial', hasServiceOption: true }, - { id: 'water-burial', label: 'Water Burial', note: 'Available in QLD only', hasServiceOption: false }, + { + id: 'water-burial', + label: 'Water Burial', + note: 'Available in QLD only', + hasServiceOption: false, + }, ]; const themeOptions = [ @@ -152,8 +157,8 @@ export const InHeroDesktop: Story = { color="text.secondary" sx={{ textAlign: 'center', mb: 4, maxWidth: 440, mx: 'auto' }} > - Whether you're thinking ahead or arranging for a loved one, find - trusted local providers with transparent pricing. + Whether you're thinking ahead or arranging for a loved one, find trusted local + providers with transparent pricing. - + Discover, Explore, and Plan Funerals in Minutes @@ -210,7 +219,8 @@ export const InHeroMobile: Story = { sx={{ height: 180, bgcolor: 'var(--fa-color-brand-200)', - backgroundImage: 'url(https://images.unsplash.com/photo-1516733968668-dbdce39c0571?w=800&h=400&fit=crop)', + backgroundImage: + 'url(https://images.unsplash.com/photo-1516733968668-dbdce39c0571?w=800&h=400&fit=crop)', backgroundSize: 'cover', backgroundPosition: 'center', }} diff --git a/src/components/organisms/FuneralFinder/FuneralFinder.tsx b/src/components/organisms/FuneralFinder/FuneralFinder.tsx index 80e2fb6..31e4e81 100644 --- a/src/components/organisms/FuneralFinder/FuneralFinder.tsx +++ b/src/components/organisms/FuneralFinder/FuneralFinder.tsx @@ -146,7 +146,11 @@ function ChoiceCard({ {description && ( - + {description} )} @@ -214,12 +218,20 @@ function TypeCard({ {description && ( - + {description} )} {note && ( - + {note} )} @@ -261,7 +273,13 @@ function CompletedRow({ onClick={onChangeClick} underline="hover" aria-label={`Change ${question.toLowerCase()}`} - sx={{ color: 'text.secondary', ml: 'auto', minHeight: 44, display: 'inline-flex', alignItems: 'center' }} + sx={{ + color: 'text.secondary', + ml: 'auto', + minHeight: 44, + display: 'inline-flex', + alignItems: 'center', + }} > Change @@ -348,9 +366,11 @@ export const FuneralFinder = React.forwardRef { @@ -409,7 +429,7 @@ export const FuneralFinder = React.forwardRef - revertTo(1)} /> + revertTo(1)} + /> - revertTo(2)} /> + revertTo(2)} + /> - revertTo(3)} /> + revertTo(3)} + /> - revertTo(4)} /> + revertTo(4)} + /> {/* ── Step 1: Intent ─────────────────────────────────────── */} @@ -467,13 +503,22 @@ export const FuneralFinder = React.forwardRef Please let us know how we can help )} How can we help you today? - + Who are you planning for? - + What type of funeral are you considering? - + {funeralTypes.map((ft) => ( Any preferences? - + (optional) - + {themeOptions.map((theme) => { const isSelected = selectedThemes.includes(theme.id); return ( @@ -573,7 +635,11 @@ export const FuneralFinder = React.forwardRef Would you like a service? - + {SERVICE_OPTIONS.map((opt) => ( selectService(opt.value)} clickable aria-pressed={serviceAnswered && servicePref === opt.value} - sx={{ justifyContent: 'flex-start', height: 44, borderRadius: 'var(--fa-border-radius-md)' }} + sx={{ + justifyContent: 'flex-start', + height: 44, + borderRadius: 'var(--fa-border-radius-md)', + }} /> ))} diff --git a/src/components/organisms/FuneralFinder/FuneralFinderV2.stories.tsx b/src/components/organisms/FuneralFinder/FuneralFinderV2.stories.tsx index c4dc175..7e71595 100644 --- a/src/components/organisms/FuneralFinder/FuneralFinderV2.stories.tsx +++ b/src/components/organisms/FuneralFinder/FuneralFinderV2.stories.tsx @@ -41,12 +41,8 @@ export const BelowMasthead: Story = { textAlign: 'center', }} > - - Funeral Arranger - - - Find trusted funeral directors near you - + Funeral Arranger + Find trusted funeral directors near you {/* Widget below masthead */} diff --git a/src/components/organisms/FuneralFinder/FuneralFinderV2.tsx b/src/components/organisms/FuneralFinder/FuneralFinderV2.tsx index daedde2..7622e5c 100644 --- a/src/components/organisms/FuneralFinder/FuneralFinderV2.tsx +++ b/src/components/organisms/FuneralFinder/FuneralFinderV2.tsx @@ -103,7 +103,10 @@ function StepCircle({ transition: 'background-color 200ms ease, color 200ms ease', ...(usePrimary ? { bgcolor: 'var(--fa-color-brand-500)', color: 'common.white' } - : { bgcolor: 'var(--fa-color-brand-200, #EBDAC8)', color: 'var(--fa-color-brand-700, #8B4E0D)' }), + : { + bgcolor: 'var(--fa-color-brand-200, #EBDAC8)', + color: 'var(--fa-color-brand-700, #8B4E0D)', + }), // Connector line from bottom of this circle toward the next ...(showConnector && { '&::after': { @@ -245,7 +248,8 @@ export const FuneralFinderV2 = React.forwardRef) => { @@ -311,11 +315,7 @@ export const FuneralFinderV2 = React.forwardRef {heading} - + {subheading} @@ -326,7 +326,10 @@ export const FuneralFinderV2 = React.forwardRef - + I’m looking to… - + Type of funeral - Select funeral type - + Select funeral type ); // ─── Sub-components ────────────────────────────────────────────────────────── /** Uppercase section label — overline style */ -function SectionLabel({ - children, - id, -}: { - children: React.ReactNode; - id?: string; -}) { +function SectionLabel({ children, id }: { children: React.ReactNode; id?: string }) { return ( - + {children} ); @@ -138,8 +125,7 @@ const StatusCard = React.forwardRef< cursor: 'pointer', fontFamily: 'inherit', textAlign: 'center', - transition: - 'border-color 200ms ease, background-color 200ms ease, transform 100ms ease', + transition: 'border-color 200ms ease, background-color 200ms ease, transform 100ms ease', '&:hover': { borderColor: selected ? 'var(--fa-color-border-brand, #BA834E)' @@ -164,9 +150,7 @@ const StatusCard = React.forwardRef< fontWeight: 600, display: 'block', mb: 0.75, - color: selected - ? 'var(--fa-color-text-brand, #B0610F)' - : 'text.primary', + color: selected ? 'var(--fa-color-text-brand, #B0610F)' : 'text.primary', }} > {title} @@ -253,317 +237,309 @@ const selectMenuProps = { * Required fields: status + location (min 3 chars). * Funeral type defaults to "show all" if not selected. */ -export const FuneralFinderV3 = React.forwardRef< - HTMLDivElement, - FuneralFinderV3Props ->((props, ref) => { - const { - onSearch, - loading = false, - heading = 'Find funeral directors near you', - subheading = - "Tell us what you need and we\u2019ll show options in your area.", - sx, - } = props; +export const FuneralFinderV3 = React.forwardRef( + (props, ref) => { + const { + onSearch, + loading = false, + heading = 'Find funeral directors near you', + subheading = 'Tell us what you need and we\u2019ll show options in your area.', + sx, + } = props; - // ─── IDs for aria-labelledby ────────────────────────────── - const id = React.useId(); - const statusLabelId = `${id}-status`; - const funeralTypeLabelId = `${id}-funeral-type`; - const locationLabelId = `${id}-location`; + // ─── IDs for aria-labelledby ────────────────────────────── + const id = React.useId(); + const statusLabelId = `${id}-status`; + const funeralTypeLabelId = `${id}-funeral-type`; + const locationLabelId = `${id}-location`; - // ─── State ─────────────────────────────────────────────── - const [status, setStatus] = React.useState('immediate'); - const [funeralType, setFuneralType] = React.useState(''); - const [location, setLocation] = React.useState(''); - const [errors, setErrors] = React.useState<{ - status?: boolean; - location?: boolean; - }>({}); + // ─── State ─────────────────────────────────────────────── + const [status, setStatus] = React.useState('immediate'); + const [funeralType, setFuneralType] = React.useState(''); + const [location, setLocation] = React.useState(''); + const [errors, setErrors] = React.useState<{ + status?: boolean; + location?: boolean; + }>({}); - // ─── Refs ──────────────────────────────────────────────── - const statusSectionRef = React.useRef(null); - const locationSectionRef = React.useRef(null); - const locationInputRef = React.useRef(null); - const cardRefs = React.useRef<(HTMLButtonElement | null)[]>([null, null]); + // ─── Refs ──────────────────────────────────────────────── + const statusSectionRef = React.useRef(null); + const locationSectionRef = React.useRef(null); + const locationInputRef = React.useRef(null); + const cardRefs = React.useRef<(HTMLButtonElement | null)[]>([null, null]); - // ─── Clear errors as fields are filled ─────────────────── - const prevStatus = React.useRef(status); - React.useEffect(() => { - if (status !== prevStatus.current) { - prevStatus.current = status; - if (status && errors.status) { - setErrors((prev) => ({ ...prev, status: false })); + // ─── Clear errors as fields are filled ─────────────────── + const prevStatus = React.useRef(status); + React.useEffect(() => { + if (status !== prevStatus.current) { + prevStatus.current = status; + if (status && errors.status) { + setErrors((prev) => ({ ...prev, status: false })); + } } - } - }, [status, errors.status]); + }, [status, errors.status]); - const prevLocation = React.useRef(location); - React.useEffect(() => { - if (location !== prevLocation.current) { - prevLocation.current = location; - if (location.trim().length >= 3 && errors.location) { - setErrors((prev) => ({ ...prev, location: false })); + const prevLocation = React.useRef(location); + React.useEffect(() => { + if (location !== prevLocation.current) { + prevLocation.current = location; + if (location.trim().length >= 3 && errors.location) { + setErrors((prev) => ({ ...prev, location: false })); + } } - } - }, [location, errors.location]); + }, [location, errors.location]); - // ─── Radiogroup keyboard nav (WAI-ARIA pattern) ────────── - const activeStatusIndex = status - ? STATUS_OPTIONS.findIndex((o) => o.key === status) - : 0; + // ─── Radiogroup keyboard nav (WAI-ARIA pattern) ────────── + const activeStatusIndex = status ? STATUS_OPTIONS.findIndex((o) => o.key === status) : 0; - const handleStatusKeyDown = (e: React.KeyboardEvent) => { - const isNext = e.key === 'ArrowRight' || e.key === 'ArrowDown'; - const isPrev = e.key === 'ArrowLeft' || e.key === 'ArrowUp'; - if (!isNext && !isPrev) return; - e.preventDefault(); - const current = cardRefs.current.indexOf( - e.target as HTMLButtonElement, - ); - if (current === -1) return; - const next = isNext - ? Math.min(current + 1, STATUS_OPTIONS.length - 1) - : Math.max(current - 1, 0); - if (next !== current) { - cardRefs.current[next]?.focus(); - setStatus(STATUS_OPTIONS[next].key); - } - }; + const handleStatusKeyDown = (e: React.KeyboardEvent) => { + const isNext = e.key === 'ArrowRight' || e.key === 'ArrowDown'; + const isPrev = e.key === 'ArrowLeft' || e.key === 'ArrowUp'; + if (!isNext && !isPrev) return; + e.preventDefault(); + const current = cardRefs.current.indexOf(e.target as HTMLButtonElement); + if (current === -1) return; + const next = isNext + ? Math.min(current + 1, STATUS_OPTIONS.length - 1) + : Math.max(current - 1, 0); + if (next !== current) { + cardRefs.current[next]?.focus(); + setStatus(STATUS_OPTIONS[next].key); + } + }; - // ─── Handlers ──────────────────────────────────────────── - const handleFuneralType = (e: SelectChangeEvent) => { - setFuneralType(e.target.value as FuneralType); - }; + // ─── Handlers ──────────────────────────────────────────── + const handleFuneralType = (e: SelectChangeEvent) => { + setFuneralType(e.target.value as FuneralType); + }; - const handleSubmit = () => { - if (!status) { - setErrors({ status: true }); - statusSectionRef.current?.scrollIntoView({ - behavior: 'smooth', - block: 'center', + const handleSubmit = () => { + if (!status) { + setErrors({ status: true }); + statusSectionRef.current?.scrollIntoView({ + behavior: 'smooth', + 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 ────────────────────────────────────────────── - return ( - - {/* ── Header ──────────────────────────────────────────── */} - - - {heading} - - - {subheading} - - - - - - {/* ── How can we help ─────────────────────────────────── */} - - How Can We Help - - {STATUS_OPTIONS.map((opt, i) => ( - { - 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} - /> - ))} - - - {errors.status && ( - - Please select how we can help - - )} - - - - {/* ── Funeral Type ────────────────────────────────────── */} - - Funeral Type - - + {heading} + + + {subheading} + - - {/* ── Location ────────────────────────────────────────── */} - - Location - - setLocation(e.target.value)} - placeholder="Enter suburb or postcode" - inputRef={locationInputRef} - startAdornment={ - - - - } - onKeyDown={(e) => { - if (e.key === 'Enter') handleSubmit(); - }} + + + {/* ── How can we help ─────────────────────────────────── */} + + How Can We Help + + > + {STATUS_OPTIONS.map((opt, i) => ( + { + 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} + /> + ))} + + + {errors.status && ( + + Please select how we can help + + )} + - - {errors.location && ( - + Funeral Type + + + + + + {/* ── Location ────────────────────────────────────────── */} + + Location + + setLocation(e.target.value)} + placeholder="Enter suburb or postcode" + inputRef={locationInputRef} + startAdornment={ + + + + } + 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, + }} + /> + + + {errors.location && ( + + Please enter a suburb or postcode + + )} + + + + + + {/* ── CTA ─────────────────────────────────────────────── */} + + + + Free to use · No obligation + - - - - {/* ── CTA ─────────────────────────────────────────────── */} - - - - Free to use · No obligation - - - - ); -}); + ); + }, +); FuneralFinderV3.displayName = 'FuneralFinderV3'; export default FuneralFinderV3; diff --git a/src/components/organisms/Navigation/Navigation.stories.tsx b/src/components/organisms/Navigation/Navigation.stories.tsx index 7a66154..759cc04 100644 --- a/src/components/organisms/Navigation/Navigation.stories.tsx +++ b/src/components/organisms/Navigation/Navigation.stories.tsx @@ -77,11 +77,7 @@ export const WithCTA: Story = { export const WithPageContent: Story = { render: () => ( - } - items={defaultItems} - ctaLabel="Start planning" - /> + } items={defaultItems} ctaLabel="Start planning" /> - Compare trusted funeral directors in your area. View services, - pricing, and reviews to find the right support for your family. + Compare trusted funeral directors in your area. View services, pricing, and reviews to + find the right support for your family. {Array.from({ length: 8 }).map((_, i) => ( ( ))} {ctaLabel && ( - )} @@ -193,14 +189,8 @@ export const Navigation = React.forwardRef( bgcolor: 'var(--fa-color-surface-subtle)', }} > - - {logo} - - + {logo} + diff --git a/src/components/organisms/PackageDetail/PackageDetail.stories.tsx b/src/components/organisms/PackageDetail/PackageDetail.stories.tsx index 52f9c54..7e1c535 100644 --- a/src/components/organisms/PackageDetail/PackageDetail.stories.tsx +++ b/src/components/organisms/PackageDetail/PackageDetail.stories.tsx @@ -10,44 +10,136 @@ import { Button } from '../../atoms/Button'; import { Navigation } from '../Navigation'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -const DEMO_IMAGE = 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=400&h=300&fit=crop'; +const DEMO_IMAGE = + 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=400&h=300&fit=crop'; const essentials = [ - { name: 'Accommodation', price: 1500, info: 'Refrigerated holding of the deceased prior to the funeral service.' }, - { name: 'Death Registration Certificate', price: 1500, info: 'Lodgement of death registration with NSW Registry of Births, Deaths & Marriages.' }, - { name: 'Doctor Fee for Cremation', price: 1500, info: 'Statutory medical referee fee required for all cremations in NSW.' }, - { name: 'NSW Government Levy — Cremation', price: 1500, info: 'NSW Government cremation levy as set by the Department of Health.' }, - { name: 'Professional Mortuary Care', price: 1500, info: 'Preparation and care of the deceased.' }, - { name: 'Professional Service Fee', price: 1500, info: 'Coordination of all funeral arrangements and services.' }, - { name: 'Allowance for Coffin', price: 1500, isAllowance: true, info: 'Allowance amount — upgrade options available during arrangement.' }, - { name: 'Allowance for Crematorium', price: 1500, isAllowance: true, info: 'Allowance for crematorium fees — varies by location.' }, - { name: 'Allowance for Hearse', price: 1500, isAllowance: true, info: 'Allowance for hearse transfer — distance surcharges may apply.' }, + { + name: 'Accommodation', + price: 1500, + info: 'Refrigerated holding of the deceased prior to the funeral service.', + }, + { + name: 'Death Registration Certificate', + price: 1500, + info: 'Lodgement of death registration with NSW Registry of Births, Deaths & Marriages.', + }, + { + name: 'Doctor Fee for Cremation', + price: 1500, + info: 'Statutory medical referee fee required for all cremations in NSW.', + }, + { + name: 'NSW Government Levy — Cremation', + price: 1500, + info: 'NSW Government cremation levy as set by the Department of Health.', + }, + { + name: 'Professional Mortuary Care', + price: 1500, + info: 'Preparation and care of the deceased.', + }, + { + name: 'Professional Service Fee', + price: 1500, + info: 'Coordination of all funeral arrangements and services.', + }, + { + name: 'Allowance for Coffin', + price: 1500, + isAllowance: true, + info: 'Allowance amount — upgrade options available during arrangement.', + }, + { + name: 'Allowance for Crematorium', + price: 1500, + isAllowance: true, + info: 'Allowance for crematorium fees — varies by location.', + }, + { + name: 'Allowance for Hearse', + price: 1500, + isAllowance: true, + info: 'Allowance for hearse transfer — distance surcharges may apply.', + }, ]; const complimentary = [ - { name: 'Dressing Fee', info: 'Dressing and preparation of the deceased — included at no charge.' }, + { + name: 'Dressing Fee', + info: 'Dressing and preparation of the deceased — included at no charge.', + }, { name: 'Viewing Fee', info: 'One private family viewing — included at no charge.' }, ]; const extras = { heading: 'Extras', items: [ - { name: 'Allowance for Flowers', price: 1500, isAllowance: true, info: 'Seasonal floral arrangements for the service.' }, - { name: 'Allowance for Master of Ceremonies', price: 1500, isAllowance: true, info: 'Professional celebrant or MC for the funeral service.' }, - { name: 'After Business Hours Service Surcharge', price: 1500, info: 'Additional fee for services held outside standard business hours.' }, - { name: 'After Hours Prayers', price: 1500, info: 'Evening prayer service at the funeral home.' }, - { name: 'Coffin Bearing by Funeral Directors', price: 1500, info: 'Professional pallbearing by funeral directors.' }, - { name: 'Digital Recording', price: 1500, info: 'Professional video recording of the funeral service.' }, + { + name: 'Allowance for Flowers', + price: 1500, + isAllowance: true, + info: 'Seasonal floral arrangements for the service.', + }, + { + name: 'Allowance for Master of Ceremonies', + price: 1500, + isAllowance: true, + info: 'Professional celebrant or MC for the funeral service.', + }, + { + name: 'After Business Hours Service Surcharge', + price: 1500, + info: 'Additional fee for services held outside standard business hours.', + }, + { + name: 'After Hours Prayers', + price: 1500, + info: 'Evening prayer service at the funeral home.', + }, + { + name: 'Coffin Bearing by Funeral Directors', + price: 1500, + info: 'Professional pallbearing by funeral directors.', + }, + { + name: 'Digital Recording', + price: 1500, + info: 'Professional video recording of the funeral service.', + }, ], }; -const termsText = '* This package includes a funeral service at a chapel or a church with a funeral procession following to the crematorium. It includes many of the most commonly selected funeral options preselected for you. Many people choose this package for the extended funeral rituals — of course, you can tailor the funeral service to meet your needs and budget as you go through the selections.'; +const termsText = + '* This package includes a funeral service at a chapel or a church with a funeral procession following to the crematorium. It includes many of the most commonly selected funeral options preselected for you. Many people choose this package for the extended funeral rituals — of course, you can tailor the funeral service to meet your needs and budget as you go through the selections.'; const packages = [ - { id: 'everyday', name: 'Everyday Funeral Package', price: 900, description: 'Our most popular package with all essential services included. Suitable for a traditional chapel or church service.' }, - { id: 'deluxe', name: 'Deluxe Funeral Package', price: 1200, description: 'An enhanced package with premium coffin and additional floral arrangements.' }, - { id: 'essential', name: 'Essential Funeral Package', price: 600, description: 'A simple, dignified service covering all necessary arrangements.' }, - { id: 'catholic', name: 'Catholic Service', price: 950, description: 'A service tailored for Catholic traditions including prayers and church ceremony.' }, + { + id: 'everyday', + name: 'Everyday Funeral Package', + price: 900, + description: + 'Our most popular package with all essential services included. Suitable for a traditional chapel or church service.', + }, + { + id: 'deluxe', + name: 'Deluxe Funeral Package', + price: 1200, + description: 'An enhanced package with premium coffin and additional floral arrangements.', + }, + { + id: 'essential', + name: 'Essential Funeral Package', + price: 600, + description: 'A simple, dignified service covering all necessary arrangements.', + }, + { + id: 'catholic', + name: 'Catholic Service', + price: 950, + description: + 'A service tailored for Catholic traditions including prayers and church ceremony.', + }, ]; const funeralTypes = ['All', 'Cremation', 'Burial', 'Memorial', 'Catholic', 'Direct Cremation']; @@ -101,9 +193,7 @@ export const CompareLoading: Story = { args: { name: 'Everyday Funeral Package', price: 900, - sections: [ - { heading: 'Essentials', items: essentials.slice(0, 4) }, - ], + sections: [{ heading: 'Essentials', items: essentials.slice(0, 4) }], total: 6000, onArrange: () => alert('Make Arrangement'), onCompare: () => {}, diff --git a/src/components/organisms/PackageDetail/PackageDetail.tsx b/src/components/organisms/PackageDetail/PackageDetail.tsx index e24280e..7737e54 100644 --- a/src/components/organisms/PackageDetail/PackageDetail.tsx +++ b/src/components/organisms/PackageDetail/PackageDetail.tsx @@ -155,15 +155,14 @@ export const PackageDetail = React.forwardRef {name} - + ${price.toLocaleString('en-AU')} {/* CTA buttons */} - +