From 4a5029ea29ac75243dfec68153051292fc70f5cf Mon Sep 17 00:00:00 2001 From: alpadev <2838324+alpadev@users.noreply.github.com> Date: Thu, 3 Jun 2021 13:44:16 +0200 Subject: Fix handling of transitionend events dispatched by nested elements(#33845) Fix handling of transitionend events dispatched by nested elements Properly handle events from nested elements Change `emulateTransitionEnd` to `executeAfterTransition` && --- js/tests/unit/util/index.spec.js | 153 +++++++++++++++++++++++++++++++-------- 1 file changed, 124 insertions(+), 29 deletions(-) (limited to 'js/tests/unit/util') diff --git a/js/tests/unit/util/index.spec.js b/js/tests/unit/util/index.spec.js index 737ecacfd..04ad6bf43 100644 --- a/js/tests/unit/util/index.spec.js +++ b/js/tests/unit/util/index.spec.js @@ -157,12 +157,13 @@ describe('Util', () => { describe('triggerTransitionEnd', () => { it('should trigger transitionend event', done => { - fixtureEl.innerHTML = '
' + fixtureEl.innerHTML = '' const el = fixtureEl.querySelector('div') + const spy = spyOn(el, 'dispatchEvent').and.callThrough() el.addEventListener('transitionend', () => { - expect().nothing() + expect(spy).toHaveBeenCalled() done() }) @@ -226,33 +227,6 @@ describe('Util', () => { }) }) - describe('emulateTransitionEnd', () => { - it('should emulate transition end', () => { - fixtureEl.innerHTML = '' - - const el = document.querySelector('div') - const spy = spyOn(window, 'setTimeout') - - Util.emulateTransitionEnd(el, 10) - expect(spy).toHaveBeenCalled() - }) - - it('should not emulate transition end if already triggered', done => { - fixtureEl.innerHTML = '' - - const el = fixtureEl.querySelector('div') - const spy = spyOn(el, 'removeEventListener') - - Util.emulateTransitionEnd(el, 10) - Util.triggerTransitionEnd(el) - - setTimeout(() => { - expect(spy).toHaveBeenCalled() - done() - }, 20) - }) - }) - describe('typeCheckConfig', () => { const namePlugin = 'collapse' @@ -660,6 +634,127 @@ describe('Util', () => { }) }) + describe('executeAfterTransition', () => { + it('should immediately execute a function when waitForTransition parameter is false', () => { + const el = document.createElement('div') + const callbackSpy = jasmine.createSpy('callback spy') + const eventListenerSpy = spyOn(el, 'addEventListener') + + Util.executeAfterTransition(callbackSpy, el, false) + + expect(callbackSpy).toHaveBeenCalled() + expect(eventListenerSpy).not.toHaveBeenCalled() + }) + + it('should execute a function when a transitionend event is dispatched', () => { + const el = document.createElement('div') + const callbackSpy = jasmine.createSpy('callback spy') + + spyOn(window, 'getComputedStyle').and.returnValue({ + transitionDuration: '0.05s', + transitionDelay: '0s' + }) + + Util.executeAfterTransition(callbackSpy, el) + + el.dispatchEvent(new TransitionEvent('transitionend')) + + expect(callbackSpy).toHaveBeenCalled() + }) + + it('should execute a function after a computed CSS transition duration and there was no transitionend event dispatched', done => { + const el = document.createElement('div') + const callbackSpy = jasmine.createSpy('callback spy') + + spyOn(window, 'getComputedStyle').and.returnValue({ + transitionDuration: '0.05s', + transitionDelay: '0s' + }) + + Util.executeAfterTransition(callbackSpy, el) + + setTimeout(() => { + expect(callbackSpy).toHaveBeenCalled() + done() + }, 70) + }) + + it('should not execute a function a second time after a computed CSS transition duration and if a transitionend event has already been dispatched', done => { + const el = document.createElement('div') + const callbackSpy = jasmine.createSpy('callback spy') + + spyOn(window, 'getComputedStyle').and.returnValue({ + transitionDuration: '0.05s', + transitionDelay: '0s' + }) + + Util.executeAfterTransition(callbackSpy, el) + + setTimeout(() => { + el.dispatchEvent(new TransitionEvent('transitionend')) + }, 50) + + setTimeout(() => { + expect(callbackSpy).toHaveBeenCalledTimes(1) + done() + }, 70) + }) + + it('should not trigger a transitionend event if another transitionend event had already happened', done => { + const el = document.createElement('div') + + spyOn(window, 'getComputedStyle').and.returnValue({ + transitionDuration: '0.05s', + transitionDelay: '0s' + }) + + Util.executeAfterTransition(() => {}, el) + + // simulate a event dispatched by the browser + el.dispatchEvent(new TransitionEvent('transitionend')) + + const dispatchSpy = spyOn(el, 'dispatchEvent').and.callThrough() + + setTimeout(() => { + // setTimeout should not have triggered another transitionend event. + expect(dispatchSpy).not.toHaveBeenCalled() + done() + }, 70) + }) + + it('should ignore transitionend events from nested elements', done => { + fixtureEl.innerHTML = [ + '