From aad5a264d27bba113d322613587997d2987ba2d3 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 16 May 2019 17:09:41 +0100 Subject: [PATCH] Event API: ensure calculateResponderRegion accounts for page offset (#15671) --- packages/react-events/src/Press.js | 31 ++++++++--- .../src/__tests__/Press-test.internal.js | 51 +++++++++++++++++++ 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/packages/react-events/src/Press.js b/packages/react-events/src/Press.js index 994a10c8f0..66589b2a9b 100644 --- a/packages/react-events/src/Press.js +++ b/packages/react-events/src/Press.js @@ -428,6 +428,29 @@ function calculateDelayMS(delay: ?number, min = 0, fallback = 0) { return Math.max(min, maybeNumber != null ? maybeNumber : fallback); } +function getAbsoluteBoundingClientRect( + target: Element, +): {left: number, right: number, bottom: number, top: number} { + const clientRect = target.getBoundingClientRect(); + let {left, right, bottom, top} = clientRect; + let node = target.parentNode; + let offsetX = 0; + let offsetY = 0; + + // Traverse through all offset nodes + while (node != null && node.nodeType !== Node.DOCUMENT_NODE) { + offsetX += (node: any).scrollLeft; + offsetY += (node: any).scrollTop; + node = node.parentNode; + } + return { + left: left + offsetX, + right: right + offsetX, + bottom: bottom + offsetY, + top: top + offsetY, + }; +} + // TODO: account for touch hit slop function calculateResponderRegion( context: ReactResponderContext, @@ -440,12 +463,8 @@ function calculateResponderRegion( props.pressRetentionOffset, ); - const clientRect = target.getBoundingClientRect(); - - let bottom = clientRect.bottom; - let left = clientRect.left; - let right = clientRect.right; - let top = clientRect.top; + const clientRect = getAbsoluteBoundingClientRect(target); + let {left, right, bottom, top} = clientRect; if (pressRetentionOffset) { if (pressRetentionOffset.bottom != null) { diff --git a/packages/react-events/src/__tests__/Press-test.internal.js b/packages/react-events/src/__tests__/Press-test.internal.js index edcd56899c..b8a5f2f58c 100644 --- a/packages/react-events/src/__tests__/Press-test.internal.js +++ b/packages/react-events/src/__tests__/Press-test.internal.js @@ -1085,6 +1085,57 @@ describe('Event responder: Press', () => { }); }); + describe('the page offset changes', () => { + it('"onPress" is called on release', () => { + let events = []; + const ref = React.createRef(); + const createEventHandler = msg => () => { + events.push(msg); + }; + + const element = ( + +
+ + ); + + ReactDOM.render(element, container); + + ref.current.getBoundingClientRect = getBoundingClientRectMock; + // Emulate the element being offset + document.body.scrollTop = 1000; + const updatedCoordinatesInside = { + pageX: coordinatesInside.pageX, + pageY: coordinatesInside.pageY + 1000, + }; + ref.current.dispatchEvent( + createEvent('pointerdown', updatedCoordinatesInside), + ); + container.dispatchEvent( + createEvent('pointermove', updatedCoordinatesInside), + ); + container.dispatchEvent( + createEvent('pointerup', updatedCoordinatesInside), + ); + jest.runAllTimers(); + document.body.scrollTop = 0; + + expect(events).toEqual([ + 'onPressStart', + 'onPressChange', + 'onPressMove', + 'onPressEnd', + 'onPressChange', + 'onPress', + ]); + }); + }); + describe('beyond bounds of hit rect', () => { /** ┌──────────────────┐ * │ ┌────────────┐ │