mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[Flare] Account for fixed elements in getAbsoluteBoundingClientRect (#15707)
This commit is contained in:
Vendored
+26
-4
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user