From c372307dfeca16767a18c383b19eb72a84b1ab73 Mon Sep 17 00:00:00 2001 From: Patrick Wehbe Date: Sat, 20 Jun 2026 13:05:42 +0300 Subject: [PATCH] fix(tooltip): forward press event to wrapped child onPress The Tooltip injects its own handlePress as the wrapped child's onPress via cloneElement. handlePress took no argument and called props.onPress?.() with nothing, so a child such as Button or IconButton with onPress={(e) => ...} received undefined for the press event on native. handlePress now accepts the GestureResponderEvent from Pressable and forwards it to the child's onPress. The onPress type in TooltipChildProps is widened to (e: GestureResponderEvent) => void to match the Button and IconButton signatures. Closes #5003 --- src/components/Tooltip/Tooltip.tsx | 27 ++++++++++++++-------- src/components/Tooltip/utils.ts | 9 ++++++-- src/components/__tests__/Tooltip.test.tsx | 28 ++++++++++++++++++++++- 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index 36dc08971b..fe451e6a9b 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -6,7 +6,11 @@ import { Platform, Pressable, } from 'react-native'; -import type { LayoutChangeEvent, ViewStyle } from 'react-native'; +import type { + GestureResponderEvent, + LayoutChangeEvent, + ViewStyle, +} from 'react-native'; import { getTooltipPosition } from './utils'; import type { Measurement, TooltipChildProps } from './utils'; @@ -146,15 +150,18 @@ const Tooltip = ({ hideTooltipTimer.current.push(id); }, [leaveTouchDelay]); - const handlePress = React.useCallback(() => { - if (touched.current) { - return null; - } - if (!isValidChild) return null; - const props = children.props as TooltipChildProps; - if (props.disabled) return null; - return props.onPress?.(); - }, [children.props, isValidChild]); + const handlePress = React.useCallback( + (e: GestureResponderEvent) => { + if (touched.current) { + return null; + } + if (!isValidChild) return null; + const props = children.props as TooltipChildProps; + if (props.disabled) return null; + return props.onPress?.(e); + }, + [children.props, isValidChild] + ); const handleHoverIn = React.useCallback(() => { handleTouchStart(); diff --git a/src/components/Tooltip/utils.ts b/src/components/Tooltip/utils.ts index 43baf684fd..304f79d988 100644 --- a/src/components/Tooltip/utils.ts +++ b/src/components/Tooltip/utils.ts @@ -1,5 +1,10 @@ import { Dimensions, StyleSheet } from 'react-native'; -import type { LayoutRectangle, StyleProp, ViewStyle } from 'react-native'; +import type { + GestureResponderEvent, + LayoutRectangle, + StyleProp, + ViewStyle, +} from 'react-native'; type ChildrenMeasurement = { width: number; @@ -19,7 +24,7 @@ export type Measurement = { export type TooltipChildProps = { style: StyleProp; disabled?: boolean; - onPress?: () => void; + onPress?: (e: GestureResponderEvent) => void; onHoverIn?: () => void; onHoverOut?: () => void; }; diff --git a/src/components/__tests__/Tooltip.test.tsx b/src/components/__tests__/Tooltip.test.tsx index 62666e9d7c..f2f7765d85 100644 --- a/src/components/__tests__/Tooltip.test.tsx +++ b/src/components/__tests__/Tooltip.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Dimensions, Text, View, Platform } from 'react-native'; -import type { ViewProps } from 'react-native'; +import type { GestureResponderEvent, ViewProps } from 'react-native'; import { afterAll, @@ -28,9 +28,12 @@ jest.mock('../../utils/addEventListener', () => ({ const DummyComponent = ({ ref, + // `onPress` is consumed by Tooltip via `children.props`, not by the inner View + onPress: _onPress, ...props }: ViewProps & { ref?: React.RefObject; + onPress?: (e: GestureResponderEvent) => void; }) => ( dummy component @@ -150,6 +153,29 @@ describe('Tooltip', () => { }); }); + describe('onPress', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('forwards the press event object to the child onPress', () => { + const onPress = jest.fn(); + const { + wrapper: { getByText }, + } = setup({ + children: , + }); + + const nativeEvent = { nativeEvent: { locationX: 1, locationY: 2 } }; + fireEvent(getTrigger(getByText), 'press', nativeEvent); + + expect(onPress).toHaveBeenCalledTimes(1); + expect(onPress).toHaveBeenCalledWith( + expect.objectContaining(nativeEvent) + ); + }); + }); + describe('pressOut', () => { // eslint-disable-next-line jest/valid-title it('hides the tooltip when the user stop pressing the component', async () => {