Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,258 @@
import { TranslationProvider } from "@suaveplan/react-i18n";
import { describe, expect, it } from "@suaveplan/testing/runner";
import { renderComponent } from "@suaveplan/testing-dom";
import { createRef } from "react";
import { createTestI18n } from "../../../tests/setup/test-i18n.js";
import { ThemeProvider } from "../../theme/theme-provider/theme-provider.js";
import { lightTheme } from "../../theme/themes/light.js";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "./alert-dialog.js";
import {
alertDialogActionVariants,
alertDialogCancelVariants,
alertDialogContentVariants,
alertDialogDescriptionVariants,
alertDialogFooterVariants,
alertDialogHeaderVariants,
alertDialogOverlayVariants,
alertDialogTitleVariants,
} from "./alert-dialog.variants.js";
async function withProviders(
node: React.ReactElement
): Promise<React.ReactElement> {
const i18n = await createTestI18n({ locale: "en-GB" });
return (
<TranslationProvider i18n={i18n}>
<ThemeProvider themes={[lightTheme]} defaultTheme="light">
{node}
</ThemeProvider>
</TranslationProvider>
);
}
describe("<AlertDialog>", () => {
it("opens via the trigger and renders alertdialog role", {
timeout: 5000,
}, async () => {
const tree = await withProviders(
<AlertDialog>
<AlertDialogTrigger>Delete</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogTitle>Confirm</AlertDialogTitle>
<AlertDialogAction>OK</AlertDialogAction>
</AlertDialogContent>
</AlertDialog>
);
const { getByText, findByRole, user } = renderComponent(tree);
await user.click(getByText("Delete"));
const alertDialog = await findByRole("alertdialog");
expect(alertDialog).not.toBeNull();
});
it("Escape closes the dialog (cancel)", { timeout: 5000 }, async () => {
const tree = await withProviders(
<AlertDialog>
<AlertDialogTrigger>Delete</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogTitle>Confirm</AlertDialogTitle>
<AlertDialogAction>OK</AlertDialogAction>
</AlertDialogContent>
</AlertDialog>
);
const { getByText, findByRole, queryByRole, user } =
renderComponent(tree);
const trigger = getByText("Delete") as HTMLElement;
await user.click(trigger);
await findByRole("alertdialog");
await user.keyboard("{Escape}");
expect(queryByRole("alertdialog")).toBeNull();
expect(document.activeElement).toBe(trigger);
});
it("AlertDialogAction closes the dialog when activated", {
timeout: 5000,
}, async () => {
const tree = await withProviders(
<AlertDialog defaultOpen>
<AlertDialogContent>
<AlertDialogTitle>Heading</AlertDialogTitle>
<AlertDialogDescription>Body</AlertDialogDescription>
<AlertDialogAction>Confirm</AlertDialogAction>
</AlertDialogContent>
</AlertDialog>
);
const { findByRole, queryByRole, user } = renderComponent(tree);
const action = await findByRole("button", { name: "Confirm" });
await user.click(action);
expect(queryByRole("alertdialog")).toBeNull();
});
it("AlertDialogCancel closes the dialog when activated", {
timeout: 5000,
}, async () => {
const tree = await withProviders(
<AlertDialog defaultOpen>
<AlertDialogContent>
<AlertDialogTitle>Confirm</AlertDialogTitle>
<AlertDialogCancel>Cancel</AlertDialogCancel>
</AlertDialogContent>
</AlertDialog>
);
const { findByText, queryByRole, user } = renderComponent(tree);
await user.click(await findByText("Cancel"));
expect(queryByRole("alertdialog")).toBeNull();
});
it("does NOT close on overlay backdrop click (modal confirmation)", {
timeout: 5000,
}, async () => {
const tree = await withProviders(
<AlertDialog defaultOpen>
<AlertDialogContent>
<AlertDialogTitle>Modal</AlertDialogTitle>
<AlertDialogAction>OK</AlertDialogAction>
</AlertDialogContent>
</AlertDialog>
);
const { findByRole, queryByRole, user } = renderComponent(tree);
const dialog = await findByRole("alertdialog");
// Find the overlay (sibling of the content) and click it.
const overlay = dialog.parentElement?.querySelector(
"[data-state]:not([role])"
) as HTMLElement | null;
if (overlay !== null) {
await user.click(overlay);
}
expect(queryByRole("alertdialog")).not.toBeNull();
});
it("forwards refs on every sub-component", { timeout: 5000 }, async () => {
const triggerRef = createRef<HTMLButtonElement>();
const contentRef = createRef<HTMLDivElement>();
const titleRef = createRef<HTMLHeadingElement>();
const descRef = createRef<HTMLParagraphElement>();
const headerRef = createRef<HTMLDivElement>();
const footerRef = createRef<HTMLDivElement>();
const actionRef = createRef<HTMLButtonElement>();
const cancelRef = createRef<HTMLButtonElement>();
const tree = await withProviders(
<AlertDialog defaultOpen>
<AlertDialogTrigger ref={triggerRef}>Open</AlertDialogTrigger>
<AlertDialogContent ref={contentRef}>
<AlertDialogHeader ref={headerRef}>
<AlertDialogTitle ref={titleRef}>T</AlertDialogTitle>
<AlertDialogDescription ref={descRef}>
D
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter ref={footerRef}>
<AlertDialogCancel ref={cancelRef}>C</AlertDialogCancel>
<AlertDialogAction ref={actionRef}>A</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
renderComponent(tree);
expect(triggerRef.current).not.toBeNull();
expect(contentRef.current).not.toBeNull();
expect(titleRef.current).not.toBeNull();
expect(descRef.current).not.toBeNull();
expect(headerRef.current).not.toBeNull();
expect(footerRef.current).not.toBeNull();
expect(actionRef.current).not.toBeNull();
expect(cancelRef.current).not.toBeNull();
});
it("merges custom className", { timeout: 5000 }, async () => {
const tree = await withProviders(
<AlertDialog defaultOpen>
<AlertDialogContent className="c-extra">
<AlertDialogHeader className="h-extra">
<AlertDialogTitle className="t-extra">
T
</AlertDialogTitle>
<AlertDialogDescription className="d-extra">
D
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter className="f-extra">
<AlertDialogAction className="a-extra">
OK
</AlertDialogAction>
<AlertDialogCancel className="x-extra">
Cancel
</AlertDialogCancel>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
const { findByRole } = renderComponent(tree);
const dialog = await findByRole("alertdialog");
expect(dialog.className).toContain("c-extra");
expect(dialog.querySelector(".h-extra")).not.toBeNull();
expect(dialog.querySelector(".t-extra")).not.toBeNull();
expect(dialog.querySelector(".d-extra")).not.toBeNull();
expect(dialog.querySelector(".f-extra")).not.toBeNull();
expect(dialog.querySelector(".a-extra")).not.toBeNull();
expect(dialog.querySelector(".x-extra")).not.toBeNull();
});
it("applies the size variant to AlertDialogContent", {
timeout: 5000,
}, async () => {
const tree = await withProviders(
<AlertDialog defaultOpen>
<AlertDialogContent size="xl">
<AlertDialogTitle>T</AlertDialogTitle>
</AlertDialogContent>
</AlertDialog>
);
const { findByRole } = renderComponent(tree);
const dialog = await findByRole("alertdialog");
expect(dialog.className).toContain("max-w-4xl");
});
it("renders Cancel + Action buttons with their canonical styling", {
timeout: 5000,
}, async () => {
const tree = await withProviders(
<AlertDialog defaultOpen>
<AlertDialogContent>
<AlertDialogTitle>T</AlertDialogTitle>
<AlertDialogAction>OK</AlertDialogAction>
<AlertDialogCancel>Cancel</AlertDialogCancel>
</AlertDialogContent>
</AlertDialog>
);
const { findByText } = renderComponent(tree);
const action = await findByText("OK");
const cancel = await findByText("Cancel");
expect(action.className).toContain("bg-primary");
expect(cancel.className).toContain("border");
});
it("exposes every alert-dialog cva resolver", { timeout: 5000 }, () => {
expect(alertDialogOverlayVariants().length).toBeGreaterThan(0);
for (const size of ["sm", "default", "lg", "xl"] as const) {
expect(alertDialogContentVariants({ size }).length).toBeGreaterThan(
0
);
}
expect(alertDialogHeaderVariants().length).toBeGreaterThan(0);
expect(alertDialogFooterVariants().length).toBeGreaterThan(0);
expect(alertDialogTitleVariants().length).toBeGreaterThan(0);
expect(alertDialogDescriptionVariants().length).toBeGreaterThan(0);
expect(alertDialogActionVariants().length).toBeGreaterThan(0);
expect(alertDialogCancelVariants().length).toBeGreaterThan(0);
});
});