Skip to content

Commit

Permalink
fix(cdk/overlay): disable backdrop animation when noop animations are…
Browse files Browse the repository at this point in the history
… enabled (angular#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.
  • Loading branch information
crisbeto authored and forsti0506 committed Apr 3, 2022
1 parent 3eae33d commit d5abb1c
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 33 deletions.
4 changes: 4 additions & 0 deletions src/cdk/overlay/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ $backdrop-animation-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;
}
}

.cdk-overlay-backdrop-noop-animation {
transition: none;
}

// Overlay parent element used with the connected position strategy. Used to constrain the
// overlay element's size to fit within the viewport.
.cdk-overlay-connected-position-bounding-box {
Expand Down
12 changes: 11 additions & 1 deletion src/cdk/overlay/overlay-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
private _document: Document,
private _location: Location,
private _outsideClickDispatcher: OverlayOutsideClickDispatcher,
private _animationsDisabled = false,
) {
if (_config.scrollStrategy) {
this._scrollStrategy = _config.scrollStrategy;
Expand Down Expand Up @@ -375,6 +376,10 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
this._backdropElement = this._document.createElement('div');
this._backdropElement.classList.add('cdk-overlay-backdrop');

if (this._animationsDisabled) {
this._backdropElement.classList.add('cdk-overlay-backdrop-noop-animation');
}

if (this._config.backdropClass) {
this._toggleClasses(this._backdropElement, this._config.backdropClass, true);
}
Expand All @@ -388,7 +393,7 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
this._backdropElement.addEventListener('click', this._backdropClickHandler);

// Add class to fade-in the backdrop after one frame.
if (typeof requestAnimationFrame !== 'undefined') {
if (!this._animationsDisabled && typeof requestAnimationFrame !== 'undefined') {
this._ngZone.runOutsideAngular(() => {
requestAnimationFrame(() => {
if (this._backdropElement) {
Expand Down Expand Up @@ -422,6 +427,11 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
return;
}

if (this._animationsDisabled) {
this._disposeBackdrop(backdropToDetach);
return;
}

backdropToDetach.classList.remove('cdk-overlay-backdrop-showing');

this._ngZone.runOutsideAngular(() => {
Expand Down
65 changes: 36 additions & 29 deletions src/cdk/overlay/overlay.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import {
waitForAsync,
fakeAsync,
tick,
ComponentFixture,
inject,
TestBed,
} from '@angular/core/testing';
import {waitForAsync, fakeAsync, tick, ComponentFixture, TestBed} from '@angular/core/testing';
import {
Component,
ViewChild,
Expand All @@ -14,6 +7,7 @@ import {
Injectable,
EventEmitter,
NgZone,
Type,
} from '@angular/core';
import {Direction, Directionality} from '@angular/cdk/bidi';
import {MockNgZone, dispatchFakeEvent} from '../testing/private';
Expand All @@ -30,6 +24,7 @@ import {
ScrollStrategy,
} from './index';
import {OverlayReference} from './overlay-reference';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';

describe('Overlay', () => {
let overlay: Overlay;
Expand All @@ -42,10 +37,10 @@ describe('Overlay', () => {
let zone: MockNgZone;
let mockLocation: SpyLocation;

beforeEach(waitForAsync(() => {
function setup(imports: Type<unknown>[] = []) {
dir = 'ltr';
TestBed.configureTestingModule({
imports: [OverlayModule, PortalModule],
imports: [OverlayModule, PortalModule, ...imports],
declarations: [PizzaMsg, TestComponentWithTemplatePortals],
providers: [
{
Expand All @@ -66,27 +61,25 @@ describe('Overlay', () => {
},
],
}).compileComponents();
}));

beforeEach(inject(
[Overlay, OverlayContainer, Location],
(o: Overlay, oc: OverlayContainer, l: Location) => {
overlay = o;
overlayContainer = oc;
overlayContainerElement = oc.getContainerElement();

const fixture = TestBed.createComponent(TestComponentWithTemplatePortals);
fixture.detectChanges();
templatePortal = fixture.componentInstance.templatePortal;
componentPortal = new ComponentPortal(PizzaMsg, fixture.componentInstance.viewContainerRef);
viewContainerFixture = fixture;
mockLocation = l as SpyLocation;
},
));

afterEach(() => {
overlay = TestBed.inject(Overlay);
overlayContainer = TestBed.inject(OverlayContainer);
overlayContainerElement = overlayContainer.getContainerElement();

const fixture = TestBed.createComponent(TestComponentWithTemplatePortals);
fixture.detectChanges();
templatePortal = fixture.componentInstance.templatePortal;
componentPortal = new ComponentPortal(PizzaMsg, fixture.componentInstance.viewContainerRef);
viewContainerFixture = fixture;
mockLocation = TestBed.inject(Location) as SpyLocation;
}

function cleanup() {
overlayContainer.ngOnDestroy();
});
}

beforeEach(waitForAsync(setup));
afterEach(cleanup);

it('should load a component into an overlay', () => {
let overlayRef = overlay.create();
Expand Down Expand Up @@ -867,6 +860,20 @@ describe('Overlay', () => {
backdrop.click();
expect(backdropClickHandler).toHaveBeenCalledTimes(1);
});

it('should set a class on the backdrop when animations are disabled', () => {
cleanup();
TestBed.resetTestingModule();
setup([NoopAnimationsModule]);

let overlayRef = overlay.create(config);
overlayRef.attach(componentPortal);

viewContainerFixture.detectChanges();
let backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement;

expect(backdrop.classList).toContain('cdk-overlay-backdrop-noop-animation');
});
});

describe('panelClass', () => {
Expand Down
4 changes: 4 additions & 0 deletions src/cdk/overlay/overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
Injectable,
Injector,
NgZone,
ANIMATION_MODULE_TYPE,
Optional,
} from '@angular/core';
import {OverlayKeyboardDispatcher} from './dispatchers/overlay-keyboard-dispatcher';
import {OverlayOutsideClickDispatcher} from './dispatchers/overlay-outside-click-dispatcher';
Expand Down Expand Up @@ -56,6 +58,7 @@ export class Overlay {
private _directionality: Directionality,
private _location: Location,
private _outsideClickDispatcher: OverlayOutsideClickDispatcher,
@Inject(ANIMATION_MODULE_TYPE) @Optional() private _animationsModuleType?: string,
) {}

/**
Expand All @@ -81,6 +84,7 @@ export class Overlay {
this._document,
this._location,
this._outsideClickDispatcher,
this._animationsModuleType === 'NoopAnimations',
);
}

Expand Down
6 changes: 3 additions & 3 deletions tools/public_api_guard/cdk/overlay.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,12 +262,12 @@ export interface OriginConnectionPosition {
// @public
export class Overlay {
constructor(
scrollStrategies: ScrollStrategyOptions, _overlayContainer: OverlayContainer, _componentFactoryResolver: ComponentFactoryResolver, _positionBuilder: OverlayPositionBuilder, _keyboardDispatcher: OverlayKeyboardDispatcher, _injector: Injector, _ngZone: NgZone, _document: any, _directionality: Directionality, _location: Location_2, _outsideClickDispatcher: OverlayOutsideClickDispatcher);
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);
create(config?: OverlayConfig): OverlayRef;
position(): OverlayPositionBuilder;
scrollStrategies: ScrollStrategyOptions;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<Overlay, never>;
static ɵfac: i0.ɵɵFactoryDeclaration<Overlay, [null, null, null, null, null, null, null, null, null, null, null, { optional: true; }]>;
// (undocumented)
static ɵprov: i0.ɵɵInjectableDeclaration<Overlay>;
}
Expand Down Expand Up @@ -364,7 +364,7 @@ export class OverlayPositionBuilder {

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

0 comments on commit d5abb1c

Please sign in to comment.