diff --git a/packages/react-aria-components/test/Tabs.test.js b/packages/react-aria-components/test/Tabs.test.js index d5d34e78e07..0f88b9ed524 100644 --- a/packages/react-aria-components/test/Tabs.test.js +++ b/packages/react-aria-components/test/Tabs.test.js @@ -1079,4 +1079,34 @@ describe('Tabs', () => { expect(menu.getOptions()).toHaveLength(3); await menu.close(); }); + + it('allows browser back/forward navigation (Meta+ArrowLeft/Right on Mac, Alt+ArrowLeft/Right on Windows)', async () => { + let {getByRole} = renderTabs(); + let tabsTester = testUtilUser.createTester('Tabs', {root: getByRole('tablist')}); + let tabs = tabsTester.getTabs(); + + // Select first tab + await tabsTester.triggerTab({tab: 0}); + expect(tabsTester.getSelectedTab()).toBe(tabs[0]); + + // Try to navigate with Meta+ArrowRight (Mac browser back/forward) - should NOT switch tabs + await user.keyboard('{Meta>}{ArrowRight}{/Meta}'); + expect(tabsTester.getSelectedTab()).toBe(tabs[0]); + + // Try to navigate with Meta+ArrowLeft - should NOT switch tabs + await user.keyboard('{Meta>}{ArrowLeft}{/Meta}'); + expect(tabsTester.getSelectedTab()).toBe(tabs[0]); + + // Try to navigate with Alt+ArrowRight (Windows browser back/forward) - should NOT switch tabs + await user.keyboard('{Alt>}{ArrowRight}{/Alt}'); + expect(tabsTester.getSelectedTab()).toBe(tabs[0]); + + // Try to navigate with Alt+ArrowLeft - should NOT switch tabs + await user.keyboard('{Alt>}{ArrowLeft}{/Alt}'); + expect(tabsTester.getSelectedTab()).toBe(tabs[0]); + + // Regular arrow keys should still work + await user.keyboard('{ArrowRight}'); + expect(tabsTester.getSelectedTab()).toBe(tabs[1]); + }); }); diff --git a/packages/react-aria/src/tabs/useTabList.ts b/packages/react-aria/src/tabs/useTabList.ts index d960df03d4d..640591c45e9 100644 --- a/packages/react-aria/src/tabs/useTabList.ts +++ b/packages/react-aria/src/tabs/useTabList.ts @@ -25,6 +25,7 @@ import {useId} from '../utils/useId'; import {useLabels} from '../utils/useLabels'; import {useLocale} from '../i18n/I18nProvider'; import {useMemo} from 'react'; +import {useKeyboard} from '../interactions/useKeyboard'; import {useSelectableCollection} from '../selection/useSelectableCollection'; export interface AriaTabListProps extends TabListProps, DOMProps, AriaLabelingProps { @@ -76,6 +77,19 @@ export function useTabList( linkBehavior: 'selection' }); + // Add keyboard handlers to allow browser back/forward navigation (Cmd+Left/Right on Mac, Alt+Left/Right on Windows) + // to propagate so the browser can handle them instead of switching tabs. + let {keyboardProps} = useKeyboard({ + shortcuts: { + // On Mac, Cmd+Left/Right are browser back/forward + 'Meta+ArrowLeft': () => ({shouldContinuePropagation: true}), + 'Meta+ArrowRight': () => ({shouldContinuePropagation: true}), + // On Windows/Linux, Alt+Left/Right are browser back/forward + 'Alt+ArrowLeft': () => ({shouldContinuePropagation: true}), + 'Alt+ArrowRight': () => ({shouldContinuePropagation: true}) + } + }); + // Compute base id for all tabs let tabsId = useId(); tabsIds.set(state, tabsId); @@ -84,7 +98,7 @@ export function useTabList( return { tabListProps: { - ...mergeProps(collectionProps, tabListLabelProps), + ...mergeProps(collectionProps, tabListLabelProps, keyboardProps), role: 'tablist', 'aria-orientation': orientation, tabIndex: undefined