Skip to content

<div role="listbox"> should be focusable #1338

Closed
@Stan-Stani

Description

@Stan-Stani
  • @testing-library/dom version:
  • Testing Framework and version:

jest@npm:29.7.0
"@testing-library/jest-dom": "^6.5.0",
"@testing-library/react": "^15.0.7",

  • DOM Environment: "@testing-library/jest-dom": "^6.6.2",
    "jest-environment-jsdom": "^29.7.0",

Problem description:

Jest does not treat a div with role="listbox" as focusable.

Relevant code or config:

This code should fail its test if the "listbox" in the dropdown component does not have tabIndex={-1}, but it does not error because dom-testing-library doesn't treat a div with role="listbox" as focusable as far as I can tell.

 it.only('should not allow focus of dropdown-options container itself', async () => {
    render(
      <>
        <button>I start focused</button>
        <Dropdown options={options} />
        <input type='text' />
      </>
    )

    const elementThatStartsFocused = screen.getByRole('button')
    const dropdownOptionsContainerDivItself =
      screen.getByTestId('dropdown-options')
    const allTextBoxes = screen.getAllByRole('textbox')
    const externalOtherInput = allTextBoxes[allTextBoxes.length - 1]
    const allDropDownOptions = screen.getAllByTestId('dropdown-option')

    // Focus button above dd
    await userEvent.click(elementThatStartsFocused)
    expect(elementThatStartsFocused).toHaveFocus()

    console.log('clickedButton', document.activeElement?.outerHTML)

    // Focus dd option
    await userEvent.tab()

    // Wait for the dropdown to become visible and options to be rendered
    await waitFor(() => {
      expect(screen.getAllByTestId('dropdown-option')).toHaveLength(
        options.length
      )
    })

    await waitFor(() => {
      expect(allDropDownOptions[0]).toHaveFocus()
    })

    console.log('tabbedintofirstoption', document.activeElement?.outerHTML)

    expect(dropdownOptionsContainerDivItself).not.toHaveFocus()
    await userEvent.click(externalOtherInput)
    console.log('clikedInput', document.activeElement?.outerHTML)

    await userEvent.tab({ shift: true })
    console.log(
      'shiftTabbed into dd 1st option',
      document.activeElement?.outerHTML
    )

    await waitFor(() => {
      expect(allDropDownOptions[0]).toHaveFocus()
    })

    console.log({ previousTOFocus: document.activeElement?.outerHTML })

    await userEvent.tab({ shift: true })

    console.log({ final: document.activeElement?.outerHTML })
    expect(dropdownOptionsContainerDivItself).not.toHaveFocus()
    expect(elementThatStartsFocused).toHaveFocus()
  })

<html>
  <head />
  <body>
    <div>
      <button>
        I start focused
      </button>
      <div
        class="column self"
        data-error="false"
        data-testid="input-frame-parent"
      >
        <div
          class="row inputRow  forceRow"
        >
          <input
            autocomplete="off"
            class="input undefined "
            data-testid="dropdown-input"
            readonly=""
            required=""
            tabindex="0"
            title=""
            value=""
          />
          <div
            class="column dropdownIcon"
            style="flex: 1;"
          >
            <svg
              class="icon"
              fill="currentColor"
              height="1em"
              stroke="currentColor"
              stroke-width="0"
              viewBox="0 0 448 512"
              width="1em"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d="M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"
              />
            </svg>
          </div>
          <div
            class="optionsContainer  "
            data-testid="dropdown-options"
            role="listbox"
            style="max-height: 200px; min-height: 100px;"
          >
            <div
              class="self column "
              data-testid="list-view"
            >
              <div
                aria-selected="false"
                class="option "
                data-testid="dropdown-option"
                role="option"
                tabindex="-1"
              >
                firstboi
              </div>
              <div
                aria-selected="false"
                class="option "
                data-testid="dropdown-option"
                role="option"
                tabindex="-1"
              >
                Test Value 1
              </div>
              <div
                aria-selected="false"
                class="option "
                data-testid="dropdown-option"
                role="option"
                tabindex="-1"
              >
                Test Value 2
              </div>
              <div
                aria-selected="false"
                class="option "
                data-testid="dropdown-option"
                role="option"
                tabindex="-1"
              >
                Test Value 3
              </div>
              <div
                aria-selected="false"
                class="option "
                data-testid="dropdown-option"
                role="option"
                tabindex="-1"
              >
                3 Things
              </div>
              <div
                aria-selected="false"
                class="option "
                data-testid="dropdown-option"
                role="option"
                tabindex="-1"
              >
                Random Test
              </div>
            </div>
            <div
              class=""
              data-testid="bottom"
            />
          </div>
        </div>
      </div>
      <input
        type="text"
      />
    </div>
  </body>
</html>

Reproduction:

I will work on this part soon!

Suggested solution:

Have dom-testing-library treat divs with role="listbox" as focusable.

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