From 683962b961789d777aa2b80a94aa64f16832b054 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Fri, 5 Feb 2021 15:55:08 -0800 Subject: [PATCH] [add] Pressable support for hover callbacks Add 'onHoverIn' and 'onHoverOut' props to Pressable. Fix #1804 --- .../exports/Pressable/__tests__/index-test.js | 6 ++++++ .../src/exports/Pressable/index.js | 19 +++++++++++++++++-- .../src/modules/useHover/index.js | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/react-native-web/src/exports/Pressable/__tests__/index-test.js b/packages/react-native-web/src/exports/Pressable/__tests__/index-test.js index 90082006..df6a59ce 100644 --- a/packages/react-native-web/src/exports/Pressable/__tests__/index-test.js +++ b/packages/react-native-web/src/exports/Pressable/__tests__/index-test.js @@ -88,11 +88,15 @@ describe('components/Pressable', () => { test('hover interaction', () => { let container; + const onHoverIn = jest.fn(); + const onHoverOut = jest.fn(); const ref = React.createRef(); act(() => { ({ container } = render( (hovered ?
: null)} + onHoverIn={onHoverIn} + onHoverOut={onHoverOut} ref={ref} style={({ hovered }) => [hovered && { outline: 'hover-ring' }]} /> @@ -103,10 +107,12 @@ describe('components/Pressable', () => { act(() => { target.pointerover(); }); + expect(onHoverIn).toBeCalled(); expect(container.firstChild).toMatchSnapshot(); act(() => { target.pointerout(); }); + expect(onHoverOut).toBeCalled(); expect(container.firstChild).toMatchSnapshot(); }); diff --git a/packages/react-native-web/src/exports/Pressable/index.js b/packages/react-native-web/src/exports/Pressable/index.js index ff30d106..32c18b9a 100644 --- a/packages/react-native-web/src/exports/Pressable/index.js +++ b/packages/react-native-web/src/exports/Pressable/index.js @@ -10,6 +10,7 @@ 'use strict'; +import type { HoverEventsConfig } from '../../modules/useHover'; import type { PressResponderConfig } from '../../modules/usePressEvents/PressResponder'; import type { ViewProps } from '../View'; @@ -51,6 +52,10 @@ type Props = $ReadOnly<{| onBlur?: $PropertyType, // Called when the view is focused onFocus?: $PropertyType, + // Called when the view is hovered + onHoverIn?: $PropertyType, + // Called when the view is no longer hovered + onHoverOut?: $PropertyType, // Called when this view's layout changes onLayout?: $PropertyType, // Called when a long-tap gesture is detected. @@ -68,6 +73,7 @@ type Props = $ReadOnly<{| /** * Used only for documentation or testing (e.g. snapshot testing). */ + testOnly_hovered?: ?boolean, testOnly_pressed?: ?boolean |}>; @@ -85,17 +91,20 @@ function Pressable(props: Props, forwardedRef): React.Node { focusable, onBlur, onFocus, + onHoverIn, + onHoverOut, onLongPress, onPress, onPressMove, onPressIn, onPressOut, style, + testOnly_hovered, testOnly_pressed, ...rest } = props; - const [hovered, setHovered] = useForceableState(false); + const [hovered, setHovered] = useForceableState(testOnly_hovered === true); const [focused, setFocused] = useForceableState(false); const [pressed, setPressed] = useForceableState(testOnly_pressed === true); @@ -131,7 +140,13 @@ function Pressable(props: Props, forwardedRef): React.Node { const pressEventHandlers = usePressEvents(hostRef, pressConfig); - useHover(hostRef, { contain: true, disabled, onHoverChange: setHovered }); + useHover(hostRef, { + contain: true, + disabled, + onHoverChange: setHovered, + onHoverStart: onHoverIn, + onHoverEnd: onHoverOut + }); const interactionState = { hovered, focused, pressed }; diff --git a/packages/react-native-web/src/modules/useHover/index.js b/packages/react-native-web/src/modules/useHover/index.js index 473dbbef..ddaca95c 100644 --- a/packages/react-native-web/src/modules/useHover/index.js +++ b/packages/react-native-web/src/modules/useHover/index.js @@ -15,7 +15,7 @@ import useLayoutEffect from '../useLayoutEffect'; * Types */ -type HoverEventsConfig = { +export type HoverEventsConfig = { contain?: ?boolean, disabled?: ?boolean, onHoverStart?: ?(e: any) => void,