import React from "react";
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import { Grid, GridItem } from "./grid";
import type { GridProps, GridItemProps } from "./grid.types";
describe("Grid Component", () => {
describe("Rendering", () => {
it("should render with default props", () => {
render(Content);
const grid = screen.getByTestId("grid");
expect(grid).toBeInTheDocument();
expect(grid.tagName).toBe("DIV");
expect(grid).toHaveClass("grid");
});
it("should render children correctly", () => {
render(
Item 1
Item 2
Item 3
);
expect(screen.getByText("Item 1")).toBeInTheDocument();
expect(screen.getByText("Item 2")).toBeInTheDocument();
expect(screen.getByText("Item 3")).toBeInTheDocument();
});
it("should render with text content", () => {
render(Plain text content);
expect(screen.getByText("Plain text content")).toBeInTheDocument();
});
it("should render with Grid.Item children", () => {
render(
Item 1
Item 2
);
expect(screen.getByText("Item 1")).toBeInTheDocument();
expect(screen.getByText("Item 2")).toBeInTheDocument();
});
});
describe("Polymorphic Rendering (as prop)", () => {
it("should render as div by default", () => {
render(Content);
expect(screen.getByTestId("grid").tagName).toBe("DIV");
});
it("should render as section when as='section'", () => {
render(
Content
);
expect(screen.getByTestId("grid").tagName).toBe("SECTION");
});
it("should render as article when as='article'", () => {
render(
Content
);
expect(screen.getByTestId("grid").tagName).toBe("ARTICLE");
});
it("should render as ul when as='ul'", () => {
render(
Item 1
);
expect(screen.getByTestId("grid").tagName).toBe("UL");
});
it("should render as ol when as='ol'", () => {
render(
Item 1
);
expect(screen.getByTestId("grid").tagName).toBe("OL");
});
});
describe("Columns Prop", () => {
it("should apply columns=1 class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-cols-1");
});
it("should apply columns=2 class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-cols-2");
});
it("should apply columns=3 class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-cols-3");
});
it("should apply columns=4 class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-cols-4");
});
it("should apply columns=6 class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-cols-6");
});
it("should apply columns=12 class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-cols-12");
});
it("should work without columns prop", () => {
render(Content);
const grid = screen.getByTestId("grid");
expect(grid).toHaveClass("grid");
expect(grid).not.toHaveClass("grid-cols-1");
});
});
describe("Auto Prop (Auto-Fit/Auto-Fill)", () => {
it("should apply auto='fit' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-auto-fit");
});
it("should apply auto='fill' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-auto-fill");
});
it("should apply inline style with auto='fit'", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveStyle({
gridTemplateColumns: "repeat(auto-fit, minmax(15rem, 1fr))",
});
});
it("should apply inline style with auto='fill'", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveStyle({
gridTemplateColumns: "repeat(auto-fill, minmax(20rem, 1fr))",
});
});
});
describe("Gap Prop", () => {
it("should apply gap='0' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-gap-0");
});
it("should apply gap='xs' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-gap-xs");
});
it("should apply gap='sm' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-gap-sm");
});
it("should apply gap='md' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-gap-md");
});
it("should apply gap='lg' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-gap-lg");
});
it("should apply gap='xl' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-gap-xl");
});
});
describe("GapX and GapY Props", () => {
it("should apply gapX='md' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-gap-x-md");
});
it("should apply gapY='lg' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-gap-y-lg");
});
it("should apply both gapX and gapY together", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveClass("grid-gap-x-xl");
expect(grid).toHaveClass("grid-gap-y-xs");
});
it("should work with gap and gapX/gapY together", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveClass("grid-gap-md");
expect(grid).toHaveClass("grid-gap-x-lg");
});
});
describe("JustifyItems Prop", () => {
it("should apply justifyItems='start' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-justify-items-start");
});
it("should apply justifyItems='center' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-justify-items-center");
});
it("should apply justifyItems='end' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-justify-items-end");
});
it("should apply justifyItems='stretch' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-justify-items-stretch");
});
});
describe("AlignItems Prop", () => {
it("should apply alignItems='start' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-align-items-start");
});
it("should apply alignItems='center' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-align-items-center");
});
it("should apply alignItems='end' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-align-items-end");
});
it("should apply alignItems='stretch' class", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveClass("grid-align-items-stretch");
});
});
describe("Combined Props", () => {
it("should apply columns and gap together", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveClass("grid");
expect(grid).toHaveClass("grid-cols-3");
expect(grid).toHaveClass("grid-gap-md");
});
it("should apply all alignment props together", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveClass("grid-cols-4");
expect(grid).toHaveClass("grid-gap-lg");
expect(grid).toHaveClass("grid-justify-items-center");
expect(grid).toHaveClass("grid-align-items-center");
});
it("should combine all props with polymorphic as prop", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid.tagName).toBe("SECTION");
expect(grid).toHaveClass("grid");
expect(grid).toHaveClass("grid-cols-2");
expect(grid).toHaveClass("grid-gap-md");
expect(grid).toHaveClass("grid-justify-items-start");
expect(grid).toHaveClass("grid-align-items-stretch");
});
});
describe("ClassName and Classes Props", () => {
it("should merge className prop with utility classes", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveClass("grid");
expect(grid).toHaveClass("custom-class");
});
it("should merge classes prop with utility classes", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveClass("grid");
expect(grid).toHaveClass("another-class");
});
it("should merge both className and classes", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveClass("grid");
expect(grid).toHaveClass("custom");
expect(grid).toHaveClass("another");
});
it("should merge custom classes with all utility classes", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveClass("grid");
expect(grid).toHaveClass("grid-cols-3");
expect(grid).toHaveClass("grid-gap-md");
expect(grid).toHaveClass("grid-justify-items-center");
expect(grid).toHaveClass("custom");
expect(grid).toHaveClass("utility");
});
});
describe("Inline Styles", () => {
it("should apply inline styles via styles prop", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveStyle({ maxWidth: "1200px" });
});
it("should apply inline styles via style prop", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveStyle({ border: "1px solid red" });
});
it("should merge style and styles props", () => {
render(
Content
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveStyle({
border: "1px solid red",
maxWidth: "1200px",
});
});
});
describe("Ref Forwarding", () => {
it("should forward ref to the underlying element", () => {
const ref = { current: null as HTMLDivElement | null };
render(
Content
);
expect(ref.current).toBeInstanceOf(HTMLDivElement);
expect(ref.current).toBe(screen.getByTestId("grid"));
});
it("should forward ref with polymorphic as prop", () => {
const ref = { current: null as HTMLElement | null };
render(
Content
);
expect(ref.current).toBeInstanceOf(HTMLElement);
expect(ref.current?.tagName).toBe("SECTION");
});
});
describe("ARIA and Accessibility Attributes", () => {
it("should accept aria-label", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveAttribute("aria-label", "Product grid");
});
it("should accept aria-labelledby", () => {
render(
<>
Products
Product 1
>
);
expect(screen.getByTestId("grid")).toHaveAttribute("aria-labelledby", "grid-title");
});
it("should accept role attribute", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveAttribute("role", "list");
});
it("should accept data attributes", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveAttribute("data-component", "product-grid");
});
it("should accept id attribute", () => {
render(
Content
);
expect(screen.getByTestId("grid")).toHaveAttribute("id", "main-grid");
});
});
describe("Edge Cases", () => {
it("should handle empty children", () => {
render();
expect(screen.getByTestId("grid")).toBeInTheDocument();
expect(screen.getByTestId("grid")).toBeEmptyDOMElement();
});
it("should handle null children", () => {
render({null});
expect(screen.getByTestId("grid")).toBeInTheDocument();
});
it("should handle undefined children", () => {
render({undefined});
expect(screen.getByTestId("grid")).toBeInTheDocument();
});
it("should handle false children (conditional rendering)", () => {
const shouldShow = false;
render({shouldShow && Hidden
});
expect(screen.getByTestId("grid")).toBeInTheDocument();
expect(screen.queryByText("Hidden")).not.toBeInTheDocument();
});
});
describe("Real-World Usage Patterns", () => {
it("should render 3-column card grid correctly", () => {
render(
{[...Array(6)].map((_, i) => (
Card {i + 1}
))}
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveClass("grid-cols-3");
expect(grid).toHaveClass("grid-gap-md");
expect(screen.getByText("Card 1")).toBeInTheDocument();
expect(screen.getByText("Card 6")).toBeInTheDocument();
});
it("should render auto-fit responsive grid correctly", () => {
render(
Item 1
Item 2
Item 3
);
const grid = screen.getByTestId("grid");
expect(grid).toHaveClass("grid-auto-fit");
expect(grid).toHaveStyle({
gridTemplateColumns: "repeat(auto-fit, minmax(15rem, 1fr))",
});
});
it("should render form layout correctly", () => {
render(
);
expect(screen.getByTestId("grid")).toHaveClass("grid-cols-2");
expect(screen.getByLabelText("Name")).toBeInTheDocument();
expect(screen.getByLabelText("Email")).toBeInTheDocument();
});
});
describe("TypeScript Type Safety", () => {
it("should accept valid GridProps", () => {
const props: GridProps = {
columns: 3,
gap: "md",
justifyItems: "center",
alignItems: "center",
as: "section",
className: "custom",
children: Content
,
};
render();
expect(screen.getByTestId("grid")).toBeInTheDocument();
});
it("should accept all column values (1-12)", () => {
const columns: GridProps["columns"][] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
columns.forEach((col, index) => {
render(
Content
);
expect(screen.getByTestId(`grid-${index}`)).toHaveClass(`grid-cols-${col}`);
});
});
});
});
describe("GridItem Component", () => {
describe("Rendering", () => {
it("should render with default props", () => {
render(Content);
const item = screen.getByTestId("grid-item");
expect(item).toBeInTheDocument();
expect(item.tagName).toBe("DIV");
});
it("should render children correctly", () => {
render(Item content);
expect(screen.getByText("Item content")).toBeInTheDocument();
});
});
describe("Polymorphic Rendering (as prop)", () => {
it("should render as div by default", () => {
render(Content);
expect(screen.getByTestId("grid-item").tagName).toBe("DIV");
});
it("should render as li when as='li'", () => {
render(
Content
);
expect(screen.getByTestId("grid-item").tagName).toBe("LI");
});
it("should render as section when as='section'", () => {
render(
Content
);
expect(screen.getByTestId("grid-item").tagName).toBe("SECTION");
});
it("should render as article when as='article'", () => {
render(
Content
);
expect(screen.getByTestId("grid-item").tagName).toBe("ARTICLE");
});
});
describe("Span Prop (Column Span)", () => {
it("should apply span=1 class", () => {
render(
Content
);
expect(screen.getByTestId("grid-item")).toHaveClass("grid-col-span-1");
});
it("should apply span=2 class", () => {
render(
Content
);
expect(screen.getByTestId("grid-item")).toHaveClass("grid-col-span-2");
});
it("should apply span=4 class", () => {
render(
Content
);
expect(screen.getByTestId("grid-item")).toHaveClass("grid-col-span-4");
});
it("should apply span=6 class", () => {
render(
Content
);
expect(screen.getByTestId("grid-item")).toHaveClass("grid-col-span-6");
});
it("should apply span=8 class", () => {
render(
Content
);
expect(screen.getByTestId("grid-item")).toHaveClass("grid-col-span-8");
});
it("should apply span=12 class", () => {
render(
Content
);
expect(screen.getByTestId("grid-item")).toHaveClass("grid-col-span-12");
});
it("should work without span prop", () => {
render(Content);
const item = screen.getByTestId("grid-item");
expect(item).not.toHaveClass("grid-col-span-1");
});
});
describe("RowSpan Prop", () => {
it("should apply rowSpan=1 class", () => {
render(
Content
);
expect(screen.getByTestId("grid-item")).toHaveClass("grid-row-span-1");
});
it("should apply rowSpan=2 class", () => {
render(
Content
);
expect(screen.getByTestId("grid-item")).toHaveClass("grid-row-span-2");
});
it("should apply rowSpan=3 class", () => {
render(
Content
);
expect(screen.getByTestId("grid-item")).toHaveClass("grid-row-span-3");
});
it("should work without rowSpan prop", () => {
render(Content);
const item = screen.getByTestId("grid-item");
expect(item).not.toHaveClass("grid-row-span-1");
});
});
describe("Combined Span Props", () => {
it("should apply both span and rowSpan together", () => {
render(
Content
);
const item = screen.getByTestId("grid-item");
expect(item).toHaveClass("grid-col-span-4");
expect(item).toHaveClass("grid-row-span-2");
});
it("should combine span with polymorphic as prop", () => {
render(
Content
);
const item = screen.getByTestId("grid-item");
expect(item.tagName).toBe("ARTICLE");
expect(item).toHaveClass("grid-col-span-6");
});
});
describe("ClassName and Classes Props", () => {
it("should merge className prop with utility classes", () => {
render(
Content
);
const item = screen.getByTestId("grid-item");
expect(item).toHaveClass("custom-class");
});
it("should merge classes prop with utility classes", () => {
render(
Content
);
const item = screen.getByTestId("grid-item");
expect(item).toHaveClass("another-class");
});
it("should merge custom classes with span classes", () => {
render(
Content
);
const item = screen.getByTestId("grid-item");
expect(item).toHaveClass("grid-col-span-4");
expect(item).toHaveClass("custom");
expect(item).toHaveClass("utility");
});
});
describe("Ref Forwarding", () => {
it("should forward ref to the underlying element", () => {
const ref = { current: null as HTMLDivElement | null };
render(
Content
);
expect(ref.current).toBeInstanceOf(HTMLDivElement);
expect(ref.current).toBe(screen.getByTestId("grid-item"));
});
it("should forward ref with polymorphic as prop", () => {
const ref = { current: null as HTMLElement | null };
render(
Content
);
expect(ref.current).toBeInstanceOf(HTMLElement);
expect(ref.current?.tagName).toBe("ARTICLE");
});
});
describe("Grid with Grid.Item Integration", () => {
it("should render Grid with Grid.Item children", () => {
render(
Main content
Sidebar
);
const grid = screen.getByTestId("grid");
const main = screen.getByTestId("main");
const sidebar = screen.getByTestId("sidebar");
expect(grid).toHaveClass("grid-cols-12");
expect(main).toHaveClass("grid-col-span-8");
expect(sidebar).toHaveClass("grid-col-span-4");
});
it("should handle mixed Grid.Item and regular children", () => {
render(
Spans 2 columns
Regular div
);
expect(screen.getByText("Spans 2 columns")).toBeInTheDocument();
expect(screen.getByText("Regular div")).toBeInTheDocument();
});
});
describe("TypeScript Type Safety", () => {
it("should accept valid GridItemProps", () => {
const props: GridItemProps = {
span: 4,
rowSpan: 2,
as: "article",
className: "custom",
children: Content
,
};
render();
expect(screen.getByTestId("grid-item")).toBeInTheDocument();
});
it("should accept all span values (1-12)", () => {
const spans: GridItemProps["span"][] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
spans.forEach((span, index) => {
render(
Content
);
expect(screen.getByTestId(`item-${index}`)).toHaveClass(`grid-col-span-${span}`);
});
});
});
});