Skip to content

Commit f5cd57e

Browse files
committed
fix(adaptive-sidebar): ensure rendered sidebar can have accessible name
adds the aria-label and aria-labedlledby props to the component which are applied when the component is rendered as a modal. This ensures consumers have the means to give the component an accessible name when needed fix #7632
1 parent 42e08ae commit f5cd57e

File tree

6 files changed

+96
-8
lines changed

6 files changed

+96
-8
lines changed

src/components/adaptive-sidebar/adaptive-sidebar.component.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ export interface AdaptiveSidebarProps
3838
open: boolean;
3939
/** Whether to render the sidebar as a modal component instead of as an inline sidebar */
4040
renderAsModal?: boolean;
41+
/** Prop to specify the aria-label of the component, applied when the component is rendered as a modal */
42+
"aria-label"?: string;
43+
/** Prop to specify the aria-labelledby property of the component, applied when the component is rendered as a modal */
44+
"aria-labelledby"?: string;
4145
/** The width of the sidebar */
4246
width?: string;
4347
}
@@ -53,6 +57,8 @@ export const AdaptiveSidebar = ({
5357
renderAsModal = false,
5458
width = "320px",
5559
restoreFocusOnClose = false,
60+
"aria-label": ariaLabel,
61+
"aria-labelledby": ariaLabelledBy,
5662
...props
5763
}: AdaptiveSidebarProps) => {
5864
const largeScreen = useIsAboveBreakpoint(adaptiveBreakpoint);
@@ -116,6 +122,8 @@ export const AdaptiveSidebar = ({
116122
open={open}
117123
p={0}
118124
ref={adaptiveSidebarRef}
125+
aria-label={ariaLabel}
126+
aria-labelledby={ariaLabelledBy}
119127
>
120128
<Box
121129
data-role="adaptive-sidebar-content-wrapper"

src/components/adaptive-sidebar/adaptive-sidebar.mdx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ closed by the user, and can be used to display additional information or actions
2020
To use the adaptive sidebar, import `AdaptiveSidebar` into your project. All content within the `AdaptiveSidebar` component is determined by the user; by
2121
default, the component will be empty, allowing for complete control of layout, etc.
2222

23+
As the `AdaptiveSidebar` can be rendered as a modal, it is **strongly** recommended that the component is given an accessible name.
24+
This can be generated via the `aria-label` or `aria-labelledby` attributes.
25+
2326
```javascript
2427
import AdaptiveSidebar from "carbon-react/lib/components/adaptive-sidebar";
2528
```

src/components/adaptive-sidebar/adaptive-sidebar.pw.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,5 +132,27 @@ test.describe("Component properties", () => {
132132
await checkAccessibility(page);
133133
});
134134
});
135+
136+
test("should pass accessibility tests for Adaptive Sidebar when `renderAsModal` is true and an accessible name is provided via `aria-label`", async ({
137+
mount,
138+
page,
139+
}) => {
140+
await mount(
141+
<DefaultAdaptiveSidebar renderAsModal aria-label="sidebar" open />,
142+
);
143+
144+
await checkAccessibility(page);
145+
});
146+
147+
test("should pass accessibility tests for Adaptive Sidebar when `renderAsModal` is true and an accessible name is provided via `aria-labelledby`", async ({
148+
mount,
149+
page,
150+
}) => {
151+
await mount(
152+
<DefaultAdaptiveSidebar aria-labelledby="accessible-name" open />,
153+
);
154+
155+
await checkAccessibility(page);
156+
});
135157
});
136158
});

src/components/adaptive-sidebar/adaptive-sidebar.stories.tsx

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,11 @@ export const Basic: Story = () => {
9494
eu elit eget lacus fermentum porta at ut dui.
9595
</Typography>
9696
</Box>
97-
<AdaptiveSidebar open={adaptiveSidebarOpen} width="300px">
97+
<AdaptiveSidebar
98+
aria-label="adaptive-sidebar"
99+
open={adaptiveSidebarOpen}
100+
width="300px"
101+
>
98102
<Box p={2} display="flex" flexDirection="column">
99103
<Button onClick={() => setAdaptiveSidebarOpen(false)} mb={2}>
100104
Close
@@ -129,7 +133,11 @@ export const Default: Story = () => {
129133
eu elit eget lacus fermentum porta at ut dui.
130134
</Typography>
131135
</Box>
132-
<AdaptiveSidebar open={adaptiveSidebarOpen} width="300px">
136+
<AdaptiveSidebar
137+
aria-label="adaptive-sidebar"
138+
open={adaptiveSidebarOpen}
139+
width="300px"
140+
>
133141
<Box
134142
display="flex"
135143
flexDirection="row"
@@ -182,7 +190,11 @@ export const Complex: Story = () => {
182190
dui.
183191
</Typography>
184192
</Box>
185-
<AdaptiveSidebar open={adaptiveSidebarOpen} width="500px">
193+
<AdaptiveSidebar
194+
aria-label="adaptive-sidebar"
195+
open={adaptiveSidebarOpen}
196+
width="500px"
197+
>
186198
<Box display="flex" flexDirection="column" pb={5}>
187199
<Button onClick={() => setAdaptiveSidebarOpen(false)} m={2}>
188200
Close
@@ -247,7 +259,11 @@ export const WithCustomWidth: Story = () => {
247259
<Box display="flex" flexDirection="row">
248260
{CommonTemplate(adaptiveSidebarOpen, setAdaptiveSidebarOpen)}
249261

250-
<AdaptiveSidebar open={adaptiveSidebarOpen} width="70%">
262+
<AdaptiveSidebar
263+
aria-label="adaptive-sidebar"
264+
open={adaptiveSidebarOpen}
265+
width="70%"
266+
>
251267
<Box display="flex" flexDirection="column">
252268
<Box
253269
display="flex"
@@ -284,7 +300,11 @@ export const WithCustomHeight: Story = () => {
284300
>
285301
{CommonTemplate(adaptiveSidebarOpen, setAdaptiveSidebarOpen)}
286302

287-
<AdaptiveSidebar open={adaptiveSidebarOpen} height="98vh">
303+
<AdaptiveSidebar
304+
aria-label="adaptive-sidebar"
305+
open={adaptiveSidebarOpen}
306+
height="98vh"
307+
>
288308
<Box display="flex" flexDirection="column">
289309
<Box
290310
display="flex"
@@ -376,6 +396,7 @@ export const BackgroundVariants: Story = () => {
376396
</Box>
377397

378398
<AdaptiveSidebar
399+
aria-label="adaptive-sidebar"
379400
backgroundColor={colour as "white" | "black" | "app"}
380401
open={adaptiveSidebarOpen}
381402
width="300px"
@@ -419,6 +440,7 @@ export const WithAdaptiveBreakpoint: Story = () => {
419440
{CommonTemplate(adaptiveSidebarOpen, setAdaptiveSidebarOpen)}
420441

421442
<AdaptiveSidebar
443+
aria-label="adaptive-sidebar"
422444
adaptiveBreakpoint={650}
423445
open={adaptiveSidebarOpen}
424446
width="300px"
@@ -454,7 +476,12 @@ export const RenderAsModal: Story = () => {
454476
<Box display="flex" flexDirection="row">
455477
{CommonTemplate(adaptiveSidebarOpen, setAdaptiveSidebarOpen)}
456478

457-
<AdaptiveSidebar renderAsModal open={adaptiveSidebarOpen} width="300px">
479+
<AdaptiveSidebar
480+
aria-label="adaptive-sidebar"
481+
renderAsModal
482+
open={adaptiveSidebarOpen}
483+
width="300px"
484+
>
458485
<Box display="flex" flexDirection="column" m={2}>
459486
<Box
460487
display="flex"
@@ -508,6 +535,7 @@ export const WithCustomBorderColor: Story = () => {
508535
</Typography>
509536
</Box>
510537
<AdaptiveSidebar
538+
aria-label="adaptive-sidebar"
511539
open={adaptiveSidebarOpen}
512540
width="300px"
513541
borderColor="--colorsActionMajor500"
@@ -590,6 +618,7 @@ export const Hidden: Story = () => {
590618
</Typography>
591619
</Box>
592620
<AdaptiveSidebar
621+
aria-label="adaptive-sidebar"
593622
hidden={adaptiveSidebarHidden}
594623
open={adaptiveSidebarOpen}
595624
width="300px"

src/components/adaptive-sidebar/adaptive-sidebar.test.tsx

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const MockApp = ({
3939
</Typography>
4040
</Box>
4141
<AdaptiveSidebar open={adaptiveSidebarOpen} {...props}>
42-
My Content
42+
<span id="accessible-name">My Content</span>
4343
<Button data-role="custom-close-button" onClick={() => {}}>
4444
Custom close
4545
</Button>
@@ -206,6 +206,32 @@ test("should render the AdaptiveSidebar component as a modal", async () => {
206206
expect(screen.getByTestId("modal-background")).toBeInTheDocument();
207207
});
208208

209+
test("when rendered as a modal, the accessible name of the modal is set via `aria-label`", async () => {
210+
const user = userEvent.setup();
211+
212+
render(<MockApp renderAsModal aria-label="sidebar" />);
213+
214+
const openButton = screen.getByTestId("adaptive-sidebar-control-button");
215+
await user.click(openButton);
216+
217+
const modal = screen.getByRole("dialog");
218+
219+
expect(modal).toHaveAccessibleName("sidebar");
220+
});
221+
222+
test("when rendered as a modal, the accessible name of the modal is set via `aria-labelledby`", async () => {
223+
const user = userEvent.setup();
224+
225+
render(<MockApp renderAsModal aria-labelledby="accessible-name" />);
226+
227+
const openButton = screen.getByTestId("adaptive-sidebar-control-button");
228+
await user.click(openButton);
229+
230+
const modal = screen.getByRole("dialog");
231+
232+
expect(modal).toHaveAccessibleName("My Content");
233+
});
234+
209235
test("should close the AdaptiveSidebar component when the control button is clicked", async () => {
210236
const user = userEvent.setup();
211237

src/components/adaptive-sidebar/components.test-pw.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const DefaultAdaptiveSidebar = ({ ...props }) => {
3636
>
3737
Close
3838
</Button>
39-
Adaptive sidebar content
39+
<span id="accessible-name">Adaptive sidebar content</span>
4040
</AdaptiveSidebar>
4141
</Box>
4242
</>

0 commit comments

Comments
 (0)