Skip to content

Commit 86a545d

Browse files
committed
refactor(cdk/testing): switch to event constructor for creating fake events
Switches from the legacy `init<..>Event` calls to the Event constructors given that IE11 no longer is supported.
1 parent b25283c commit 86a545d

File tree

4 files changed

+37
-97
lines changed

4 files changed

+37
-97
lines changed

src/cdk/a11y/fake-event-detection.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ export function isFakeMousedownFromScreenReader(event: MouseEvent): boolean {
1111
// Some screen readers will dispatch a fake `mousedown` event when pressing enter or space on
1212
// a clickable element. We can distinguish these events when both `offsetX` and `offsetY` are
1313
// zero. Note that there's an edge case where the user could click the 0x0 spot of the screen
14-
// themselves, but that is unlikely to contain interaction elements. Historially we used to check
15-
// `event.buttons === 0`, however that no longer works on recent versions of NVDA.
14+
// themselves, but that is unlikely to contain interaction elements. Historically we used to
15+
// check `event.buttons === 0`, however that no longer works on recent versions of NVDA.
1616
return event.offsetX === 0 && event.offsetY === 0;
1717
}
1818

src/cdk/testing/testbed/fake-events/dispatch-events.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ export function dispatchEvent<T extends Event>(node: Node | Window, event: T): T
2828
* Shorthand to dispatch a fake event on a specified node.
2929
* @docs-private
3030
*/
31-
export function dispatchFakeEvent(node: Node | Window, type: string, canBubble?: boolean): Event {
32-
return dispatchEvent(node, createFakeEvent(type, canBubble));
31+
export function dispatchFakeEvent(node: Node | Window, type: string, bubbles?: boolean): Event {
32+
return dispatchEvent(node, createFakeEvent(type, bubbles));
3333
}
3434

3535
/**

src/cdk/testing/testbed/fake-events/element-focus.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ function triggerFocusChange(element: HTMLElement, event: 'focus' | 'blur') {
2121

2222
/**
2323
* Patches an elements focus and blur methods to emit events consistently and predictably.
24-
* This is necessary, because some browsers, like IE11, will call the focus handlers asynchronously,
24+
* This is necessary, because some browsers can call the focus handlers asynchronously,
2525
* while others won't fire them at all if the browser window is not focused.
2626
* @docs-private
2727
*/
28+
// TODO: Check if this element focus patching is still needed for local testing,
29+
// where browser is not necessarily focused.
2830
export function patchElementFocus(element: HTMLElement) {
2931
element.focus = () => dispatchFakeEvent(element, 'focus');
3032
element.blur = () => dispatchFakeEvent(element, 'blur');

src/cdk/testing/testbed/fake-events/event-objects.ts

Lines changed: 30 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -17,44 +17,33 @@ let uniqueIds = 0;
1717
*/
1818
export function createMouseEvent(
1919
type: string, clientX = 0, clientY = 0, button = 0, modifiers: ModifierKeys = {}) {
20-
const event = document.createEvent('MouseEvent');
21-
const originalPreventDefault = event.preventDefault.bind(event);
22-
2320
// Note: We cannot determine the position of the mouse event based on the screen
2421
// because the dimensions and position of the browser window are not available
2522
// To provide reasonable `screenX` and `screenY` coordinates, we simply use the
2623
// client coordinates as if the browser is opened in fullscreen.
2724
const screenX = clientX;
2825
const screenY = clientY;
2926

30-
event.initMouseEvent(type,
31-
/* canBubble */ true,
32-
/* cancelable */ true,
33-
/* view */ window,
34-
/* detail */ 0,
35-
/* screenX */ screenX,
36-
/* screenY */ screenY,
37-
/* clientX */ clientX,
38-
/* clientY */ clientY,
39-
/* ctrlKey */ !!modifiers.control,
40-
/* altKey */ !!modifiers.alt,
41-
/* shiftKey */ !!modifiers.shift,
42-
/* metaKey */ !!modifiers.meta,
43-
/* button */ button,
44-
/* relatedTarget */ null);
27+
const event = new MouseEvent(type, {
28+
bubbles: true,
29+
cancelable: true,
30+
screenX,
31+
screenY,
32+
clientX,
33+
clientY,
34+
ctrlKey: modifiers.control,
35+
altKey: modifiers.alt,
36+
shiftKey: modifiers.shift,
37+
metaKey: modifiers.meta,
38+
button: button,
39+
buttons: 1,
40+
});
4541

46-
// `initMouseEvent` doesn't allow us to pass these properties into the constructor.
47-
// Override them to 1, because they're used for fake screen reader event detection.
48-
defineReadonlyEventProperty(event, 'buttons', 1);
42+
// The `MouseEvent` constructor doesn't allow us to pass these properties into the constructor.
43+
// Override them to `1`, because they're used for fake screen reader event detection.
4944
defineReadonlyEventProperty(event, 'offsetX', 1);
5045
defineReadonlyEventProperty(event, 'offsetY', 1);
5146

52-
// IE won't set `defaultPrevented` on synthetic events so we need to do it manually.
53-
event.preventDefault = function() {
54-
defineReadonlyEventProperty(event, 'defaultPrevented', true);
55-
return originalPreventDefault();
56-
};
57-
5847
return event;
5948
}
6049

@@ -85,8 +74,8 @@ export function createPointerEvent(type: string, clientX = 0, clientY = 0,
8574
* @docs-private
8675
*/
8776
export function createTouchEvent(type: string, pageX = 0, pageY = 0, clientX = 0, clientY = 0) {
88-
// In favor of creating events that work for most of the browsers, the event is created
89-
// as a basic UI Event. The necessary details for the event will be set manually.
77+
// We cannot use the `TouchEvent` or `Touch` because Firefox and Safari lack support.
78+
// TODO: Switch to the constructor API when it is available for Firefox and Safari.
9079
const event = document.createEvent('UIEvent');
9180
const touchDetails = {pageX, pageY, clientX, clientY, id: uniqueIds++};
9281

@@ -108,75 +97,24 @@ export function createTouchEvent(type: string, pageX = 0, pageY = 0, clientX = 0
10897
*/
10998
export function createKeyboardEvent(type: string, keyCode: number = 0, key: string = '',
11099
modifiers: ModifierKeys = {}) {
111-
const event = document.createEvent('KeyboardEvent');
112-
const originalPreventDefault = event.preventDefault.bind(event);
113-
114-
// Firefox does not support `initKeyboardEvent`, but supports `initKeyEvent`.
115-
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/initKeyEvent.
116-
if ((event as any).initKeyEvent !== undefined) {
117-
(event as any).initKeyEvent(type, true, true, window, modifiers.control, modifiers.alt,
118-
modifiers.shift, modifiers.meta, keyCode);
119-
} else {
120-
// `initKeyboardEvent` expects to receive modifiers as a whitespace-delimited string
121-
// See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/initKeyboardEvent
122-
let modifiersList = '';
123-
124-
if (modifiers.control) {
125-
modifiersList += 'Control ';
126-
}
127-
128-
if (modifiers.alt) {
129-
modifiersList += 'Alt ';
130-
}
131-
132-
if (modifiers.shift) {
133-
modifiersList += 'Shift ';
134-
}
135-
136-
if (modifiers.meta) {
137-
modifiersList += 'Meta ';
138-
}
139-
140-
// TS3.6 removed the `initKeyboardEvent` method and suggested porting to
141-
// `new KeyboardEvent()` constructor. We cannot use that as we support IE11.
142-
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/initKeyboardEvent.
143-
(event as any).initKeyboardEvent(type,
144-
true, /* canBubble */
145-
true, /* cancelable */
146-
window, /* view */
147-
0, /* char */
148-
key, /* key */
149-
0, /* location */
150-
modifiersList.trim(), /* modifiersList */
151-
false /* repeat */);
152-
}
153-
154-
// Webkit Browsers don't set the keyCode when calling the init function.
155-
// See related bug https://bugs.webkit.org/show_bug.cgi?id=16735
156-
defineReadonlyEventProperty(event, 'keyCode', keyCode);
157-
defineReadonlyEventProperty(event, 'key', key);
158-
defineReadonlyEventProperty(event, 'ctrlKey', !!modifiers.control);
159-
defineReadonlyEventProperty(event, 'altKey', !!modifiers.alt);
160-
defineReadonlyEventProperty(event, 'shiftKey', !!modifiers.shift);
161-
defineReadonlyEventProperty(event, 'metaKey', !!modifiers.meta);
162-
163-
// IE won't set `defaultPrevented` on synthetic events so we need to do it manually.
164-
event.preventDefault = function() {
165-
defineReadonlyEventProperty(event, 'defaultPrevented', true);
166-
return originalPreventDefault();
167-
};
168-
169-
return event;
100+
return new KeyboardEvent(type, {
101+
bubbles: true,
102+
cancelable: true,
103+
keyCode: keyCode,
104+
key: key,
105+
shiftKey: modifiers.shift,
106+
metaKey: modifiers.meta,
107+
altKey: modifiers.alt,
108+
ctrlKey: modifiers.control,
109+
});
170110
}
171111

172112
/**
173113
* Creates a fake event object with any desired event type.
174114
* @docs-private
175115
*/
176-
export function createFakeEvent(type: string, canBubble = false, cancelable = true) {
177-
const event = document.createEvent('Event');
178-
event.initEvent(type, canBubble, cancelable);
179-
return event;
116+
export function createFakeEvent(type: string, bubbles = false, cancelable = true) {
117+
return new Event(type, {bubbles, cancelable});
180118
}
181119

182120
/**

0 commit comments

Comments
 (0)