diff --git a/.circleci/config.yml b/.circleci/config.yml index d86c2e93a985..3fcd7a21c36c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -188,6 +188,8 @@ commands: steps: - run: name: Save month to file + # Note: Make sure this file is excluded in the `.gitignore` as otherwise the + # snapshot stamping would have the `-with-local-changes` suffix. command: date +%Y-%m > month.txt # Normally this would be an individual job instead of a command. diff --git a/.circleci/env.sh b/.circleci/env.sh index 177ac4cfecf1..2dfcdf0e0fb7 100755 --- a/.circleci/env.sh +++ b/.circleci/env.sh @@ -3,7 +3,6 @@ # Variables readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..") readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh"; -readonly bashEnvCachePath="$projectDir/.circleci/bash_env_cache"; # Load helpers and make them available everywhere (through `$BASH_ENV`). source $envHelpersPath; diff --git a/.github/workflows/dev-infra.yml b/.github/workflows/dev-infra.yml index ca29f1be5ad0..3c161d13dbbb 100644 --- a/.github/workflows/dev-infra.yml +++ b/.github/workflows/dev-infra.yml @@ -4,6 +4,10 @@ on: pull_request_target: types: [opened, synchronize, reopened] +# Declare default permissions as read only. +permissions: + contents: read + jobs: labels: runs-on: ubuntu-latest diff --git a/.github/workflows/feature-requests.yml b/.github/workflows/feature-requests.yml index 601fa01ce081..c7025b56d617 100644 --- a/.github/workflows/feature-requests.yml +++ b/.github/workflows/feature-requests.yml @@ -5,6 +5,10 @@ on: # Run at 14:00 every day - cron: '0 14 * * *' +# Declare default permissions as read only. +permissions: + contents: read + jobs: feature_triage: if: github.repository == 'angular/angular' diff --git a/.github/workflows/lock-closed.yml b/.github/workflows/lock-closed.yml index 3e8f0f977057..e4612769f2b5 100644 --- a/.github/workflows/lock-closed.yml +++ b/.github/workflows/lock-closed.yml @@ -5,6 +5,10 @@ on: # Run at 16:00 every day - cron: '0 16 * * *' +# Declare default permissions as read only. +permissions: + contents: read + jobs: lock_closed: if: github.repository == 'angular/angular' diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index c6bd335524f0..ae9e41a65483 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -8,7 +8,8 @@ on: workflow_dispatch: # Declare default permissions as read only. -permissions: read-all +permissions: + contents: read jobs: analysis: diff --git a/.gitignore b/.gitignore index 140aed39fc01..e6081cab0bc9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,10 @@ *.log node_modules +# CircleCI temporary file for cache key computation. +# See `save_month_to_file` in `.circleci/config.yml`. +month.txt + # Include when developing application packages. pubspec.lock .c9 diff --git a/CHANGELOG.md b/CHANGELOG.md index fd0df42c2543..38744f04117a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ + +# 13.2.5 (2022-03-02) +### animations +| Commit | Type | Description | +| -- | -- | -- | +| [6c61d20476](https://github.com/angular/angular/commit/6c61d20476bf214176994dff4e46d6452bd821cf) | fix | allow animations with unsupported CSS properties ([#45185](https://github.com/angular/angular/pull/45185)) | +### common +| Commit | Type | Description | +| -- | -- | -- | +| [64da1daa78](https://github.com/angular/angular/commit/64da1daa7827ca99c6d0b3992ac10b1ea0ec5b4a) | fix | canceled JSONP requests won't throw console error with missing callback function ([#36807](https://github.com/angular/angular/pull/36807)) | +| [56ca7d385b](https://github.com/angular/angular/commit/56ca7d385b263411cf231808fd54ec67ff643b58) | perf | make `NgLocalization` token tree-shakable ([#45118](https://github.com/angular/angular/pull/45118)) ([#45226](https://github.com/angular/angular/pull/45226)) | +### compiler-cli +| Commit | Type | Description | +| -- | -- | -- | +| [6c906a5bb9](https://github.com/angular/angular/commit/6c906a5bb9f7d7c86122bfc36275d6e6b81a0631) | fix | Support resolve animation name from the DTS ([#45169](https://github.com/angular/angular/pull/45169)) | +### core +| Commit | Type | Description | +| -- | -- | -- | +| [e8fd452bd2](https://github.com/angular/angular/commit/e8fd452bd24985f23ab6316690bb9431bbbe8ed8) | fix | remove individual commands for updating gold files ([#45198](https://github.com/angular/angular/pull/45198)) | +| [82d772857c](https://github.com/angular/angular/commit/82d772857ce2d6f07af4c99380676b6bf2cf7912) | perf | make `Compiler`, `ApplicationRef` and `ApplicationInitStatus` tree-shakable ([#45102](https://github.com/angular/angular/pull/45102)) ([#45222](https://github.com/angular/angular/pull/45222)) | +| [71ff12c1cc](https://github.com/angular/angular/commit/71ff12c1cc009e50977b77f1cba1fe03c2f81946) | perf | make `LOCALE_ID` and other tokens from `ApplicationModule` tree-shakable ([#45102](https://github.com/angular/angular/pull/45102)) ([#45222](https://github.com/angular/angular/pull/45222)) | +### localize +| Commit | Type | Description | +| -- | -- | -- | +| [d388522745](https://github.com/angular/angular/commit/d388522745835b8e30b66597560254f0e821c040) | fix | avoid imports into `compiler-cli` package ([#45180](https://github.com/angular/angular/pull/45180)) | +## Special Thanks +Andrew Kushnir, Andrew Scott, Charles Lyding, Guillaume Bonnet, Jessica Janiuk, JoostK, Martin Sikora, Paul Gschwendtner, Theodore Brown, dario-piotrowicz and ivanwonder + + + # 13.2.4 (2022-02-23) ### animations diff --git a/aio/content/examples/animations/e2e/src/app.e2e-spec.ts b/aio/content/examples/animations/e2e/src/app.e2e-spec.ts index b2b2306c30e1..3462e7f29408 100644 --- a/aio/content/examples/animations/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/animations/e2e/src/app.e2e-spec.ts @@ -11,6 +11,8 @@ import { getLinkById, sleepFor } from './util'; import { getComponentSection, getToggleButton } from './querying.po'; describe('Animation Tests', () => { + const routingAnimationDuration = 350; + const openCloseHref = getLinkById('open-close'); const statusSliderHref = getLinkById('status'); const toggleHref = getLinkById('toggle'); @@ -20,6 +22,8 @@ describe('Animation Tests', () => { const heroGroupsHref = getLinkById('hero-groups'); const queryingHref = getLinkById('querying'); + const newPageSleepFor = (ms = 0) => sleepFor(ms + routingAnimationDuration); + beforeAll(() => browser.get('')); describe('Open/Close Component', () => { @@ -28,7 +32,7 @@ describe('Animation Tests', () => { beforeAll(async () => { await openCloseHref.click(); - await sleepFor(); + await newPageSleepFor(300); }); it('should be open', async () => { @@ -84,7 +88,7 @@ describe('Animation Tests', () => { beforeAll(async () => { await statusSliderHref.click(); - await sleepFor(2000); + await newPageSleepFor(2000); }); it('should be inactive with a blue background', async () => { @@ -125,7 +129,7 @@ describe('Animation Tests', () => { describe('Toggle Animations Component', () => { beforeAll(async () => { await toggleHref.click(); - await sleepFor(); + await newPageSleepFor(); }); it('should disabled animations on the child element', async () => { @@ -143,7 +147,7 @@ describe('Animation Tests', () => { describe('Enter/Leave Component', () => { beforeAll(async () => { await enterLeaveHref.click(); - await sleepFor(100); + await newPageSleepFor(100); }); it('should attach a flyInOut trigger to the list of items', async () => { @@ -169,7 +173,7 @@ describe('Animation Tests', () => { describe('Auto Calculation Component', () => { beforeAll(async () => { await autoHref.click(); - await sleepFor(300); + await newPageSleepFor(); }); it('should attach a shrinkOut trigger to the list of items', async () => { @@ -193,7 +197,7 @@ describe('Animation Tests', () => { describe('Filter/Stagger Component', () => { beforeAll(async () => { await filterHref.click(); - await sleepFor(); + await newPageSleepFor(); }); it('should attach a filterAnimations trigger to the list container', async () => { @@ -220,7 +224,7 @@ describe('Animation Tests', () => { describe('Hero Groups Component', () => { beforeAll(async () => { await heroGroupsHref.click(); - await sleepFor(400); + await newPageSleepFor(400); }); it('should attach a flyInOut trigger to the list of items', async () => { @@ -250,7 +254,7 @@ describe('Animation Tests', () => { beforeAll(async () => { await queryingHref.click(); - await sleepFor(queryingAnimationDuration); + await newPageSleepFor(queryingAnimationDuration); }); it('should toggle the section', async () => { @@ -261,14 +265,14 @@ describe('Animation Tests', () => { // toggling off await toggleButton.click(); - await sleepFor(queryingAnimationDuration); + await newPageSleepFor(queryingAnimationDuration); expect(await section.isPresent()).toBe(false); // toggling on await toggleButton.click(); - await sleepFor(queryingAnimationDuration); + await newPageSleepFor(queryingAnimationDuration); expect(await section.isPresent()).toBe(true); - await sleepFor(queryingAnimationDuration); + await newPageSleepFor(queryingAnimationDuration); }); it(`should disable the button for the animation's duration`, async () => { @@ -278,13 +282,13 @@ describe('Animation Tests', () => { // toggling off await toggleButton.click(); expect(await toggleButton.isEnabled()).toBe(false); - await sleepFor(queryingAnimationDuration); + await newPageSleepFor(queryingAnimationDuration); expect(await toggleButton.isEnabled()).toBe(true); // toggling on await toggleButton.click(); expect(await toggleButton.isEnabled()).toBe(false); - await sleepFor(queryingAnimationDuration); + await newPageSleepFor(queryingAnimationDuration); expect(await toggleButton.isEnabled()).toBe(true); }); diff --git a/aio/content/examples/animations/src/app/app.component.html b/aio/content/examples/animations/src/app/app.component.html index 50195d3f5639..3082ece03ed8 100644 --- a/aio/content/examples/animations/src/app/app.component.html +++ b/aio/content/examples/animations/src/app/app.component.html @@ -21,7 +21,7 @@

Animations

-
- +
+
diff --git a/aio/content/examples/animations/src/app/app.component.ts b/aio/content/examples/animations/src/app/app.component.ts index 2c79714b5d74..39b2c72e6b24 100644 --- a/aio/content/examples/animations/src/app/app.component.ts +++ b/aio/content/examples/animations/src/app/app.component.ts @@ -11,7 +11,7 @@ import { } from '@angular/animations'; // #enddocregion imports -import { RouterOutlet } from '@angular/router'; +import { ChildrenOutletContexts, RouterOutlet } from '@angular/router'; import { slideInAnimation } from './animations'; // #docregion decorator, toggle-app-animations, define @@ -34,12 +34,13 @@ export class AppComponent { public animationsDisabled = false; // #enddocregion toggle-app-animations -// #docregion prepare-router-outlet - prepareRoute(outlet: RouterOutlet) { - return outlet?.activatedRouteData?.['animation']; - } +// #docregion get-route-animations-data + constructor(private contexts: ChildrenOutletContexts) {} -// #enddocregion prepare-router-outlet + getRouteAnimationData() { + return this.contexts.getContext('primary')?.route?.snapshot?.data?.['animation']; + } +// #enddocregion get-route-animations-data toggleAnimations() { this.animationsDisabled = !this.animationsDisabled; diff --git a/aio/content/examples/http/src/app/app.component.css b/aio/content/examples/http/src/app/app.component.css new file mode 100644 index 000000000000..ea5fdfb1cdac --- /dev/null +++ b/aio/content/examples/http/src/app/app.component.css @@ -0,0 +1,16 @@ +.checkboxes { + display: flex; + flex-wrap: wrap; + margin-bottom: 2rem; +} + +.checkboxes > label { + flex: 0 0 max-content; + margin: 0.5rem; + cursor: pointer; +} + +section:not(:last-of-type) { + padding-bottom: 2rem; + border-bottom: 1px solid black; +} diff --git a/aio/content/examples/http/src/app/app.component.html b/aio/content/examples/http/src/app/app.component.html index dd5ae8926e3c..1a16ba0ed062 100644 --- a/aio/content/examples/http/src/app/app.component.html +++ b/aio/content/examples/http/src/app/app.component.html @@ -1,29 +1,46 @@

HttpClient Example

-
- - +
+ - - + - - + - - + - - +
- -
- -
- -
- -
- -
- +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
diff --git a/aio/content/examples/http/src/app/app.component.ts b/aio/content/examples/http/src/app/app.component.ts index 655b55027610..09cd2020eee4 100644 --- a/aio/content/examples/http/src/app/app.component.ts +++ b/aio/content/examples/http/src/app/app.component.ts @@ -2,7 +2,8 @@ import { Component } from '@angular/core'; @Component({ selector: 'app-root', - templateUrl: './app.component.html' + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] }) export class AppComponent { showHeroes = true; @@ -16,4 +17,4 @@ export class AppComponent { toggleDownloader() { this.showDownloader = !this.showDownloader; } toggleUploader() { this.showUploader = !this.showUploader; } toggleSearch() { this.showSearch = !this.showSearch; } - } +} diff --git a/aio/content/examples/http/src/app/heroes/heroes.component.css b/aio/content/examples/http/src/app/heroes/heroes.component.css index d5a402a03974..ec5cb40d5655 100644 --- a/aio/content/examples/http/src/app/heroes/heroes.component.css +++ b/aio/content/examples/http/src/app/heroes/heroes.component.css @@ -9,57 +9,71 @@ } .heroes li { - display: flex; - width: 100%; + display: grid; + grid-template-columns: min-content max-content 1fr max-content max-content; + grid-template-areas: "badge name . edit delete"; + margin: 1rem 0; + align-items: center; + background-color: #eee; + border-radius: 4px; } -.heroes li:hover { - left: .1em; +.heroes li .badge { + grid-area: badge; } -.heroes button.delete { - color: black; - display: block; - font-size: 1.2rem; - background-color: #eee; - border-radius: 0 4px 4px 0; +.heroes li .name { + grid-area: name; +} + +.heroes li button { + margin: 0.3rem; + padding: 0.3rem 0.8rem; +} + +.heroes li button.edit { + grid-area: edit; +} + +.heroes li button.delete { + grid-area: delete; } -.heroes a:hover { - color: #2c3a41; - background-color: #e6e6e6; +.heroes li button:hover, .heros li button:focus { + color: #eee; + background-color: #000; } .heroes .badge { - padding: .5em .6em; + padding: 0.5rem 0.6rem; color: white; background-color: #435B60; - min-width: 16px; - margin-right: .8em; + margin-right: 0.8rem; border-radius: 4px 0 0 4px; -} - -.heroes button.edit { - flex: 1; - text-align: left; - padding: 0; - margin-right: 0; + align-self: stretch; display: flex; align-items: center; - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -button.delete { - right: -8px; - top: 5px; - background-color: gray; - color: white; - padding: 5px 8px; - width: 2em; } .heroes input { max-width: 12rem; padding: .25rem; } + +@media (max-width: 550px) { + .heroes li { + grid-template-columns: min-content 1fr 1fr; + grid-template-rows: 2rem max-content; + grid-template-areas: + "badge name name" + "badge edit delete"; + } + + .heroes li .name { + margin-top: 0.3rem; + } + + .heroes li button { + justify-self: center; + } +} \ No newline at end of file diff --git a/aio/content/examples/http/src/app/heroes/heroes.component.html b/aio/content/examples/http/src/app/heroes/heroes.component.html index 3e9fb41478cb..75b3f81363fb 100644 --- a/aio/content/examples/http/src/app/heroes/heroes.component.html +++ b/aio/content/examples/http/src/app/heroes/heroes.component.html @@ -1,29 +1,29 @@

Heroes

diff --git a/aio/content/examples/http/src/app/heroes/heroes.component.ts b/aio/content/examples/http/src/app/heroes/heroes.component.ts index ea0a0f263c3f..79124cfeebbd 100644 --- a/aio/content/examples/http/src/app/heroes/heroes.component.ts +++ b/aio/content/examples/http/src/app/heroes/heroes.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { Hero } from './hero'; import { HeroesService } from './heroes.service'; @@ -12,9 +12,17 @@ import { HeroesService } from './heroes.service'; export class HeroesComponent implements OnInit { heroes: Hero[] = []; editHero: Hero | undefined; // the hero currently being edited + heroName = ''; constructor(private heroesService: HeroesService) {} + @ViewChild('heroEditInput') + set heroEditInput(element: ElementRef) { + if (element) { + element.nativeElement.focus(); + } + } + ngOnInit() { this.getHeroes(); } @@ -55,8 +63,9 @@ export class HeroesComponent implements OnInit { */ } - edit(hero: Hero) { - this.editHero = hero; + edit(heroName: string) { + this.update(heroName); + this.editHero = undefined; } search(searchTerm: string) { @@ -65,13 +74,15 @@ export class HeroesComponent implements OnInit { this.heroesService .searchHeroes(searchTerm) .subscribe(heroes => (this.heroes = heroes)); + } else { + this.getHeroes(); } } - update() { - if (this.editHero) { + update(heroName: string) { + if (heroName && this.editHero && this.editHero.name !== heroName) { this.heroesService - .updateHero(this.editHero) + .updateHero({...this.editHero, name: heroName}) .subscribe(hero => { // replace the hero in the heroes list with update from server const ix = hero ? this.heroes.findIndex(h => h.id === hero.id) : -1; diff --git a/aio/content/examples/http/src/app/messages/messages.component.css b/aio/content/examples/http/src/app/messages/messages.component.css new file mode 100644 index 000000000000..9c0f3c715633 --- /dev/null +++ b/aio/content/examples/http/src/app/messages/messages.component.css @@ -0,0 +1,9 @@ +:host { + display: block; + padding: 1rem; + background-color: rgb(255, 245, 187); +} + +h2 { + margin: 0; +} diff --git a/aio/content/examples/http/src/app/messages/messages.component.html b/aio/content/examples/http/src/app/messages/messages.component.html index 92dafa8cd7c2..5d05ac003275 100644 --- a/aio/content/examples/http/src/app/messages/messages.component.html +++ b/aio/content/examples/http/src/app/messages/messages.component.html @@ -1,8 +1,8 @@ -
-

Messages

+

Messages

+
  1. {{message}}
-
+ diff --git a/aio/content/examples/http/src/app/messages/messages.component.ts b/aio/content/examples/http/src/app/messages/messages.component.ts index d54a1216651c..034c0ac0afc6 100644 --- a/aio/content/examples/http/src/app/messages/messages.component.ts +++ b/aio/content/examples/http/src/app/messages/messages.component.ts @@ -3,7 +3,8 @@ import { MessageService } from '../message.service'; @Component({ selector: 'app-messages', - templateUrl: './messages.component.html' + templateUrl: './messages.component.html', + styleUrls: ['./messages.component.css'] }) export class MessagesComponent { constructor(public messageService: MessageService) {} diff --git a/aio/content/examples/i18n/angular.json b/aio/content/examples/i18n/angular.json index cbcc13ff4244..b708244e8380 100644 --- a/aio/content/examples/i18n/angular.json +++ b/aio/content/examples/i18n/angular.json @@ -44,13 +44,8 @@ "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "src/styles.css" - ], + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.css"], "scripts": [], // #docregion missing-translation-error "i18nMissingTranslation": "error" @@ -82,6 +77,7 @@ "outputHashing": "all" }, "development": { + "localize": false, "buildOptimizer": false, "optimization": false, "vendorChunk": true, @@ -91,9 +87,7 @@ }, // #docregion build-single-locale, build-production-french "fr": { - "localize": [ - "fr" - ] + "localize": ["fr"] } }, // #enddocregion build-single-locale, build-production-french @@ -113,7 +107,7 @@ }, // #docregion build-single-locale, build-production-french "fr": { - "browserTarget": "angular.io-example:build:fr" + "browserTarget": "angular.io-example:build:development,fr" } }, // #enddocregion build-single-locale, build-production-french @@ -134,13 +128,8 @@ "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.spec.json", "karmaConfig": "karma.conf.js", - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "src/styles.css" - ], + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.css"], "scripts": [] } }, diff --git a/aio/content/examples/router/src/app/app.component.2.html b/aio/content/examples/router/src/app/app.component.2.html index 51e62b5bd178..a361c1d7e14f 100644 --- a/aio/content/examples/router/src/app/app.component.2.html +++ b/aio/content/examples/router/src/app/app.component.2.html @@ -4,6 +4,6 @@

Angular Router

Crisis Center Heroes -
- +
+
\ No newline at end of file diff --git a/aio/content/examples/router/src/app/app.component.2.ts b/aio/content/examples/router/src/app/app.component.2.ts index 533022096e94..8e0a63362d99 100644 --- a/aio/content/examples/router/src/app/app.component.2.ts +++ b/aio/content/examples/router/src/app/app.component.2.ts @@ -2,7 +2,7 @@ // #docregion import { Component } from '@angular/core'; // #docregion animation-imports -import { RouterOutlet } from '@angular/router'; +import { ChildrenOutletContexts } from '@angular/router'; import { slideInAnimation } from './animations'; @Component({ @@ -14,8 +14,10 @@ import { slideInAnimation } from './animations'; // #enddocregion animation-imports // #docregion function-binding export class AppComponent { - getAnimationData(outlet: RouterOutlet) { - return outlet?.activatedRouteData?.['animation']; + constructor(private contexts: ChildrenOutletContexts) {} + + getAnimationData() { + return this.contexts.getContext('primary')?.route?.snapshot?.data?.['animation']; } } // #enddocregion function-binding diff --git a/aio/content/examples/router/src/app/app.component.4.html b/aio/content/examples/router/src/app/app.component.4.html index 4a32aedb34c2..1db98d81a9e9 100644 --- a/aio/content/examples/router/src/app/app.component.4.html +++ b/aio/content/examples/router/src/app/app.component.4.html @@ -8,8 +8,8 @@

Angular Router

-
- +
+
\ No newline at end of file diff --git a/aio/content/examples/router/src/app/app.component.5.html b/aio/content/examples/router/src/app/app.component.5.html index 4cc8c91ffc7c..a5c392a60257 100644 --- a/aio/content/examples/router/src/app/app.component.5.html +++ b/aio/content/examples/router/src/app/app.component.5.html @@ -6,7 +6,7 @@

Angular Router

Admin Contact -
- +
+
\ No newline at end of file diff --git a/aio/content/examples/router/src/app/app.component.6.html b/aio/content/examples/router/src/app/app.component.6.html index b082558ecb34..48cc9ba0bbc3 100644 --- a/aio/content/examples/router/src/app/app.component.6.html +++ b/aio/content/examples/router/src/app/app.component.6.html @@ -7,7 +7,7 @@

Angular Router

Login Contact -
- +
+
\ No newline at end of file diff --git a/aio/content/examples/router/src/app/app.component.html b/aio/content/examples/router/src/app/app.component.html index eff3c6d5d259..2cbe855b524b 100644 --- a/aio/content/examples/router/src/app/app.component.html +++ b/aio/content/examples/router/src/app/app.component.html @@ -8,8 +8,8 @@

Angular Router

Login Contact -
- +
+
diff --git a/aio/content/examples/router/src/app/app.component.ts b/aio/content/examples/router/src/app/app.component.ts index b3dd99b684ea..58f6c90c6642 100644 --- a/aio/content/examples/router/src/app/app.component.ts +++ b/aio/content/examples/router/src/app/app.component.ts @@ -1,7 +1,7 @@ // #docplaster // #docregion import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; +import { ChildrenOutletContexts } from '@angular/router'; import { slideInAnimation } from './animations'; @Component({ @@ -11,7 +11,9 @@ import { slideInAnimation } from './animations'; animations: [ slideInAnimation ] }) export class AppComponent { - getAnimationData(outlet: RouterOutlet) { - return outlet?.activatedRouteData?.['animation']; + constructor(private contexts: ChildrenOutletContexts) {} + + getRouteAnimationData() { + return this.contexts.getContext('primary')?.route?.snapshot?.data?.['animation']; } } diff --git a/aio/content/guide/example-apps-list.md b/aio/content/guide/example-apps-list.md index 10456e327994..048a2af15f26 100644 --- a/aio/content/guide/example-apps-list.md +++ b/aio/content/guide/example-apps-list.md @@ -430,6 +430,7 @@ Demonstrates Angular's fundamental routing techniques. For more information, see [Using Angular routes in a single-page application](guide/router-tutorial). +## Documentation ### Style guide for Documentation contributions diff --git a/aio/content/guide/route-animations.md b/aio/content/guide/route-animations.md index b41cbab7fe5d..2c99bc73325c 100644 --- a/aio/content/guide/route-animations.md +++ b/aio/content/guide/route-animations.md @@ -59,15 +59,15 @@ In addition to `path` and `component`, the `data` property of each route defines After configuring the routes, add a `` inside the root `AppComponent` template. The `` directive tells the Angular router where to render the views when matched with a route. -The `` directive holds the custom data set for the currently active route which can be accessed via the directive's `activatedRouteData` property, we can use such data to animate our routing transitions. +The `ChildrenOutletContexts` holds information about outlets and activated routes. We can use the `data` property of each `Route` to animate our routing transitions. `AppComponent` defines a method that can detect when a view changes. The method assigns an animation state value to the animation trigger (`@routeAnimation`) based on the route configuration `data` property value. Here's an example of an `AppComponent` method that detects when a route change happens. - + -Here, the `prepareRoute()` method takes the value of the outlet directive (established through `#outlet="outlet"`) and returns a string value representing the state of the animation based on the custom data of the current active route. Use this data to control which transition to execute for each route. +Here, the `getRouteAnimationData()` method takes the value of the outlet and returns a string which represents the state of the animation based on the custom data of the current active route. Use this data to control which transition to execute for each route. ## Animation definition diff --git a/aio/content/guide/router-tutorial-toh.md b/aio/content/guide/router-tutorial-toh.md index a2cacc9d1500..332feddee411 100644 --- a/aio/content/guide/router-tutorial-toh.md +++ b/aio/content/guide/router-tutorial-toh.md @@ -1225,8 +1225,8 @@ This example uses a variable of `routerOutlet`. -The `@routeAnimation` property is bound to the `getAnimationData()` with the provided `routerOutlet` reference, so the next step is to define that function in the `AppComponent`. -The `getAnimationData()` function returns the animation property from the `data` provided through the `ActivatedRoute`. The `animation` property matches the `transition` names you used in the `slideInAnimation` defined in `animations.ts`. +The `@routeAnimation` property is bound to the `getAnimationData()` which returns the animation property from the `data` provided by the primary route. The `animation` property matches the `transition` names you used in the `slideInAnimation` defined in `animations.ts`. + diff --git a/aio/tools/examples/shared/boilerplate/i18n/package.json b/aio/tools/examples/shared/boilerplate/i18n/package.json index ba65cc42c7de..804074a5947f 100644 --- a/aio/tools/examples/shared/boilerplate/i18n/package.json +++ b/aio/tools/examples/shared/boilerplate/i18n/package.json @@ -8,7 +8,6 @@ "start": "ng serve", "start:fr": "ng serve --configuration=fr", "build": "ng build", - "build:fr": "ng build --configuration=production-fr", "watch": "ng build --watch --configuration development", "test": "ng test", "e2e": "ng e2e", diff --git a/goldens/public-api/common/common.md b/goldens/public-api/common/common.md index 878431650b5f..78a90f2c9dbd 100644 --- a/goldens/public-api/common/common.md +++ b/goldens/public-api/common/common.md @@ -503,6 +503,10 @@ export class NgLocaleLocalization extends NgLocalization { export abstract class NgLocalization { // (undocumented) abstract getPluralCategory(value: any, locale?: string): string; + // (undocumented) + static ɵfac: i0.ɵɵFactoryDeclaration; + // (undocumented) + static ɵprov: i0.ɵɵInjectableDeclaration; } // @public diff --git a/goldens/size-tracking/aio-payloads.json b/goldens/size-tracking/aio-payloads.json index 96cfbaaf1021..8d03c28c7ac4 100755 --- a/goldens/size-tracking/aio-payloads.json +++ b/goldens/size-tracking/aio-payloads.json @@ -15,7 +15,7 @@ "master": { "uncompressed": { "runtime": 4343, - "main": 451244, + "main": 450239, "polyfills": 37297, "styles": 70379, "light-theme": 77582, diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json index e4c2eee5906c..a8ccddce36dc 100644 --- a/goldens/size-tracking/integration-payloads.json +++ b/goldens/size-tracking/integration-payloads.json @@ -3,7 +3,7 @@ "master": { "uncompressed": { "runtime": 1083, - "main": 127944, + "main": 124282, "polyfills": 37226 } } @@ -24,7 +24,7 @@ "master": { "uncompressed": { "runtime": 1105, - "main": 133608, + "main": 131270, "polyfills": 37248 } } @@ -33,7 +33,7 @@ "master": { "uncompressed": { "runtime": 929, - "main": 126275, + "main": 123958, "polyfills": 37933 } } @@ -42,7 +42,7 @@ "master": { "uncompressed": { "runtime": 2835, - "main": 231989, + "main": 230780, "polyfills": 37244, "src_app_lazy_lazy_module_ts": 795 } @@ -52,7 +52,7 @@ "master": { "uncompressed": { "runtime": 1063, - "main": 159637, + "main": 155607, "polyfills": 36975 } } @@ -61,7 +61,7 @@ "master": { "uncompressed": { "runtime": 1070, - "main": 159755, + "main": 155563, "polyfills": 37242 } } diff --git a/integration/typings_test_ts44/include-all.ts b/integration/typings_test_ts44/include-all.ts index 86b57f2789d8..925e0ef07853 100644 --- a/integration/typings_test_ts44/include-all.ts +++ b/integration/typings_test_ts44/include-all.ts @@ -21,6 +21,7 @@ import * as core from '@angular/core'; import * as coreTesting from '@angular/core/testing'; import * as elements from '@angular/elements'; import * as forms from '@angular/forms'; +import * as localize from '@angular/localize'; import * as platformBrowser from '@angular/platform-browser'; import * as platformBrowserDynamic from '@angular/platform-browser-dynamic'; import * as platformBrowserDynamicTesting from '@angular/platform-browser-dynamic/testing'; @@ -51,6 +52,7 @@ export default { coreTesting, elements, forms, + localize, platformBrowser, platformBrowserTesting, platformBrowserDynamic, diff --git a/integration/typings_test_ts44/package.json b/integration/typings_test_ts44/package.json index 11b24fd1a827..9a4bcb938a03 100644 --- a/integration/typings_test_ts44/package.json +++ b/integration/typings_test_ts44/package.json @@ -11,6 +11,7 @@ "@angular/core": "file:../../dist/packages-dist/core", "@angular/elements": "file:../../dist/packages-dist/elements", "@angular/forms": "file:../../dist/packages-dist/forms", + "@angular/localize": "file:../../dist/packages-dist/localize", "@angular/platform-browser": "file:../../dist/packages-dist/platform-browser", "@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic", "@angular/platform-server": "file:../../dist/packages-dist/platform-server", diff --git a/integration/typings_test_ts45/include-all.ts b/integration/typings_test_ts45/include-all.ts index 86b57f2789d8..925e0ef07853 100644 --- a/integration/typings_test_ts45/include-all.ts +++ b/integration/typings_test_ts45/include-all.ts @@ -21,6 +21,7 @@ import * as core from '@angular/core'; import * as coreTesting from '@angular/core/testing'; import * as elements from '@angular/elements'; import * as forms from '@angular/forms'; +import * as localize from '@angular/localize'; import * as platformBrowser from '@angular/platform-browser'; import * as platformBrowserDynamic from '@angular/platform-browser-dynamic'; import * as platformBrowserDynamicTesting from '@angular/platform-browser-dynamic/testing'; @@ -51,6 +52,7 @@ export default { coreTesting, elements, forms, + localize, platformBrowser, platformBrowserTesting, platformBrowserDynamic, diff --git a/integration/typings_test_ts45/package.json b/integration/typings_test_ts45/package.json index ee8b39d77c3d..4f8eed6b8573 100644 --- a/integration/typings_test_ts45/package.json +++ b/integration/typings_test_ts45/package.json @@ -11,6 +11,7 @@ "@angular/core": "file:../../dist/packages-dist/core", "@angular/elements": "file:../../dist/packages-dist/elements", "@angular/forms": "file:../../dist/packages-dist/forms", + "@angular/localize": "file:../../dist/packages-dist/localize", "@angular/platform-browser": "file:../../dist/packages-dist/platform-browser", "@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic", "@angular/platform-server": "file:../../dist/packages-dist/platform-server", diff --git a/package.json b/package.json index ec6a4845c1a4..3c4971377bb4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-srcs", - "version": "13.2.4", + "version": "13.2.5", "private": true, "description": "Angular - a web framework for modern web apps", "homepage": "https://github.com/angular/angular", @@ -51,13 +51,14 @@ "@angular/platform-browser-dynamic-12": "npm:@angular/platform-browser-dynamic@12.2.13", "@angular/platform-server-12": "npm:@angular/platform-server@12.2.13", "@angular/router-12": "npm:@angular/router@12.2.13", - "@babel/cli": "7.17.0", - "@babel/core": "7.8.6", - "@babel/generator": "7.8.6", - "@babel/parser": "7.9.4", - "@babel/preset-env": "7.10.2", - "@babel/template": "7.8.6", - "@babel/traverse": "7.8.6", + "@babel/cli": "7.17.3", + "@babel/core": "7.17.5", + "@babel/generator": "7.17.3", + "@babel/parser": "7.17.3", + "@babel/preset-env": "7.16.11", + "@babel/template": "7.16.7", + "@babel/traverse": "7.17.3", + "@babel/types": "7.17.0", "@bazel/concatjs": "4.6.0", "@bazel/esbuild": "4.6.0", "@bazel/jasmine": "4.6.0", @@ -72,10 +73,10 @@ "@rollup/plugin-node-resolve": "^13.0.4", "@schematics/angular": "13.2.3", "@types/angular": "^1.6.47", - "@types/babel__core": "7.1.6", - "@types/babel__generator": "7.6.1", - "@types/babel__template": "7.0.2", - "@types/babel__traverse": "7.0.9", + "@types/babel__core": "7.1.18", + "@types/babel__generator": "7.6.4", + "@types/babel__template": "7.4.1", + "@types/babel__traverse": "7.14.2", "@types/base64-js": "1.3.0", "@types/bluebird": "^3.5.27", "@types/convert-source-map": "^1.5.1", @@ -192,19 +193,11 @@ }, "// 4": "Overwrite graceful-fs to a version that does not rely on the 'natives' package. This fixes gulp for >= 10.13, more information: #28213", "// 5": "Ensure a single version of webdriver-manager so it is hoisted as the integration tests depend on it being found at ../../node_modules/webdriver-manager", - "// 6": "Ensure that `@babel/*` packages match the below versions to avoid conflicts with `types/babel__*`", - "// 7": "Ensure that transitive dependencies on `https-proxy-agent` are at minimum v5 as older versions patch NodeJS directly, breaking tools like webdriver which is used by the karma-sauce-launcher as an example.", - "// 8": "Ensure that a single instance of the `saucelabs` package is used. Protractor and the Karma sauce launcher pull this package as dependency. A single instance allows for e.g. easier patching in the Karma config.", + "// 6": "Ensure that transitive dependencies on `https-proxy-agent` are at minimum v5 as older versions patch NodeJS directly, breaking tools like webdriver which is used by the karma-sauce-launcher as an example.", + "// 7": "Ensure that a single instance of the `saucelabs` package is used. Protractor and the Karma sauce launcher pull this package as dependency. A single instance allows for e.g. easier patching in the Karma config.", "resolutions": { "**/graceful-fs": "4.2.9", "**/webdriver-manager": "12.1.8", - "@babel/core": "7.8.6", - "@babel/generator": "7.8.6", - "@babel/parser": "7.9.4", - "@babel/preset-env": "7.10.2", - "@babel/template": "7.8.6", - "@babel/traverse": "7.8.6", - "@babel/types": "7.8.6", "**/https-proxy-agent": "5.0.0", "**/saucelabs": "7.1.3" } diff --git a/packages/animations/browser/src/dsl/animation.ts b/packages/animations/browser/src/dsl/animation.ts index f861efe91203..ba51897ac6da 100644 --- a/packages/animations/browser/src/dsl/animation.ts +++ b/packages/animations/browser/src/dsl/animation.ts @@ -10,6 +10,7 @@ import {AnimationMetadata, AnimationMetadataType, AnimationOptions, ɵStyleData} import {buildingFailed, validationFailed} from '../error_helpers'; import {AnimationDriver} from '../render/animation_driver'; import {ENTER_CLASSNAME, LEAVE_CLASSNAME, normalizeStyles} from '../util'; +import {warnValidation} from '../warning_helpers'; import {Ast} from './animation_ast'; import {buildAnimationAst} from './animation_ast_builder'; @@ -21,10 +22,14 @@ export class Animation { private _animationAst: Ast; constructor(private _driver: AnimationDriver, input: AnimationMetadata|AnimationMetadata[]) { const errors: Error[] = []; - const ast = buildAnimationAst(_driver, input, errors); + const warnings: string[] = []; + const ast = buildAnimationAst(_driver, input, errors, warnings); if (errors.length) { throw validationFailed(errors); } + if (warnings.length) { + warnValidation(warnings); + } this._animationAst = ast; } diff --git a/packages/animations/browser/src/dsl/animation_ast_builder.ts b/packages/animations/browser/src/dsl/animation_ast_builder.ts index fb49c29ea325..7b708533fbe2 100644 --- a/packages/animations/browser/src/dsl/animation_ast_builder.ts +++ b/packages/animations/browser/src/dsl/animation_ast_builder.ts @@ -11,6 +11,7 @@ import {invalidDefinition, invalidKeyframes, invalidOffset, invalidParallelAnima import {AnimationDriver} from '../render/animation_driver'; import {getOrSetAsInMap} from '../render/shared'; import {copyObj, extractStyleParams, iteratorToArray, NG_ANIMATING_SELECTOR, NG_TRIGGER_SELECTOR, normalizeAnimationEntry, resolveTiming, SUBSTITUTION_EXPR_START, validateStyleParams, visitDslNode} from '../util'; +import {pushUnrecognizedPropertiesWarning} from '../warning_helpers'; import {AnimateAst, AnimateChildAst, AnimateRefAst, Ast, DynamicTimingAst, GroupAst, KeyframesAst, QueryAst, ReferenceAst, SequenceAst, StaggerAst, StateAst, StyleAst, TimingAst, TransitionAst, TriggerAst} from './animation_ast'; import {AnimationDslVisitor} from './animation_dsl_visitor'; @@ -56,9 +57,9 @@ const SELF_TOKEN_REGEX = new RegExp(`\s*${SELF_TOKEN}\s*,?`, 'g'); * Otherwise an error will be thrown. */ export function buildAnimationAst( - driver: AnimationDriver, metadata: AnimationMetadata|AnimationMetadata[], - errors: Error[]): Ast { - return new AnimationAstBuilderVisitor(driver).build(metadata, errors); + driver: AnimationDriver, metadata: AnimationMetadata|AnimationMetadata[], errors: Error[], + warnings: string[]): Ast { + return new AnimationAstBuilderVisitor(driver).build(metadata, errors, warnings); } const ROOT_SELECTOR = ''; @@ -66,12 +67,20 @@ const ROOT_SELECTOR = ''; export class AnimationAstBuilderVisitor implements AnimationDslVisitor { constructor(private _driver: AnimationDriver) {} - build(metadata: AnimationMetadata|AnimationMetadata[], errors: Error[]): + build(metadata: AnimationMetadata|AnimationMetadata[], errors: Error[], warnings: string[]): Ast { const context = new AnimationAstBuilderContext(errors); this._resetContextStyleTimingState(context); - return >visitDslNode( - this, normalizeAnimationEntry(metadata), context); + const ast = + >visitDslNode(this, normalizeAnimationEntry(metadata), context); + + if (context.unsupportedCSSPropertiesFound.size) { + pushUnrecognizedPropertiesWarning( + warnings, + [...context.unsupportedCSSPropertiesFound.keys()], + ); + } + return ast; } private _resetContextStyleTimingState(context: AnimationAstBuilderContext) { @@ -303,7 +312,8 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor { Object.keys(tuple).forEach(prop => { if (!this._driver.validateStyleProperty(prop)) { - context.errors.push(invalidProperty(prop)); + delete tuple[prop]; + context.unsupportedCSSPropertiesFound.add(prop); return; } @@ -507,6 +517,7 @@ export class AnimationAstBuilderContext { public currentTime: number = 0; public collectedStyles: {[selectorName: string]: {[propName: string]: StyleTimeTuple}} = {}; public options: AnimationOptions|null = null; + public unsupportedCSSPropertiesFound: Set = new Set(); constructor(public errors: Error[]) {} } diff --git a/packages/animations/browser/src/render/animation_engine_next.ts b/packages/animations/browser/src/render/animation_engine_next.ts index 2202743e87fa..f34f17273d4a 100644 --- a/packages/animations/browser/src/render/animation_engine_next.ts +++ b/packages/animations/browser/src/render/animation_engine_next.ts @@ -12,6 +12,7 @@ import {buildAnimationAst} from '../dsl/animation_ast_builder'; import {AnimationTrigger, buildTrigger} from '../dsl/animation_trigger'; import {AnimationStyleNormalizer} from '../dsl/style_normalization/animation_style_normalizer'; import {triggerBuildFailed} from '../error_helpers'; +import {warnTriggerBuild} from '../warning_helpers'; import {AnimationDriver} from './animation_driver'; import {parseTimelineCommand} from './shared'; @@ -44,11 +45,15 @@ export class AnimationEngine { let trigger = this._triggerCache[cacheKey]; if (!trigger) { const errors: Error[] = []; - const ast = - buildAnimationAst(this._driver, metadata as AnimationMetadata, errors) as TriggerAst; + const warnings: string[] = []; + const ast = buildAnimationAst( + this._driver, metadata as AnimationMetadata, errors, warnings) as TriggerAst; if (errors.length) { throw triggerBuildFailed(name, errors); } + if (warnings.length) { + warnTriggerBuild(name, warnings); + } trigger = buildTrigger(name, ast, this._normalizer); this._triggerCache[cacheKey] = trigger; } diff --git a/packages/animations/browser/src/render/timeline_animation_engine.ts b/packages/animations/browser/src/render/timeline_animation_engine.ts index 77469cfebdb0..2aa999c68d7a 100644 --- a/packages/animations/browser/src/render/timeline_animation_engine.ts +++ b/packages/animations/browser/src/render/timeline_animation_engine.ts @@ -15,6 +15,7 @@ import {ElementInstructionMap} from '../dsl/element_instruction_map'; import {AnimationStyleNormalizer} from '../dsl/style_normalization/animation_style_normalizer'; import {createAnimationFailed, missingOrDestroyedAnimation, missingPlayer, registerFailed} from '../error_helpers'; import {ENTER_CLASSNAME, LEAVE_CLASSNAME} from '../util'; +import {warnRegister} from '../warning_helpers'; import {AnimationDriver} from './animation_driver'; import {getOrSetAsInMap, listenOnPlayer, makeAnimationEvent, normalizeKeyframes, optimizeGroupPlayer} from './shared'; @@ -32,10 +33,14 @@ export class TimelineAnimationEngine { register(id: string, metadata: AnimationMetadata|AnimationMetadata[]) { const errors: Error[] = []; - const ast = buildAnimationAst(this._driver, metadata, errors); + const warnings: string[] = []; + const ast = buildAnimationAst(this._driver, metadata, errors, warnings); if (errors.length) { throw registerFailed(errors); } else { + if (warnings.length) { + warnRegister(warnings); + } this._animations[id] = ast; } } diff --git a/packages/animations/browser/src/warning_helpers.ts b/packages/animations/browser/src/warning_helpers.ts new file mode 100644 index 000000000000..d93ca15dd7ef --- /dev/null +++ b/packages/animations/browser/src/warning_helpers.ts @@ -0,0 +1,43 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * 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 + */ + +const NG_DEV_MODE = typeof ngDevMode === 'undefined' || !!ngDevMode; + +function createListOfWarnings(warnings: string[]): string { + const LINE_START = '\n - '; + return `${LINE_START}${warnings.filter(Boolean).map(warning => warning).join(LINE_START)}`; +} + +export function warnValidation(warnings: string[]): void { + NG_DEV_MODE && console.warn(`animation validation warnings:${createListOfWarnings(warnings)}`); +} + +export function warnTriggerBuild(name: string, warnings: string[]): void { + NG_DEV_MODE && + console.warn(`The animation trigger "${name}" has built with the following warnings:${ + createListOfWarnings(warnings)}`); +} + +export function warnRegister(warnings: string[]): void { + NG_DEV_MODE && + console.warn(`Animation built with the following warnings:${createListOfWarnings(warnings)}`); +} + +export function triggerParsingWarnings(name: string, warnings: string[]): void { + NG_DEV_MODE && + console.warn(`Animation parsing for the ${name} trigger presents the following warnings:${ + createListOfWarnings(warnings)}`); +} + +export function pushUnrecognizedPropertiesWarning(warnings: string[], props: string[]): void { + if (ngDevMode && props.length) { + warnings.push( + `The provided CSS properties are not recognized properties supported for animations: ${ + props.join(', ')}`); + } +} diff --git a/packages/animations/browser/test/dsl/animation_spec.ts b/packages/animations/browser/test/dsl/animation_spec.ts index 330d03ecbcba..c76b4afdaa65 100644 --- a/packages/animations/browser/test/dsl/animation_spec.ts +++ b/packages/animations/browser/test/dsl/animation_spec.ts @@ -218,16 +218,28 @@ function createDiv() { /state\("panfinal", ...\) must define default values for all the following style substitutions: greyColor/); }); - it('should throw an error if an invalid CSS property is used in the animation', () => { + it('should provide a warning if an invalid CSS property is used in the animation', () => { const steps = [animate(1000, style({abc: '500px'}))]; - expect(() => { - validateAndThrowAnimationSequence(steps); - }) - .toThrowError( - /The provided animation property "abc" is not a supported CSS property for animations/); + expect(getValidationWarningsForAnimationSequence(steps)).toEqual([ + 'The provided CSS properties are not recognized properties supported for animations: abc' + ]); }); + it('should provide a warning if multiple invalid CSS properties are used in the animation', + () => { + const steps = [ + state('state', style({ + '123': '100px', + })), + style({abc: '200px'}), animate(1000, style({xyz: '300px'})) + ]; + + expect(getValidationWarningsForAnimationSequence(steps)).toEqual([ + 'The provided CSS properties are not recognized properties supported for animations: 123, abc, xyz' + ]); + }); + it('should allow a vendor-prefixed property to be used in an animation sequence without throwing an error', () => { const steps = [ @@ -1070,12 +1082,20 @@ function invokeAnimationSequence( function validateAndThrowAnimationSequence(steps: AnimationMetadata|AnimationMetadata[]) { const driver = new MockAnimationDriver(); const errors: Error[] = []; - const ast = buildAnimationAst(driver, steps, errors); + const ast = buildAnimationAst(driver, steps, errors, []); if (errors.length) { throw new Error(errors.join('\n')); } } +function getValidationWarningsForAnimationSequence(steps: AnimationMetadata| + AnimationMetadata[]): string[] { + const driver = new MockAnimationDriver(); + const warnings: string[] = []; + buildAnimationAst(driver, steps, [], warnings); + return warnings; +} + function buildParams(params: {[name: string]: any}): AnimationOptions { return {params}; } diff --git a/packages/animations/browser/test/render/transition_animation_engine_spec.ts b/packages/animations/browser/test/render/transition_animation_engine_spec.ts index 1a041e1fd3d4..5134bd39eacb 100644 --- a/packages/animations/browser/test/render/transition_animation_engine_spec.ts +++ b/packages/animations/browser/test/render/transition_animation_engine_spec.ts @@ -732,9 +732,11 @@ function registerTrigger( element: any, engine: TransitionAnimationEngine, metadata: AnimationTriggerMetadata, id: string = DEFAULT_NAMESPACE_ID) { const errors: Error[] = []; + const warnings: string[] = []; const driver = new MockAnimationDriver(); const name = metadata.name; - const ast = buildAnimationAst(driver, metadata as AnimationMetadata, errors) as TriggerAst; + const ast = + buildAnimationAst(driver, metadata as AnimationMetadata, errors, warnings) as TriggerAst; if (errors.length) { } const trigger = buildTrigger(name, ast, new NoopAnimationStyleNormalizer()); diff --git a/packages/animations/browser/test/shared.ts b/packages/animations/browser/test/shared.ts index bcbd55a66486..e699c243dbcc 100644 --- a/packages/animations/browser/test/shared.ts +++ b/packages/animations/browser/test/shared.ts @@ -13,16 +13,21 @@ import {buildAnimationAst} from '../src/dsl/animation_ast_builder'; import {AnimationTrigger, buildTrigger} from '../src/dsl/animation_trigger'; import {NoopAnimationStyleNormalizer} from '../src/dsl/style_normalization/animation_style_normalizer'; import {triggerParsingFailed} from '../src/error_helpers'; +import {triggerParsingWarnings} from '../src/warning_helpers'; import {MockAnimationDriver} from '../testing/src/mock_animation_driver'; export function makeTrigger( name: string, steps: any, skipErrors: boolean = false): AnimationTrigger { const driver = new MockAnimationDriver(); const errors: Error[] = []; + const warnings: string[] = []; const triggerData = trigger(name, steps); - const triggerAst = buildAnimationAst(driver, triggerData, errors) as TriggerAst; + const triggerAst = buildAnimationAst(driver, triggerData, errors, warnings) as TriggerAst; if (!skipErrors && errors.length) { throw triggerParsingFailed(name, errors); } + if (warnings.length) { + triggerParsingWarnings(name, warnings); + } return buildTrigger(name, triggerAst, new NoopAnimationStyleNormalizer()); } diff --git a/packages/common/http/src/jsonp.ts b/packages/common/http/src/jsonp.ts index e5be9634973f..318c4c8b1fc2 100644 --- a/packages/common/http/src/jsonp.ts +++ b/packages/common/http/src/jsonp.ts @@ -21,6 +21,12 @@ import {HttpErrorResponse, HttpEvent, HttpEventType, HttpResponse, HttpStatusCod // is shared among all applications on the page. let nextRequestId: number = 0; +/** + * When a pending