Skip to main content

toBeAccessibleTabs()

Tabs are a set of layered sections of content, known as tab panels, that display one panel of content at a time.

Each tab panel has an associated tab element, that when activated, displays the panel. The list of tab elements is arranged along one edge of the currently displayed panel, most commonly the top edge.

  • Apple
  • Banana

Usage

Syntax

import { Tabs } from 'your-component-library'
import { render, screen } from '@testing-library/react'

test('tablist', () => {
render(<Tabs data-testid="tablist" {...}>)

expect(screen.getByTestId('tablist')).toBeAccessibleTabs()
})

Test Summary

The matcher tests the following on the tabs container element:

✓ element has role tablist
✓ element has accessible name
✓ element contains a single tab with aria-selected set to true
✓ element is part of tab sequence

The matcher tests the following on each tab:

✓ element has role tab and is contained within tablist
✓ element has aria-controls referring to its associated tabpanel
✓ element supports arrow keyboard navigation

The matcher tests the following on each tab panel:

✓ element has role tabpanel
✓ element has aria-labelledby referring to its associated tab element
✓ element (or first focusable child) is part of tab sequence

WAI-ARIA Roles, States, and Properties

1. The container widget has a role of tablist.

<!-- ✓ element has role tablist -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-selected="true" role="tab">1</div>
<div role="tab">2</div>
</div>

<!-- ❌ element has role tablist -->
<div aria-label="The Tabs" tabindex="0">
<div aria-selected="true" role="tab">1</div>
<div role="tab">2</div>
</div>

2. The container widget has an accessible name.

<!-- ✓ element has accessible name -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-selected="true" role="tab">1</div>
<div role="tab">2</div>
</div>

<!-- ❌ element has accessible name -->
<div role="tablist" tabindex="0">
<div aria-selected="true" role="tab">1</div>
<div role="tab">2</div>
</div>

3. The container element contains a single tab element with aria-selected="true".

<!-- ✓ element contains a single selected tab element -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-selected="true" role="tab">1</div>
<div role="tab">2</div>
</div>

<!-- ❌ element contains a single selected tab element -->
<div role="tablist" tabindex="0">
<div role="tab">1</div>
<div role="tab">2</div>
</div>

<!-- ❌ element contains a single selected tab element -->
<div role="tablist">
<div aria-selected="true" role="tab">1</div>
<div aria-selected="true" role="tab">2</div>
</div>

4. Each tab element has role tab and is within the tablist element

<!-- ✓ element has role tab and is within the tablist element -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-selected="true" role="tab">1</div>
<div role="tab">2</div>
</div>

<!-- ❌ element has role tab -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-selected="true">1</div>
<div>2</div>
</div>

<!-- ❌ element is within tablist element -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-selected="true" role="tab">1</div>
</div>
<div role="tab">2</div>

5. Each tab element has aria-controls referring to its associated tabpanel

<!-- ✓ element has aria-controls referring to its associated tabpanel -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-controls="one" aria-selected="true" id="1" role="tab">1</div>
<div aria-controls="two" id="2" role="tab">2</div>
</div>
<div aria-labelledby="1" id="one" role="tabpanel">One</div>
<div aria-labelledby="2" id="two" role="tabpanel">Two</div>

<!-- ❌ element has aria-controls referring to its associated tabpanel -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-selected="true" id="1" role="tab">1</div>
<div aria-controls="two" id="2" role="tab">2</div>
</div>
<div aria-labelledby="1" id="one" role="tabpanel">One</div>
<div aria-labelledby="2" id="two" role="tabpanel">Two</div>

6. Each tab content panel has role tabpanel

<!-- ✓ element has role tabpanel -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-controls="one" aria-selected="true" id="1" role="tab">1</div>
<div aria-controls="two" id="2" role="tab">2</div>
</div>
<div aria-labelledby="1" id="one" role="tabpanel">One</div>
<div aria-labelledby="2" id="two" role="tabpanel">Two</div>

<!-- ❌ element has role tabpanel -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-controls="one" aria-selected="true" id="1" role="tab">1</div>
<div aria-controls="two" id="2" role="tab">2</div>
</div>
<div aria-labelledby="1" id="one">One</div>
<div aria-labelledby="2" id="two" role="tabpanel">Two</div>

7. Each tabpanel has aria-labelledby referring to its associated tab element

<!-- ✓ element has aria-labelledby referring to its associated tab element -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-controls="one" aria-selected="true" id="1" role="tab">1</div>
<div aria-controls="two" id="2" role="tab">2</div>
</div>
<div aria-labelledby="1" id="one" role="tabpanel">One</div>
<div aria-labelledby="2" id="two" role="tabpanel">Two</div>

<!-- ❌ element has aria-labelledby referring to its associated tab element -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-controls="one" aria-selected="true" id="1" role="tab">1</div>
<div aria-controls="two" id="2" role="tab">2</div>
</div>
<div id="one">One</div>
<div id="two" role="tabpanel">Two</div>

8. Each tabpanel element or its first focusable child are part of the tab sequence

<!-- ✓ element or its first focusable child are part of the tab sequence -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-controls="one" aria-selected="true" id="1" role="tab">1</div>
<div aria-controls="two" id="2" role="tab">2</div>
</div>
<div aria-labelledby="1" id="one" role="tabpanel" tabindex="0">One</div>
<div aria-labelledby="2" id="two" role="tabpanel" tabindex="-1">
<button>Two</button>
</div>

<!-- ❌ element or its first focusable child are part of the tab sequence -->
<div aria-label="The Tabs" role="tablist" tabindex="0">
<div aria-controls="one" aria-selected="true" id="1" role="tab">1</div>
<div aria-controls="two" id="2" role="tab">2</div>
</div>
<div aria-labelledby="1" id="one" role="tabpanel" tabindex="-1">One</div>
<div aria-labelledby="2" id="two" role="tabpanel" tabindex="0"><button>Two</button></div>

Keyboard Interaction

  • Tab:
    • When focus moves into the tab list, places focus on the active tab element.
    • When the tab list contains the focus, moves focus to the next element in the page tab sequence outside the tablist, which is the tabpanel unless the first element containing meaningful content inside the tabpanel is focusable.
  • Right Arrow moves focus to the next tab. If focus is on the last tab, moves focus to the first tab.
  • Left Arrow moves focus to the previous tab. If focus is on the first tab, moves focus to the last tab.

External Resources

Web Accessibility Initiative