Promote HelpBar to a shared molecule

Lifts HelpBar out of WizardLayout's internal scope into
src/components/molecules/HelpBar/ so both WizardLayout and pages that
bypass WizardLayout's chrome (currently: ProvidersStep's mobile
map-first branch) render an identical sticky-footer.

Before: WizardLayout had an internal HelpBar with the right styling
(sticky, responsive px, phone format helper); ProvidersStep's
mobile-map branch hand-rewrote the footer inline and had drifted —
missing position: sticky, missing the md:4 responsive px, hard-coded
phone number bypassing the prop default. This consolidates both to
one source of truth.

Props: `phone?` (defaults to FA's support number, spaces preserved
in label, stripped in tel href) + `sx?` for caller chrome overrides.
Two Storybook stories (Default, CustomNumber).

Verified: footer text / height / sticky position identical between
mobile-list (WizardLayout) and mobile-map (direct HelpBar).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 10:16:04 +10:00
parent 4f433e2a8f
commit 2b39f43264
5 changed files with 102 additions and 51 deletions

View File

@@ -0,0 +1,32 @@
import type { Meta, StoryObj } from '@storybook/react';
import Box from '@mui/material/Box';
import { HelpBar } from './HelpBar';
const meta: Meta<typeof HelpBar> = {
title: 'Molecules/HelpBar',
component: HelpBar,
tags: ['autodocs'],
parameters: { layout: 'fullscreen' },
decorators: [
(Story) => (
// Fake page content so the sticky footer has something to sit under.
<Box sx={{ minHeight: 400, display: 'flex', flexDirection: 'column' }}>
<Box sx={{ flex: 1, p: 4, bgcolor: 'background.default' }}>
Page content scrolls above the help bar.
</Box>
<Story />
</Box>
),
],
};
export default meta;
type Story = StoryObj<typeof HelpBar>;
/** Default — uses FA's standard support number. */
export const Default: Story = {};
/** Custom number — spaces preserved in the label, stripped in the tel link. */
export const CustomNumber: Story = {
args: { phone: '1300 000 000' },
};

View File

@@ -0,0 +1,64 @@
import React from 'react';
import Box from '@mui/material/Box';
import PhoneIcon from '@mui/icons-material/Phone';
import type { SxProps, Theme } from '@mui/material/styles';
import { Typography } from '../../atoms/Typography';
import { Link } from '../../atoms/Link';
// ─── Types ──────────────────────────────────────────────────────────────────
/** Props for the FA HelpBar molecule */
export interface HelpBarProps {
/** Phone number shown in the bar. Spaces preserved in the label,
* stripped in the `tel:` href. Defaults to FA's support number. */
phone?: string;
/** MUI sx prop — merged onto the default footer chrome. */
sx?: SxProps<Theme>;
}
// ─── Component ──────────────────────────────────────────────────────────────
/**
* Sticky help footer used at the bottom of every wizard page. Shows a
* phone-icon prefix + "Need help? Call us on" + the support number as a
* tel-link. White fill, top border, sticky to the viewport bottom.
*
* Used by `WizardLayout` (for all variants that don't set `hideHelpBar`)
* and by pages that bypass WizardLayout's chrome (e.g. the mobile-map-first
* layout on `ProvidersStep`). Promoted from a WizardLayout-internal
* component so both sources render an identical footer — preventing drift
* if the phone number or styling ever changes.
*/
export const HelpBar = React.forwardRef<HTMLDivElement, HelpBarProps>(
({ phone = '1800 987 888', sx }, ref) => (
<Box
ref={ref}
component="footer"
sx={[
{
position: 'sticky',
bottom: 0,
zIndex: 10,
bgcolor: 'background.paper',
borderTop: '1px solid',
borderColor: 'divider',
py: 1.5,
px: { xs: 2, md: 4 },
textAlign: 'center',
},
...(Array.isArray(sx) ? sx : [sx]),
]}
>
<Typography variant="body2" color="text.secondary" component="span">
<PhoneIcon sx={{ fontSize: 16, verticalAlign: 'text-bottom', mr: 0.5 }} />
Need help? Call us on{' '}
<Link href={`tel:${phone.replace(/\s/g, '')}`} sx={{ fontWeight: 600 }}>
{phone}
</Link>
</Typography>
</Box>
),
);
HelpBar.displayName = 'HelpBar';
export default HelpBar;

View File

@@ -0,0 +1 @@
export { HelpBar, type HelpBarProps } from './HelpBar';

View File

@@ -13,7 +13,6 @@ import useMediaQuery from '@mui/material/useMediaQuery';
import SwapVertIcon from '@mui/icons-material/SwapVert';
import ViewListOutlinedIcon from '@mui/icons-material/ViewListOutlined';
import MapOutlinedIcon from '@mui/icons-material/MapOutlined';
import PhoneIcon from '@mui/icons-material/Phone';
import type { SxProps, Theme } from '@mui/material/styles';
import { useTheme } from '@mui/material/styles';
import { WizardLayout } from '../../templates/WizardLayout';
@@ -21,6 +20,7 @@ import { ProviderCard } from '../../molecules/ProviderCard';
import { FilterPanel } from '../../molecules/FilterPanel';
import { MapProviderDrawer } from '../../molecules/MapProviderDrawer';
import { LocationSearchInput } from '../../molecules/LocationSearchInput';
import { HelpBar } from '../../molecules/HelpBar';
import {
ProviderMap,
type ProviderMapActiveState,
@@ -28,7 +28,6 @@ import {
} from '../../organisms/ProviderMap';
import { Button } from '../../atoms/Button';
import { Chip } from '../../atoms/Chip';
import { Link } from '../../atoms/Link';
import { Switch } from '../../atoms/Switch';
import { Typography } from '../../atoms/Typography';
import { Divider } from '../../atoms/Divider';
@@ -673,26 +672,9 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
/>
</Box>
{/* Sticky help bar (matches WizardLayout) */}
<Box
component="footer"
sx={{
bgcolor: 'background.paper',
borderTop: '1px solid',
borderColor: 'divider',
py: 1.5,
px: 2,
textAlign: 'center',
}}
>
<Typography variant="body2" color="text.secondary" component="span">
<PhoneIcon sx={{ fontSize: 16, verticalAlign: 'text-bottom', mr: 0.5 }} />
Need help? Call us on{' '}
<Link href="tel:1800987888" sx={{ fontWeight: 600 }}>
1800 987 888
</Link>
</Typography>
</Box>
{/* Sticky help bar — shared HelpBar molecule so this footer stays
identical to WizardLayout's (which we bypass in this branch). */}
<HelpBar />
</Box>
);
}

View File

@@ -2,10 +2,9 @@ import React from 'react';
import Box from '@mui/material/Box';
import Container from '@mui/material/Container';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import PhoneIcon from '@mui/icons-material/Phone';
import type { SxProps, Theme } from '@mui/material/styles';
import { Link } from '../../atoms/Link';
import { Typography } from '../../atoms/Typography';
import { HelpBar } from '../../molecules/HelpBar';
// ─── Types ───────────────────────────────────────────────────────────────────
@@ -51,33 +50,6 @@ export interface WizardLayoutProps {
sx?: SxProps<Theme>;
}
// ─── Help bar ────────────────────────────────────────────────────────────────
const HelpBar: React.FC<{ phone: string }> = ({ phone }) => (
<Box
component="footer"
sx={{
position: 'sticky',
bottom: 0,
zIndex: 10,
bgcolor: 'background.paper',
borderTop: '1px solid',
borderColor: 'divider',
py: 1.5,
px: { xs: 2, md: 4 },
textAlign: 'center',
}}
>
<Typography variant="body2" color="text.secondary" component="span">
<PhoneIcon sx={{ fontSize: 16, verticalAlign: 'text-bottom', mr: 0.5 }} />
Need help? Call us on{' '}
<Link href={`tel:${phone.replace(/\s/g, '')}`} sx={{ fontWeight: 600 }}>
{phone}
</Link>
</Typography>
</Box>
);
// ─── Back link ───────────────────────────────────────────────────────────────
const BackLink: React.FC<{ label: string; onClick?: () => void }> = ({ label, onClick }) => (