Skip to content

Commit 800f9a0

Browse files
devongovettLFDanLu
andauthored
Fix infinite resizing issue in overlay positioning (#6312)
* Fix infinite resizing issue in overlay positioning * Fix arrow position --------- Co-authored-by: Daniel Lu <dl1644@gmail.com>
1 parent 4864fab commit 800f9a0

File tree

3 files changed

+85
-80
lines changed

3 files changed

+85
-80
lines changed

packages/@react-aria/overlays/src/calculatePosition.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,15 +417,15 @@ export function calculatePositionInternal(
417417

418418
// All values are transformed so that 0 is at the top/left of the overlay depending on the orientation
419419
// Prefer the arrow being in the center of the trigger/overlay anchor element
420-
let preferredArrowPosition = childOffset[crossAxis] + .5 * childOffset[crossSize] - overlaySize[crossAxis];
420+
let preferredArrowPosition = childOffset[crossAxis] + .5 * childOffset[crossSize] - position[crossAxis];
421421

422422
// Min/Max position limits for the arrow with respect to the overlay
423423
const arrowMinPosition = arrowSize / 2 + arrowBoundaryOffset;
424424
const arrowMaxPosition = overlaySize[crossSize] - (arrowSize / 2) - arrowBoundaryOffset;
425425

426426
// Min/Max position limits for the arrow with respect to the trigger/overlay anchor element
427-
const arrowOverlappingChildMinEdge = childOffset[crossAxis] - overlaySize[crossAxis] + (arrowSize / 2);
428-
const arrowOverlappingChildMaxEdge = childOffset[crossAxis] + childOffset[crossSize] - overlaySize[crossAxis] - (arrowSize / 2);
427+
const arrowOverlappingChildMinEdge = childOffset[crossAxis] - position[crossAxis] + (arrowSize / 2);
428+
const arrowOverlappingChildMaxEdge = childOffset[crossAxis] + childOffset[crossSize] - position[crossAxis] - (arrowSize / 2);
429429

430430
// Clamp the arrow positioning so that it always is within the bounds of the anchor and the overlay
431431
const arrowPositionOverlappingChild = clamp(preferredArrowPosition, arrowOverlappingChildMinEdge, arrowOverlappingChildMaxEdge);

packages/@react-aria/overlays/src/useOverlayPosition.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,11 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
145145

146146
// Always reset the overlay's previous max height if not defined by the user so that we can compensate for
147147
// RAC collections populating after a second render and properly set a correct max height + positioning when it populates.
148+
let overlay = (overlayRef.current as HTMLElement);
148149
if (!maxHeight && overlayRef.current) {
149-
(overlayRef.current as HTMLElement).style.maxHeight = 'none';
150+
overlay.style.top = '0px';
151+
overlay.style.bottom = '';
152+
overlay.style.maxHeight = (window.visualViewport?.height ?? window.innerHeight) + 'px';
150153
}
151154

152155
let position = calculatePosition({
@@ -166,8 +169,10 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
166169

167170
// Modify overlay styles directly so positioning happens immediately without the need of a second render
168171
// This is so we don't have to delay autoFocus scrolling or delay applying preventScroll for popovers
169-
Object.keys(position.position).forEach(key => (overlayRef.current as HTMLElement).style[key] = position.position[key] + 'px');
170-
(overlayRef.current as HTMLElement).style.maxHeight = position.maxHeight != null ? position.maxHeight + 'px' : undefined;
172+
overlay.style.top = '';
173+
overlay.style.bottom = '';
174+
Object.keys(position.position).forEach(key => overlay.style[key] = position.position[key] + 'px');
175+
overlay.style.maxHeight = position.maxHeight != null ? position.maxHeight + 'px' : undefined;
171176

172177
// Trigger a set state for a second render anyway for arrow positioning
173178
setPosition(position);

packages/@react-aria/overlays/test/calculatePosition.test.ts

Lines changed: 74 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -199,123 +199,123 @@ describe('calculatePosition', function () {
199199
const testCases = [
200200
{
201201
placement: 'left',
202-
noOffset: [50, 200, undefined, 196, 350],
203-
offsetBefore: [-200, 50, undefined, 50, 500],
202+
noOffset: [50, 200, undefined, 100, 350],
203+
offsetBefore: [-200, 50, undefined, 4, 500],
204204
offsetAfter: [300, 350, undefined, 196, 200],
205-
crossAxisOffsetPositive: [50, 210, undefined, 196, 340],
206-
crossAxisOffsetNegative: [50, 190, undefined, 196, 360],
207-
mainAxisOffset: [40, 200, undefined, 196, 350],
208-
arrowBoundaryOffset: [50, 322, undefined, 176, 228]
205+
crossAxisOffsetPositive: [50, 210, undefined, 90, 340],
206+
crossAxisOffsetNegative: [50, 190, undefined, 110, 360],
207+
mainAxisOffset: [40, 200, undefined, 100, 350],
208+
arrowBoundaryOffset: [50, 322, undefined, 24, 228]
209209
},
210210
{
211211
placement: 'left top',
212-
noOffset: [50, 250, undefined, 196, 300],
213-
offsetBefore: [-200, 50, undefined, 50, 500],
212+
noOffset: [50, 250, undefined, 50, 300],
213+
offsetBefore: [-200, 50, undefined, 4, 500],
214214
offsetAfter: [300, 350, undefined, 196, 200],
215-
crossAxisOffsetPositive: [50, 260, undefined, 196, 290],
216-
crossAxisOffsetNegative: [50, 240, undefined, 196, 310],
217-
mainAxisOffset: [40, 250, undefined, 196, 300],
218-
arrowBoundaryOffset: [50, 322, undefined, 176, 228]
215+
crossAxisOffsetPositive: [50, 260, undefined, 40, 290],
216+
crossAxisOffsetNegative: [50, 240, undefined, 60, 310],
217+
mainAxisOffset: [40, 250, undefined, 50, 300],
218+
arrowBoundaryOffset: [50, 322, undefined, 24, 228]
219219
},
220220
{
221221
placement: 'left bottom',
222-
noOffset: [50, 150, undefined, 196, 300],
223-
offsetBefore: [-200, 50, undefined, 50, 200],
222+
noOffset: [50, 150, undefined, 150, 300],
223+
offsetBefore: [-200, 50, undefined, 4, 200],
224224
offsetAfter: [300, 350, undefined, 196, 500],
225-
crossAxisOffsetPositive: [50, 160, undefined, 196, 310],
226-
crossAxisOffsetNegative: [50, 140, undefined, 196, 290],
227-
mainAxisOffset: [40, 150, undefined, 196, 300],
228-
arrowBoundaryOffset: [50, 322, undefined, 176, 472]
225+
crossAxisOffsetPositive: [50, 160, undefined, 140, 310],
226+
crossAxisOffsetNegative: [50, 140, undefined, 160, 290],
227+
mainAxisOffset: [40, 150, undefined, 150, 300],
228+
arrowBoundaryOffset: [50, 322, undefined, 24, 472]
229229
},
230230
{
231231
placement: 'top',
232-
noOffset: [200, 50, 196, undefined, 200],
233-
offsetBefore: [50, -200, 50, undefined, 0],
232+
noOffset: [200, 50, 100, undefined, 200],
233+
offsetBefore: [50, -200, 4, undefined, 0],
234234
offsetAfter: [350, 300, 196, undefined, 450],
235-
crossAxisOffsetPositive: [210, 50, 196, undefined, 200],
236-
crossAxisOffsetNegative: [190, 50, 196, undefined, 200],
237-
mainAxisOffset: [200, 40, 196, undefined, 190],
238-
arrowBoundaryOffset: [322, 50, 176, undefined, 200]
235+
crossAxisOffsetPositive: [210, 50, 90, undefined, 200],
236+
crossAxisOffsetNegative: [190, 50, 110, undefined, 200],
237+
mainAxisOffset: [200, 40, 100, undefined, 190],
238+
arrowBoundaryOffset: [322, 50, 24, undefined, 200]
239239
},
240240
{
241241
placement: 'top left',
242-
noOffset: [250, 50, 196, undefined, 200],
243-
offsetBefore: [50, -200, 50, undefined, 0],
242+
noOffset: [250, 50, 50, undefined, 200],
243+
offsetBefore: [50, -200, 4, undefined, 0],
244244
offsetAfter: [350, 300, 196, undefined, 450],
245-
crossAxisOffsetPositive: [260, 50, 196, undefined, 200],
246-
crossAxisOffsetNegative: [240, 50, 196, undefined, 200],
247-
mainAxisOffset: [250, 40, 196, undefined, 190],
248-
arrowBoundaryOffset: [322, 50, 176, undefined, 200]
245+
crossAxisOffsetPositive: [260, 50, 40, undefined, 200],
246+
crossAxisOffsetNegative: [240, 50, 60, undefined, 200],
247+
mainAxisOffset: [250, 40, 50, undefined, 190],
248+
arrowBoundaryOffset: [322, 50, 24, undefined, 200]
249249
},
250250
{
251251
placement: 'top right',
252-
noOffset: [150, 50, 196, undefined, 200],
253-
offsetBefore: [50, -200, 50, undefined, 0],
252+
noOffset: [150, 50, 150, undefined, 200],
253+
offsetBefore: [50, -200, 4, undefined, 0],
254254
offsetAfter: [350, 300, 196, undefined, 450],
255-
crossAxisOffsetPositive: [160, 50, 196, undefined, 200],
256-
crossAxisOffsetNegative: [140, 50, 196, undefined, 200],
257-
mainAxisOffset: [150, 40, 196, undefined, 190],
258-
arrowBoundaryOffset: [322, 50, 176, undefined, 200]
255+
crossAxisOffsetPositive: [160, 50, 140, undefined, 200],
256+
crossAxisOffsetNegative: [140, 50, 160, undefined, 200],
257+
mainAxisOffset: [150, 40, 150, undefined, 190],
258+
arrowBoundaryOffset: [322, 50, 24, undefined, 200]
259259
},
260260
{
261261
placement: 'bottom',
262-
noOffset: [200, 350, 196, undefined, 200],
263-
offsetBefore: [50, 100, 50, undefined, 450],
262+
noOffset: [200, 350, 100, undefined, 200],
263+
offsetBefore: [50, 100, 4, undefined, 450],
264264
offsetAfter: [350, 600, 196, undefined, 0],
265-
crossAxisOffsetPositive: [210, 350, 196, undefined, 200],
266-
crossAxisOffsetNegative: [190, 350, 196, undefined, 200],
267-
mainAxisOffset: [200, 360, 196, undefined, 190],
268-
arrowBoundaryOffset: [322, 350, 176, undefined, 200]
265+
crossAxisOffsetPositive: [210, 350, 90, undefined, 200],
266+
crossAxisOffsetNegative: [190, 350, 110, undefined, 200],
267+
mainAxisOffset: [200, 360, 100, undefined, 190],
268+
arrowBoundaryOffset: [322, 350, 24, undefined, 200]
269269
},
270270
{
271271
placement: 'bottom left',
272-
noOffset: [250, 350, 196, undefined, 200],
273-
offsetBefore: [50, 100, 50, undefined, 450],
272+
noOffset: [250, 350, 50, undefined, 200],
273+
offsetBefore: [50, 100, 4, undefined, 450],
274274
offsetAfter: [350, 600, 196, undefined, 0],
275-
crossAxisOffsetPositive: [260, 350, 196, undefined, 200],
276-
crossAxisOffsetNegative: [240, 350, 196, undefined, 200],
277-
mainAxisOffset: [250, 360, 196, undefined, 190],
278-
arrowBoundaryOffset: [322, 350, 176, undefined, 200]
275+
crossAxisOffsetPositive: [260, 350, 40, undefined, 200],
276+
crossAxisOffsetNegative: [240, 350, 60, undefined, 200],
277+
mainAxisOffset: [250, 360, 50, undefined, 190],
278+
arrowBoundaryOffset: [322, 350, 24, undefined, 200]
279279
},
280280
{
281281
placement: 'bottom right',
282-
noOffset: [150, 350, 196, undefined, 200],
283-
offsetBefore: [50, 100, 50, undefined, 450],
282+
noOffset: [150, 350, 150, undefined, 200],
283+
offsetBefore: [50, 100, 4, undefined, 450],
284284
offsetAfter: [350, 600, 196, undefined, 0],
285-
crossAxisOffsetPositive: [160, 350, 196, undefined, 200],
286-
crossAxisOffsetNegative: [140, 350, 196, undefined, 200],
287-
mainAxisOffset: [150, 360, 196, undefined, 190],
288-
arrowBoundaryOffset: [322, 350, 176, undefined, 200]
285+
crossAxisOffsetPositive: [160, 350, 140, undefined, 200],
286+
crossAxisOffsetNegative: [140, 350, 160, undefined, 200],
287+
mainAxisOffset: [150, 360, 150, undefined, 190],
288+
arrowBoundaryOffset: [322, 350, 24, undefined, 200]
289289
},
290290
{
291291
placement: 'right',
292-
noOffset: [350, 200, undefined, 196, 350],
293-
offsetBefore: [100, 50, undefined, 50, 500],
292+
noOffset: [350, 200, undefined, 100, 350],
293+
offsetBefore: [100, 50, undefined, 4, 500],
294294
offsetAfter: [600, 350, undefined, 196, 200],
295-
crossAxisOffsetPositive: [350, 210, undefined, 196, 340],
296-
crossAxisOffsetNegative: [350, 190, undefined, 196, 360],
297-
mainAxisOffset: [360, 200, undefined, 196, 350],
298-
arrowBoundaryOffset: [350, 322, undefined, 176, 228]
295+
crossAxisOffsetPositive: [350, 210, undefined, 90, 340],
296+
crossAxisOffsetNegative: [350, 190, undefined, 110, 360],
297+
mainAxisOffset: [360, 200, undefined, 100, 350],
298+
arrowBoundaryOffset: [350, 322, undefined, 24, 228]
299299
},
300300
{
301301
placement: 'right top',
302-
noOffset: [350, 250, undefined, 196, 300],
303-
offsetBefore: [100, 50, undefined, 50, 500],
302+
noOffset: [350, 250, undefined, 50, 300],
303+
offsetBefore: [100, 50, undefined, 4, 500],
304304
offsetAfter: [600, 350, undefined, 196, 200],
305-
crossAxisOffsetPositive: [350, 260, undefined, 196, 290],
306-
crossAxisOffsetNegative: [350, 240, undefined, 196, 310],
307-
mainAxisOffset: [360, 250, undefined, 196, 300],
308-
arrowBoundaryOffset: [350, 322, undefined, 176, 228]
305+
crossAxisOffsetPositive: [350, 260, undefined, 40, 290],
306+
crossAxisOffsetNegative: [350, 240, undefined, 60, 310],
307+
mainAxisOffset: [360, 250, undefined, 50, 300],
308+
arrowBoundaryOffset: [350, 322, undefined, 24, 228]
309309
},
310310
{
311311
placement: 'right bottom',
312-
noOffset: [350, 150, undefined, 196, 300],
313-
offsetBefore: [100, 50, undefined, 50, 200],
312+
noOffset: [350, 150, undefined, 150, 300],
313+
offsetBefore: [100, 50, undefined, 4, 200],
314314
offsetAfter: [600, 350, undefined, 196, 500],
315-
crossAxisOffsetPositive: [350, 160, undefined, 196, 310],
316-
crossAxisOffsetNegative: [350, 140, undefined, 196, 290],
317-
mainAxisOffset: [360, 150, undefined, 196, 300],
318-
arrowBoundaryOffset: [350, 322, undefined, 176, 472]
315+
crossAxisOffsetPositive: [350, 160, undefined, 140, 310],
316+
crossAxisOffsetNegative: [350, 140, undefined, 160, 290],
317+
mainAxisOffset: [360, 150, undefined, 150, 300],
318+
arrowBoundaryOffset: [350, 322, undefined, 24, 472]
319319
}
320320
];
321321

@@ -381,14 +381,14 @@ describe('calculatePosition', function () {
381381

382382
describe('positive offset', function () {
383383
checkPosition(
384-
'left', getTargetDimension({left: 0, top: 250}), [110, 200, undefined, 196, 350], 10, 0, true
384+
'left', getTargetDimension({left: 0, top: 250}), [110, 200, undefined, 100, 350], 10, 0, true
385385
);
386386
});
387387
});
388388

389389
describe('overlay smaller than target aligns in center', function () {
390390
checkPosition(
391-
'right', getTargetDimension({left: 250, top: 250}, overlaySize.height + 100, overlaySize.width + 100), [550, 300, undefined, 196, 250]
391+
'right', getTargetDimension({left: 250, top: 250}, overlaySize.height + 100, overlaySize.width + 100), [550, 300, undefined, 100, 250]
392392
);
393393
});
394394

0 commit comments

Comments
 (0)