diff --git a/tests/Modal.test.svelte b/tests/Modal.test.svelte
deleted file mode 100644
index e9f5332b..00000000
--- a/tests/Modal.test.svelte
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
- {
- console.log(detail);
- open = false;
- }}
- on:open
- on:close
- on:submit
- on:click:button--primary
->
- Create a new Cloudant database in the US South region.
-
diff --git a/tests/Modal/Modal.test.svelte b/tests/Modal/Modal.test.svelte
new file mode 100644
index 00000000..d6ac5f26
--- /dev/null
+++ b/tests/Modal/Modal.test.svelte
@@ -0,0 +1,57 @@
+
+
+ console.log("open")}
+ on:close={() => console.log("close")}
+ on:submit={() => console.log("submit")}
+ on:click:button--primary={() => console.log("click:button--primary")}
+ on:click:button--secondary={(e) =>
+ console.log("click:button--secondary", e.detail)}
+ on:transitionend={(e) => console.log("transitionend", e.detail)}
+>
+
+
+
diff --git a/tests/Modal/Modal.test.ts b/tests/Modal/Modal.test.ts
new file mode 100644
index 00000000..10268226
--- /dev/null
+++ b/tests/Modal/Modal.test.ts
@@ -0,0 +1,284 @@
+import { render, screen } from "@testing-library/svelte";
+import { tick } from "svelte";
+import { user } from "../setup-tests";
+import ModalTest from "./Modal.test.svelte";
+
+describe("Modal", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it("renders with default props", async () => {
+ const { container } = render(ModalTest, {
+ props: {
+ open: true,
+ modalHeading: "Test Modal",
+ primaryButtonText: "Save",
+ secondaryButtonText: "Cancel",
+ },
+ });
+
+ // Check if modal container is rendered
+ const modalContainer = container.querySelector(".bx--modal-container");
+ expect(modalContainer).toBeInTheDocument();
+
+ // Check if modal heading is rendered
+ expect(screen.getByText("Test Modal")).toBeInTheDocument();
+
+ // Check if buttons are rendered
+ expect(screen.getByText("Save")).toBeInTheDocument();
+ expect(screen.getByText("Cancel")).toBeInTheDocument();
+
+ // Check if close button is rendered
+ const closeButton = screen.getByLabelText("Close the modal");
+ expect(closeButton).toBeInTheDocument();
+
+ // Check if modal has correct ARIA attributes
+ expect(modalContainer).toHaveAttribute("role", "dialog");
+ expect(modalContainer).toHaveAttribute("aria-modal", "true");
+ expect(modalContainer).toHaveAttribute("aria-label", "Test Modal");
+ });
+
+ it("renders with basic structure", () => {
+ render(ModalTest, {
+ props: {
+ open: true,
+ modalHeading: "Test Modal",
+ primaryButtonText: "Save",
+ secondaryButtonText: "Cancel",
+ },
+ });
+
+ expect(screen.getByRole("dialog")).toBeInTheDocument();
+ expect(screen.getByText("Test Modal")).toBeInTheDocument();
+ expect(screen.getByText("Save")).toBeInTheDocument();
+ expect(screen.getByText("Cancel")).toBeInTheDocument();
+ expect(screen.getByLabelText("Close the modal")).toBeInTheDocument();
+ expect(screen.getByRole("dialog")).toHaveAttribute("aria-modal", "true");
+ });
+
+ it("opens and closes properly", async () => {
+ const consoleLog = vi.spyOn(console, "log");
+ const { component } = render(ModalTest, {
+ props: {
+ open: false,
+ modalHeading: "Test Modal",
+ },
+ });
+
+ // Open the modal
+ component.$set({ open: true });
+ await tick();
+ expect(screen.getByRole("dialog")).toBeInTheDocument();
+ expect(consoleLog).toHaveBeenCalledWith("open");
+
+ // Close the modal
+ component.$set({ open: false });
+ await tick();
+ expect(consoleLog).toHaveBeenCalledWith("close");
+ });
+
+ it("handles form submission", async () => {
+ const consoleLog = vi.spyOn(console, "log");
+ render(ModalTest, {
+ props: {
+ open: true,
+ hasForm: true,
+ modalHeading: "Form Modal",
+ primaryButtonText: "Save",
+ },
+ });
+
+ const primaryButton = screen.getByRole("button", { name: "Save" });
+ await user.click(primaryButton);
+ expect(consoleLog).toHaveBeenCalledWith("submit");
+ expect(consoleLog).toHaveBeenCalledWith("click:button--primary");
+ });
+
+ it("handles button clicks", async () => {
+ const consoleLog = vi.spyOn(console, "log");
+ render(ModalTest, {
+ props: {
+ open: true,
+ primaryButtonText: "Save",
+ secondaryButtonText: "Cancel",
+ },
+ });
+
+ await user.click(screen.getByText("Save"));
+ expect(consoleLog).toHaveBeenCalledWith("click:button--primary");
+
+ await user.click(screen.getByText("Cancel"));
+ expect(consoleLog).toHaveBeenCalledWith("click:button--secondary", {
+ text: "Cancel",
+ });
+ });
+
+ it("supports different modal sizes", () => {
+ type Size = "xs" | "sm" | "lg";
+ const sizeMappings = {
+ xs: "bx--modal-container--xs",
+ sm: "bx--modal-container--sm",
+ lg: "bx--modal-container--lg",
+ } as const;
+
+ // Test specific sizes
+ (Object.keys(sizeMappings) as Size[]).forEach((size) => {
+ const { unmount } = render(ModalTest, {
+ props: {
+ open: true,
+ size,
+ modalHeading: `${size} Modal`,
+ },
+ });
+
+ const modal = screen.getByRole("dialog");
+ expect(modal).toHaveClass(sizeMappings[size]);
+ unmount();
+ });
+
+ // Test default (medium) size
+ const { unmount } = render(ModalTest, {
+ props: {
+ open: true,
+ modalHeading: "Medium Modal",
+ },
+ });
+
+ const modal = screen.getByRole("dialog");
+ expect(modal).toHaveClass("bx--modal-container");
+ expect(modal).not.toHaveClass("bx--modal-container--xs");
+ expect(modal).not.toHaveClass("bx--modal-container--sm");
+ expect(modal).not.toHaveClass("bx--modal-container--lg");
+ unmount();
+ });
+
+ it("supports danger and alert variants", () => {
+ render(ModalTest, {
+ props: {
+ open: true,
+ danger: true,
+ alert: true,
+ modalHeading: "Danger Alert Modal",
+ primaryButtonText: "Delete",
+ },
+ });
+
+ const primaryButton = screen.getByRole("button", { name: "Delete" });
+ expect(primaryButton).toHaveClass("bx--btn--danger");
+
+ const modal = screen.getByRole("alertdialog");
+ expect(modal).toHaveAttribute("aria-label", "Danger Alert Modal");
+ });
+
+ it("handles scrolling content", () => {
+ render(ModalTest, {
+ props: {
+ open: true,
+ hasScrollingContent: true,
+ modalHeading: "Scrolling Modal",
+ },
+ });
+
+ const modalBody = screen.getByRole("region");
+ expect(modalBody).toHaveClass("bx--modal-scroll-content");
+ });
+
+ it("should focus close button when open", async () => {
+ render(ModalTest, {
+ props: {
+ open: true,
+ },
+ });
+
+ const closeButton = screen.getByLabelText("Close the modal");
+ expect(closeButton).toHaveFocus();
+ });
+
+ it("respects the selectorPrimaryFocus prop", async () => {
+ render(ModalTest, {
+ props: {
+ open: true,
+ modalHeading: "Focus Test",
+ selectorPrimaryFocus: "#test-focus",
+ },
+ });
+
+ expect(screen.getByTestId("test-focus")).toHaveFocus();
+ });
+
+ it("prevents closing when clicking outside if configured", async () => {
+ const { component } = render(ModalTest, {
+ props: {
+ open: true,
+ preventCloseOnClickOutside: true,
+ modalHeading: "Prevent Close Test",
+ },
+ });
+
+ const closeHandler = vi.fn();
+ component.$on("close", closeHandler);
+
+ // Click outside the modal
+ await user.click(document.body);
+ expect(closeHandler).not.toHaveBeenCalled();
+ });
+
+ it("supports passive modal variant", () => {
+ render(ModalTest, {
+ props: {
+ open: true,
+ passiveModal: true,
+ modalHeading: "Passive Modal",
+ primaryButtonText: "Save",
+ secondaryButtonText: "Cancel",
+ },
+ });
+
+ // Verify close button is in header
+ const closeButton = screen.getByLabelText("Close the modal");
+ expect(closeButton.closest(".bx--modal-header")).toBeInTheDocument();
+
+ // Verify no footer is present
+ expect(
+ screen.queryByRole("button", { name: "Save" }),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.queryByRole("button", { name: "Cancel" }),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.queryByRole("button", { name: "Close the modal" }),
+ ).toBeInTheDocument();
+ });
+
+ it("handles closing through various methods", async () => {
+ const consoleLog = vi.spyOn(console, "log");
+ const { component } = render(ModalTest, {
+ props: {
+ open: true,
+ modalHeading: "Close Test Modal",
+ },
+ });
+
+ // Close via escape key
+ await user.keyboard("{Escape}");
+ expect(consoleLog).toHaveBeenCalledWith("close");
+
+ component.$set({ open: true });
+ await tick();
+
+ expect(consoleLog).toHaveBeenCalledWith("open");
+
+ // Close via clicking outside
+ await user.click(document.body);
+ expect(consoleLog).toHaveBeenCalledWith("close");
+
+ component.$set({ open: true });
+ await tick();
+
+ // Close via close button
+ const closeButton = screen.getByLabelText("Close the modal");
+ await user.click(closeButton);
+ expect(consoleLog).toHaveBeenCalledWith("close");
+ });
+});
diff --git a/tests/ModalExtraSmall.test.svelte b/tests/ModalExtraSmall.test.svelte
deleted file mode 100644
index 455c8f66..00000000
--- a/tests/ModalExtraSmall.test.svelte
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- Create a new Cloudant database in the US South region.
-
diff --git a/tests/ModalLarge.test.svelte b/tests/ModalLarge.test.svelte
deleted file mode 100644
index 62f2d83f..00000000
--- a/tests/ModalLarge.test.svelte
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- Create a new Cloudant database in the US South region.
-
diff --git a/tests/ModalPreventOutsideClick.test.svelte b/tests/ModalPreventOutsideClick.test.svelte
deleted file mode 100644
index 3dff7788..00000000
--- a/tests/ModalPreventOutsideClick.test.svelte
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- Create a new Cloudant database in the US South region.
-
diff --git a/tests/ModalSmall.test.svelte b/tests/ModalSmall.test.svelte
deleted file mode 100644
index 0144c685..00000000
--- a/tests/ModalSmall.test.svelte
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- Create a new Cloudant database in the US South region.
-