[Flare] Account for fixed elements in getAbsoluteBoundingClientRect (#15707)

This commit is contained in:
Dominic Gannaway
2019-05-22 19:54:08 +01:00
committed by GitHub
parent f3109ad8a7
commit b5dff62faf
2 changed files with 147 additions and 7 deletions
+26 -4
View File
@@ -436,10 +436,32 @@ function getAbsoluteBoundingClientRect(
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;
while (node != null) {
const parent = node.parentNode;
const scrollTop = node.scrollTop;
const scrollLeft = node.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.
if (
!isParentDocumentNode &&
node.offsetParent === null &&
(scrollLeft !== 0 || scrollTop !== 0)
) {
break;
}
offsetX += scrollLeft;
offsetY += scrollTop;
if (isParentDocumentNode) {
break;
}
node = parent;
}
return {
left: left + offsetX,
@@ -1109,8 +1109,8 @@ describe('Event responder: Press', () => {
ReactDOM.render(element, container);
ref.current.getBoundingClientRect = getBoundingClientRectMock;
// Emulate the element being offset
document.body.scrollTop = 1000;
// Emulate the <html> element being offset with scroll
document.firstElementChild.scrollTop = 1000;
const updatedCoordinatesInside = {
pageX: coordinatesInside.pageX,
pageY: coordinatesInside.pageY + 1000,
@@ -1125,7 +1125,125 @@ describe('Event responder: Press', () => {
createEvent('pointerup', updatedCoordinatesInside),
);
jest.runAllTimers();
document.body.scrollTop = 0;
document.firstElementChild.scrollTop = 0;
expect(events).toEqual([
'onPressStart',
'onPressChange',
'onPressMove',
'onPressEnd',
'onPressChange',
'onPress',
]);
});
it('"onPress" is called on release inside a fixed container', () => {
let events = [];
const ref = React.createRef();
const fixedContainerRef = React.createRef();
const createEventHandler = msg => () => {
events.push(msg);
};
const element = (
<div ref={fixedContainerRef}>
<Press
onPress={createEventHandler('onPress')}
onPressChange={createEventHandler('onPressChange')}
onPressMove={createEventHandler('onPressMove')}
onPressStart={createEventHandler('onPressStart')}
onPressEnd={createEventHandler('onPressEnd')}>
<div ref={ref} />
</Press>
</div>
);
ReactDOM.render(element, container);
const fixedDiv = fixedContainerRef.current;
Object.defineProperty(fixedDiv, 'offsetParent', {
value: null,
});
// The fixed container is not scrolled
fixedDiv.scrollTop = 0;
ref.current.getBoundingClientRect = getBoundingClientRectMock;
// Emulate the <html> element being offset with scroll
document.firstElementChild.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.firstElementChild.scrollTop = 0;
expect(events).toEqual([
'onPressStart',
'onPressChange',
'onPressMove',
'onPressEnd',
'onPressChange',
'onPress',
]);
});
it('"onPress" is called on release inside a fixed scrolled container', () => {
let events = [];
const ref = React.createRef();
const fixedContainerRef = React.createRef();
const createEventHandler = msg => () => {
events.push(msg);
};
const element = (
<div ref={fixedContainerRef}>
<Press
onPress={createEventHandler('onPress')}
onPressChange={createEventHandler('onPressChange')}
onPressMove={createEventHandler('onPressMove')}
onPressStart={createEventHandler('onPressStart')}
onPressEnd={createEventHandler('onPressEnd')}>
<div ref={ref} />
</Press>
</div>
);
ReactDOM.render(element, container);
const fixedDiv = fixedContainerRef.current;
Object.defineProperty(fixedDiv, 'offsetParent', {
value: null,
});
// The fixed container is scrolled
fixedDiv.scrollTop = 100;
ref.current.getBoundingClientRect = getBoundingClientRectMock;
// Emulate the <html> element being offset with scroll
document.firstElementChild.scrollTop = 1000;
const updatedCoordinatesInside = {
pageX: coordinatesInside.pageX,
pageY: coordinatesInside.pageY + 100,
};
ref.current.dispatchEvent(
createEvent('pointerdown', updatedCoordinatesInside),
);
container.dispatchEvent(
createEvent('pointermove', updatedCoordinatesInside),
);
container.dispatchEvent(
createEvent('pointerup', updatedCoordinatesInside),
);
jest.runAllTimers();
document.firstElementChild.scrollTop = 0;
expect(events).toEqual([
'onPressStart',