From 025b07b61038300486b44c71f796a74cbdcd5d9f Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 23 May 2019 13:51:14 +0100 Subject: [PATCH] [Flare] Ensure getAbsoluteBoundingClientRect aligns with offsetParent (#15720) --- packages/react-events/src/Press.js | 36 ++++++++++++------- .../src/__tests__/Press-test.internal.js | 1 + 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/react-events/src/Press.js b/packages/react-events/src/Press.js index db86f553b3df..d9111a7c8b88 100644 --- a/packages/react-events/src/Press.js +++ b/packages/react-events/src/Press.js @@ -426,6 +426,18 @@ function calculateDelayMS(delay: ?number, min = 0, fallback = 0) { return Math.max(min, maybeNumber != null ? maybeNumber : fallback); } +function isNodeFixedPositioned(node: Node | null | void): boolean { + return ( + node != null && + (node: any).offsetParent === null && + !isNodeDocumentNode(node.parentNode) + ); +} + +function isNodeDocumentNode(node: Node | null | void): boolean { + return node != null && node.nodeType === Node.DOCUMENT_NODE; +} + function getAbsoluteBoundingClientRect( target: Element, ): {left: number, right: number, bottom: number, top: number} { @@ -440,25 +452,23 @@ function getAbsoluteBoundingClientRect( const parent = node.parentNode; const scrollTop = (node: any).scrollTop; const scrollLeft = (node: any).scrollLeft; - const isParentDocumentNode = - parent != null && parent.nodeType === Node.DOCUMENT_NODE; - - // Check if the current node is fixed position, by using - // offsetParent node for a fast-path. Then we need to - // check if it's a scrollable container by checking if - // either scrollLeft or scrollTop are not 0. If it is - // a scrollable container and the parent node is not - // the document, then we can stop traversing the tree. + + // We first need to check if it's a scrollable container by + // checking if either scrollLeft or scrollTop are not 0. + // Then we check if either the current node or its parent + // are fixed position, using offsetParent node for a fast-path. + // We need to check both as offsetParent accounts for both + // itself and the parent; so we need to align with that API. + // If these all pass, we can stop traversing the tree. if ( - !isParentDocumentNode && - (node: any).offsetParent === null && - (scrollLeft !== 0 || scrollTop !== 0) + (scrollLeft !== 0 || scrollTop !== 0) && + (isNodeFixedPositioned(parent) || isNodeFixedPositioned(node)) ) { break; } offsetX += scrollLeft; offsetY += scrollTop; - if (isParentDocumentNode) { + if (isNodeDocumentNode(parent)) { break; } node = parent; diff --git a/packages/react-events/src/__tests__/Press-test.internal.js b/packages/react-events/src/__tests__/Press-test.internal.js index 32bae5e13af6..46bb14fd72b2 100644 --- a/packages/react-events/src/__tests__/Press-test.internal.js +++ b/packages/react-events/src/__tests__/Press-test.internal.js @@ -1167,6 +1167,7 @@ describe('Event responder: Press', () => { // The fixed container is not scrolled fixedDiv.scrollTop = 0; + fixedDiv.scrollLeft = 0; ref.current.getBoundingClientRect = getBoundingClientRectMock; // Emulate the element being offset with scroll document.firstElementChild.scrollTop = 1000;