From edd59eb0bff21aa41fc7e537a2df2dbd2143a15a Mon Sep 17 00:00:00 2001 From: Matt Driscoll Date: Tue, 20 Jun 2023 17:01:35 -0700 Subject: [PATCH] fix(alert): update alert queue when an alert is removed from the DOM (#7189) **Related Issue:** #6616 ## Summary - Adds new internal event `calciteInternalAlertUnregister` to dispatch when an alert is removed from the DOM. - Event is dispatched on the window since that is where the listeners are and dispatching on the element won't reach the window since the element would no longer be in the DOM. - When the event is received, the queue is updated to remove the removed alert element. - queue is kept n'sync. ![giphy](https://github.com/Esri/calcite-components/assets/1231455/691e1744-6045-48b2-8e55-97479a2b8a5b) --- .../src/components/alert/alert.e2e.ts | 71 +++++++++++++++++++ .../src/components/alert/alert.tsx | 20 +++++- .../src/components/alert/interfaces.ts | 5 ++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/packages/calcite-components/src/components/alert/alert.e2e.ts b/packages/calcite-components/src/components/alert/alert.e2e.ts index aad265b893e..3c98f1d01bf 100644 --- a/packages/calcite-components/src/components/alert/alert.e2e.ts +++ b/packages/calcite-components/src/components/alert/alert.e2e.ts @@ -328,6 +328,77 @@ describe("calcite-alert", () => { expect(await container.isVisible()).toBe(false); }); + it("should update number of queued alerts with a calcite-chip when removing an alert", async () => { + const page = await newE2EPage(); + await page.setContent(html` + open alert + open alert + + open alert + + +
Title of alert Uno
+
Message text of the alert Uno
+ Retry +
+ + +
Title of alert Dos
+
Message text of the alert Dos
+ Retry +
+ + +
Title of alert Dos
+
Message text of the alert Dos
+ Retry +
+ `); + const buttonOne = await page.find("#buttonOne"); + const buttonTwo = await page.find("#buttonTwo"); + const buttonThree = await page.find("#buttonThree"); + const alertOne = await page.find("#first-open"); + const alertTwo = await page.find("#second-open"); + const alertThree = await page.find("#third-open"); + + await buttonOne.click(); + await page.waitForTimeout(animationDurationInMs); + expect(await alertOne.isVisible()).toBe(true); + + await buttonTwo.click(); + expect(await alertTwo.isVisible()).toBe(true); + + await buttonThree.click(); + expect(await alertThree.isVisible()).toBe(true); + + const chip = await page.find("calcite-alert[id='first-open'] >>> calcite-chip"); + const chipQueueCount2 = "+2"; + expect(await chip.getProperty("value")).toEqual(chipQueueCount2); + expect(chip.textContent).toEqual(chipQueueCount2); + + await page.$eval("#third-open", (alert: HTMLCalciteAlertElement) => { + alert.remove(); + }); + await page.waitForChanges(); + + const chipQueueCount1 = "+1"; + expect(await chip.getProperty("value")).toEqual(chipQueueCount1); + expect(chip.textContent).toEqual(chipQueueCount1); + + await page.$eval("#second-open", (alert: HTMLCalciteAlertElement) => { + alert.remove(); + }); + await page.waitForChanges(); + + expect(await page.find("calcite-alert[id='first-open'] >>> calcite-chip")).toBeNull(); + }); + describe("auto-close behavior on queued items", () => { it("should display number of queued alerts with a calcite-chip", async () => { const page = await newE2EPage(); diff --git a/packages/calcite-components/src/components/alert/alert.tsx b/packages/calcite-components/src/components/alert/alert.tsx index a5c84406150..c4f3acec46c 100644 --- a/packages/calcite-components/src/components/alert/alert.tsx +++ b/packages/calcite-components/src/components/alert/alert.tsx @@ -46,7 +46,7 @@ import { import { Kind, Scale } from "../interfaces"; import { KindIcons } from "../resources"; import { AlertMessages } from "./assets/alert/t9n"; -import { AlertDuration, Sync } from "./interfaces"; +import { AlertDuration, Sync, Unregister } from "./interfaces"; import { CSS, DURATIONS, SLOTS } from "./resources"; /** @@ -203,6 +203,11 @@ export class Alert implements OpenCloseComponent, LoadableComponent, T9nComponen } disconnectedCallback(): void { + window.dispatchEvent( + new CustomEvent("calciteInternalAlertUnregister", { + detail: { alert: this.el } + }) + ); window.clearTimeout(this.autoCloseTimeoutId); window.clearTimeout(this.queueTimeout); disconnectOpenCloseComponent(this); @@ -353,6 +358,19 @@ export class Alert implements OpenCloseComponent, LoadableComponent, T9nComponen this.determineActiveAlert(); } + // Event is dispatched on the window because the element is not in the DOM so bubbling won't occur. + @Listen("calciteInternalAlertUnregister", { target: "window" }) + alertUnregister(event: CustomEvent): void { + const queue = this.queue.filter((el) => el !== event.detail.alert); + this.queue = queue; + + window.dispatchEvent( + new CustomEvent("calciteInternalAlertSync", { + detail: { queue } + }) + ); + } + //-------------------------------------------------------------------------- // // Public Methods diff --git a/packages/calcite-components/src/components/alert/interfaces.ts b/packages/calcite-components/src/components/alert/interfaces.ts index 0ffe0b27966..f3ac1d8bb90 100644 --- a/packages/calcite-components/src/components/alert/interfaces.ts +++ b/packages/calcite-components/src/components/alert/interfaces.ts @@ -1,4 +1,9 @@ export type AlertDuration = "fast" | "medium" | "slow"; + export interface Sync { queue: HTMLCalciteAlertElement[]; } + +export interface Unregister { + alert: HTMLCalciteAlertElement; +}