import type { Meta, StoryObj } from "@storybook/react-vite"; import { within, expect } from "storybook/test"; import { Row } from "./row"; const meta: Meta = { title: "FP.React Components/Layout/Row", component: Row, tags: ["autodocs", "beta", "layout"], parameters: { docs: { description: { component: "Row provides a flex container for 12-column layouts with customizable gap, alignment, and wrapping. Always includes the `.col-row` base class and adds variant utilities based on props. Use with Col components for responsive column layouts.", }, }, }, argTypes: { gap: { control: "select", options: ["0", "xs", "sm", "md", "lg", "xl"], description: "Gap size between columns", }, justify: { control: "select", options: ["start", "center", "end", "between", "around", "evenly"], description: "Horizontal alignment (justify-content)", }, align: { control: "select", options: ["start", "center", "end", "baseline", "stretch"], description: "Vertical alignment (align-items)", }, wrap: { control: "select", options: ["wrap", "nowrap", "wrap-reverse"], description: "Flex wrap behavior", }, alwaysProportional: { control: "boolean", description: "Maintains proportional layout on tablets+ (≥480px). Columns still stack on phones (<480px).", }, as: { control: "select", options: ["div", "section", "article", "ul", "ol", "nav"], description: "Element type to render", }, }, }; export default meta; type Story = StoryObj; /** * Default Row with basic two-column layout. * Demonstrates the simplest usage with 50/50 columns. */ export const Default: Story = { args: { children: ( <>
Column 1 (50%)
Column 2 (50%)
), }, play: async ({ canvasElement, step }) => { const canvas = within(canvasElement); await step("Row renders with .col-row class", async () => { const row = canvasElement.querySelector(".col-row"); expect(row).toBeInTheDocument(); }); await step("Columns render correctly", async () => { expect(canvas.getByText(/Column 1/)).toBeInTheDocument(); expect(canvas.getByText(/Column 2/)).toBeInTheDocument(); }); }, }; /** * Custom Gap - Demonstrates all gap size options. * Shows how gap prop controls spacing between columns. */ export const CustomGap: Story = { args: { gap: "xl", children: ( <>
Column 1
Column 2
Column 3
), }, play: async ({ canvasElement, step }) => { await step("Row has gap-xl utility class", async () => { const row = canvasElement.querySelector(".col-row-gap-xl"); expect(row).toBeInTheDocument(); }); }, }; /** * Justify Content - Demonstrates horizontal alignment options. * Shows center, end, space-between, space-around, and space-evenly. */ export const JustifyContent: Story = { args: { justify: "center", children: ( <>
Centered
Content
), }, play: async ({ canvasElement, step }) => { await step("Row has justify-center utility class", async () => { const row = canvasElement.querySelector(".col-row-justify-center"); expect(row).toBeInTheDocument(); }); }, }; /** * Align Items - Demonstrates vertical alignment options. * Uses columns with different heights to show alignment effect. */ export const AlignItems: Story = { args: { align: "center", children: ( <>
Tall Column
Short
Medium
), }, play: async ({ canvasElement, step }) => { await step("Row has align-center utility class", async () => { const row = canvasElement.querySelector(".col-row-align-center"); expect(row).toBeInTheDocument(); }); }, }; /** * Complex Layout - Demonstrates combining multiple features. * Shows gap, justify, align, and various column spans together. */ export const ComplexLayout: Story = { args: { gap: "lg", justify: "between", align: "stretch", children: ( <>
Col 3
Col 6 (Main Content)
Col 3
), }, play: async ({ canvasElement, step }) => { await step("Row has all utility classes", async () => { const row = canvasElement.querySelector( ".col-row.col-row-gap-lg.col-row-justify-between.col-row-align-stretch" ); expect(row).toBeInTheDocument(); }); }, }; /** * Responsive Behavior - Demonstrates mobile-first responsive behavior. * Columns stack vertically on mobile (<48rem) and display side-by-side on desktop. */ export const ResponsiveBehavior: Story = { args: { gap: "md", children: ( <>
100% mobile, 33% desktop
100% mobile, 33% desktop
100% mobile, 33% desktop
), }, parameters: { docs: { description: { story: "Resize the viewport to see responsive behavior. Columns stack on mobile (<768px) and display in a row on desktop (>=768px).", }, }, }, }; /** * Responsive Stacking - Default mobile-first behavior. * Below 768px, columns stack vertically at 100% width. * Demonstrates the default responsive stacking behavior. */ export const ResponsiveStacking: Story = { args: { gap: "md", children: ( <>
Column 1 (col-6)
Column 2 (col-6)
), }, parameters: { docs: { description: { story: "Default responsive behavior. Columns stack on mobile (< 768px) and display side-by-side on desktop (≥ 768px).", }, }, }, play: async ({ canvasElement, step }) => { await step("Row renders without proportional class", async () => { const row = canvasElement.querySelector(".col-row"); expect(row).toBeInTheDocument(); expect(row).not.toHaveClass("col-row-proportional"); }); }, }; /** * Always Proportional - Maintains layout on tablets and larger. * Columns stack on phones (< 480px) but maintain 50/50 split on tablets+ (≥ 480px). * Prevents wrapping on tablets and desktop while still stacking on phones. */ export const AlwaysProportional: Story = { args: { alwaysProportional: true, gap: "md", children: ( <>
Column 1 (50% on tablets+)
Column 2 (50% on tablets+)
), }, parameters: { docs: { description: { story: "Proportional layout mode. Columns still stack on phones (< 480px) but maintain their proportional widths on tablets (≥ 480px) and larger. Resize to see the difference from default behavior.", }, }, }, play: async ({ canvasElement, step }) => { await step("Row has proportional class", async () => { const row = canvasElement.querySelector(".col-row-proportional"); expect(row).toBeInTheDocument(); }); }, }; /** * Three Column Proportional - Multiple columns with proportional layout. * Demonstrates three-column layout that maintains proportions on tablets and larger. */ export const ThreeColumnsProportional: Story = { args: { alwaysProportional: true, gap: "sm", children: ( <>
33.33%
33.33%
33.33%
), }, parameters: { docs: { description: { story: "Three-column layout with proportional mode. Columns remain in a 3-column layout on tablets and desktop, but stack on phones.", }, }, }, play: async ({ canvasElement, step }) => { await step("Row has proportional class", async () => { const row = canvasElement.querySelector(".col-row-proportional"); expect(row).toBeInTheDocument(); }); await step("All three columns render", async () => { const columns = canvasElement.querySelectorAll(".col-4"); expect(columns).toHaveLength(3); }); }, }; /** * Semantic HTML - Demonstrates using different element types. * Shows Row as a