diff --git a/core/src/components/modal/modal.tsx b/core/src/components/modal/modal.tsx index bae8648d765..d32e092b195 100644 --- a/core/src/components/modal/modal.tsx +++ b/core/src/components/modal/modal.tsx @@ -10,7 +10,6 @@ import { Style as StatusBarStyle, StatusBar } from '@utils/native/status-bar'; import { GESTURE, BACKDROP, - activeAnimations, dismiss, eventMethod, prepareOverlay, @@ -705,8 +704,6 @@ export class Modal implements ComponentInterface, OverlayInterface { this.keyboardOpenCallback = undefined; } - const enteringAnimation = activeAnimations.get(this) || []; - const dismissed = await dismiss( this, data, @@ -733,8 +730,6 @@ export class Modal implements ComponentInterface, OverlayInterface { if (this.gesture) { this.gesture.destroy(); } - - enteringAnimation.forEach((ani) => ani.destroy()); } this.currentBreakpoint = undefined; this.animation = undefined; diff --git a/core/src/components/modal/test/animations/modal.e2e.ts b/core/src/components/modal/test/animations/modal.e2e.ts new file mode 100644 index 00000000000..8d541003ff8 --- /dev/null +++ b/core/src/components/modal/test/animations/modal.e2e.ts @@ -0,0 +1,45 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ config, title }) => { + test.describe(title('modal: animations'), () => { + test.beforeEach(async ({ page }) => { + await page.setContent( + ` + + `, + config + ); + }); + test('card modal should clean up animations on dismiss', async ({ page }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/28352', + }); + + const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss'); + + const modal = page.locator('ion-modal'); + + const initialAnimations = await modal.evaluate((el: HTMLIonModalElement) => { + return el.shadowRoot!.getAnimations(); + }); + + // While the modal is open, it should have animations + expect(initialAnimations.length).toBeGreaterThan(0); + + await modal.evaluate((el: HTMLIonModalElement) => { + el.dismiss(); + }); + + await ionModalDidDismiss.next(); + + const currentAnimations = await modal.evaluate((el: HTMLIonModalElement) => { + return el.shadowRoot!.getAnimations(); + }); + + // Once the modal has finished closing, there should be no animations + expect(currentAnimations.length).toBe(0); + }); + }); +}); diff --git a/core/src/utils/overlays.ts b/core/src/utils/overlays.ts index bd56ac36844..bead295cd0b 100644 --- a/core/src/utils/overlays.ts +++ b/core/src/utils/overlays.ts @@ -577,6 +577,12 @@ export const dismiss = async ( overlay.didDismiss.emit({ data, role }); overlay.didDismissShorthand?.emit({ data, role }); + // Get a reference to all animations currently assigned to this overlay + // Then tear them down to return the overlay to its initial visual state + const animations = activeAnimations.get(overlay) || []; + + animations.forEach((ani) => ani.destroy()); + activeAnimations.delete(overlay); /**