fix(animations): ensure :animating queries collect previous animation elements properly

This commit is contained in:
Matias Niemelä 2017-07-06 10:39:22 -07:00 committed by Jason Aden
parent f85b543cc1
commit 3203639d7d
2 changed files with 122 additions and 16 deletions

View File

@ -18,6 +18,9 @@ import {ENTER_CLASSNAME, LEAVE_CLASSNAME, NG_ANIMATING_CLASSNAME, NG_ANIMATING_S
import {AnimationDriver} from './animation_driver'; import {AnimationDriver} from './animation_driver';
import {getOrSetAsInMap, listenOnPlayer, makeAnimationEvent, normalizeKeyframes, optimizeGroupPlayer} from './shared'; import {getOrSetAsInMap, listenOnPlayer, makeAnimationEvent, normalizeKeyframes, optimizeGroupPlayer} from './shared';
const QUEUED_CLASSNAME = 'ng-animate-queued';
const QUEUED_SELECTOR = '.ng-animate-queued';
const EMPTY_PLAYER_ARRAY: TransitionAnimationPlayer[] = []; const EMPTY_PLAYER_ARRAY: TransitionAnimationPlayer[] = [];
const NULL_REMOVAL_STATE: ElementAnimationState = { const NULL_REMOVAL_STATE: ElementAnimationState = {
namespaceId: '', namespaceId: '',
@ -235,12 +238,11 @@ export class AnimationTransitionNamespace {
{element, triggerName, transition, fromState, toState, player, isFallbackTransition}); {element, triggerName, transition, fromState, toState, player, isFallbackTransition});
if (!isFallbackTransition) { if (!isFallbackTransition) {
addClass(element, NG_ANIMATING_CLASSNAME); addClass(element, QUEUED_CLASSNAME);
player.onStart(() => { removeClass(element, QUEUED_CLASSNAME); });
} }
player.onDone(() => { player.onDone(() => {
removeClass(element, NG_ANIMATING_CLASSNAME);
let index = this.players.indexOf(player); let index = this.players.indexOf(player);
if (index >= 0) { if (index >= 0) {
this.players.splice(index, 1); this.players.splice(index, 1);

View File

@ -39,7 +39,7 @@ export function main() {
}); });
describe('query()', () => { describe('query()', () => {
it('should be able to query all animation triggers via `@*`', () => { it('should be able to query all elements that contain animation triggers via @*', () => {
@Component({ @Component({
selector: 'ani-cmp', selector: 'ani-cmp',
template: ` template: `
@ -56,30 +56,40 @@ export function main() {
'parent', 'parent',
[ [
transition( transition(
'* => *', '* => go',
[ [
query( query(
'@*:animating', '@*',
[ [
animate(1000, style({background: 'red'})), style({ backgroundColor: 'blue' }),
animate(1000, style({backgroundColor: 'red'})),
]), ]),
]), ]),
]), ]),
trigger( trigger(
'a', 'a',
[ [
transition('* => *', []), transition('* => 1', [
animate(1000, style({ opacity: 0 }))
]),
]), ]),
trigger( trigger(
'b', 'b',
[ [
transition('* => *', []), transition('* => 1', [
animate(1000, style({ opacity: 0 })),
query('.b-inner', [
animate(1000, style({ opacity: 0 }))
]),
]),
]), ]),
trigger( trigger(
'c', 'c',
[ [
transition('* => *', []), transition('* => 1', [
]) animate(1000, style({ opacity: 0 }))
]),
]),
] ]
}) })
class Cmp { class Cmp {
@ -90,23 +100,117 @@ export function main() {
TestBed.configureTestingModule({declarations: [Cmp]}); TestBed.configureTestingModule({declarations: [Cmp]});
const engine = TestBed.get(ɵAnimationEngine);
const fixture = TestBed.createComponent(Cmp); const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance; const cmp = fixture.componentInstance;
cmp.exp0 = 1; cmp.exp0 = 'go';
fixture.detectChanges();
let players = getLog();
expect(players.length).toEqual(3); // a,b,c
resetLog();
const [p1, p2, p3] = players;
expect(p1.element.classList.contains('a')).toBeTruthy();
expect(p2.element.classList.contains('b')).toBeTruthy();
expect(p3.element.classList.contains('c')).toBeTruthy();
});
it('should be able to query currently animating elements via :animating', () => {
@Component({
selector: 'ani-cmp',
template: `
<div [@parent]="exp0">
<div class="a" [@a]="exp1"></div>
<div class="b" [@b]="exp2">
<div class="b-inner"></div>
</div>
<div class="c" [@c]="exp3"></div>
</div>
`,
animations: [
trigger(
'parent',
[
transition(
'* => go',
[
query(
':animating',
[
style({ backgroundColor: 'blue' }),
animate(1000, style({backgroundColor: 'red'})),
]),
]),
]),
trigger(
'a',
[
transition('* => 1', [
animate(1000, style({ opacity: 0 }))
]),
]),
trigger(
'b',
[
transition('* => 1', [
animate(1000, style({ opacity: 0 })),
query('.b-inner', [
animate(1000, style({ opacity: 0 }))
]),
]),
]),
trigger(
'c',
[
transition('* => 1', [
animate(1000, style({ opacity: 0 }))
]),
]),
]
})
class Cmp {
public exp0: any;
public exp1: any;
public exp2: any;
public exp3: any;
}
TestBed.configureTestingModule({declarations: [Cmp]});
const fixture = TestBed.createComponent(Cmp);
const cmp = fixture.componentInstance;
cmp.exp0 = '';
cmp.exp1 = 1; cmp.exp1 = 1;
cmp.exp2 = 1; cmp.exp2 = 1;
// note that exp3 is skipped here
fixture.detectChanges(); fixture.detectChanges();
engine.flush();
const players = getLog(); let players = getLog();
expect(players.length).toEqual(3); // a,b,b-inner and not c
resetLog();
cmp.exp0 = 'go';
fixture.detectChanges();
const expectedKeyframes = [
{backgroundColor: 'blue', offset: 0},
{backgroundColor: 'red', offset: 1},
];
players = getLog();
expect(players.length).toEqual(3); expect(players.length).toEqual(3);
const [p1, p2, p3] = players; const [p1, p2, p3] = players;
expect(p1.element.classList.contains('a')).toBeTruthy(); expect(p1.element.classList.contains('a')).toBeTruthy();
expect(p1.keyframes).toEqual(expectedKeyframes);
expect(p2.element.classList.contains('b')).toBeTruthy(); expect(p2.element.classList.contains('b')).toBeTruthy();
expect(p3.element.classList.contains('c')).toBeTruthy(); expect(p2.keyframes).toEqual(expectedKeyframes);
expect(p3.element.classList.contains('b-inner')).toBeTruthy();
expect(p3.keyframes).toEqual(expectedKeyframes);
}); });
it('should be able to query triggers directly by name', () => { it('should be able to query triggers directly by name', () => {