Skip to content

Arrowleft handler does not respect preventDefault resulting in incorrect selectionStart #578

@adamayres

Description

@adamayres

Environment

  • @testing-library/user-event@^12.8.1
  • jest@^26.6.3
  • jsdom@^16.4.0

What you did

  • Created an input element that has a keydown event handler that calls event.preventDefault() if the event.key is ArrowLeft.
  • Use userEvent.type to enter text into the input element.
  • Use userEvent.type to enter the {arrowleft} special key.
  • Attempted to assert that the input's selectionStart was not moved by the arrow left key press.

What happened:

When using userEvent.type to enter the {arrowleft} special key the selectionStart is updated despite preventDefault being called on the event by an event handler attached on the input element.

Reproduction repository:

https://codesandbox.io/s/react-testing-library-selectionstart-issue-yb15q?file=/src/App.test.js

export default function CustomInput() {
  return (
    <input
      data-testid="testInput"
      type="text"
      onKeyDown={(event) => {
        if (event.key === "ArrowLeft") {
          event.preventDefault();
        }
      }}
    />
  );
}
describe("user-event {arrowkey}", () => {
  it("should respect preventDefault when updating selectionStart", () => {
    const { container } = render(<CustomInput />);

    const inputElement = getByTestId(container, "testInput");
    userEvent.type(inputElement, "Hello, World!");

    expect(inputElement.selectionStart).toBe(13);

    userEvent.type(inputElement, "{arrowleft}");

    expect(inputElement.selectionStart).toBe(13);
  });
});

Problem description:

The preventDefault does not appear to be respected with regards to using the arrow keys when determining the selectionStart.

In type.js I can see that the arrow keys uses navigationKey which does not appear to have any handling for if the event default was prevented.

[specialCharMap.arrowLeft]: navigationKey('ArrowLeft')

However, I do see that some other characters do have special handling for this use case, for example the backspace key:

[specialCharMap.backspace]: handleBackspace,
...

function handleBackspace({currentElement, eventOverrides}) {
  ...

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

  if (keyPressDefaultNotPrevented) {
    fireInputEventIfNeeded({

Suggested solution:

Minimally it seems like navigationKey method should have support for not updating the selectionStart when the event default is prevented. However, I see that in other cases in type.js there are other special key event handlers that also do not respect event.preventDefault, namely: handleArrowDown, handleArrowUp, handleEsc, handleSelectAll, and handleSpace. It's worth noting that some of these handlers for the special keys utilize multiple key events (keyDown and keyUp as opposed to keyPress). Perhaps in all cases where they are custom event handlers attached to these native events there should be some checking if the default was prevented.

Related and possibly duplicative

Apologies if this issue has already been captured elsewhere in sufficient detail.

#515
#521
#322
#404

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions