Skip to content

Commit 2852509

Browse files
authored
feat: separate input devices from pointer/keyboard API (#1003)
feat: * **pointer**: dispatch `auxclick` events fix: * **keyboard**: switch modifier state of lock keys on the correct event * **keyboard**: remove platform-specific additional key events for `Control` on `AltGraph` * **pointer**: dispatch `contextmenu` events with `detail: 0` * **pointer**: always set `PointerEvent.isPrimary` * **pointer**: set `button` property on pointer events separately from legacy mouse events * **pointer**: click closest common ancestor if `mousedown` and `mouseup` happen on different elements * **pointer**: omit click event on release if another button is released first * **pointer**: dispatch `mouseover`, `mouseenter` and `mousemove` on disabled elements * **pointer**: prevent `mouse*` events per `pointerdown` event handler * **pointer**: dispatch `*out` and `*over` events when moving into / out of nested elements * **pointer**: dispatch `*enter` and `*leave` events on ancestors
1 parent c6aafb7 commit 2852509

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1562
-1304
lines changed

src/convenience/hover.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export async function hover(this: Instance, element: Element) {
88
export async function unhover(this: Instance, element: Element) {
99
assertPointerEvents(
1010
this[Config],
11-
this[Config].pointerState.position.mouse.target as Element,
11+
this[Config].system.pointer.getMouseTarget(this[Config]),
1212
)
1313
return this.pointer({target: element.ownerDocument.body})
1414
}

src/event/behavior/keydown.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,12 @@ const keydownBehavior: {
102102
}
103103
}
104104
},
105-
Tab: (event, target, {keyboardState}) => {
105+
Tab: (event, target, config) => {
106106
return () => {
107-
const dest = getTabDestination(target, keyboardState.modifiers.Shift)
107+
const dest = getTabDestination(
108+
target,
109+
config.system.keyboard.modifiers.Shift,
110+
)
108111
focus(dest)
109112
if (hasOwnSelection(dest)) {
110113
setUISelection(dest, {
@@ -121,7 +124,7 @@ const combinationBehavior: BehaviorPlugin<'keydown'> = (
121124
target,
122125
config,
123126
) => {
124-
if (event.code === 'KeyA' && config.keyboardState.modifiers.Control) {
127+
if (event.code === 'KeyA' && config.system.keyboard.modifiers.Control) {
125128
return () => selectAll(target)
126129
}
127130
}

src/event/behavior/keypress.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ behavior.keypress = (event, target, config) => {
3737
if (isEditable(target)) {
3838
const inputType =
3939
event.key === 'Enter'
40-
? isContentEditable(target) && !config.keyboardState.modifiers.Shift
40+
? isContentEditable(target) && !config.system.keyboard.modifiers.Shift
4141
? 'insertParagraph'
4242
: 'insertLineBreak'
4343
: 'insertText'

src/event/createEvent.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {createEvent as createEventBase} from '@testing-library/dom'
2-
import {eventMap, eventMapKeys} from './eventMap'
3-
import {isMouseEvent} from './eventTypes'
2+
import {eventMap, eventMapKeys, isMouseEvent} from './eventMap'
43
import {EventType, PointerCoords} from './types'
54

65
export type EventTypeInit<K extends EventType> = SpecificEventInit<

src/event/eventMap.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import {eventMap as baseEventMap} from '@testing-library/dom/dist/event-map.js'
2+
import {EventType} from './types'
23

34
export const eventMap = {
45
...baseEventMap,
56

7+
auxclick: {
8+
// like other events this should be PointerEvent, but this is missing in Jsdom
9+
// see https://github.com/jsdom/jsdom/issues/2527
10+
EventType: 'MouseEvent',
11+
defaultInit: {bubbles: true, cancelable: true, composed: true},
12+
},
613
beforeInput: {
714
EventType: 'InputEvent',
815
defaultInit: {bubbles: true, cancelable: true, composed: true},
@@ -12,3 +19,17 @@ export const eventMap = {
1219
export const eventMapKeys: {
1320
[k in keyof DocumentEventMap]?: keyof typeof eventMap
1421
} = Object.fromEntries(Object.keys(eventMap).map(k => [k.toLowerCase(), k]))
22+
23+
function getEventClass(type: EventType) {
24+
const k = eventMapKeys[type]
25+
return k && eventMap[k].EventType
26+
}
27+
28+
const mouseEvents = ['MouseEvent', 'PointerEvent']
29+
export function isMouseEvent(type: EventType) {
30+
return mouseEvents.includes(getEventClass(type) as string)
31+
}
32+
33+
export function isKeyboardEvent(type: EventType) {
34+
return getEventClass(type) === 'KeyboardEvent'
35+
}

src/event/eventTypes.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/event/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import {Config} from '../setup'
2-
import {getUIEventModifiers} from '../utils'
32
import {createEvent, EventTypeInit} from './createEvent'
43
import {dispatchEvent} from './dispatchEvent'
5-
import {isKeyboardEvent, isMouseEvent} from './eventTypes'
4+
import {isKeyboardEvent, isMouseEvent} from './eventMap'
65
import {EventType, PointerCoords} from './types'
76

87
export type {EventType, PointerCoords}
@@ -17,7 +16,7 @@ export function dispatchUIEvent<K extends EventType>(
1716
if (isMouseEvent(type) || isKeyboardEvent(type)) {
1817
init = {
1918
...init,
20-
...getUIEventModifiers(config.keyboardState),
19+
...config.system.getUIEventModifiers(),
2120
} as EventTypeInit<K>
2221
}
2322

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export {userEvent as default} from './setup'
2-
export type {keyboardKey} from './keyboard'
3-
export type {pointerKey} from './pointer'
2+
export type {keyboardKey} from './system/keyboard'
3+
export type {pointerKey} from './system/pointer'
44
export {PointerEventsCheckLevel} from './options'

src/keyboard/index.ts

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,54 @@
11
import {Config, Instance} from '../setup'
2-
import {keyboardAction, KeyboardAction, releaseAllKeys} from './keyboardAction'
2+
import {keyboardKey} from '../system/keyboard'
3+
import {wait} from '../utils'
34
import {parseKeyDef} from './parseKeyDef'
4-
import type {keyboardState, keyboardKey} from './types'
55

6-
export {releaseAllKeys}
7-
export type {keyboardKey, keyboardState}
6+
interface KeyboardAction {
7+
keyDef: keyboardKey
8+
releasePrevious: boolean
9+
releaseSelf: boolean
10+
repeat: number
11+
}
812

913
export async function keyboard(this: Instance, text: string): Promise<void> {
1014
const actions: KeyboardAction[] = parseKeyDef(this[Config].keyboardMap, text)
1115

12-
return keyboardAction(this[Config], actions)
16+
for (let i = 0; i < actions.length; i++) {
17+
await wait(this[Config])
18+
19+
await keyboardAction(this[Config], actions[i])
20+
}
21+
}
22+
23+
async function keyboardAction(
24+
config: Config,
25+
{keyDef, releasePrevious, releaseSelf, repeat}: KeyboardAction,
26+
) {
27+
const {system} = config
28+
29+
// Release the key automatically if it was pressed before.
30+
if (system.keyboard.isKeyPressed(keyDef)) {
31+
await system.keyboard.keyup(config, keyDef)
32+
}
33+
34+
if (!releasePrevious) {
35+
for (let i = 1; i <= repeat; i++) {
36+
await system.keyboard.keydown(config, keyDef)
37+
38+
if (i < repeat) {
39+
await wait(config)
40+
}
41+
}
42+
43+
// Release the key only on the last iteration on `state.repeatKey`.
44+
if (releaseSelf) {
45+
await system.keyboard.keyup(config, keyDef)
46+
}
47+
}
1348
}
1449

15-
export function createKeyboardState(): keyboardState {
16-
return {
17-
activeElement: null,
18-
pressed: [],
19-
carryChar: '',
20-
modifiers: {
21-
Alt: false,
22-
AltGraph: false,
23-
Control: false,
24-
CapsLock: false,
25-
Fn: false,
26-
FnLock: false,
27-
Meta: false,
28-
NumLock: false,
29-
ScrollLock: false,
30-
Shift: false,
31-
Symbol: false,
32-
SymbolLock: false,
33-
},
34-
modifierPhase: {},
50+
export async function releaseAllKeys(config: Config) {
51+
for (const k of config.system.keyboard.getPressedKeys()) {
52+
await config.system.keyboard.keyup(config, k)
3553
}
3654
}

src/keyboard/keyMap.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {DOM_KEY_LOCATION, keyboardKey} from './types'
1+
import {DOM_KEY_LOCATION, keyboardKey} from '../system/keyboard'
22

33
/**
44
* Mapping for a default US-104-QWERTY keyboard

src/keyboard/keyboardAction.ts

Lines changed: 0 additions & 148 deletions
This file was deleted.

0 commit comments

Comments
 (0)