feat(animations): add support for disabling animations through BrowserAnimationsModule.withConfig (#40731)
Currently the only way to disable animations is by providing the `NoopAnimationsModule` which doesn't allow for it to be disabled based on runtime information. These changes add support for disabling animations based on runtime information by using `BrowserAnimationsModule.withConfig({disableAnimations: true})`. PR Close #40731
This commit is contained in:
parent
3c24136b98
commit
29d8a0ab09
|
@ -1,6 +1,11 @@
|
||||||
export declare const ANIMATION_MODULE_TYPE: InjectionToken<"NoopAnimations" | "BrowserAnimations">;
|
export declare const ANIMATION_MODULE_TYPE: InjectionToken<"NoopAnimations" | "BrowserAnimations">;
|
||||||
|
|
||||||
export declare class BrowserAnimationsModule {
|
export declare class BrowserAnimationsModule {
|
||||||
|
static withConfig(config: BrowserAnimationsModuleConfig): ModuleWithProviders<BrowserAnimationsModule>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare interface BrowserAnimationsModuleConfig {
|
||||||
|
disableAnimations?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class NoopAnimationsModule {
|
export declare class NoopAnimationsModule {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 3033,
|
"runtime-es2015": 3033,
|
||||||
"main-es2015": 447894,
|
"main-es2015": 448055,
|
||||||
"polyfills-es2015": 52493
|
"polyfills-es2015": 52493
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 3153,
|
"runtime-es2015": 3153,
|
||||||
"main-es2015": 432647,
|
"main-es2015": 433285,
|
||||||
"polyfills-es2015": 52493
|
"polyfills-es2015": 52493
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,8 +46,7 @@ describe('animation tests', function() {
|
||||||
{declarations: [SharedAnimationCmp], imports: [BrowserAnimationsModule]});
|
{declarations: [SharedAnimationCmp], imports: [BrowserAnimationsModule]});
|
||||||
|
|
||||||
const fixture = TestBed.createComponent(SharedAnimationCmp);
|
const fixture = TestBed.createComponent(SharedAnimationCmp);
|
||||||
const cmp = fixture.componentInstance;
|
expect(fixture.componentInstance.animationType).toEqual('BrowserAnimations');
|
||||||
expect(cmp.animationType).toEqual('BrowserAnimations');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hint at NoopAnimationsModule being used', () => {
|
it('should hint at NoopAnimationsModule being used', () => {
|
||||||
|
@ -56,9 +55,20 @@ describe('animation tests', function() {
|
||||||
{declarations: [SharedAnimationCmp], imports: [NoopAnimationsModule]});
|
{declarations: [SharedAnimationCmp], imports: [NoopAnimationsModule]});
|
||||||
|
|
||||||
const fixture = TestBed.createComponent(SharedAnimationCmp);
|
const fixture = TestBed.createComponent(SharedAnimationCmp);
|
||||||
const cmp = fixture.componentInstance;
|
expect(fixture.componentInstance.animationType).toEqual('NoopAnimations');
|
||||||
expect(cmp.animationType).toEqual('NoopAnimations');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should hint at NoopAnimationsModule being used when BrowserAnimationsModule is provided with disabled animations',
|
||||||
|
() => {
|
||||||
|
TestBed.resetTestingModule();
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SharedAnimationCmp],
|
||||||
|
imports: [BrowserAnimationsModule.withConfig({disableAnimations: true})]
|
||||||
|
});
|
||||||
|
|
||||||
|
const fixture = TestBed.createComponent(SharedAnimationCmp);
|
||||||
|
expect(fixture.componentInstance.animationType).toEqual('NoopAnimations');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@Component({template: '<p>template text</p>'})
|
@Component({template: '<p>template text</p>'})
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
* @description
|
* @description
|
||||||
* Entry point for all animation APIs of the animation browser package.
|
* Entry point for all animation APIs of the animation browser package.
|
||||||
*/
|
*/
|
||||||
export {BrowserAnimationsModule, NoopAnimationsModule} from './module';
|
export {BrowserAnimationsModule, BrowserAnimationsModuleConfig, NoopAnimationsModule} from './module';
|
||||||
|
|
||||||
export {ANIMATION_MODULE_TYPE} from './providers';
|
export {ANIMATION_MODULE_TYPE} from './providers';
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,23 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {NgModule} from '@angular/core';
|
import {ModuleWithProviders, NgModule} from '@angular/core';
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
|
|
||||||
import {BROWSER_ANIMATIONS_PROVIDERS, BROWSER_NOOP_ANIMATIONS_PROVIDERS} from './providers';
|
import {BROWSER_ANIMATIONS_PROVIDERS, BROWSER_NOOP_ANIMATIONS_PROVIDERS} from './providers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object used to configure the behavior of {@link BrowserAnimationsModule}
|
||||||
|
* @publicApi
|
||||||
|
*/
|
||||||
|
export interface BrowserAnimationsModuleConfig {
|
||||||
|
/**
|
||||||
|
* Whether animations should be disabled. Passing this is identical to providing the
|
||||||
|
* `NoopAnimationsModule`, but it can be controlled based on a runtime value.
|
||||||
|
*/
|
||||||
|
disableAnimations?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports `BrowserModule` with additional [dependency-injection providers](guide/glossary#provider)
|
* Exports `BrowserModule` with additional [dependency-injection providers](guide/glossary#provider)
|
||||||
* for use with animations. See [Animations](guide/animations).
|
* for use with animations. See [Animations](guide/animations).
|
||||||
|
@ -20,6 +32,30 @@ import {BROWSER_ANIMATIONS_PROVIDERS, BROWSER_NOOP_ANIMATIONS_PROVIDERS} from '.
|
||||||
providers: BROWSER_ANIMATIONS_PROVIDERS,
|
providers: BROWSER_ANIMATIONS_PROVIDERS,
|
||||||
})
|
})
|
||||||
export class BrowserAnimationsModule {
|
export class BrowserAnimationsModule {
|
||||||
|
/**
|
||||||
|
* Configures the module based on the specified object.
|
||||||
|
*
|
||||||
|
* @param config Object used to configure the behavior of the `BrowserAnimationsModule`.
|
||||||
|
* @see `BrowserAnimationsModuleConfig`
|
||||||
|
*
|
||||||
|
* @usageNotes
|
||||||
|
* When registering the `BrowserAnimationsModule`, you can use the `withConfig`
|
||||||
|
* function as follows:
|
||||||
|
* ```
|
||||||
|
* @NgModule({
|
||||||
|
* imports: [BrowserAnimationsModule.withConfig(config)]
|
||||||
|
* })
|
||||||
|
* class MyNgModule {}
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
static withConfig(config: BrowserAnimationsModuleConfig):
|
||||||
|
ModuleWithProviders<BrowserAnimationsModule> {
|
||||||
|
return {
|
||||||
|
ngModule: BrowserAnimationsModule,
|
||||||
|
providers: config.disableAnimations ? BROWSER_NOOP_ANIMATIONS_PROVIDERS :
|
||||||
|
BROWSER_ANIMATIONS_PROVIDERS
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,97 +9,121 @@ import {animate, style, transition, trigger} from '@angular/animations';
|
||||||
import {ɵAnimationEngine} from '@angular/animations/browser';
|
import {ɵAnimationEngine} from '@angular/animations/browser';
|
||||||
import {Component} from '@angular/core';
|
import {Component} from '@angular/core';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
{
|
describe('NoopAnimationsModule', () => {
|
||||||
describe('NoopAnimationsModule', () => {
|
beforeEach(() => {
|
||||||
beforeEach(() => {
|
TestBed.configureTestingModule({imports: [NoopAnimationsModule]});
|
||||||
TestBed.configureTestingModule({imports: [NoopAnimationsModule]});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('should flush and fire callbacks when the zone becomes stable', (async) => {
|
noopAnimationTests();
|
||||||
@Component({
|
});
|
||||||
selector: 'my-cmp',
|
|
||||||
template:
|
describe('BrowserAnimationsModule with disableAnimations = true', () => {
|
||||||
'<div [@myAnimation]="exp" (@myAnimation.start)="onStart($event)" (@myAnimation.done)="onDone($event)"></div>',
|
beforeEach(() => {
|
||||||
animations: [trigger(
|
TestBed.configureTestingModule(
|
||||||
'myAnimation',
|
{imports: [BrowserAnimationsModule.withConfig({disableAnimations: true})]});
|
||||||
[transition(
|
});
|
||||||
'* => state', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
|
|
||||||
})
|
noopAnimationTests();
|
||||||
class Cmp {
|
});
|
||||||
exp: any;
|
|
||||||
startEvent: any;
|
|
||||||
doneEvent: any;
|
function noopAnimationTests() {
|
||||||
onStart(event: any) {
|
it('should flush and fire callbacks when the zone becomes stable', (async) => {
|
||||||
this.startEvent = event;
|
// This test is only meant to be run inside the browser.
|
||||||
}
|
if (isNode) {
|
||||||
onDone(event: any) {
|
async();
|
||||||
this.doneEvent = event;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-cmp',
|
||||||
|
template:
|
||||||
|
'<div [@myAnimation]="exp" (@myAnimation.start)="onStart($event)" (@myAnimation.done)="onDone($event)"></div>',
|
||||||
|
animations: [trigger(
|
||||||
|
'myAnimation',
|
||||||
|
[transition(
|
||||||
|
'* => state', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
|
||||||
|
})
|
||||||
|
class Cmp {
|
||||||
|
exp: any;
|
||||||
|
startEvent: any;
|
||||||
|
doneEvent: any;
|
||||||
|
onStart(event: any) {
|
||||||
|
this.startEvent = event;
|
||||||
}
|
}
|
||||||
|
onDone(event: any) {
|
||||||
|
this.doneEvent = event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||||
|
|
||||||
const fixture = TestBed.createComponent(Cmp);
|
const fixture = TestBed.createComponent(Cmp);
|
||||||
const cmp = fixture.componentInstance;
|
const cmp = fixture.componentInstance;
|
||||||
cmp.exp = 'state';
|
cmp.exp = 'state';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
expect(cmp.startEvent.triggerName).toEqual('myAnimation');
|
expect(cmp.startEvent.triggerName).toEqual('myAnimation');
|
||||||
expect(cmp.startEvent.phaseName).toEqual('start');
|
expect(cmp.startEvent.phaseName).toEqual('start');
|
||||||
expect(cmp.doneEvent.triggerName).toEqual('myAnimation');
|
expect(cmp.doneEvent.triggerName).toEqual('myAnimation');
|
||||||
expect(cmp.doneEvent.phaseName).toEqual('done');
|
expect(cmp.doneEvent.phaseName).toEqual('done');
|
||||||
async();
|
async();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should handle leave animation callbacks even if the element is destroyed in the process',
|
it('should handle leave animation callbacks even if the element is destroyed in the process',
|
||||||
(async) => {
|
(async) => {
|
||||||
@Component({
|
// This test is only meant to be run inside the browser.
|
||||||
selector: 'my-cmp',
|
if (isNode) {
|
||||||
template:
|
async();
|
||||||
'<div *ngIf="exp" @myAnimation (@myAnimation.start)="onStart($event)" (@myAnimation.done)="onDone($event)"></div>',
|
return;
|
||||||
animations: [trigger(
|
}
|
||||||
'myAnimation',
|
|
||||||
[transition(
|
@Component({
|
||||||
':leave', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
|
selector: 'my-cmp',
|
||||||
})
|
template:
|
||||||
class Cmp {
|
'<div *ngIf="exp" @myAnimation (@myAnimation.start)="onStart($event)" (@myAnimation.done)="onDone($event)"></div>',
|
||||||
exp: any;
|
animations: [trigger(
|
||||||
startEvent: any;
|
'myAnimation',
|
||||||
doneEvent: any;
|
[transition(
|
||||||
onStart(event: any) {
|
':leave', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
|
||||||
this.startEvent = event;
|
})
|
||||||
}
|
class Cmp {
|
||||||
onDone(event: any) {
|
exp: any;
|
||||||
this.doneEvent = event;
|
startEvent: any;
|
||||||
}
|
doneEvent: any;
|
||||||
|
onStart(event: any) {
|
||||||
|
this.startEvent = event;
|
||||||
}
|
}
|
||||||
|
onDone(event: any) {
|
||||||
|
this.doneEvent = event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||||
const engine = TestBed.inject(ɵAnimationEngine);
|
const engine = TestBed.inject(ɵAnimationEngine);
|
||||||
const fixture = TestBed.createComponent(Cmp);
|
const fixture = TestBed.createComponent(Cmp);
|
||||||
const cmp = fixture.componentInstance;
|
const cmp = fixture.componentInstance;
|
||||||
|
|
||||||
cmp.exp = true;
|
cmp.exp = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
cmp.startEvent = null;
|
||||||
|
cmp.doneEvent = null;
|
||||||
|
|
||||||
|
cmp.exp = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
cmp.startEvent = null;
|
expect(cmp.startEvent.triggerName).toEqual('myAnimation');
|
||||||
cmp.doneEvent = null;
|
expect(cmp.startEvent.phaseName).toEqual('start');
|
||||||
|
expect(cmp.startEvent.toState).toEqual('void');
|
||||||
cmp.exp = false;
|
expect(cmp.doneEvent.triggerName).toEqual('myAnimation');
|
||||||
fixture.detectChanges();
|
expect(cmp.doneEvent.phaseName).toEqual('done');
|
||||||
fixture.whenStable().then(() => {
|
expect(cmp.doneEvent.toState).toEqual('void');
|
||||||
expect(cmp.startEvent.triggerName).toEqual('myAnimation');
|
async();
|
||||||
expect(cmp.startEvent.phaseName).toEqual('start');
|
|
||||||
expect(cmp.startEvent.toState).toEqual('void');
|
|
||||||
expect(cmp.doneEvent.triggerName).toEqual('myAnimation');
|
|
||||||
expect(cmp.doneEvent.phaseName).toEqual('done');
|
|
||||||
expect(cmp.doneEvent.toState).toEqual('void');
|
|
||||||
async();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue