From 3e6e4f2ee35c00d79814f57f3d3a3d68d7ae268c Mon Sep 17 00:00:00 2001 From: Sage Abdullah <sage.abdullah@torchbox.com> Date: Mon, 1 Jul 2024 14:13:56 +0100 Subject: [PATCH] Add confirm() method to DialogController Like hide(), but dispatches an event to indicate that the dialog was 'confirmed' --- .../src/controllers/DialogController.test.js | 64 +++++++++++++++++++ client/src/controllers/DialogController.ts | 19 ++++-- 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/client/src/controllers/DialogController.test.js b/client/src/controllers/DialogController.test.js index 1e41bfc848..6b71a08ba3 100644 --- a/client/src/controllers/DialogController.test.js +++ b/client/src/controllers/DialogController.test.js @@ -103,6 +103,70 @@ describe('DialogController', () => { expect(document.documentElement.style.overflowY).toBe(''); }); + it('should support the ability to confirm the dialog with an event to indicate the confirmation', async () => { + const hiddenListener = jest.fn(); + document.addEventListener('w-dialog:hidden', hiddenListener); + + const confirmedListener = jest.fn(); + document.addEventListener('w-dialog:confirmed', confirmedListener); + + // Add a confirm button to the dialog + const dialogBody = document.getElementById('dialog-body'); + const confirmButton = document.createElement('button'); + confirmButton.type = 'button'; + confirmButton.setAttribute('data-action', 'w-dialog#confirm'); + dialogBody.appendChild(confirmButton); + + application.start(); + + await Promise.resolve(); + + expect(hiddenListener).not.toHaveBeenCalled(); + expect(confirmedListener).not.toHaveBeenCalled(); + + const dialog = document.getElementById('dialog-container'); + + // closed by default + expect(dialog.getAttribute('aria-hidden')).toEqual('true'); + expect(document.documentElement.style.overflowY).toBe(''); + + // show the dialog manually + dialog.dispatchEvent(new CustomEvent('w-dialog:show')); + + expect(dialog.getAttribute('aria-hidden')).toEqual(null); + expect(hiddenListener).not.toHaveBeenCalled(); + expect(confirmedListener).not.toHaveBeenCalled(); + // add style to root element on shown by default + expect(document.documentElement.style.overflowY).toBe('hidden'); + + // hide the dialog using the confirm button + confirmButton.click(); + + expect(dialog.getAttribute('aria-hidden')).toEqual('true'); + + // w-dialog:hide event should still be dispatched + expect(hiddenListener).toHaveBeenCalledWith( + expect.objectContaining({ + detail: expect.objectContaining({ + body: dialogBody, + dialog: expect.any(Object), + }), + }), + ); + // reset style on root element when hidden by default + expect(document.documentElement.style.overflowY).toBe(''); + + // w-dialog:confirmed event should be dispatched + expect(confirmedListener).toHaveBeenCalledWith( + expect.objectContaining({ + detail: expect.objectContaining({ + body: dialogBody, + dialog: expect.any(Object), + }), + }), + ); + }); + it('should support the ability use a theme to avoid document style change', async () => { const dialog = document.getElementById('dialog-container'); diff --git a/client/src/controllers/DialogController.ts b/client/src/controllers/DialogController.ts index 51dcba7b4b..a60d10369f 100644 --- a/client/src/controllers/DialogController.ts +++ b/client/src/controllers/DialogController.ts @@ -29,14 +29,17 @@ export class DialogController extends Controller<HTMLElement> { /** Optional targets that will be dispatched events for key dialog events. */ declare readonly notifyTargets: HTMLElement[]; + get eventDetail() { + return { body: this.bodyTarget, dialog: this.dialog }; + } + connect() { this.dialog = new A11yDialog(this.element); - const detail = { body: this.bodyTarget, dialog: this.dialog }; const isFloating = this.themeValue === FLOATING; this.dialog .on('show', () => { if (!isFloating) document.documentElement.style.overflowY = 'hidden'; - this.dispatch('shown', { detail, cancelable: false }); + this.dispatch('shown', { detail: this.eventDetail, cancelable: false }); this.notifyTargets.forEach((target) => { this.dispatch('shown', { target, @@ -47,7 +50,10 @@ export class DialogController extends Controller<HTMLElement> { }) .on('hide', () => { if (!isFloating) document.documentElement.style.overflowY = ''; - this.dispatch('hidden', { detail, cancelable: false }); + this.dispatch('hidden', { + detail: this.eventDetail, + cancelable: false, + }); this.notifyTargets.forEach((target) => { this.dispatch('hidden', { target, @@ -56,7 +62,7 @@ export class DialogController extends Controller<HTMLElement> { }); }); }); - this.dispatch('ready', { detail }); + this.dispatch('ready', { detail: this.eventDetail }); if (this.notifyTargets && Array.isArray(this.notifyTargets)) { this.notifyTargets.forEach((target) => { this.dispatch('ready', { target, bubbles: false, cancelable: false }); @@ -72,4 +78,9 @@ export class DialogController extends Controller<HTMLElement> { show() { this.dialog.show(); } + + confirm() { + this.hide(); + this.dispatch('confirmed', { detail: this.eventDetail, cancelable: false }); + } }