From ae57e50d56fde48cca0ab01fd2791edab6efd0ec Mon Sep 17 00:00:00 2001 From: Martin Gustafsson Date: Mon, 21 Mar 2016 20:38:06 +0100 Subject: [PATCH] fix(if): queue up changes when animating Fixes aurelia/animator-css#44 --- src/if.js | 31 ++++++++++++++++++++++++++++--- test/if.spec.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/if.js b/src/if.js index 78b6e66..2c4e350 100644 --- a/src/if.js +++ b/src/if.js @@ -40,9 +40,34 @@ export class If { * @param newValue The new value */ valueChanged(newValue) { + if (this.__queuedChanges) { + this.__queuedChanges.push(newValue); + return; + } + + let maybePromise = this._runValueChanged(newValue); + if (maybePromise instanceof Promise) { + let queuedChanges = this.__queuedChanges = []; + + let runQueuedChanges = () => { + if (!queuedChanges.length) { + this.__queuedChanges = undefined; + return; + } + + let nextPromise = this._runValueChanged(queuedChanges.shift()) || Promise.resolve(); + nextPromise.then(runQueuedChanges); + }; + + maybePromise.then(runQueuedChanges); + } + } + + _runValueChanged(newValue) { if (!newValue) { + let viewOrPromise; if (this.view !== null && this.showing) { - let viewOrPromise = this.viewSlot.remove(this.view); + viewOrPromise = this.viewSlot.remove(this.view); if (viewOrPromise instanceof Promise) { viewOrPromise.then(() => this.view.unbind()); } else { @@ -51,7 +76,7 @@ export class If { } this.showing = false; - return; + return viewOrPromise; } if (this.view === null) { @@ -64,7 +89,7 @@ export class If { if (!this.showing) { this.showing = true; - this.viewSlot.add(this.view); + return this.viewSlot.add(this.view); } } diff --git a/test/if.spec.js b/test/if.spec.js index 3155e1e..4641a49 100644 --- a/test/if.spec.js +++ b/test/if.spec.js @@ -104,6 +104,50 @@ describe('if', () => { expect(view.bind).toHaveBeenCalled(); }); + + describe('during animation', () => { + let delay = ms => new Promise(resolve => setTimeout(resolve, ms)); + + let removeWithAnimation = animationDuration => { + return function(view) { + return delay(animationDuration).then(() => { + this.children = []; + }); + }; + }; + + let addWithAnimation = animationDuration => { + return function(view) { + return delay(animationDuration).then(() => { + this.children.push(view); + }); + }; + }; + + beforeEach(() => { + spyOn(viewSlot, 'remove').and.callFake(removeWithAnimation(600)); + spyOn(viewSlot, 'add').and.callFake(addWithAnimation(0)); + }); + + it('should correctly handle value changes during remove animation', done => { + sut.showing = false; + sut.view = new ViewMock(); + sut.view.isBound = true; + sut.viewSlot.children = []; + sut.valueChanged(true); + + delay(200).then(() => { + sut.valueChanged(false); + return delay(400); + }).then(() => { + sut.valueChanged(true); + return delay(600); + }).then(() => { + expect(viewSlot.children.length).toEqual(1); + }) + .then(() => done()) + }) + }); }); class ViewSlotMock {