diff --git a/goldens/cdk/overlay/index.api.md b/goldens/cdk/overlay/index.api.md index 2a4176493bbe..ac0dfcb18729 100644 --- a/goldens/cdk/overlay/index.api.md +++ b/goldens/cdk/overlay/index.api.md @@ -332,6 +332,9 @@ export class FullscreenOverlayContainer extends OverlayContainer implements OnDe static ɵprov: i0.ɵɵInjectableDeclaration; } +// @public +export function getAttachedOverlays(): OverlayRef[]; + // @public export class GlobalPositionStrategy implements PositionStrategy { apply(): void; diff --git a/src/cdk/overlay/overlay-ref.ts b/src/cdk/overlay/overlay-ref.ts index 040efd324f95..f88b3ce97233 100644 --- a/src/cdk/overlay/overlay-ref.ts +++ b/src/cdk/overlay/overlay-ref.ts @@ -37,6 +37,13 @@ export function isElement(value: any): value is Element { return value && (value as Element).nodeType === 1; } +const attachedOverlays = new Set(); + +/** Gets all overlays that are currently attached. */ +export function getAttachedOverlays(): OverlayRef[] { + return Array.from(attachedOverlays); +} + /** * Reference to an overlay that has been created with the Overlay service. * Used to manipulate or dispose of said overlay. @@ -142,6 +149,7 @@ export class OverlayRef implements PortalOutlet { this._updateStackingOrder(); this._updateElementSize(); this._updateElementDirection(); + attachedOverlays.add(this); if (this._scrollStrategy) { this._scrollStrategy.enable(); @@ -248,6 +256,7 @@ export class OverlayRef implements PortalOutlet { this._detachContentWhenEmpty(); this._locationChanges.unsubscribe(); this._outsideClickDispatcher.remove(this); + attachedOverlays.delete(this); return detachmentResult; } @@ -284,6 +293,7 @@ export class OverlayRef implements PortalOutlet { this._detachments.complete(); this._completeDetachContent(); this._disposed = true; + attachedOverlays.delete(this); } /** Whether the overlay has attached content. */ diff --git a/src/cdk/overlay/overlay.spec.ts b/src/cdk/overlay/overlay.spec.ts index a6eb23b80a8a..43b1a1936909 100644 --- a/src/cdk/overlay/overlay.spec.ts +++ b/src/cdk/overlay/overlay.spec.ts @@ -29,6 +29,7 @@ import { PositionStrategy, ScrollStrategy, createOverlayRef, + getAttachedOverlays, } from './index'; describe('Overlay', () => { @@ -478,6 +479,28 @@ describe('Overlay', () => { expect(document.querySelector('.cdk-overlay-pane')).toBeFalsy(); }); + it('should track when an overlay is attached and detached', () => { + const overlayRef = createOverlayRef(injector); + expect(getAttachedOverlays()).toEqual([]); + + overlayRef.attach(componentPortal); + expect(getAttachedOverlays()).toEqual([overlayRef]); + + overlayRef.detach(); + expect(getAttachedOverlays()).toEqual([]); + }); + + it('should track when an overlay is attached and disposed', () => { + const overlayRef = createOverlayRef(injector); + expect(getAttachedOverlays()).toEqual([]); + + overlayRef.attach(componentPortal); + expect(getAttachedOverlays()).toEqual([overlayRef]); + + overlayRef.dispose(); + expect(getAttachedOverlays()).toEqual([]); + }); + describe('positioning', () => { let config: OverlayConfig; diff --git a/src/cdk/overlay/public-api.ts b/src/cdk/overlay/public-api.ts index 95819b1ce4ea..edc718feff06 100644 --- a/src/cdk/overlay/public-api.ts +++ b/src/cdk/overlay/public-api.ts @@ -20,7 +20,7 @@ export { CDK_CONNECTED_OVERLAY_DEFAULT_CONFIG, } from './overlay-directives'; export {FullscreenOverlayContainer} from './fullscreen-overlay-container'; -export {OverlayRef, OverlaySizeConfig} from './overlay-ref'; +export {OverlayRef, OverlaySizeConfig, getAttachedOverlays} from './overlay-ref'; export {ViewportRuler} from '../scrolling'; export {ComponentType} from '../portal'; export {OverlayPositionBuilder} from './position/overlay-position-builder';