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:
32
src/components/molecules/HelpBar/HelpBar.stories.tsx
Normal file
32
src/components/molecules/HelpBar/HelpBar.stories.tsx
Normal 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' },
|
||||
};
|
||||
64
src/components/molecules/HelpBar/HelpBar.tsx
Normal file
64
src/components/molecules/HelpBar/HelpBar.tsx
Normal 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;
|
||||
1
src/components/molecules/HelpBar/index.ts
Normal file
1
src/components/molecules/HelpBar/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { HelpBar, type HelpBarProps } from './HelpBar';
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 }) => (
|
||||
|
||||
Reference in New Issue
Block a user