Skip to content

Commit 9100c21

Browse files
fix: pointer events evaluation (#1395)
1 parent caf5150 commit 9100c21

File tree

2 files changed

+54
-57
lines changed

2 files changed

+54
-57
lines changed

src/__tests__/fireEvent.test.tsx

Lines changed: 41 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -254,115 +254,108 @@ test('should not fire on non-editable TextInput with nested Text', () => {
254254
expect(onChangeTextMock).not.toHaveBeenCalled();
255255
});
256256

257-
test('should not fire on none pointerEvents View', () => {
258-
const handlePress = jest.fn();
259-
257+
test('should not fire inside View with pointerEvents="none"', () => {
258+
const onPress = jest.fn();
260259
const screen = render(
261260
<View pointerEvents="none">
262-
<Pressable onPress={handlePress}>
261+
<Pressable onPress={onPress}>
263262
<Text>Trigger</Text>
264263
</Pressable>
265264
</View>
266265
);
267266

268267
fireEvent.press(screen.getByText('Trigger'));
269-
expect(handlePress).not.toHaveBeenCalled();
268+
expect(onPress).not.toHaveBeenCalled();
270269
});
271270

272-
test('should not fire on box-only pointerEvents View', () => {
273-
const handlePress = jest.fn();
274-
271+
test('should not fire inside View with pointerEvents="box-only"', () => {
272+
const onPress = jest.fn();
275273
const screen = render(
276274
<View pointerEvents="box-only">
277-
<Pressable onPress={handlePress}>
275+
<Pressable onPress={onPress}>
278276
<Text>Trigger</Text>
279277
</Pressable>
280278
</View>
281279
);
282280

283281
fireEvent.press(screen.getByText('Trigger'));
284-
expect(handlePress).not.toHaveBeenCalled();
282+
expect(onPress).not.toHaveBeenCalled();
285283
});
286284

287-
test('should fire on box-none pointerEvents View', () => {
288-
const handlePress = jest.fn();
289-
285+
test('should fire inside View with pointerEvents="box-none"', () => {
286+
const onPress = jest.fn();
290287
const screen = render(
291288
<View pointerEvents="box-none">
292-
<Pressable onPress={handlePress}>
289+
<Pressable onPress={onPress}>
293290
<Text>Trigger</Text>
294291
</Pressable>
295292
</View>
296293
);
297294

298295
fireEvent.press(screen.getByText('Trigger'));
299-
expect(handlePress).toHaveBeenCalled();
296+
expect(onPress).toHaveBeenCalled();
300297
});
301298

302-
test('should fire on auto pointerEvents View', () => {
303-
const handlePress = jest.fn();
304-
299+
test('should fire inside View with pointerEvents="auto"', () => {
300+
const onPress = jest.fn();
305301
const screen = render(
306302
<View pointerEvents="auto">
307-
<Pressable onPress={handlePress}>
303+
<Pressable onPress={onPress}>
308304
<Text>Trigger</Text>
309305
</Pressable>
310306
</View>
311307
);
312308

313309
fireEvent.press(screen.getByText('Trigger'));
314-
expect(handlePress).toHaveBeenCalled();
310+
expect(onPress).toHaveBeenCalled();
315311
});
316312

317-
test('should not fire on box-only pointerEvents View with nested elements', () => {
318-
const handlePress = jest.fn();
319-
313+
test('should not fire deeply inside View with pointerEvents="box-only"', () => {
314+
const onPress = jest.fn();
320315
const screen = render(
321316
<View pointerEvents="box-only">
322317
<View>
323-
<Pressable onPress={handlePress}>
318+
<Pressable onPress={onPress}>
324319
<Text>Trigger</Text>
325320
</Pressable>
326321
</View>
327322
</View>
328323
);
329324

330325
fireEvent.press(screen.getByText('Trigger'));
331-
expect(handlePress).not.toHaveBeenCalled();
326+
expect(onPress).not.toHaveBeenCalled();
332327
});
333328

334-
test('should fire non-pointer events on box-none pointerEvents View', () => {
335-
const handleTouchStart = jest.fn();
336-
329+
test('should fire non-pointer events inside View with pointerEvents="box-none"', () => {
330+
const onTouchStart = jest.fn();
337331
const screen = render(
338-
<View
339-
pointerEvents="box-none"
340-
onTouchStart={handleTouchStart}
341-
testID="touch-start-view"
342-
>
343-
<Pressable onPress={() => {}}>
344-
<Text>Trigger</Text>
345-
</Pressable>
346-
</View>
332+
<View testID="view" pointerEvents="box-none" onTouchStart={onTouchStart} />
347333
);
348334

349-
fireEvent(screen.getByTestId('touch-start-view'), 'touchStart');
350-
expect(handleTouchStart).toHaveBeenCalled();
335+
fireEvent(screen.getByTestId('view'), 'touchStart');
336+
expect(onTouchStart).toHaveBeenCalled();
351337
});
352338

353-
test('should fire non-touch events on box-none pointerEvents View', () => {
354-
const handleLayout = jest.fn();
339+
test('should fire non-touch events inside View with pointerEvents="box-none"', () => {
340+
const onLayout = jest.fn();
341+
const screen = render(
342+
<View testID="view" pointerEvents="box-none" onLayout={onLayout} />
343+
);
344+
345+
fireEvent(screen.getByTestId('view'), 'layout');
346+
expect(onLayout).toHaveBeenCalled();
347+
});
355348

349+
// This test if pointerEvents="box-only" on composite `Pressable` is blocking
350+
// the 'press' event on host View rendered by pressable.
351+
test('should fire on Pressable with pointerEvents="box-only', () => {
352+
const onPress = jest.fn();
356353
const screen = render(
357-
<View pointerEvents="box-none" onLayout={handleLayout} testID="layout-view">
358-
<Pressable onPress={() => {}}>
359-
<Text>Trigger</Text>
360-
</Pressable>
361-
</View>
354+
<Pressable testID="pressable" pointerEvents="box-only" onPress={onPress} />
362355
);
363356

364-
fireEvent(screen.getByTestId('layout-view'), 'layout');
365-
expect(handleLayout).toHaveBeenCalled();
357+
fireEvent.press(screen.getByTestId('pressable'));
358+
expect(onPress).toHaveBeenCalled();
366359
});
367360

368361
test('should pass event up on disabled TouchableOpacity', () => {

src/fireEvent.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ReactTestInstance } from 'react-test-renderer';
22
import { TextInput } from 'react-native';
33
import act from './act';
4-
import { isHostElement } from './helpers/component-tree';
4+
import { getHostParent, isHostElement } from './helpers/component-tree';
55
import { filterNodeByType } from './helpers/filterNodeByType';
66
import { getHostComponentNames } from './helpers/host-component-names';
77

@@ -29,28 +29,32 @@ const isTouchResponder = (element?: ReactTestInstance) => {
2929
};
3030

3131
const isPointerEventEnabled = (
32-
element?: ReactTestInstance,
32+
element: ReactTestInstance,
3333
isParent?: boolean
3434
): boolean => {
35-
const parentCondition = isParent
36-
? element?.props.pointerEvents === 'box-only'
37-
: element?.props.pointerEvents === 'box-none';
35+
const pointerEvents = element.props.pointerEvents;
36+
if (pointerEvents === 'none') {
37+
return false;
38+
}
3839

39-
if (element?.props.pointerEvents === 'none' || parentCondition) {
40+
if (isParent ? pointerEvents === 'box-only' : pointerEvents === 'box-none') {
4041
return false;
4142
}
4243

43-
if (!element?.parent) return true;
44+
const parent = getHostParent(element);
45+
if (!parent) {
46+
return true;
47+
}
4448

45-
return isPointerEventEnabled(element.parent, true);
49+
return isPointerEventEnabled(parent, true);
4650
};
4751

4852
const isTouchEvent = (eventName?: string) => {
4953
return eventName === 'press';
5054
};
5155

5256
const isEventEnabled = (
53-
element?: ReactTestInstance,
57+
element: ReactTestInstance,
5458
touchResponder?: ReactTestInstance,
5559
eventName?: string
5660
) => {

0 commit comments

Comments
 (0)