diff --git a/packages/calcite-components/src/components/tooltip/TooltipManager.ts b/packages/calcite-components/src/components/tooltip/TooltipManager.ts index 4c6da729fac..5ee59a8df6b 100644 --- a/packages/calcite-components/src/components/tooltip/TooltipManager.ts +++ b/packages/calcite-components/src/components/tooltip/TooltipManager.ts @@ -140,22 +140,29 @@ export default class TooltipManager { this.toggleTooltip(tooltip, true); }; - private focusInHandler = (event: FocusEvent): void => { - this.queryFocusedTooltip(event, true); + private blurHandler = (): void => { + this.closeActiveTooltip(); }; - private focusOutHandler = (event: FocusEvent): void => { - this.queryFocusedTooltip(event, false); + private focusInHandler = (event: FocusEvent): void => { + const composedPath = event.composedPath(); + const tooltip = this.queryTooltip(composedPath); + + this.closeTooltipIfNotActive(tooltip); + + if (!tooltip) { + return; + } + + this.toggleFocusedTooltip(tooltip, true); }; private addShadowListeners(shadowRoot: ShadowRoot): void { shadowRoot.addEventListener("focusin", this.focusInHandler, { capture: true }); - shadowRoot.addEventListener("focusout", this.focusOutHandler, { capture: true }); } private removeShadowListeners(shadowRoot: ShadowRoot): void { shadowRoot.removeEventListener("focusin", this.focusInHandler, { capture: true }); - shadowRoot.removeEventListener("focusout", this.focusOutHandler, { capture: true }); } private addListeners(): void { @@ -163,7 +170,7 @@ export default class TooltipManager { window.addEventListener("pointermove", this.pointerMoveHandler, { capture: true }); window.addEventListener("click", this.clickHandler, { capture: true }); window.addEventListener("focusin", this.focusInHandler, { capture: true }); - window.addEventListener("focusout", this.focusOutHandler, { capture: true }); + window.addEventListener("blur", this.blurHandler); } private removeListeners(): void { @@ -171,7 +178,7 @@ export default class TooltipManager { window.removeEventListener("pointermove", this.pointerMoveHandler, { capture: true }); window.removeEventListener("click", this.clickHandler, { capture: true }); window.removeEventListener("focusin", this.focusInHandler, { capture: true }); - window.removeEventListener("focusout", this.focusOutHandler, { capture: true }); + window.removeEventListener("blur", this.blurHandler); } private clearHoverOpenTimeout(): void { @@ -242,23 +249,11 @@ export default class TooltipManager { }, TOOLTIP_CLOSE_DELAY_MS); }; - private queryFocusedTooltip(event: FocusEvent, open: boolean): void { - const composedPath = event.composedPath(); - const tooltip = this.queryTooltip(composedPath); - - this.closeTooltipIfNotActive(tooltip); - - if (!tooltip) { - return; - } - - this.toggleFocusedTooltip(tooltip, open); - } - private registerShadowRoot(shadowRoot: ShadowRoot): void { const { registeredShadowRootCounts } = this; - const newCount = (registeredShadowRootCounts.get(shadowRoot) ?? 0) + 1; + const count = registeredShadowRootCounts.get(shadowRoot); + const newCount = Math.min((typeof count === "number" ? count : 0) + 1, 1); if (newCount === 1) { this.addShadowListeners(shadowRoot); @@ -270,7 +265,8 @@ export default class TooltipManager { private unregisterShadowRoot(shadowRoot: ShadowRoot): void { const { registeredShadowRootCounts } = this; - const newCount = registeredShadowRootCounts.get(shadowRoot) - 1; + const count = registeredShadowRootCounts.get(shadowRoot); + const newCount = Math.max((typeof count === "number" ? count : 1) - 1, 0); if (newCount === 0) { this.removeShadowListeners(shadowRoot); diff --git a/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts b/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts index 446be26d13d..a26d919bd90 100644 --- a/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts +++ b/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts @@ -1108,4 +1108,55 @@ describe("calcite-tooltip", () => { expect(await tooltip1.getProperty("open")).toBe(false); expect(await tooltip2.getProperty("open")).toBe(true); }); + + describe("allows clicking on an open tooltip", () => { + const pageContent = html` + content + + + `; + + it("should work when clicking on a reference element first", async () => { + const page = await newE2EPage(); + await page.setContent(pageContent); + await page.waitForChanges(); + const tooltip = await page.find("calcite-tooltip"); + const referenceElement = await page.find("#ref"); + + await referenceElement.click(); + await page.waitForChanges(); + expect(await tooltip.getProperty("open")).toBe(true); + + await tooltip.click(); + await page.waitForChanges(); + expect(await tooltip.getProperty("open")).toBe(true); + + await page.$eval("#other", (el: HTMLElement) => { + el.dispatchEvent(new MouseEvent("click", { cancelable: true, bubbles: true })); + }); + await page.waitForChanges(); + expect(await tooltip.getProperty("open")).toBe(false); + }); + + it("should work when focusing on a reference element first", async () => { + const page = await newE2EPage(); + await page.setContent(pageContent); + await page.waitForChanges(); + const tooltip = await page.find("calcite-tooltip"); + const referenceElement = await page.find("#ref"); + + await referenceElement.focus(); + await page.waitForChanges(); + expect(await tooltip.getProperty("open")).toBe(true); + + await tooltip.click(); + await page.waitForChanges(); + expect(await tooltip.getProperty("open")).toBe(true); + + const other = await page.find("#other"); + await other.focus(); + await page.waitForChanges(); + expect(await tooltip.getProperty("open")).toBe(false); + }); + }); });