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

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

Closed
Stan-Stani opened this issue Oct 29, 2024 · 3 comments
Closed

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

Stan-Stani opened this issue Oct 29, 2024 · 3 comments

Comments

@Stan-Stani
Copy link

Stan-Stani commented Oct 29, 2024

  • @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.

@MatanBobi
Copy link
Member

Hi @Stan-Stani, thanks for opening this.
We're not the one's deciding which element is focusable or not. Are you able to focus on the element programmatically without testing library (using JSDOM) and this doesn't work when you're adding DTL?

@Stan-Stani
Copy link
Author

@MatanBobi Ah, I'll check.

@Stan-Stani
Copy link
Author

Nevermind. the role='listbox' was a red herring. Basically am dealing with browser specific implementations of when to make an element focusable which I'd assume neither dom-testing-library nor jsdom concerns itself with. See https://www.cassey.dev/til/2019-11-19-overflow-scroll-gets-focus/ and https://codepen.io/stan-stan/pen/Jjgvwvq for more info.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants