Search code examples
reactjsaccessibilityreact-testing-library

Aria-hidden does not hide the element from queries


Why is RTL showing hidden elements as visible?

expect(screen.queryByText(/months in operation\?/i)).not.toBeVisible()

Received element is visible: <label aria-hidden="true" for="monthsDropdown" />

expect(screen.queryByText(/months in operation \?/i)).not.toBeInTheDocument()

expected document not to contain element, found <label aria-hidden="true" for="monthsDropdown">Months in operation?</label> instead

The parent div for this label also has the styles of display: none; visibility: hidden; height: 0;

Is this expected behavior?


Solution

  • TL;DR: The aria-hidden attribute only hides the element from *ByRole queries.


    Let's assume we have the following component with two buttons.

    const TestComponent = () => {
      return (
        <>
          <div style={{ display: "none", visibility: "hidden", height: 0 }}>
            <button>Hidden Button</button>
          </div>
          <button aria-hidden="true">Aria Hidden Button</button>
        </>
      );
    };
    

    Even though the first element is not visible due to the parent's style, and the second is being excluded from the accessibility tree with aria-hidden, they'll both be accessible through getByText queries since they're both present in the DOM.

    // Both assertions will be pass
    expect(screen.getByText("Hidden Button")).toBeInTheDocument();
    expect(screen.getByText("Aria Hidden Button")).toBeInTheDocument();
    

    However, trying to access them with getByRole queries won't be possible as that query looks to the accessibility tree - unless the hidden option is passed in the query.

    // Will not find the elements
    const [hiddenButton, visibleButton] = screen.getAllByRole("button")
    
    // Will return both elements
    const [hiddenButton, visibleButton] = screen.getAllByRole("button", { hidden: true })
    

    In terms of visibility, the second button is visible while the first one is not.

    // Both assertions will be pass
    const [hiddenButton, visibleButton] = screen.getAllByRole("button", { hidden: true })
    expect(hiddenButton).not.toBeVisible();
    expect(visibleButton).toBeVisible();