From 41efb78335a6b0483ac5db4e1fa2920776ae87c0 Mon Sep 17 00:00:00 2001 From: Richie Date: Sun, 29 Mar 2026 14:16:55 +1100 Subject: [PATCH] Add Collapse atom for progressive disclosure Thin MUI Collapse wrapper with unmountOnExit default. Used in the arrangement wizard to reveal fields after a selection is made (slide-down animation). Stories include interactive toggle and wizard field-reveal pattern demo. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../atoms/Collapse/Collapse.stories.tsx | 165 ++++++++++++++++++ src/components/atoms/Collapse/Collapse.tsx | 43 +++++ src/components/atoms/Collapse/index.ts | 2 + 3 files changed, 210 insertions(+) create mode 100644 src/components/atoms/Collapse/Collapse.stories.tsx create mode 100644 src/components/atoms/Collapse/Collapse.tsx create mode 100644 src/components/atoms/Collapse/index.ts diff --git a/src/components/atoms/Collapse/Collapse.stories.tsx b/src/components/atoms/Collapse/Collapse.stories.tsx new file mode 100644 index 0000000..eaaac3b --- /dev/null +++ b/src/components/atoms/Collapse/Collapse.stories.tsx @@ -0,0 +1,165 @@ +import { useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { Collapse } from './Collapse'; +import Box from '@mui/material/Box'; +import { Typography } from '../Typography'; +import { Button } from '../Button'; + +const meta: Meta = { + title: 'Atoms/Collapse', + component: Collapse, + tags: ['autodocs'], + parameters: { + layout: 'centered', + }, + argTypes: { + in: { + control: 'boolean', + description: 'Whether the content is expanded', + }, + timeout: { + control: 'number', + description: 'Transition duration in ms (or { enter, exit })', + table: { defaultValue: { summary: '300' } }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +// ─── Default (controlled via args) ────────────────────────────────────────── + +/** Toggle the `in` control to expand/collapse */ +export const Default: Story = { + args: { + in: true, + }, + render: (args) => ( + + + + + This content is revealed with a smooth slide-down animation. + + + + + ), +}; + +// ─── Interactive toggle ───────────────────────────────────────────────────── + +/** Click the button to toggle progressive disclosure */ +export const Interactive: Story = { + render: () => { + const [open, setOpen] = useState(false); + return ( + + + + + + Additional details are revealed here. + + + This pattern is used in the wizard for progressive disclosure — fields appear after a + previous selection is made. + + + + + ); + }, +}; + +// ─── Wizard field reveal ──────────────────────────────────────────────────── + +/** Simulates the wizard pattern: selecting an option reveals the next field */ +export const WizardFieldReveal: Story = { + render: () => { + const [step, setStep] = useState(0); + return ( + + + Who is this funeral being arranged for? + + + + + + + = 2}> + + + Has the person died? + + + + + + + + + ); + }, +}; + +// ─── Collapsed ────────────────────────────────────────────────────────────── + +/** Content is hidden (collapsed state) */ +export const Collapsed: Story = { + args: { + in: false, + }, + render: (args) => ( + + + The content below is collapsed: + + + + You should not see this. + + + + ), +}; diff --git a/src/components/atoms/Collapse/Collapse.tsx b/src/components/atoms/Collapse/Collapse.tsx new file mode 100644 index 0000000..fa2a1cc --- /dev/null +++ b/src/components/atoms/Collapse/Collapse.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import MuiCollapse from '@mui/material/Collapse'; +import type { CollapseProps as MuiCollapseProps } from '@mui/material/Collapse'; + +// ─── Types ─────────────────────────────────────────────────────────────────── + +/** Props for the FA Collapse component */ +export interface CollapseProps extends MuiCollapseProps { + /** Whether the content is expanded */ + in: boolean; + /** Content to reveal/hide */ + children: React.ReactNode; +} + +// ─── Component ─────────────────────────────────────────────────────────────── + +/** + * Progressive disclosure wrapper for the FA design system. + * + * Thin wrapper around MUI Collapse with sensible defaults for the + * arrangement wizard's progressive disclosure pattern (fields revealed + * after a selection is made). + * + * Uses a smooth slide-down animation. Unmounts children when collapsed + * to keep the DOM clean and prevent focus on hidden fields. + * + * Usage: + * ```tsx + * + * + * + * ``` + */ +export const Collapse = React.forwardRef( + ({ children, ...props }, ref) => ( + + {children} + + ), +); + +Collapse.displayName = 'Collapse'; +export default Collapse; diff --git a/src/components/atoms/Collapse/index.ts b/src/components/atoms/Collapse/index.ts new file mode 100644 index 0000000..4ea7034 --- /dev/null +++ b/src/components/atoms/Collapse/index.ts @@ -0,0 +1,2 @@ +export { default } from './Collapse'; +export * from './Collapse';