Skip to content

Commit 3237f1f

Browse files
authored
fix(TabList): respect event default prevention (#2463)
1 parent 3c9ed9b commit 3237f1f

File tree

4 files changed

+61
-15
lines changed

4 files changed

+61
-15
lines changed

src/components/tabs/Tab.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,11 @@ import {Label} from '../Label';
66

77
import {bTab} from './constants';
88
import {useTab} from './hooks/useTab';
9-
import type {TabComponentElementType, TabComponentProps, TabLinkProps, TabProps} from './types';
9+
import type {TabComponentElementType, TabProps} from './types';
10+
import {isTabComponentProps, isTabLinkProps} from './utils';
1011

1112
import './Tab.scss';
1213

13-
function isTabComponentProps<T extends TabComponentElementType>(
14-
p: TabProps<T>,
15-
): p is TabComponentProps<Exclude<T, undefined>> {
16-
return p.component !== undefined;
17-
}
18-
19-
function isTabLinkProps<T extends TabComponentElementType>(p: TabProps<T>): p is TabLinkProps {
20-
return p.href !== undefined;
21-
}
22-
2314
export const Tab = React.forwardRef<HTMLAnchorElement | HTMLButtonElement, TabProps>(function Tab<
2415
T extends TabComponentElementType,
2516
>(

src/components/tabs/__tests__/TabList.test.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,31 @@ test('should call onUpdate on tab click', async () => {
108108
expect(onUpdateFn).toHaveBeenCalledWith(tab1.value);
109109
});
110110

111+
test('should not call onUpdate if default prevented', async () => {
112+
const onUpdateFn = jest.fn();
113+
const user = userEvent.setup();
114+
115+
render(
116+
<TabList onUpdate={onUpdateFn}>
117+
<Tab value={tab1.value} qa={tab1.qa} onClick={(e) => e.preventDefault()}>
118+
{tab1.title}
119+
</Tab>
120+
<Tab value={tab2.value} qa={tab2.qa}>
121+
{tab2.title}
122+
</Tab>
123+
</TabList>,
124+
);
125+
126+
const tabComponent1 = screen.getByTestId(tab1.qa);
127+
const tabComponent2 = screen.getByTestId(tab2.qa);
128+
129+
await user.click(tabComponent1);
130+
await user.click(tabComponent2);
131+
132+
expect(onUpdateFn).toBeCalledTimes(1);
133+
expect(onUpdateFn).toHaveBeenCalledWith(tab2.value);
134+
});
135+
111136
test('should wrap tabs', () => {
112137
const wrapQaId = 'wrap';
113138

src/components/tabs/hooks/useTab.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,33 @@ export function useTab<T extends TabComponentElementType>(
2323
const isFocused = tabContext.isFocused;
2424

2525
const onClick = (event: React.MouseEvent) => {
26-
if (!tabProps.disabled) {
26+
if (tabProps.disabled) {
27+
return;
28+
}
29+
30+
if (tabProps.onClick) {
31+
tabProps.onClick(event);
32+
}
33+
34+
if (!event.defaultPrevented) {
2735
tabContext.onUpdate?.(tabProps.value);
28-
tabProps.onClick?.(event);
2936
}
3037
};
3138

3239
const onKeyDown = (event: React.KeyboardEvent) => {
33-
if ((event.key === KeyCode.SPACEBAR || event.key === KeyCode.ENTER) && !tabProps.disabled) {
40+
if (tabProps.disabled) {
41+
return;
42+
}
43+
44+
if (tabProps.onKeyDown) {
45+
tabProps.onKeyDown(event);
46+
}
47+
48+
if (
49+
!event.defaultPrevented &&
50+
(event.key === KeyCode.SPACEBAR || event.key === KeyCode.ENTER)
51+
) {
3452
tabContext.onUpdate?.(tabProps.value);
35-
tabProps.onKeyDown?.(event);
3653
}
3754
};
3855

src/components/tabs/utils.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type {TabComponentElementType, TabComponentProps, TabLinkProps, TabProps} from './types';
2+
3+
export function isTabComponentProps<T extends TabComponentElementType>(
4+
p: TabProps<T>,
5+
): p is TabComponentProps<Exclude<T, undefined>> {
6+
return p.component !== undefined;
7+
}
8+
9+
export function isTabLinkProps<T extends TabComponentElementType>(
10+
p: TabProps<T>,
11+
): p is TabLinkProps {
12+
return p.href !== undefined;
13+
}

0 commit comments

Comments
 (0)