@samitouri / QOS-React-2 / commits / bbd21066e6

[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.

Nicolas Gallagher committed Jul 22, 2019 at 18:16 UTC bbd21066e634c03db67f0b9570de60fdb4879dc0
2 files changed +79 -15
packages/react-events/src/dom/Press.js
+25 -5
@@ -685,14 +685,35 @@ const PressResponder: ReactDOMEventResponder = {
685 const isKeyboardEvent = pointerType === 'keyboard';
686 const isMouseEvent = pointerType === 'mouse';
687
688 + // Ignore emulated mouse events
689 + if (type === 'mousedown' && state.ignoreEmulatedMouseEvents) {
690 + return;
691 + }
692 +
693 + state.shouldPreventClick = false;
694 if (isPointerEvent || isTouchEvent) {
695 state.ignoreEmulatedMouseEvents = true;
690 - } else if (type === 'mousedown' && state.ignoreEmulatedMouseEvents) {
691 - // Ignore emulated mouse events
692 - return;
696 } else if (isKeyboardEvent) {
697 // 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 {
717 return;
718 }
719 }
@@ -920,7 +941,6 @@ const PressResponder: ReactDOMEventResponder = {
941 }
942
943 // Determine whether to call preventDefault on subsequent native events.
923 - state.shouldPreventClick = false;
944 if (
945 context.isTargetWithinEventComponent(target) &&
946 context.isTargetWithinHostComponent(target, 'a', true)
packages/react-events/src/dom/__tests__/Press-test.internal.js
+54 -10
@@ -46,11 +46,7 @@ function createTouchEvent(type, id, data) {
46 }
47
48 const createKeyboardEvent = (type, data) => {
49 - return new KeyboardEvent(type, {
50 - bubbles: true,
51 - cancelable: true,
52 - ...data,
53 - });
49 + return createEvent(type, data);
50 };
51
52 function init() {
@@ -216,13 +212,20 @@ describe('Event responder: Press', () => {
212 });
213
214 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();
220 ref.current.dispatchEvent(createKeyboardEvent('keypress', {key: ' '}));
221 ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: ' '}));
222 ref.current.dispatchEvent(createKeyboardEvent('keypress', {key: ' '}));
223 expect(onPressStart).toHaveBeenCalledTimes(1);
224 expect(onPressStart).toHaveBeenCalledWith(
225 - expect.objectContaining({pointerType: 'keyboard', type: 'pressstart'}),
225 + expect.objectContaining({
226 + pointerType: 'keyboard',
227 + type: 'pressstart',
228 + }),
229 );
230 });
231
@@ -411,7 +414,6 @@ describe('Event responder: Press', () => {
414 target: ref.current,
415 }),
416 );
414 - ref.current.dispatchEvent(createEvent('mousedown'));
417 ref.current.dispatchEvent(
418 createEvent('pointerup', {pointerType: 'touch'}),
419 );
@@ -420,7 +422,9 @@ describe('Event responder: Press', () => {
422 target: ref.current,
423 }),
424 );
425 + ref.current.dispatchEvent(createEvent('mousedown'));
426 ref.current.dispatchEvent(createEvent('mouseup'));
427 + ref.current.dispatchEvent(createEvent('click'));
428 expect(onPressEnd).toHaveBeenCalledTimes(1);
429 expect(onPressEnd).toHaveBeenCalledWith(
430 expect.objectContaining({pointerType: 'touch', type: 'pressend'}),
@@ -2419,7 +2423,7 @@ describe('Event responder: Press', () => {
2423 });
2424
2425 describe('link components', () => {
2422 - it('prevents native behaviour by default', () => {
2426 + it('prevents native behaviour for pointer events by default', () => {
2427 const onPress = jest.fn();
2428 const preventDefault = jest.fn();
2429 const ref = React.createRef();
@@ -2444,6 +2448,26 @@ describe('Event responder: Press', () => {
2448 );
2449 });
2450
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 +
2471 it('deeply prevents native behaviour by default', () => {
2472 const onPress = jest.fn();
2473 const preventDefault = jest.fn();
@@ -2527,7 +2551,7 @@ describe('Event responder: Press', () => {
2551 });
2552 });
2553
2530 - it('uses native behaviour if preventDefault is false', () => {
2554 + it('uses native behaviour for pointer events if preventDefault is false', () => {
2555 const onPress = jest.fn();
2556 const preventDefault = jest.fn();
2557 const ref = React.createRef();
@@ -2552,6 +2576,26 @@ describe('Event responder: Press', () => {
2576 );
2577 });
2578
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 +
2599 it('warns when preventDefault is used in an event hook', () => {
2600 const onPress = jest.fn();
2601 const preventDefault = jest.fn();