Skip to content

Commit

Permalink
feat(action-popover): add aria-labelledby and aria-describedby props
Browse files Browse the repository at this point in the history
fix #7107
  • Loading branch information
nuria1110 committed Dec 5, 2024
1 parent 6675b86 commit 48552f1
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { IconType } from "../../icon";

export type ActionPopoverMenuButtonAria = {
"aria-haspopup": string;
"aria-label": string;
"aria-labelledby"?: string;
"aria-describedby"?: string;
"aria-controls": string;
"aria-expanded": string;
};
Expand Down Expand Up @@ -41,6 +42,7 @@ export const ActionPopoverMenuButton = ({
iconPosition,
size,
children,
ariaAttributes,
...props
}: ActionPopoverMenuButtonProps) => (
<MenuButtonOverrideWrapper>
Expand All @@ -49,6 +51,7 @@ export const ActionPopoverMenuButton = ({
iconType={iconType}
iconPosition={iconPosition}
size={size}
{...ariaAttributes}
{...props}
>
{children}
Expand Down
52 changes: 51 additions & 1 deletion src/components/action-popover/action-popover-test.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ActionPopoverItem,
ActionPopoverMenu,
ActionPopoverProps,
ActionPopoverMenuButton,
} from ".";
import {
FlatTable,
Expand All @@ -21,7 +22,7 @@ import Box from "../box";

export default {
title: "Action Popover/Test",
includeStories: ["Default", "LongMenuExample"],
includeStories: ["Default", "LongMenuExample", "WithAriaAttributes"],
parameters: {
info: { disable: true },
chromatic: {
Expand Down Expand Up @@ -800,3 +801,52 @@ export const LongMenuExample = () => {
</Box>
);
};

export const WithAriaAttributes = () => {
return (
<>
<span id="test-label-id">
Instead of "actions", this should be the label{" "}
</span>
<br />
<span id="test-description-id">This should be the description</span>

<ActionPopover
aria-labelledby="test-label-id"
aria-describedby="test-description-id"
>
<ActionPopoverItem>example item</ActionPopoverItem>
</ActionPopover>

<span id="foo-label-id">Instead of "Foo", this should be the label </span>
<br />
<span id="foo-description-id">
This should be the description for Foo
</span>

<ActionPopover
aria-labelledby="foo-label-id"
aria-describedby="foo-description-id"
renderButton={({
tabIndex,
"data-element": dataElement,
ariaAttributes,
}) => (
<ActionPopoverMenuButton
buttonType="tertiary"
iconType="dropdown"
iconPosition="after"
size="small"
tabIndex={tabIndex}
data-element={dataElement}
ariaAttributes={ariaAttributes}
>
Foo
</ActionPopoverMenuButton>
)}
>
<ActionPopoverItem onClick={() => {}}>foo</ActionPopoverItem>
</ActionPopover>
</>
);
};
14 changes: 12 additions & 2 deletions src/components/action-popover/action-popover.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ export interface RenderButtonProps {
"data-element": string;
ariaAttributes: {
"aria-haspopup": string;
"aria-label": string;
"aria-labelledby"?: string;
"aria-describedby"?: string;
"aria-controls": string;
"aria-expanded": string;
};
Expand All @@ -62,6 +63,10 @@ export interface ActionPopoverProps extends MarginProps {
rightAlignMenu?: boolean;
/** Prop to specify an aria-label for the component */
"aria-label"?: string;
/** Prop to specify an aria-labelledby for the component */
"aria-labelledby"?: string;
/** Prop to specify an aria-describedby for the component */
"aria-describedby"?: string;
}

const onOpenDefault = () => {};
Expand All @@ -78,6 +83,8 @@ export const ActionPopover = ({
horizontalAlignment = "left",
submenuPosition = "left",
"aria-label": ariaLabel,
"aria-labelledby": ariaLabelledBy,
"aria-describedby": ariaDescribedBy,
...rest
}: ActionPopoverProps) => {
const l = useLocale();
Expand Down Expand Up @@ -232,7 +239,8 @@ export const ActionPopover = ({
"data-element": "action-popover-button",
ariaAttributes: {
"aria-haspopup": "true",
"aria-label": ariaLabel || l.actionPopover.ariaLabel(),
"aria-labelledby": ariaLabelledBy,
"aria-describedby": ariaDescribedBy,
"aria-controls": menuID,
"aria-expanded": `${isOpen}`,
},
Expand All @@ -244,6 +252,8 @@ export const ActionPopover = ({
role="button"
aria-haspopup="true"
aria-label={ariaLabel || l.actionPopover.ariaLabel()}
aria-labelledby={ariaLabelledBy}
aria-describedby={ariaDescribedBy}
aria-controls={menuID}
aria-expanded={isOpen}
tabIndex={isOpen ? -1 : 0}
Expand Down
59 changes: 58 additions & 1 deletion src/components/action-popover/action-popover.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,32 @@ test("uses the aria-label prop if provided", () => {
expect(screen.getByRole("button")).toHaveAccessibleName("test aria label");
});

test("renders with the provided aria-labelledby prop", () => {
render(
<>
<span id="test-label-id">test label</span>
<ActionPopover aria-labelledby="test-label-id">
<ActionPopoverItem>example item</ActionPopoverItem>
</ActionPopover>
</>,
);
expect(screen.getByRole("button")).toHaveAccessibleName("test label");
});

test("renders with the provided aria-describedby prop", () => {
render(
<>
<span id="test-description-id">test description</span>
<ActionPopover aria-describedby="test-description-id">
<ActionPopoverItem>example item</ActionPopoverItem>
</ActionPopover>
</>,
);
expect(screen.getByRole("button")).toHaveAccessibleDescription(
"test description",
);
});

test("renders with the menu closed by default", () => {
render(
<ActionPopover>
Expand Down Expand Up @@ -1825,7 +1851,7 @@ test("an error is thrown, with appropriate error message, if an invalid element
globalConsoleSpy.mockRestore();
});

test("an error is thrown, with appropriate error message, if a submenu has incorrecr children", async () => {
test("an error is thrown, with appropriate error message, if a submenu has incorrect children", async () => {
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });

const globalConsoleSpy = jest
Expand Down Expand Up @@ -1911,6 +1937,37 @@ describe("when the renderButton prop is passed", () => {

expect(menuButton).toHaveAttribute("tabindex", "-1");
});

it("renders the menu button with the provided aria-labelledby and aria-describedby", () => {
render(
<>
<span id="test-label-id">test label</span>
<span id="test-description-id">test description</span>
<ActionPopover
aria-labelledby="test-label-id"
aria-describedby="test-description-id"
renderButton={(props) => (
<ActionPopoverMenuButton
buttonType="tertiary"
iconType="dropdown"
iconPosition="after"
size="small"
{...props}
>
Foo
</ActionPopoverMenuButton>
)}
>
<ActionPopoverItem onClick={() => {}}>foo</ActionPopoverItem>
</ActionPopover>
</>,
);

const menuButton = screen.getByRole("button");
expect(menuButton).toBeVisible();
expect(menuButton).toHaveAccessibleName("test label");
expect(menuButton).toHaveAccessibleDescription("test description");
});
});

describe("When ActionPopoverMenu contains multiple disabled items", () => {
Expand Down

0 comments on commit 48552f1

Please sign in to comment.