Skip to content

Commit 5416554

Browse files
committed
fix(tooltip): cancel timer when element is clicked
This prevents the tooltip from appearing if a user hits enter or space while focusing an element.
1 parent f799d3a commit 5416554

File tree

3 files changed

+61
-2
lines changed

3 files changed

+61
-2
lines changed

packages/tooltip/src/__tests__/Tooltipped.tsx

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { ReactNode } from "react";
1+
import React, { MouseEvent, ReactNode } from "react";
22
import { render, fireEvent, act } from "@testing-library/react";
33
import { SimplePosition, UserInteractionModeListener } from "@react-md/utils";
44

@@ -7,6 +7,7 @@ import { Tooltipped } from "../Tooltipped";
77
jest.useFakeTimers();
88

99
interface TestProps {
10+
onClick?(event: MouseEvent): void;
1011
position?: SimplePosition;
1112
defaultPosition?: SimplePosition;
1213
}
@@ -118,6 +119,7 @@ describe("Tooltipped", () => {
118119
const onBlur = jest.fn();
119120
const onFocus = jest.fn();
120121
const onKeyDown = jest.fn();
122+
const onClick = jest.fn();
121123
const onTouchStart = jest.fn();
122124
const onContextMenu = jest.fn();
123125
const onMouseEnter = jest.fn();
@@ -129,6 +131,7 @@ describe("Tooltipped", () => {
129131
tooltip="Look at me!"
130132
onBlur={onBlur}
131133
onFocus={onFocus}
134+
onClick={onClick}
132135
onKeyDown={onKeyDown}
133136
onMouseEnter={onMouseEnter}
134137
onMouseLeave={onMouseLeave}
@@ -158,6 +161,9 @@ describe("Tooltipped", () => {
158161
fireEvent.touchStart(button);
159162
expect(onTouchStart).toBeCalledTimes(1);
160163

164+
fireEvent.click(button);
165+
expect(onClick).toBeCalledTimes(1);
166+
161167
fireEvent.contextMenu(button);
162168
expect(onContextMenu).toBeCalledTimes(1);
163169
});
@@ -456,4 +462,56 @@ describe("Tooltipped", () => {
456462
});
457463
expect(() => getByRole("tooltip")).toThrow();
458464
});
465+
466+
it("should throw an error if a javascript user provides an invalid position", () => {
467+
const error = jest.spyOn(console, "error").mockImplementation(() => {
468+
// do nothing
469+
});
470+
471+
// @ts-expect-error
472+
expect(() => render(<Test position="abvoe" />)).toThrow(
473+
"Invalid position: abvoe"
474+
);
475+
476+
error.mockRestore();
477+
});
478+
479+
it("should cancel the tooltip timer if the element is clicked", () => {
480+
const onClick = jest.fn().mockImplementationOnce((event: MouseEvent) => {
481+
event.stopPropagation();
482+
});
483+
const { getByRole } = render(<Test onClick={onClick} />);
484+
485+
const button = getByRole("button");
486+
fireEvent.mouseEnter(button);
487+
fireEvent.click(button);
488+
489+
act(() => {
490+
jest.advanceTimersByTime(1000);
491+
});
492+
// event.stopPropagation was called
493+
expect(() => getByRole("tooltip")).not.toThrow();
494+
495+
fireEvent.mouseLeave(button);
496+
act(() => {
497+
jest.runAllTimers();
498+
});
499+
expect(() => getByRole("tooltip")).toThrow();
500+
501+
fireEvent.mouseEnter(button);
502+
act(() => {
503+
jest.advanceTimersByTime(500);
504+
});
505+
expect(() => getByRole("tooltip")).toThrow();
506+
507+
fireEvent.click(button);
508+
act(() => {
509+
jest.advanceTimersByTime(500);
510+
});
511+
expect(() => getByRole("tooltip")).toThrow();
512+
act(() => {
513+
jest.runAllTimers();
514+
});
515+
expect(() => getByRole("tooltip")).toThrow();
516+
});
459517
});

packages/tooltip/src/useTooltip.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ export function useTooltip<E extends HTMLElement>({
351351

352352
setVisible(false);
353353
setInitiatedBy(null);
354+
window.clearTimeout(timeout.current);
354355
},
355356
onMouseEnter: (event) => {
356357
onMouseEnter?.(event);

packages/utils/src/hover/useHoverMode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ export function useHoverMode<E extends HTMLElement>({
261261
window.clearTimeout(timeoutRef.current);
262262
};
263263

264-
// this is just used so the `defaultOption` can be used
264+
// this is just used so the `defaultVisible` option can be used
265265
if (!skipReset.current) {
266266
reset();
267267
}

0 commit comments

Comments
 (0)