import type { Meta, StoryObj } from "@storybook/react-vite"; import { within, expect } from "storybook/test"; import Flex from "./flex"; import "./flex.scss"; /** * Flexbox Utilities and Component Story * Comprehensive documentation of responsive flexbox utility classes and React Flex component */ const meta: Meta = { title: "FP.React Components/Layout/Flex", component: Flex, tags: ["autodocs", "beta"], parameters: { docs: { description: { component: ` # Flex Container Component A flexible container component for creating flexbox layouts with a declarative React API. Supports responsive props, preset variants, and compound pattern with Flex.Item and Flex.Spacer. ## Features - **Compound Pattern**: Use \`Flex.Item\` and \`Flex.Spacer\` sub-components - **Responsive Props**: Different layouts at sm/md/lg/xl breakpoints - **Preset Variants**: Common patterns like 'center', 'between', 'stack' - **CSS Custom Properties**: Runtime theming via styles prop - **Polymorphic**: Render as any HTML element via 'as' prop - **Type-Safe**: Full TypeScript support with autocomplete ## Usage Approaches ### 1. React Component (Recommended) \`\`\`tsx Content 1 Content 2 \`\`\` ### 2. Utility Classes (Direct HTML) \`\`\`html
Content 1
Content 2
\`\`\` ## Breakpoints - **sm**: 30rem (480px) - **md**: 48rem (768px) - **lg**: 62rem (992px) - **xl**: 80rem (1280px) ## CSS Custom Properties Override default spacing: \`\`\`css :root { --flex-gap-xs: 0.25rem; --flex-gap-sm: 0.5rem; --flex-gap-md: 0.75rem; --flex-gap-lg: 1rem; --flex-gap-xl: 1.5rem; } \`\`\` `, }, }, }, }; export default meta; type Story = StoryObj; // ============================================================================ // REACT COMPONENT STORIES // ============================================================================ /** * Basic Flex component usage */ export const FlexComponent: Story = { args: { gap: "md", children: ( <>
Item 1
Item 2
Item 3
), }, play: async ({ canvasElement, step }) => { const canvas = within(canvasElement); await step("Flex container renders correctly", async () => { const flexContainer = canvas.getByText("Item 1").parentElement?.parentElement; expect(flexContainer).toBeInTheDocument(); expect(flexContainer).toHaveClass("flex"); }); }, parameters: { docs: { description: { story: "Basic Flex component with gap spacing. The component automatically generates the appropriate utility classes from props.", }, }, }, }; /** * Flex with various layout props */ export const FlexWithProps: Story = { render: () => (

Row direction with space between

Left
Center
Right

Column direction with center alignment

Item 1
Item 2
Item 3
), parameters: { docs: { description: { story: "Demonstrates different combinations of direction, justify, align, and gap props.", }, }, }, }; /** * Responsive Flex layout */ export const FlexResponsive: Story = { render: () => (
Column on mobile
Row on medium+
flex="1" on medium+
Responsive layout with gap changes
Resize to see behavior
), parameters: { docs: { description: { story: "Responsive layout that changes from column on mobile to row on medium+ screens, with different gap sizes at different breakpoints.", }, }, chromatic: { viewports: [375, 768, 1280], }, }, }; /** * Flex.Item sub-component */ export const FlexWithItems: Story = { render: () => (

Equal width items (flex="1")

flex="1"
flex="1"
flex="1"

Mixed sizing (flex="none" + flex="1")

Fixed 8rem
Fills remaining space

Custom alignment (alignSelf)

Start
Center
End
), parameters: { docs: { description: { story: "Demonstrates Flex.Item sub-component with different flex sizing and alignment options.", }, }, }, }; /** * Flex.Spacer sub-component */ export const FlexWithSpacer: Story = { render: () => (

Push items to opposite edges

Left side
Right side

Multiple spacers for even distribution

Start
Middle
End
), parameters: { docs: { description: { story: "Flex.Spacer creates auto-expanding space (flex: 1) to push items apart. Commonly used for navbar layouts and toolbars.", }, }, }, }; /** * Preset variants */ export const FlexVariants: Story = { render: () => (

variant="center"

Centered both axes

variant="between"

Left
Right

variant="stack"

Stacked Item 1
Stacked Item 2
Stacked Item 3

Stack variant uses column layout by default, becomes row on medium+ screens

variant="spread"

Equal
Width
Items
), parameters: { docs: { description: { story: "Preset variants provide common flexbox patterns: 'center', 'between', 'around', 'stack', 'spread'.", }, }, }, }; /** * Nested Flex containers */ export const NestedFlex: Story = { render: () => (
Header Left
Nav 1
Nav 2
Nav 3
Sidebar
Main content area
Column 1
Column 2
), parameters: { docs: { description: { story: "Complex layout demonstrating nested Flex containers to create a typical application layout with header, sidebar, and main content.", }, }, }, }; /** * Custom styling with CSS variables */ export const CustomStyling: Story = { render: () => (
Custom gap via --flex-gap
Custom styles
Via styles prop
), parameters: { docs: { description: { story: "Demonstrates custom styling using CSS custom properties and inline styles via the styles prop.", }, }, }, }; /** * Polymorphic rendering */ export const PolymorphicFlex: Story = { render: () => (

Render as <nav>

Home About Contact

Render as <section>

Section Title

Content in a semantic section element

), parameters: { docs: { description: { story: "The 'as' prop allows Flex to render as any HTML element, enabling semantic markup while maintaining flexbox behavior.", }, }, }, }; // ============================================================================ // UTILITY CLASS STORIES (Preserved for backward compatibility) // ============================================================================ /** * Basic flex container with default gap */ export const BasicFlex: Story = { render: () => (
Item 1
Item 2
Item 3
), parameters: { docs: { description: { story: "Basic flex container with default gap. The `.flex` class provides `display: flex` with automatic gap spacing.", }, }, }, }; /** * Flex direction utilities */ export const FlexDirection: Story = { render: () => (

Row (default)

1
2
3

Column

1
2
3

Row Reverse

1
2
3
), parameters: { docs: { description: { story: "Flex direction utilities: `.flex-row`, `.flex-col`, `.flex-row-reverse`, `.flex-col-reverse`", }, }, }, }; /** * Justify content (main axis alignment) */ export const JustifyContent: Story = { render: () => (
{[ { class: "justify-start", label: "Start" }, { class: "justify-center", label: "Center" }, { class: "justify-end", label: "End" }, { class: "justify-between", label: "Space Between" }, { class: "justify-around", label: "Space Around" }, { class: "justify-evenly", label: "Space Evenly" }, ].map(({ class: className, label }) => (

{label}

A
B
C
))}
), parameters: { docs: { description: { story: "Justify content utilities control main axis alignment: `.justify-start`, `.justify-center`, `.justify-end`, `.justify-between`, `.justify-around`, `.justify-evenly`", }, }, }, }; /** * Align items (cross axis alignment) */ export const AlignItems: Story = { render: () => (
{[ { class: "items-start", label: "Start" }, { class: "items-center", label: "Center" }, { class: "items-end", label: "End" }, { class: "items-stretch", label: "Stretch" }, { class: "items-baseline", label: "Baseline" }, ].map(({ class: className, label }) => (

{label}

Small
Medium
Small
))}
), parameters: { docs: { description: { story: "Align items utilities control cross axis alignment: `.items-start`, `.items-center`, `.items-end`, `.items-stretch`, `.items-baseline`", }, }, }, }; /** * Gap utilities */ export const GapUtilities: Story = { render: () => (
{[ { class: "gap-0", label: "No Gap (gap-0)" }, { class: "gap-xs", label: "Extra Small (gap-xs)" }, { class: "gap-sm", label: "Small (gap-sm)" }, { class: "gap-md", label: "Medium (gap-md)" }, { class: "gap-lg", label: "Large (gap-lg)" }, { class: "gap-xl", label: "Extra Large (gap-xl)" }, ].map(({ class: className, label }) => (

{label}

Item 1
Item 2
Item 3
))}
), parameters: { docs: { description: { story: "Gap utilities use fluid spacing with `clamp()`. Available: `.gap-0`, `.gap-xs`, `.gap-sm`, `.gap-md`, `.gap-lg`, `.gap-xl`. Also supports `.row-gap-*` and `.col-gap-*` for independent control.", }, }, }, }; /** * Flex item sizing */ export const FlexSizing: Story = { render: () => (

flex-1 (Equal width items)

flex-1
flex-1
flex-1

flex-auto (Content-based sizing)

Short
Medium content here
Longer content that takes more space

flex-none (Fixed width)

Fixed 8rem
flex-1 fills remaining
), parameters: { docs: { description: { story: "Flex item sizing: `.flex-1` (equal distribution), `.flex-auto` (content-based), `.flex-initial` (default), `.flex-none` (no grow/shrink)", }, }, }, }; /** * Common patterns */ export const CommonPatterns: Story = { render: () => (

flex-center (Center both axes)

Centered Content

flex-between (Space between with center alignment)

Left
Right

flex-stack (Vertical stack, becomes row on md+)

Item 1
Item 2
Item 3

Resize viewport to see responsive behavior (column → row at 48rem)

flex-spread (Equal width children)

A
B
C
), parameters: { docs: { description: { story: "Common flexbox patterns: `.flex-center`, `.flex-between`, `.flex-around`, `.flex-stack`, `.flex-spread`", }, }, }, }; /** * Responsive utilities demonstration */ export const ResponsiveUtilities: Story = { render: () => (

Responsive Direction

Column on mobile, row on medium+ screens

Column 1
Column 2
Column 3

Responsive Gaps

Small gap on mobile, large gap on medium+ screens

1
2
3

Responsive Justification

Start alignment on mobile, space between on large+ screens

Left
Right
💡 Tip: Resize your browser window or use Storybook's viewport toolbar to see responsive behavior at different breakpoints (sm: 480px, md: 768px, lg: 992px, xl: 1280px).
), parameters: { docs: { description: { story: `Responsive modifiers allow utilities to activate at specific breakpoints. **Format:** \`{breakpoint}:{utility}\` **Examples:** - \`sm:flex-row\` - Row direction on screens ≥ 480px - \`md:justify-center\` - Center justify on screens ≥ 768px - \`lg:gap-xl\` - Extra large gap on screens ≥ 992px - \`xl:items-end\` - End alignment on screens ≥ 1280px All base utilities support responsive modifiers: direction, wrap, justify, align, gap, and flex sizing.`, }, }, chromatic: { viewports: [375, 480, 768, 992, 1280], }, }, }; /** * Wrapping behavior */ export const FlexWrap: Story = { render: () => (

flex-wrap (default)

{Array.from({ length: 12 }, (_, i) => (
Item {i + 1}
))}

flex-nowrap

{Array.from({ length: 12 }, (_, i) => (
Item {i + 1}
))}
), parameters: { docs: { description: { story: "Flex wrap utilities: `.flex-wrap`, `.flex-nowrap`, `.flex-wrap-reverse`", }, }, }, };