refactor(core): Remove custom event and replay behavior. (#55695)

These behaviors have been moved back to g3.

PR Close #55695
This commit is contained in:
Tom Wilkinson
2024-05-06 16:57:39 -05:00
committed by Andrew Scott
parent 7c9d37d798
commit eb31f2c775
8 changed files with 6 additions and 728 deletions
@@ -32,8 +32,6 @@ export class EventContract implements UnrenamedEventContract {
addEvent(eventType: string, prefixedEventType?: string): void;
cleanUp(): void;
// (undocumented)
static CUSTOM_EVENT_SUPPORT: boolean;
// (undocumented)
ecaacs?: (updateEventInfoForA11yClick: typeof a11yClickLib.updateEventInfoForA11yClick, preventDefaultForA11yClick: typeof a11yClickLib.preventDefaultForA11yClick, populateClickOnlyAction: typeof a11yClickLib.populateClickOnlyAction) => void;
ecrd(dispatcher: Dispatcher, restriction: Restriction): void;
exportAddA11yClickSupport(): void;
@@ -32,7 +32,6 @@ const DEFAULT_EVENT_TYPE: string = EventType.CLICK;
/** Resolves actions for Events. */
export class ActionResolver {
private a11yClickSupport: boolean = false;
private readonly customEventSupport: boolean;
private readonly syntheticMouseEventSupport: boolean;
private updateEventInfoForA11yClick?: (eventInfo: eventInfoLib.EventInfo) => void = undefined;
@@ -46,30 +45,14 @@ export class ActionResolver {
) => void = undefined;
constructor({
customEventSupport = false,
syntheticMouseEventSupport = false,
}: {
customEventSupport?: boolean;
syntheticMouseEventSupport?: boolean;
} = {}) {
this.customEventSupport = customEventSupport;
this.syntheticMouseEventSupport = syntheticMouseEventSupport;
}
resolve(eventInfo: eventInfoLib.EventInfo) {
if (this.customEventSupport) {
if (eventInfoLib.getEventType(eventInfo) === EventType.CUSTOM) {
const detail = (eventInfoLib.getEvent(eventInfo) as 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;
}
eventInfoLib.setEventType(eventInfo, detail['_type']);
}
}
this.populateAction(eventInfo);
}
@@ -1,124 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {EventType} from './/event_type';
// We use '_type' for the event contract, which lives in a separate
// compilation unit.
declare interface UnrenamedCustomEventDetail {
_type: string;
}
/** The detail interface provided for custom events. */
export interface CustomEventDetail<T> {
type: string;
data?: T;
triggeringEvent?: Event;
}
/**
* Create a custom event with the specified data.
* @param type The type of the action, e.g., 'submit'.
* @param data An optional data payload.
* @param triggeringEvent The event that triggers this custom event. This can be
* accessed from the custom event's action flow like so:
* actionFlow.event().detail.triggeringEvent.
* @return The new custom event.
*/
export function createCustomEvent<T>(type: string, data?: T, triggeringEvent?: Event): Event {
let event: CustomEvent<CustomEventDetail<T> & UnrenamedCustomEventDetail>;
const unrenamedDetail: UnrenamedCustomEventDetail = {
'_type': type,
};
const renamedDetail: CustomEventDetail<T> = {
type,
data,
triggeringEvent,
};
const detail = {...unrenamedDetail, ...renamedDetail};
try {
// We don't use the CustomEvent constructor directly since it isn't
// supported in IE 9 or 10 and initCustomEvent below works just fine.
event = document.createEvent('CustomEvent');
event.initCustomEvent(EventType.CUSTOM, true, false, detail);
} catch (e) {
// If custom events aren't supported, fall back to custom-named HTMLEvent.
// Fallback used by Android Gingerbread, FF4-5.
// Hack to emulate `CustomEvent`, `HTMLEvents` doesn't satisfy `CustomEvent`
// type.
// tslint:disable-next-line:no-any
event = document.createEvent('HTMLEvents') as any;
event.initEvent(EventType.CUSTOM, true, false);
// Hack to emulate `CustomEvent`, `detail` is readonly on `CustomEvent`.
// tslint:disable-next-line:no-any
(event as any)['detail'] = detail;
}
return event;
}
/**
* 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.
* @param triggeringEvent An optional data for the Event triggered this custom
* event.
*/
export function fireCustomEvent<T>(
target: Element,
type: string,
data?: T,
triggeringEvent?: Event,
) {
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);
};
}
@@ -17,9 +17,3 @@ export const A11Y_CLICK_SUPPORT = false;
* flag can be overridden in a build rule.
*/
export const MOUSE_SPECIAL_SUPPORT = false;
/**
* @define Support for custom events, which are type EventType.CUSTOM. These are
* native DOM events with an additional type field and an optional payload.
*/
export const CUSTOM_EVENT_SUPPORT = false;
@@ -35,11 +35,7 @@ import {ActionResolver} from './action_resolver';
import {EarlyJsactionData, EarlyJsactionDataContainer} from './earlyeventcontract';
import * as eventLib from './event';
import {EventContractContainerManager} from './event_contract_container';
import {
A11Y_CLICK_SUPPORT,
CUSTOM_EVENT_SUPPORT,
MOUSE_SPECIAL_SUPPORT,
} from './event_contract_defines';
import {A11Y_CLICK_SUPPORT, MOUSE_SPECIAL_SUPPORT} from './event_contract_defines';
import * as eventInfoLib from './event_info';
import {EventType} from './event_type';
import {Restriction} from './restriction';
@@ -87,14 +83,12 @@ type EventHandler = (eventType: string, event: Event, container: Element) => voi
* be delay loaded in a generic way.
*/
export class EventContract implements UnrenamedEventContract {
static CUSTOM_EVENT_SUPPORT = CUSTOM_EVENT_SUPPORT;
static A11Y_CLICK_SUPPORT = A11Y_CLICK_SUPPORT;
static MOUSE_SPECIAL_SUPPORT = MOUSE_SPECIAL_SUPPORT;
private containerManager: EventContractContainerManager | null;
private readonly actionResolver = new ActionResolver({
customEventSupport: EventContract.CUSTOM_EVENT_SUPPORT,
syntheticMouseEventSupport: EventContract.MOUSE_SPECIAL_SUPPORT,
});
@@ -135,9 +129,6 @@ export class EventContract implements UnrenamedEventContract {
constructor(containerManager: EventContractContainerManager) {
this.containerManager = containerManager;
if (EventContract.CUSTOM_EVENT_SUPPORT) {
this.addEvent(EventType.CUSTOM);
}
if (EventContract.A11Y_CLICK_SUPPORT) {
// Add a11y click support to the `EventContract`.
this.addA11yClickSupport();
@@ -1,459 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* @fileoverview Functions for replaying events by the jsaction
* Dispatcher.
* All ts-ignores in this file are due to APIs that are no longer in the browser.
*/
import {createCustomEvent} from './/custom_events';
import * as jsactionEvent from './/event';
import {EventType} from './/event_type';
type Writeable<T> = {
-readonly [P in keyof T]: T[P];
};
/**
* Replays an event.
*/
export function replayEvent(event: Event, targetElement: Element, eventType?: string) {
triggerEvent(targetElement, createEvent(event, eventType));
}
/**
* Checks if a given event was triggered by the keyboard.
* @param eventType The event type.
* @return Whether it's a keyboard event.
*/
function isKeyboardEvent(eventType: string): boolean {
return (
eventType === EventType.KEYPRESS ||
eventType === EventType.KEYDOWN ||
eventType === EventType.KEYUP
);
}
/**
* Checks if a given event was triggered by the mouse.
* @param eventType The event type.
* @return Whether it's a mouse event.
*/
function isMouseEvent(eventType: string): boolean {
// TODO: Verify if Drag events should be bound here.
return (
eventType === EventType.CLICK ||
eventType === EventType.DBLCLICK ||
eventType === EventType.MOUSEDOWN ||
eventType === EventType.MOUSEOVER ||
eventType === EventType.MOUSEOUT ||
eventType === EventType.MOUSEMOVE
);
}
/**
* Checks if a given event is a general UI event.
* @param eventType The event type.
* @return Whether it's a focus event.
*/
function isUiEvent(eventType: string): boolean {
// Almost nobody supports the W3C method of creating FocusEvents.
// For now, we're going to use the UIEvent as a super-interface.
return (
eventType === EventType.FOCUS ||
eventType === EventType.BLUR ||
eventType === EventType.FOCUSIN ||
eventType === EventType.FOCUSOUT ||
eventType === EventType.SCROLL
);
}
/**
* Create a whitespace-delineated list of modifier keys that should be
* considered to be active on the event's key. See details at
* https://developer.mozilla.org/en/DOM/KeyboardEvent.
* @param alt Alt pressed.
* @param ctrl Control pressed.
* @param meta Command pressed (OSX only).
* @param shift Shift pressed.
* @return The constructed modifier keys string.
*/
function createKeyboardModifiersList(
alt: boolean,
ctrl: boolean,
meta: boolean,
shift: boolean,
): string {
const keys = [];
if (alt) {
keys.push('Alt');
}
if (ctrl) {
keys.push('Control');
}
if (meta) {
keys.push('Meta');
}
if (shift) {
keys.push('Shift');
}
return keys.join(' ');
}
/**
* Creates a UI event object for replaying through the DOM.
* @param original The event to create a new event from.
* @param opt_eventType The type this event is being handled as by jsaction.
* e.g. blur events are handled as focusout
* @return The event object.
*/
export function createUiEvent(original: Event, opt_eventType?: string): Event {
let event: Writeable<UIEvent> & {originalTimestamp?: DOMHighResTimeStamp};
if (document.createEvent) {
const originalUiEvent = original as UIEvent;
// Event creation as per W3C event model specification. This codepath
// is used by most non-IE browsers and also by IE 9 and later.
event = document.createEvent('UIEvent');
// On IE and Opera < 12, we must provide non-undefined values to
// initEvent, otherwise it will fail.
event.initUIEvent(
opt_eventType || originalUiEvent.type,
originalUiEvent.bubbles !== undefined ? originalUiEvent.bubbles : true,
originalUiEvent.cancelable || false,
originalUiEvent.view || window,
(original as CustomEvent).detail || 0,
);
// detail
} else {
// Older versions of IE (up to version 8) do not support the
// W3C event model. Use the IE specific function instead.
// Suppressing errors for ts-migration.
// TS2339: Property 'createEventObject' does not exist on type 'Document'.
// @ts-ignore
event = document.createEventObject();
event.type = opt_eventType || original.type;
event.bubbles = original.bubbles !== undefined ? original.bubbles : true;
event.cancelable = original.cancelable || false;
event.view = (original as Writeable<UIEvent>).view || window;
event.detail = (original as CustomEvent).detail || 0;
}
// Some focus events also have a nullable relatedTarget value which isn't
// directly supported in the initUIEvent() method.
(event as Writeable<FocusEvent>).relatedTarget = (original as FocusEvent).relatedTarget || null;
event.originalTimestamp = original.timeStamp;
return event;
}
/**
* Creates a keyboard event object for replaying through the DOM.
* @param original The event to create a new event from.
* @param opt_eventType The type this event is being handled as by jsaction.
* E.g. a keypress is handled as click in some cases.
* @return The event object.
* @suppress {strictMissingProperties} Two definitions of initKeyboardEvent.
*/
export function createKeyboardEvent(original: Event, opt_eventType?: string): Event {
let event;
const keyboardEvent = original as KeyboardEvent;
if (document.createEvent) {
// Event creation as per W3C event model specification. This codepath
// is used by most non-IE browsers and also by IE 9 and later.
event = document.createEvent('KeyboardEvent');
if (event.initKeyboardEvent) {
if (jsactionEvent.isIe) {
// IE9+
// https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/ff975945(v=vs.85)
const modifiers = createKeyboardModifiersList(
keyboardEvent.altKey,
keyboardEvent.ctrlKey,
keyboardEvent.metaKey,
keyboardEvent.shiftKey,
);
event.initKeyboardEvent(
opt_eventType || keyboardEvent.type,
true,
true,
window,
keyboardEvent.key,
keyboardEvent.location,
// Suppressing errors for ts-migration.
// TS2345: Argument of type 'string' is not assignable to
// parameter of type 'boolean | undefined'.
// @ts-ignore
modifiers,
keyboardEvent.repeat,
// @ts-ignore This doesn't exist
keyboardEvent.locale,
);
} else {
// W3C DOM Level 3 Events model.
// https://www.w3.org/TR/uievents/#idl-interface-KeyboardEvent-initializers
event.initKeyboardEvent(
opt_eventType || original.type,
true,
true,
window,
keyboardEvent.key,
keyboardEvent.location,
keyboardEvent.ctrlKey,
keyboardEvent.altKey,
keyboardEvent.shiftKey,
keyboardEvent.metaKey,
);
Object.defineProperty(event, 'repeat', {
get: () => (original as KeyboardEvent).repeat,
enumerable: true,
});
// Add missing 'locale' which is not part of the spec.
// https://bugs.chromium.org/p/chromium/issues/detail?id=168971
Object.defineProperty(event, 'locale', {
// Suppressing errors for ts-migration.
// TS2339: Property 'locale' does not exist on type 'Event'.
// @ts-ignore
get: () => original.locale,
enumerable: true,
});
// Apple WebKit has a non-standard altGraphKey that is not implemented
// here.
// https://developer.apple.com/documentation/webkitjs/keyboardevent/1633753-initkeyboardevent
}
// Blink and Webkit had a bug that causes the `charCode`, `keyCode`, and
// `which` properties to always be unset when synthesizing a keyboard
// event. Details at: https://bugs.webkit.org/show_bug.cgi?id=16735. With
// these properties being deprecated, the bug has evolved into affecting
// the `key` property. We work around it by redefining the `key` and
// deprecated properties; a simple assignment here would fail because the
// native properties are readonly.
if (jsactionEvent.isWebKit) {
if (keyboardEvent.key && event.key === '') {
Object.defineProperty(event, 'key', {
get: () => keyboardEvent.key,
enumerable: true,
});
}
}
// Re-implement the deprecated `charCode`, `keyCode` and `which` which
// are also an issue on IE9+.
if (jsactionEvent.isWebKit || jsactionEvent.isIe || jsactionEvent.isGecko) {
Object.defineProperty(event, 'charCode', {
get: () => (original as KeyboardEvent).charCode,
enumerable: true,
});
const keyCodeGetter = () => (original as KeyboardEvent).keyCode;
Object.defineProperty(event, 'keyCode', {
get: keyCodeGetter,
enumerable: true,
});
Object.defineProperty(event, 'which', {
get: keyCodeGetter,
enumerable: true,
});
}
} else {
// Gecko only supports an older/deprecated version from DOM Level 2. See
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/initKeyEvent
// for details.
// @ts-ignore ditto
event.initKeyEvent(
opt_eventType || original.type,
true,
true,
window,
// Suppressing errors for ts-migration.
// TS2339: Property 'ctrlKey' does not exist on type 'Event'.
// @ts-ignore
original.ctrlKey,
// Suppressing errors for ts-migration.
// TS2339: Property 'altKey' does not exist on type 'Event'.
// @ts-ignore
original.altKey,
// Suppressing errors for ts-migration.
// TS2339: Property 'shiftKey' does not exist on type 'Event'.
// @ts-ignore
original.shiftKey,
// Suppressing errors for ts-migration.
// TS2339: Property 'metaKey' does not exist on type 'Event'.
// @ts-ignore
original.metaKey,
// Suppressing errors for ts-migration.
// TS2339: Property 'keyCode' does not exist on type 'Event'.
// @ts-ignore
original.keyCode,
// Suppressing errors for ts-migration.
// TS2339: Property 'charCode' does not exist on type 'Event'.
// @ts-ignore
original.charCode,
);
}
} else {
// Older versions of IE (up to version 8) do not support the
// W3C event model. Use the IE specific function instead.
// Suppressing errors for ts-migration.
// @ts-ignore
event = document.createEventObject();
event.type = opt_eventType || original.type;
const originalKeyboardEvent = original as KeyboardEvent;
event.repeat = originalKeyboardEvent.repeat;
event.ctrlKey = originalKeyboardEvent.ctrlKey;
event.altKey = originalKeyboardEvent.altKey;
event.shiftKey = originalKeyboardEvent.shiftKey;
event.metaKey = originalKeyboardEvent.metaKey;
event.key = originalKeyboardEvent.key;
event.keyCode = originalKeyboardEvent.keyCode;
event.charCode = originalKeyboardEvent.charCode;
}
event.originalTimestamp = original.timeStamp;
return event;
}
/**
* Creates a mouse event object for replaying through the DOM.
* @param original The event to create a new event from.
* @param opt_eventType The type this event is being handled as by jsaction.
* E.g. a keypress is handled as click in some cases.
* @return The event object.
*/
export function createMouseEvent(original: Event, opt_eventType?: string): MouseEvent {
let event;
const originalMouseEvent = original as MouseEvent;
if (document.createEvent) {
// Event creation as per W3C event model specification. This codepath
// is used by most non-IE browsers and also by IE 9 and later.
event = document.createEvent('MouseEvent');
// On IE and Opera < 12, we must provide non-undefined values to
// initMouseEvent, otherwise it will fail.
event.initMouseEvent(
opt_eventType || original.type,
true, // canBubble
true, // cancelable
window,
(original as CustomEvent).detail || 1,
originalMouseEvent.screenX || 0,
originalMouseEvent.screenY || 0,
originalMouseEvent.clientX || 0,
originalMouseEvent.clientY || 0,
originalMouseEvent.ctrlKey || false,
originalMouseEvent.altKey || false,
originalMouseEvent.shiftKey || false,
originalMouseEvent.metaKey || false,
originalMouseEvent.button || 0,
originalMouseEvent.relatedTarget || null,
);
} else {
// Older versions of IE (up to version 8) do not support the
// W3C event model. Use the IE specific function instead.
// @ts-ignore
event = document.createEventObject();
event.type = opt_eventType || original.type;
event.clientX = originalMouseEvent.clientX;
event.clientY = originalMouseEvent.clientY;
event.button = originalMouseEvent.button;
event.detail = (original as CustomEvent).detail;
event.ctrlKey = originalMouseEvent.ctrlKey;
event.altKey = originalMouseEvent.altKey;
event.shiftKey = originalMouseEvent.shiftKey;
event.metaKey = originalMouseEvent.metaKey;
}
event.originalTimestamp = original.timeStamp;
return event;
}
/**
* Creates a generic event object for replaying through the DOM.
* @param original The event to create a new event from.
* @param opt_eventType The type this event is being handled as by jsaction.
* E.g. a keypress is handled as click in some cases.
* @return The event object.
*/
function createGenericEvent(original: Event, opt_eventType?: string): Event {
let event;
if (document.createEvent) {
// Event creation as per W3C event model specification. This codepath
// is used by most non-IE browsers and also by IE 9 and later.
event = document.createEvent('Event');
event.initEvent(opt_eventType || original.type, true, true);
} else {
// Older versions of IE (up to version 8) do not support the
// W3C event model. Use the IE specific function instead.
// Suppressing errors for ts-migration.
// TS2339: Property 'createEventObject' does not exist on type 'Document'.
// @ts-ignore
event = document.createEventObject();
event.type = opt_eventType || original.type;
}
event.originalTimestamp = original.timeStamp;
return event;
}
/**
* Creates an event object for replaying through the DOM.
* NOTE: This function is visible just for testing. Please don't use
* it outside JsAction internal testing.
* TODO: Add support for FocusEvent and WheelEvent.
* @param base The event to create a new event from.
* @param opt_eventType The type this event is being handled as by jsaction.
* E.g. a keypress is handled as click in some cases.
* @return The event object.
*/
export function createEvent(base: unknown, opt_eventType?: string): Event {
const original = base as Event;
let event;
let eventType;
if (original.type === EventType.CUSTOM) {
eventType = EventType.CUSTOM;
} else {
eventType = opt_eventType || original.type;
}
if (isKeyboardEvent(eventType)) {
event = createKeyboardEvent(original, opt_eventType);
} else if (isMouseEvent(eventType)) {
event = createMouseEvent(original, opt_eventType);
} else if (isUiEvent(eventType)) {
event = createUiEvent(original, opt_eventType);
} else if (eventType === EventType.CUSTOM) {
event = createCustomEvent(
opt_eventType!,
(original as CustomEvent)['detail']['data'],
(original as CustomEvent)['detail']['triggeringEvent'],
);
(event as {originalTimestamp?: number | null} | null)!.originalTimestamp = original.timeStamp;
} else {
// This ensures we don't send an undefined event object to the replayer.
event = createGenericEvent(original, opt_eventType);
}
return event;
}
/**
* Sends an event for replay to the DOM.
* @param target The target for the event.
* @param event The event object.
* @return The return value of the event replay, i.e., whether preventDefault()
* was called on it.
*/
export function triggerEvent(target: EventTarget, event: Event): boolean {
if (target.dispatchEvent) {
return target.dispatchEvent(event);
} else {
// Suppressing errors for ts-migration.
// TS2339: Property 'fireEvent' does not exist on type 'Element'.
// @ts-ignore
return (target as Element).fireEvent('on' + event.type, event);
}
}
/** Do not use outiside of testing. */
export const testing = {
createKeyboardModifiersList,
createGenericEvent,
isKeyboardEvent,
isMouseEvent,
isUiEvent,
};
@@ -14,10 +14,9 @@ import {
EventInfoWrapper,
unsetAction,
} from '../src/event_info';
import {createEvent} from '../src/replay';
function createMockClickEvent() {
return createEvent({type: 'click'} as Event);
function createClickEvent() {
return new MouseEvent('click', {bubbles: true, cancelable: true});
}
function createTestActionInfo({
@@ -29,7 +28,7 @@ function createTestActionInfo({
function createTestEventInfo({
eventType = 'click',
event = createMockClickEvent(),
event = createClickEvent(),
targetElement = document.createElement('div'),
container = document.createElement('div'),
timestamp = 0,
@@ -74,7 +73,7 @@ describe('dispatcher test.ts', () => {
}),
);
expect(eventInfo).not.toBeNull();
expect(eventInfo.getEventType()).toBe(createMockClickEvent().type);
expect(eventInfo.getEventType()).toBe('click');
expect(eventInfo.getAction()!.element).toBe(actionElement);
});
@@ -219,7 +218,7 @@ describe('dispatcher test.ts', () => {
dispatcher.registerGlobalHandler('click', handler);
const eventInfo = createTestEventInfo({
event: createEvent({type: 'mousedown'} as Event),
event: new MouseEvent('mousedown', {bubbles: true, cancelable: true}),
eventType: 'mousedown',
});
unsetAction(eventInfo);
@@ -7,7 +7,6 @@
*/
import * as cache from '../src/cache';
import {bootstrapCustomEventSupport, fireCustomEvent} from '../src/custom_events';
import {stopPropagation} from '../src/dispatcher';
import {
EarlyEventContract,
@@ -99,12 +98,6 @@ const domContent = `
</div>
</div>
<div id="custom-event-container">
<div id="custom-event-action-element" jsaction="custom-event:handleCustomEvent">
<div id="custom-event-target-element"></div>
</div>
</div>
<div id="anchor-click-container">
<a id="anchor-click-action-element" href="javascript:void(0);" jsaction="handleClick">
<span id="anchor-click-target-element"></span>
@@ -338,7 +331,6 @@ describe('EventContract', () => {
safeElement.setInnerHtml(document.body, testonlyHtml(domContent));
EventContract.A11Y_CLICK_SUPPORT = false;
EventContract.MOUSE_SPECIAL_SUPPORT = false;
EventContract.CUSTOM_EVENT_SUPPORT = false;
// Normalize timestamp.
spyOn(Date, 'now').and.returnValue(0);
@@ -852,102 +844,6 @@ describe('EventContract', () => {
expect(eventContract.handler(EventType.CLICK)).toBeUndefined();
});
describe('custom event', () => {
beforeEach(() => {
EventContract.CUSTOM_EVENT_SUPPORT = true;
});
it('dispatches event', () => {
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');
createEventContract({
eventContractContainerManager: new EventContractContainer(container),
eventTypes: ['custom-event'],
dispatcher,
});
// createEvent/initEvent is used to support IE11
// tslint:disable:deprecation
const customEvent = document.createEvent('Event');
customEvent.initEvent('custom-event', true, true);
// tslint:enable:deprecation
targetElement.dispatchEvent(customEvent);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('custom-event');
expect(eventInfoWrapper.getEvent()).toBe(customEvent);
expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
expect(eventInfoWrapper.getAction()?.name).toBe('handleCustomEvent');
expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
});
it('dispatches event with data', () => {
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');
createEventContract({
eventContractContainerManager: new EventContractContainer(container),
eventTypes: ['custom-event'],
dispatcher,
});
const data = {'test': 1};
fireCustomEvent(targetElement, 'custom-event', data);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('custom-event');
const customEvent = eventInfoWrapper.getEvent() as CustomEvent;
expect(customEvent.type).toBe(EventType.CUSTOM);
expect(customEvent.detail.data).toBe(data);
expect(eventInfoWrapper.getTargetElement()).toBe(targetElement);
expect(eventInfoWrapper.getAction()?.name).toBe('handleCustomEvent');
expect(eventInfoWrapper.getAction()?.element).toBe(actionElement);
});
});
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(1);
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');