diff --git a/packages/calcite-components/src/components/modal/modal.e2e.ts b/packages/calcite-components/src/components/modal/modal.e2e.ts
index aa8068dc4a8..68100fcbecb 100644
--- a/packages/calcite-components/src/components/modal/modal.e2e.ts
+++ b/packages/calcite-components/src/components/modal/modal.e2e.ts
@@ -187,6 +187,48 @@ it("calls the beforeClose method prior to closing via attribute", async () => {
expect(await modal.getProperty("opened")).toBe(false);
});
+it("should handle rejected 'beforeClose' promise'", async () => {
+ const page = await newE2EPage();
+
+ const mockCallBack = jest.fn().mockReturnValue(() => Promise.reject());
+ await page.exposeFunction("beforeClose", mockCallBack);
+
+ await page.setContent(``);
+
+ await page.$eval(
+ "calcite-modal",
+ (elm: HTMLCalciteModalElement) =>
+ (elm.beforeClose = (window as typeof window & Pick).beforeClose)
+ );
+
+ const modal = await page.find("calcite-modal");
+ modal.setProperty("open", false);
+ await page.waitForChanges();
+
+ expect(mockCallBack).toHaveBeenCalledTimes(1);
+});
+
+it("should remain open with rejected 'beforeClose' promise'", async () => {
+ const page = await newE2EPage();
+
+ await page.exposeFunction("beforeClose", () => Promise.reject());
+ await page.setContent(``);
+
+ await page.$eval(
+ "calcite-modal",
+ (elm: HTMLCalciteModalElement) =>
+ (elm.beforeClose = (window as typeof window & Pick).beforeClose)
+ );
+
+ const modal = await page.find("calcite-modal");
+ modal.setProperty("open", false);
+ await page.waitForChanges();
+
+ expect(await modal.getProperty("open")).toBe(true);
+ expect(await modal.getProperty("opened")).toBe(true);
+ expect(modal.getAttribute("open")).toBe(""); // Makes sure attribute is added back
+});
+
describe("opening and closing behavior", () => {
it("opens and closes", async () => {
const page = await newE2EPage();
diff --git a/packages/calcite-components/src/components/modal/modal.tsx b/packages/calcite-components/src/components/modal/modal.tsx
index cc6fd44d864..db9dcffd38e 100644
--- a/packages/calcite-components/src/components/modal/modal.tsx
+++ b/packages/calcite-components/src/components/modal/modal.tsx
@@ -503,6 +503,10 @@ export class Modal
@Watch("open")
async toggleModal(value: boolean): Promise {
+ if (this.ignoreOpenChange) {
+ return;
+ }
+
onToggleOpenCloseComponent(this);
if (value) {
this.transitionEl?.classList.add(CSS.openingIdle);
@@ -557,7 +561,17 @@ export class Modal
}
if (this.beforeClose) {
- await this.beforeClose(this.el);
+ try {
+ await this.beforeClose(this.el);
+ } catch (_error) {
+ // close prevented
+ requestAnimationFrame(() => {
+ this.ignoreOpenChange = true;
+ this.open = true;
+ this.ignoreOpenChange = false;
+ });
+ return;
+ }
}
this.ignoreOpenChange = true;