Skip to content

feat(cdk/overlay): Extend cdkConnectedOverlayOrigin to support more types. #23253

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
26 changes: 23 additions & 3 deletions src/cdk/overlay/overlay-directives.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Component, ViewChild} from '@angular/core';
import {Component, ElementRef, ViewChild} from '@angular/core';
import {By} from '@angular/platform-browser';
import {
ComponentFixture,
Expand Down Expand Up @@ -28,7 +28,10 @@ import {
ConnectedOverlayPositionChange,
ConnectionPositionPair,
} from './position/connected-position';
import {FlexibleConnectedPositionStrategy} from './position/flexible-connected-position-strategy';
import {
FlexibleConnectedPositionStrategy,
FlexibleConnectedPositionStrategyOrigin,
} from './position/flexible-connected-position-strategy';
import {Subject} from 'rxjs';


Expand Down Expand Up @@ -408,6 +411,21 @@ describe('Overlay directives', () => {
expect(Math.floor(triggerRect.bottom)).toBe(Math.floor(overlayRect.top));
});

it('should be able to use non-directive origin', () => {
const testComponent = fixture.componentInstance;

testComponent.triggerOverride = testComponent.nonDirectiveTrigger;
testComponent.isOpen = true;
fixture.detectChanges();

const triggerRect =
fixture.nativeElement.querySelector('#nonDirectiveTrigger').getBoundingClientRect();
const overlayRect = getPaneElement().getBoundingClientRect();

expect(Math.floor(triggerRect.left)).toBe(Math.floor(overlayRect.left));
expect(Math.floor(triggerRect.bottom)).toBe(Math.floor(overlayRect.top));
});

it('should update the positions if they change after init', () => {
const trigger = fixture.nativeElement.querySelector('#trigger');

Expand Down Expand Up @@ -674,6 +692,7 @@ describe('Overlay directives', () => {
template: `
<button cdk-overlay-origin id="trigger" #trigger="cdkOverlayOrigin">Toggle menu</button>
<button cdk-overlay-origin id="otherTrigger" #otherTrigger="cdkOverlayOrigin">Toggle menu</button>
<button id="nonDirectiveTrigger" #nonDirectiveTrigger>Toggle menu</button>

<ng-template cdk-connected-overlay
[cdkConnectedOverlayOpen]="isOpen"
Expand Down Expand Up @@ -708,6 +727,7 @@ class ConnectedOverlayDirectiveTest {
@ViewChild(CdkConnectedOverlay) connectedOverlayDirective: CdkConnectedOverlay;
@ViewChild('trigger') trigger: CdkOverlayOrigin;
@ViewChild('otherTrigger') otherTrigger: CdkOverlayOrigin;
@ViewChild('nonDirectiveTrigger') nonDirectiveTrigger: ElementRef<HTMLElement>;

isOpen = false;
width: number | string;
Expand All @@ -717,7 +737,7 @@ class ConnectedOverlayDirectiveTest {
minHeight: number | string;
offsetX: number;
offsetY: number;
triggerOverride: CdkOverlayOrigin;
triggerOverride: CdkOverlayOrigin|FlexibleConnectedPositionStrategyOrigin;
hasBackdrop: boolean;
disableClose: boolean;
viewportMargin: number;
Expand Down
33 changes: 21 additions & 12 deletions src/cdk/overlay/overlay-directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {ConnectedOverlayPositionChange} from './position/connected-position';
import {
ConnectedPosition,
FlexibleConnectedPositionStrategy,
FlexibleConnectedPositionStrategyOrigin,
} from './position/flexible-connected-position-strategy';
import {
RepositionScrollStrategy,
Expand Down Expand Up @@ -114,7 +115,8 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
private _scrollStrategyFactory: () => ScrollStrategy;

/** Origin for the connected overlay. */
@Input('cdkConnectedOverlayOrigin') origin: CdkOverlayOrigin;
@Input('cdkConnectedOverlayOrigin')
origin: CdkOverlayOrigin|FlexibleConnectedPositionStrategyOrigin;

/** Registered connected position pairs. */
@Input('cdkConnectedOverlayPositions') positions: ConnectedPosition[];
Expand Down Expand Up @@ -352,24 +354,32 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
panelClass: currentPosition.panelClass || undefined,
}));

return positionStrategy
.setOrigin(this.origin.elementRef)
.withPositions(positions)
.withFlexibleDimensions(this.flexibleDimensions)
.withPush(this.push)
.withGrowAfterOpen(this.growAfterOpen)
.withViewportMargin(this.viewportMargin)
.withLockedPosition(this.lockPosition)
.withTransformOriginOn(this.transformOriginSelector);
return positionStrategy.setOrigin(this._getFlexibleConnectedPositionStrategyOrigin())
.withPositions(positions)
.withFlexibleDimensions(this.flexibleDimensions)
.withPush(this.push)
.withGrowAfterOpen(this.growAfterOpen)
.withViewportMargin(this.viewportMargin)
.withLockedPosition(this.lockPosition)
.withTransformOriginOn(this.transformOriginSelector);
}

/** Returns the position strategy of the overlay to be set on the overlay config */
private _createPositionStrategy(): FlexibleConnectedPositionStrategy {
const strategy = this._overlay.position().flexibleConnectedTo(this.origin.elementRef);
const strategy = this._overlay.position().flexibleConnectedTo(
this._getFlexibleConnectedPositionStrategyOrigin());
this._updatePositionStrategy(strategy);
return strategy;
}

private _getFlexibleConnectedPositionStrategyOrigin(): FlexibleConnectedPositionStrategyOrigin {
if (this.origin instanceof CdkOverlayOrigin) {
return this.origin.elementRef;
} else {
return this.origin;
}
}

/** Attaches the overlay and subscribes to backdrop clicks if backdrop exists */
private _attachOverlay() {
if (!this._overlayRef) {
Expand Down Expand Up @@ -425,7 +435,6 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
static ngAcceptInputType_push: BooleanInput;
}


/** @docs-private */
export function CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay):
() => RepositionScrollStrategy {
Expand Down
16 changes: 6 additions & 10 deletions src/material-experimental/mdc-select/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
ContentChild,
ContentChildren,
Directive,
ElementRef,
OnInit,
QueryList,
ViewEncapsulation,
Expand Down Expand Up @@ -109,7 +110,7 @@ export class MatSelect extends _MatSelectBase<MatSelectChange> implements OnInit
];

/** Ideal origin for the overlay panel. */
_preferredOverlayOrigin: CdkOverlayOrigin | undefined;
_preferredOverlayOrigin: CdkOverlayOrigin | ElementRef | undefined;

/** Width of the overlay panel. */
_overlayWidth: number;
Expand All @@ -134,14 +135,7 @@ export class MatSelect extends _MatSelectBase<MatSelectChange> implements OnInit
// Note that it's important that we read this in `ngAfterViewInit`, because
// reading it earlier will cause the form field to return a different element.
if (this._parentFormField) {
// TODO(crisbeto): currently the MDC select is based on the standard one which uses the
// connected overlay directive for its panel. In order to keep the logic as similar as
// possible, we have to use the directive here which only accepts a `CdkOverlayOrigin` as
// its origin. For now we fake an origin directive by constructing an object that looks
// like it, although eventually we should switch to creating the OverlayRef here directly.
this._preferredOverlayOrigin = {
elementRef: this._parentFormField.getConnectedOverlayOrigin()
};
this._preferredOverlayOrigin = this._parentFormField.getConnectedOverlayOrigin();
}
}

Expand Down Expand Up @@ -193,7 +187,9 @@ export class MatSelect extends _MatSelectBase<MatSelectChange> implements OnInit

/** Gets how wide the overlay panel should be. */
private _getOverlayWidth() {
const refToMeasure = (this._preferredOverlayOrigin?.elementRef || this._elementRef);
const refToMeasure = this._preferredOverlayOrigin instanceof CdkOverlayOrigin ?
this._preferredOverlayOrigin.elementRef :
this._preferredOverlayOrigin || this._elementRef;
return refToMeasure.nativeElement.getBoundingClientRect().width;
}
}
2 changes: 1 addition & 1 deletion tools/public_api_guard/cdk/overlay.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
get offsetY(): number;
set offsetY(offsetY: number);
open: boolean;
origin: CdkOverlayOrigin;
origin: CdkOverlayOrigin | FlexibleConnectedPositionStrategyOrigin;
readonly overlayKeydown: EventEmitter<KeyboardEvent>;
readonly overlayOutsideClick: EventEmitter<MouseEvent>;
get overlayRef(): OverlayRef;
Expand Down