0
0
mirror of https://github.com/wagtail/wagtail.git synced 2024-11-25 05:02:57 +01:00

Enhance Stimulus InitController to support additional custom event dispatching

This commit is contained in:
LB Johnston 2024-01-15 06:06:31 +10:00 committed by LB (Ben Johnston)
parent 0ee97d45de
commit b93508f4ce
2 changed files with 58 additions and 1 deletions

View File

@ -103,4 +103,49 @@ describe('InitController', () => {
expect(handleEvent).toHaveBeenCalledTimes(1);
});
});
describe('when using custom event names', () => {
const handleEvent = jest.fn();
document.addEventListener('w-init:ready', handleEvent);
document.addEventListener('custom:event', handleEvent);
document.addEventListener('other-custom:event', handleEvent);
beforeAll(() => {
jest.clearAllMocks();
application?.stop();
// intentionally adding extra spaces in the event-value below
const events = 'custom:event other-custom:event ';
document.body.innerHTML = `
<div
id="test"
class="hide-me"
data-controller="w-init"
data-w-init-event-value="${events}"
>
Test body
</div>
`;
application = Application.start();
});
it('should dispatch additional events', async () => {
expect(handleEvent).not.toHaveBeenCalled();
application.register('w-init', InitController);
await Promise.resolve(); // no delay, just wait for the next tick
expect(handleEvent).toHaveBeenCalledTimes(3);
expect(handleEvent.mock.calls.map(([event]) => event.type)).toEqual([
'w-init:ready',
'custom:event',
'other-custom:event',
]);
});
});
});

View File

@ -5,21 +5,28 @@ import { debounce } from '../utils/debounce';
* Adds the ability for a controlled element to add or remove classes
* when ready to be interacted with.
*
* @example
* @example - Dynamic classes when ready
* <div class="keep-me hide-me" data-controller="w-init" data-w-init-remove-class="hide-me" data-w-init-ready-class="loaded">
* When the DOM is ready, this div will have the class 'loaded' added and 'hide-me' removed.
* </div>
*
* @example - Custom event dispatching
* <div class="keep-me hide-me" data-controller="w-init" data-w-init-event-value="custom:event other-custom:event">
* When the DOM is ready, two additional custom events will be dispatched; `custom:event` and `other-custom:event`.
* </div>
*/
export class InitController extends Controller<HTMLElement> {
static classes = ['ready', 'remove'];
static values = {
delay: { default: -1, type: Number },
event: { default: '', type: String },
};
declare readonly readyClasses: string[];
declare readonly removeClasses: string[];
declare eventValue: string;
declare delayValue: number;
connect() {
@ -31,14 +38,19 @@ export class InitController extends Controller<HTMLElement> {
* By default, the action will be immediate (negative value).
* Even when immediate, allow for a microtask delay to allow for other
* controllers to connect, then do any updates do classes/dispatch events.
* Support the ability to also dispatch custom event names.
*/
ready() {
const events = this.eventValue.split(' ').filter(Boolean);
const delayValue = this.delayValue;
debounce(() => true, delayValue < 0 ? null : delayValue)().then(() => {
this.element.classList.add(...this.readyClasses);
this.element.classList.remove(...this.removeClasses);
this.dispatch('ready', { bubbles: true, cancelable: false });
events.forEach((name) => {
this.dispatch(name, { bubbles: true, cancelable: false, prefix: '' });
});
this.remove();
});
}