From 29d73a3b689995f30ef998a33e079218340683a2 Mon Sep 17 00:00:00 2001 From: Chiemezuo Date: Tue, 17 Oct 2023 12:21:48 +0100 Subject: [PATCH] DialogController add support for notifyTargets - Allowing the dispatching of events to internal targets for key modal events - See ##11029 --- CHANGELOG.txt | 1 + .../src/controllers/DialogController.test.js | 70 +++++++++++++++++++ client/src/controllers/DialogController.ts | 27 ++++++- docs/releases/5.2.md | 1 + 4 files changed, 96 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 7d772ff738..4a926f313f 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -115,6 +115,7 @@ Changelog * Maintenance: Add generic `InspectView` to `ModelViewSet` (Sage Abdullah) * Maintenance: Migrate select all on focus/click behavior to Stimulus, used on the image URL generator (Chiemezuo Akujobi) * Maintenance: Add support for a `reset` method to support Stimulus driven dynamic field resets via the `w-action` controller (Chiemezuo Akujobi) + * Maintenance: Add support for a `notify` target on the Stimulus dialog for dispatching events internally (Chiemezuo Akujobi) 5.1.3 (xx.xx.20xx) - IN DEVELOPMENT ~~~~~~~~~~~~~~~~~~ diff --git a/client/src/controllers/DialogController.test.js b/client/src/controllers/DialogController.test.js index 41ea2bdb95..1e41bfc848 100644 --- a/client/src/controllers/DialogController.test.js +++ b/client/src/controllers/DialogController.test.js @@ -136,4 +136,74 @@ describe('DialogController', () => { expect(hiddenListener).toHaveBeenCalled(); }); }); + + describe('dispatching events internally via notify targets', () => { + const eventHandler = jest.fn(); + + beforeEach(() => { + application?.stop(); + + document.body.innerHTML = ` +
+ +
`; + + const doc = document.getElementById('inner-content'); + doc.addEventListener('w-dialog:shown', eventHandler); + doc.addEventListener('w-dialog:hidden', eventHandler); + doc.addEventListener('w-dialog:ready', eventHandler); + + application = new Application(); + application.register('w-dialog', DialogController); + + application.start(); + }); + + afterEach(() => { + document.body.innerHTML = ''; + jest.clearAllMocks(); + }); + + it('should dispatch events to notify targets', async () => { + const dialogContainer = document.getElementById('dialog-container'); + + dialogContainer.dispatchEvent(new CustomEvent('w-dialog:show')); + + // twice, because of show and ready + expect(eventHandler).toHaveBeenCalledTimes(2); + // checking the first mock function called + expect(eventHandler.mock.calls[0][0]).toMatchObject({ + type: 'w-dialog:ready', + bubbles: false, + }); + // checking the second mock function called + expect(eventHandler.mock.calls[1][0]).toMatchObject({ + type: 'w-dialog:shown', + bubbles: false, + }); + + dialogContainer.dispatchEvent(new CustomEvent('w-dialog:hide')); + + // called once again, therefore 3 times + expect(eventHandler).toHaveBeenCalledTimes(3); + // checking the third mock function called + expect(eventHandler.mock.calls[2][0]).toMatchObject({ + type: 'w-dialog:hidden', + bubbles: false, + }); + }); + }); }); diff --git a/client/src/controllers/DialogController.ts b/client/src/controllers/DialogController.ts index 54e8b782f2..51dcba7b4b 100644 --- a/client/src/controllers/DialogController.ts +++ b/client/src/controllers/DialogController.ts @@ -21,11 +21,13 @@ export class DialogController extends Controller { theme: { default: '', type: String }, }; - static targets = ['body']; + static targets = ['body', 'notify']; declare dialog: A11yDialog; declare readonly bodyTarget: HTMLElement; declare readonly themeValue: string; + /** Optional targets that will be dispatched events for key dialog events. */ + declare readonly notifyTargets: HTMLElement[]; connect() { this.dialog = new A11yDialog(this.element); @@ -34,13 +36,32 @@ export class DialogController extends Controller { this.dialog .on('show', () => { if (!isFloating) document.documentElement.style.overflowY = 'hidden'; - this.dispatch('shown', { detail }); + this.dispatch('shown', { detail, cancelable: false }); + this.notifyTargets.forEach((target) => { + this.dispatch('shown', { + target, + bubbles: false, + cancelable: false, + }); + }); }) .on('hide', () => { if (!isFloating) document.documentElement.style.overflowY = ''; - this.dispatch('hidden', { detail }); + this.dispatch('hidden', { detail, cancelable: false }); + this.notifyTargets.forEach((target) => { + this.dispatch('hidden', { + target, + bubbles: false, + cancelable: false, + }); + }); }); this.dispatch('ready', { detail }); + if (this.notifyTargets && Array.isArray(this.notifyTargets)) { + this.notifyTargets.forEach((target) => { + this.dispatch('ready', { target, bubbles: false, cancelable: false }); + }); + } return this.dialog; } diff --git a/docs/releases/5.2.md b/docs/releases/5.2.md index d6dcfe3961..23402b7870 100644 --- a/docs/releases/5.2.md +++ b/docs/releases/5.2.md @@ -146,6 +146,7 @@ This feature was developed by Paarth Agarwal and Thibaud Colas as part of the Go * Add generic `InspectView` to `ModelViewSet` (Sage Abdullah) * Migrate select all on focus/click behavior to Stimulus, used on the image URL generator (Chiemezuo Akujobi) * Add support for a `reset` method to support Stimulus driven dynamic field resets via the `w-action` controller (Chiemezuo Akujobi) + * Add support for a `notify` target on the Stimulus dialog for dispatching events internally (Chiemezuo Akujobi) ## Upgrade considerations - changes affecting all projects