diff --git a/test/unitylibs/scripts/transition-screen.test.js b/test/unitylibs/scripts/transition-screen.test.js index f272c7c21..dffe01723 100644 --- a/test/unitylibs/scripts/transition-screen.test.js +++ b/test/unitylibs/scripts/transition-screen.test.js @@ -45,15 +45,17 @@ describe('TransitionScreen', () => { }); describe('progressBarHandler', () => { - it('should call updateProgressBar and recurse', () => { + beforeEach(() => { splashScreenEl.innerHTML = `
0%
`; + }); + + it('should call updateProgressBar and recurse', () => { const spy = sinon.spy(screen, 'updateProgressBar'); - // Use fake timers to control setTimeout const clock = sinon.useFakeTimers(); screen.progressBarHandler(splashScreenEl, 10, 10, true); clock.tick(20); @@ -61,6 +63,48 @@ describe('TransitionScreen', () => { spy.restore(); clock.restore(); }); + + it('should ignore stale callbacks after clearProgressBarHandler', () => { + const clock = sinon.useFakeTimers(); + screen.progressBarHandler(splashScreenEl, 10, 10, true); + clock.tick(15); + screen.clearProgressBarHandler(); + const valueBeforeStaleTick = splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value'); + clock.tick(100); + expect(splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value')).to.equal(valueBeforeStaleTick); + clock.restore(); + }); + + it('should restart cleanly when initialized after cancel', () => { + const clock = sinon.useFakeTimers(); + screen.progressBarHandler(splashScreenEl, 10, 10, true); + clock.tick(15); + screen.clearProgressBarHandler(); + screen.progressBarHandler(splashScreenEl, 10, 10, true); + expect(splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value')).to.equal('0'); + clock.tick(15); + expect(parseInt(splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value'), 10)).to.be.greaterThan(0); + clock.restore(); + }); + }); + + describe('clearProgressBarHandler', () => { + it('should stop pending progress updates when updateProgressBar reaches 100', () => { + splashScreenEl.innerHTML = ` +
+
0%
+
+
+ `; + const clock = sinon.useFakeTimers(); + screen.progressBarHandler(splashScreenEl, 10, 10, true); + clock.tick(15); + screen.updateProgressBar(splashScreenEl, 100); + expect(splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value')).to.equal('100'); + clock.tick(100); + expect(splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value')).to.equal('100'); + clock.restore(); + }); }); describe('handleSplashProgressBar', () => { @@ -97,10 +141,23 @@ describe('TransitionScreen', () => { document.body.innerHTML = '
'; }); it('should hide splash and reset LOADER_LIMIT when displayOn is false', () => { + const clock = sinon.useFakeTimers(); + splashScreenEl.innerHTML = ` +
+
0%
+
+
+ `; + screen.progressBarHandler(splashScreenEl, 10, 10, true); + clock.tick(15); screen.splashVisibilityController(false); + const valueAtCancel = splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value'); + clock.tick(100); expect(screen.LOADER_LIMIT).to.equal(95); expect(splashScreenEl.classList.contains('show')).to.be.false; expect(splashScreenEl.parentElement.classList.contains('hide-splash-overflow')).to.be.false; + expect(splashScreenEl.querySelector('.spectrum-ProgressBar').getAttribute('value')).to.equal(valueAtCancel); + clock.restore(); }); it('should show splash and set aria-hidden when displayOn is true', () => { const stub = sinon.stub(screen, 'progressBarHandler'); diff --git a/unitylibs/core/widgets/prompt-bar-upload/prompt-bar-upload.css b/unitylibs/core/widgets/prompt-bar-upload/prompt-bar-upload.css index 59a6db0a1..fd4926916 100644 --- a/unitylibs/core/widgets/prompt-bar-upload/prompt-bar-upload.css +++ b/unitylibs/core/widgets/prompt-bar-upload/prompt-bar-upload.css @@ -78,8 +78,8 @@ display: flex; flex-direction: column; align-items: flex-start; gap: 17px; -border-right: 1px solid rgb(255 255 255 / 12%); -padding-right: 16px; +border-inline-end: 1px solid rgb(255 255 255 / 12%); +padding-inline-end: 16px; box-sizing: border-box; } @@ -134,6 +134,31 @@ width: min(339px, 100%); max-width: 100%; } +.unity-prompt-bar-upload.unity-enabled .pbu-main .alert-holder .alert-toast .alert-content { +min-width: 0; +} + +.unity-prompt-bar-upload.unity-enabled .pbu-main .alert-holder .alert-toast .alert-content .alert-icon { +flex: 1 1 0%; +justify-content: flex-start; +min-width: 0; +width: auto; +} + +.unity-prompt-bar-upload.unity-enabled .pbu-main .alert-holder .alert-toast .alert-content .alert-text { +flex: 1 1 auto; +max-width: 100%; +min-width: 0; +width: 100%; +padding-inline: 0; +overflow-wrap: break-word; +text-align: center; +} + +.unity-prompt-bar-upload.unity-enabled .pbu-main .alert-holder .alert-toast .alert-content .alert-text p { +padding-top: 0; +} + .unity-prompt-bar-upload.unity-enabled .pbu-right-section .unity-slf-prompt-label { margin-bottom: 0px; margin-inline: 0; @@ -911,6 +936,11 @@ border: 2px solid; background: rgb(64 105 253 / 14%); } +.pbu-drop-zone-wrap .drop-zone:focus-visible { +outline: none; +border: 2px solid #4069FD; +} + .pbu-drop-zone-wrap .drop-zone.hidden { display: none; } @@ -1138,11 +1168,11 @@ line-height: normal; .unity-prompt-bar-upload.unity-enabled .pbu-left-section { width: 100%; max-width: 100%; - border-right: none; - padding-right: 0; - padding-bottom: 16px; + border-inline-end: none; + padding-inline-end: 0; + padding-block-end: 16px; margin-bottom: 0; - border-bottom: 1px solid rgb(255 255 255 / 12%); + border-block-end: 1px solid rgb(255 255 255 / 12%); } .unity-prompt-bar-upload.unity-enabled .pbu-drop-zone-wrap { diff --git a/unitylibs/scripts/transition-screen.js b/unitylibs/scripts/transition-screen.js index f49cb7311..420ed8aba 100644 --- a/unitylibs/scripts/transition-screen.js +++ b/unitylibs/scripts/transition-screen.js @@ -20,6 +20,16 @@ export default class TransitionScreen { this.isDesktop = isDesktop; this.headingElements = []; this.progressText = ''; + this.progressBarTimeoutId = null; + this.progressBarGeneration = 0; + } + + clearProgressBarHandler() { + if (this.progressBarTimeoutId != null) { + clearTimeout(this.progressBarTimeoutId); + this.progressBarTimeoutId = null; + } + this.progressBarGeneration += 1; } setProgressTextFromDOM() { @@ -44,6 +54,7 @@ export default class TransitionScreen { ? this.progressText.replace('%', `${p}%`) : `${p}%`; if (status && status.textContent !== newStatus) status.textContent = newStatus; + if (percentage >= 100) this.clearProgressBarHandler(); } static createProgressBar() { @@ -63,13 +74,18 @@ export default class TransitionScreen { const newDelay = Math.min(delay + 100, 2000); const newI = Math.max(i - 5, 5); const progressBar = s.querySelector('.spectrum-ProgressBar'); - if (initialize) this.updateProgressBar(s, 0); - else { + if (initialize) { + this.clearProgressBarHandler(); + this.updateProgressBar(s, 0); + } else { const currentValue = parseInt(progressBar?.getAttribute('value'), 10); if (currentValue === 100 || currentValue >= this.LOADER_LIMIT) return; } - setTimeout(() => { + const generation = this.progressBarGeneration; + this.progressBarTimeoutId = setTimeout(() => { + this.progressBarTimeoutId = null; + if (generation !== this.progressBarGeneration) return; const v = initialize ? 0 : parseInt(progressBar.getAttribute('value'), 10); if (v === 100) return; this.updateProgressBar(s, v + newI); @@ -213,6 +229,7 @@ export default class TransitionScreen { splashVisibilityController(displayOn) { if (!displayOn) { + this.clearProgressBarHandler(); this.LOADER_LIMIT = 95; this.splashScreenEl.parentElement?.classList.remove('hide-splash-overflow'); this.splashScreenEl.classList.remove('show');