diff --git a/packages/calcite-components/src/components/popover/PopoverManager.ts b/packages/calcite-components/src/components/popover/PopoverManager.ts index 74cc669c574..a295cf325e6 100644 --- a/packages/calcite-components/src/components/popover/PopoverManager.ts +++ b/packages/calcite-components/src/components/popover/PopoverManager.ts @@ -1,9 +1,26 @@ // @ts-strict-ignore import { ReferenceElement } from "../../utils/floating-ui"; import { isActivationKey } from "../../utils/key"; -import { isKeyboardTriggeredClick } from "../../utils/dom"; +import { isKeyboardTriggeredClick, isPrimaryPointerButton } from "../../utils/dom"; import type { Popover } from "./popover"; +const clickTolerance = 5; + +export function isDrag({ + startX, + startY, + endX, + endY, +}: { + startX: number; + startY: number; + endX: number; + endY: number; +}): boolean { + const distance = Math.hypot(endX - startX, endY - startY); + return distance > clickTolerance; +} + export default class PopoverManager { // -------------------------------------------------------------------------- // @@ -15,6 +32,8 @@ export default class PopoverManager { private registeredElementCount = 0; + private pointerDownPosition?: { x: number; y: number }; + // -------------------------------------------------------------------------- // // Public Methods @@ -86,20 +105,43 @@ export default class PopoverManager { } }; + private pointerDownHandler = (event: PointerEvent): void => { + if (event.defaultPrevented || !isPrimaryPointerButton(event)) { + return; + } + + const { clientX, clientY } = event; + this.pointerDownPosition = { x: clientX, y: clientY }; + }; + private clickHandler = (event: PointerEvent): void => { - if (isKeyboardTriggeredClick(event) || event.defaultPrevented || window.getSelection()?.type === "Range") { + if ( + isKeyboardTriggeredClick(event) || + event.defaultPrevented || + (this.pointerDownPosition && + isDrag({ + endY: event.clientY, + endX: event.clientX, + startY: this.pointerDownPosition.y, + startX: this.pointerDownPosition.x, + })) + ) { return; } + this.pointerDownPosition = undefined; + this.togglePopovers(event); }; private addListeners(): void { + window.addEventListener("pointerdown", this.pointerDownHandler); window.addEventListener("click", this.clickHandler); window.addEventListener("keydown", this.keyDownHandler); } private removeListeners(): void { + window.removeEventListener("pointerdown", this.pointerDownHandler); window.removeEventListener("click", this.clickHandler); window.removeEventListener("keydown", this.keyDownHandler); } diff --git a/packages/calcite-components/src/components/popover/popover.e2e.ts b/packages/calcite-components/src/components/popover/popover.e2e.ts index 88ea382d5d5..4de83a79798 100644 --- a/packages/calcite-components/src/components/popover/popover.e2e.ts +++ b/packages/calcite-components/src/components/popover/popover.e2e.ts @@ -400,49 +400,24 @@ describe("calcite-popover", () => { expect(await popover.getProperty("open")).toBe(false); }); - it("should not toggle popovers if selection range is present", async () => { - const page = await newE2EPage(); - - await page.setContent(html` - Content - - `); - - const popover = await page.find("calcite-popover"); - - expect(await popover.getProperty("open")).toBe(false); - - await page.$eval("button#ref", (el) => { - const selection = window.getSelection(); - selection.removeAllRanges(); - const range = document.createRange(); - range.selectNode(el); - selection.addRange(range); - el.click(); - }); - await page.waitForChanges(); - - expect(await popover.getProperty("open")).toBe(false); - }); - - it("should not close active popover if selection range occurs within the popover", async () => { + it("should not close active popover if click starts within the popover but ends outside", async () => { const page = await newE2EPage(); await page.setContent(html`
Content
+
Outside node
`); const popover = await page.find("calcite-popover"); expect(await popover.getProperty("open")).toBe(true); - await page.$eval("div#content", (el) => { - const selection = window.getSelection(); - selection.removeAllRanges(); - const range = document.createRange(); - range.selectNode(el); - selection.addRange(range); + await page.evaluate(() => { + const content = document.getElementById("content"); + content.dispatchEvent(new MouseEvent("pointerdown", { bubbles: true })); + const outsideNode = document.getElementById("outsideNode"); + outsideNode.dispatchEvent(new MouseEvent("pointerup", { bubbles: true })); }); await page.waitForChanges(); @@ -453,16 +428,6 @@ describe("calcite-popover", () => { await ref.click(); await page.waitForChanges(); - expect(await popover.getProperty("open")).toBe(true); - - await page.evaluate(() => { - const selection = window.getSelection(); - selection.removeAllRanges(); - }); - - await ref.click(); - await page.waitForChanges(); - expect(await popover.getProperty("open")).toBe(false); }); diff --git a/packages/calcite-components/src/components/tooltip/TooltipManager.ts b/packages/calcite-components/src/components/tooltip/TooltipManager.ts index 551f09fda7a..8610d06bcdb 100644 --- a/packages/calcite-components/src/components/tooltip/TooltipManager.ts +++ b/packages/calcite-components/src/components/tooltip/TooltipManager.ts @@ -98,22 +98,12 @@ export default class TooltipManager { } }; - private activeTooltipHasSelection(): boolean { - const selection = window.getSelection(); - return selection?.type === "Range" && this.activeTooltip?.contains(selection?.anchorNode); - } - private pointerLeaveHandler = (event: PointerEvent): void => { if (event.defaultPrevented) { return; } this.clearHoverTimeout(); - - if (this.activeTooltipHasSelection()) { - return; - } - this.closeHoveredTooltip(); }; @@ -127,7 +117,7 @@ export default class TooltipManager { const tooltip = this.queryTooltip(composedPath); - if (this.activeTooltipHasSelection() || this.pathHasOpenTooltip(tooltip, composedPath)) { + if (this.pathHasOpenTooltip(tooltip, composedPath)) { this.clearHoverTimeout(); return; } @@ -159,8 +149,8 @@ export default class TooltipManager { ); } - private clickHandler = (event: Event): void => { - if (event.defaultPrevented || window.getSelection()?.type === "Range") { + private clickHandler = (event: PointerEvent): void => { + if (event.defaultPrevented) { return; } diff --git a/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts b/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts index 3df5b1ce3ac..e0a778c596b 100644 --- a/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts +++ b/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts @@ -706,84 +706,6 @@ describe("calcite-tooltip", () => { expect(await hoverTip.getProperty("open")).toBe(false); }); - it("should not open on click if selection range is present", async () => { - const page = await newE2EPage(); - - await page.setContent(html` - Content - -
Selection
- `); - - const hoverTip = await page.find("#hoverTip"); - - expect(await hoverTip.getProperty("open")).toBe(false); - - await page.$eval("div#selection", (el) => { - const selection = window.getSelection(); - selection.removeAllRanges(); - const range = document.createRange(); - range.selectNode(el); - selection.addRange(range); - }); - - await dispatchClickEvent(page, "#hoverRef"); - await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS); - - expect(await hoverTip.getProperty("open")).toBe(false); - - await page.evaluate(() => { - const selection = window.getSelection(); - selection.removeAllRanges(); - }); - await page.waitForChanges(); - - await dispatchClickEvent(page, "#hoverRef"); - await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS); - - expect(await hoverTip.getProperty("open")).toBe(true); - }); - - it("should not close if selection range occurs within the tooltip", async () => { - const page = await newE2EPage(); - - await page.setContent(html` - -
Content
- - `); - - const hoverTip = await page.find("#hoverTip"); - await dispatchPointerEvent(page, "#hoverRef"); - await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS); - expect(await hoverTip.getProperty("open")).toBe(true); - - await page.$eval("div#content", (el) => { - const selection = window.getSelection(); - selection.removeAllRanges(); - const range = document.createRange(); - range.selectNode(el); - selection.addRange(range); - }); - await page.waitForChanges(); - - await dispatchPointerEvent(page, "#otherRef"); - await page.waitForTimeout(TOOLTIP_CLOSE_DELAY_MS); - - expect(await hoverTip.getProperty("open")).toBe(true); - - await page.evaluate(() => { - const selection = window.getSelection(); - selection.removeAllRanges(); - }); - await page.waitForChanges(); - - await dispatchPointerEvent(page, "#otherRef"); - await page.waitForTimeout(TOOLTIP_CLOSE_DELAY_MS); - - expect(await hoverTip.getProperty("open")).toBe(false); - }); - describe("owns a floating-ui", () => { floatingUIOwner( `content
referenceElement
`,