Skip to content

Commit bbd2106

Browse files
authored
[Flare] Press: fix keyboard interactions (#16179)
Prevents Spacebar from scrolling the window. Prevents Enter from triggering a navigation if preventDefault is true. Fixes the emulated mouse events test.
1 parent 03944bf commit bbd2106

File tree

2 files changed

+79
-15
lines changed

2 files changed

+79
-15
lines changed

packages/react-events/src/dom/Press.js

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -685,14 +685,35 @@ const PressResponder: ReactDOMEventResponder = {
685685
const isKeyboardEvent = pointerType === 'keyboard';
686686
const isMouseEvent = pointerType === 'mouse';
687687

688+
// Ignore emulated mouse events
689+
if (type === 'mousedown' && state.ignoreEmulatedMouseEvents) {
690+
return;
691+
}
692+
693+
state.shouldPreventClick = false;
688694
if (isPointerEvent || isTouchEvent) {
689695
state.ignoreEmulatedMouseEvents = true;
690-
} else if (type === 'mousedown' && state.ignoreEmulatedMouseEvents) {
691-
// Ignore emulated mouse events
692-
return;
693696
} else if (isKeyboardEvent) {
694697
// Ignore unrelated key events
695-
if (!isValidKeyboardEvent(nativeEvent)) {
698+
if (isValidKeyboardEvent(nativeEvent)) {
699+
const {
700+
altKey,
701+
ctrlKey,
702+
metaKey,
703+
shiftKey,
704+
} = (nativeEvent: MouseEvent);
705+
if (nativeEvent.key === ' ') {
706+
nativeEvent.preventDefault();
707+
} else if (
708+
props.preventDefault !== false &&
709+
!shiftKey &&
710+
!metaKey &&
711+
!ctrlKey &&
712+
!altKey
713+
) {
714+
state.shouldPreventClick = true;
715+
}
716+
} else {
696717
return;
697718
}
698719
}
@@ -920,7 +941,6 @@ const PressResponder: ReactDOMEventResponder = {
920941
}
921942

922943
// Determine whether to call preventDefault on subsequent native events.
923-
state.shouldPreventClick = false;
924944
if (
925945
context.isTargetWithinEventComponent(target) &&
926946
context.isTargetWithinHostComponent(target, 'a', true)

packages/react-events/src/dom/__tests__/Press-test.internal.js

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,7 @@ function createTouchEvent(type, id, data) {
4646
}
4747

4848
const createKeyboardEvent = (type, data) => {
49-
return new KeyboardEvent(type, {
50-
bubbles: true,
51-
cancelable: true,
52-
...data,
53-
});
49+
return createEvent(type, data);
5450
};
5551

5652
function init() {
@@ -216,13 +212,20 @@ describe('Event responder: Press', () => {
216212
});
217213

218214
it('is called once after "keydown" events for Spacebar', () => {
219-
ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: ' '}));
215+
const preventDefault = jest.fn();
216+
ref.current.dispatchEvent(
217+
createKeyboardEvent('keydown', {key: ' ', preventDefault}),
218+
);
219+
expect(preventDefault).toBeCalled();
220220
ref.current.dispatchEvent(createKeyboardEvent('keypress', {key: ' '}));
221221
ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: ' '}));
222222
ref.current.dispatchEvent(createKeyboardEvent('keypress', {key: ' '}));
223223
expect(onPressStart).toHaveBeenCalledTimes(1);
224224
expect(onPressStart).toHaveBeenCalledWith(
225-
expect.objectContaining({pointerType: 'keyboard', type: 'pressstart'}),
225+
expect.objectContaining({
226+
pointerType: 'keyboard',
227+
type: 'pressstart',
228+
}),
226229
);
227230
});
228231

@@ -411,7 +414,6 @@ describe('Event responder: Press', () => {
411414
target: ref.current,
412415
}),
413416
);
414-
ref.current.dispatchEvent(createEvent('mousedown'));
415417
ref.current.dispatchEvent(
416418
createEvent('pointerup', {pointerType: 'touch'}),
417419
);
@@ -420,7 +422,9 @@ describe('Event responder: Press', () => {
420422
target: ref.current,
421423
}),
422424
);
425+
ref.current.dispatchEvent(createEvent('mousedown'));
423426
ref.current.dispatchEvent(createEvent('mouseup'));
427+
ref.current.dispatchEvent(createEvent('click'));
424428
expect(onPressEnd).toHaveBeenCalledTimes(1);
425429
expect(onPressEnd).toHaveBeenCalledWith(
426430
expect.objectContaining({pointerType: 'touch', type: 'pressend'}),
@@ -2419,7 +2423,7 @@ describe('Event responder: Press', () => {
24192423
});
24202424

24212425
describe('link components', () => {
2422-
it('prevents native behaviour by default', () => {
2426+
it('prevents native behaviour for pointer events by default', () => {
24232427
const onPress = jest.fn();
24242428
const preventDefault = jest.fn();
24252429
const ref = React.createRef();
@@ -2444,6 +2448,26 @@ describe('Event responder: Press', () => {
24442448
);
24452449
});
24462450

2451+
it('prevents native behaviour for keyboard events by default', () => {
2452+
const onPress = jest.fn();
2453+
const preventDefault = jest.fn();
2454+
const ref = React.createRef();
2455+
const element = (
2456+
<Press onPress={onPress}>
2457+
<a href="#" ref={ref} />
2458+
</Press>
2459+
);
2460+
ReactDOM.render(element, container);
2461+
2462+
ref.current.dispatchEvent(createEvent('keydown', {key: 'Enter'}));
2463+
ref.current.dispatchEvent(createEvent('click', {preventDefault}));
2464+
ref.current.dispatchEvent(createEvent('keyup', {key: 'Enter'}));
2465+
expect(preventDefault).toBeCalled();
2466+
expect(onPress).toHaveBeenCalledWith(
2467+
expect.objectContaining({defaultPrevented: true}),
2468+
);
2469+
});
2470+
24472471
it('deeply prevents native behaviour by default', () => {
24482472
const onPress = jest.fn();
24492473
const preventDefault = jest.fn();
@@ -2527,7 +2551,7 @@ describe('Event responder: Press', () => {
25272551
});
25282552
});
25292553

2530-
it('uses native behaviour if preventDefault is false', () => {
2554+
it('uses native behaviour for pointer events if preventDefault is false', () => {
25312555
const onPress = jest.fn();
25322556
const preventDefault = jest.fn();
25332557
const ref = React.createRef();
@@ -2552,6 +2576,26 @@ describe('Event responder: Press', () => {
25522576
);
25532577
});
25542578

2579+
it('uses native behaviour for keyboard events if preventDefault is false', () => {
2580+
const onPress = jest.fn();
2581+
const preventDefault = jest.fn();
2582+
const ref = React.createRef();
2583+
const element = (
2584+
<Press onPress={onPress} preventDefault={false}>
2585+
<a href="#" ref={ref} />
2586+
</Press>
2587+
);
2588+
ReactDOM.render(element, container);
2589+
2590+
ref.current.dispatchEvent(createEvent('keydown', {key: 'Enter'}));
2591+
ref.current.dispatchEvent(createEvent('click', {preventDefault}));
2592+
ref.current.dispatchEvent(createEvent('keyup', {key: 'Enter'}));
2593+
expect(preventDefault).not.toBeCalled();
2594+
expect(onPress).toHaveBeenCalledWith(
2595+
expect.objectContaining({defaultPrevented: false}),
2596+
);
2597+
});
2598+
25552599
it('warns when preventDefault is used in an event hook', () => {
25562600
const onPress = jest.fn();
25572601
const preventDefault = jest.fn();

0 commit comments

Comments
 (0)