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

Event API: ensure calculateResponderRegion accounts for page offset (#15671)

Dominic Gannaway committed May 16, 2019 at 17:09 UTC aad5a264d27bba113d322613587997d2987ba2d3
2 files changed +76 -6
packages/react-events/src/Press.js
+25 -6
@@ -428,6 +428,29 @@ function calculateDelayMS(delay: ?number, min = 0, fallback = 0) {
428 return Math.max(min, maybeNumber != null ? maybeNumber : fallback);
429 }
430
431 +function getAbsoluteBoundingClientRect(
432 + target: Element,
433 +): {left: number, right: number, bottom: number, top: number} {
434 + const clientRect = target.getBoundingClientRect();
435 + let {left, right, bottom, top} = clientRect;
436 + let node = target.parentNode;
437 + let offsetX = 0;
438 + let offsetY = 0;
439 +
440 + // Traverse through all offset nodes
441 + while (node != null && node.nodeType !== Node.DOCUMENT_NODE) {
442 + offsetX += (node: any).scrollLeft;
443 + offsetY += (node: any).scrollTop;
444 + node = node.parentNode;
445 + }
446 + return {
447 + left: left + offsetX,
448 + right: right + offsetX,
449 + bottom: bottom + offsetY,
450 + top: top + offsetY,
451 + };
452 +}
453 +
454 // TODO: account for touch hit slop
455 function calculateResponderRegion(
456 context: ReactResponderContext,
@@ -440,12 +463,8 @@ function calculateResponderRegion(
463 props.pressRetentionOffset,
464 );
465
443 - const clientRect = target.getBoundingClientRect();
444 -
445 - let bottom = clientRect.bottom;
446 - let left = clientRect.left;
447 - let right = clientRect.right;
448 - let top = clientRect.top;
466 + const clientRect = getAbsoluteBoundingClientRect(target);
467 + let {left, right, bottom, top} = clientRect;
468
469 if (pressRetentionOffset) {
470 if (pressRetentionOffset.bottom != null) {
packages/react-events/src/__tests__/Press-test.internal.js
+51
@@ -1085,6 +1085,57 @@ describe('Event responder: Press', () => {
1085 });
1086 });
1087
1088 + describe('the page offset changes', () => {
1089 + it('"onPress" is called on release', () => {
1090 + let events = [];
1091 + const ref = React.createRef();
1092 + const createEventHandler = msg => () => {
1093 + events.push(msg);
1094 + };
1095 +
1096 + const element = (
1097 + <Press
1098 + onPress={createEventHandler('onPress')}
1099 + onPressChange={createEventHandler('onPressChange')}
1100 + onPressMove={createEventHandler('onPressMove')}
1101 + onPressStart={createEventHandler('onPressStart')}
1102 + onPressEnd={createEventHandler('onPressEnd')}>
1103 + <div ref={ref} />
1104 + </Press>
1105 + );
1106 +
1107 + ReactDOM.render(element, container);
1108 +
1109 + ref.current.getBoundingClientRect = getBoundingClientRectMock;
1110 + // Emulate the element being offset
1111 + document.body.scrollTop = 1000;
1112 + const updatedCoordinatesInside = {
1113 + pageX: coordinatesInside.pageX,
1114 + pageY: coordinatesInside.pageY + 1000,
1115 + };
1116 + ref.current.dispatchEvent(
1117 + createEvent('pointerdown', updatedCoordinatesInside),
1118 + );
1119 + container.dispatchEvent(
1120 + createEvent('pointermove', updatedCoordinatesInside),
1121 + );
1122 + container.dispatchEvent(
1123 + createEvent('pointerup', updatedCoordinatesInside),
1124 + );
1125 + jest.runAllTimers();
1126 + document.body.scrollTop = 0;
1127 +
1128 + expect(events).toEqual([
1129 + 'onPressStart',
1130 + 'onPressChange',
1131 + 'onPressMove',
1132 + 'onPressEnd',
1133 + 'onPressChange',
1134 + 'onPress',
1135 + ]);
1136 + });
1137 + });
1138 +
1139 describe('beyond bounds of hit rect', () => {
1140 /** ┌──────────────────┐
1141 * │ ┌────────────┐ │