Skip to content

Commit 5f7b175

Browse files
authored
Pressable click fix (facebook#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
1 parent 4f59a14 commit 5f7b175

File tree

3 files changed

+14
-193
lines changed

3 files changed

+14
-193
lines changed

packages/react-dom/src/shared/ReactDOMTypes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import type {
1616
} from 'shared/ReactTypes';
1717
import type {DOMTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
1818

19-
type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | Touch;
19+
type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | TouchEvent;
2020

2121
export type PointerType =
2222
| ''

packages/react-interactions/events/src/dom/PressLegacy.js

Lines changed: 7 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ type PressState = {
6464
|}>,
6565
ignoreEmulatedMouseEvents: boolean,
6666
activePointerId: null | number,
67-
shouldPreventClick: boolean,
6867
touchEvent: null | Touch,
6968
...
7069
};
@@ -199,7 +198,6 @@ function createPressEvent(
199198
x: clientX,
200199
y: clientY,
201200
preventDefault() {
202-
state.shouldPreventClick = true;
203201
if (nativeEvent) {
204202
pressEvent.defaultPrevented = true;
205203
nativeEvent.preventDefault();
@@ -229,8 +227,7 @@ function dispatchEvent(
229227
const target = ((state.pressTarget: any): Element | Document);
230228
const pointerType = state.pointerType;
231229
const defaultPrevented =
232-
(event != null && event.nativeEvent.defaultPrevented === true) ||
233-
(name === 'press' && state.shouldPreventClick);
230+
event != null && event.nativeEvent.defaultPrevented === true;
234231
const touchEvent = state.touchEvent;
235232
const syntheticEvent = createPressEvent(
236233
context,
@@ -529,7 +526,6 @@ const pressResponderImpl = {
529526
responderRegionOnDeactivation: null,
530527
ignoreEmulatedMouseEvents: false,
531528
activePointerId: null,
532-
shouldPreventClick: false,
533529
touchEvent: null,
534530
};
535531
},
@@ -567,7 +563,6 @@ const pressResponderImpl = {
567563
return;
568564
}
569565

570-
state.shouldPreventClick = false;
571566
if (isTouchEvent) {
572567
state.ignoreEmulatedMouseEvents = true;
573568
} else if (isKeyboardEvent) {
@@ -587,7 +582,6 @@ const pressResponderImpl = {
587582
!altKey
588583
) {
589584
nativeEvent.preventDefault();
590-
state.shouldPreventClick = true;
591585
}
592586
} else {
593587
return;
@@ -645,9 +639,6 @@ const pressResponderImpl = {
645639
}
646640

647641
case 'click': {
648-
if (state.shouldPreventClick) {
649-
nativeEvent.preventDefault();
650-
}
651642
const onPress = props.onPress;
652643

653644
if (isFunction(onPress) && isScreenReaderVirtualClick(nativeEvent)) {
@@ -751,7 +742,6 @@ const pressResponderImpl = {
751742
case 'touchend': {
752743
if (isPressed) {
753744
const buttons = state.buttons;
754-
let isKeyboardEvent = false;
755745
let touchEvent;
756746
if (
757747
type === 'pointerup' &&
@@ -770,79 +760,13 @@ const pressResponderImpl = {
770760
if (!isValidKeyboardEvent(nativeEvent)) {
771761
return;
772762
}
773-
isKeyboardEvent = true;
774763
removeRootEventTypes(context, state);
775764
} else if (buttons === 4) {
776765
// Remove the root events here as no 'click' event is dispatched when this 'button' is pressed.
777766
removeRootEventTypes(context, state);
778767
}
779768

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;
807769
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-
}
846770
state.touchEvent = null;
847771
} else if (type === 'mouseup') {
848772
state.ignoreEmulatedMouseEvents = false;
@@ -855,6 +779,12 @@ const pressResponderImpl = {
855779
if (previousPointerType !== 'keyboard') {
856780
removeRootEventTypes(context, state);
857781
}
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+
}
858788
break;
859789
}
860790

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

Lines changed: 6 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -469,15 +469,12 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
469469
});
470470

471471
// @gate experimental
472-
it('is called after valid "keyup" event', () => {
472+
it('is called after valid "click" event', () => {
473473
componentInit();
474474
const target = createEventTarget(ref.current);
475-
target.keydown({key: 'Enter'});
476-
target.keyup({key: 'Enter'});
475+
target.pointerdown();
476+
target.pointerup();
477477
expect(onPress).toHaveBeenCalledTimes(1);
478-
expect(onPress).toHaveBeenCalledWith(
479-
expect.objectContaining({pointerType: 'keyboard', type: 'press'}),
480-
);
481478
});
482479

483480
// @gate experimental
@@ -804,40 +801,6 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
804801
});
805802
});
806803

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-
841804
// @gate experimental
842805
it('"onPress" is called on re-entry to hit rect', () => {
843806
componentInit();
@@ -926,8 +889,8 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
926889
'pointerdown',
927890
'inner: onPressEnd',
928891
'inner: onPressChange',
929-
'inner: onPress',
930892
'pointerup',
893+
'inner: onPress',
931894
]);
932895
});
933896
}
@@ -1023,28 +986,6 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1023986
// @gate experimental
1024987
it('prevents native behavior by default', () => {
1025988
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();
1048989
const ref = React.createRef();
1049990

1050991
const Component = () => {
@@ -1054,59 +995,9 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
1054995
ReactDOM.render(<Component />, container);
1055996

1056997
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);
1082998
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();
11101001
});
11111002

11121003
// @gate experimental

0 commit comments

Comments
 (0)