mirror of
https://github.com/callstack/react-native-testing-library.git
synced 2026-09-18 23:09:04 +08:00
fix: non-responder wrapping host elements ignore disabled prop (#572)
* test showing the issue * don't pass non-responder host elements as decsecendants * linter fixs * attempting to improve readability * refactor: re-structure code for clarity & redability Co-authored-by: Maciej Jastrzebski <mdjastrzebski@gmail.com>
This commit is contained in:
@@ -282,3 +282,26 @@ test('is not fooled by non-native disabled prop', () => {
|
||||
fireEvent.press(screen.getByText('Trigger Test'));
|
||||
expect(handlePress).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
function TestChildTouchableComponent({ onPress, someProp }) {
|
||||
return (
|
||||
<View>
|
||||
<TouchableOpacity onPress={onPress} disabled={someProp}>
|
||||
<Text>Trigger</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
test('is not fooled by non-responder wrapping host elements', () => {
|
||||
const handlePress = jest.fn();
|
||||
|
||||
const screen = render(
|
||||
<View>
|
||||
<TestChildTouchableComponent onPress={handlePress} someProp={true} />
|
||||
</View>
|
||||
);
|
||||
|
||||
fireEvent.press(screen.getByText('Trigger'));
|
||||
expect(handlePress).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
+29
-14
@@ -2,32 +2,47 @@
|
||||
import act from './act';
|
||||
import { ErrorWithStack } from './helpers/errors';
|
||||
|
||||
const isTextInputComponent = (element: ReactTestInstance) => {
|
||||
const isHostElement = (element?: ReactTestInstance) => {
|
||||
return typeof element?.type === 'string';
|
||||
};
|
||||
|
||||
const isTextInput = (element?: ReactTestInstance) => {
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const { TextInput } = require('react-native');
|
||||
return element.type === TextInput;
|
||||
return element?.type === TextInput;
|
||||
};
|
||||
|
||||
const isTouchResponder = (element?: ReactTestInstance) => {
|
||||
if (!isHostElement(element)) return false;
|
||||
|
||||
return !!element?.props.onStartShouldSetResponder || isTextInput(element);
|
||||
};
|
||||
|
||||
const isEventEnabled = (
|
||||
element?: ReactTestInstance,
|
||||
touchResponder?: ReactTestInstance
|
||||
) => {
|
||||
if (isTextInput(element)) return element?.props.editable !== false;
|
||||
|
||||
return touchResponder?.props.onStartShouldSetResponder?.() !== false;
|
||||
};
|
||||
|
||||
const findEventHandler = (
|
||||
element: ReactTestInstance,
|
||||
eventName: string,
|
||||
callsite?: any,
|
||||
nearestHostDescendent?: ReactTestInstance,
|
||||
nearestTouchResponder?: ReactTestInstance,
|
||||
hasDescendandHandler?: boolean
|
||||
) => {
|
||||
const touchResponder = isTouchResponder(element)
|
||||
? element
|
||||
: nearestTouchResponder;
|
||||
|
||||
const handler = getEventHandler(element, eventName);
|
||||
const hasHandler = handler != null || hasDescendandHandler;
|
||||
|
||||
const isHostComponent = typeof element.type === 'string';
|
||||
const hostElement = isHostComponent ? element : nearestHostDescendent;
|
||||
|
||||
const isEventEnabled = isTextInputComponent(element)
|
||||
? element.props.editable !== false
|
||||
: hostElement?.props.onStartShouldSetResponder?.() !== false;
|
||||
|
||||
if (handler && isEventEnabled) return handler;
|
||||
if (handler && isEventEnabled(element, touchResponder)) return handler;
|
||||
|
||||
// Do not bubble event to the root element
|
||||
const hasHandler = handler != null || hasDescendandHandler;
|
||||
if (element.parent === null || element.parent.parent === null) {
|
||||
if (hasHandler) {
|
||||
return null;
|
||||
@@ -43,7 +58,7 @@ const findEventHandler = (
|
||||
element.parent,
|
||||
eventName,
|
||||
callsite,
|
||||
hostElement,
|
||||
touchResponder,
|
||||
hasHandler
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user