Initial Commit
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user