Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix(type): fire type events on active element (#299)
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
  • Loading branch information
kayleighridd and kentcdodds committed Jun 4, 2020
1 parent 5dec819 commit a6f1326
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 15 deletions.
62 changes: 61 additions & 1 deletion src/__tests__/type.js
@@ -1,4 +1,4 @@
import React from 'react'
import React, {Fragment} from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '../../src'

Expand Down Expand Up @@ -256,3 +256,63 @@ test.each(['input', 'textarea'])(
expect(onKeyUp).not.toHaveBeenCalled()
},
)

test('should fire events on the currently focused element', async () => {
const changeFocusLimit = 7
const onKeyDown = jest.fn(event => {
if (event.target.value.length === changeFocusLimit) {
screen.getByTestId('input2').focus()
}
})

render(
<Fragment>
<input data-testid="input1" onKeyDown={onKeyDown} />
<input data-testid="input2" />
</Fragment>,
)

const text = 'Hello, world!'

const input1 = screen.getByTestId('input1')
const input2 = screen.getByTestId('input2')

await userEvent.type(input1, text)

expect(input1).toHaveValue(text.slice(0, changeFocusLimit))
expect(input2).toHaveValue(text.slice(changeFocusLimit))
expect(input2).toHaveFocus()
})

test('should enter text up to maxLength of the current element if provided', async () => {
const changeFocusLimit = 7
const input2MaxLength = 2

const onKeyDown = jest.fn(event => {
if (event.target.value.length === changeFocusLimit) {
screen.getByTestId('input2').focus()
}
})

render(
<>
<input data-testid="input" onKeyDown={onKeyDown} />
<input data-testid="input2" maxLength={input2MaxLength} />
</>,
)

const text = 'Hello, world!'
const input2ExpectedValue = text.slice(
changeFocusLimit,
changeFocusLimit + input2MaxLength,
)

const input1 = screen.getByTestId('input')
const input2 = screen.getByTestId('input2')

await userEvent.type(input1, text)

expect(input1).toHaveValue(text.slice(0, changeFocusLimit))
expect(input2).toHaveValue(input2ExpectedValue)
expect(input2).toHaveFocus()
})
37 changes: 23 additions & 14 deletions src/index.js
Expand Up @@ -334,21 +334,30 @@ async function type(...args) {

async function typeImpl(element, text, {allAtOnce = false, delay} = {}) {
if (element.disabled) return
const previousText = element.value

const computedText =
element.maxLength > 0
? text.slice(0, Math.max(element.maxLength - previousText.length, 0))
element.focus()

// The focussed element could change between each event, so get the currently active element each time
const currentElement = () => element.ownerDocument.activeElement
const currentValue = () => element.ownerDocument.activeElement.value

const computeText = () =>
currentElement().maxLength > 0
? text.slice(
0,
Math.max(currentElement().maxLength - currentValue().length, 0),
)
: text

if (allAtOnce) {
if (!element.readOnly) {
const previousText = element.value

fireEvent.input(element, {
target: {value: previousText + computedText},
target: {value: previousText + computeText()},
})
}
} else {
let actuallyTyped = previousText
for (let index = 0; index < text.length; index++) {
const char = text[index]
const key = char // TODO: check if this also valid for characters with diacritic markers e.g. úé etc
Expand All @@ -357,28 +366,28 @@ async function typeImpl(element, text, {allAtOnce = false, delay} = {}) {
// eslint-disable-next-line no-await-in-loop
if (delay > 0) await wait(delay)

const downEvent = fireEvent.keyDown(element, {
if (currentElement().disabled) return

const downEvent = fireEvent.keyDown(currentElement(), {
key,
keyCode,
which: keyCode,
})

if (downEvent) {
const pressEvent = fireEvent.keyPress(element, {
const pressEvent = fireEvent.keyPress(currentElement(), {
key,
keyCode,
charCode: keyCode,
})

const isTextPastThreshold =
(actuallyTyped + key).length > (previousText + computedText).length
const isTextPastThreshold = !computeText().length

if (pressEvent && !isTextPastThreshold) {
actuallyTyped += key
if (!element.readOnly) {
fireEvent.input(element, {
fireEvent.input(currentElement(), {
target: {
value: actuallyTyped,
value: currentValue() + key,
},
bubbles: true,
cancelable: true,
Expand All @@ -387,7 +396,7 @@ async function typeImpl(element, text, {allAtOnce = false, delay} = {}) {
}
}

fireEvent.keyUp(element, {
fireEvent.keyUp(currentElement(), {
key,
keyCode,
which: keyCode,
Expand Down

0 comments on commit a6f1326

Please sign in to comment.