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;