Skip to content

Commit 81f216b

Browse files
TMaszkomdjastrzebski
authored andcommitted
fix: editable event check
1 parent 674b2f9 commit 81f216b

File tree

3 files changed

+147
-53
lines changed

3 files changed

+147
-53
lines changed
+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import React from 'react';
2+
import { Text, TextInput, TextInputProps } from 'react-native';
3+
import { render, fireEvent } from '..';
4+
5+
function WrappedTextInput(props: TextInputProps) {
6+
return <TextInput {...props} />;
7+
}
8+
9+
function DoubleWrappedTextInput(props: TextInputProps) {
10+
return <WrappedTextInput {...props} />;
11+
}
12+
13+
const layoutEvent = { nativeEvent: { layout: { width: 100, height: 100 } } };
14+
15+
test('should fire only non-touch-related events on non-editable TextInput', () => {
16+
const onFocus = jest.fn();
17+
const onChangeText = jest.fn();
18+
const onSubmitEditing = jest.fn();
19+
const onLayout = jest.fn();
20+
21+
const view = render(
22+
<TextInput
23+
editable={false}
24+
testID="subject"
25+
onFocus={onFocus}
26+
onChangeText={onChangeText}
27+
onSubmitEditing={onSubmitEditing}
28+
onLayout={onLayout}
29+
/>
30+
);
31+
32+
const subject = view.getByTestId('subject');
33+
fireEvent(subject, 'focus');
34+
fireEvent.changeText(subject, 'Text');
35+
fireEvent(subject, 'submitEditing', { nativeEvent: { text: 'Text' } });
36+
fireEvent(subject, 'layout', layoutEvent);
37+
38+
expect(onFocus).not.toHaveBeenCalled();
39+
expect(onChangeText).not.toHaveBeenCalled();
40+
expect(onSubmitEditing).not.toHaveBeenCalled();
41+
expect(onLayout).toHaveBeenCalledWith(layoutEvent);
42+
});
43+
44+
test('should fire only non-touch-related events on non-editable TextInput with nested Text', () => {
45+
const onFocus = jest.fn();
46+
const onChangeText = jest.fn();
47+
const onSubmitEditing = jest.fn();
48+
const onLayout = jest.fn();
49+
50+
const view = render(
51+
<TextInput
52+
editable={false}
53+
testID="subject"
54+
onFocus={onFocus}
55+
onChangeText={onChangeText}
56+
onSubmitEditing={onSubmitEditing}
57+
onLayout={onLayout}
58+
>
59+
<Text>Nested Text</Text>
60+
</TextInput>
61+
);
62+
63+
const subject = view.getByText('Nested Text');
64+
fireEvent(subject, 'focus');
65+
fireEvent.changeText(subject, 'Text');
66+
fireEvent(subject, 'submitEditing', { nativeEvent: { text: 'Text' } });
67+
fireEvent(subject, 'layout', layoutEvent);
68+
69+
expect(onFocus).not.toHaveBeenCalled();
70+
expect(onChangeText).not.toHaveBeenCalled();
71+
expect(onSubmitEditing).not.toHaveBeenCalled();
72+
expect(onLayout).toHaveBeenCalledWith(layoutEvent);
73+
});
74+
75+
test('should fire only non-touch-related events on non-editable wrapped TextInput', () => {
76+
const onFocus = jest.fn();
77+
const onChangeText = jest.fn();
78+
const onSubmitEditing = jest.fn();
79+
const onLayout = jest.fn();
80+
81+
const view = render(
82+
<WrappedTextInput
83+
editable={false}
84+
testID="subject"
85+
onFocus={onFocus}
86+
onChangeText={onChangeText}
87+
onSubmitEditing={onSubmitEditing}
88+
onLayout={onLayout}
89+
/>
90+
);
91+
92+
const subject = view.getByTestId('subject');
93+
fireEvent(subject, 'focus');
94+
fireEvent.changeText(subject, 'Text');
95+
fireEvent(subject, 'submitEditing', { nativeEvent: { text: 'Text' } });
96+
fireEvent(subject, 'layout', layoutEvent);
97+
98+
expect(onFocus).not.toHaveBeenCalled();
99+
expect(onChangeText).not.toHaveBeenCalled();
100+
expect(onSubmitEditing).not.toHaveBeenCalled();
101+
expect(onLayout).toHaveBeenCalledWith(layoutEvent);
102+
});
103+
104+
test('should fire only non-touch-related events on non-editable double wrapped TextInput', () => {
105+
const onFocus = jest.fn();
106+
const onChangeText = jest.fn();
107+
const onSubmitEditing = jest.fn();
108+
const onLayout = jest.fn();
109+
110+
const view = render(
111+
<DoubleWrappedTextInput
112+
editable={false}
113+
testID="subject"
114+
onFocus={onFocus}
115+
onChangeText={onChangeText}
116+
onSubmitEditing={onSubmitEditing}
117+
onLayout={onLayout}
118+
/>
119+
);
120+
121+
const subject = view.getByTestId('subject');
122+
fireEvent(subject, 'focus');
123+
fireEvent.changeText(subject, 'Text');
124+
fireEvent(subject, 'submitEditing', { nativeEvent: { text: 'Text' } });
125+
fireEvent(subject, 'layout', layoutEvent);
126+
127+
expect(onFocus).not.toHaveBeenCalled();
128+
expect(onChangeText).not.toHaveBeenCalled();
129+
expect(onSubmitEditing).not.toHaveBeenCalled();
130+
expect(onLayout).toHaveBeenCalledWith(layoutEvent);
131+
});

src/__tests__/fireEvent.test.tsx

-38
Original file line numberDiff line numberDiff line change
@@ -216,44 +216,6 @@ test('should not fire on disabled Pressable', () => {
216216
expect(handlePress).not.toHaveBeenCalled();
217217
});
218218

219-
test('should not fire on non-editable TextInput', () => {
220-
const testID = 'my-text-input';
221-
const onChangeTextMock = jest.fn();
222-
const NEW_TEXT = 'New text';
223-
224-
const { getByTestId } = render(
225-
<TextInput
226-
editable={false}
227-
testID={testID}
228-
onChangeText={onChangeTextMock}
229-
/>
230-
);
231-
232-
fireEvent.changeText(getByTestId(testID), NEW_TEXT);
233-
expect(onChangeTextMock).not.toHaveBeenCalled();
234-
});
235-
236-
test('should not fire on non-editable TextInput with nested Text', () => {
237-
const placeholder = 'Test placeholder';
238-
const onChangeTextMock = jest.fn();
239-
const NEW_TEXT = 'New text';
240-
241-
const { getByPlaceholderText } = render(
242-
<View>
243-
<TextInput
244-
editable={false}
245-
placeholder={placeholder}
246-
onChangeText={onChangeTextMock}
247-
>
248-
<Text>Test text</Text>
249-
</TextInput>
250-
</View>
251-
);
252-
253-
fireEvent.changeText(getByPlaceholderText(placeholder), NEW_TEXT);
254-
expect(onChangeTextMock).not.toHaveBeenCalled();
255-
});
256-
257219
test('should not fire inside View with pointerEvents="none"', () => {
258220
const onPress = jest.fn();
259221
const screen = render(

src/fireEvent.ts

+16-15
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,21 @@
11
import { ReactTestInstance } from 'react-test-renderer';
2-
import { TextInput } from 'react-native';
32
import act from './act';
43
import { getHostParent, isHostElement } from './helpers/component-tree';
5-
import { filterNodeByType } from './helpers/filterNodeByType';
64
import { getHostComponentNames } from './helpers/host-component-names';
75

86
type EventHandler = (...args: unknown[]) => unknown;
97

10-
function isTextInput(element: ReactTestInstance) {
11-
// We have to test if the element type is either the `TextInput` component
12-
// (for composite component) or the string "TextInput" (for host component)
13-
// All queries return host components but since fireEvent bubbles up
14-
// it would trigger the parent prop without the composite component check.
15-
return (
16-
filterNodeByType(element, TextInput) ||
17-
filterNodeByType(element, getHostComponentNames().textInput)
18-
);
19-
}
8+
const isHostTextInput = (element?: ReactTestInstance) => {
9+
return element?.type === getHostComponentNames().textInput;
10+
};
2011

2112
function isTouchResponder(element: ReactTestInstance) {
2213
if (!isHostElement(element)) {
2314
return false;
2415
}
2516

2617
return (
27-
Boolean(element.props.onStartShouldSetResponder) || isTextInput(element)
18+
Boolean(element.props.onStartShouldSetResponder) || isHostTextInput(element)
2819
);
2920
}
3021

@@ -57,13 +48,23 @@ function isTouchEvent(eventName: string) {
5748
return touchEventNames.includes(eventName);
5849
}
5950

51+
// Experimentally checked which events are called on non-editable TextInput
52+
const textInputEventsNotAffectedByEditableProp = [
53+
'contentSizeChange',
54+
'layout',
55+
'scroll',
56+
];
57+
6058
function isEventEnabled(
6159
element: ReactTestInstance,
6260
eventName: string,
6361
nearestTouchResponder?: ReactTestInstance
6462
) {
65-
if (isTextInput(element)) {
66-
return element.props.editable !== false;
63+
if (isHostTextInput(nearestTouchResponder)) {
64+
return (
65+
nearestTouchResponder?.props.editable !== false ||
66+
textInputEventsNotAffectedByEditableProp.includes(eventName)
67+
);
6768
}
6869

6970
if (isTouchEvent(eventName) && !isPointerEventEnabled(element)) {

0 commit comments

Comments
 (0)