Skip to content

Commit d75cbe1

Browse files
committed
fix: RN Gesture Handler Pressable support
1 parent 1b90704 commit d75cbe1

File tree

5 files changed

+158
-17
lines changed

5 files changed

+158
-17
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"chalk": "^4.1.2",
5151
"jest-matcher-utils": "^29.7.0",
5252
"pretty-format": "^29.7.0",
53+
"react-native-gesture-handler": "^2.23.1",
5354
"redent": "^3.0.0"
5455
},
5556
"peerDependencies": {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import 'react-native-gesture-handler/jestSetup.js';
2+
import React from 'react';
3+
import { View } from 'react-native';
4+
import { Pressable } from 'react-native-gesture-handler';
5+
6+
import { fireEvent, render, screen, userEvent } from '..';
7+
import { createEventLogger, getEventsNames } from '../test-utils';
8+
9+
test('fireEvent can invoke press events for RNGH Pressable', () => {
10+
const onPress = jest.fn();
11+
const onPressIn = jest.fn();
12+
const onPressOut = jest.fn();
13+
const onLongPress = jest.fn();
14+
15+
render(
16+
<View>
17+
<Pressable
18+
testID="pressable"
19+
onPress={onPress}
20+
onPressIn={onPressIn}
21+
onPressOut={onPressOut}
22+
onLongPress={onLongPress}
23+
/>
24+
</View>,
25+
);
26+
27+
const pressable = screen.getByTestId('pressable');
28+
29+
fireEvent.press(pressable);
30+
expect(onPress).toHaveBeenCalled();
31+
32+
fireEvent(pressable, 'pressIn');
33+
expect(onPressIn).toHaveBeenCalled();
34+
35+
fireEvent(pressable, 'pressOut');
36+
expect(onPressOut).toHaveBeenCalled();
37+
38+
fireEvent(pressable, 'longPress');
39+
expect(onLongPress).toHaveBeenCalled();
40+
});
41+
42+
test('userEvent can invoke press events for RNGH Pressable', async () => {
43+
const { events, logEvent } = createEventLogger();
44+
const user = userEvent.setup();
45+
46+
render(
47+
<View>
48+
<Pressable
49+
testID="pressable"
50+
onPress={logEvent('press')}
51+
onPressIn={logEvent('pressIn')}
52+
onPressOut={logEvent('pressOut')}
53+
onLongPress={logEvent('longPress')}
54+
/>
55+
</View>,
56+
);
57+
58+
expect(screen.toJSON()).toMatchInlineSnapshot(`
59+
<View>
60+
<RNGestureHandlerButton
61+
collapsable={false}
62+
enabled={true}
63+
hitSlop={
64+
{
65+
"bottom": 0,
66+
"left": 0,
67+
"right": 0,
68+
"top": 0,
69+
}
70+
}
71+
rippleColor="transparent"
72+
style={
73+
[
74+
{},
75+
undefined,
76+
]
77+
}
78+
testID="pressable"
79+
testOnly_onLongPress={[Function]}
80+
testOnly_onPress={[Function]}
81+
testOnly_onPressIn={[Function]}
82+
testOnly_onPressOut={[Function]}
83+
/>
84+
</View>
85+
`);
86+
87+
const pressable = screen.getByTestId('pressable');
88+
await user.press(pressable);
89+
expect(getEventsNames(events)).toEqual(['pressIn', 'pressOut', 'press']);
90+
});

src/helpers/host-component-names.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,28 @@ const HOST_SWITCH_NAMES = ['RCTSwitch'];
99
const HOST_SCROLL_VIEW_NAMES = ['RCTScrollView'];
1010
const HOST_MODAL_NAMES = ['Modal'];
1111

12+
export type HostText = HostTestInstance & {
13+
type: 'Text';
14+
};
15+
1216
/**
1317
* Checks if the given element is a host Text element.
1418
* @param element The element to check.
1519
*/
16-
export function isHostText(element: ReactTestInstance): element is HostTestInstance {
20+
export function isHostText(element: ReactTestInstance): element is HostText {
1721
return typeof element?.type === 'string' && HOST_TEXT_NAMES.includes(element.type);
1822
}
1923

24+
export type HostTextInput = HostTestInstance & {
25+
type: 'TextInput';
26+
editable?: boolean;
27+
};
28+
2029
/**
2130
* Checks if the given element is a host TextInput element.
2231
* @param element The element to check.
2332
*/
24-
export function isHostTextInput(element: ReactTestInstance): element is HostTestInstance {
33+
export function isHostTextInput(element: ReactTestInstance): element is HostTextInput {
2534
return typeof element?.type === 'string' && HOST_TEXT_INPUT_NAMES.includes(element.type);
2635
}
2736

src/user-event/press/press.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { ReactTestInstance } from 'react-test-renderer';
22

33
import act from '../../act';
4-
import { getHostParent } from '../../helpers/component-tree';
5-
import { isHostText } from '../../helpers/host-component-names';
4+
import { getEventHandler } from '../../event-handler';
5+
import { getHostParent, isHostElement } from '../../helpers/component-tree';
6+
import { isHostTextInput } from '../../helpers/host-component-names';
67
import { isPointerEventEnabled } from '../../helpers/pointer-events';
78
import { isEditableTextInput } from '../../helpers/text-input';
89
import { EventBuilder } from '../event-builder';
@@ -45,13 +46,13 @@ const basePress = async (
4546
element: ReactTestInstance,
4647
options: BasePressOptions,
4748
): Promise<void> => {
48-
if (isPressableText(element)) {
49-
await emitTextPressEvents(config, element, options);
49+
if (isEditableTextInput(element) && isPointerEventEnabled(element)) {
50+
await emitTextInputPressEvents(config, element, options);
5051
return;
5152
}
5253

53-
if (isEditableTextInput(element) && isPointerEventEnabled(element)) {
54-
await emitTextInputPressEvents(config, element, options);
54+
if (isHostPressable(element)) {
55+
await emitTextPressEvents(config, element, options);
5556
return;
5657
}
5758

@@ -96,16 +97,16 @@ const isEnabledTouchResponder = (element: ReactTestInstance) => {
9697
return isPointerEventEnabled(element) && element.props.onStartShouldSetResponder?.();
9798
};
9899

99-
const isPressableText = (element: ReactTestInstance) => {
100-
const hasPressEventHandler = Boolean(
101-
element.props.onPress ||
102-
element.props.onLongPress ||
103-
element.props.onPressIn ||
104-
element.props.onPressOut,
105-
);
100+
const isHostPressable = (element: ReactTestInstance) => {
101+
const hasPressEventHandler =
102+
getEventHandler(element, 'press') ||
103+
getEventHandler(element, 'longPress') ||
104+
getEventHandler(element, 'pressIn') ||
105+
getEventHandler(element, 'pressOut');
106106

107107
return (
108-
isHostText(element) &&
108+
isHostElement(element) &&
109+
!isHostTextInput(element) &&
109110
isPointerEventEnabled(element) &&
110111
!element.props.disabled &&
111112
hasPressEventHandler

yarn.lock

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1601,6 +1601,15 @@ __metadata:
16011601
languageName: node
16021602
linkType: hard
16031603

1604+
"@egjs/hammerjs@npm:^2.0.17":
1605+
version: 2.0.17
1606+
resolution: "@egjs/hammerjs@npm:2.0.17"
1607+
dependencies:
1608+
"@types/hammerjs": "npm:^2.0.36"
1609+
checksum: 10c0/dbedc15a0e633f887c08394bd636faf6a3abd05726dc0909a0e01209d5860a752d9eca5e512da623aecfabe665f49f1d035de3103eb2f9022c5cea692f9cc9be
1610+
languageName: node
1611+
linkType: hard
1612+
16041613
"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0":
16051614
version: 4.4.1
16061615
resolution: "@eslint-community/eslint-utils@npm:4.4.1"
@@ -2844,6 +2853,7 @@ __metadata:
28442853
pretty-format: "npm:^29.7.0"
28452854
react: "npm:^19.0.0"
28462855
react-native: "npm:0.78.0-rc.5"
2856+
react-native-gesture-handler: "npm:^2.23.1"
28472857
react-test-renderer: "npm:^19.0.0"
28482858
redent: "npm:^3.0.0"
28492859
release-it: "npm:^18.0.0"
@@ -2924,6 +2934,13 @@ __metadata:
29242934
languageName: node
29252935
linkType: hard
29262936

2937+
"@types/hammerjs@npm:^2.0.36":
2938+
version: 2.0.46
2939+
resolution: "@types/hammerjs@npm:2.0.46"
2940+
checksum: 10c0/f3c1cb20dc2f0523f7b8c76065078544d50d8ae9b0edc1f62fed657210ed814266ff2dfa835d2c157a075991001eec3b64c88bf92e3e6e895c0db78d05711d06
2941+
languageName: node
2942+
linkType: hard
2943+
29272944
"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1":
29282945
version: 2.0.6
29292946
resolution: "@types/istanbul-lib-coverage@npm:2.0.6"
@@ -6103,6 +6120,15 @@ __metadata:
61036120
languageName: node
61046121
linkType: hard
61056122

6123+
"hoist-non-react-statics@npm:^3.3.0":
6124+
version: 3.3.2
6125+
resolution: "hoist-non-react-statics@npm:3.3.2"
6126+
dependencies:
6127+
react-is: "npm:^16.7.0"
6128+
checksum: 10c0/fe0889169e845d738b59b64badf5e55fa3cf20454f9203d1eb088df322d49d4318df774828e789898dcb280e8a5521bb59b3203385662ca5e9218a6ca5820e74
6129+
languageName: node
6130+
linkType: hard
6131+
61066132
"hosted-git-info@npm:^7.0.0":
61076133
version: 7.0.2
61086134
resolution: "hosted-git-info@npm:7.0.2"
@@ -9141,7 +9167,7 @@ __metadata:
91419167
languageName: node
91429168
linkType: hard
91439169

9144-
"react-is@npm:^16.13.1":
9170+
"react-is@npm:^16.13.1, react-is@npm:^16.7.0":
91459171
version: 16.13.1
91469172
resolution: "react-is@npm:16.13.1"
91479173
checksum: 10c0/33977da7a5f1a287936a0c85639fec6ca74f4f15ef1e59a6bc20338fc73dc69555381e211f7a3529b8150a1f71e4225525b41b60b52965bda53ce7d47377ada1
@@ -9162,6 +9188,20 @@ __metadata:
91629188
languageName: node
91639189
linkType: hard
91649190

9191+
"react-native-gesture-handler@npm:^2.23.1":
9192+
version: 2.23.1
9193+
resolution: "react-native-gesture-handler@npm:2.23.1"
9194+
dependencies:
9195+
"@egjs/hammerjs": "npm:^2.0.17"
9196+
hoist-non-react-statics: "npm:^3.3.0"
9197+
invariant: "npm:^2.2.4"
9198+
peerDependencies:
9199+
react: "*"
9200+
react-native: "*"
9201+
checksum: 10c0/02ad664662a529836aaae2f6bce5943ccd3193d33641e6328d1306e6cb82eb5d859c5f9975c557f6e6daf1ffb7e7f6cd8432dac70a727110bff887e554673c7c
9202+
languageName: node
9203+
linkType: hard
9204+
91659205
"react-native@npm:0.78.0-rc.5":
91669206
version: 0.78.0-rc.5
91679207
resolution: "react-native@npm:0.78.0-rc.5"

0 commit comments

Comments
 (0)