Skip to content

Commit d0fda80

Browse files
committed
fix(utils): Click Behavior for Hover Mode
Need to start the disable timer after a click since clicking an element _might_ cause the element to disappear or the element to no longer be under the mouse. When this happens, the `onMouseLeave` event will never be triggered as well. The `useTooltip` was also updated to set the visibility to false on click while the `useHoverMode` only starts the disable timer.
1 parent 0a6aed9 commit d0fda80

File tree

5 files changed

+50
-24
lines changed

5 files changed

+50
-24
lines changed

packages/tooltip/src/Tooltipped.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type ChildProps = Partial<Omit<TooltippedProvidedProps, "tooltip">>;
3636
type ChildElement = ReactElement<ChildProps>;
3737

3838
const MERGABLE_PROPS: (keyof TooltippedElementEventHandlers<HTMLElement>)[] = [
39+
"onClick",
3940
"onMouseEnter",
4041
"onMouseLeave",
4142
"onTouchStart",
@@ -140,6 +141,7 @@ export function Tooltipped({
140141
position: propPosition,
141142
positionThreshold,
142143
threshold = positionThreshold ?? DEFAULT_TOOLTIP_THRESHOLD,
144+
onClick,
143145
onMouseEnter,
144146
onMouseLeave,
145147
onTouchStart,
@@ -178,6 +180,7 @@ export function Tooltipped({
178180
onFocus,
179181
onBlur,
180182
onKeyDown,
183+
onClick,
181184
onMouseEnter,
182185
onMouseLeave,
183186
onTouchStart,

packages/tooltip/src/__tests__/Tooltipped.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,10 @@ describe("Tooltipped", () => {
258258
const { getByRole } = render(<Test />);
259259
const button = getByRole("button");
260260

261+
// force into "touch mode"
262+
fireEvent.touchStart(document);
263+
fireEvent.touchEnd(document);
264+
261265
fireEvent.touchStart(button);
262266
expect(() => getByRole("tooltip")).toThrow();
263267

packages/tooltip/src/useTooltip.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ export function useTooltip<E extends HTMLElement>({
313313
onFocus: propOnFocus,
314314
onBlur: propOnBlur,
315315
onKeyDown: propOnKeyDown,
316+
onClick,
316317
onMouseEnter,
317318
onMouseLeave,
318319
onTouchStart: propOnTouchStart,
@@ -343,6 +344,15 @@ export function useTooltip<E extends HTMLElement>({
343344
...others
344345
} = useHoverMode<E>({
345346
disabled,
347+
onClick: (event) => {
348+
onClick?.(event);
349+
if (event.isPropagationStopped()) {
350+
return;
351+
}
352+
353+
setVisible(false);
354+
setInitiatedBy(null);
355+
},
346356
onMouseEnter: (event) => {
347357
onMouseEnter?.(event);
348358
if (initiatedBy !== null) {
@@ -473,11 +483,11 @@ export function useTooltip<E extends HTMLElement>({
473483
return;
474484
}
475485

476-
window.addEventListener("touchmove", hide);
477-
window.addEventListener("touchend", hide);
486+
window.addEventListener("touchmove", hide, true);
487+
window.addEventListener("touchend", hide, true);
478488
return () => {
479-
window.removeEventListener("touchmove", hide);
480-
window.removeEventListener("touchend", hide);
489+
window.removeEventListener("touchmove", hide, true);
490+
window.removeEventListener("touchend", hide, true);
481491
};
482492
}, [hide, initiatedBy, setVisible]);
483493

packages/utils/src/hover/__tests__/HoverMode.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// comment
12
import React, { MouseEventHandler } from "react";
23
import { act, fireEvent, render } from "@testing-library/react";
34
import {

packages/utils/src/hover/useHoverMode.ts

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,29 +27,21 @@ export interface HoverModeEventHandlers<E extends HTMLElement> {
2727
* If this function calls `event.stopPropagation()`, the hover mode behavior
2828
* will be disabled.
2929
*/
30-
onMouseEnter?: MouseEventHandler<E>;
30+
onClick?: MouseEventHandler<E>;
3131

3232
/**
3333
* An optional event handler to merge with the hover mode visibility hander.
3434
* If this function calls `event.stopPropagation()`, the hover mode behavior
3535
* will be disabled.
3636
*/
37-
onMouseLeave?: MouseEventHandler<E>;
38-
}
37+
onMouseEnter?: MouseEventHandler<E>;
3938

40-
/**
41-
* Event handlers that should be used when the {@link HoverModeOptions.sticky}
42-
* is enabled to temporarily disable the mouse leave behavior.
43-
*
44-
* @remarks \@since 2.8.0
45-
*/
46-
export interface StickyHoverModeEventHandlers<E extends HTMLElement>
47-
extends HoverModeEventHandlers<E> {
4839
/**
49-
* An optional event handler to merge with the "sticky" mode's `onClick`
50-
* behavior.
40+
* An optional event handler to merge with the hover mode visibility hander.
41+
* If this function calls `event.stopPropagation()`, the hover mode behavior
42+
* will be disabled.
5143
*/
52-
onClick?: MouseEventHandler<E>;
44+
onMouseLeave?: MouseEventHandler<E>;
5345
}
5446

5547
/** @remarks \@since 2.8.0 */
@@ -83,7 +75,7 @@ export interface HoverModeOnlyOptions<E extends HTMLElement>
8375
/** @remarks \@since 2.8.0 */
8476
export interface HoverModeOptions<E extends HTMLElement>
8577
extends HoverModeOnlyOptions<E>,
86-
StickyHoverModeEventHandlers<E> {
78+
HoverModeEventHandlers<E> {
8779
/**
8880
* Boolean if the hover mode should also provide a "sticky" mode which allows
8981
* the exit behavior to be disabled if the element is clicked.
@@ -124,7 +116,7 @@ export interface HoverModeReturnValue<E extends HTMLElement>
124116
stuck?: boolean;
125117

126118
/** {@inheritDoc StickyHoverModeEventHandlers} */
127-
stickyHandlers?: Required<StickyHoverModeEventHandlers<E>>;
119+
stickyHandlers?: Required<HoverModeEventHandlers<E>>;
128120
}
129121

130122
/**
@@ -225,7 +217,7 @@ export function useHoverMode<E extends HTMLElement>(
225217
options: HoverModeOptions<E> & { sticky: true }
226218
): HoverModeReturnValue<E> & {
227219
stuck: boolean;
228-
stickyHandlers: Required<StickyHoverModeEventHandlers<E>>;
220+
stickyHandlers: Required<HoverModeEventHandlers<E>>;
229221
};
230222
export function useHoverMode<E extends HTMLElement>({
231223
disabled = false,
@@ -321,7 +313,19 @@ export function useHoverMode<E extends HTMLElement>({
321313
const onClick = useCallback(
322314
(event: MouseEvent<E>) => {
323315
propOnClick?.(event);
324-
if (disabled || event.isPropagationStopped()) {
316+
if (event.isPropagationStopped() || disabled) {
317+
return;
318+
}
319+
320+
startDisableTimer();
321+
},
322+
[disabled, propOnClick, startDisableTimer]
323+
);
324+
325+
const onStickyClick = useCallback(
326+
(event: MouseEvent<E>) => {
327+
propOnClick?.(event);
328+
if (event.isPropagationStopped() || disabled) {
325329
return;
326330
}
327331

@@ -338,13 +342,17 @@ export function useHoverMode<E extends HTMLElement>({
338342
);
339343

340344
const handlers: Required<HoverModeEventHandlers<E>> = {
345+
onClick,
341346
onMouseEnter,
342347
onMouseLeave,
343348
};
344349

345-
let stickyHandlers: Required<StickyHoverModeEventHandlers<E>> | undefined;
350+
let stickyHandlers: Required<HoverModeEventHandlers<E>> | undefined;
346351
if (sticky) {
347-
stickyHandlers = { ...handlers, onClick };
352+
stickyHandlers = {
353+
...handlers,
354+
onClick: onStickyClick,
355+
};
348356
}
349357

350358
return {

0 commit comments

Comments
 (0)