Skip to content

Commit

Permalink
fix(animations): make sure that the animation function delay is appli…
Browse files Browse the repository at this point in the history
…ed (angular#47285)

make sure that when an animation is defined via the `animation` function
(and used via `useAnimation`) and a delay has been provided then that
delay gets correctly applied

resolves angular#47283

PR Close angular#47285
  • Loading branch information
dario-piotrowicz authored and vyom1611 committed Sep 18, 2022
1 parent 4a41465 commit e21a08b
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 1 deletion.
23 changes: 23 additions & 0 deletions packages/animations/browser/src/dsl/animation_timeline_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,34 @@ export class AnimationTimelineBuilderVisitor implements AstVisitor {
visitAnimateRef(ast: AnimateRefAst, context: AnimationTimelineContext): any {
const innerContext = context.createSubContext(ast.options);
innerContext.transformIntoNewTimeline();
this._applyAnimateRefDelay(ast.animation, context, innerContext);
this.visitReference(ast.animation, innerContext);
context.transformIntoNewTimeline(innerContext.currentTimeline.currentTime);
context.previousNode = ast;
}

private _applyAnimateRefDelay(
animation: ReferenceAst, context: AnimationTimelineContext,
innerContext: AnimationTimelineContext) {
const animationDelay = animation.options?.delay;

if (!animationDelay) {
return;
}

let animationDelayValue: number;

if (typeof animationDelay === 'string') {
const interpolatedDelay =
interpolateParams(animationDelay, animation.options?.params ?? {}, context.errors);
animationDelayValue = resolveTimingValue(interpolatedDelay);
} else {
animationDelayValue = animationDelay;
}

innerContext.delayNextStep(animationDelayValue);
}

private _visitSubInstructions(
instructions: AnimationTimelineInstruction[], context: AnimationTimelineContext,
options: AnimateChildOptions): number {
Expand Down
139 changes: 138 additions & 1 deletion packages/core/test/animation/animation_integration_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {animate, animateChild, AnimationEvent, AnimationMetadata, AnimationOptions, AUTO_STYLE, group, keyframes, query, state, style, transition, trigger, ɵPRE_STYLE as PRE_STYLE} from '@angular/animations';
import {animate, animateChild, animation, AnimationEvent, AnimationMetadata, AnimationOptions, AUTO_STYLE, group, keyframes, query, state, style, transition, trigger, useAnimation, ɵPRE_STYLE as PRE_STYLE} from '@angular/animations';
import {AnimationDriver, ɵAnimationEngine, ɵNoopAnimationDriver as NoopAnimationDriver} from '@angular/animations/browser';
import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/browser/testing';
import {ChangeDetectorRef, Component, HostBinding, HostListener, Inject, RendererFactory2, ViewChild} from '@angular/core';
Expand Down Expand Up @@ -3830,6 +3830,143 @@ describe('animation tests', function() {
/only state\(\) and transition\(\) definitions can sit inside of a trigger\(\)/);
});

describe('animation and useAnimation functions', () => {
it('should apply the delay specified in the animation', () => {
const animationMetaData = animation(
[
style({color: 'red'}),
animate(1000, style({color: 'green'})),
],
{delay: 3000});

@Component({
selector: 'cmp',
template: `
<div @anim *ngIf="exp">
</div>
`,
animations: [
trigger('anim', [transition(
':enter',
useAnimation(animationMetaData),
)]),
]
})
class Cmp {
exp: boolean = false;
}

TestBed.configureTestingModule({declarations: [Cmp]});

const engine = TestBed.inject(ɵAnimationEngine);
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
cmp.exp = true;

fixture.detectChanges();
engine.flush();

const players = getLog();
expect(players.length).toEqual(1);
const [player] = players;
expect(player.delay).toEqual(3000);
expect(player.duration).toEqual(1000);
expect(player.keyframes).toEqual([
new Map<string, string|number>([['color', 'red'], ['offset', 0]]),
new Map<string, string|number>([['color', 'green'], ['offset', 1]]),
]);
});

it('should apply the delay specified in the animation using params', () => {
const animationMetaData = animation(
[
style({color: 'red'}),
animate(500, style({color: 'green'})),
],
{delay: '{{animationDelay}}ms', params: {animationDelay: 5500}});

@Component({
selector: 'cmp',
template: `
<div @anim *ngIf="exp">
</div>
`,
animations: [
trigger('anim', [transition(
':enter',
useAnimation(animationMetaData),
)]),
]
})
class Cmp {
exp: boolean = false;
}

TestBed.configureTestingModule({declarations: [Cmp]});

const engine = TestBed.inject(ɵAnimationEngine);
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
cmp.exp = true;

fixture.detectChanges();
engine.flush();

const players = getLog();
expect(players.length).toEqual(1);
const [player] = players;
expect(player.delay).toEqual(5500);
expect(player.duration).toEqual(500);
expect(player.keyframes).toEqual([
new Map<string, string|number>([['color', 'red'], ['offset', 0]]),
new Map<string, string|number>([['color', 'green'], ['offset', 1]]),
]);
});

it('should combine the delay specified in the animation with that of the caller', () => {
const animationMetaData = animation(
[
style({color: 'red'}),
animate(500, style({color: 'green'})),
],
{delay: 2000});

@Component({
selector: 'cmp',
template: `
<div @anim *ngIf="exp">
</div>
`,
animations: [
trigger('anim', [transition(':enter', useAnimation(animationMetaData), {delay: 750})]),
]
})
class Cmp {
exp: boolean = false;
}

TestBed.configureTestingModule({declarations: [Cmp]});

const engine = TestBed.inject(ɵAnimationEngine);
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
cmp.exp = true;

fixture.detectChanges();
engine.flush();

const players = getLog();
expect(players.length).toEqual(1);
const [player] = players;
expect(player.delay).toEqual(2750);
expect(player.duration).toEqual(500);
expect(player.keyframes).toEqual([
new Map<string, string|number>([['color', 'red'], ['offset', 0]]),
new Map<string, string|number>([['color', 'green'], ['offset', 1]]),
]);
});
});

it('should combine multiple errors together into one exception when an animation fails to be built',
() => {
@Component({
Expand Down

0 comments on commit e21a08b

Please sign in to comment.