Skip to content

Commit 2f114b3

Browse files
authored
fix(Tabs): default shouldSelectOnPressUp to true if item is a link (adobe#9827)
* useTab: default shouldSelectOnPressUp to true if item is a link * add story * Revert "add story" This reverts commit 981c1d8.
1 parent 7b18ba0 commit 2f114b3

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

packages/react-aria-components/test/Tabs.test.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import {act, fireEvent, pointerMap, render, waitFor, within} from '@react-spectrum/test-utils-internal';
1414
import {Button} from '../src/Button';
1515
import React, {useState} from 'react';
16+
import {RouterProvider} from 'react-aria/private/utils/openLink';
1617
import {Tab, TabList, TabPanel, TabPanels, Tabs} from '../src/Tabs';
1718
import {TabsExample} from '../stories/Tabs.stories';
1819
import {Tooltip, TooltipTrigger} from '../src/Tooltip';
@@ -497,6 +498,35 @@ describe('Tabs', () => {
497498
expect(tabs[2]).toHaveAttribute('aria-selected', 'true');
498499
});
499500

501+
it('should navigate linked tabs on click rather than mouse down', async function () {
502+
let navigate = jest.fn();
503+
let {getAllByRole} = render(
504+
<RouterProvider navigate={navigate}>
505+
<Tabs selectedKey="/a">
506+
<TabList aria-label="Test">
507+
<Tab id="/a" href="/a">A</Tab>
508+
<Tab id="/b" href="/b">B</Tab>
509+
</TabList>
510+
<TabPanel id="/a">A</TabPanel>
511+
<TabPanel id="/b">B</TabPanel>
512+
</Tabs>
513+
</RouterProvider>
514+
);
515+
516+
let tabs = getAllByRole('tab');
517+
fireEvent.mouseDown(tabs[1], {button: 0, detail: 1});
518+
expect(navigate).not.toHaveBeenCalled();
519+
520+
fireEvent.mouseLeave(tabs[1]);
521+
fireEvent.mouseEnter(tabs[1]);
522+
expect(navigate).not.toHaveBeenCalled();
523+
524+
fireEvent.mouseUp(tabs[1], {button: 0, detail: 1});
525+
fireEvent.click(tabs[1], {button: 0, detail: 1});
526+
expect(navigate).toHaveBeenCalledTimes(1);
527+
expect(navigate).toHaveBeenCalledWith('/b', undefined);
528+
});
529+
500530
it('should render tab with aria-label', () => {
501531
let {getAllByRole} = render(
502532
<Tabs>

packages/react-aria/src/tabs/useTab.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,23 @@ export function useTab<T>(
5454
let isSelected = key === selectedKey;
5555

5656
let isDisabled = propsDisabled || state.isDisabled || state.selectionManager.isDisabled(key);
57+
let item = state.collection.getItem(key);
5758
let {itemProps, isPressed} = useSelectableItem({
5859
selectionManager: manager,
5960
key,
6061
ref,
6162
isDisabled,
62-
shouldSelectOnPressUp,
63+
// Link tabs should behave like native anchors (navigate on press up)
64+
// This avoids reopening beforeunload dialogs when browsers replay
65+
// queued pointer enter/leave events after cancellation.
66+
shouldSelectOnPressUp: shouldSelectOnPressUp ?? item?.props.href != null,
6367
linkBehavior: 'selection'
6468
});
6569

6670
let tabId = generateId(state, key, 'tab');
6771
let tabPanelId = generateId(state, key, 'tabpanel');
6872
let {tabIndex} = itemProps;
6973

70-
let item = state.collection.getItem(key);
7174
let domProps = filterDOMProps(item?.props, {labelable: true});
7275
delete domProps.id;
7376
let linkProps = useLinkProps(item?.props);

0 commit comments

Comments
 (0)