diff --git a/packages/calcite-components/src/components/tooltip/TooltipManager.ts b/packages/calcite-components/src/components/tooltip/TooltipManager.ts index a1739687393..4c6da729fac 100644 --- a/packages/calcite-components/src/components/tooltip/TooltipManager.ts +++ b/packages/calcite-components/src/components/tooltip/TooltipManager.ts @@ -18,10 +18,6 @@ export default class TooltipManager { private hoverCloseTimeout: number = null; - private hoveredTooltip: HTMLCalciteTooltipElement = null; - - private clickedTooltip: HTMLCalciteTooltipElement = null; - private activeTooltip: HTMLCalciteTooltipElement = null; private registeredElementCount = 0; @@ -96,38 +92,52 @@ export default class TooltipManager { private pointerMoveHandler = (event: PointerEvent): void => { const composedPath = event.composedPath(); const { activeTooltip } = this; - const hoveringActiveTooltip = activeTooltip?.open && composedPath.includes(activeTooltip); - - if (hoveringActiveTooltip) { - this.clearHoverTimeout(); - return; - } const tooltip = this.queryTooltip(composedPath); - this.hoveredTooltip = tooltip; - if (this.isClosableClickedTooltip(tooltip)) { + if (this.pathHasOpenTooltip(tooltip, composedPath)) { + this.clearHoverTimeout(); return; } - this.clickedTooltip = null; - if (tooltip) { this.openHoveredTooltip(tooltip); - } else if (activeTooltip) { + } else if (activeTooltip?.open) { this.closeHoveredTooltip(); } }; - private clickHandler = (event: PointerEvent): void => { - const clickedTooltip = this.queryTooltip(event.composedPath()); + private pathHasOpenTooltip(tooltip: HTMLCalciteTooltipElement, composedPath: EventTarget[]): boolean { + const { activeTooltip } = this; - this.clickedTooltip = clickedTooltip; + return ( + (activeTooltip?.open && composedPath.includes(activeTooltip)) || (tooltip?.open && composedPath.includes(tooltip)) + ); + } - if (clickedTooltip?.closeOnClick) { - this.toggleTooltip(clickedTooltip, false); + private clickHandler = (event: Event): void => { + const composedPath = event.composedPath(); + const tooltip = this.queryTooltip(composedPath); + + if (this.pathHasOpenTooltip(tooltip, composedPath)) { this.clearHoverTimeout(); + return; } + + this.closeActiveTooltip(); + + if (!tooltip) { + return; + } + + this.clearHoverTimeout(); + + if (tooltip.closeOnClick) { + this.toggleTooltip(tooltip, false); + return; + } + + this.toggleTooltip(tooltip, true); }; private focusInHandler = (event: FocusEvent): void => { @@ -179,6 +189,12 @@ export default class TooltipManager { this.clearHoverCloseTimeout(); } + private closeTooltipIfNotActive(tooltip: HTMLCalciteTooltipElement): void { + if (this.activeTooltip !== tooltip) { + this.closeActiveTooltip(); + } + } + private closeActiveTooltip(): void { const { activeTooltip } = this; @@ -188,8 +204,6 @@ export default class TooltipManager { } private toggleFocusedTooltip(tooltip: HTMLCalciteTooltipElement, open: boolean): void { - this.closeActiveTooltip(); - if (open) { this.clearHoverTimeout(); } @@ -211,20 +225,10 @@ export default class TooltipManager { } this.clearHoverCloseTimeout(); - - if (this.activeTooltip === this.hoveredTooltip) { - return; - } - - this.closeActiveTooltip(); - - if (tooltip !== this.hoveredTooltip) { - return; - } - + this.closeTooltipIfNotActive(tooltip); this.toggleTooltip(tooltip, true); }, - this.activeTooltip ? 0 : TOOLTIP_OPEN_DELAY_MS, + this.activeTooltip?.open ? 0 : TOOLTIP_OPEN_DELAY_MS, ); }; @@ -239,19 +243,18 @@ export default class TooltipManager { }; private queryFocusedTooltip(event: FocusEvent, open: boolean): void { - const tooltip = this.queryTooltip(event.composedPath()); + const composedPath = event.composedPath(); + const tooltip = this.queryTooltip(composedPath); + + this.closeTooltipIfNotActive(tooltip); - if (!tooltip || this.isClosableClickedTooltip(tooltip)) { + if (!tooltip) { return; } this.toggleFocusedTooltip(tooltip, open); } - private isClosableClickedTooltip(tooltip: HTMLCalciteTooltipElement): boolean { - return tooltip?.closeOnClick && tooltip === this.clickedTooltip; - } - private registerShadowRoot(shadowRoot: ShadowRoot): void { const { registeredShadowRootCounts } = this; diff --git a/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts b/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts index 36a0a9ebbd9..446be26d13d 100644 --- a/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts +++ b/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts @@ -371,7 +371,7 @@ describe("calcite-tooltip", () => { expect(await tooltip.getProperty("open")).toBe(false); }); - it("should not open tooltip when clicked", async () => { + it("should handle mouse events", async () => { const page = await newE2EPage(); await page.setContent(html` @@ -388,7 +388,37 @@ describe("calcite-tooltip", () => { await page.evaluate(() => { const ref = document.getElementById("ref"); - ref.click(); + const event1 = new MouseEvent("click", { + cancelable: true, + bubbles: true, + }); + ref.dispatchEvent(event1); + }); + + await page.waitForChanges(); + + expect(await tooltip.getProperty("open")).toBe(true); + + await page.evaluate(() => { + const ref = document.getElementById("ref"); + const event1 = new MouseEvent("click", { + cancelable: true, + bubbles: true, + }); + ref.dispatchEvent(event1); + }); + + await page.waitForChanges(); + + expect(await tooltip.getProperty("open")).toBe(true); + + await page.evaluate(() => { + const ref = document.getElementById("test"); + const event1 = new MouseEvent("click", { + cancelable: true, + bubbles: true, + }); + ref.dispatchEvent(event1); }); await page.waitForChanges(); @@ -516,7 +546,7 @@ describe("calcite-tooltip", () => { expect(await hoverTip.getProperty("open")).toBe(false); await page.$eval("#hoverRef", (el: HTMLElement) => { - el.dispatchEvent(new Event("pointermove")); + el.dispatchEvent(new PointerEvent("pointermove")); }); await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS); @@ -572,7 +602,7 @@ describe("calcite-tooltip", () => { expect(await hoverTip.getProperty("open")).toBe(false); await page.$eval("#hoverRef", (el: HTMLElement) => { - el.dispatchEvent(new Event("pointermove")); + el.dispatchEvent(new PointerEvent("pointermove")); }); await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS); @@ -911,7 +941,7 @@ describe("calcite-tooltip", () => { const { delay, selector } = pointerMoves[i]; await page.waitForTimeout(delay); await page.$eval(selector, (el: HTMLElement) => { - el.dispatchEvent(new Event("pointermove")); + el.dispatchEvent(new PointerEvent("pointermove")); }); expect(await tooltip.getProperty(pointerMoves[i].property)).toBe(pointerMoves[i].value); @@ -970,7 +1000,7 @@ describe("calcite-tooltip", () => { const { delay, selector } = pointerMoves[i]; await page.waitForTimeout(delay); await page.$eval(selector, (el: HTMLElement) => { - el.dispatchEvent(new Event("pointermove")); + el.dispatchEvent(new PointerEvent("pointermove")); }); expect(await tooltip.getProperty(pointerMoves[i].property)).toBe(pointerMoves[i].value); @@ -1029,7 +1059,7 @@ describe("calcite-tooltip", () => { .querySelector("shadow-component-b") .shadowRoot.querySelector("shadow-component-a") .shadowRoot.querySelector("button"); - referenceElement.dispatchEvent(new Event("focusin")); + referenceElement.dispatchEvent(new FocusEvent("focusin")); }); } @@ -1061,7 +1091,7 @@ describe("calcite-tooltip", () => { expect(await tooltip2.getProperty("open")).toBe(false); await page.$eval("#ref1", (el: HTMLElement) => { - el.dispatchEvent(new Event("pointermove")); + el.dispatchEvent(new PointerEvent("pointermove")); }); await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS); await page.waitForChanges(); @@ -1070,7 +1100,7 @@ describe("calcite-tooltip", () => { expect(await tooltip2.getProperty("open")).toBe(false); await page.$eval("#ref2", (el: HTMLElement) => { - el.dispatchEvent(new Event("pointermove")); + el.dispatchEvent(new PointerEvent("pointermove")); }); await page.waitForTimeout(0); await page.waitForChanges();