From 582671e60503a26a5e26acb24cbd5db93de41db8 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 3 Sep 2026 09:20:23 +0200 Subject: [PATCH] fix(cdk/overlay): expose currently open overlays Adds the `getAttachedOverlays` function that allows users to check which overlays are open at the moment. --- goldens/cdk/overlay/index.api.md | 3 +++ src/cdk/overlay/overlay-ref.ts | 10 ++++++++++ src/cdk/overlay/overlay.spec.ts | 23 +++++++++++++++++++++++ src/cdk/overlay/public-api.ts | 2 +- 4 files changed, 37 insertions(+), 1 deletion(-) 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';