From 43e7191ead4684a3702027acf7b35d2ffff8f24c Mon Sep 17 00:00:00 2001 From: Richie Date: Wed, 25 Mar 2026 20:57:18 +1100 Subject: [PATCH] =?UTF-8?q?Add=20StepIndicator=20molecule=20=E2=80=94=20ho?= =?UTF-8?q?rizontal=20segmented=20progress=20bar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Maps to Figma Progress Bar - Steps (2375:47468) - Segmented bars: brand gold for completed/current, grey for incomplete - Current step label bolded, responsive bar height (10px/6px) - role="navigation" + aria-current="step" for accessibility - 7 stories: Default, AllStates, TwoSteps, ManySteps, Interactive, Completed, NarrowContainer Co-Authored-By: Claude Opus 4.6 (1M context) --- .../StepIndicator/StepIndicator.stories.tsx | 165 ++++++++++++++++++ .../molecules/StepIndicator/StepIndicator.tsx | 113 ++++++++++++ .../molecules/StepIndicator/index.ts | 2 + 3 files changed, 280 insertions(+) create mode 100644 src/components/molecules/StepIndicator/StepIndicator.stories.tsx create mode 100644 src/components/molecules/StepIndicator/StepIndicator.tsx create mode 100644 src/components/molecules/StepIndicator/index.ts diff --git a/src/components/molecules/StepIndicator/StepIndicator.stories.tsx b/src/components/molecules/StepIndicator/StepIndicator.stories.tsx new file mode 100644 index 0000000..f09c845 --- /dev/null +++ b/src/components/molecules/StepIndicator/StepIndicator.stories.tsx @@ -0,0 +1,165 @@ +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { StepIndicator } from './StepIndicator'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; + +const meta: Meta = { + title: 'Molecules/StepIndicator', + component: StepIndicator, + tags: ['autodocs'], + parameters: { + layout: 'centered', + design: { + type: 'figma', + url: 'https://www.figma.com/design/XUDUrw4yMkEexBCCYHXUvT/Parsons?node-id=2375-47468', + }, + }, + decorators: [ + (Story) => ( + + + + ), + ], +}; + +export default meta; +type Story = StoryObj; + +// ─── Arrangement Flow Steps ───────────────────────────────────────────────── + +const arrangementSteps = [ + { label: 'Details' }, + { label: 'Venue' }, + { label: 'Service' }, + { label: 'Extras' }, + { label: 'Review' }, +]; + +// ─── Default ──────────────────────────────────────────────────────────────── + +/** Default — 5-step arrangement flow, currently on step 3 */ +export const Default: Story = { + args: { + steps: arrangementSteps, + currentStep: 2, + }, +}; + +// ─── All States ───────────────────────────────────────────────────────────── + +/** Shows progression through every step position */ +export const AllStates: Story = { + render: () => ( + + {arrangementSteps.map((_, index) => ( + + + Step {index + 1} of {arrangementSteps.length} + + + + ))} + + ), +}; + +// ─── Two Steps ────────────────────────────────────────────────────────────── + +/** Minimal — 2-step flow (e.g. choose + confirm) */ +export const TwoSteps: Story = { + args: { + steps: [{ label: 'Choose' }, { label: 'Confirm' }], + currentStep: 0, + }, +}; + +// ─── Many Steps ───────────────────────────────────────────────────────────── + +/** Maximum — 8-step flow showing label truncation */ +export const ManySteps: Story = { + args: { + steps: [ + { label: 'Details' }, + { label: 'Director' }, + { label: 'Venue' }, + { label: 'Service' }, + { label: 'Coffin' }, + { label: 'Extras' }, + { label: 'Payment' }, + { label: 'Review' }, + ], + currentStep: 3, + }, +}; + +// ─── Interactive ──────────────────────────────────────────────────────────── + +/** Interactive — navigate forward and back through steps */ +export const Interactive: Story = { + render: function Render() { + const [step, setStep] = React.useState(0); + + return ( + + + + + + {arrangementSteps[step].label} + + + Step {step + 1} of {arrangementSteps.length} — content for this step would appear here. + + + + + + + + + ); + }, +}; + +// ─── Completed ────────────────────────────────────────────────────────────── + +/** All steps completed — final step active, all bars filled */ +export const Completed: Story = { + args: { + steps: arrangementSteps, + currentStep: arrangementSteps.length - 1, + }, +}; + +// ─── Narrow Container ─────────────────────────────────────────────────────── + +/** Mobile-width container showing responsive sizing */ +export const NarrowContainer: Story = { + decorators: [ + (Story) => ( + + + + ), + ], + args: { + steps: arrangementSteps, + currentStep: 2, + }, +}; diff --git a/src/components/molecules/StepIndicator/StepIndicator.tsx b/src/components/molecules/StepIndicator/StepIndicator.tsx new file mode 100644 index 0000000..ac59daf --- /dev/null +++ b/src/components/molecules/StepIndicator/StepIndicator.tsx @@ -0,0 +1,113 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import type { SxProps, Theme } from '@mui/material/styles'; +import { Typography } from '../../atoms/Typography'; + +// ─── Types ─────────────────────────────────────────────────────────────────── + +/** A single step in the progress indicator */ +export interface Step { + /** Step label displayed below the bar segment */ + label: string; +} + +/** Props for the FA StepIndicator molecule */ +export interface StepIndicatorProps { + /** Array of steps in order */ + steps: Step[]; + /** Zero-indexed current step (this step and all before it are filled) */ + currentStep: number; + /** MUI sx prop for style overrides */ + sx?: SxProps; +} + +// ─── Component ─────────────────────────────────────────────────────────────── + +/** + * Horizontal progress bar for multi-step flows in the FA design system. + * + * Shows a segmented bar where completed and current steps are filled with + * the brand colour, and upcoming steps are grey. Each segment has a label + * below it. The current step label is bold for emphasis. + * + * Maps to Figma "Progress Bar - Steps" (2375:47468). Supports 2-8 steps. + * + * Bar height: 10px desktop, 6px mobile (via responsive styles). + * Labels: body2 desktop, caption mobile. + * + * Usage: + * ```tsx + * + * ``` + */ +export const StepIndicator = React.forwardRef( + ({ steps, currentStep, sx }, ref) => { + return ( + + {steps.map((step, index) => { + const isCompleted = index < currentStep; + const isCurrent = index === currentStep; + const isFilled = isCompleted || isCurrent; + + return ( + + {/* Bar segment */} + + + {/* Step label */} + + {step.label} + + + ); + })} + + ); + }, +); + +StepIndicator.displayName = 'StepIndicator'; +export default StepIndicator; diff --git a/src/components/molecules/StepIndicator/index.ts b/src/components/molecules/StepIndicator/index.ts new file mode 100644 index 0000000..50594c2 --- /dev/null +++ b/src/components/molecules/StepIndicator/index.ts @@ -0,0 +1,2 @@ +export { StepIndicator } from './StepIndicator'; +export type { StepIndicatorProps, Step } from './StepIndicator';