diff --git a/client/src/controllers/SessionController.test.js b/client/src/controllers/SessionController.test.js index b0eaca01f4..9b82737487 100644 --- a/client/src/controllers/SessionController.test.js +++ b/client/src/controllers/SessionController.test.js @@ -238,9 +238,10 @@ describe('SessionController', () => { workflowActionButton.addEventListener('click', handleWorkflowAction, { capture: true, }); - document.addEventListener('w-dialog:shown', handleDialogShow); - document.addEventListener('w-dialog:hidden', handleDialogHidden); - document.addEventListener('w-dialog:confirmed', handleDialogConfirmed); + const dialog = document.getElementById('w-overwrite-changes-dialog'); + dialog.addEventListener('w-dialog:shown', handleDialogShow); + dialog.addEventListener('w-dialog:hidden', handleDialogHidden); + dialog.addEventListener('w-dialog:confirmed', handleDialogConfirmed); }); afterEach(() => { @@ -410,6 +411,91 @@ describe('SessionController', () => { expect(dialog.getAttribute('aria-hidden')).toBeNull(); expect(confirmButton.textContent).toEqual('Approve'); }); + + it("should hide the submit button's dialog when it shows the confirmation dialog and show it again afterwards", async () => { + const confirmButton = document.getElementById('confirm'); + // Mark the confirm button as DialogController's confirm target + confirmButton.setAttribute('data-w-dialog-target', 'confirm'); + + const otherDialog = document.createElement('div'); + otherDialog.id = 'w-schedule-publishing-dialog'; + otherDialog.setAttribute('aria-hidden', 'true'); + otherDialog.setAttribute('data-controller', 'w-dialog'); + otherDialog.setAttribute( + 'data-action', + 'w-dialog:hide->w-dialog#hide w-dialog:show->w-dialog#show', + ); + otherDialog.innerHTML = /* html */ ` +
+
+ Set the publishing schedule + + + + + +
+
+ `; + + const otherDialogTrigger = document.createElement('button'); + otherDialogTrigger.type = 'button'; + otherDialogTrigger.setAttribute( + 'data-a11y-dialog-show', + 'w-schedule-publishing-dialog', + ); + otherDialogTrigger.innerHTML = 'Set schedule'; + + form.appendChild(otherDialog); + form.appendChild(otherDialogTrigger); + + // Reconnect the DialogController so the submit button in the + // schedule publishing dialog works + const dialog = document.querySelector('#w-overwrite-changes-dialog'); + dialog.removeAttribute('data-controller'); + await Promise.resolve(); + dialog.setAttribute('data-controller', 'w-dialog'); + await Promise.resolve(); + + expect(handleDialogShow).not.toHaveBeenCalled(); + + // Show the schedule publishing dialog + otherDialogTrigger.click(); + expect(otherDialog.getAttribute('aria-hidden')).toBeNull(); + + // Should not trigger the confirmation dialog yet + expect(handleDialogShow).not.toHaveBeenCalled(); + + const scheduleSubmitButton = otherDialog.querySelector( + 'button[type="submit"]', + ); + scheduleSubmitButton.click(); + await Promise.resolve(); + + // Should trigger the confirmation dialog + expect(handleSubmit).not.toHaveBeenCalled(); + expect(handleWorkflowAction).not.toHaveBeenCalled(); + expect(handleDialogShow).toHaveBeenCalled(); + expect(dialog.getAttribute('aria-hidden')).toBeNull(); + expect(confirmButton.textContent).toEqual('Save schedule'); + + // Should hide the schedule publishing dialog + expect(otherDialog.getAttribute('aria-hidden')).toEqual('true'); + + // Confirm the dialog + confirmButton.click(); + await Promise.resolve(); + + // Should hide the confirmation dialog and continue the action + expect(handleDialogHidden).toHaveBeenCalled(); + expect(handleDialogConfirmed).toHaveBeenCalled(); + expect(handleSubmit).toHaveBeenCalledTimes(1); + expect(handleWorkflowAction).not.toHaveBeenCalled(); + expect(dialog.getAttribute('aria-hidden')).toEqual('true'); + + // The schedule publishing dialog should still be hidden + expect(otherDialog.getAttribute('aria-hidden')).toEqual('true'); + }); }); describe('storing unsaved changes state to a checkbox input and update reload buttons accordingly', () => { diff --git a/client/src/controllers/SessionController.ts b/client/src/controllers/SessionController.ts index 918cc9249c..cbccee9157 100644 --- a/client/src/controllers/SessionController.ts +++ b/client/src/controllers/SessionController.ts @@ -87,14 +87,6 @@ export class SessionController extends Controller { } connect(): void { - this.interceptTargets.forEach((button) => { - // Match the event listener configuration of workflow-action that uses - // capture so we can intercept the workflow-action's listener. - button.addEventListener('click', this.showConfirmationDialog, { - capture: true, - }); - }); - // Do a ping so the sessions list can be loaded immediately. this.ping(); } @@ -175,6 +167,12 @@ export class SessionController extends Controller { // workflow action modal) after the user confirms the dialog. if (!this.interceptValue || !this.hasWDialogOutlet) return; + // If the action button is inside a dialog, we need to hide the dialog first + // so it doesn't interfere with the confirmation dialog + this.lastActionButton + ?.closest('[data-controller="w-dialog"]') + ?.dispatchEvent(new Event('w-dialog:hide')); + // Prevent form submission event.preventDefault(); // Prevent triggering other event listeners e.g. workflow actions modal @@ -197,12 +195,32 @@ export class SessionController extends Controller { } wDialogOutletConnected(): void { + // Attach the event listener to the buttons that will be intercepted. + // Do it here instead of in connect() so hopefully this includes any buttons + // that are inside a dialog that is connected after this controller + // (e.g. the schedule publishing dialog). + this.interceptTargets.forEach((button) => { + // Match the event listener configuration of workflow-action that uses + // capture so we can intercept the workflow-action's listener. + button.addEventListener('click', this.showConfirmationDialog, { + capture: true, + }); + }); + this.wDialogOutlet.element.addEventListener( 'w-dialog:confirmed', this.confirmAction, ); } + wDialogOutletDisconnected(): void { + this.interceptTargets?.forEach((button) => { + button.removeEventListener('click', this.showConfirmationDialog, { + capture: true, + }); + }); + } + /** * Sets the unsaved changes input state based on the event type dispatched by * the w-unsaved controller. If the event type is w-unsaved:add, the input is @@ -293,10 +311,5 @@ export class SessionController extends Controller { if (this.interval) { window.clearInterval(this.interval); } - this.interceptTargets?.forEach((button) => { - button.removeEventListener('click', this.showConfirmationDialog, { - capture: true, - }); - }); } }