Skip to content

Commit

Permalink
refactor(core): Add a boostrapCustomEvent method. (#55603)
Browse files Browse the repository at this point in the history
This can replace CUSTOM_EVENT_SUPPORT by allowing users to bootstrap
custom event support separately from the `EventContract`.

PR Close #55603
  • Loading branch information
tbondwilkinson authored and AndrewKushnir committed May 1, 2024
1 parent b9a997c commit bf25dfa
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 1 deletion.
40 changes: 40 additions & 0 deletions packages/core/primitives/event-dispatch/src/custom_events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ export function createCustomEvent<T>(type: string, data?: T, triggeringEvent?: E
* Fires a custom event with an optional payload. Only intended to be consumed
* by jsaction itself. Supported in Firefox 6+, IE 9+, and all Chrome versions.
*
* `bootstrapCustomEventSupport` is required to add a listener that handles the
* event.
*
* @param target The target element.
* @param type The type of the action, e.g., 'submit'.
* @param data An optional data payload.
Expand All @@ -82,3 +85,40 @@ export function fireCustomEvent<T>(
const event = createCustomEvent(type, data, triggeringEvent);
target.dispatchEvent(event);
}

/**
* Bootstraps `CustomEvent` support on the container.
*
* This is required to handle events fired by `fireCustomEvent` in the `container`.
*
* @param container The JSAction container to add an event listener on.
* @param trigger A function that can trigger JSAction Event dispatch. Generally this function
* should delegate to an `EventContract` handler for the event type provided defined by
* `event.detail['_type']`.
* @returns A function that removes the event listener.
*/
export function bootstrapCustomEventSupport(container: Element, trigger: (event: Event) => void) {
const customEventListener = (event: Event) => {
let customEvent = event as CustomEvent;
const detail = customEvent.detail;
// For custom events, use a secondary dispatch based on the internal
// custom type of the event.
if (!detail || !detail['_type']) {
// This should never happen.
return;
}
// Mirrors code from Wiz's internal `trigger()` method.
const syntheticCustomEvent = {
'type': detail['_type'],
'target': event.target,
'bubbles': true,
detail,
};
// tslint:disable-next-line:no-any
trigger(syntheticCustomEvent as any);
};
container.addEventListener('_custom', customEventListener);
return () => {
container.removeEventListener('_custom', customEventListener);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import * as cache from '../src/cache';
import {fireCustomEvent} from '../src/custom_events';
import {bootstrapCustomEventSupport, fireCustomEvent} from '../src/custom_events';
import {stopPropagation} from '../src/dispatcher';
import {EarlyEventContract, EarlyJsactionData} from '../src/earlyeventcontract';
import {
Expand Down Expand Up @@ -899,6 +899,42 @@ describe('EventContract', () => {
});
});

describe('bootstrap custom event', () => {
it('dispatches with fireCustomEvent', () => {
const container = getRequiredElementById('custom-event-container');
const targetElement = getRequiredElementById('custom-event-target-element');
const actionElement = getRequiredElementById('custom-event-action-element');

const dispatcher = jasmine.createSpy<Dispatcher>('dispatcher');
const eventContract = createEventContract({
eventContractContainerManager: new EventContractContainer(container),
eventTypes: [],
dispatcher,
});
bootstrapCustomEventSupport(container, (event) => {
let handler = eventContract.handler(event.type);
if (!handler) {
eventContract.addEvent(event.type);
handler = eventContract.handler(event.type);
}
handler!(event.type, event, container);
});

const data = {'test': 1};
fireCustomEvent(targetElement, 'custom-event', data);

expect(dispatcher).toHaveBeenCalledTimes(2);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('custom-event');
const customEvent = eventInfoWrapper.getEvent() as CustomEvent;
expect(customEvent.type).toBe('custom-event');
expect(customEvent.detail.data).toBe(data);
expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
expect(eventInfoWrapper.getAction()?.name).toBe('handleCustomEvent');
expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
});
});

it('prevents default for click on anchor child', () => {
const container = getRequiredElementById('anchor-click-container');
const actionElement = getRequiredElementById('anchor-click-action-element');
Expand Down

0 comments on commit bf25dfa

Please sign in to comment.