Skip to content

Commit f8dadb3

Browse files
authored
fix(cdk/overlay): disable backdrop animation when noop animations are enabled (#24687)
Fixes that we weren't disabling the CDK backdrop transition when the noop animations module is used. This has caused test flakes in the past.
1 parent 94ec041 commit f8dadb3

File tree

5 files changed

+58
-33
lines changed

5 files changed

+58
-33
lines changed

src/cdk/overlay/_index.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ $backdrop-animation-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;
111111
}
112112
}
113113

114+
.cdk-overlay-backdrop-noop-animation {
115+
transition: none;
116+
}
117+
114118
// Overlay parent element used with the connected position strategy. Used to constrain the
115119
// overlay element's size to fit within the viewport.
116120
.cdk-overlay-connected-position-bounding-box {

src/cdk/overlay/overlay-ref.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
6565
private _document: Document,
6666
private _location: Location,
6767
private _outsideClickDispatcher: OverlayOutsideClickDispatcher,
68+
private _animationsDisabled = false,
6869
) {
6970
if (_config.scrollStrategy) {
7071
this._scrollStrategy = _config.scrollStrategy;
@@ -375,6 +376,10 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
375376
this._backdropElement = this._document.createElement('div');
376377
this._backdropElement.classList.add('cdk-overlay-backdrop');
377378

379+
if (this._animationsDisabled) {
380+
this._backdropElement.classList.add('cdk-overlay-backdrop-noop-animation');
381+
}
382+
378383
if (this._config.backdropClass) {
379384
this._toggleClasses(this._backdropElement, this._config.backdropClass, true);
380385
}
@@ -388,7 +393,7 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
388393
this._backdropElement.addEventListener('click', this._backdropClickHandler);
389394

390395
// Add class to fade-in the backdrop after one frame.
391-
if (typeof requestAnimationFrame !== 'undefined') {
396+
if (!this._animationsDisabled && typeof requestAnimationFrame !== 'undefined') {
392397
this._ngZone.runOutsideAngular(() => {
393398
requestAnimationFrame(() => {
394399
if (this._backdropElement) {
@@ -422,6 +427,11 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
422427
return;
423428
}
424429

430+
if (this._animationsDisabled) {
431+
this._disposeBackdrop(backdropToDetach);
432+
return;
433+
}
434+
425435
backdropToDetach.classList.remove('cdk-overlay-backdrop-showing');
426436

427437
this._ngZone.runOutsideAngular(() => {

src/cdk/overlay/overlay.spec.ts

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
import {
2-
waitForAsync,
3-
fakeAsync,
4-
tick,
5-
ComponentFixture,
6-
inject,
7-
TestBed,
8-
} from '@angular/core/testing';
1+
import {waitForAsync, fakeAsync, tick, ComponentFixture, TestBed} from '@angular/core/testing';
92
import {
103
Component,
114
ViewChild,
@@ -14,6 +7,7 @@ import {
147
Injectable,
158
EventEmitter,
169
NgZone,
10+
Type,
1711
} from '@angular/core';
1812
import {Direction, Directionality} from '@angular/cdk/bidi';
1913
import {MockNgZone, dispatchFakeEvent} from '../testing/private';
@@ -30,6 +24,7 @@ import {
3024
ScrollStrategy,
3125
} from './index';
3226
import {OverlayReference} from './overlay-reference';
27+
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
3328

3429
describe('Overlay', () => {
3530
let overlay: Overlay;
@@ -42,10 +37,10 @@ describe('Overlay', () => {
4237
let zone: MockNgZone;
4338
let mockLocation: SpyLocation;
4439

45-
beforeEach(waitForAsync(() => {
40+
function setup(imports: Type<unknown>[] = []) {
4641
dir = 'ltr';
4742
TestBed.configureTestingModule({
48-
imports: [OverlayModule, PortalModule],
43+
imports: [OverlayModule, PortalModule, ...imports],
4944
declarations: [PizzaMsg, TestComponentWithTemplatePortals],
5045
providers: [
5146
{
@@ -66,27 +61,25 @@ describe('Overlay', () => {
6661
},
6762
],
6863
}).compileComponents();
69-
}));
7064

71-
beforeEach(inject(
72-
[Overlay, OverlayContainer, Location],
73-
(o: Overlay, oc: OverlayContainer, l: Location) => {
74-
overlay = o;
75-
overlayContainer = oc;
76-
overlayContainerElement = oc.getContainerElement();
77-
78-
const fixture = TestBed.createComponent(TestComponentWithTemplatePortals);
79-
fixture.detectChanges();
80-
templatePortal = fixture.componentInstance.templatePortal;
81-
componentPortal = new ComponentPortal(PizzaMsg, fixture.componentInstance.viewContainerRef);
82-
viewContainerFixture = fixture;
83-
mockLocation = l as SpyLocation;
84-
},
85-
));
86-
87-
afterEach(() => {
65+
overlay = TestBed.inject(Overlay);
66+
overlayContainer = TestBed.inject(OverlayContainer);
67+
overlayContainerElement = overlayContainer.getContainerElement();
68+
69+
const fixture = TestBed.createComponent(TestComponentWithTemplatePortals);
70+
fixture.detectChanges();
71+
templatePortal = fixture.componentInstance.templatePortal;
72+
componentPortal = new ComponentPortal(PizzaMsg, fixture.componentInstance.viewContainerRef);
73+
viewContainerFixture = fixture;
74+
mockLocation = TestBed.inject(Location) as SpyLocation;
75+
}
76+
77+
function cleanup() {
8878
overlayContainer.ngOnDestroy();
89-
});
79+
}
80+
81+
beforeEach(waitForAsync(setup));
82+
afterEach(cleanup);
9083

9184
it('should load a component into an overlay', () => {
9285
let overlayRef = overlay.create();
@@ -867,6 +860,20 @@ describe('Overlay', () => {
867860
backdrop.click();
868861
expect(backdropClickHandler).toHaveBeenCalledTimes(1);
869862
});
863+
864+
it('should set a class on the backdrop when animations are disabled', () => {
865+
cleanup();
866+
TestBed.resetTestingModule();
867+
setup([NoopAnimationsModule]);
868+
869+
let overlayRef = overlay.create(config);
870+
overlayRef.attach(componentPortal);
871+
872+
viewContainerFixture.detectChanges();
873+
let backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement;
874+
875+
expect(backdrop.classList).toContain('cdk-overlay-backdrop-noop-animation');
876+
});
870877
});
871878

872879
describe('panelClass', () => {

src/cdk/overlay/overlay.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import {
1616
Injectable,
1717
Injector,
1818
NgZone,
19+
ANIMATION_MODULE_TYPE,
20+
Optional,
1921
} from '@angular/core';
2022
import {OverlayKeyboardDispatcher} from './dispatchers/overlay-keyboard-dispatcher';
2123
import {OverlayOutsideClickDispatcher} from './dispatchers/overlay-outside-click-dispatcher';
@@ -56,6 +58,7 @@ export class Overlay {
5658
private _directionality: Directionality,
5759
private _location: Location,
5860
private _outsideClickDispatcher: OverlayOutsideClickDispatcher,
61+
@Inject(ANIMATION_MODULE_TYPE) @Optional() private _animationsModuleType?: string,
5962
) {}
6063

6164
/**
@@ -81,6 +84,7 @@ export class Overlay {
8184
this._document,
8285
this._location,
8386
this._outsideClickDispatcher,
87+
this._animationsModuleType === 'NoopAnimations',
8488
);
8589
}
8690

tools/public_api_guard/cdk/overlay.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,12 +262,12 @@ export interface OriginConnectionPosition {
262262
// @public
263263
export class Overlay {
264264
constructor(
265-
scrollStrategies: ScrollStrategyOptions, _overlayContainer: OverlayContainer, _componentFactoryResolver: ComponentFactoryResolver, _positionBuilder: OverlayPositionBuilder, _keyboardDispatcher: OverlayKeyboardDispatcher, _injector: Injector, _ngZone: NgZone, _document: any, _directionality: Directionality, _location: Location_2, _outsideClickDispatcher: OverlayOutsideClickDispatcher);
265+
scrollStrategies: ScrollStrategyOptions, _overlayContainer: OverlayContainer, _componentFactoryResolver: ComponentFactoryResolver, _positionBuilder: OverlayPositionBuilder, _keyboardDispatcher: OverlayKeyboardDispatcher, _injector: Injector, _ngZone: NgZone, _document: any, _directionality: Directionality, _location: Location_2, _outsideClickDispatcher: OverlayOutsideClickDispatcher, _animationsModuleType?: string | undefined);
266266
create(config?: OverlayConfig): OverlayRef;
267267
position(): OverlayPositionBuilder;
268268
scrollStrategies: ScrollStrategyOptions;
269269
// (undocumented)
270-
static ɵfac: i0.ɵɵFactoryDeclaration<Overlay, never>;
270+
static ɵfac: i0.ɵɵFactoryDeclaration<Overlay, [null, null, null, null, null, null, null, null, null, null, null, { optional: true; }]>;
271271
// (undocumented)
272272
static ɵprov: i0.ɵɵInjectableDeclaration<Overlay>;
273273
}
@@ -364,7 +364,7 @@ export class OverlayPositionBuilder {
364364

365365
// @public
366366
export class OverlayRef implements PortalOutlet, OverlayReference {
367-
constructor(_portalOutlet: PortalOutlet, _host: HTMLElement, _pane: HTMLElement, _config: ImmutableObject<OverlayConfig>, _ngZone: NgZone, _keyboardDispatcher: OverlayKeyboardDispatcher, _document: Document, _location: Location_2, _outsideClickDispatcher: OverlayOutsideClickDispatcher);
367+
constructor(_portalOutlet: PortalOutlet, _host: HTMLElement, _pane: HTMLElement, _config: ImmutableObject<OverlayConfig>, _ngZone: NgZone, _keyboardDispatcher: OverlayKeyboardDispatcher, _document: Document, _location: Location_2, _outsideClickDispatcher: OverlayOutsideClickDispatcher, _animationsDisabled?: boolean);
368368
addPanelClass(classes: string | string[]): void;
369369
// (undocumented)
370370
attach<T>(portal: ComponentPortal<T>): ComponentRef<T>;

0 commit comments

Comments
 (0)