Card compact and expanded template

This commit is contained in:
adilallo
2026-02-11 22:02:10 -07:00
parent f60df15c2b
commit b2ed1d438c
44 changed files with 1920 additions and 48 deletions
+14
View File
@@ -133,6 +133,20 @@ describe("Create", () => {
expect(stepper).toHaveAttribute("aria-valuemax", "5");
});
it("renders custom header when headerContent is provided", () => {
renderWithProviders(
<Create
{...defaultProps}
title="Default Title"
description="Default description"
headerContent={<span>Custom header</span>}
/>,
);
expect(screen.getByText("Custom header")).toBeInTheDocument();
expect(screen.queryByText("Default Title")).not.toBeInTheDocument();
expect(screen.queryByText("Default description")).not.toBeInTheDocument();
});
it("renders custom footer content", () => {
renderWithProviders(
<Create
+15
View File
@@ -1,6 +1,10 @@
import React from "react";
import { describe, it, expect } from "vitest";
import { screen } from "@testing-library/react";
import "@testing-library/jest-dom/vitest";
import TextArea from "../../app/components/controls/TextArea";
import { componentTestSuite } from "../utils/componentTestSuite";
import { renderWithProviders } from "../utils/test-utils";
type TextAreaProps = React.ComponentProps<typeof TextArea>;
@@ -28,3 +32,14 @@ componentTestSuite<TextAreaProps>({
errorProps: { error: true },
},
});
describe("TextArea appearance", () => {
it("renders with appearance embedded and applies borderless styling", () => {
renderWithProviders(
<TextArea label="Notes" value="Some text" appearance="embedded" />,
);
const textarea = screen.getByRole("textbox", { name: /notes/i });
expect(textarea).toBeInTheDocument();
expect(textarea).toHaveClass("border-0");
});
});
+68
View File
@@ -0,0 +1,68 @@
import {
renderWithProviders as render,
screen,
cleanup,
within,
} from "../utils/test-utils";
import userEvent from "@testing-library/user-event";
import { describe, test, expect, afterEach } from "vitest";
import CardsPage from "../../app/create/cards/page";
afterEach(() => {
cleanup();
});
describe("Create flow cards page", () => {
test("clicking a card opens the Create modal", async () => {
const user = userEvent.setup();
render(<CardsPage />);
const signalCards = screen.getAllByRole("button", {
name: /Signal: Encrypted messaging/,
});
await user.click(signalCards[0]);
const dialog = screen.getByRole("dialog");
expect(dialog).toBeInTheDocument();
expect(within(dialog).getByText("Signal")).toBeInTheDocument();
expect(within(dialog).getByText("Add Platform")).toBeInTheDocument();
});
test("renders without error", () => {
render(<CardsPage />);
expect(
screen.getByText("How should this community communicate with each-other?"),
).toBeInTheDocument();
});
test("renders HeaderLockup and CardStack content", () => {
render(<CardsPage />);
expect(
screen.getByText(
"You can select multiple methods for different needs or add your own",
),
).toBeInTheDocument();
expect(
screen.getByRole("button", { name: "See all communication approaches" }),
).toBeInTheDocument();
});
test("toggle expands and shows Show less", async () => {
const user = userEvent.setup();
render(<CardsPage />);
const toggle = screen.getByRole("button", {
name: "See all communication approaches",
});
await user.click(toggle);
expect(screen.getByRole("button", { name: "Show less" })).toBeInTheDocument();
expect(
screen.getByText(
"What method should this community use to communicate with eachother?",
),
).toBeInTheDocument();
});
});
+105
View File
@@ -0,0 +1,105 @@
import {
renderWithProviders as render,
screen,
fireEvent,
} from "../utils/test-utils";
import { describe, it, expect, vi } from "vitest";
import Card from "../../app/components/cards/Card";
describe("Card Component", () => {
const defaultProps = {
label: "Label",
supportText: "Support text here",
orientation: "horizontal",
};
it("renders label and supportText", () => {
render(<Card {...defaultProps} />);
expect(screen.getByText("Label")).toBeInTheDocument();
expect(screen.getByText("Support text here")).toBeInTheDocument();
});
it("renders RECOMMENDED pill when recommended is true", () => {
render(<Card {...defaultProps} recommended={true} />);
expect(screen.getByText("RECOMMENDED")).toBeInTheDocument();
});
it("does not render RECOMMENDED pill when recommended is false", () => {
render(<Card {...defaultProps} recommended={false} />);
expect(screen.queryByText("RECOMMENDED")).not.toBeInTheDocument();
});
it("renders SELECTED pill and inset dashed outline when selected is true", () => {
render(<Card {...defaultProps} selected={true} />);
expect(screen.getByText("SELECTED")).toBeInTheDocument();
const card = screen.getByRole("button");
expect(card).toHaveClass("outline-dashed");
});
it("applies horizontal layout by default", () => {
render(<Card {...defaultProps} />);
expect(screen.getByText("Label")).toBeInTheDocument();
expect(screen.getByText("Support text here")).toBeInTheDocument();
});
it("applies vertical layout when orientation is vertical", () => {
render(<Card {...defaultProps} orientation="vertical" />);
const card = screen.getByRole("button");
expect(card).toHaveClass("flex-row");
});
it("handles click events", () => {
const handleClick = vi.fn();
render(<Card {...defaultProps} onClick={handleClick} />);
const card = screen.getByRole("button");
fireEvent.click(card);
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("handles keyboard events", () => {
const handleClick = vi.fn();
render(<Card {...defaultProps} onClick={handleClick} />);
const card = screen.getByRole("button");
fireEvent.keyDown(card, { key: "Enter" });
expect(handleClick).toHaveBeenCalledTimes(1);
fireEvent.keyDown(card, { key: " " });
expect(handleClick).toHaveBeenCalledTimes(2);
});
it("renders with custom className", () => {
const customClass = "custom-card";
render(<Card {...defaultProps} className={customClass} />);
const card = screen.getByRole("button");
expect(card).toHaveClass(customClass);
});
it("renders with proper accessibility attributes", () => {
render(<Card {...defaultProps} />);
const card = screen.getByRole("button");
expect(card).toHaveAttribute(
"aria-label",
"Label: Support text here",
);
expect(card).toHaveAttribute("tabIndex", "0");
});
it("renders without supportText", () => {
render(<Card label="Label only" orientation="horizontal" />);
expect(screen.getByText("Label only")).toBeInTheDocument();
expect(screen.getByRole("button")).toHaveAttribute("aria-label", "Label only");
});
});
+101
View File
@@ -0,0 +1,101 @@
import {
renderWithProviders as render,
screen,
cleanup,
fireEvent,
} from "../utils/test-utils";
import userEvent from "@testing-library/user-event";
import { vi, describe, test, expect, afterEach } from "vitest";
import CardStack from "../../app/components/utility/CardStack";
const SAMPLE_CARDS = [
{ id: "1", label: "Option A", supportText: "Description A", recommended: true },
{ id: "2", label: "Option B", supportText: "Description B", recommended: false },
{ id: "3", label: "Option C", supportText: "Description C", recommended: true },
];
afterEach(() => {
cleanup();
});
describe("CardStack Component", () => {
test("renders header when title is provided", () => {
render(
<CardStack
cards={SAMPLE_CARDS}
title="How should this community communicate?"
description="Pick one or more."
/>,
);
expect(
screen.getByText("How should this community communicate?"),
).toBeInTheDocument();
expect(screen.getByText("Pick one or more.")).toBeInTheDocument();
});
test("renders up to 5 recommended cards in compact (grid) mode", () => {
render(<CardStack cards={SAMPLE_CARDS} expanded={false} />);
expect(screen.getAllByText("Option A").length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText("Option C").length).toBeGreaterThanOrEqual(1);
expect(screen.queryByText("Option B")).not.toBeInTheDocument();
});
test("renders all cards in expanded (list) mode", () => {
render(<CardStack cards={SAMPLE_CARDS} expanded={true} />);
expect(screen.getByText("Option A")).toBeInTheDocument();
expect(screen.getByText("Option B")).toBeInTheDocument();
expect(screen.getByText("Option C")).toBeInTheDocument();
});
test("shows See all toggle when hasMore is true", () => {
render(<CardStack cards={SAMPLE_CARDS} hasMore={true} />);
expect(
screen.getByRole("button", { name: "See all communication approaches" }),
).toBeInTheDocument();
});
test("does not show toggle when hasMore is false", () => {
render(<CardStack cards={SAMPLE_CARDS} hasMore={false} />);
expect(
screen.queryByRole("button", { name: "See all communication approaches" }),
).not.toBeInTheDocument();
});
test("toggle expands when clicked", async () => {
const user = userEvent.setup();
render(<CardStack cards={SAMPLE_CARDS} hasMore={true} />);
const toggle = screen.getByRole("button", {
name: "See all communication approaches",
});
await user.click(toggle);
expect(
screen.getByRole("button", { name: "Show less" }),
).toBeInTheDocument();
});
test("calls onCardSelect when a card is clicked", () => {
const onCardSelect = vi.fn();
render(
<CardStack cards={SAMPLE_CARDS} onCardSelect={onCardSelect} />,
);
const cardButtons = screen.getAllByRole("button", {
name: "Option A: Description A",
});
fireEvent.click(cardButtons[0]);
expect(onCardSelect).toHaveBeenCalledWith("1");
});
test("renders with selectedId", () => {
render(<CardStack cards={SAMPLE_CARDS} selectedId="1" />);
expect(screen.getAllByText("SELECTED").length).toBeGreaterThanOrEqual(1);
});
});
+49
View File
@@ -0,0 +1,49 @@
import { describe, test, expect } from "vitest";
import { screen } from "@testing-library/react";
import "@testing-library/jest-dom/vitest";
import { renderWithProviders } from "../utils/test-utils";
import Scrollbar from "../../app/components/utility/Scrollbar";
describe("Scrollbar", () => {
test("renders children", () => {
renderWithProviders(
<Scrollbar>
<span>Scrollable content</span>
</Scrollbar>,
);
expect(screen.getByText("Scrollable content")).toBeInTheDocument();
});
test("wrapper has scrollbar-design class and overflow-y-auto for default orientation", () => {
const { container } = renderWithProviders(
<Scrollbar>
<div>Content</div>
</Scrollbar>,
);
const wrapper = container.firstChild;
expect(wrapper).toHaveClass("scrollbar-design");
expect(wrapper).toHaveClass("overflow-y-auto");
});
test("applies horizontal overflow when orientation is horizontal", () => {
const { container } = renderWithProviders(
<Scrollbar orientation="horizontal">
<div>Content</div>
</Scrollbar>,
);
const wrapper = container.firstChild;
expect(wrapper).toHaveClass("scrollbar-design");
expect(wrapper).toHaveClass("overflow-x-auto");
});
test("applies overflow-auto when orientation is both", () => {
const { container } = renderWithProviders(
<Scrollbar orientation="both">
<div>Content</div>
</Scrollbar>,
);
const wrapper = container.firstChild;
expect(wrapper).toHaveClass("scrollbar-design");
expect(wrapper).toHaveClass("overflow-auto");
});
});
+23
View File
@@ -0,0 +1,23 @@
import { describe, test, expect } from "vitest";
import { screen } from "@testing-library/react";
import "@testing-library/jest-dom/vitest";
import { renderWithProviders } from "../utils/test-utils";
import Tag from "../../app/components/utility/Tag";
describe("Tag", () => {
test("renders with variant recommended and shows default label RECOMMENDED", () => {
renderWithProviders(<Tag variant="recommended" />);
expect(screen.getByText("RECOMMENDED")).toBeInTheDocument();
});
test("renders with variant selected and shows default label SELECTED", () => {
renderWithProviders(<Tag variant="selected" />);
expect(screen.getByText("SELECTED")).toBeInTheDocument();
});
test("renders custom children when provided", () => {
renderWithProviders(<Tag variant="recommended">Custom label</Tag>);
expect(screen.getByText("Custom label")).toBeInTheDocument();
expect(screen.queryByText("RECOMMENDED")).not.toBeInTheDocument();
});
});