Skip to content

Commit 7f97369

Browse files
committed
Add support for modifier keys to typeInElement
1 parent 748ee9a commit 7f97369

File tree

6 files changed

+82
-84
lines changed

6 files changed

+82
-84
lines changed

src/cdk/keycodes/modifiers.spec.ts

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,51 @@ import {hasModifierKey} from './modifiers';
44
describe('keyboard modifiers', () => {
55
it('should check whether the alt key is pressed', () => {
66
const event = createKeyboardEvent('keydown', 0);
7+
const altEvent = createKeyboardEvent('keydown', 0, '', undefined, {alt: true});
78

89
expect(hasModifierKey(event)).toBe(false);
9-
Object.defineProperty(event, 'altKey', {get: () => true});
10-
expect(hasModifierKey(event)).toBe(true);
10+
expect(hasModifierKey(altEvent)).toBe(true);
1111
});
1212

1313
it('should check whether the shift key is pressed', () => {
1414
const event = createKeyboardEvent('keydown', 0);
15+
const shiftEvent = createKeyboardEvent('keydown', 0, '', undefined, {shift: true});
1516

1617
expect(hasModifierKey(event)).toBe(false);
17-
Object.defineProperty(event, 'shiftKey', {get: () => true});
18-
expect(hasModifierKey(event)).toBe(true);
18+
expect(hasModifierKey(shiftEvent)).toBe(true);
1919
});
2020

2121
it('should check whether the meta key is pressed', () => {
2222
const event = createKeyboardEvent('keydown', 0);
23+
const metaEvent = createKeyboardEvent('keydown', 0, '', undefined, {meta: true});
2324

2425
expect(hasModifierKey(event)).toBe(false);
25-
Object.defineProperty(event, 'metaKey', {get: () => true});
26-
expect(hasModifierKey(event)).toBe(true);
26+
expect(hasModifierKey(metaEvent)).toBe(true);
2727
});
2828

2929
it('should check whether the ctrl key is pressed', () => {
3030
const event = createKeyboardEvent('keydown', 0);
31+
const ctrlEvent = createKeyboardEvent('keydown', 0, '', undefined, {control: true});
3132

3233
expect(hasModifierKey(event)).toBe(false);
33-
Object.defineProperty(event, 'ctrlKey', {get: () => true});
34-
expect(hasModifierKey(event)).toBe(true);
34+
expect(hasModifierKey(ctrlEvent)).toBe(true);
3535
});
3636

3737
it('should check if a particular modifier key is pressed', () => {
38-
const event = createKeyboardEvent('keydown', 0);
39-
Object.defineProperty(event, 'ctrlKey', {get: () => true});
38+
const ctrlEvent = createKeyboardEvent('keydown', 0, '', undefined, {control: true});
39+
const ctrlAltEvent = createKeyboardEvent(
40+
'keydown', 0, '', undefined, {control: true, alt: true});
4041

41-
expect(hasModifierKey(event, 'altKey')).toBe(false);
42-
Object.defineProperty(event, 'altKey', {get: () => true});
43-
expect(hasModifierKey(event, 'altKey')).toBe(true);
42+
expect(hasModifierKey(ctrlEvent, 'altKey')).toBe(false);
43+
expect(hasModifierKey(ctrlAltEvent, 'altKey')).toBe(true);
4444
});
4545

4646
it('should check if multiple specific modifier keys are pressed', () => {
47-
const event = createKeyboardEvent('keydown', 0);
48-
Object.defineProperty(event, 'ctrlKey', {get: () => true});
47+
const ctrlEvent = createKeyboardEvent('keydown', 0, '', undefined, {control: true});
48+
const ctrlAltShiftEvent = createKeyboardEvent(
49+
'keydown', 0, '', undefined, {control: true, alt: true, shift: true});
4950

50-
expect(hasModifierKey(event, 'altKey', 'shiftKey')).toBe(false);
51-
Object.defineProperty(event, 'altKey', {get: () => true});
52-
Object.defineProperty(event, 'shiftKey', {get: () => true});
53-
expect(hasModifierKey(event, 'altKey', 'shiftKey')).toBe(true);
51+
expect(hasModifierKey(ctrlEvent, 'altKey', 'shiftKey')).toBe(false);
52+
expect(hasModifierKey(ctrlAltShiftEvent, 'altKey', 'shiftKey')).toBe(true);
5453
});
55-
5654
});

src/cdk/testing/dispatch-events.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
createFakeEvent,
1111
createKeyboardEvent,
1212
createMouseEvent,
13-
createTouchEvent
13+
createTouchEvent,
14+
ModifierKeys
1415
} from './event-objects';
1516

1617
/**
@@ -34,9 +35,10 @@ export function dispatchFakeEvent(node: Node | Window, type: string, canBubble?:
3435
* Shorthand to dispatch a keyboard event with a specified key code.
3536
* @docs-private
3637
*/
37-
export function dispatchKeyboardEvent(node: Node, type: string, keyCode: number, key?: string,
38-
target?: Element): KeyboardEvent {
39-
return dispatchEvent(node, createKeyboardEvent(type, keyCode, key, target)) as KeyboardEvent;
38+
export function dispatchKeyboardEvent(node: Node, type: string, keyCode?: number, key?: string,
39+
target?: Element, modifiers?: ModifierKeys): KeyboardEvent {
40+
return dispatchEvent(node,
41+
createKeyboardEvent(type, keyCode, key, target, modifiers)) as KeyboardEvent;
4042
}
4143

4244
/**

src/cdk/testing/event-objects.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
/** Modifier keys that may be held while typing. */
10+
export interface ModifierKeys {
11+
control?: boolean;
12+
alt?: boolean;
13+
shift?: boolean;
14+
meta?: boolean;
15+
}
16+
917
/**
1018
* Creates a browser MouseEvent with the specified options.
1119
* @docs-private
@@ -63,23 +71,31 @@ export function createTouchEvent(type: string, pageX = 0, pageY = 0) {
6371
* Dispatches a keydown event from an element.
6472
* @docs-private
6573
*/
66-
export function createKeyboardEvent(type: string, keyCode: number, key?: string, target?: Element) {
74+
export function createKeyboardEvent(type: string, keyCode: number = 0, key: string = '',
75+
target?: Element, modifiers: ModifierKeys = {}) {
6776
let event = document.createEvent('KeyboardEvent') as any;
6877
let originalPreventDefault = event.preventDefault;
6978

7079
// Firefox does not support `initKeyboardEvent`, but supports `initKeyEvent`.
7180
if (event.initKeyEvent) {
72-
event.initKeyEvent(type, true, true, window, 0, 0, 0, 0, 0, keyCode);
81+
event.initKeyEvent(type, true, true, window, modifiers.control, modifiers.alt, modifiers.shift,
82+
modifiers.meta, keyCode);
7383
} else {
74-
event.initKeyboardEvent(type, true, true, window, 0, key, 0, '', false);
84+
let modifiersStr = (modifiers.control ? 'Control ' : '' + modifiers.alt ? 'Alt ' : '' +
85+
modifiers.shift ? 'Shift ' : '' + modifiers.meta ? 'Meta' : '').trim();
86+
event.initKeyboardEvent(type, true, true, window, 0, key, 0, modifiersStr, false);
7587
}
7688

7789
// Webkit Browsers don't set the keyCode when calling the init function.
7890
// See related bug https://bugs.webkit.org/show_bug.cgi?id=16735
7991
Object.defineProperties(event, {
8092
keyCode: { get: () => keyCode },
8193
key: { get: () => key },
82-
target: { get: () => target }
94+
target: { get: () => target },
95+
ctrlKey: { get: () => !!modifiers.control },
96+
altKey: { get: () => !!modifiers.alt },
97+
shiftKey: { get: () => !!modifiers.shift },
98+
metaKey: { get: () => !!modifiers.meta }
8399
});
84100

85101
// IE won't set `defaultPrevented` on synthetic events so we need to do it manually.

src/cdk/testing/type-in-element.ts

Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,7 @@
88

99
import {dispatchFakeEvent, dispatchKeyboardEvent} from './dispatch-events';
1010
import {triggerFocus} from './element-focus';
11-
12-
/** Modifier keys that may be held while typing. */
13-
export interface KeyModifiers {
14-
control?: boolean;
15-
alt?: boolean;
16-
shift?: boolean;
17-
meta?: boolean;
18-
}
19-
20-
/**
21-
* Represents a special key that does not result in a character being inputed in a text field.
22-
* @docs-private
23-
*/
24-
export interface SpecialKey {
25-
keyCode: number;
26-
key?: string;
27-
}
11+
import {ModifierKeys} from './event-objects';
2812

2913
/**
3014
* Checks whether the given Element is a text input element.
@@ -42,7 +26,8 @@ export function isTextInput(element: Element): element is HTMLInputElement | HTM
4226
* @param keys The keys to send to the element.
4327
* @docs-private
4428
*/
45-
export function typeInElement(element: HTMLElement, ...keys: (string | SpecialKey)[]): void;
29+
export function typeInElement(
30+
element: HTMLElement, ...keys: (string | {keyCode?: number, key?: string})[]): void;
4631

4732
/**
4833
* Focuses an input, sets its value and dispatches
@@ -52,40 +37,34 @@ export function typeInElement(element: HTMLElement, ...keys: (string | SpecialKe
5237
* @param keys The keys to send to the element.
5338
* @docs-private
5439
*/
55-
export function typeInElement(
56-
element: HTMLElement, modifiers: KeyModifiers, ...keys: (string | SpecialKey)[]): void;
40+
export function typeInElement(element: HTMLElement, modifiers: ModifierKeys,
41+
...keys: (string | {keyCode?: number, key?: string})[]): void;
5742

5843
export function typeInElement(element: HTMLElement, ...modifiersAndKeys: any) {
5944
const first = modifiersAndKeys[0];
60-
let modifiers: KeyModifiers;
61-
let keys: (string | SpecialKey)[];
62-
if (typeof first !== 'string' && first.keyCode === undefined) {
45+
let modifiers: ModifierKeys;
46+
let rest: (string | {keyCode?: number, key?: string})[];
47+
if (typeof first !== 'string' && first.keyCode === undefined && first.key === undefined) {
6348
modifiers = first;
64-
keys = modifiersAndKeys.slice(1);
49+
rest = modifiersAndKeys.slice(1);
6550
} else {
6651
modifiers = {};
67-
keys = modifiersAndKeys;
52+
rest = modifiersAndKeys;
6853
}
54+
const keys: {keyCode?: number, key?: string}[] = rest
55+
.map(k => typeof k === 'string' ?
56+
k.split('').map(c => ({keyCode: c.toUpperCase().charCodeAt(0), key: c})) : [k])
57+
.reduce((arr, k) => arr.concat(k), []);
6958

70-
// TODO: pass through modifiers
7159
triggerFocus(element);
72-
for (const keyOrStr of keys) {
73-
if (typeof keyOrStr === 'string') {
74-
for (const key of keyOrStr) {
75-
const keyCode = key.charCodeAt(0);
76-
dispatchKeyboardEvent(element, 'keydown', keyCode);
77-
dispatchKeyboardEvent(element, 'keypress', keyCode);
78-
if (isTextInput(element)) {
79-
element.value += key;
80-
dispatchFakeEvent(element, 'input');
81-
}
82-
dispatchKeyboardEvent(element, 'keyup', keyCode);
83-
}
84-
} else {
85-
dispatchKeyboardEvent(element, 'keydown', keyOrStr.keyCode, keyOrStr.key);
86-
dispatchKeyboardEvent(element, 'keypress', keyOrStr.keyCode, keyOrStr.key);
87-
dispatchKeyboardEvent(element, 'keyup', keyOrStr.keyCode, keyOrStr.key);
60+
for (const key of keys) {
61+
dispatchKeyboardEvent(element, 'keydown', key.keyCode, key.key, element, modifiers);
62+
dispatchKeyboardEvent(element, 'keypress', key.keyCode, key.key, element, modifiers);
63+
if (isTextInput(element) && key.key && key.key.length === 1) {
64+
element.value += key.key;
65+
dispatchFakeEvent(element, 'input');
8866
}
67+
dispatchKeyboardEvent(element, 'keyup', key.keyCode, key.key, element, modifiers);
8968
}
9069
}
9170

src/material/autocomplete/autocomplete.spec.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,6 @@ import {
5151
MatAutocompleteTrigger,
5252
} from './index';
5353

54-
function old__typeInElement(value: string, element: HTMLInputElement) {
55-
element.focus();
56-
element.value = value;
57-
dispatchFakeEvent(element, 'input');
58-
}
59-
6054
describe('MatAutocomplete', () => {
6155
let overlayContainer: OverlayContainer;
6256
let overlayContainerElement: HTMLElement;

tools/public_api_guard/cdk/testing.d.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ export declare function clearElement(element: HTMLInputElement | HTMLTextAreaEle
22

33
export declare function createFakeEvent(type: string, canBubble?: boolean, cancelable?: boolean): Event;
44

5-
export declare function createKeyboardEvent(type: string, keyCode: number, key?: string, target?: Element): any;
5+
export declare function createKeyboardEvent(type: string, keyCode?: number, key?: string, target?: Element, modifiers?: ModifierKeys): any;
66

77
export declare function createMouseEvent(type: string, x?: number, y?: number, button?: number): MouseEvent;
88

@@ -12,23 +12,32 @@ export declare function dispatchEvent(node: Node | Window, event: Event): Event;
1212

1313
export declare function dispatchFakeEvent(node: Node | Window, type: string, canBubble?: boolean): Event;
1414

15-
export declare function dispatchKeyboardEvent(node: Node, type: string, keyCode: number, key?: string, target?: Element): KeyboardEvent;
15+
export declare function dispatchKeyboardEvent(node: Node, type: string, keyCode?: number, key?: string, target?: Element, modifiers?: ModifierKeys): KeyboardEvent;
1616

1717
export declare function dispatchMouseEvent(node: Node, type: string, x?: number, y?: number, event?: MouseEvent): MouseEvent;
1818

1919
export declare function dispatchTouchEvent(node: Node, type: string, x?: number, y?: number): Event;
2020

2121
export declare function isTextInput(element: Element): element is HTMLInputElement | HTMLTextAreaElement;
2222

23-
export declare function patchElementFocus(element: HTMLElement): void;
24-
25-
export interface SpecialKey {
26-
key?: string;
27-
keyCode: number;
23+
export interface ModifierKeys {
24+
alt?: boolean;
25+
control?: boolean;
26+
meta?: boolean;
27+
shift?: boolean;
2828
}
2929

30+
export declare function patchElementFocus(element: HTMLElement): void;
31+
3032
export declare function triggerBlur(element: HTMLElement): void;
3133

3234
export declare function triggerFocus(element: HTMLElement): void;
3335

34-
export declare function typeInElement(element: HTMLElement, ...value: (string | SpecialKey)[]): void;
36+
export declare function typeInElement(element: HTMLElement, ...keys: (string | {
37+
keyCode?: number;
38+
key?: string;
39+
})[]): void;
40+
export declare function typeInElement(element: HTMLElement, modifiers: ModifierKeys, ...keys: (string | {
41+
keyCode?: number;
42+
key?: string;
43+
})[]): void;

0 commit comments

Comments
 (0)