From a77b3aa1448537d9b74cdce0a26fd711849c253b Mon Sep 17 00:00:00 2001 From: RivaIvanova Date: Fri, 30 Jan 2026 17:16:33 +0200 Subject: [PATCH 1/9] fix(overlay): conditionally cache element size --- .../core/src/services/overlay/overlay.spec.ts | 16 +++++++++++++--- .../core/src/services/overlay/overlay.ts | 14 +++++++++++--- .../core/src/services/overlay/utilities.ts | 5 +++++ .../notification/notifications.directive.ts | 3 ++- .../tooltip/tooltip-target.directive.ts | 1 + 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts b/projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts index 2dd45ea03e2..de05d6e0ce7 100644 --- a/projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts +++ b/projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts @@ -569,7 +569,8 @@ describe('igxOverlay', () => { scrollStrategy: new NoOpScrollStrategy(), modal: true, closeOnOutsideClick: true, - closeOnEscape: false + closeOnEscape: false, + cacheSize: true }; spyOn(overlayInstance.contentAppending, 'emit'); @@ -3506,7 +3507,7 @@ describe('igxOverlay', () => { })); // 4. Css - it('Should use component initial container\'s properties when is with 100% width and show in overlay element', + it('Should use component initial container\'s properties based on cacheSize when it\'s with 100% width and shown in overlay element', fakeAsync(() => { const fixture = TestBed.createComponent(WidthTestOverlayComponent); fixture.detectChanges(); @@ -3525,6 +3526,15 @@ describe('igxOverlay', () => { // content element has no height, so the shown element will calculate its own height by itself // expect(overlayChild.style.height).toEqual('100%'); // expect(overlayChild.getBoundingClientRect().height).toEqual(280); + + fixture.componentInstance.overlaySettings.cacheSize = false; + fixture.componentInstance.buttonElement.nativeElement.click(); + tick(); + const componentElement2 = fixture.componentInstance.customComponent.nativeElement; + expect(componentElement2.style.width).toEqual('100%'); + expect(componentElement2.getBoundingClientRect().width).toEqual(123); + // Check overlay content element width + expect(componentElement2.parentElement.getBoundingClientRect().width).toEqual(123); fixture.componentInstance.overlay.detachAll(); })); }); @@ -4679,7 +4689,7 @@ export class TwoButtonsComponent {
- Some Content +

Some Content

`, styles: [`button { diff --git a/projects/igniteui-angular/core/src/services/overlay/overlay.ts b/projects/igniteui-angular/core/src/services/overlay/overlay.ts index 687d0aa143e..07fcd87f03f 100644 --- a/projects/igniteui-angular/core/src/services/overlay/overlay.ts +++ b/projects/igniteui-angular/core/src/services/overlay/overlay.ts @@ -130,7 +130,8 @@ export class IgxOverlayService implements OnDestroy { scrollStrategy: new NoOpScrollStrategy(), modal: true, closeOnOutsideClick: true, - closeOnEscape: false + closeOnEscape: false, + cacheSize: true }; constructor() { @@ -331,11 +332,18 @@ export class IgxOverlayService implements OnDestroy { info.settings = eventArgs.settings; this._overlayInfos.push(info); info.hook = this.placeElementHook(info.elementRef.nativeElement); - const elementRect = info.elementRef.nativeElement.getBoundingClientRect(); - info.initialSize = { width: elementRect.width, height: elementRect.height }; + let elementRect; + // Get the element rect size before moving it into the overlay to cache its size. + if (info.settings.cacheSize) { + elementRect = info.elementRef.nativeElement.getBoundingClientRect(); + } // Get the size before moving the container into the overlay so that it does not forget about inherited styles. this.getComponentSize(info); this.moveElementToOverlay(info); + if (!info.settings.cacheSize) { + elementRect = info.elementRef.nativeElement.getBoundingClientRect(); + } + info.initialSize = { width: elementRect.width, height: elementRect.height }; // Update the container size after moving if there is size. if (info.size) { info.elementRef.nativeElement.parentElement.style.setProperty('--ig-size', info.size); diff --git a/projects/igniteui-angular/core/src/services/overlay/utilities.ts b/projects/igniteui-angular/core/src/services/overlay/utilities.ts index 13a954ce26d..5986231200e 100644 --- a/projects/igniteui-angular/core/src/services/overlay/utilities.ts +++ b/projects/igniteui-angular/core/src/services/overlay/utilities.ts @@ -134,6 +134,11 @@ export interface OverlaySettings { * Clicking on the elements in this collection will not close the overlay when closeOnOutsideClick = true. */ excludeFromOutsideClick?: HTMLElement[]; + /** + * @hidden @internal + * Set if the element should retain its size when moved to the overlay. + */ + cacheSize?: boolean; } export interface OverlayEventArgs extends IBaseEventArgs { diff --git a/projects/igniteui-angular/directives/src/directives/notification/notifications.directive.ts b/projects/igniteui-angular/directives/src/directives/notification/notifications.directive.ts index 0df270be7f5..4d1116ded16 100644 --- a/projects/igniteui-angular/directives/src/directives/notification/notifications.directive.ts +++ b/projects/igniteui-angular/directives/src/directives/notification/notifications.directive.ts @@ -86,7 +86,8 @@ export abstract class IgxNotificationsDirective extends IgxToggleDirective closeOnEscape: false, closeOnOutsideClick: false, modal: false, - outlet: this.outlet + outlet: this.outlet, + cacheSize: false }; super.open(overlaySettings); diff --git a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts index a9c1f2ef56e..67f7375677b 100644 --- a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts +++ b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts @@ -425,6 +425,7 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen this._overlayDefaults.positionStrategy = new TooltipPositionStrategy(this._positionSettings); this._overlayDefaults.closeOnOutsideClick = false; this._overlayDefaults.closeOnEscape = true; + this._overlayDefaults.cacheSize = false; this.target.closing.pipe(takeUntil(this._destroy$)).subscribe((event) => { if (this.target.tooltipTarget !== this) { From ad17735d8817a5250d0ed9197818e5d95d8fa9d2 Mon Sep 17 00:00:00 2001 From: RivaIvanova Date: Fri, 30 Jan 2026 18:05:49 +0200 Subject: [PATCH 2/9] chore(*): update api docs; add typing --- projects/igniteui-angular/core/src/services/overlay/overlay.ts | 2 +- .../igniteui-angular/core/src/services/overlay/utilities.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/core/src/services/overlay/overlay.ts b/projects/igniteui-angular/core/src/services/overlay/overlay.ts index 07fcd87f03f..ad90d0381de 100644 --- a/projects/igniteui-angular/core/src/services/overlay/overlay.ts +++ b/projects/igniteui-angular/core/src/services/overlay/overlay.ts @@ -332,7 +332,7 @@ export class IgxOverlayService implements OnDestroy { info.settings = eventArgs.settings; this._overlayInfos.push(info); info.hook = this.placeElementHook(info.elementRef.nativeElement); - let elementRect; + let elementRect: DOMRect; // Get the element rect size before moving it into the overlay to cache its size. if (info.settings.cacheSize) { elementRect = info.elementRef.nativeElement.getBoundingClientRect(); diff --git a/projects/igniteui-angular/core/src/services/overlay/utilities.ts b/projects/igniteui-angular/core/src/services/overlay/utilities.ts index 5986231200e..8ed0412a20f 100644 --- a/projects/igniteui-angular/core/src/services/overlay/utilities.ts +++ b/projects/igniteui-angular/core/src/services/overlay/utilities.ts @@ -136,7 +136,8 @@ export interface OverlaySettings { excludeFromOutsideClick?: HTMLElement[]; /** * @hidden @internal - * Set if the element should retain its size when moved to the overlay. + * Controls whether element size is measured before (true) or after (false) moving to the overlay container. + * Default is true to retain element size. */ cacheSize?: boolean; } From 6e78973f253902781d3c0a0d63a0650c00a72ed3 Mon Sep 17 00:00:00 2001 From: RivaIvanova Date: Fri, 6 Feb 2026 16:25:37 +0200 Subject: [PATCH 3/9] fix(tooltip/snackbar): use overlay service size helpers --- .../core/src/services/overlay/overlay.spec.ts | 15 +------ .../core/src/services/overlay/overlay.ts | 40 ++++++++++++------- .../core/src/services/overlay/utilities.ts | 6 --- .../notification/notifications.directive.ts | 3 +- .../tooltip/tooltip-target.directive.ts | 3 +- .../tooltip/tooltip.directive.spec.ts | 30 +++++++++++++- .../directives/tooltip/tooltip.directive.ts | 25 +++++++++++- .../src/snackbar/snackbar.component.spec.ts | 38 +++++++++++++++++- .../src/snackbar/snackbar.component.ts | 29 +++++++++++++- .../test-utils/tooltip-components.spec.ts | 32 ++++++++++++++- 10 files changed, 176 insertions(+), 45 deletions(-) diff --git a/projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts b/projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts index de05d6e0ce7..8afa51faef9 100644 --- a/projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts +++ b/projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts @@ -569,8 +569,7 @@ describe('igxOverlay', () => { scrollStrategy: new NoOpScrollStrategy(), modal: true, closeOnOutsideClick: true, - closeOnEscape: false, - cacheSize: true + closeOnEscape: false }; spyOn(overlayInstance.contentAppending, 'emit'); @@ -3507,7 +3506,7 @@ describe('igxOverlay', () => { })); // 4. Css - it('Should use component initial container\'s properties based on cacheSize when it\'s with 100% width and shown in overlay element', + it('Should use component initial container\'s properties when is with 100% width and show in overlay element', fakeAsync(() => { const fixture = TestBed.createComponent(WidthTestOverlayComponent); fixture.detectChanges(); @@ -3526,16 +3525,6 @@ describe('igxOverlay', () => { // content element has no height, so the shown element will calculate its own height by itself // expect(overlayChild.style.height).toEqual('100%'); // expect(overlayChild.getBoundingClientRect().height).toEqual(280); - - fixture.componentInstance.overlaySettings.cacheSize = false; - fixture.componentInstance.buttonElement.nativeElement.click(); - tick(); - const componentElement2 = fixture.componentInstance.customComponent.nativeElement; - expect(componentElement2.style.width).toEqual('100%'); - expect(componentElement2.getBoundingClientRect().width).toEqual(123); - // Check overlay content element width - expect(componentElement2.parentElement.getBoundingClientRect().width).toEqual(123); - fixture.componentInstance.overlay.detachAll(); })); }); diff --git a/projects/igniteui-angular/core/src/services/overlay/overlay.ts b/projects/igniteui-angular/core/src/services/overlay/overlay.ts index ad90d0381de..530c9ce971e 100644 --- a/projects/igniteui-angular/core/src/services/overlay/overlay.ts +++ b/projects/igniteui-angular/core/src/services/overlay/overlay.ts @@ -40,11 +40,12 @@ import { @Injectable({ providedIn: 'root' }) export class IgxOverlayService implements OnDestroy { private _appRef = inject(ApplicationRef); - private document = inject(DOCUMENT); private _zone = inject(NgZone); - protected platformUtil = inject(PlatformUtil); private animationService = inject(IgxAngularAnimationService); + protected document = inject(DOCUMENT); + protected platformUtil = inject(PlatformUtil); + /** * Emitted just before the overlay content starts to open. * ```typescript @@ -130,8 +131,7 @@ export class IgxOverlayService implements OnDestroy { scrollStrategy: new NoOpScrollStrategy(), modal: true, closeOnOutsideClick: true, - closeOnEscape: false, - cacheSize: true + closeOnEscape: false }; constructor() { @@ -332,18 +332,12 @@ export class IgxOverlayService implements OnDestroy { info.settings = eventArgs.settings; this._overlayInfos.push(info); info.hook = this.placeElementHook(info.elementRef.nativeElement); - let elementRect: DOMRect; - // Get the element rect size before moving it into the overlay to cache its size. - if (info.settings.cacheSize) { - elementRect = info.elementRef.nativeElement.getBoundingClientRect(); - } // Get the size before moving the container into the overlay so that it does not forget about inherited styles. this.getComponentSize(info); - this.moveElementToOverlay(info); - if (!info.settings.cacheSize) { - elementRect = info.elementRef.nativeElement.getBoundingClientRect(); - } - info.initialSize = { width: elementRect.width, height: elementRect.height }; + this.setInitialSize( + info, + () => this.moveElementToOverlay(info) + ); // Update the container size after moving if there is size. if (info.size) { info.elementRef.nativeElement.parentElement.style.setProperty('--ig-size', info.size); @@ -564,6 +558,24 @@ export class IgxOverlayService implements OnDestroy { return info; } + /** + * Measures the element's initial size and controls *when* the element is moved into the overlay outlet. + * + * The elements inherit constraining parent styles, so + * for some of them (e.g., Tooltip, Snackbar) their pre-move size is incorrect. + * Those can **override** this method to measure **after** moving to get an accurate size. + * + * - **Default**: Measures in-place (current parent), then moves to the overlay. + * + * @param info OverlayInfo for the content being attached. + * @param moveToOverlay Moves the element into the overlay. + */ + protected setInitialSize(info: OverlayInfo, moveToOverlay: () => void): void { + const elementRect = info.elementRef.nativeElement.getBoundingClientRect(); + info.initialSize = { width: elementRect.width, height: elementRect.height }; + moveToOverlay(); + } + private _hide(id: string, event?: Event) { const info: OverlayInfo = this.getOverlayById(id); if (!info) { diff --git a/projects/igniteui-angular/core/src/services/overlay/utilities.ts b/projects/igniteui-angular/core/src/services/overlay/utilities.ts index 8ed0412a20f..13a954ce26d 100644 --- a/projects/igniteui-angular/core/src/services/overlay/utilities.ts +++ b/projects/igniteui-angular/core/src/services/overlay/utilities.ts @@ -134,12 +134,6 @@ export interface OverlaySettings { * Clicking on the elements in this collection will not close the overlay when closeOnOutsideClick = true. */ excludeFromOutsideClick?: HTMLElement[]; - /** - * @hidden @internal - * Controls whether element size is measured before (true) or after (false) moving to the overlay container. - * Default is true to retain element size. - */ - cacheSize?: boolean; } export interface OverlayEventArgs extends IBaseEventArgs { diff --git a/projects/igniteui-angular/directives/src/directives/notification/notifications.directive.ts b/projects/igniteui-angular/directives/src/directives/notification/notifications.directive.ts index 4d1116ded16..0df270be7f5 100644 --- a/projects/igniteui-angular/directives/src/directives/notification/notifications.directive.ts +++ b/projects/igniteui-angular/directives/src/directives/notification/notifications.directive.ts @@ -86,8 +86,7 @@ export abstract class IgxNotificationsDirective extends IgxToggleDirective closeOnEscape: false, closeOnOutsideClick: false, modal: false, - outlet: this.outlet, - cacheSize: false + outlet: this.outlet }; super.open(overlaySettings); diff --git a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts index 67f7375677b..1446fe4bcdf 100644 --- a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts +++ b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts @@ -1,5 +1,5 @@ import { - Directive, OnInit, OnDestroy, Output, ElementRef, ViewContainerRef, + Directive, OnInit, OnDestroy, Output, ViewContainerRef, Input, EventEmitter, booleanAttribute, TemplateRef, ComponentRef, Renderer2, EnvironmentInjector, createComponent, @@ -425,7 +425,6 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen this._overlayDefaults.positionStrategy = new TooltipPositionStrategy(this._positionSettings); this._overlayDefaults.closeOnOutsideClick = false; this._overlayDefaults.closeOnEscape = true; - this._overlayDefaults.cacheSize = false; this.target.closing.pipe(takeUntil(this._destroy$)).subscribe((event) => { if (this.target.tooltipTarget !== this) { diff --git a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts index 26ea0aee198..7ed0ba0d453 100644 --- a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts +++ b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts @@ -2,7 +2,7 @@ import { DebugElement } from '@angular/core'; import { fakeAsync, TestBed, tick, flush, waitForAsync, ComponentFixture } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { IgxTooltipSingleTargetComponent, IgxTooltipMultipleTargetsComponent, IgxTooltipPlainStringComponent, IgxTooltipWithToggleActionComponent, IgxTooltipWithCloseButtonComponent, IgxTooltipWithNestedContentComponent, IgxTooltipNestedTooltipsComponent } from '../../../../test-utils/tooltip-components.spec'; +import { IgxTooltipSingleTargetComponent, IgxTooltipMultipleTargetsComponent, IgxTooltipPlainStringComponent, IgxTooltipWithToggleActionComponent, IgxTooltipWithCloseButtonComponent, IgxTooltipWithNestedContentComponent, IgxTooltipNestedTooltipsComponent, IgxTooltipSizeComponent } from '../../../../test-utils/tooltip-components.spec'; import { UIInteractions } from '../../../../test-utils/ui-interactions.spec'; import { HorizontalAlignment, VerticalAlignment, AutoPositionStrategy } from '../../../../core/src/services/public_api'; import { IgxTooltipDirective } from './tooltip.directive'; @@ -32,7 +32,8 @@ describe('IgxTooltip', () => { IgxTooltipWithToggleActionComponent, IgxTooltipWithCloseButtonComponent, IgxTooltipWithNestedContentComponent, - IgxTooltipNestedTooltipsComponent + IgxTooltipNestedTooltipsComponent, + IgxTooltipSizeComponent ] }).compileComponents(); UIInteractions.clearOverlay(); @@ -980,6 +981,31 @@ describe('IgxTooltip', () => { expect(fix.componentInstance.toggleDir.collapsed).toBe(false); })); + + it('correctly sizes the tooltip/overlay content when inside an element - issue #16458', fakeAsync(() => { + const fixture = TestBed.createComponent(IgxTooltipSizeComponent); + fixture.detectChanges(); + + fixture.componentInstance.target1.showTooltip(); + fixture.componentInstance.target2.showTooltip(); + fixture.componentInstance.target3.showTooltip(); + flush(); + + const tooltip1Rect = fixture.componentInstance.tooltip1.element.getBoundingClientRect(); + const tooltip2Rect = fixture.componentInstance.tooltip2.element.getBoundingClientRect(); + const tooltip3Rect = fixture.componentInstance.tooltip3.element.getBoundingClientRect(); + + const tooltip1ParentRect = fixture.componentInstance.tooltip1.element.parentElement.getBoundingClientRect(); + const tooltip2ParentRect = fixture.componentInstance.tooltip2.element.parentElement.getBoundingClientRect(); + const tooltip3ParentRect = fixture.componentInstance.tooltip3.element.parentElement.getBoundingClientRect(); + + expect(tooltip1Rect.width).toEqual(tooltip1ParentRect.width); + expect(tooltip1Rect.height).toEqual(tooltip1ParentRect.height); + expect(tooltip2Rect.width).toEqual(tooltip2ParentRect.width); + expect(tooltip2Rect.height).toEqual(tooltip2ParentRect.height); + expect(tooltip3Rect.width).toEqual(tooltip3ParentRect.width); + expect(tooltip3Rect.height).toEqual(tooltip3ParentRect.height); + })); }); describe('Tooltip Sticky with Close Button', () => { diff --git a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts index a2238396300..84ed1a4c594 100644 --- a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts +++ b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts @@ -3,10 +3,25 @@ import { OnDestroy, inject, HostListener, Renderer2, AfterViewInit, + Injectable, } from '@angular/core'; -import { OverlaySettings, PlatformUtil } from 'igniteui-angular/core'; +import { IgxOverlayService, OverlaySettings, PlatformUtil } from 'igniteui-angular/core'; import { IgxToggleDirective } from '../toggle/toggle.directive'; import { IgxTooltipTargetDirective } from './tooltip-target.directive'; +import { OverlayInfo } from '../../../../core/src/services/overlay/utilities'; + +/** + * Measures **after** moving the element into the overlay outlet so that parent + * style constraints do not affect the initial size. + */ +@Injectable() +export class TooltipOverlayServiceHelper extends IgxOverlayService { + protected override setInitialSize(info: OverlayInfo, moveToOverlay: () => void): void { + moveToOverlay(); + const elementRect = info.elementRef.nativeElement.getBoundingClientRect(); + info.initialSize = { width: elementRect.width, height: elementRect.height }; + } +} let NEXT_ID = 0; /** @@ -26,7 +41,13 @@ let NEXT_ID = 0; @Directive({ exportAs: 'tooltip', selector: '[igxTooltip]', - standalone: true + standalone: true, + providers: [ + { + provide: IgxOverlayService, + useClass: TooltipOverlayServiceHelper + } + ] }) export class IgxTooltipDirective extends IgxToggleDirective implements AfterViewInit, OnDestroy { /** diff --git a/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.spec.ts b/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.spec.ts index d8cf22d0adb..fffee7f0ae4 100644 --- a/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.spec.ts +++ b/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.spec.ts @@ -14,7 +14,8 @@ describe('IgxSnackbar', () => { imports: [ NoopAnimationsModule, SnackbarInitializeTestComponent, - SnackbarCustomContentComponent + SnackbarCustomContentComponent, + SnackbarSizeTestComponent ] }).compileComponents(); })); @@ -183,6 +184,27 @@ describe('IgxSnackbar', () => { expect(customPositionSettings.openAnimation.options.params).toEqual({duration: '1000ms'}); expect(customPositionSettings.minSize).toEqual({height: 100, width: 100}); }); + + it('correctly sizes the snackbar/overlay content when inside an element - issue #16458', () => { + const fix = TestBed.createComponent(SnackbarSizeTestComponent); + fix.detectChanges(); + snackbar = fix.componentInstance.snackbar; + + const parentDivRect = snackbar.element.parentElement.getBoundingClientRect(); + expect(parentDivRect.width).toBe(600); + + snackbar.open(); + fix.detectChanges(); + + const snackbarRect = snackbar.element.getBoundingClientRect(); + const overlayContentRect = snackbar.element.parentElement.getBoundingClientRect(); + const { marginLeft, marginRight, paddingLeft, paddingRight } = getComputedStyle(snackbar.element); + const horizontalMargins = parseFloat(marginLeft) + parseFloat(marginRight); + const horizontalPaddings = parseFloat(paddingLeft) + parseFloat(paddingRight); + + expect(snackbarRect.width).toEqual(200 + horizontalPaddings); + expect(overlayContentRect.width).toEqual(snackbarRect.width + horizontalMargins); + }); }); describe('IgxSnackbar with custom content', () => { @@ -273,3 +295,17 @@ class SnackbarCustomContentComponent { @ViewChild(IgxSnackbarComponent, { static: true }) public snackbar: IgxSnackbarComponent; public text: string; } + +@Component({ + template: ` +
+ +
Snackbar Message
+
+
+ `, + imports: [IgxSnackbarComponent] +}) +class SnackbarSizeTestComponent { + @ViewChild(IgxSnackbarComponent, { static: true }) public snackbar: IgxSnackbarComponent; +} diff --git a/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts b/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts index cd7b50468fe..53bdf7b13a7 100644 --- a/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts +++ b/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts @@ -3,15 +3,34 @@ import { Component, EventEmitter, HostBinding, + Injectable, Input, OnInit, Output } from '@angular/core'; import { takeUntil } from 'rxjs/operators'; import { ContainerPositionStrategy, GlobalPositionStrategy, HorizontalAlignment, - PositionSettings, VerticalAlignment } from 'igniteui-angular/core'; + IgxOverlayService, PositionSettings, VerticalAlignment } from 'igniteui-angular/core'; import { ToggleViewEventArgs, IgxButtonDirective, IgxNotificationsDirective } from 'igniteui-angular/directives'; import { fadeIn, fadeOut } from 'igniteui-angular/animations'; +import { OverlayInfo } from '../../../core/src/services/overlay/utilities'; + +/** + * Measures **after** moving the element into the overlay outlet so that parent + * style constraints do not affect the initial size. + */ +@Injectable() +export class SnackbarOverlayServiceHelper extends IgxOverlayService { + protected override setInitialSize(info: OverlayInfo, moveToOverlay: () => void): void { + moveToOverlay(); + const elementRect = info.elementRef.nativeElement.getBoundingClientRect(); + // Needs full element width (margins included) to set proper width for the overlay container. + // Otherwise, the snackbar appears smaller and the text inside it might be misaligned. + const styles = this.document.defaultView.getComputedStyle(info.elementRef.nativeElement); + const horizontalMargins = parseFloat(styles.marginLeft) + parseFloat(styles.marginRight); + info.initialSize = { width: elementRect.width + horizontalMargins, height: elementRect.height }; + } +} let NEXT_ID = 0; /** @@ -34,7 +53,13 @@ let NEXT_ID = 0; @Component({ selector: 'igx-snackbar', templateUrl: 'snackbar.component.html', - imports: [IgxButtonDirective] + imports: [IgxButtonDirective], + providers: [ + { + provide: IgxOverlayService, + useClass: SnackbarOverlayServiceHelper + } + ], }) export class IgxSnackbarComponent extends IgxNotificationsDirective implements OnInit { diff --git a/projects/igniteui-angular/test-utils/tooltip-components.spec.ts b/projects/igniteui-angular/test-utils/tooltip-components.spec.ts index 01f45faf660..e4aa8d9e101 100644 --- a/projects/igniteui-angular/test-utils/tooltip-components.spec.ts +++ b/projects/igniteui-angular/test-utils/tooltip-components.spec.ts @@ -1,5 +1,6 @@ import { Component, TemplateRef, ViewChild } from '@angular/core'; -import { IgxToggleActionDirective, IgxToggleDirective, IgxTooltipDirective, IgxTooltipTargetDirective, ITooltipHideEventArgs, ITooltipShowEventArgs } from 'igniteui-angular/directives'; +import { IgxButtonDirective, IgxIconButtonDirective, IgxToggleActionDirective, IgxToggleDirective, IgxTooltipDirective, IgxTooltipTargetDirective, ITooltipHideEventArgs, ITooltipShowEventArgs } from 'igniteui-angular/directives'; +import { IgxIconComponent } from 'igniteui-angular/icon'; @Component({ @@ -207,3 +208,32 @@ export class IgxTooltipNestedTooltipsComponent { @ViewChild('tooltipLevel2', { read: IgxTooltipDirective, static: true }) public tooltipLevel2: IgxTooltipDirective; @ViewChild('tooltipLevel3', { read: IgxTooltipDirective, static: true }) public tooltipLevel3: IgxTooltipDirective; } + +@Component({ + template: ` +
+
{{ message }}
+
+ + + `, + imports: [IgxTooltipDirective, IgxTooltipTargetDirective, IgxIconComponent, IgxIconButtonDirective, IgxButtonDirective], + standalone: true +}) +export class IgxTooltipSizeComponent { + @ViewChild('target1', { read: IgxTooltipTargetDirective, static: true }) public target1: IgxTooltipTargetDirective; + @ViewChild('target2', { read: IgxTooltipTargetDirective, static: true }) public target2: IgxTooltipTargetDirective; + @ViewChild('target3', { read: IgxTooltipTargetDirective, static: true }) public target3: IgxTooltipTargetDirective; + + @ViewChild('tooltip1', { read: IgxTooltipDirective, static: true }) public tooltip1: IgxTooltipDirective; + @ViewChild('tooltip2', { read: IgxTooltipDirective, static: true }) public tooltip2: IgxTooltipDirective; + @ViewChild('tooltip3', { read: IgxTooltipDirective, static: true }) public tooltip3: IgxTooltipDirective; + + public message: string = 'Long tooltip message for testing purposes'; +} From d4b5b2e0ae64a82c9aff0c6fce18b4be39c29c7d Mon Sep 17 00:00:00 2001 From: RivaIvanova Date: Fri, 6 Feb 2026 16:52:08 +0200 Subject: [PATCH 4/9] fix(tooltip/snackbar): add proper OverlayInfo import --- projects/igniteui-angular/core/src/services/public_api.ts | 2 +- .../directives/src/directives/tooltip/tooltip.directive.ts | 3 +-- .../snackbar/src/snackbar/snackbar.component.ts | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/projects/igniteui-angular/core/src/services/public_api.ts b/projects/igniteui-angular/core/src/services/public_api.ts index 69ae76e9358..f359d54cbc5 100644 --- a/projects/igniteui-angular/core/src/services/public_api.ts +++ b/projects/igniteui-angular/core/src/services/public_api.ts @@ -9,7 +9,7 @@ export * from './overlay/scroll'; export { AbsolutePosition, ConnectedFit, HorizontalAlignment, OffsetMode, OverlayAnimationEventArgs, OverlayCancelableEventArgs, OverlayClosingEventArgs, OverlayCreateSettings, OverlayEventArgs, OverlaySettings, Point, PositionSettings, RelativePosition, RelativePositionStrategy, Size, VerticalAlignment, Util, - IgxOverlayOutletDirective + IgxOverlayOutletDirective, OverlayInfo } from './overlay/utilities'; export * from './transaction/base-transaction'; export * from './transaction/hierarchical-transaction'; diff --git a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts index 84ed1a4c594..1ad0d19d9da 100644 --- a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts +++ b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts @@ -5,10 +5,9 @@ import { AfterViewInit, Injectable, } from '@angular/core'; -import { IgxOverlayService, OverlaySettings, PlatformUtil } from 'igniteui-angular/core'; +import { IgxOverlayService, OverlayInfo, OverlaySettings, PlatformUtil } from 'igniteui-angular/core'; import { IgxToggleDirective } from '../toggle/toggle.directive'; import { IgxTooltipTargetDirective } from './tooltip-target.directive'; -import { OverlayInfo } from '../../../../core/src/services/overlay/utilities'; /** * Measures **after** moving the element into the overlay outlet so that parent diff --git a/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts b/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts index 53bdf7b13a7..54c4324c11b 100644 --- a/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts +++ b/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts @@ -10,10 +10,9 @@ import { } from '@angular/core'; import { takeUntil } from 'rxjs/operators'; import { ContainerPositionStrategy, GlobalPositionStrategy, HorizontalAlignment, - IgxOverlayService, PositionSettings, VerticalAlignment } from 'igniteui-angular/core'; + IgxOverlayService, OverlayInfo, PositionSettings, VerticalAlignment } from 'igniteui-angular/core'; import { ToggleViewEventArgs, IgxButtonDirective, IgxNotificationsDirective } from 'igniteui-angular/directives'; import { fadeIn, fadeOut } from 'igniteui-angular/animations'; -import { OverlayInfo } from '../../../core/src/services/overlay/utilities'; /** * Measures **after** moving the element into the overlay outlet so that parent From 1ad87d480e7d772819599d7901d99e87b88e71c0 Mon Sep 17 00:00:00 2001 From: RivaIvanova Date: Fri, 6 Feb 2026 16:59:00 +0200 Subject: [PATCH 5/9] test(snackbar): extract content width --- .../snackbar/src/snackbar/snackbar.component.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.spec.ts b/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.spec.ts index fffee7f0ae4..01bdb10e8ea 100644 --- a/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.spec.ts +++ b/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.spec.ts @@ -201,8 +201,9 @@ describe('IgxSnackbar', () => { const { marginLeft, marginRight, paddingLeft, paddingRight } = getComputedStyle(snackbar.element); const horizontalMargins = parseFloat(marginLeft) + parseFloat(marginRight); const horizontalPaddings = parseFloat(paddingLeft) + parseFloat(paddingRight); + const contentWidth = 200; - expect(snackbarRect.width).toEqual(200 + horizontalPaddings); + expect(snackbarRect.width).toEqual(contentWidth + horizontalPaddings); expect(overlayContentRect.width).toEqual(snackbarRect.width + horizontalMargins); }); }); From 203e016ac14f341ab1435acd3ee4d94b84303ada Mon Sep 17 00:00:00 2001 From: RivaIvanova Date: Mon, 9 Feb 2026 16:23:59 +0200 Subject: [PATCH 6/9] fix(tooltip/snackbar): use add overlay size registry --- .../core/src/services/overlay/overlay.spec.ts | 3 +- .../core/src/services/overlay/overlay.ts | 50 ++++++++------- .../core/src/services/overlay/utilities.ts | 28 ++++++++- .../core/src/services/public_api.ts | 2 +- .../directives/tooltip/tooltip.directive.ts | 46 +++++++------- .../src/snackbar/snackbar.component.ts | 61 ++++++++++--------- 6 files changed, 114 insertions(+), 76 deletions(-) diff --git a/projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts b/projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts index 8afa51faef9..2dd45ea03e2 100644 --- a/projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts +++ b/projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts @@ -3525,6 +3525,7 @@ describe('igxOverlay', () => { // content element has no height, so the shown element will calculate its own height by itself // expect(overlayChild.style.height).toEqual('100%'); // expect(overlayChild.getBoundingClientRect().height).toEqual(280); + fixture.componentInstance.overlay.detachAll(); })); }); @@ -4678,7 +4679,7 @@ export class TwoButtonsComponent {
-

Some Content

+ Some Content
`, styles: [`button { diff --git a/projects/igniteui-angular/core/src/services/overlay/overlay.ts b/projects/igniteui-angular/core/src/services/overlay/overlay.ts index 530c9ce971e..aa2326c1899 100644 --- a/projects/igniteui-angular/core/src/services/overlay/overlay.ts +++ b/projects/igniteui-angular/core/src/services/overlay/overlay.ts @@ -5,7 +5,7 @@ import { filter, takeUntil } from 'rxjs/operators'; import { fadeIn, fadeOut, IAnimationParams, scaleInHorLeft, scaleInHorRight, scaleInVerBottom, scaleInVerTop, scaleOutHorLeft, scaleOutHorRight, scaleOutVerBottom, scaleOutVerTop, slideInBottom, slideInTop, slideOutBottom, slideOutTop } from 'igniteui-angular/animations'; import { PlatformUtil } from '../../core/utils'; -import { IgxOverlayOutletDirective } from './utilities'; +import { IgxOverlayOutletDirective, OverlaySizeRegistry } from './utilities'; import { IgxAngularAnimationService } from '../animation/angular-animation-service'; import { AnimationService } from '../animation/animation'; import { AutoPositionStrategy } from './position/auto-position-strategy'; @@ -40,11 +40,11 @@ import { @Injectable({ providedIn: 'root' }) export class IgxOverlayService implements OnDestroy { private _appRef = inject(ApplicationRef); + private document = inject(DOCUMENT); private _zone = inject(NgZone); - private animationService = inject(IgxAngularAnimationService); - - protected document = inject(DOCUMENT); protected platformUtil = inject(PlatformUtil); + private animationService = inject(IgxAngularAnimationService); + private sizeRegistry = inject(OverlaySizeRegistry); /** * Emitted just before the overlay content starts to open. @@ -558,24 +558,6 @@ export class IgxOverlayService implements OnDestroy { return info; } - /** - * Measures the element's initial size and controls *when* the element is moved into the overlay outlet. - * - * The elements inherit constraining parent styles, so - * for some of them (e.g., Tooltip, Snackbar) their pre-move size is incorrect. - * Those can **override** this method to measure **after** moving to get an accurate size. - * - * - **Default**: Measures in-place (current parent), then moves to the overlay. - * - * @param info OverlayInfo for the content being attached. - * @param moveToOverlay Moves the element into the overlay. - */ - protected setInitialSize(info: OverlayInfo, moveToOverlay: () => void): void { - const elementRect = info.elementRef.nativeElement.getBoundingClientRect(); - info.initialSize = { width: elementRect.width, height: elementRect.height }; - moveToOverlay(); - } - private _hide(id: string, event?: Event) { const info: OverlayInfo = this.getOverlayById(id); if (!info) { @@ -1020,4 +1002,28 @@ export class IgxOverlayService implements OnDestroy { info.size = size; } } + + /** + * Measures the element's initial size and controls *when* the element is moved into the overlay outlet. + * + * The elements inherit constraining parent styles, so + * for some of them (e.g., Tooltip, Snackbar) their pre-move size is incorrect. + * Those can **override** this method to measure **after** moving to get an accurate size. + * + * - **Default**: Measures in-place (current parent), then moves to the overlay. + * + * @param info OverlayInfo for the content being attached. + * @param moveToOverlay Moves the element into the overlay. + */ + private setInitialSize(info: OverlayInfo, moveToOverlay: () => void): void { + const override = this.sizeRegistry.get(info); + if (override) { + override(info, moveToOverlay); + return; + } + + const elementRect = info.elementRef.nativeElement.getBoundingClientRect(); + info.initialSize = { width: elementRect.width, height: elementRect.height }; + moveToOverlay(); + } } diff --git a/projects/igniteui-angular/core/src/services/overlay/utilities.ts b/projects/igniteui-angular/core/src/services/overlay/utilities.ts index 13a954ce26d..efc780b7f38 100644 --- a/projects/igniteui-angular/core/src/services/overlay/utilities.ts +++ b/projects/igniteui-angular/core/src/services/overlay/utilities.ts @@ -1,10 +1,36 @@ import { AnimationReferenceMetadata } from '@angular/animations'; -import { ComponentRef, Directive, ElementRef, inject, Injector, NgZone } from '@angular/core'; +import { ComponentRef, Directive, ElementRef, inject, Injectable, Injector, NgZone } from '@angular/core'; import { CancelableBrowserEventArgs, CancelableEventArgs, cloneValue, IBaseEventArgs } from '../../core/utils'; import { AnimationPlayer } from '../animation/animation'; import { IPositionStrategy } from './position/IPositionStrategy'; import { IScrollStrategy } from './scroll'; +/** @hidden @internal */ +export type SetInitialSizeFn = (info: OverlayInfo, moveToOverlay: () => void) => void; + +/** @hidden @internal */ +@Injectable({ providedIn: 'root' }) +export class OverlaySizeRegistry { + private readonly map = new Map(); + + public register(host: HTMLElement, fn: SetInitialSizeFn): void { + this.map.set(host, fn); + } + + public clear(host: HTMLElement): void { + this.map.delete(host); + } + + public get(info: OverlayInfo): SetInitialSizeFn | undefined { + if (!info.elementRef || !info.elementRef.nativeElement) { + return; + } + + return this.map.get(info.elementRef.nativeElement); + } +} + + /** * Mark an element as an igxOverlay outlet container. * Directive instance is exported as `overlay-outlet` to be assigned to templates variables: diff --git a/projects/igniteui-angular/core/src/services/public_api.ts b/projects/igniteui-angular/core/src/services/public_api.ts index f359d54cbc5..498bda162d6 100644 --- a/projects/igniteui-angular/core/src/services/public_api.ts +++ b/projects/igniteui-angular/core/src/services/public_api.ts @@ -9,7 +9,7 @@ export * from './overlay/scroll'; export { AbsolutePosition, ConnectedFit, HorizontalAlignment, OffsetMode, OverlayAnimationEventArgs, OverlayCancelableEventArgs, OverlayClosingEventArgs, OverlayCreateSettings, OverlayEventArgs, OverlaySettings, Point, PositionSettings, RelativePosition, RelativePositionStrategy, Size, VerticalAlignment, Util, - IgxOverlayOutletDirective, OverlayInfo + IgxOverlayOutletDirective, OverlayInfo, OverlaySizeRegistry } from './overlay/utilities'; export * from './transaction/base-transaction'; export * from './transaction/hierarchical-transaction'; diff --git a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts index 1ad0d19d9da..fde245d00ca 100644 --- a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts +++ b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts @@ -3,25 +3,12 @@ import { OnDestroy, inject, HostListener, Renderer2, AfterViewInit, - Injectable, + OnInit, } from '@angular/core'; -import { IgxOverlayService, OverlayInfo, OverlaySettings, PlatformUtil } from 'igniteui-angular/core'; +import { OverlayInfo, OverlaySettings, OverlaySizeRegistry, PlatformUtil } from 'igniteui-angular/core'; import { IgxToggleDirective } from '../toggle/toggle.directive'; import { IgxTooltipTargetDirective } from './tooltip-target.directive'; -/** - * Measures **after** moving the element into the overlay outlet so that parent - * style constraints do not affect the initial size. - */ -@Injectable() -export class TooltipOverlayServiceHelper extends IgxOverlayService { - protected override setInitialSize(info: OverlayInfo, moveToOverlay: () => void): void { - moveToOverlay(); - const elementRect = info.elementRef.nativeElement.getBoundingClientRect(); - info.initialSize = { width: elementRect.width, height: elementRect.height }; - } -} - let NEXT_ID = 0; /** * **Ignite UI for Angular Tooltip** - @@ -40,15 +27,9 @@ let NEXT_ID = 0; @Directive({ exportAs: 'tooltip', selector: '[igxTooltip]', - standalone: true, - providers: [ - { - provide: IgxOverlayService, - useClass: TooltipOverlayServiceHelper - } - ] + standalone: true }) -export class IgxTooltipDirective extends IgxToggleDirective implements AfterViewInit, OnDestroy { +export class IgxTooltipDirective extends IgxToggleDirective implements OnInit, AfterViewInit, OnDestroy { /** * @hidden */ @@ -136,6 +117,7 @@ export class IgxTooltipDirective extends IgxToggleDirective implements AfterView private _role: 'tooltip' | 'status' = 'tooltip'; private _renderer = inject(Renderer2); private _platformUtil = inject(PlatformUtil); + private _sizeRegistry = inject(OverlaySizeRegistry); /** @hidden */ public ngAfterViewInit(): void { @@ -144,6 +126,12 @@ export class IgxTooltipDirective extends IgxToggleDirective implements AfterView } } + /** @hidden */ + public override ngOnInit() { + super.ngOnInit(); + this._sizeRegistry.register(this.element, this.setInitialSize); + } + /** @hidden */ public override ngOnDestroy() { super.ngOnDestroy(); @@ -151,6 +139,8 @@ export class IgxTooltipDirective extends IgxToggleDirective implements AfterView if (this.arrow) { this._removeArrow(); } + + this._sizeRegistry.clear(this.element); } /** @@ -226,4 +216,14 @@ export class IgxTooltipDirective extends IgxToggleDirective implements AfterView this._arrowEl.remove(); this._arrowEl = null; } + + /** + * Measures **after** moving the element into the overlay outlet so that parent + * style constraints do not affect the initial size. + */ + private setInitialSize = (info: OverlayInfo, moveToOverlay: () => void) => { + moveToOverlay(); + const elementRect = info.elementRef.nativeElement.getBoundingClientRect(); + info.initialSize = { width: elementRect.width, height: elementRect.height }; + }; } diff --git a/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts b/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts index 54c4324c11b..5166b41882c 100644 --- a/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts +++ b/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts @@ -1,36 +1,21 @@ import { useAnimation } from '@angular/animations'; import { Component, + DOCUMENT, EventEmitter, HostBinding, - Injectable, + inject, Input, + OnDestroy, OnInit, Output } from '@angular/core'; import { takeUntil } from 'rxjs/operators'; import { ContainerPositionStrategy, GlobalPositionStrategy, HorizontalAlignment, - IgxOverlayService, OverlayInfo, PositionSettings, VerticalAlignment } from 'igniteui-angular/core'; + OverlayInfo, OverlaySizeRegistry, PositionSettings, VerticalAlignment } from 'igniteui-angular/core'; import { ToggleViewEventArgs, IgxButtonDirective, IgxNotificationsDirective } from 'igniteui-angular/directives'; import { fadeIn, fadeOut } from 'igniteui-angular/animations'; -/** - * Measures **after** moving the element into the overlay outlet so that parent - * style constraints do not affect the initial size. - */ -@Injectable() -export class SnackbarOverlayServiceHelper extends IgxOverlayService { - protected override setInitialSize(info: OverlayInfo, moveToOverlay: () => void): void { - moveToOverlay(); - const elementRect = info.elementRef.nativeElement.getBoundingClientRect(); - // Needs full element width (margins included) to set proper width for the overlay container. - // Otherwise, the snackbar appears smaller and the text inside it might be misaligned. - const styles = this.document.defaultView.getComputedStyle(info.elementRef.nativeElement); - const horizontalMargins = parseFloat(styles.marginLeft) + parseFloat(styles.marginRight); - info.initialSize = { width: elementRect.width + horizontalMargins, height: elementRect.height }; - } -} - let NEXT_ID = 0; /** * **Ignite UI for Angular Snackbar** - @@ -52,16 +37,12 @@ let NEXT_ID = 0; @Component({ selector: 'igx-snackbar', templateUrl: 'snackbar.component.html', - imports: [IgxButtonDirective], - providers: [ - { - provide: IgxOverlayService, - useClass: SnackbarOverlayServiceHelper - } - ], + imports: [IgxButtonDirective] }) -export class IgxSnackbarComponent extends IgxNotificationsDirective - implements OnInit { +export class IgxSnackbarComponent extends IgxNotificationsDirective implements OnInit, OnDestroy { + private _document = inject(DOCUMENT); + private _sizeRegistry = inject(OverlaySizeRegistry); + /** * Sets/gets the `id` of the snackbar. * If not set, the `id` of the first snackbar component will be `"igx-snackbar-0"`; @@ -220,5 +201,29 @@ export class IgxSnackbarComponent extends IgxNotificationsDirective const closedEventArgs: ToggleViewEventArgs = { owner: this, id: this._overlayId }; this.animationDone.emit(closedEventArgs); }); + + this._sizeRegistry.register(this.element, this.setInitialSize); } + + /** + * @hidden + */ + public override ngOnDestroy() { + super.ngOnDestroy(); + this._sizeRegistry.clear(this.element); + } + + /** + * Measures **after** moving the element into the overlay outlet so that parent + * style constraints do not affect the initial size. + */ + private setInitialSize = (info: OverlayInfo, moveToOverlay: () => void) => { + moveToOverlay(); + const elementRect = info.elementRef.nativeElement.getBoundingClientRect(); + // Needs full element width (margins included) to set proper width for the overlay container. + // Otherwise, the snackbar appears smaller and the text inside it might be misaligned. + const styles = this._document.defaultView.getComputedStyle(info.elementRef.nativeElement); + const horizontalMargins = parseFloat(styles.marginLeft) + parseFloat(styles.marginRight); + info.initialSize = { width: elementRect.width + horizontalMargins, height: elementRect.height }; + }; } From 62cce1f1942863dd04bd7c57175b6142233b7342 Mon Sep 17 00:00:00 2001 From: RivaIvanova Date: Mon, 9 Feb 2026 16:44:07 +0200 Subject: [PATCH 7/9] test(tooltip): remove standalone prop --- .../test-utils/tooltip-components.spec.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/projects/igniteui-angular/test-utils/tooltip-components.spec.ts b/projects/igniteui-angular/test-utils/tooltip-components.spec.ts index e4aa8d9e101..d44881471ff 100644 --- a/projects/igniteui-angular/test-utils/tooltip-components.spec.ts +++ b/projects/igniteui-angular/test-utils/tooltip-components.spec.ts @@ -162,8 +162,7 @@ export class IgxTooltipWithCloseButtonComponent { `, - imports: [IgxTooltipDirective, IgxTooltipTargetDirective], - standalone: true + imports: [IgxTooltipDirective, IgxTooltipTargetDirective] }) export class IgxTooltipWithNestedContentComponent { @ViewChild(IgxTooltipDirective, { static: true }) public tooltip!: IgxTooltipDirective; @@ -196,8 +195,7 @@ export class IgxTooltipWithNestedContentComponent { `, - imports: [IgxTooltipDirective, IgxTooltipTargetDirective], - standalone: true + imports: [IgxTooltipDirective, IgxTooltipTargetDirective] }) export class IgxTooltipNestedTooltipsComponent { @ViewChild('targetLevel1', { read: IgxTooltipTargetDirective, static: true }) public targetLevel1: IgxTooltipTargetDirective; @@ -223,8 +221,7 @@ export class IgxTooltipNestedTooltipsComponent {
{{ message }}
`, - imports: [IgxTooltipDirective, IgxTooltipTargetDirective, IgxIconComponent, IgxIconButtonDirective, IgxButtonDirective], - standalone: true + imports: [IgxTooltipDirective, IgxTooltipTargetDirective, IgxIconComponent, IgxIconButtonDirective, IgxButtonDirective] }) export class IgxTooltipSizeComponent { @ViewChild('target1', { read: IgxTooltipTargetDirective, static: true }) public target1: IgxTooltipTargetDirective; From 5207d759ed53b81ece324eeeec268f5a4be7cb28 Mon Sep 17 00:00:00 2001 From: RivaIvanova Date: Tue, 10 Feb 2026 11:37:08 +0200 Subject: [PATCH 8/9] chore(*): refactor api docs --- .../core/src/services/overlay/overlay.ts | 2 +- .../core/src/services/overlay/utilities.ts | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/projects/igniteui-angular/core/src/services/overlay/overlay.ts b/projects/igniteui-angular/core/src/services/overlay/overlay.ts index aa2326c1899..ee7bc91c916 100644 --- a/projects/igniteui-angular/core/src/services/overlay/overlay.ts +++ b/projects/igniteui-angular/core/src/services/overlay/overlay.ts @@ -1008,7 +1008,7 @@ export class IgxOverlayService implements OnDestroy { * * The elements inherit constraining parent styles, so * for some of them (e.g., Tooltip, Snackbar) their pre-move size is incorrect. - * Those can **override** this method to measure **after** moving to get an accurate size. + * Those can register an override via `OverlaySizeRegistry` to measure **after** moving to get an accurate size. * * - **Default**: Measures in-place (current parent), then moves to the overlay. * diff --git a/projects/igniteui-angular/core/src/services/overlay/utilities.ts b/projects/igniteui-angular/core/src/services/overlay/utilities.ts index efc780b7f38..38b45d8631b 100644 --- a/projects/igniteui-angular/core/src/services/overlay/utilities.ts +++ b/projects/igniteui-angular/core/src/services/overlay/utilities.ts @@ -5,10 +5,15 @@ import { AnimationPlayer } from '../animation/animation'; import { IPositionStrategy } from './position/IPositionStrategy'; import { IScrollStrategy } from './scroll'; -/** @hidden @internal */ -export type SetInitialSizeFn = (info: OverlayInfo, moveToOverlay: () => void) => void; +type SetInitialSizeFn = (info: OverlayInfo, moveToOverlay: () => void) => void; + +/** + * Maps a host `HTMLElement` to a sizing strategy (`SetInitialSizeFn`). + * + * @hidden + * @internal + */ -/** @hidden @internal */ @Injectable({ providedIn: 'root' }) export class OverlaySizeRegistry { private readonly map = new Map(); @@ -30,7 +35,6 @@ export class OverlaySizeRegistry { } } - /** * Mark an element as an igxOverlay outlet container. * Directive instance is exported as `overlay-outlet` to be assigned to templates variables: From bd2145099a1a6e42b339c696f047ba56c0469146 Mon Sep 17 00:00:00 2001 From: RivaIvanova Date: Tue, 10 Feb 2026 16:21:52 +0200 Subject: [PATCH 9/9] chore(tooltip/snackbar): minor refactor changes --- .../src/directives/tooltip/tooltip.directive.ts | 14 +++++++------- .../snackbar/src/snackbar/snackbar.component.ts | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts index fde245d00ca..b11e56f462a 100644 --- a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts +++ b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.ts @@ -119,6 +119,12 @@ export class IgxTooltipDirective extends IgxToggleDirective implements OnInit, A private _platformUtil = inject(PlatformUtil); private _sizeRegistry = inject(OverlaySizeRegistry); + /** @hidden */ + public override ngOnInit() { + super.ngOnInit(); + this._sizeRegistry.register(this.element, this.setInitialSize); + } + /** @hidden */ public ngAfterViewInit(): void { if (this._platformUtil.isBrowser) { @@ -126,12 +132,6 @@ export class IgxTooltipDirective extends IgxToggleDirective implements OnInit, A } } - /** @hidden */ - public override ngOnInit() { - super.ngOnInit(); - this._sizeRegistry.register(this.element, this.setInitialSize); - } - /** @hidden */ public override ngOnDestroy() { super.ngOnDestroy(); @@ -225,5 +225,5 @@ export class IgxTooltipDirective extends IgxToggleDirective implements OnInit, A moveToOverlay(); const elementRect = info.elementRef.nativeElement.getBoundingClientRect(); info.initialSize = { width: elementRect.width, height: elementRect.height }; - }; + } } diff --git a/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts b/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts index 5166b41882c..4e74362cc1c 100644 --- a/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts +++ b/projects/igniteui-angular/snackbar/src/snackbar/snackbar.component.ts @@ -225,5 +225,5 @@ export class IgxSnackbarComponent extends IgxNotificationsDirective implements O const styles = this._document.defaultView.getComputedStyle(info.elementRef.nativeElement); const horizontalMargins = parseFloat(styles.marginLeft) + parseFloat(styles.marginRight); info.initialSize = { width: elementRect.width + horizontalMargins, height: elementRect.height }; - }; + } }