@samitouri / QOS-React-2 / commits / 5f7b175b35

Pressable click fix (#18625)

* Update press-legacy to use native click events * update tests for pressable change * fix formatting issue * Address comments. Bring back some tests, remove others. Cleanup * Fix flow and lint errors * formatting fix missed by yarn lint

Naman Goel committed Apr 20, 2020 at 15:13 UTC 5f7b175b35fa6faccb0da14e897dcda9c13d6031
3 files changed +14 -193
packages/react-dom/src/shared/ReactDOMTypes.js
+1 -1
@@ -16,7 +16,7 @@ import type {
16 } from 'shared/ReactTypes';
17 import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
18
19 -type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | Touch;
19 +type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | TouchEvent;
20
21 export type PointerType =
22 | ''
packages/react-interactions/events/src/dom/PressLegacy.js
+7 -77
@@ -64,7 +64,6 @@ type PressState = {
64 |}>,
65 ignoreEmulatedMouseEvents: boolean,
66 activePointerId: null | number,
67 - shouldPreventClick: boolean,
67 touchEvent: null | Touch,
68 ...
69 };
@@ -199,7 +198,6 @@ function createPressEvent(
198 x: clientX,
199 y: clientY,
200 preventDefault() {
202 - state.shouldPreventClick = true;
201 if (nativeEvent) {
202 pressEvent.defaultPrevented = true;
203 nativeEvent.preventDefault();
@@ -229,8 +227,7 @@ function dispatchEvent(
227 const target = ((state.pressTarget: any): Element | Document);
228 const pointerType = state.pointerType;
229 const defaultPrevented =
232 - (event != null && event.nativeEvent.defaultPrevented === true) ||
233 - (name === 'press' && state.shouldPreventClick);
230 + event != null && event.nativeEvent.defaultPrevented === true;
231 const touchEvent = state.touchEvent;
232 const syntheticEvent = createPressEvent(
233 context,
@@ -529,7 +526,6 @@ const pressResponderImpl = {
526 responderRegionOnDeactivation: null,
527 ignoreEmulatedMouseEvents: false,
528 activePointerId: null,
532 - shouldPreventClick: false,
529 touchEvent: null,
530 };
531 },
@@ -567,7 +563,6 @@ const pressResponderImpl = {
563 return;
564 }
565
570 - state.shouldPreventClick = false;
566 if (isTouchEvent) {
567 state.ignoreEmulatedMouseEvents = true;
568 } else if (isKeyboardEvent) {
@@ -587,7 +582,6 @@ const pressResponderImpl = {
582 !altKey
583 ) {
584 nativeEvent.preventDefault();
590 - state.shouldPreventClick = true;
585 }
586 } else {
587 return;
@@ -645,9 +639,6 @@ const pressResponderImpl = {
639 }
640
641 case 'click': {
648 - if (state.shouldPreventClick) {
649 - nativeEvent.preventDefault();
650 - }
642 const onPress = props.onPress;
643
644 if (isFunction(onPress) && isScreenReaderVirtualClick(nativeEvent)) {
@@ -751,7 +742,6 @@ const pressResponderImpl = {
742 case 'touchend': {
743 if (isPressed) {
744 const buttons = state.buttons;
754 - let isKeyboardEvent = false;
745 let touchEvent;
746 if (
747 type === 'pointerup' &&
@@ -770,79 +760,13 @@ const pressResponderImpl = {
760 if (!isValidKeyboardEvent(nativeEvent)) {
761 return;
762 }
773 - isKeyboardEvent = true;
763 removeRootEventTypes(context, state);
764 } else if (buttons === 4) {
765 // Remove the root events here as no 'click' event is dispatched when this 'button' is pressed.
766 removeRootEventTypes(context, state);
767 }
768
780 - // Determine whether to call preventDefault on subsequent native events.
781 - if (
782 - target !== null &&
783 - context.isTargetWithinResponder(target) &&
784 - context.isTargetWithinHostComponent(target, 'a')
785 - ) {
786 - const {
787 - altKey,
788 - ctrlKey,
789 - metaKey,
790 - shiftKey,
791 - } = (nativeEvent: MouseEvent);
792 - // Check "open in new window/tab" and "open context menu" key modifiers
793 - const preventDefault = props.preventDefault;
794 -
795 - if (
796 - preventDefault !== false &&
797 - !shiftKey &&
798 - !metaKey &&
799 - !ctrlKey &&
800 - !altKey
801 - ) {
802 - state.shouldPreventClick = true;
803 - }
804 - }
805 -
806 - const pressTarget = state.pressTarget;
769 dispatchPressEndEvents(event, context, props, state);
808 - const onPress = props.onPress;
809 -
810 - if (pressTarget !== null && isFunction(onPress)) {
811 - if (
812 - !isKeyboardEvent &&
813 - pressTarget !== null &&
814 - target !== null &&
815 - !targetIsDocument(pressTarget)
816 - ) {
817 - if (
818 - pointerType === 'mouse' &&
819 - context.isTargetWithinNode(target, pressTarget)
820 - ) {
821 - state.isPressWithinResponderRegion = true;
822 - } else {
823 - // If the event target isn't within the press target, check if we're still
824 - // within the responder region. The region may have changed if the
825 - // element's layout was modified after activation.
826 - updateIsPressWithinResponderRegion(
827 - touchEvent || nativeEvent,
828 - context,
829 - props,
830 - state,
831 - );
832 - }
833 - }
834 -
835 - if (state.isPressWithinResponderRegion && buttons !== 4) {
836 - dispatchEvent(
837 - event,
838 - onPress,
839 - context,
840 - state,
841 - 'press',
842 - DiscreteEvent,
843 - );
844 - }
845 - }
770 state.touchEvent = null;
771 } else if (type === 'mouseup') {
772 state.ignoreEmulatedMouseEvents = false;
@@ -855,6 +779,12 @@ const pressResponderImpl = {
779 if (previousPointerType !== 'keyboard') {
780 removeRootEventTypes(context, state);
781 }
782 +
783 + const pressTarget = state.pressTarget;
784 + const onPress = props.onPress;
785 + if (pressTarget !== null && isFunction(onPress)) {
786 + dispatchEvent(event, onPress, context, state, 'press', DiscreteEvent);
787 + }
788 break;
789 }
790
packages/react-interactions/events/src/dom/__tests__/PressLegacy-test.internal.js
+6 -115
@@ -469,15 +469,12 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
469 });
470
471 // @gate experimental
472 - it('is called after valid "keyup" event', () => {
472 + it('is called after valid "click" event', () => {
473 componentInit();
474 const target = createEventTarget(ref.current);
475 - target.keydown({key: 'Enter'});
476 - target.keyup({key: 'Enter'});
475 + target.pointerdown();
476 + target.pointerup();
477 expect(onPress).toHaveBeenCalledTimes(1);
478 - expect(onPress).toHaveBeenCalledWith(
479 - expect.objectContaining({pointerType: 'keyboard', type: 'press'}),
480 - );
478 });
479
480 // @gate experimental
@@ -804,40 +801,6 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
801 });
802 });
803
807 - describe('beyond bounds of hit rect', () => {
808 - /** ┌──────────────────┐
809 - * │ ┌────────────┐ │
810 - * │ │ VisualRect │ │
811 - * │ └────────────┘ │
812 - * │ HitRect │
813 - * └──────────────────┘
814 - * X <= Move to X and release
815 - */
816 - // @gate experimental
817 - it('"onPress" is not called on release', () => {
818 - componentInit();
819 - const target = createEventTarget(ref.current);
820 - const targetContainer = createEventTarget(container);
821 - target.setBoundingClientRect(rectMock);
822 - target.pointerdown({pointerType});
823 - target.pointermove({pointerType, ...coordinatesInside});
824 - if (pointerType === 'mouse') {
825 - // TODO: use setPointerCapture so this is only true for fallback mouse events.
826 - targetContainer.pointermove({pointerType, ...coordinatesOutside});
827 - targetContainer.pointerup({pointerType, ...coordinatesOutside});
828 - } else {
829 - target.pointermove({pointerType, ...coordinatesOutside});
830 - target.pointerup({pointerType, ...coordinatesOutside});
831 - }
832 - expect(events.filter(removePressMoveStrings)).toEqual([
833 - 'onPressStart',
834 - 'onPressChange',
835 - 'onPressEnd',
836 - 'onPressChange',
837 - ]);
838 - });
839 - });
840 -
804 // @gate experimental
805 it('"onPress" is called on re-entry to hit rect', () => {
806 componentInit();
@@ -926,8 +889,8 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
889 'pointerdown',
890 'inner: onPressEnd',
891 'inner: onPressChange',
929 - 'inner: onPress',
892 'pointerup',
893 + 'inner: onPress',
894 ]);
895 });
896 }
@@ -1023,28 +986,6 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
986 // @gate experimental
987 it('prevents native behavior by default', () => {
988 const onPress = jest.fn();
1026 - const preventDefault = jest.fn();
1027 - const ref = React.createRef();
1028 -
1029 - const Component = () => {
1030 - const listener = usePress({onPress});
1031 - return <a href="#" ref={ref} DEPRECATED_flareListeners={listener} />;
1032 - };
1033 - ReactDOM.render(<Component />, container);
1034 -
1035 - const target = createEventTarget(ref.current);
1036 - target.pointerdown();
1037 - target.pointerup({preventDefault});
1038 - expect(preventDefault).toBeCalled();
1039 - expect(onPress).toHaveBeenCalledWith(
1040 - expect.objectContaining({defaultPrevented: true}),
1041 - );
1042 - });
1043 -
1044 - // @gate experimental
1045 - it('prevents native behaviour for keyboard events by default', () => {
1046 - const onPress = jest.fn();
1047 - const preventDefault = jest.fn();
989 const ref = React.createRef();
990
991 const Component = () => {
@@ -1054,59 +995,9 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
995 ReactDOM.render(<Component />, container);
996
997 const target = createEventTarget(ref.current);
1057 - target.keydown({key: 'Enter', preventDefault});
1058 - target.keyup({key: 'Enter'});
1059 - expect(preventDefault).toBeCalled();
1060 - expect(onPress).toHaveBeenCalledWith(
1061 - expect.objectContaining({defaultPrevented: true}),
1062 - );
1063 - });
1064 -
1065 - // @gate experimental
1066 - it('deeply prevents native behaviour by default', () => {
1067 - const onPress = jest.fn();
1068 - const preventDefault = jest.fn();
1069 - const buttonRef = React.createRef();
1070 -
1071 - const Component = () => {
1072 - const listener = usePress({onPress});
1073 - return (
1074 - <a href="#">
1075 - <button ref={buttonRef} DEPRECATED_flareListeners={listener} />
1076 - </a>
1077 - );
1078 - };
1079 - ReactDOM.render(<Component />, container);
1080 -
1081 - const target = createEventTarget(buttonRef.current);
998 target.pointerdown();
1083 - target.pointerup({preventDefault});
1084 - expect(preventDefault).toBeCalled();
1085 - });
1086 -
1087 - // @gate experimental
1088 - it('prevents native behaviour by default with nested elements', () => {
1089 - const onPress = jest.fn();
1090 - const preventDefault = jest.fn();
1091 - const ref = React.createRef();
1092 -
1093 - const Component = () => {
1094 - const listener = usePress({onPress});
1095 - return (
1096 - <a href="#" DEPRECATED_flareListeners={listener}>
1097 - <div ref={ref} />
1098 - </a>
1099 - );
1100 - };
1101 - ReactDOM.render(<Component />, container);
1102 -
1103 - const target = createEventTarget(ref.current);
1104 - target.pointerdown();
1105 - target.pointerup({preventDefault});
1106 - expect(preventDefault).toBeCalled();
1107 - expect(onPress).toHaveBeenCalledWith(
1108 - expect.objectContaining({defaultPrevented: true}),
1109 - );
999 + target.pointerup();
1000 + expect(onPress).toBeCalled();
1001 });
1002
1003 // @gate experimental