From 7e0ab7f2002b9593d7bd224212bf5c51589f7011 Mon Sep 17 00:00:00 2001 From: Vaadin Bot Date: Wed, 17 Apr 2024 12:45:56 +0200 Subject: [PATCH] fix: share animationend listener reference in notification (#7342) (CP: 24.3) (#7344) Co-authored-by: Tomi Virkki --- .../notification/src/vaadin-notification.js | 24 ++++++---- packages/notification/test/animation.test.js | 47 ++++++++++++++++++- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/packages/notification/src/vaadin-notification.js b/packages/notification/src/vaadin-notification.js index 716f72c81f..842b549d4f 100644 --- a/packages/notification/src/vaadin-notification.js +++ b/packages/notification/src/vaadin-notification.js @@ -484,16 +484,22 @@ class Notification extends OverlayClassMixin(ThemePropertyMixin(ElementMixin(Pol } } + /** @private */ + __cleanUpOpeningClosingState() { + this._card.removeAttribute('opening'); + this._card.removeAttribute('closing'); + this._card.removeEventListener('animationend', this.__animationEndListener); + } + /** @private */ _animatedAppendNotificationCard() { if (this._card) { + this.__cleanUpOpeningClosingState(); + this._card.setAttribute('opening', ''); this._appendNotificationCard(); - const listener = () => { - this._card.removeEventListener('animationend', listener); - this._card.removeAttribute('opening'); - }; - this._card.addEventListener('animationend', listener); + this.__animationEndListener = () => this.__cleanUpOpeningClosingState(); + this._card.addEventListener('animationend', this.__animationEndListener); this._didAnimateNotificationAppend = true; } else { this._didAnimateNotificationAppend = false; @@ -538,14 +544,16 @@ class Notification extends OverlayClassMixin(ThemePropertyMixin(ElementMixin(Pol /** @private */ _animatedRemoveNotificationCard() { + this.__cleanUpOpeningClosingState(); + this._card.setAttribute('closing', ''); const name = getComputedStyle(this._card).getPropertyValue('animation-name'); if (name && name !== 'none') { - const listener = () => { + this.__animationEndListener = () => { this._removeNotificationCard(); - this._card.removeEventListener('animationend', listener); + this.__cleanUpOpeningClosingState(); }; - this._card.addEventListener('animationend', listener); + this._card.addEventListener('animationend', this.__animationEndListener); } else { this._removeNotificationCard(); } diff --git a/packages/notification/test/animation.test.js b/packages/notification/test/animation.test.js index 7cb2d58ca6..d22c140d75 100644 --- a/packages/notification/test/animation.test.js +++ b/packages/notification/test/animation.test.js @@ -53,8 +53,12 @@ describe('animated notifications', () => { await aTimeout(duration); }); - afterEach(() => { + afterEach(async () => { notifications.forEach((n) => n.close()); + // Wait for the notification container to be removed + while (document.querySelector('body > vaadin-notification-container')) { + await aTimeout(1); + } }); describe('animation', () => { @@ -85,9 +89,50 @@ describe('animated notifications', () => { }); it('should set `opening` attribute and remove later', async () => { + await oneEvent(notifications[1]._card, 'animationend'); + notifications[1].open(); expect(notifications[1]._card.hasAttribute('opening')).to.be.true; await oneEvent(notifications[1]._card, 'animationend'); expect(notifications[1]._card.hasAttribute('opening')).to.be.false; }); + + describe('simultaneous opening and closing', () => { + let notification, card; + + beforeEach(() => { + // Use the animated notification for these tests + notification = notifications[1]; + notification.duration = -1; + card = notification._card; + // Close the non-animated notification as it's not relevant for these tests + notifications[0].close(); + }); + + it('should remain opened after closing and opening', async () => { + // Simultanously close and open the animated notification + notification.close(); + notification.open(); + await oneEvent(card, 'animationend'); + + expect(notification.opened).to.be.true; + expect(card.hasAttribute('closing')).to.be.false; + expect(card.hasAttribute('opening')).to.be.false; + expect(container.parentNode).to.equal(document.body); + expect(container.contains(card)).to.be.true; + }); + + it('should remain closed after opening and closing', async () => { + // Simultanously open and close the animated notification + notification.close(); + notification.open(); + notification.close(); + await oneEvent(card, 'animationend'); + + expect(notification.opened).to.be.false; + expect(card.hasAttribute('closing')).to.be.false; + expect(card.hasAttribute('opening')).to.be.false; + expect(container.parentNode).to.not.equal(document.body); + }); + }); }); });