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 class BrowserAnimationsModule {
|
||||
static withConfig(config: BrowserAnimationsModuleConfig): ModuleWithProviders<BrowserAnimationsModule>;
|
||||
}
|
||||
|
||||
export declare interface BrowserAnimationsModuleConfig {
|
||||
disableAnimations?: boolean;
|
||||
}
|
||||
|
||||
export declare class NoopAnimationsModule {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 3033,
|
||||
"main-es2015": 447894,
|
||||
"main-es2015": 448055,
|
||||
"polyfills-es2015": 52493
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
|||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 3153,
|
||||
"main-es2015": 432647,
|
||||
"main-es2015": 433285,
|
||||
"polyfills-es2015": 52493
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,7 @@ describe('animation tests', function() {
|
|||
{declarations: [SharedAnimationCmp], imports: [BrowserAnimationsModule]});
|
||||
|
||||
const fixture = TestBed.createComponent(SharedAnimationCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
expect(cmp.animationType).toEqual('BrowserAnimations');
|
||||
expect(fixture.componentInstance.animationType).toEqual('BrowserAnimations');
|
||||
});
|
||||
|
||||
it('should hint at NoopAnimationsModule being used', () => {
|
||||
|
@ -56,9 +55,20 @@ describe('animation tests', function() {
|
|||
{declarations: [SharedAnimationCmp], imports: [NoopAnimationsModule]});
|
||||
|
||||
const fixture = TestBed.createComponent(SharedAnimationCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
expect(cmp.animationType).toEqual('NoopAnimations');
|
||||
expect(fixture.componentInstance.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>'})
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
* @description
|
||||
* 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';
|
||||
|
||||
|
|
|
@ -5,11 +5,23 @@
|
|||
* 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
|
||||
*/
|
||||
import {NgModule} from '@angular/core';
|
||||
import {ModuleWithProviders, NgModule} from '@angular/core';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
|
||||
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)
|
||||
* 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,
|
||||
})
|
||||
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 {Component} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||
|
||||
{
|
||||
describe('NoopAnimationsModule', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({imports: [NoopAnimationsModule]});
|
||||
});
|
||||
describe('NoopAnimationsModule', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({imports: [NoopAnimationsModule]});
|
||||
});
|
||||
|
||||
it('should flush and fire callbacks when the zone becomes stable', (async) => {
|
||||
@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;
|
||||
}
|
||||
noopAnimationTests();
|
||||
});
|
||||
|
||||
describe('BrowserAnimationsModule with disableAnimations = true', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule(
|
||||
{imports: [BrowserAnimationsModule.withConfig({disableAnimations: true})]});
|
||||
});
|
||||
|
||||
noopAnimationTests();
|
||||
});
|
||||
|
||||
|
||||
function noopAnimationTests() {
|
||||
it('should flush and fire callbacks when the zone becomes stable', (async) => {
|
||||
// This test is only meant to be run inside the browser.
|
||||
if (isNode) {
|
||||
async();
|
||||
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 cmp = fixture.componentInstance;
|
||||
cmp.exp = 'state';
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(cmp.startEvent.triggerName).toEqual('myAnimation');
|
||||
expect(cmp.startEvent.phaseName).toEqual('start');
|
||||
expect(cmp.doneEvent.triggerName).toEqual('myAnimation');
|
||||
expect(cmp.doneEvent.phaseName).toEqual('done');
|
||||
async();
|
||||
});
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.exp = 'state';
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(cmp.startEvent.triggerName).toEqual('myAnimation');
|
||||
expect(cmp.startEvent.phaseName).toEqual('start');
|
||||
expect(cmp.doneEvent.triggerName).toEqual('myAnimation');
|
||||
expect(cmp.doneEvent.phaseName).toEqual('done');
|
||||
async();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle leave animation callbacks even if the element is destroyed in the process',
|
||||
(async) => {
|
||||
@Component({
|
||||
selector: 'my-cmp',
|
||||
template:
|
||||
'<div *ngIf="exp" @myAnimation (@myAnimation.start)="onStart($event)" (@myAnimation.done)="onDone($event)"></div>',
|
||||
animations: [trigger(
|
||||
'myAnimation',
|
||||
[transition(
|
||||
':leave', [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;
|
||||
}
|
||||
it('should handle leave animation callbacks even if the element is destroyed in the process',
|
||||
(async) => {
|
||||
// This test is only meant to be run inside the browser.
|
||||
if (isNode) {
|
||||
async();
|
||||
return;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-cmp',
|
||||
template:
|
||||
'<div *ngIf="exp" @myAnimation (@myAnimation.start)="onStart($event)" (@myAnimation.done)="onDone($event)"></div>',
|
||||
animations: [trigger(
|
||||
'myAnimation',
|
||||
[transition(
|
||||
':leave', [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]});
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
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.whenStable().then(() => {
|
||||
cmp.startEvent = null;
|
||||
cmp.doneEvent = null;
|
||||
|
||||
cmp.exp = false;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(cmp.startEvent.triggerName).toEqual('myAnimation');
|
||||
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();
|
||||
});
|
||||
expect(cmp.startEvent.triggerName).toEqual('myAnimation');
|
||||
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