diff --git a/goldens/public-api/platform-browser/animations/index.md b/goldens/public-api/platform-browser/animations/index.md index 0a27ff1cdd879..8480695cd761d 100644 --- a/goldens/public-api/platform-browser/animations/index.md +++ b/goldens/public-api/platform-browser/animations/index.md @@ -8,6 +8,7 @@ import { ANIMATION_MODULE_TYPE } from '@angular/core'; import * as i0 from '@angular/core'; import * as i1 from '@angular/platform-browser'; import { ModuleWithProviders } from '@angular/core'; +import { Provider } from '@angular/core'; export { ANIMATION_MODULE_TYPE } @@ -37,6 +38,12 @@ export class NoopAnimationsModule { static ɵmod: i0.ɵɵNgModuleDeclaration; } +// @public +export function provideAnimations(): Provider[]; + +// @public +export function provideNoopAnimations(): Provider[]; + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/platform-browser/animations/src/animations.ts b/packages/platform-browser/animations/src/animations.ts index 45378a0170978..5def93c50dbad 100644 --- a/packages/platform-browser/animations/src/animations.ts +++ b/packages/platform-browser/animations/src/animations.ts @@ -12,6 +12,6 @@ * Entry point for all animation APIs of the animation browser package. */ export {ANIMATION_MODULE_TYPE} from '@angular/core'; -export {BrowserAnimationsModule, BrowserAnimationsModuleConfig, NoopAnimationsModule} from './module'; +export {BrowserAnimationsModule, BrowserAnimationsModuleConfig, NoopAnimationsModule, provideAnimations, provideNoopAnimations} from './module'; export * from './private_export'; diff --git a/packages/platform-browser/animations/src/module.ts b/packages/platform-browser/animations/src/module.ts index bd854c35b4d5e..c3f54ce3320f7 100644 --- a/packages/platform-browser/animations/src/module.ts +++ b/packages/platform-browser/animations/src/module.ts @@ -5,7 +5,7 @@ * 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 {ModuleWithProviders, NgModule} from '@angular/core'; +import {ModuleWithProviders, NgModule, Provider} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {BROWSER_ANIMATIONS_PROVIDERS, BROWSER_NOOP_ANIMATIONS_PROVIDERS} from './providers'; @@ -58,6 +58,35 @@ export class BrowserAnimationsModule { } } +/** + * Returns the set of [dependency-injection providers](guide/glossary#provider) + * to enable animations in an application. See [animations guide](guide/animations) + * to learn more about animations in Angular. + * + * @usageNotes + * + * The function is useful when you want to enable animations in an application + * bootstrapped using the `bootstrapApplication` function. In this scenario there + * is no need to import the `BrowserAnimationsModule` NgModule at all, just add + * providers returned by this function to the `providers` list as show below. + * + * ```typescript + * bootstrapApplication(RootComponent, { + * providers: [ + * provideAnimations() + * ] + * }); + * ``` + * + * @publicApi + * @developerPreview + */ +export function provideAnimations(): Provider[] { + // Return a copy to prevent changes to the original array in case any in-place + // alterations are performed to the `provideAnimations` call results in app code. + return [...BROWSER_ANIMATIONS_PROVIDERS]; +} + /** * A null player that must be imported to allow disabling of animations. * @publicApi @@ -68,3 +97,31 @@ export class BrowserAnimationsModule { }) export class NoopAnimationsModule { } + +/** + * Returns the set of [dependency-injection providers](guide/glossary#provider) + * to disable animations in an application. See [animations guide](guide/animations) + * to learn more about animations in Angular. + * + * @usageNotes + * + * The function is useful when you want to bootstrap an application using + * the `bootstrapApplication` function, but you need to disable animations + * (for example, when running tests). + * + * ```typescript + * bootstrapApplication(RootComponent, { + * providers: [ + * provideNoopAnimations() + * ] + * }); + * ``` + * + * @publicApi + * @developerPreview + */ +export function provideNoopAnimations(): Provider[] { + // Return a copy to prevent changes to the original array in case any in-place + // alterations are performed to the `provideNoopAnimations` call results in app code. + return [...BROWSER_NOOP_ANIMATIONS_PROVIDERS]; +} diff --git a/packages/platform-browser/animations/test/noop_animations_module_spec.ts b/packages/platform-browser/animations/test/noop_animations_module_spec.ts index 6c18d9bf51914..67e19d593359f 100644 --- a/packages/platform-browser/animations/test/noop_animations_module_spec.ts +++ b/packages/platform-browser/animations/test/noop_animations_module_spec.ts @@ -9,7 +9,7 @@ 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 {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations'; +import {BrowserAnimationsModule, NoopAnimationsModule, provideNoopAnimations} from '@angular/platform-browser/animations'; describe('NoopAnimationsModule', () => { beforeEach(() => { @@ -28,6 +28,14 @@ describe('BrowserAnimationsModule with disableAnimations = true', () => { noopAnimationTests(); }); +describe('provideNoopAnimations()', () => { + beforeEach(() => { + TestBed.configureTestingModule({providers: [provideNoopAnimations()]}); + }); + + noopAnimationTests(); +}); + function noopAnimationTests() { it('should flush and fire callbacks when the zone becomes stable', (async) => { diff --git a/packages/platform-browser/test/browser/bootstrap_spec.ts b/packages/platform-browser/test/browser/bootstrap_spec.ts index 0a42dcb47775e..e984a4cccd101 100644 --- a/packages/platform-browser/test/browser/bootstrap_spec.ts +++ b/packages/platform-browser/test/browser/bootstrap_spec.ts @@ -6,8 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ +import {animate, state, style, transition, trigger} from '@angular/animations'; import {DOCUMENT, isPlatformBrowser, ɵgetDOM as getDOM} from '@angular/common'; -import {APP_INITIALIZER, Compiler, Component, createPlatformFactory, CUSTOM_ELEMENTS_SCHEMA, Directive, ErrorHandler, Inject, InjectionToken, Injector, Input, LOCALE_ID, NgModule, NgModuleRef, OnDestroy, Pipe, PLATFORM_ID, PLATFORM_INITIALIZER, Provider, Sanitizer, StaticProvider, Testability, TestabilityRegistry, Type, VERSION} from '@angular/core'; +import {ANIMATION_MODULE_TYPE, APP_INITIALIZER, Compiler, Component, createPlatformFactory, CUSTOM_ELEMENTS_SCHEMA, Directive, ErrorHandler, Inject, inject as _inject, InjectionToken, Injector, Input, LOCALE_ID, NgModule, NgModuleRef, OnDestroy, Pipe, PLATFORM_ID, PLATFORM_INITIALIZER, Provider, Sanitizer, StaticProvider, Testability, TestabilityRegistry, Type, VERSION} from '@angular/core'; import {ApplicationRef, destroyPlatform} from '@angular/core/src/application_ref'; import {Console} from '@angular/core/src/console'; import {ComponentRef} from '@angular/core/src/linker/component_factory'; @@ -15,6 +16,7 @@ import {inject, TestBed} from '@angular/core/testing'; import {Log} from '@angular/core/testing/src/testing_internal'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; +import {provideAnimations, provideNoopAnimations} from '@angular/platform-browser/animations'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {bootstrapApplication} from '../../src/browser'; @@ -314,6 +316,58 @@ function bootstrap( 'make sure it has the `@Component` decorator.'; expect(() => bootstrapApplication(NonAnnotatedClass)).toThrowError(msg); }); + + describe('with animations', () => { + @Component({ + standalone: true, + selector: 'hello-app', + template: ` +
Hello from AnimationCmp!
`, + animations: [trigger( + 'myAnimation', [transition('void => *', [style({opacity: 1}), animate(5)])])], + }) + class AnimationCmp { + renderer = _inject(ANIMATION_MODULE_TYPE, {optional: true}) ?? 'not found'; + startEvent?: {}; + onStart(event: {}) { + this.startEvent = event; + } + } + + it('should enable animations when using provideAnimations()', async () => { + const appRef = await bootstrapApplication(AnimationCmp, { + providers: [provideAnimations()], + }); + const cmp = appRef.components[0].instance; + + // Wait until animation is completed. + await new Promise(resolve => setTimeout(resolve, 10)); + + expect(cmp.renderer).toBe('BrowserAnimations'); + expect(cmp.startEvent.triggerName).toEqual('myAnimation'); + expect(cmp.startEvent.phaseName).toEqual('start'); + + expect(el.innerText).toBe('Hello from AnimationCmp!'); + }); + + it('should use noop animations renderer when using provideNoopAnimations()', async () => { + const appRef = await bootstrapApplication(AnimationCmp, { + providers: [provideNoopAnimations()], + }); + const cmp = appRef.components[0].instance; + + // Wait until animation is completed. + await new Promise(resolve => setTimeout(resolve, 10)); + + expect(cmp.renderer).toBe('NoopAnimations'); + expect(cmp.startEvent.triggerName).toEqual('myAnimation'); + expect(cmp.startEvent.phaseName).toEqual('start'); + + expect(el.innerText).toBe('Hello from AnimationCmp!'); + }); + }); }); it('should throw if bootstrapped Directive is not a Component', done => {