Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hide tooltips on touchend event #5830

Merged
merged 11 commits into from
Apr 3, 2025
5 changes: 5 additions & 0 deletions .changeset/chilly-jars-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": minor
---

Hide `TooltipV2` tooltips on `touchend` event
45 changes: 29 additions & 16 deletions packages/react/src/TooltipV2/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,18 @@ export type TooltipProps = React.PropsWithChildren<
> &
React.HTMLAttributes<HTMLElement>

type TriggerPropsType = {
'aria-describedby'?: string
'aria-labelledby'?: string
'aria-label'?: string
onBlur?: React.FocusEventHandler
onFocus?: React.FocusEventHandler
onMouseEnter?: React.MouseEventHandler
onMouseLeave?: React.MouseEventHandler
type TriggerPropsType = Pick<
React.HTMLAttributes<HTMLElement>,
| 'aria-describedby'
| 'aria-labelledby'
| 'onBlur'
| 'onTouchEnd'
| 'onFocus'
| 'onMouseOverCapture'
| 'onMouseLeave'
| 'onTouchCancel'
| 'onTouchEnd'
> & {
ref?: React.RefObject<HTMLElement>
}

Expand Down Expand Up @@ -214,7 +218,7 @@ export const Tooltip = React.forwardRef(

const [isPopoverOpen, setIsPopoverOpen] = useState(false)

const timeoutRef = React.useRef<number | null>(null)
const openTimeoutRef = React.useRef<number | null>(null)

const {safeSetTimeout, safeClearTimeout} = useSafeTimeout()

Expand Down Expand Up @@ -261,6 +265,10 @@ export const Tooltip = React.forwardRef(
}
}
const closeTooltip = () => {
if (openTimeoutRef.current) {
safeClearTimeout(openTimeoutRef.current)
openTimeoutRef.current = null
}
try {
if (
tooltipElRef.current &&
Expand Down Expand Up @@ -362,6 +370,13 @@ export const Tooltip = React.forwardRef(
closeTooltip()
child.props.onBlur?.(event)
},
onTouchEnd: (event: React.TouchEvent) => {
child.props.onTouchEnd?.(event)

// Hide tooltips on tap to essentially disable them on touch devices;
// this still allows viewing the tooltip on tap-and-hold
safeSetTimeout(() => closeTooltip(), 10)
},
onFocus: (event: React.FocusEvent) => {
// only show tooltip on :focus-visible, not on :focus
try {
Expand All @@ -374,19 +389,17 @@ export const Tooltip = React.forwardRef(
openTooltip()
child.props.onFocus?.(event)
},
onMouseEnter: (event: React.MouseEvent) => {
// show tooltip after mosue has been hovering for at least 50ms
onMouseOverCapture: (event: React.MouseEvent) => {
// We use a `capture` event to ensure this is called first before
// events that might cancel the opening timeout (like `onTouchEnd`)
// show tooltip after mouse has been hovering for at least 50ms
// (prevent showing tooltip when mouse is just passing through)
timeoutRef.current = safeSetTimeout(() => {
openTimeoutRef.current = safeSetTimeout(() => {
openTooltip()
child.props.onMouseEnter?.(event)
}, 50)
},
onMouseLeave: (event: React.MouseEvent) => {
if (timeoutRef.current) {
safeClearTimeout(timeoutRef.current)
timeoutRef.current = null
}
closeTooltip()
child.props.onMouseLeave?.(event)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3325,8 +3325,9 @@ exports[`TextInput renders trailingAction icon button 1`] = `
onBlur={[Function]}
onClick={[MockFunction]}
onFocus={[Function]}
onMouseEnter={[Function]}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this will be problematic in integrations in dotcom?

onMouseLeave={[Function]}
onMouseOverCapture={[Function]}
onTouchEnd={[Function]}
type="button"
>
<svg
Expand Down Expand Up @@ -4090,8 +4091,9 @@ exports[`TextInput renders trailingAction text button with a tooltip 1`] = `
onBlur={[Function]}
onClick={[MockFunction]}
onFocus={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
onMouseOverCapture={[Function]}
onTouchEnd={[Function]}
style={
{
"--button-color": "fg.subtle",
Expand Down
Loading