Skip to content

Commit 29d8a0a

Browse files
crisbetozarend
authored andcommitted
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
1 parent 3c24136 commit 29d8a0a

File tree

6 files changed

+162
-87
lines changed

6 files changed

+162
-87
lines changed

goldens/public-api/platform-browser/animations/animations.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
export declare const ANIMATION_MODULE_TYPE: InjectionToken<"NoopAnimations" | "BrowserAnimations">;
22

33
export declare class BrowserAnimationsModule {
4+
static withConfig(config: BrowserAnimationsModuleConfig): ModuleWithProviders<BrowserAnimationsModule>;
5+
}
6+
7+
export declare interface BrowserAnimationsModuleConfig {
8+
disableAnimations?: boolean;
49
}
510

611
export declare class NoopAnimationsModule {

goldens/size-tracking/aio-payloads.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"master": {
1313
"uncompressed": {
1414
"runtime-es2015": 3033,
15-
"main-es2015": 447894,
15+
"main-es2015": 448055,
1616
"polyfills-es2015": 52493
1717
}
1818
}
@@ -21,7 +21,7 @@
2121
"master": {
2222
"uncompressed": {
2323
"runtime-es2015": 3153,
24-
"main-es2015": 432647,
24+
"main-es2015": 433285,
2525
"polyfills-es2015": 52493
2626
}
2727
}

packages/core/test/animation/animation_integration_spec.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ describe('animation tests', function() {
4646
{declarations: [SharedAnimationCmp], imports: [BrowserAnimationsModule]});
4747

4848
const fixture = TestBed.createComponent(SharedAnimationCmp);
49-
const cmp = fixture.componentInstance;
50-
expect(cmp.animationType).toEqual('BrowserAnimations');
49+
expect(fixture.componentInstance.animationType).toEqual('BrowserAnimations');
5150
});
5251

5352
it('should hint at NoopAnimationsModule being used', () => {
@@ -56,9 +55,20 @@ describe('animation tests', function() {
5655
{declarations: [SharedAnimationCmp], imports: [NoopAnimationsModule]});
5756

5857
const fixture = TestBed.createComponent(SharedAnimationCmp);
59-
const cmp = fixture.componentInstance;
60-
expect(cmp.animationType).toEqual('NoopAnimations');
58+
expect(fixture.componentInstance.animationType).toEqual('NoopAnimations');
6159
});
60+
61+
it('should hint at NoopAnimationsModule being used when BrowserAnimationsModule is provided with disabled animations',
62+
() => {
63+
TestBed.resetTestingModule();
64+
TestBed.configureTestingModule({
65+
declarations: [SharedAnimationCmp],
66+
imports: [BrowserAnimationsModule.withConfig({disableAnimations: true})]
67+
});
68+
69+
const fixture = TestBed.createComponent(SharedAnimationCmp);
70+
expect(fixture.componentInstance.animationType).toEqual('NoopAnimations');
71+
});
6272
});
6373

6474
@Component({template: '<p>template text</p>'})

packages/platform-browser/animations/src/animations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* @description
1212
* Entry point for all animation APIs of the animation browser package.
1313
*/
14-
export {BrowserAnimationsModule, NoopAnimationsModule} from './module';
14+
export {BrowserAnimationsModule, BrowserAnimationsModuleConfig, NoopAnimationsModule} from './module';
1515

1616
export {ANIMATION_MODULE_TYPE} from './providers';
1717

packages/platform-browser/animations/src/module.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,23 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import {NgModule} from '@angular/core';
8+
import {ModuleWithProviders, NgModule} from '@angular/core';
99
import {BrowserModule} from '@angular/platform-browser';
1010

1111
import {BROWSER_ANIMATIONS_PROVIDERS, BROWSER_NOOP_ANIMATIONS_PROVIDERS} from './providers';
1212

13+
/**
14+
* Object used to configure the behavior of {@link BrowserAnimationsModule}
15+
* @publicApi
16+
*/
17+
export interface BrowserAnimationsModuleConfig {
18+
/**
19+
* Whether animations should be disabled. Passing this is identical to providing the
20+
* `NoopAnimationsModule`, but it can be controlled based on a runtime value.
21+
*/
22+
disableAnimations?: boolean;
23+
}
24+
1325
/**
1426
* Exports `BrowserModule` with additional [dependency-injection providers](guide/glossary#provider)
1527
* for use with animations. See [Animations](guide/animations).
@@ -20,6 +32,30 @@ import {BROWSER_ANIMATIONS_PROVIDERS, BROWSER_NOOP_ANIMATIONS_PROVIDERS} from '.
2032
providers: BROWSER_ANIMATIONS_PROVIDERS,
2133
})
2234
export class BrowserAnimationsModule {
35+
/**
36+
* Configures the module based on the specified object.
37+
*
38+
* @param config Object used to configure the behavior of the `BrowserAnimationsModule`.
39+
* @see `BrowserAnimationsModuleConfig`
40+
*
41+
* @usageNotes
42+
* When registering the `BrowserAnimationsModule`, you can use the `withConfig`
43+
* function as follows:
44+
* ```
45+
* @NgModule({
46+
* imports: [BrowserAnimationsModule.withConfig(config)]
47+
* })
48+
* class MyNgModule {}
49+
* ```
50+
*/
51+
static withConfig(config: BrowserAnimationsModuleConfig):
52+
ModuleWithProviders<BrowserAnimationsModule> {
53+
return {
54+
ngModule: BrowserAnimationsModule,
55+
providers: config.disableAnimations ? BROWSER_NOOP_ANIMATIONS_PROVIDERS :
56+
BROWSER_ANIMATIONS_PROVIDERS
57+
};
58+
}
2359
}
2460

2561
/**

packages/platform-browser/animations/test/noop_animations_module_spec.ts

Lines changed: 103 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -9,97 +9,121 @@ import {animate, style, transition, trigger} from '@angular/animations';
99
import {ɵAnimationEngine} from '@angular/animations/browser';
1010
import {Component} from '@angular/core';
1111
import {TestBed} from '@angular/core/testing';
12-
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
12+
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';
1313

14-
{
15-
describe('NoopAnimationsModule', () => {
16-
beforeEach(() => {
17-
TestBed.configureTestingModule({imports: [NoopAnimationsModule]});
18-
});
14+
describe('NoopAnimationsModule', () => {
15+
beforeEach(() => {
16+
TestBed.configureTestingModule({imports: [NoopAnimationsModule]});
17+
});
18+
19+
noopAnimationTests();
20+
});
21+
22+
describe('BrowserAnimationsModule with disableAnimations = true', () => {
23+
beforeEach(() => {
24+
TestBed.configureTestingModule(
25+
{imports: [BrowserAnimationsModule.withConfig({disableAnimations: true})]});
26+
});
27+
28+
noopAnimationTests();
29+
});
30+
31+
32+
function noopAnimationTests() {
33+
it('should flush and fire callbacks when the zone becomes stable', (async) => {
34+
// This test is only meant to be run inside the browser.
35+
if (isNode) {
36+
async();
37+
return;
38+
}
1939

20-
it('should flush and fire callbacks when the zone becomes stable', (async) => {
21-
@Component({
22-
selector: 'my-cmp',
23-
template:
24-
'<div [@myAnimation]="exp" (@myAnimation.start)="onStart($event)" (@myAnimation.done)="onDone($event)"></div>',
25-
animations: [trigger(
26-
'myAnimation',
27-
[transition(
28-
'* => state', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
29-
})
30-
class Cmp {
31-
exp: any;
32-
startEvent: any;
33-
doneEvent: any;
34-
onStart(event: any) {
35-
this.startEvent = event;
36-
}
37-
onDone(event: any) {
38-
this.doneEvent = event;
39-
}
40+
@Component({
41+
selector: 'my-cmp',
42+
template:
43+
'<div [@myAnimation]="exp" (@myAnimation.start)="onStart($event)" (@myAnimation.done)="onDone($event)"></div>',
44+
animations: [trigger(
45+
'myAnimation',
46+
[transition(
47+
'* => state', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
48+
})
49+
class Cmp {
50+
exp: any;
51+
startEvent: any;
52+
doneEvent: any;
53+
onStart(event: any) {
54+
this.startEvent = event;
4055
}
56+
onDone(event: any) {
57+
this.doneEvent = event;
58+
}
59+
}
4160

42-
TestBed.configureTestingModule({declarations: [Cmp]});
61+
TestBed.configureTestingModule({declarations: [Cmp]});
4362

44-
const fixture = TestBed.createComponent(Cmp);
45-
const cmp = fixture.componentInstance;
46-
cmp.exp = 'state';
47-
fixture.detectChanges();
48-
fixture.whenStable().then(() => {
49-
expect(cmp.startEvent.triggerName).toEqual('myAnimation');
50-
expect(cmp.startEvent.phaseName).toEqual('start');
51-
expect(cmp.doneEvent.triggerName).toEqual('myAnimation');
52-
expect(cmp.doneEvent.phaseName).toEqual('done');
53-
async();
54-
});
63+
const fixture = TestBed.createComponent(Cmp);
64+
const cmp = fixture.componentInstance;
65+
cmp.exp = 'state';
66+
fixture.detectChanges();
67+
fixture.whenStable().then(() => {
68+
expect(cmp.startEvent.triggerName).toEqual('myAnimation');
69+
expect(cmp.startEvent.phaseName).toEqual('start');
70+
expect(cmp.doneEvent.triggerName).toEqual('myAnimation');
71+
expect(cmp.doneEvent.phaseName).toEqual('done');
72+
async();
5573
});
74+
});
5675

57-
it('should handle leave animation callbacks even if the element is destroyed in the process',
58-
(async) => {
59-
@Component({
60-
selector: 'my-cmp',
61-
template:
62-
'<div *ngIf="exp" @myAnimation (@myAnimation.start)="onStart($event)" (@myAnimation.done)="onDone($event)"></div>',
63-
animations: [trigger(
64-
'myAnimation',
65-
[transition(
66-
':leave', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
67-
})
68-
class Cmp {
69-
exp: any;
70-
startEvent: any;
71-
doneEvent: any;
72-
onStart(event: any) {
73-
this.startEvent = event;
74-
}
75-
onDone(event: any) {
76-
this.doneEvent = event;
77-
}
76+
it('should handle leave animation callbacks even if the element is destroyed in the process',
77+
(async) => {
78+
// This test is only meant to be run inside the browser.
79+
if (isNode) {
80+
async();
81+
return;
82+
}
83+
84+
@Component({
85+
selector: 'my-cmp',
86+
template:
87+
'<div *ngIf="exp" @myAnimation (@myAnimation.start)="onStart($event)" (@myAnimation.done)="onDone($event)"></div>',
88+
animations: [trigger(
89+
'myAnimation',
90+
[transition(
91+
':leave', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
92+
})
93+
class Cmp {
94+
exp: any;
95+
startEvent: any;
96+
doneEvent: any;
97+
onStart(event: any) {
98+
this.startEvent = event;
99+
}
100+
onDone(event: any) {
101+
this.doneEvent = event;
78102
}
103+
}
79104

80-
TestBed.configureTestingModule({declarations: [Cmp]});
81-
const engine = TestBed.inject(ɵAnimationEngine);
82-
const fixture = TestBed.createComponent(Cmp);
83-
const cmp = fixture.componentInstance;
105+
TestBed.configureTestingModule({declarations: [Cmp]});
106+
const engine = TestBed.inject(ɵAnimationEngine);
107+
const fixture = TestBed.createComponent(Cmp);
108+
const cmp = fixture.componentInstance;
84109

85-
cmp.exp = true;
110+
cmp.exp = true;
111+
fixture.detectChanges();
112+
fixture.whenStable().then(() => {
113+
cmp.startEvent = null;
114+
cmp.doneEvent = null;
115+
116+
cmp.exp = false;
86117
fixture.detectChanges();
87118
fixture.whenStable().then(() => {
88-
cmp.startEvent = null;
89-
cmp.doneEvent = null;
90-
91-
cmp.exp = false;
92-
fixture.detectChanges();
93-
fixture.whenStable().then(() => {
94-
expect(cmp.startEvent.triggerName).toEqual('myAnimation');
95-
expect(cmp.startEvent.phaseName).toEqual('start');
96-
expect(cmp.startEvent.toState).toEqual('void');
97-
expect(cmp.doneEvent.triggerName).toEqual('myAnimation');
98-
expect(cmp.doneEvent.phaseName).toEqual('done');
99-
expect(cmp.doneEvent.toState).toEqual('void');
100-
async();
101-
});
119+
expect(cmp.startEvent.triggerName).toEqual('myAnimation');
120+
expect(cmp.startEvent.phaseName).toEqual('start');
121+
expect(cmp.startEvent.toState).toEqual('void');
122+
expect(cmp.doneEvent.triggerName).toEqual('myAnimation');
123+
expect(cmp.doneEvent.phaseName).toEqual('done');
124+
expect(cmp.doneEvent.toState).toEqual('void');
125+
async();
102126
});
103127
});
104-
});
128+
});
105129
}

0 commit comments

Comments
 (0)