Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions packages/react-events/src/Press.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Comment thread
trueadm marked this conversation as resolved.
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,
Expand Down
124 changes: 121 additions & 3 deletions packages/react-events/src/__tests__/Press-test.internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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',
Expand Down