Skip to content

Commit 95efa3b

Browse files
author
Hector Arce De Las Heras
committed
Accessibility Improvements and Code Refactoring for Carousel Component
This commit enhances the accessibility and usability of the Carousel component by adding aria attributes, modifying test cases, and refactoring the code for better readability and maintainability. Changes include semantic structure improvements, addition of ScreenReaderOnly component, updates to test cases, and simplification of the codebase. The Storybook stories have also been updated to reflect these changes.
1 parent e7eddbc commit 95efa3b

File tree

9 files changed

+96
-19
lines changed

9 files changed

+96
-19
lines changed

src/components/mediaProgressBar/__tests__/mediaProgressBar.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const mockProps = {
1515
dataTestIdBar: 'testIdBar',
1616
dataTestIdProgressBar: 'testIdProgressBar',
1717
onBarChange: jest.fn(),
18-
barsAriaLabels: ['aria-label-0'],
18+
barAriaLabel: 'bar-aria-label',
1919
};
2020

2121
describe('MediaProgressBar Component', () => {

src/components/mediaProgressBar/mediaProgressBarStandAlone.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ import {
1010
ProgressBarStyled,
1111
} from './mediaProgressBar.styled';
1212
import { IMediaProgressBarStandAlone } from './types';
13-
import { mediaProgressBarIndex } from './utils/mediaProgressBarIndex';
13+
import {
14+
buildMediaProgessBarAriaLabel,
15+
mediaProgressBarIndex,
16+
} from './utils/mediaProgressBarUtils';
1417

1518
const MediaProgressBarStandaloneComponent = (
1619
{ barsNum, ...props }: IMediaProgressBarStandAlone,
@@ -32,7 +35,8 @@ const MediaProgressBarStandaloneComponent = (
3235
<BarContainerStyled
3336
key={newIndex}
3437
ref={props.barRef}
35-
aria-label={props.barsAriaLabels?.[newIndex]}
38+
aria-current={newIndex === props.currentBar}
39+
aria-label={buildMediaProgessBarAriaLabel(newIndex, barsNum, props.barAriaLabel)}
3640
barFocused={newIndex === props.currentBar}
3741
clickableBars={props.clickableBars}
3842
role={ROLES.BUTTON}

src/components/mediaProgressBar/stories/argtypes.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,14 @@ export const argtypes = (variants: IThemeObjectVariants, themeSelected: string):
101101
category: CATEGORY_CONTROL.MODIFIERS,
102102
},
103103
},
104-
barsAriaLabels: {
104+
barAriaLabel: {
105105
control: { type: 'object' },
106-
type: { name: 'array' },
107-
description: 'aria labels for each bar',
106+
type: { name: 'string' },
107+
description:
108+
'Aria label for each bar. Can be build using the current bar and the bars length using the keywords "{{currentBar}}" and "{{barsNum}}"',
108109
table: {
109110
type: {
110-
summary: 'string[]',
111+
summary: 'string',
111112
},
112113
category: CATEGORY_CONTROL.ACCESIBILITY,
113114
},

src/components/mediaProgressBar/stories/mediaProgressBar.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ const commonArgs: IMediaProgressBar = {
7575
)[0] as string,
7676
barsNum: 1,
7777
barProgressDuration: 5000,
78-
barsAriaLabels: ['aria-label-0'],
78+
barAriaLabel: 'Bar {{currentBar}} of {{barsNum}}',
7979
clickableBars: true,
8080
};
8181

src/components/mediaProgressBar/types/mediaProgressBar.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ import { CustomTokenTypes, DeviceBreakpointsType } from '@/types';
22

33
import { MediaProgressBarVariantStylesType } from './mediaProgressBarTheme';
44

5+
export const BUILD_SCREEN_READER_CURRENT_PAGE_KEY = '{{currentBar}}';
6+
export const BUILD_SCREEN_READER_NUM_PAGES_KEY = '{{barsNum}}';
7+
58
export interface IMediaProgressBarStandAlone {
69
styles: MediaProgressBarVariantStylesType;
710
barsNum: number;
8-
barsAriaLabels?: string[];
11+
barAriaLabel?: string;
912
barProgressDuration: number;
1013
dataTestIdBar?: string;
1114
dataTestIdProgressBar?: string;

src/components/mediaProgressBar/utils/__tests__/mediaProgressBarIndex.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DeviceBreakpointsType } from '@/types';
22

3-
import { mediaProgressBarIndex } from '../mediaProgressBarIndex';
3+
import { buildMediaProgessBarAriaLabel, mediaProgressBarIndex } from '../mediaProgressBarUtils';
44

55
const mock_props = {
66
barsNum: 5,
@@ -73,4 +73,22 @@ describe('mediaProgressBarIndex', () => {
7373

7474
expect(result).toBeUndefined();
7575
});
76+
it('should return undefined when barAriaLabel is undefined', () => {
77+
const currentBar = 2;
78+
const barsNum = 5;
79+
const barAriaLabel = undefined;
80+
81+
const result = buildMediaProgessBarAriaLabel(currentBar, barsNum, barAriaLabel);
82+
83+
expect(result).toBeUndefined();
84+
});
85+
it('should return the correct aria label when barAriaLabel is defined', () => {
86+
const currentBar = 2;
87+
const barsNum = 5;
88+
const barAriaLabel = 'Page {{currentBar}} of {{barsNum}}';
89+
90+
const result = buildMediaProgessBarAriaLabel(currentBar, barsNum, barAriaLabel);
91+
92+
expect(result).toBe('Page 3 of 5');
93+
});
7694
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { DeviceBreakpointsType } from '@/types';
2+
3+
import { BUILD_SCREEN_READER_CURRENT_PAGE_KEY, BUILD_SCREEN_READER_NUM_PAGES_KEY } from '../types';
4+
5+
interface IUseMediaProgressBarIndexProps {
6+
index: number;
7+
barsNum: number;
8+
currentBar: number;
9+
device: DeviceBreakpointsType;
10+
maxBars: ({ default: number } & { [device in DeviceBreakpointsType]?: number }) | undefined;
11+
}
12+
export const mediaProgressBarIndex = ({
13+
index,
14+
barsNum,
15+
currentBar,
16+
device,
17+
maxBars,
18+
}: IUseMediaProgressBarIndexProps): number | undefined => {
19+
const MAX_NUMBER_OF_BARS = maxBars?.[device] || maxBars?.default;
20+
21+
const lastBarIndex = barsNum - 1;
22+
23+
if (!MAX_NUMBER_OF_BARS) return index;
24+
25+
// avoid render more bars than MAX_NUMBER_OF_BARS
26+
if (index >= MAX_NUMBER_OF_BARS) return;
27+
// currentBar goes to the last bar when number of bars is greater than MAX_NUMBER_OF_BARS
28+
if (currentBar === lastBarIndex && barsNum > MAX_NUMBER_OF_BARS) {
29+
return index + (currentBar + 1 - MAX_NUMBER_OF_BARS);
30+
// currentBar stays in the next to last bar when there is more bars than MAX_NUMBER_OF_BARS
31+
} else if (currentBar + 2 >= MAX_NUMBER_OF_BARS && currentBar !== lastBarIndex) {
32+
return index + (currentBar + 2 - MAX_NUMBER_OF_BARS);
33+
}
34+
return index;
35+
};
36+
37+
export const buildMediaProgessBarAriaLabel = (
38+
currentBar: number,
39+
barsNum: number,
40+
barAriaLabel?: string
41+
): string | undefined => {
42+
if (!barAriaLabel) {
43+
return barAriaLabel;
44+
}
45+
const currentPageRegExp = new RegExp(BUILD_SCREEN_READER_CURRENT_PAGE_KEY, 'g');
46+
const numPagesRegExp = new RegExp(BUILD_SCREEN_READER_NUM_PAGES_KEY, 'g');
47+
48+
return barAriaLabel
49+
.replace(currentPageRegExp, String(currentBar + 1))
50+
.replace(numPagesRegExp, String(barsNum));
51+
};

src/components/pageControlAutomate/stories/pageControlAutomate.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const StoryWithHooks = args => {
3030
const mediaProgressBar = {
3131
barsNum: 3,
3232
barProgressDuration: 2000,
33-
barsAriaLabels: ['aria-label-0', 'aria-label-1', 'aria-label-2'],
33+
barAriaLabel: 'Bar {{currentBar}} of {{barsNum}}',
3434
clickableBars: true,
3535
};
3636

src/components/storybook/replaceContent/replaceContent.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
import React from 'react';
22

3+
import { AriaType, ROLES } from '@/types';
4+
35
import { ReplaceContentStyled } from './replaceContent.styled';
46

5-
export const ReplaceContent = ({
6-
children,
7-
width,
8-
height,
9-
id,
10-
}: {
7+
interface IReplaceContent extends AriaType {
118
children?: React.ReactNode;
129
width?: string;
1310
height?: string;
1411
id?: string;
15-
}): JSX.Element => {
12+
role?: ROLES;
13+
}
14+
15+
export const ReplaceContent = ({ children, ...props }: IReplaceContent): JSX.Element => {
1616
return (
17-
<ReplaceContentStyled height={height} id={id} width={width}>
17+
<ReplaceContentStyled {...props}>
1818
{!children ? <span>Replace here your Content</span> : children}
1919
</ReplaceContentStyled>
2020
);

0 commit comments

Comments
 (0)