Skip to content

Commit 306b704

Browse files
crisbetojelbourn
authored andcommitted
fix(overlay): incorrect bottom offset using upward-flowing flexible position with a viewport margin (#10650)
Fixes the `bottom` value and the viewport margin not being calculated correctly when using the `FlexibleConnectedPositionStrategy` with a flexible height and a viewport margin.
1 parent e30824c commit 306b704

File tree

2 files changed

+38
-8
lines changed

2 files changed

+38
-8
lines changed

src/cdk/overlay/position/flexible-connected-position-strategy.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,33 @@ describe('FlexibleConnectedPositionStrategy', () => {
13611361
expect(boundingBox.style.right).toBe('0px');
13621362
});
13631363

1364+
it('should calculate the bottom offset correctly with a viewport margin', () => {
1365+
const viewportMargin = 5;
1366+
1367+
originElement.style.top = `${OVERLAY_HEIGHT / 2}px`;
1368+
originElement.style.right = '200px';
1369+
1370+
positionStrategy
1371+
.withFlexibleHeight()
1372+
.withViewportMargin(viewportMargin)
1373+
.withPositions([
1374+
{
1375+
originX: 'start',
1376+
originY: 'top',
1377+
overlayX: 'start',
1378+
overlayY: 'bottom'
1379+
}
1380+
]);
1381+
1382+
attachOverlay({positionStrategy});
1383+
1384+
const originRect = originElement.getBoundingClientRect();
1385+
const overlayRect = overlayRef.overlayElement.getBoundingClientRect();
1386+
1387+
expect(Math.floor(overlayRect.bottom)).toBe(Math.floor(originRect.top));
1388+
expect(Math.floor(overlayRect.top)).toBe(viewportMargin);
1389+
});
1390+
13641391
});
13651392

13661393
describe('onPositionChange with scrollable view properties', () => {

src/cdk/overlay/position/flexible-connected-position-strategy.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -581,16 +581,19 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
581581
*/
582582
private _calculateBoundingBoxRect(origin: Point, position: ConnectedPosition): BoundingBoxRect {
583583
const viewport = this._viewportRect;
584+
const isRtl = this._isRtl();
584585
let height, top, bottom;
585586

586587
if (position.overlayY === 'top') {
587588
// Overlay is opening "downward" and thus is bound by the bottom viewport edge.
588589
top = origin.y;
589590
height = viewport.bottom - origin.y;
590591
} else if (position.overlayY === 'bottom') {
591-
// Overlay is opening "upward" and thus is bound by the top viewport edge.
592-
bottom = viewport.height - origin.y + this._viewportMargin;
593-
height = viewport.height - bottom;
592+
// Overlay is opening "upward" and thus is bound by the top viewport edge. We need to add
593+
// the viewport margin back in, because the viewport rect is narrowed down to remove the
594+
// margin, whereas the `origin` position is calculated based on its `ClientRect`.
595+
bottom = viewport.height - origin.y + this._viewportMargin * 2;
596+
height = viewport.height - bottom + this._viewportMargin;
594597
} else {
595598
// If neither top nor bottom, it means that the overlay
596599
// is vertically centered on the origin point.
@@ -608,13 +611,13 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
608611

609612
// The overlay is opening 'right-ward' (the content flows to the right).
610613
const isBoundedByRightViewportEdge =
611-
(position.overlayX === 'start' && !this._isRtl()) ||
612-
(position.overlayX === 'end' && this._isRtl());
614+
(position.overlayX === 'start' && !isRtl) ||
615+
(position.overlayX === 'end' && isRtl);
613616

614617
// The overlay is opening 'left-ward' (the content flows to the left).
615618
const isBoundedByLeftViewportEdge =
616-
(position.overlayX === 'end' && !this._isRtl()) ||
617-
(position.overlayX === 'start' && this._isRtl());
619+
(position.overlayX === 'end' && !isRtl) ||
620+
(position.overlayX === 'start' && isRtl);
618621

619622
let width, left, right;
620623

@@ -887,7 +890,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
887890
return {
888891
top: scrollPosition.top + this._viewportMargin,
889892
left: scrollPosition.left + this._viewportMargin,
890-
right: scrollPosition.left + width - this._viewportMargin,
893+
right: scrollPosition.left + width - this._viewportMargin,
891894
bottom: scrollPosition.top + height - this._viewportMargin,
892895
width: width - (2 * this._viewportMargin),
893896
height: height - (2 * this._viewportMargin),

0 commit comments

Comments
 (0)