diff --git a/packages/react-events/src/Press.js b/packages/react-events/src/Press.js
index ddefbc3cb2..d6d76e6bbf 100644
--- a/packages/react-events/src/Press.js
+++ b/packages/react-events/src/Press.js
@@ -68,6 +68,7 @@ type PressState = {
top: number,
|}>,
ignoreEmulatedMouseEvents: boolean,
+ allowPressReentry: boolean,
};
type PressEventType =
@@ -301,7 +302,6 @@ function dispatchPressEndEvents(
deactivate(context, props, state);
}
}
- removeRootEventTypes(context, state);
}
function isAnchorTagElement(eventTarget: EventTarget): boolean {
@@ -395,6 +395,7 @@ function unmountResponder(
state: PressState,
): void {
if (state.isPressed) {
+ removeRootEventTypes(context, state);
dispatchPressEndEvents(context, props, state);
}
}
@@ -411,8 +412,11 @@ function dispatchCancel(
(nativeEvent: any).preventDefault();
} else {
state.ignoreEmulatedMouseEvents = false;
+ removeRootEventTypes(context, state);
dispatchPressEndEvents(context, props, state);
}
+ } else if (state.allowPressReentry) {
+ removeRootEventTypes(context, state);
}
}
@@ -432,6 +436,7 @@ function removeRootEventTypes(
): void {
if (state.addedRootEvents) {
state.addedRootEvents = false;
+ state.allowPressReentry = false;
context.removeRootEventTypes(rootEventTypes);
}
}
@@ -455,6 +460,7 @@ const PressResponder = {
responderRegionOnActivation: null,
responderRegionOnDeactivation: null,
ignoreEmulatedMouseEvents: false,
+ allowPressReentry: false,
};
},
stopLocalPropagation: true,
@@ -467,6 +473,7 @@ const PressResponder = {
const {target, type} = event;
if (props.disabled) {
+ removeRootEventTypes(context, state);
dispatchPressEndEvents(context, props, state);
state.ignoreEmulatedMouseEvents = false;
return;
@@ -512,6 +519,7 @@ const PressResponder = {
return;
}
+ state.allowPressReentry = true;
state.pointerType = pointerType;
state.pressTarget = getEventCurrentTarget(event, context);
state.responderRegionOnActivation = calculateResponderRegion(
@@ -565,7 +573,7 @@ const PressResponder = {
case 'pointermove':
case 'mousemove':
case 'touchmove': {
- if (state.isPressed) {
+ if (state.isPressed || state.allowPressReentry) {
// Ignore emulated events (pointermove will dispatch touch and mouse events)
// Ignore pointermove events during a keyboard press.
if (state.pointerType !== pointerType) {
@@ -589,18 +597,24 @@ const PressResponder = {
);
if (state.isPressWithinResponderRegion) {
- if (props.onPressMove) {
- dispatchEvent(context, state, 'pressmove', props.onPressMove, {
- discrete: false,
- });
+ if (state.isPressed) {
+ if (props.onPressMove) {
+ dispatchEvent(context, state, 'pressmove', props.onPressMove, {
+ discrete: false,
+ });
+ }
+ } else {
+ dispatchPressStartEvents(context, props, state);
}
} else {
+ if (!state.allowPressReentry) {
+ removeRootEventTypes(context, state);
+ }
dispatchPressEndEvents(context, props, state);
}
}
break;
}
-
// END
case 'pointerup':
case 'keyup':
@@ -635,6 +649,7 @@ const PressResponder = {
}
const wasLongPressed = state.isLongPressed;
+ removeRootEventTypes(context, state);
dispatchPressEndEvents(context, props, state);
if (state.pressTarget !== null && props.onPress) {
@@ -652,6 +667,8 @@ const PressResponder = {
}
} else if (type === 'mouseup' && state.ignoreEmulatedMouseEvents) {
state.ignoreEmulatedMouseEvents = false;
+ } else if (state.allowPressReentry) {
+ removeRootEventTypes(context, state);
}
break;
}
diff --git a/packages/react-events/src/__tests__/Press-test.internal.js b/packages/react-events/src/__tests__/Press-test.internal.js
index 1a5d44d86d..abf190025d 100644
--- a/packages/react-events/src/__tests__/Press-test.internal.js
+++ b/packages/react-events/src/__tests__/Press-test.internal.js
@@ -1148,6 +1148,193 @@ describe('Event responder: Press', () => {
expect(events).toEqual([]);
});
});
+
+ it('"onPress" is not called on release with mouse', () => {
+ let events = [];
+ const ref = React.createRef();
+ const createEventHandler = msg => () => {
+ events.push(msg);
+ };
+
+ const element = (
+
+
+
+ );
+
+ ReactDOM.render(element, container);
+
+ ref.current.getBoundingClientRect = getBoundingClientRectMock;
+ ref.current.dispatchEvent(
+ createPointerEvent('pointerdown', {
+ pointerType: 'mouse',
+ }),
+ );
+ ref.current.dispatchEvent(
+ createPointerEvent('pointermove', {
+ ...coordinatesInside,
+ pointerType: 'mouse',
+ }),
+ );
+ container.dispatchEvent(
+ createPointerEvent('pointermove', {
+ ...coordinatesOutside,
+ pointerType: 'mouse',
+ }),
+ );
+ container.dispatchEvent(
+ createPointerEvent('pointerup', {
+ ...coordinatesOutside,
+ pointerType: 'mouse',
+ }),
+ );
+ jest.runAllTimers();
+
+ expect(events).toEqual([
+ 'onPressStart',
+ 'onPressChange',
+ 'onPressMove',
+ 'onPressEnd',
+ 'onPressChange',
+ ]);
+ });
+
+ it('"onPress" is called on re-entry to hit rect for mouse', () => {
+ let events = [];
+ const ref = React.createRef();
+ const createEventHandler = msg => () => {
+ events.push(msg);
+ };
+
+ const element = (
+
+
+
+ );
+
+ ReactDOM.render(element, container);
+
+ ref.current.getBoundingClientRect = getBoundingClientRectMock;
+ ref.current.dispatchEvent(
+ createPointerEvent('pointerdown', {
+ pointerType: 'mouse',
+ }),
+ );
+ ref.current.dispatchEvent(
+ createPointerEvent('pointermove', {
+ ...coordinatesInside,
+ pointerType: 'mouse',
+ }),
+ );
+ container.dispatchEvent(
+ createPointerEvent('pointermove', {
+ ...coordinatesOutside,
+ pointerType: 'mouse',
+ }),
+ );
+ container.dispatchEvent(
+ createPointerEvent('pointermove', {
+ ...coordinatesInside,
+ pointerType: 'mouse',
+ }),
+ );
+ container.dispatchEvent(
+ createPointerEvent('pointerup', {
+ ...coordinatesInside,
+ pointerType: 'mouse',
+ }),
+ );
+ jest.runAllTimers();
+
+ expect(events).toEqual([
+ 'onPressStart',
+ 'onPressChange',
+ 'onPressMove',
+ 'onPressEnd',
+ 'onPressChange',
+ 'onPressStart',
+ 'onPressChange',
+ 'onPressEnd',
+ 'onPressChange',
+ 'onPress',
+ ]);
+ });
+
+ it('"onPress" is called on re-entry to hit rect for touch', () => {
+ let events = [];
+ const ref = React.createRef();
+ const createEventHandler = msg => () => {
+ events.push(msg);
+ };
+
+ const element = (
+
+
+
+ );
+
+ ReactDOM.render(element, container);
+
+ ref.current.getBoundingClientRect = getBoundingClientRectMock;
+ ref.current.dispatchEvent(
+ createPointerEvent('pointerdown', {
+ pointerType: 'touch',
+ }),
+ );
+ ref.current.dispatchEvent(
+ createPointerEvent('pointermove', {
+ ...coordinatesInside,
+ pointerType: 'touch',
+ }),
+ );
+ container.dispatchEvent(
+ createPointerEvent('pointermove', {
+ ...coordinatesOutside,
+ pointerType: 'touch',
+ }),
+ );
+ container.dispatchEvent(
+ createPointerEvent('pointermove', {
+ ...coordinatesInside,
+ pointerType: 'touch',
+ }),
+ );
+ container.dispatchEvent(
+ createPointerEvent('pointerup', {
+ ...coordinatesInside,
+ pointerType: 'touch',
+ }),
+ );
+ jest.runAllTimers();
+
+ expect(events).toEqual([
+ 'onPressStart',
+ 'onPressChange',
+ 'onPressMove',
+ 'onPressEnd',
+ 'onPressChange',
+ 'onPressStart',
+ 'onPressChange',
+ 'onPressEnd',
+ 'onPressChange',
+ 'onPress',
+ ]);
+ });
});
describe('delayed and multiple events', () => {