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
6 changes: 3 additions & 3 deletions goldens/cdk/overlay/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
// (undocumented)
detach(): void;
dispose(): void;
getPopoverInsertionPoint(): Element | null;
getPopoverInsertionPoint(): Element | null | {type: 'parent', element: Element};
_origin: FlexibleConnectedPositionStrategyOrigin;
positionChanges: Observable<ConnectedOverlayPositionChange>;
get positions(): ConnectionPositionPair[];
Expand All @@ -311,7 +311,7 @@ export type FlexibleConnectedPositionStrategyOrigin = ElementRef | Element | (Po
});

// @public
export type FlexibleOverlayPopoverLocation = 'global' | 'inline';
export type FlexibleOverlayPopoverLocation = 'global' | 'inline' | {type: 'parent', element: Element };

// @public
export class FullscreenOverlayContainer extends OverlayContainer implements OnDestroy {
Expand Down Expand Up @@ -534,7 +534,7 @@ export interface PositionStrategy {
attach(overlayRef: OverlayRef): void;
detach?(): void;
dispose(): void;
getPopoverInsertionPoint?(): Element | null;
getPopoverInsertionPoint?(): Element | null | {type: 'parent', element: Element};
}

// @public
Expand Down
2 changes: 1 addition & 1 deletion src/cdk/overlay/overlay-directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
.withViewportMargin(this.viewportMargin)
.withLockedPosition(this.lockPosition)
.withTransformOriginOn(this.transformOriginSelector)
.withPopoverLocation(this.usePopover === 'global' ? 'global' : 'inline');
.withPopoverLocation(this.usePopover === null ? 'global' : this.usePopover);
}

/** Returns the position strategy of the overlay to be set on the overlay config */
Expand Down
8 changes: 7 additions & 1 deletion src/cdk/overlay/overlay-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,13 @@ export class OverlayRef implements PortalOutlet {
: null;

if (customInsertionPoint) {
customInsertionPoint.after(this._host);
if (customInsertionPoint instanceof Element) {
customInsertionPoint.after(this._host);
} else {
if (customInsertionPoint.type === 'parent') {
customInsertionPoint.element?.appendChild(this._host);
}
}
} else {
this._previousHostParent?.appendChild(this._host);
}
Expand Down
8 changes: 7 additions & 1 deletion src/cdk/overlay/overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,13 @@ export function createOverlayRef(injector: Injector, config?: OverlayConfig): Ov
// it's going to end up at the custom insertion point anyways. We need to do it,
// because some internal clients depend on the host passing through the container first.
if (customInsertionPoint) {
customInsertionPoint.after(host);
if (customInsertionPoint instanceof Element) {
customInsertionPoint.after(host);
} else {
if (customInsertionPoint.type === 'parent') {
customInsertionPoint.element?.appendChild(host);
}
}
}

return new OverlayRef(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2959,11 +2959,14 @@ describe('FlexibleConnectedPositionStrategy', () => {
let positionStrategy: FlexibleConnectedPositionStrategy;
let containerElement: HTMLElement;
let originElement: HTMLElement;
let customHostElement: HTMLElement;

beforeEach(() => {
containerElement = overlayContainer.getContainerElement();
originElement = createPositionedBlockElement();
customHostElement = createBlockElement('span');
document.body.appendChild(originElement);
document.body.appendChild(customHostElement);

positionStrategy = createFlexibleConnectedPositionStrategy(injector, originElement)
.withPopoverLocation('inline')
Expand All @@ -2979,6 +2982,7 @@ describe('FlexibleConnectedPositionStrategy', () => {

afterEach(() => {
originElement.remove();
customHostElement.remove();
});

it('should place the overlay inside the overlay container by default', () => {
Expand Down Expand Up @@ -3014,6 +3018,32 @@ describe('FlexibleConnectedPositionStrategy', () => {
overlayRef.attach(portal);
expect(originElement.nextElementSibling).toBe(overlayRef.hostElement);
});

it('should insert the overlay as a child of a custom element', () => {
if (!('showPopover' in document.body)) {
return;
}

positionStrategy.withPopoverLocation({type: 'parent', element: customHostElement});
attachOverlay({positionStrategy, usePopover: true});

expect(containerElement.contains(overlayRef.hostElement)).toBe(false);
expect(customHostElement.contains(overlayRef.hostElement)).toBe(true);
expect(overlayRef.hostElement.getAttribute('popover')).toBe('manual');
});

it('should insert the overlay as a child of the origin', () => {
if (!('showPopover' in document.body)) {
return;
}

positionStrategy.withPopoverLocation({type: 'parent', element: originElement});
attachOverlay({positionStrategy, usePopover: true});

expect(containerElement.contains(overlayRef.hostElement)).toBe(false);
expect(originElement.contains(overlayRef.hostElement)).toBe(true);
expect(overlayRef.hostElement.getAttribute('popover')).toBe('manual');
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ export function createFlexibleConnectedPositionStrategy(
}

/** Supported locations in the DOM for connected overlays. */
export type FlexibleOverlayPopoverLocation = 'global' | 'inline';
export type FlexibleOverlayPopoverLocation =
| 'global'
| 'inline'
| {type: 'parent'; element: Element};

/**
* A strategy for positioning overlays. Using this strategy, an overlay is given an
Expand Down Expand Up @@ -522,26 +525,43 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
* @param location Configures the location in the DOM. Supports the following values:
* - `global` - The default which inserts the overlay inside the overlay container.
* - `inline` - Inserts the overlay next to the trigger.
* - {type: 'parent', element: element} - Inserts the overlay to a child of a custom parent
* element.
*/
withPopoverLocation(location: FlexibleOverlayPopoverLocation): this {
this._popoverLocation = location;
return this;
}

/** @docs-private */
getPopoverInsertionPoint(): Element | null {
// Return null so it falls back to inserting into the overlay container.
getPopoverInsertionPoint(): Element | null | {type: 'parent'; element: Element} {
if (this._popoverLocation === 'global') {
return null;
}

const origin = this._origin;
let hostElement: Element | null = null;

if (origin instanceof ElementRef) {
return origin.nativeElement;
} else if (origin instanceof Element) {
return origin;
if (this._popoverLocation === 'inline') {
if (this._origin instanceof ElementRef) {
hostElement = this._origin.nativeElement;
} else if (this._origin instanceof Element) {
hostElement = this._origin;
}
} else {
// this._popoverLocation is {type: 'parent', element: Element}
hostElement = this._popoverLocation.element;
}

// If the location is 'inline', we're inserting as a sibling.
if (this._popoverLocation === 'inline') {
return hostElement;
}

// Otherwise we're inserting as a child.
if (hostElement) {
return {type: 'parent', element: hostElement};
}

return null;
}

Expand Down
2 changes: 1 addition & 1 deletion src/cdk/overlay/position/position-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ export interface PositionStrategy {
* Gets the element in the DOM after which to insert
* the overlay when it is rendered out as a popover.
*/
getPopoverInsertionPoint?(): Element | null;
getPopoverInsertionPoint?(): Element | null | {type: 'parent'; element: Element};
}
Loading