Skip to content
Open
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
10 changes: 8 additions & 2 deletions packages/react-aria/src/overlays/calculatePosition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,18 +343,24 @@ function getMaxHeight(
let boundaryToContainerTransformOffset = isContainerDescendentOfBoundary
? containerOffsetWithBoundary.top
: 0;
// Use the same visual viewport origin as getContainerDimensions. WebKit can
// report an offsetTop that disagrees with pageTop while the keyboard is open.
// Reading from body also handles documents that scroll on body rather than html.
let visualViewportTop = visualViewport
? getContainerDimensions(document.body, visualViewport).top
: null;
let boundingRect = {
// This should be boundary top in container coord system vs viewport top in container coord system
// For the viewport top, there are several cases
// 1. pinchzoom case where we want the viewports offset top as top here
// 2. case where container is offset from the boundary and is contained by the boundary. In this case the top we want here is NOT 0, we want to take boundary's top even though is is a negative number OR the visual viewport, whichever is more restrictive
top: Math.max(
boundaryDimensions.top + boundaryToContainerTransformOffset,
(visualViewport?.offsetTop ?? boundaryDimensions.top) + boundaryToContainerTransformOffset
(visualViewportTop ?? boundaryDimensions.top) + boundaryToContainerTransformOffset
),
bottom: Math.min(
boundaryDimensions.top + boundaryDimensions.height + boundaryToContainerTransformOffset,
(visualViewport?.offsetTop ?? 0) + (visualViewport?.height ?? 0)
(visualViewportTop ?? 0) + (visualViewport?.height ?? 0)
)
};
let maxHeight =
Expand Down
51 changes: 50 additions & 1 deletion packages/react-aria/test/overlays/calculatePosition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* governing permissions and limitations under the License.
*/

import {calculatePosition} from '../../src/overlays/calculatePosition';
import {calculatePosition, calculatePositionInternal} from '../../src/overlays/calculatePosition';

const FLIPPED_DIRECTION = {
left: 'right'
Expand Down Expand Up @@ -101,6 +101,55 @@ const overlaySize = {
const PROVIDER_OFFSET = 50;

describe('calculatePosition', function () {
it('keeps an upward popover visible with the iOS keyboard open', () => {
let previousScrollTop = document.documentElement.scrollTop;
let previousBodyScrollTop = document.body.scrollTop;
document.documentElement.scrollTop = 466;

try {
let dimensions = {
width: 390,
height: 404,
totalWidth: 390,
totalHeight: 714,
top: 0,
left: 0,
scroll: {top: 466, left: 0}
};
let position = (offsetTop: number) =>
calculatePositionInternal(
'top',
{top: 680, left: 100, width: 100, height: 40},
{top: 0, left: 0, width: 200, height: 200},
{top: 0, left: 0, width: 200, height: 200},
{top: 0, bottom: 0, left: 0, right: 0},
8,
false,
dimensions,
dimensions,
{top: 0, left: 0, width: 0, height: 0},
8,
0,
false,
undefined,
0,
0,
true,
{pageTop: 466, offsetTop, height: 404, scale: 1} as VisualViewport
);

expect(position(0).maxHeight).toBe(198);
expect(position(310).maxHeight).toBe(198);

document.documentElement.scrollTop = 0;
document.body.scrollTop = 466;
expect(position(310).maxHeight).toBe(198);
} finally {
document.documentElement.scrollTop = previousScrollTop;
document.body.scrollTop = previousBodyScrollTop;
}
});

function checkPositionCommon(
title,
expected,
Expand Down
20 changes: 20 additions & 0 deletions packages/react-aria/test/overlays/useOverlayPosition.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,26 @@ describe('useOverlayPosition', function () {
`);
});

it('keeps a top popover open above the iOS keyboard when offsetTop is inconsistent', () => {
let clientHeight = jest
.spyOn(document.documentElement, 'clientHeight', 'get')
.mockReturnValue(714);
let previousScrollTop = document.documentElement.scrollTop;
document.documentElement.scrollTop = 466;
Object.assign(window.visualViewport!, {pageTop: 466, offsetTop: 310, height: 404});

try {
let res = render(<Example placement="top" triggerTop={214} />);
let overlay = res.getByTestId('overlay');

expect(overlay).toHaveTextContent('placement: top');
expect(parseFloat(overlay.style.maxHeight)).toBeGreaterThan(100);
} finally {
document.documentElement.scrollTop = previousScrollTop;
clientHeight.mockRestore();
}
});

it('should update the position on window resize', function () {
let res = render(<Example triggerTop={400} />);
let overlay = res.getByTestId('overlay');
Expand Down
Loading