diff --git a/src/components/organisms/Navigation/Navigation.stories.tsx b/src/components/organisms/Navigation/Navigation.stories.tsx new file mode 100644 index 0000000..ab1bc41 --- /dev/null +++ b/src/components/organisms/Navigation/Navigation.stories.tsx @@ -0,0 +1,174 @@ +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { Navigation } from './Navigation'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; + +// Placeholder logo — in production this would be an SVG or +const FALogo = () => ( + + FuneralArranger + +); + +const defaultItems = [ + { label: 'FAQ', href: '/faq' }, + { label: 'Contact Us', href: '/contact' }, + { label: 'Log in', href: '/login' }, +]; + +const meta: Meta = { + title: 'Organisms/Navigation', + component: Navigation, + tags: ['autodocs'], + parameters: { + layout: 'fullscreen', + design: { + type: 'figma', + url: 'https://www.figma.com/design/XUDUrw4yMkEexBCCYHXUvT/Parsons?node-id=14-108', + }, + }, + argTypes: { + ctaLabel: { control: 'text' }, + }, +}; + +export default meta; +type Story = StoryObj; + +// ─── Default ──────────────────────────────────────────────────────────────── + +/** Desktop — logo left, navigation links right */ +export const Default: Story = { + args: { + logo: , + items: defaultItems, + }, +}; + +// ─── With CTA ─────────────────────────────────────────────────────────────── + +/** Desktop with a primary call-to-action button */ +export const WithCTA: Story = { + args: { + logo: , + items: defaultItems, + ctaLabel: 'Start planning', + onCtaClick: () => alert('Navigate to arrangement flow'), + }, +}; + +// ─── With Page Content ────────────────────────────────────────────────────── + +/** Full page layout — sticky header with scrollable content */ +export const WithPageContent: Story = { + render: () => ( + + } + items={defaultItems} + ctaLabel="Start planning" + /> + + + Find a funeral director + + + 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) => ( + + Provider {i + 1} + + Placeholder content to demonstrate scroll behaviour with sticky header. + + + ))} + + + ), +}; + +// ─── Minimal ──────────────────────────────────────────────────────────────── + +/** Minimal — logo only, no navigation items */ +export const Minimal: Story = { + args: { + logo: , + }, +}; + +// ─── Extended Navigation ──────────────────────────────────────────────────── + +/** More nav items for arrangements flow context */ +export const ExtendedNavigation: Story = { + args: { + logo: , + items: [ + { label: 'Directors', href: '/directors' }, + { label: 'Venues', href: '/venues' }, + { label: 'Pricing', href: '/pricing' }, + { label: 'FAQ', href: '/faq' }, + { label: 'Contact Us', href: '/contact' }, + { label: 'Log in', href: '/login' }, + ], + ctaLabel: 'Start planning', + }, +}; + +// ─── Mobile Price Tracker ─────────────────────────────────────────────────── + +/** Mobile with price tracker trailing content (resize browser to see) */ +export const MobilePriceTracker: Story = { + args: { + logo: , + items: defaultItems, + mobileTrailing: ( + + + Your plan + + + $3,600 + + + ), + }, +}; diff --git a/src/components/organisms/Navigation/Navigation.tsx b/src/components/organisms/Navigation/Navigation.tsx new file mode 100644 index 0000000..160e3bd --- /dev/null +++ b/src/components/organisms/Navigation/Navigation.tsx @@ -0,0 +1,293 @@ +import React from 'react'; +import AppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import Box from '@mui/material/Box'; +import Drawer from '@mui/material/Drawer'; +import List from '@mui/material/List'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemText from '@mui/material/ListItemText'; +import useMediaQuery from '@mui/material/useMediaQuery'; +import MenuIcon from '@mui/icons-material/Menu'; +import CloseIcon from '@mui/icons-material/Close'; +import type { SxProps, Theme } from '@mui/material/styles'; +import { IconButton } from '../../atoms/IconButton'; +import { Link } from '../../atoms/Link'; +import { Button } from '../../atoms/Button'; +import { Typography } from '../../atoms/Typography'; +import { Divider } from '../../atoms/Divider'; + +// ─── Types ─────────────────────────────────────────────────────────────────── + +/** A navigation link item */ +export interface NavItem { + /** Display label */ + label: string; + /** URL to navigate to */ + href: string; + /** Click handler (alternative to href for SPA navigation) */ + onClick?: () => void; +} + +/** Props for the FA Navigation organism */ +export interface NavigationProps { + /** Site logo — rendered on the left (desktop) or centre (mobile) */ + logo: React.ReactNode; + /** Click handler for the logo (navigate to home) */ + onLogoClick?: () => void; + /** Navigation links displayed in the header */ + items?: NavItem[]; + /** Optional CTA button (e.g. "Start planning") on desktop */ + ctaLabel?: string; + /** Click handler for the CTA button */ + onCtaClick?: () => void; + /** Optional right-aligned content for mobile (e.g. price tracker) */ + mobileTrailing?: React.ReactNode; + /** MUI sx prop for the root AppBar */ + sx?: SxProps; +} + +// ─── Component ─────────────────────────────────────────────────────────────── + +/** + * Site header navigation for the FA design system. + * + * Responsive header with logo, navigation links, and optional CTA. + * Desktop shows links inline; mobile collapses to hamburger + drawer. + * + * Maps to Figma "Main Nav" (14:108) desktop and "Mobile Header" + * (2391:41508) mobile patterns. + * + * Composes AppBar + Link + IconButton + Button + Divider + Drawer. + * + * Usage: + * ```tsx + * } + * onLogoClick={() => navigate('/')} + * items={[ + * { label: 'FAQ', href: '/faq' }, + * { label: 'Contact Us', href: '/contact' }, + * { label: 'Log in', href: '/login' }, + * ]} + * ctaLabel="Start planning" + * onCtaClick={() => navigate('/arrange')} + * /> + * ``` + */ +export const Navigation = React.forwardRef( + ({ logo, onLogoClick, items = [], ctaLabel, onCtaClick, mobileTrailing, sx }, ref) => { + const [drawerOpen, setDrawerOpen] = React.useState(false); + const isMobile = useMediaQuery((theme: Theme) => theme.breakpoints.down('md')); + + const handleDrawerToggle = () => setDrawerOpen((prev) => !prev); + + return ( + <> + + + {/* Left: hamburger (mobile) + logo */} + + {isMobile && ( + + + + )} + + + {logo} + + + + {/* Right: nav links (desktop) or trailing content (mobile) */} + {!isMobile ? ( + + {items.map((item) => ( + + {item.label} + + ))} + + {ctaLabel && ( + + )} + + ) : ( + mobileTrailing && ( + + {mobileTrailing} + + ) + )} + + + + {/* Mobile drawer */} + {isMobile && ( + + {/* Drawer header */} + + + {logo} + + + + + + + + + {/* Nav items */} + + {items.map((item) => ( + { + if (item.onClick) { + e.preventDefault(); + item.onClick(); + } + setDrawerOpen(false); + }} + sx={{ + py: 1.5, + px: 3, + '&:hover': { + bgcolor: 'var(--fa-color-brand-100)', + }, + }} + > + + + ))} + + + {ctaLabel && ( + + + + )} + + {/* Footer */} + + + + + Need help? Call us on + + + 1800 987 888 + + + + + )} + + ); + }, +); + +Navigation.displayName = 'Navigation'; +export default Navigation; diff --git a/src/components/organisms/Navigation/index.ts b/src/components/organisms/Navigation/index.ts new file mode 100644 index 0000000..21320c7 --- /dev/null +++ b/src/components/organisms/Navigation/index.ts @@ -0,0 +1,2 @@ +export { Navigation } from './Navigation'; +export type { NavigationProps, NavItem } from './Navigation';