testing-library / user-event

🐕 Simulate user events
https://testing-library.com/user-event
MIT License
2.14k stars 242 forks source link

Arrow keys on radio buttons are backwards #1198

Open joshferrell opened 4 months ago

joshferrell commented 4 months ago

Reproduction example

https://codesandbox.io/p/devbox/github/joshferrell/user-event-radio-demo/tree/main/

Prerequisites

Create a series of radio inputs

<label><input type="radio" name="group" value="a" />A</label>
<label><input type="radio" name="group" value="d" />D</label>
<label><input type="radio" name="group" value="e" />E</label>

Create a test that confirms radio behavior using the userEvent keyboard function

describe('Radio implementation', () => {
    test.each([
      {
        name: "per [ArrowDown]",
        focus: 'A',
        key: "[ArrowDown]",
        expectedTarget: 'D',
      },
      {
        name: "per [ArrowLeft]",
        focus: 'D',
        key: "[ArrowLeft]",
        expectedTarget: 'A',
      },
      {
        name: "per [ArrowRight]",
        focus: 'A',
        key: "[ArrowRight]",
        expectedTarget: 'D',
      },
      {
        name: "per [ArrowUp]",
        focus: 'D',
        key: "[ArrowUp]",
        expectedTarget: 'A',
      },
      {
        name: "forward around the corner",
        focus: 'E',
        key: "[ArrowRight]",
        expectedTarget: 'A',
      },
      {
        name: "backward around the corner",
        focus: 'A',
        key: "[ArrowUp]",
        expectedTarget: 'E',
      },
    ])('$name', async ({ focus, key, expectedTarget }) => {
      const user = userEvent.setup()
        render(<App />)
        screen.getByLabelText(focus).focus()
        await user.keyboard(key)
        expect(screen.getByLabelText(expectedTarget)).toHaveFocus()
    })
})

Expected behavior

Behavior

Arrow Right

If A is selected and ArrowRight is pressed, D should be selected If E is selected and ArrowRight is pressed, A should be selected

Arrow Down

If A is selected and ArrowDown is pressed, D should be selected If E is selected and ArrowDown is pressed, A should be selected

Arrow Left

If D is selected and ArrowLeft is pressed, A should be selected If A is selected and ArrowLeft is pressed, E should be selected

Arrow Up

If D is selected and ArrowUp is pressed, A should be selected If A is selected and ArrowUp is pressed, E should be selected

Actual behavior

Behavior

Arrow Right

If A is selected and ArrowRight is pressed, E is selected If E is selected and ArrowRight is pressed, D is selected

Arrow Down

If A is selected and ArrowDown is pressed, D is selected If E is selected and ArrowDown is pressed, A is selected

Arrow Left

If D is selected and ArrowLeft is pressed, E is selected If A is selected and ArrowLeft is pressed, D is selected

Arrow Up

If D is selected and ArrowUp is pressed, A is selected If A is selected and ArrowUp is pressed, E is selected

User-event version

14.5.2

Environment

Testing Library framework: @testing-library/react@14.2.1 and @testing-library/user-event:14.5.2

JS framework: react@18.2.0

Test environment: vitest@1.3.1 Also ran in the user-event test suite and had the same issue

DOM implementation: jsdom@24.0.0

Additional context

There are two issues with the current implementation of the keyboard event and handling radios.

  1. The walk radio function does not quit the for loop if it finds the next element
  2. Arrow Down and Arrow Up directions are inverted.