From eae8162d0dc2e0bd7a9d56a3662a8e5f5d142b72 Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Mon, 23 Oct 2023 12:28:03 -0400 Subject: [PATCH] fix(animation): progressEnd coercion is reset before onFinish (#28394) Issue number: resolves #28393 --------- ## What is the current behavior? Our animation library's value coercion is not reset before developer `onFinish` callbacks fire. This can lead to developers getting incorrect state when querying for `getDuration` or `getDirection` ## What is the new behavior? - Internal value coercion is reset before developer `onFinish` callbacks fire. ## Does this introduce a breaking change? - [ ] Yes - [x] No I'm putting this in a minor release to minimize risk. This is not a breaking change, but there may be developers relying on this broken behavior to implement a workaround. ## Other information Note: This change is needed for the toast swipe to dismiss feature (FW-2004) --- core/src/utils/animation/animation.ts | 28 +++++++++---------- .../utils/animation/test/animation.spec.ts | 17 +++++++++++ 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/core/src/utils/animation/animation.ts b/core/src/utils/animation/animation.ts index c547bafd3c..14c609b103 100644 --- a/core/src/utils/animation/animation.ts +++ b/core/src/utils/animation/animation.ts @@ -579,6 +579,17 @@ export const createAnimation = (animationId?: string): Animation => { } }); + /** + * Clean up any value coercion before + * the user callbacks fire otherwise + * they may get stale values. For example, + * if someone calls progressStart(0) the + * animation may still be reversed. + */ + forceDurationValue = undefined; + forceDirectionValue = undefined; + forceDelayValue = undefined; + onFinishCallbacks.forEach((onFinishCallback) => { return onFinishCallback.c(currentStep, ani); }); @@ -826,21 +837,8 @@ export const createAnimation = (animationId?: string): Animation => { } } - if (playTo !== undefined) { - onFinish( - () => { - forceDurationValue = undefined; - forceDirectionValue = undefined; - forceDelayValue = undefined; - }, - { - oneTimeCallback: true, - } - ); - - if (!parentAnimation) { - play(); - } + if (playTo !== undefined && !parentAnimation) { + play(); } return ani; diff --git a/core/src/utils/animation/test/animation.spec.ts b/core/src/utils/animation/test/animation.spec.ts index b9cfb18c3d..0e4337e01c 100644 --- a/core/src/utils/animation/test/animation.spec.ts +++ b/core/src/utils/animation/test/animation.spec.ts @@ -4,6 +4,23 @@ import { processKeyframes } from '../animation-utils'; import { getTimeGivenProgression } from '../cubic-bezier'; describe('Animation Class', () => { + describe('progressEnd callbacks', () => { + test('coerced state should be reset before onFinish runs', (done) => { + const el = document.createElement('div'); + const animation = createAnimation() + .addElement(el) + .fromTo('transform', 'translateX(0px)', 'translateX(100px)') + .duration(50); + + animation + .onFinish(() => { + expect(animation.getDirection()).toBe('normal'); + expect(animation.getDuration()).toBe(50); + done(); + }) + .progressEnd(0, 0.5, 10); + }); + }); describe('play()', () => { it('should resolve when the animation is cancelled', async () => { // Tell Jest to expect 1 assertion for async code