Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions src/Display/Accordion/Accordion.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,99 @@ describe('<Accordion /> click behavior', () => {
expect(panelContent2).toBeVisible();
});
});

describe('<Accordion /> accessibility', () => {
test('container should have proper ARIA attributes', () => {
const { container } = render(
<Accordion>
<Accordion.Panel label="label" content="content" />
</Accordion>
);

const accordionContainer = container.querySelector('.aries-accordion');
expect(accordionContainer).toHaveAttribute('role', 'region');
expect(accordionContainer).toHaveAttribute(
'aria-label',
'Accordion Container'
);
});

test('panel wrapper should have proper ARIA attributes', () => {
const { container } = render(
<Accordion>
<Accordion.Panel label="label" content="content" />
</Accordion>
);

const panelWrapper = container.querySelector('.panel-wrapper');
expect(panelWrapper).toHaveAttribute('role', 'tab');
expect(panelWrapper).toHaveAttribute('aria-expanded', 'false');
});

test('label wrapper should have proper ARIA attributes', () => {
const { container } = render(
<Accordion>
<Accordion.Panel label="label" content="content" />
</Accordion>
);

const labelWrapper = container.querySelector('.label-wrapper');
expect(labelWrapper).toHaveAttribute('role', 'button');
expect(labelWrapper).toHaveAttribute('aria-expanded', 'false');
expect(labelWrapper).toHaveAttribute('aria-controls');
expect(labelWrapper).toHaveAttribute('id');
});

test('content wrapper should have proper ARIA attributes', () => {
const { container } = render(
<Accordion>
<Accordion.Panel label="label" content="content" />
</Accordion>
);

const contentWrapper = container.querySelector('.content-wrapper');
expect(contentWrapper).toHaveAttribute('role', 'region');
expect(contentWrapper).toHaveAttribute('id');
expect(contentWrapper).toHaveAttribute('aria-labelledby');
});

test('ARIA attributes should correctly reference each other', () => {
const { container } = render(
<Accordion>
<Accordion.Panel label="label" content="content" />
</Accordion>
);

const labelWrapper = container.querySelector('.label-wrapper');
const contentWrapper = container.querySelector('.content-wrapper');

const labelId = labelWrapper.getAttribute('id');
const contentId = contentWrapper.getAttribute('id');

expect(labelWrapper).toHaveAttribute('aria-controls', contentId);
expect(contentWrapper).toHaveAttribute('aria-labelledby', labelId);
});

test('aria-expanded should update when panel is toggled', () => {
const { container, getByText } = render(
<Accordion>
<Accordion.Panel label="label" content="content" />
</Accordion>
);

const panelWrapper = container.querySelector('.panel-wrapper');
const labelWrapper = container.querySelector('.label-wrapper');
expect(panelWrapper).toHaveAttribute('aria-expanded', 'false');
expect(labelWrapper).toHaveAttribute('aria-expanded', 'false');

userEvent.click(getByText('label'));

expect(panelWrapper).toHaveAttribute('aria-expanded', 'true');
expect(labelWrapper).toHaveAttribute('aria-expanded', 'true');

userEvent.click(getByText('label'));

expect(panelWrapper).toHaveAttribute('aria-expanded', 'false');
expect(labelWrapper).toHaveAttribute('aria-expanded', 'false');
});
});
11 changes: 10 additions & 1 deletion src/Display/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,27 @@ const Accordion: Accordion = ({
};

return (
<Container className={classNames('aries-accordion', className)}>
<Container
className={classNames('aries-accordion', className)}
role="region"
aria-label="Accordion Container"
>
{React.Children.map(
children,
(child: React.ReactElement<AccordionPanelProps>, index) => {
const { label, content, ...restChildProps } = child.props;
const headingId = `accordion-heading-${index}`;
const contentId = `accordion-content-${index}`;

return React.cloneElement(child, {
key: index,
label: label,
content: content,
active: currIndex === index,
iconOptions: iconOptions,
onOpen: () => handleOpen(index),
headingId: headingId,
contentId: contentId,
...restChildProps,
});
}
Expand Down
16 changes: 15 additions & 1 deletion src/Display/Accordion/AccordionPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const AccordionPanel: React.FunctionComponent<Props> = props => {
iconOptions: { activeIcon, inactiveIcon, position },
onOpen,
onClick,
headingId,
contentId,
...restProps
} = props;
const handleClick = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
Expand Down Expand Up @@ -49,12 +51,22 @@ const AccordionPanel: React.FunctionComponent<Props> = props => {
tabIndex={-1}
position={position}
active={active}
role="button"
aria-expanded={active}
aria-controls={contentId}
id={headingId}
>
{position === 'left' && renderIcon()}
<Label>{label}</Label>
{position === 'right' && renderIcon()}
</IconLabelWrapper>
<ContentWrapper className="content-wrapper" active={active}>
<ContentWrapper
className="content-wrapper"
active={active}
role="region"
id={contentId}
aria-labelledby={headingId}
>
<Content position={position}>{content}</Content>
</ContentWrapper>
</PanelWrapper>
Expand All @@ -68,6 +80,8 @@ export type Props = React.ComponentPropsWithoutRef<typeof PanelWrapper> & {
iconOptions?: IconOptions;
onOpen?(): void;
onClick?(e: React.MouseEvent<HTMLDivElement, MouseEvent>): void;
headingId?: string;
contentId?: string;
};

export default AccordionPanel;
36 changes: 36 additions & 0 deletions src/Display/Accordion/__snapshots__/Accordion.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
exports[`<Accordion /> prop className should match snapshot when class name test is passed 1`] = `
<DocumentFragment>
<div
aria-label="Accordion Container"
class="AccordionStyle__Container-sc-7hk6zs-1 bCUcSS aries-accordion test"
role="region"
>
<div
aria-expanded="false"
Expand All @@ -12,7 +14,11 @@ exports[`<Accordion /> prop className should match snapshot when class name test
tabindex="0"
>
<div
aria-controls="accordion-content-0"
aria-expanded="false"
class="AccordionStyle__IconLabelWrapper-sc-7hk6zs-2 kOQHnF label-wrapper"
id="accordion-heading-0"
role="button"
tabindex="-1"
>
<div
Expand All @@ -38,7 +44,10 @@ exports[`<Accordion /> prop className should match snapshot when class name test
</div>
</div>
<div
aria-labelledby="accordion-heading-0"
class="AccordionStyle__ContentWrapper-sc-7hk6zs-3 iJedH content-wrapper"
id="accordion-content-0"
role="region"
>
<div
class="AccordionStyle__Content-sc-7hk6zs-4 hAQoeJ"
Expand All @@ -54,7 +63,9 @@ exports[`<Accordion /> prop className should match snapshot when class name test
exports[`<Accordion /> prop className should match snapshot when class name undefined is passed 1`] = `
<DocumentFragment>
<div
aria-label="Accordion Container"
class="AccordionStyle__Container-sc-7hk6zs-1 bCUcSS aries-accordion"
role="region"
>
<div
aria-expanded="false"
Expand All @@ -63,7 +74,11 @@ exports[`<Accordion /> prop className should match snapshot when class name unde
tabindex="0"
>
<div
aria-controls="accordion-content-0"
aria-expanded="false"
class="AccordionStyle__IconLabelWrapper-sc-7hk6zs-2 kOQHnF label-wrapper"
id="accordion-heading-0"
role="button"
tabindex="-1"
>
<div
Expand All @@ -89,7 +104,10 @@ exports[`<Accordion /> prop className should match snapshot when class name unde
</div>
</div>
<div
aria-labelledby="accordion-heading-0"
class="AccordionStyle__ContentWrapper-sc-7hk6zs-3 iJedH content-wrapper"
id="accordion-content-0"
role="region"
>
<div
class="AccordionStyle__Content-sc-7hk6zs-4 hAQoeJ"
Expand All @@ -105,7 +123,9 @@ exports[`<Accordion /> prop className should match snapshot when class name unde
exports[`<Accordion.Panel /> prop iconPosition should match snapshot when iconPosition left is passed 1`] = `
<DocumentFragment>
<div
aria-label="Accordion Container"
class="AccordionStyle__Container-sc-7hk6zs-1 bCUcSS aries-accordion"
role="region"
>
<div
aria-expanded="false"
Expand All @@ -114,7 +134,11 @@ exports[`<Accordion.Panel /> prop iconPosition should match snapshot when iconPo
tabindex="0"
>
<div
aria-controls="accordion-content-0"
aria-expanded="false"
class="AccordionStyle__IconLabelWrapper-sc-7hk6zs-2 kOQHnF label-wrapper"
id="accordion-heading-0"
role="button"
tabindex="-1"
>
<div
Expand All @@ -140,7 +164,10 @@ exports[`<Accordion.Panel /> prop iconPosition should match snapshot when iconPo
</div>
</div>
<div
aria-labelledby="accordion-heading-0"
class="AccordionStyle__ContentWrapper-sc-7hk6zs-3 iJedH content-wrapper"
id="accordion-content-0"
role="region"
>
<div
class="AccordionStyle__Content-sc-7hk6zs-4 hAQoeJ"
Expand All @@ -156,7 +183,9 @@ exports[`<Accordion.Panel /> prop iconPosition should match snapshot when iconPo
exports[`<Accordion.Panel /> prop iconPosition should match snapshot when iconPosition right is passed 1`] = `
<DocumentFragment>
<div
aria-label="Accordion Container"
class="AccordionStyle__Container-sc-7hk6zs-1 bCUcSS aries-accordion"
role="region"
>
<div
aria-expanded="false"
Expand All @@ -165,7 +194,11 @@ exports[`<Accordion.Panel /> prop iconPosition should match snapshot when iconPo
tabindex="0"
>
<div
aria-controls="accordion-content-0"
aria-expanded="false"
class="AccordionStyle__IconLabelWrapper-sc-7hk6zs-2 bQDRgc label-wrapper"
id="accordion-heading-0"
role="button"
tabindex="-1"
>
<div
Expand All @@ -191,7 +224,10 @@ exports[`<Accordion.Panel /> prop iconPosition should match snapshot when iconPo
</div>
</div>
<div
aria-labelledby="accordion-heading-0"
class="AccordionStyle__ContentWrapper-sc-7hk6zs-3 iJedH content-wrapper"
id="accordion-content-0"
role="region"
>
<div
class="AccordionStyle__Content-sc-7hk6zs-4 bBDEAR"
Expand Down