Skip to content

Commit 68f0723

Browse files
authored
fix(tooltip, popover): honor prevented events (#11278)
**Related Issue:** #11244 ## Summary - updates tooltip and popover to take no action on prevented events - adds tests - This allows users to prevent an event instead of stopping propagation on it.
1 parent d35d44a commit 68f0723

File tree

4 files changed

+156
-1
lines changed

4 files changed

+156
-1
lines changed

Diff for: packages/calcite-components/src/components/popover/PopoverManager.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export default class PopoverManager {
8787
};
8888

8989
private clickHandler = (event: PointerEvent): void => {
90-
if (isKeyboardTriggeredClick(event)) {
90+
if (isKeyboardTriggeredClick(event) || event.defaultPrevented) {
9191
return;
9292
}
9393

Diff for: packages/calcite-components/src/components/popover/popover.e2e.ts

+29
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,35 @@ describe("calcite-popover", () => {
368368
expect(await popover.getProperty("open")).toBe(true);
369369
});
370370

371+
it("should not open popovers if event is prevented", async () => {
372+
const page = await newE2EPage();
373+
374+
await page.setContent(html`
375+
<calcite-popover reference-element="ref">Content</calcite-popover>
376+
<div id="ref">Button</div>
377+
`);
378+
379+
await page.waitForChanges();
380+
381+
const popover = await page.find("calcite-popover");
382+
383+
expect(await popover.getProperty("open")).toBe(false);
384+
385+
await page.$eval("#ref", (ref) => {
386+
ref.addEventListener("click", (event) => {
387+
event.preventDefault();
388+
});
389+
});
390+
391+
const referenceElement = await page.find("#ref");
392+
393+
await referenceElement.click();
394+
395+
await page.waitForChanges();
396+
397+
expect(await popover.getProperty("open")).toBe(false);
398+
});
399+
371400
it("should not be visible if reference is hidden", async () => {
372401
const page = await newE2EPage();
373402

Diff for: packages/calcite-components/src/components/tooltip/TooltipManager.ts

+12
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ export default class TooltipManager {
9797
};
9898

9999
private pointerMoveHandler = (event: PointerEvent): void => {
100+
if (event.defaultPrevented) {
101+
return;
102+
}
103+
100104
const composedPath = event.composedPath();
101105
const { activeTooltip } = this;
102106

@@ -129,6 +133,10 @@ export default class TooltipManager {
129133
}
130134

131135
private clickHandler = (event: Event): void => {
136+
if (event.defaultPrevented) {
137+
return;
138+
}
139+
132140
this.clickedTooltip = null;
133141
const composedPath = event.composedPath();
134142
const tooltip = this.queryTooltip(composedPath);
@@ -160,6 +168,10 @@ export default class TooltipManager {
160168
};
161169

162170
private focusInHandler = (event: FocusEvent): void => {
171+
if (event.defaultPrevented) {
172+
return;
173+
}
174+
163175
const composedPath = event.composedPath();
164176
const tooltip = this.queryTooltip(composedPath);
165177

Diff for: packages/calcite-components/src/components/tooltip/tooltip.e2e.ts

+114
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,34 @@ describe("calcite-tooltip", () => {
320320
expect(await positionContainer.isVisible()).toBe(true);
321321
});
322322

323+
it("should not open when hover event is prevented", async () => {
324+
const page = await newE2EPage();
325+
326+
await page.setContent(
327+
`<calcite-tooltip reference-element="ref">content</calcite-tooltip><div id="ref">referenceElement</div>`,
328+
);
329+
330+
await page.waitForChanges();
331+
332+
const positionContainer = await page.find(`calcite-tooltip >>> .${CSS.positionContainer}`);
333+
334+
expect(await positionContainer.isVisible()).toBe(false);
335+
336+
await page.$eval("#ref", (ref) => {
337+
ref.addEventListener("pointermove", (event) => {
338+
event.preventDefault();
339+
});
340+
});
341+
342+
const ref = await page.find("#ref");
343+
344+
await ref.hover();
345+
346+
await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS);
347+
348+
expect(await positionContainer.isVisible()).toBe(false);
349+
});
350+
323351
it("should honor hover interaction with span inside", async () => {
324352
const page = await newE2EPage();
325353

@@ -426,6 +454,34 @@ describe("calcite-tooltip", () => {
426454
expect(await tooltip.getProperty("open")).toBe(false);
427455
});
428456

457+
it("should not open if focus event is prevented", async () => {
458+
const page = await newE2EPage();
459+
460+
await page.setContent(html`
461+
<button id="test">test</button>
462+
<calcite-tooltip reference-element="ref">Content</calcite-tooltip>
463+
<button id="ref">Button</button>
464+
`);
465+
466+
await page.waitForChanges();
467+
468+
const tooltip = await page.find("calcite-tooltip");
469+
470+
expect(await tooltip.getProperty("open")).toBe(false);
471+
472+
await page.$eval("#ref", (ref) => {
473+
ref.addEventListener("focusin", (event) => {
474+
event.preventDefault();
475+
});
476+
477+
ref.dispatchEvent(new FocusEvent("focusin", { bubbles: true, cancelable: true }));
478+
});
479+
480+
await page.waitForChanges();
481+
482+
expect(await tooltip.getProperty("open")).toBe(false);
483+
});
484+
429485
it("should handle mouse events", async () => {
430486
const page = await newE2EPage();
431487

@@ -547,6 +603,45 @@ describe("calcite-tooltip", () => {
547603
await assertEscapeKeyCanceled(page, true);
548604
});
549605

606+
it("should not close with ESC key if event is prevented", async () => {
607+
const page = await newE2EPage();
608+
609+
await page.setContent(html`
610+
<calcite-tooltip reference-element="ref">Content</calcite-tooltip>
611+
<button id="ref">Button</button>
612+
`);
613+
614+
await page.waitForChanges();
615+
616+
const tooltip = await page.find("calcite-tooltip");
617+
618+
expect(await tooltip.getProperty("open")).toBe(false);
619+
620+
const referenceElement = await page.find("#ref");
621+
622+
await referenceElement.focus();
623+
624+
await referenceElement.hover();
625+
626+
await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS);
627+
628+
await page.waitForChanges();
629+
630+
expect(await tooltip.getProperty("open")).toBe(true);
631+
632+
await page.evaluate(() => {
633+
document.body.addEventListener("keydown", (event) => {
634+
if (event.key === "Escape") {
635+
event.preventDefault();
636+
}
637+
});
638+
});
639+
640+
await dispatchKeydownEvent(page, "#ref", "Escape");
641+
642+
expect(await tooltip.getProperty("open")).toBe(true);
643+
});
644+
550645
it("should only open the last focused tooltip", async () => {
551646
const page = await newE2EPage();
552647

@@ -1175,6 +1270,25 @@ describe("calcite-tooltip", () => {
11751270
expect(await tooltip.getProperty("open")).toBe(false);
11761271
});
11771272

1273+
it("should not open when click event is prevented", async () => {
1274+
const page = await newE2EPage();
1275+
await page.setContent(pageContent);
1276+
await skipAnimations(page);
1277+
await page.waitForChanges();
1278+
const tooltip = await page.find("calcite-tooltip");
1279+
1280+
await page.$eval("#ref", (ref) => {
1281+
ref.addEventListener("click", (event) => {
1282+
event.preventDefault();
1283+
});
1284+
1285+
ref.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
1286+
});
1287+
1288+
await page.waitForChanges();
1289+
expect(await tooltip.getProperty("open")).toBe(false);
1290+
});
1291+
11781292
it("should work when focusing on a reference element first", async () => {
11791293
const page = await newE2EPage();
11801294
await page.setContent(pageContent);

0 commit comments

Comments
 (0)