Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

separate input devices from pointer/keyboard API #1003

Merged
merged 7 commits into from Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/convenience/hover.ts
Expand Up @@ -8,7 +8,7 @@ export async function hover(this: Instance, element: Element) {
export async function unhover(this: Instance, element: Element) {
assertPointerEvents(
this[Config],
this[Config].pointerState.position.mouse.target as Element,
this[Config].system.pointer.getMouseTarget(this[Config]),
)
return this.pointer({target: element.ownerDocument.body})
}
9 changes: 6 additions & 3 deletions src/event/behavior/keydown.ts
Expand Up @@ -102,9 +102,12 @@ const keydownBehavior: {
}
}
},
Tab: (event, target, {keyboardState}) => {
Tab: (event, target, config) => {
return () => {
const dest = getTabDestination(target, keyboardState.modifiers.Shift)
const dest = getTabDestination(
target,
config.system.keyboard.modifiers.Shift,
)
focus(dest)
if (hasOwnSelection(dest)) {
setUISelection(dest, {
Expand All @@ -121,7 +124,7 @@ const combinationBehavior: BehaviorPlugin<'keydown'> = (
target,
config,
) => {
if (event.code === 'KeyA' && config.keyboardState.modifiers.Control) {
if (event.code === 'KeyA' && config.system.keyboard.modifiers.Control) {
return () => selectAll(target)
}
}
2 changes: 1 addition & 1 deletion src/event/behavior/keypress.ts
Expand Up @@ -37,7 +37,7 @@ behavior.keypress = (event, target, config) => {
if (isEditable(target)) {
const inputType =
event.key === 'Enter'
? isContentEditable(target) && !config.keyboardState.modifiers.Shift
? isContentEditable(target) && !config.system.keyboard.modifiers.Shift
? 'insertParagraph'
: 'insertLineBreak'
: 'insertText'
Expand Down
3 changes: 1 addition & 2 deletions src/event/createEvent.ts
@@ -1,6 +1,5 @@
import {createEvent as createEventBase} from '@testing-library/dom'
import {eventMap, eventMapKeys} from './eventMap'
import {isMouseEvent} from './eventTypes'
import {eventMap, eventMapKeys, isMouseEvent} from './eventMap'
import {EventType, PointerCoords} from './types'

export type EventTypeInit<K extends EventType> = SpecificEventInit<
Expand Down
21 changes: 21 additions & 0 deletions src/event/eventMap.ts
@@ -1,8 +1,15 @@
import {eventMap as baseEventMap} from '@testing-library/dom/dist/event-map.js'
import {EventType} from './types'

export const eventMap = {
...baseEventMap,

auxclick: {
// like other events this should be PointerEvent, but this is missing in Jsdom
// see https://github.com/jsdom/jsdom/issues/2527
EventType: 'MouseEvent',
defaultInit: {bubbles: true, cancelable: true, composed: true},
},
beforeInput: {
EventType: 'InputEvent',
defaultInit: {bubbles: true, cancelable: true, composed: true},
Expand All @@ -12,3 +19,17 @@ export const eventMap = {
export const eventMapKeys: {
[k in keyof DocumentEventMap]?: keyof typeof eventMap
} = Object.fromEntries(Object.keys(eventMap).map(k => [k.toLowerCase(), k]))

function getEventClass(type: EventType) {
const k = eventMapKeys[type]
return k && eventMap[k].EventType
}

const mouseEvents = ['MouseEvent', 'PointerEvent']
export function isMouseEvent(type: EventType) {
return mouseEvents.includes(getEventClass(type) as string)
}

export function isKeyboardEvent(type: EventType) {
return getEventClass(type) === 'KeyboardEvent'
}
22 changes: 0 additions & 22 deletions src/event/eventTypes.ts

This file was deleted.

5 changes: 2 additions & 3 deletions src/event/index.ts
@@ -1,8 +1,7 @@
import {Config} from '../setup'
import {getUIEventModifiers} from '../utils'
import {createEvent, EventTypeInit} from './createEvent'
import {dispatchEvent} from './dispatchEvent'
import {isKeyboardEvent, isMouseEvent} from './eventTypes'
import {isKeyboardEvent, isMouseEvent} from './eventMap'
import {EventType, PointerCoords} from './types'

export type {EventType, PointerCoords}
Expand All @@ -17,7 +16,7 @@ export function dispatchUIEvent<K extends EventType>(
if (isMouseEvent(type) || isKeyboardEvent(type)) {
init = {
...init,
...getUIEventModifiers(config.keyboardState),
...config.system.getUIEventModifiers(),
} as EventTypeInit<K>
}

Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
@@ -1,4 +1,4 @@
export {userEvent as default} from './setup'
export type {keyboardKey} from './keyboard'
export type {pointerKey} from './pointer'
export type {keyboardKey} from './system/keyboard'
export type {pointerKey} from './system/pointer'
export {PointerEventsCheckLevel} from './options'
68 changes: 43 additions & 25 deletions src/keyboard/index.ts
@@ -1,36 +1,54 @@
import {Config, Instance} from '../setup'
import {keyboardAction, KeyboardAction, releaseAllKeys} from './keyboardAction'
import {keyboardKey} from '../system/keyboard'
import {wait} from '../utils'
import {parseKeyDef} from './parseKeyDef'
import type {keyboardState, keyboardKey} from './types'

export {releaseAllKeys}
export type {keyboardKey, keyboardState}
interface KeyboardAction {
keyDef: keyboardKey
releasePrevious: boolean
releaseSelf: boolean
repeat: number
}

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

return keyboardAction(this[Config], actions)
for (let i = 0; i < actions.length; i++) {
await wait(this[Config])

await keyboardAction(this[Config], actions[i])
}
}

async function keyboardAction(
config: Config,
{keyDef, releasePrevious, releaseSelf, repeat}: KeyboardAction,
) {
const {system} = config

// Release the key automatically if it was pressed before.
if (system.keyboard.isKeyPressed(keyDef)) {
await system.keyboard.keyup(config, keyDef)
}

if (!releasePrevious) {
for (let i = 1; i <= repeat; i++) {
await system.keyboard.keydown(config, keyDef)

if (i < repeat) {
await wait(config)
}
}

// Release the key only on the last iteration on `state.repeatKey`.
if (releaseSelf) {
await system.keyboard.keyup(config, keyDef)
}
}
}

export function createKeyboardState(): keyboardState {
return {
activeElement: null,
pressed: [],
carryChar: '',
modifiers: {
Alt: false,
AltGraph: false,
Control: false,
CapsLock: false,
Fn: false,
FnLock: false,
Meta: false,
NumLock: false,
ScrollLock: false,
Shift: false,
Symbol: false,
SymbolLock: false,
},
modifierPhase: {},
export async function releaseAllKeys(config: Config) {
for (const k of config.system.keyboard.getPressedKeys()) {
await config.system.keyboard.keyup(config, k)
}
}
2 changes: 1 addition & 1 deletion src/keyboard/keyMap.ts
@@ -1,4 +1,4 @@
import {DOM_KEY_LOCATION, keyboardKey} from './types'
import {DOM_KEY_LOCATION, keyboardKey} from '../system/keyboard'

/**
* Mapping for a default US-104-QWERTY keyboard
Expand Down
148 changes: 0 additions & 148 deletions src/keyboard/keyboardAction.ts

This file was deleted.