Skip to content

Commit 08c5baf

Browse files
authored
feat(Tabs): improve a11y, and add renderPanel prop (#1393)
WEB-2148
1 parent 8c3ca44 commit 08c5baf

10 files changed

Lines changed: 209 additions & 97 deletions

playroom/snippets.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,27 +1133,37 @@ const tabsSnippets: Array<Snippet> = [
11331133
name: 'Tabs (without icons)',
11341134
code: `
11351135
<Tabs
1136-
selectedIndex={getState('tabIndex', 0)}
1137-
onChange={setState('tabIndex')}
1136+
selectedIndex={getState('selectedTab', 0)}
1137+
onChange={setState('selectedTab')}
11381138
tabs={[
11391139
{text: 'Tab 1'},
11401140
{text: 'Tab 2'},
11411141
{text: 'Tab 3'},
11421142
]}
1143+
renderPanel={({selectedIndex, panelProps}) => (
1144+
<div {...panelProps}>
1145+
<Text3 regular>Panel {selectedIndex + 1}</Text3>
1146+
</div>
1147+
)}
11431148
/>`,
11441149
},
11451150
{
11461151
group: 'Tabs',
11471152
name: 'Tabs (with icons)',
11481153
code: `
11491154
<Tabs
1150-
selectedIndex={getState('tabIndex', 0)}
1151-
onChange={setState('tabIndex')}
1155+
selectedIndex={getState('selectedTab', 0)}
1156+
onChange={setState('selectedTab')}
11521157
tabs={[
11531158
{text: 'Tab 1', Icon: IconAppointmentRegular},
11541159
{text: 'Tab 2', Icon: IconBrainRegular},
11551160
{text: 'Tab 3', Icon: IconBusRegular},
11561161
]}
1162+
renderPanel={({selectedIndex, panelProps}) => (
1163+
<div {...panelProps}>
1164+
<Text3 regular>Panel {selectedIndex + 1}</Text3>
1165+
</div>
1166+
)}
11571167
/>`,
11581168
},
11591169
];
1.44 KB
Loading
2.96 KB
Loading
1.38 KB
Loading
3.04 KB
Loading

src/__screenshot_tests__/tabs-screenshot-test.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {within} from '@telefonica/acceptance-testing';
12
import {openStoryPage, screen, setRootFontSize} from '../test-utils';
23

34
const THEME_VARIANTS = ['default', 'inverse', 'alternative'] as const;
@@ -55,25 +56,22 @@ test('Tabs with long text and icon', async () => {
5556
});
5657

5758
test('Tabs selected line appears properly', async () => {
58-
const page = await openStoryPage({
59+
await openStoryPage({
5960
id: 'components-tabs--default',
6061
device: 'MOBILE_IOS',
6162
});
6263

63-
const tabs = await screen.findByRole('tablist');
64+
const tabslist = await screen.findByRole('tablist');
65+
const tabs = await within(tabslist).findAllByRole('tab');
6466

65-
await page.evaluate(() => {
66-
document.querySelector<HTMLElement>('[data-tabindex="1"]')?.click();
67-
});
67+
await tabs[1].click();
6868

69-
const secondTabActive = await tabs.screenshot();
69+
const secondTabActive = await tabslist.screenshot();
7070
expect(secondTabActive).toMatchImageSnapshot();
7171

72-
await page.evaluate(() => {
73-
document.querySelector<HTMLElement>('[data-tabindex="2"]')?.click();
74-
});
72+
await tabs[2].click();
7573

76-
const thirdTabActive = await tabs.screenshot();
74+
const thirdTabActive = await tabslist.screenshot();
7775
expect(thirdTabActive).toMatchImageSnapshot();
7876
});
7977

src/__stories__/tabs-story.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import * as React from 'react';
2-
import {Box, IconCloseRegular, ResponsiveLayout, Tabs} from '..';
2+
import {
3+
Box,
4+
IconAppointmentRegular,
5+
IconBrainRegular,
6+
IconBusRegular,
7+
ResponsiveLayout,
8+
Tabs,
9+
Text3,
10+
} from '..';
311

412
import type {Variant} from '../theme-variant-context';
513

@@ -35,8 +43,15 @@ export const Default: StoryComponent<Args> = ({tabCount, text, theme, icon}) =>
3543
onChange={setSelectedIndex}
3644
tabs={Array.from({length: tabCount}).map((_, index) => ({
3745
text: `${text} ${index + 1}`,
38-
Icon: icon ? IconCloseRegular : undefined,
46+
Icon: icon
47+
? [IconAppointmentRegular, IconBrainRegular, IconBusRegular][index]
48+
: undefined,
3949
}))}
50+
renderPanel={({selectedIndex, panelProps}) => (
51+
<div {...panelProps}>
52+
<Text3 regular>Panel {selectedIndex + 1}</Text3>
53+
</div>
54+
)}
4055
/>
4156
</Box>
4257
</ResponsiveLayout>

src/tabs.css.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ const baseTab = style([
5959
background: 'transparent',
6060
}),
6161
{
62+
outlineOffset: -8,
6263
textAlign: 'center',
6364
verticalAlign: 'baseline',
6465
borderBottom: '2px solid transparent',
@@ -88,9 +89,19 @@ export const tabVariants = styleVariants({
8889
],
8990
});
9091

92+
const focusOutlineStyles = {
93+
// the revert important! is needed to bypass the styles applied by TabFocus component
94+
outline: 'revert !important',
95+
outlineOffset: -8,
96+
};
97+
9198
export const tabHover = styleVariants({
9299
default: [
93100
style({
101+
':focus-visible': {
102+
...focusOutlineStyles,
103+
color: vars.colors.textPrimary,
104+
},
94105
'@media': {
95106
[mq.supportsHover]: {
96107
':hover': {
@@ -102,6 +113,10 @@ export const tabHover = styleVariants({
102113
],
103114
inverse: [
104115
style({
116+
':focus-visible': {
117+
...focusOutlineStyles,
118+
color: vars.colors.textPrimaryInverse,
119+
},
105120
'@media': {
106121
[mq.supportsHover]: {
107122
':hover': {

0 commit comments

Comments
 (0)