- centered-form: single column ~600px for form steps (intro, auth, etc.) - list-map: 40/60 split for provider search (card list + map) - list-detail: 40/60 master-detail for package selection - grid-sidebar: 25/75 filter sidebar + card grid (coffins) - detail-toggles: 50/50 hero image + product info (venue/coffin details) Common elements: nav slot, sticky help bar, optional back link, optional progress stepper + running total (grid-sidebar, detail-toggles). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
425 lines
13 KiB
TypeScript
425 lines
13 KiB
TypeScript
import type { Meta, StoryObj } from '@storybook/react';
|
|
import { WizardLayout } from './WizardLayout';
|
|
import Box from '@mui/material/Box';
|
|
import { Typography } from '../../atoms/Typography';
|
|
import { Button } from '../../atoms/Button';
|
|
import { Divider } from '../../atoms/Divider';
|
|
import { Navigation } from '../../organisms/Navigation';
|
|
import { StepIndicator } from '../../molecules/StepIndicator';
|
|
|
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
|
|
const FALogo = () => (
|
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
|
<Box
|
|
component="img"
|
|
src="/brandlogo/logo-full.svg"
|
|
alt="Funeral Arranger"
|
|
sx={{ height: 28, display: { xs: 'none', md: 'block' } }}
|
|
/>
|
|
<Box
|
|
component="img"
|
|
src="/brandlogo/logo-short.svg"
|
|
alt="Funeral Arranger"
|
|
sx={{ height: 28, display: { xs: 'block', md: 'none' } }}
|
|
/>
|
|
</Box>
|
|
);
|
|
|
|
const nav = (
|
|
<Navigation
|
|
logo={<FALogo />}
|
|
items={[
|
|
{ label: 'FAQ', href: '/faq' },
|
|
{ label: 'Contact Us', href: '/contact' },
|
|
{ label: 'Log in', href: '/login' },
|
|
]}
|
|
/>
|
|
);
|
|
|
|
const stepper = (
|
|
<StepIndicator
|
|
steps={[
|
|
{ label: 'Details' },
|
|
{ label: 'Service' },
|
|
{ label: 'Venue' },
|
|
{ label: 'Coffin' },
|
|
{ label: 'Extras' },
|
|
]}
|
|
currentStep={2}
|
|
/>
|
|
);
|
|
|
|
const runningTotal = (
|
|
<Box
|
|
sx={{
|
|
bgcolor: 'background.paper',
|
|
border: '1px solid',
|
|
borderColor: 'divider',
|
|
borderRadius: 1,
|
|
px: 2,
|
|
py: 0.75,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: 1,
|
|
}}
|
|
>
|
|
<Typography variant="body2" color="text.secondary">
|
|
Your plan
|
|
</Typography>
|
|
<Typography variant="h6" color="primary.main">
|
|
$3,600
|
|
</Typography>
|
|
</Box>
|
|
);
|
|
|
|
const PlaceholderCard: React.FC<{ title: string; height?: number }> = ({ title, height = 100 }) => (
|
|
<Box
|
|
sx={{
|
|
p: 3,
|
|
mb: 2,
|
|
bgcolor: 'background.paper',
|
|
borderRadius: 1,
|
|
border: '1px solid',
|
|
borderColor: 'divider',
|
|
minHeight: height,
|
|
}}
|
|
>
|
|
<Typography variant="h6">{title}</Typography>
|
|
<Typography variant="body2" color="text.secondary">
|
|
Placeholder content for layout demonstration.
|
|
</Typography>
|
|
</Box>
|
|
);
|
|
|
|
// ─── Meta ────────────────────────────────────────────────────────────────────
|
|
|
|
const meta: Meta<typeof WizardLayout> = {
|
|
title: 'Templates/WizardLayout',
|
|
component: WizardLayout,
|
|
tags: ['autodocs'],
|
|
parameters: {
|
|
layout: 'fullscreen',
|
|
},
|
|
argTypes: {
|
|
variant: {
|
|
control: 'select',
|
|
options: ['centered-form', 'list-map', 'list-detail', 'grid-sidebar', 'detail-toggles'],
|
|
description: 'Layout variant',
|
|
table: { defaultValue: { summary: 'centered-form' } },
|
|
},
|
|
showBackLink: { control: 'boolean' },
|
|
hideHelpBar: { control: 'boolean' },
|
|
backLabel: { control: 'text' },
|
|
helpPhone: { control: 'text' },
|
|
},
|
|
};
|
|
|
|
export default meta;
|
|
type Story = StoryObj<typeof WizardLayout>;
|
|
|
|
// ─── Centered Form ──────────────────────────────────────────────────────────
|
|
|
|
/** Step 1 (Intro) style — single centered column, no back link, no progress stepper */
|
|
export const CenteredForm: Story = {
|
|
args: {
|
|
variant: 'centered-form',
|
|
navigation: nav,
|
|
},
|
|
render: (args) => (
|
|
<WizardLayout {...args}>
|
|
<Typography variant="h4" sx={{ mb: 1 }}>
|
|
Let's get started
|
|
</Typography>
|
|
<Typography variant="body1" color="text.secondary" sx={{ mb: 4 }}>
|
|
We'll guide you through arranging a funeral, step by step.
|
|
</Typography>
|
|
|
|
<Box sx={{ mb: 3 }}>
|
|
<Typography variant="label" sx={{ mb: 1, display: 'block' }}>
|
|
Who is this funeral being arranged for?
|
|
</Typography>
|
|
<Box sx={{ display: 'flex', gap: 2 }}>
|
|
<Button variant="soft" color="secondary" size="large" fullWidth>
|
|
Myself
|
|
</Button>
|
|
<Button variant="soft" color="secondary" size="large" fullWidth>
|
|
Someone else
|
|
</Button>
|
|
</Box>
|
|
</Box>
|
|
|
|
<Divider sx={{ my: 4 }} />
|
|
|
|
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
|
|
<Button variant="contained" size="large">
|
|
Continue
|
|
</Button>
|
|
</Box>
|
|
</WizardLayout>
|
|
),
|
|
};
|
|
|
|
// ─── Centered Form with Back ────────────────────────────────────────────────
|
|
|
|
/** Form step with back link (e.g. date/time, payment) */
|
|
export const CenteredFormWithBack: Story = {
|
|
args: {
|
|
variant: 'centered-form',
|
|
navigation: nav,
|
|
showBackLink: true,
|
|
backLabel: 'Back',
|
|
},
|
|
render: (args) => (
|
|
<WizardLayout {...args}>
|
|
<Typography variant="h4" sx={{ mb: 1 }}>
|
|
Date and time
|
|
</Typography>
|
|
<Typography variant="body1" color="text.secondary" sx={{ mb: 4 }}>
|
|
When would you like the service to take place?
|
|
</Typography>
|
|
<PlaceholderCard title="Date picker" height={200} />
|
|
<PlaceholderCard title="Time selector" />
|
|
<Divider sx={{ my: 4 }} />
|
|
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
|
|
<Button variant="contained" size="large">
|
|
Continue
|
|
</Button>
|
|
</Box>
|
|
</WizardLayout>
|
|
),
|
|
};
|
|
|
|
// ─── List + Map ──────────────────────────────────────────────────────────────
|
|
|
|
/** Provider search — scrollable card list with map panel */
|
|
export const ListMap: Story = {
|
|
args: {
|
|
variant: 'list-map',
|
|
navigation: nav,
|
|
showBackLink: true,
|
|
},
|
|
render: (args) => (
|
|
<WizardLayout
|
|
{...args}
|
|
secondaryPanel={
|
|
<Box
|
|
sx={{
|
|
bgcolor: 'var(--fa-color-sage-200)',
|
|
height: '100%',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
}}
|
|
>
|
|
<Typography variant="body1" color="text.secondary">
|
|
Map placeholder
|
|
</Typography>
|
|
</Box>
|
|
}
|
|
>
|
|
<Typography variant="h5" sx={{ mb: 0.5 }}>
|
|
Choose a funeral provider
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
|
|
Explore providers near you
|
|
</Typography>
|
|
<PlaceholderCard title="Search bar + filters" height={48} />
|
|
<Typography variant="caption" color="text.secondary" sx={{ mb: 2, display: 'block' }}>
|
|
Showing results from 5 providers
|
|
</Typography>
|
|
{Array.from({ length: 4 }).map((_, i) => (
|
|
<PlaceholderCard key={i} title={`Provider ${i + 1}`} />
|
|
))}
|
|
</WizardLayout>
|
|
),
|
|
};
|
|
|
|
// ─── List + Detail ───────────────────────────────────────────────────────────
|
|
|
|
/** Package selection — list left, detail panel right */
|
|
export const ListDetail: Story = {
|
|
args: {
|
|
variant: 'list-detail',
|
|
navigation: nav,
|
|
showBackLink: true,
|
|
},
|
|
render: (args) => (
|
|
<WizardLayout
|
|
{...args}
|
|
secondaryPanel={
|
|
<Box>
|
|
<Typography variant="h5" sx={{ mb: 1 }}>
|
|
Everyday Funeral Package
|
|
</Typography>
|
|
<Typography variant="h4" color="primary.main" sx={{ mb: 3 }}>
|
|
$2,700
|
|
</Typography>
|
|
<Button variant="contained" size="large" fullWidth sx={{ mb: 3 }}>
|
|
Select Package
|
|
</Button>
|
|
<Divider sx={{ mb: 2 }} />
|
|
{['Essentials', 'Complimentary Items', 'Extras'].map((section) => (
|
|
<Box key={section} sx={{ mb: 3 }}>
|
|
<Typography variant="label" sx={{ mb: 1, display: 'block' }}>
|
|
{section}
|
|
</Typography>
|
|
{Array.from({ length: 3 }).map((_, i) => (
|
|
<PlaceholderCard key={i} title={`Item ${i + 1}`} height={40} />
|
|
))}
|
|
</Box>
|
|
))}
|
|
</Box>
|
|
}
|
|
>
|
|
<PlaceholderCard title="Provider card (compact)" height={80} />
|
|
<Typography variant="h6" sx={{ mb: 2 }}>
|
|
Packages
|
|
</Typography>
|
|
{Array.from({ length: 4 }).map((_, i) => (
|
|
<PlaceholderCard key={i} title={`Package ${i + 1} — $${900 * (i + 1)}`} />
|
|
))}
|
|
</WizardLayout>
|
|
),
|
|
};
|
|
|
|
// ─── Grid + Sidebar ──────────────────────────────────────────────────────────
|
|
|
|
/** Coffin selection — filter sidebar + responsive card grid */
|
|
export const GridSidebar: Story = {
|
|
args: {
|
|
variant: 'grid-sidebar',
|
|
navigation: nav,
|
|
showBackLink: true,
|
|
progressStepper: stepper,
|
|
runningTotal: runningTotal,
|
|
},
|
|
render: (args) => (
|
|
<WizardLayout
|
|
{...args}
|
|
secondaryPanel={
|
|
<Box>
|
|
<Typography variant="h5" sx={{ mb: 0.5 }}>
|
|
Coffins
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
|
|
Browse our selection of bespoke designer coffins.
|
|
</Typography>
|
|
<Box
|
|
sx={{
|
|
display: 'grid',
|
|
gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr', md: '1fr 1fr 1fr' },
|
|
gap: 2,
|
|
}}
|
|
>
|
|
{Array.from({ length: 6 }).map((_, i) => (
|
|
<PlaceholderCard key={i} title={`Coffin ${i + 1}`} height={160} />
|
|
))}
|
|
</Box>
|
|
</Box>
|
|
}
|
|
>
|
|
<Typography variant="h6" sx={{ mb: 2 }}>
|
|
Categories
|
|
</Typography>
|
|
{['Materials', 'Colour', 'Environmental', 'Religious'].map((cat) => (
|
|
<PlaceholderCard key={cat} title={cat} height={40} />
|
|
))}
|
|
<Typography variant="h6" sx={{ mt: 3, mb: 2 }}>
|
|
Price
|
|
</Typography>
|
|
<PlaceholderCard title="Price range slider" height={48} />
|
|
</WizardLayout>
|
|
),
|
|
};
|
|
|
|
// ─── Detail + Toggles ───────────────────────────────────────────────────────
|
|
|
|
/** Venue/coffin detail — hero image + product info */
|
|
export const DetailToggles: Story = {
|
|
args: {
|
|
variant: 'detail-toggles',
|
|
navigation: nav,
|
|
showBackLink: true,
|
|
progressStepper: stepper,
|
|
runningTotal: runningTotal,
|
|
},
|
|
render: (args) => (
|
|
<WizardLayout
|
|
{...args}
|
|
secondaryPanel={
|
|
<Box>
|
|
<Typography variant="h4" sx={{ mb: 0.5 }}>
|
|
West Chapel
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
|
|
Wentworth, NSW
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
|
Capacity: 120 guests
|
|
</Typography>
|
|
<Typography variant="body1" sx={{ mb: 2 }}>
|
|
A beautiful heritage chapel set in peaceful gardens, offering a serene setting for
|
|
farewell services.
|
|
</Typography>
|
|
<Typography variant="h5" color="primary.main" sx={{ mb: 2 }}>
|
|
$900
|
|
</Typography>
|
|
<Button variant="contained" size="large" fullWidth>
|
|
Add to package
|
|
</Button>
|
|
</Box>
|
|
}
|
|
>
|
|
<Box
|
|
sx={{
|
|
bgcolor: 'var(--fa-color-sage-200)',
|
|
borderRadius: 2,
|
|
height: 300,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
mb: 2,
|
|
}}
|
|
>
|
|
<Typography variant="body1" color="text.secondary">
|
|
Venue image placeholder
|
|
</Typography>
|
|
</Box>
|
|
<Box sx={{ display: 'flex', gap: 1 }}>
|
|
{Array.from({ length: 5 }).map((_, i) => (
|
|
<Box
|
|
key={i}
|
|
sx={{
|
|
bgcolor: 'var(--fa-color-sage-200)',
|
|
borderRadius: 1,
|
|
width: 60,
|
|
height: 48,
|
|
}}
|
|
/>
|
|
))}
|
|
</Box>
|
|
</WizardLayout>
|
|
),
|
|
};
|
|
|
|
// ─── No Navigation ──────────────────────────────────────────────────────────
|
|
|
|
/** Minimal — no nav, no help bar (for embedded use) */
|
|
export const Minimal: Story = {
|
|
args: {
|
|
variant: 'centered-form',
|
|
hideHelpBar: true,
|
|
},
|
|
render: (args) => (
|
|
<WizardLayout {...args}>
|
|
<Typography variant="h4" sx={{ mb: 1 }}>
|
|
Embedded form
|
|
</Typography>
|
|
<Typography variant="body1" color="text.secondary">
|
|
No navigation or help bar — for iframe or modal contexts.
|
|
</Typography>
|
|
</WizardLayout>
|
|
),
|
|
};
|