diff --git a/packages/components/src/Tabs/TabPanel.tsx b/packages/components/src/Tabs/TabPanel.tsx index 93d3d501db9..941f3b77f86 100644 --- a/packages/components/src/Tabs/TabPanel.tsx +++ b/packages/components/src/Tabs/TabPanel.tsx @@ -30,11 +30,22 @@ import styled from 'styled-components' export interface TabPanelProps { className?: string selected?: boolean + /** + * Set to `true` if you would like TabPanel to be reached via tab-key. + * Generally this is _only_ the case when the TabPanel contains no tab-stopping items (a, button, etc.) + * @default false + */ + isTabStop?: boolean } -const TabPanelLayout: FC = ({ children, className, selected }) => +const TabPanelLayout: FC = ({ + children, + className, + selected, + isTabStop = false, +}) => selected ? ( -
+
{children}
) : null diff --git a/packages/components/src/Tabs/Tabs.test.tsx b/packages/components/src/Tabs/Tabs.test.tsx index e92397aeea6..25e25203f85 100644 --- a/packages/components/src/Tabs/Tabs.test.tsx +++ b/packages/components/src/Tabs/Tabs.test.tsx @@ -29,6 +29,7 @@ import '@testing-library/jest-dom/extend-expect' import { renderWithTheme } from '@looker/components-test-utils' import React from 'react' import { fireEvent, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' import { Tab } from './Tab' import { TabList } from './TabList' import { TabPanel } from './TabPanel' @@ -197,6 +198,54 @@ describe('focus behavior', () => { ) }) + test('Tab Focus: By default, TabPanel is not tabbable', () => { + renderWithTheme( + + + tab1 + tab2 + + + + + + this is tab2 content + + + ) + userEvent.tab() + expect(screen.getByText('tab1')).toHaveFocus() + + userEvent.tab() + expect(screen.getByText('Some button')).toHaveFocus() + }) + + test('Tab Focus: TabPanel can use "isTabStop" prop to become tabbable element', () => { + renderWithTheme( + + + tab1 + tab2 + + + + + + this is tab2 content + + + ) + userEvent.tab() + expect(screen.getByText('tab1')).toHaveFocus() + + const button = screen.getByText('Some button') + userEvent.tab() + expect(button.closest('div')) + + userEvent.tab() + expect(button).toHaveFocus() + }) + test('Tab keyboard navigation', () => { renderWithTheme() const tab1 = screen.getByText('tab1') diff --git a/www/src/documentation/components/content/tabs.mdx b/www/src/documentation/components/content/tabs.mdx index b9d957dda50..8826daa8b76 100644 --- a/www/src/documentation/components/content/tabs.mdx +++ b/www/src/documentation/components/content/tabs.mdx @@ -132,3 +132,26 @@ Using the distribute prop on `TabList` will equally distributed each `Tab` in th ) } ``` + +## Keyboard Navigation + +When focused on a `Tab`, use the left and right arrow keys to navigate to sibling `Tab`s. + +By default, `TabPanel` is not reachable via the "Tab" key. If you'd like `TabPanel` to be reachable via the "Tab" key, set the `isTabStop` prop to `true`. + +```jsx +;() => { + return ( + + + isTabStop = true + isTabStop = false (Default) + + + I'm reachable by pressing the tab key + I'm not reachable by pressing the tab key + + + ) +} +```