diff --git a/src/components/accordion/__snapshots__/accordion.test.tsx.snap b/src/components/accordion/__snapshots__/accordion.test.tsx.snap index af0032bd422..460bc19f21f 100644 --- a/src/components/accordion/__snapshots__/accordion.test.tsx.snap +++ b/src/components/accordion/__snapshots__/accordion.test.tsx.snap @@ -39,7 +39,7 @@ exports[`EuiAccordion behavior closes when clicked twice 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="25" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -95,7 +95,7 @@ exports[`EuiAccordion behavior does not open when isDisabled 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="23" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -148,7 +148,7 @@ exports[`EuiAccordion behavior opens when clicked once 1`] = ` aria-labelledby="generated-id" class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isOpen" id="22" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -201,7 +201,7 @@ exports[`EuiAccordion behavior opens when div is clicked if element is a div 1`] aria-labelledby="generated-id" class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isOpen" id="24" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -257,7 +257,7 @@ exports[`EuiAccordion is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="0" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -309,7 +309,7 @@ exports[`EuiAccordion isDisabled is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="21" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -344,7 +344,7 @@ exports[`EuiAccordion props arrowDisplay none is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="14" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -394,7 +394,7 @@ exports[`EuiAccordion props arrowDisplay right is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="13" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -446,7 +446,7 @@ exports[`EuiAccordion props arrowProps is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="15" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -500,7 +500,7 @@ exports[`EuiAccordion props buttonContent is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="3" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -550,7 +550,7 @@ exports[`EuiAccordion props buttonContentClassName is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="2" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -598,7 +598,7 @@ exports[`EuiAccordion props buttonElement is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="10" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -650,7 +650,7 @@ exports[`EuiAccordion props buttonProps is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="4" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -701,7 +701,7 @@ exports[`EuiAccordion props buttonProps paddingSize l 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="7" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -752,7 +752,7 @@ exports[`EuiAccordion props buttonProps paddingSize m 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="6" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -803,7 +803,7 @@ exports[`EuiAccordion props buttonProps paddingSize s 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="5" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -851,7 +851,7 @@ exports[`EuiAccordion props element is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="1" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -908,7 +908,7 @@ exports[`EuiAccordion props extraAction is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="11" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -958,7 +958,7 @@ exports[`EuiAccordion props forceState closed is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="16" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -1011,7 +1011,7 @@ exports[`EuiAccordion props forceState open is rendered 1`] = ` aria-labelledby="generated-id" class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isOpen" id="17" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -1064,7 +1064,7 @@ exports[`EuiAccordion props initialIsOpen is rendered 1`] = ` aria-labelledby="generated-id" class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isOpen" id="12" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -1127,7 +1127,7 @@ exports[`EuiAccordion props isLoading is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="19" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -1186,7 +1186,7 @@ exports[`EuiAccordion props isLoadingMessage is rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="20" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > diff --git a/src/components/accordion/accordion.test.tsx b/src/components/accordion/accordion.test.tsx index bb02e3d6ac7..64599153f7c 100644 --- a/src/components/accordion/accordion.test.tsx +++ b/src/components/accordion/accordion.test.tsx @@ -41,6 +41,15 @@ describe('EuiAccordion', () => { }); }); + test('role', () => { + const { queryByRole } = render( + + ); + + expect(queryByRole('region')).toBeInTheDocument(); + expect(queryByRole('group')).not.toBeInTheDocument(); + }); + describe('buttonContentClassName', () => { it('is rendered', () => { const { container } = render( @@ -313,7 +322,7 @@ describe('EuiAccordion', () => { it('moves focus to the content when expanded', () => { const component = mount(); - const childWrapper = component.find('div[role="region"]').getDOMNode(); + const childWrapper = component.find('div[role="group"]').getDOMNode(); expect(childWrapper).not.toBeFalsy(); expect(childWrapper).not.toBe(document.activeElement); diff --git a/src/components/accordion/accordion.tsx b/src/components/accordion/accordion.tsx index 80572d5babd..640576bc606 100644 --- a/src/components/accordion/accordion.tsx +++ b/src/components/accordion/accordion.tsx @@ -26,13 +26,21 @@ export const PADDING_SIZES = ['none', 'xs', 's', 'm', 'l', 'xl'] as const; export type EuiAccordionPaddingSize = (typeof PADDING_SIZES)[number]; export type EuiAccordionProps = CommonProps & - Omit, 'id'> & { + Omit, 'id' | 'role'> & { id: string; /** * Applied to the entire .euiAccordion wrapper. * When using `fieldset`, it will enforce `buttonElement = legend` as well. */ element?: 'div' | 'fieldset'; + /** + * Defaults to the [group role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/group_role). + * + * If your accordion contains significant enough content to be a document + * [landmark role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/region_role#accessibility_concerns), consider using the `region` role instead. + * @default group + */ + role?: HTMLAttributes['role']; /** * Class that will apply to the trigger for the accordion. */ @@ -124,6 +132,7 @@ export class EuiAccordionClass extends Component< isLoadingMessage: false, element: 'div' as const, buttonElement: 'button' as const, + role: 'group' as const, }; state = { @@ -184,6 +193,7 @@ export class EuiAccordionClass extends Component< children, className, id, + role, element: Element = 'div', buttonElement, buttonProps, @@ -239,6 +249,7 @@ export class EuiAccordionClass extends Component< /> & Pick< EuiAccordionProps, - 'children' | 'paddingSize' | 'isLoading' | 'isLoadingMessage' + 'role' | 'children' | 'paddingSize' | 'isLoading' | 'isLoadingMessage' > & { isOpen: boolean; accordionChildrenRef: Ref; @@ -37,6 +37,7 @@ type _EuiAccordionChildrenProps = HTMLAttributes & export const EuiAccordionChildren: FunctionComponent< _EuiAccordionChildrenProps > = ({ + role, children, accordionChildrenRef, paddingSize, @@ -90,7 +91,7 @@ export const EuiAccordionChildren: FunctionComponent< css={wrapperCssStyles} style={heightInlineStyle} ref={accordionChildrenRef} - role="region" + role={role} tabIndex={-1} // @ts-expect-error - inert property not yet available in React TS defs. TODO: Remove this once https://github.com/DefinitelyTyped/DefinitelyTyped/pull/60822 is merged inert={!isOpen ? '' : undefined} // Can't pass a boolean currently, Jest throws errors diff --git a/src/components/collapsible_nav/collapsible_nav_group/__snapshots__/collapsible_nav_group.test.tsx.snap b/src/components/collapsible_nav/collapsible_nav_group/__snapshots__/collapsible_nav_group.test.tsx.snap index 9c7a4bfde42..d60f9fd81de 100644 --- a/src/components/collapsible_nav/collapsible_nav_group/__snapshots__/collapsible_nav_group.test.tsx.snap +++ b/src/components/collapsible_nav/collapsible_nav_group/__snapshots__/collapsible_nav_group.test.tsx.snap @@ -254,7 +254,7 @@ exports[`EuiCollapsibleNavGroup when isCollapsible is true accepts accordion pro class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="id" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -319,7 +319,7 @@ exports[`EuiCollapsibleNavGroup when isCollapsible is true will render an accord class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="id" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > diff --git a/src/components/collapsible_nav_beta/collapsible_nav_group/__snapshots__/collapsible_nav_group.test.tsx.snap b/src/components/collapsible_nav_beta/collapsible_nav_group/__snapshots__/collapsible_nav_group.test.tsx.snap index debc4a82780..dd82c4d6852 100644 --- a/src/components/collapsible_nav_beta/collapsible_nav_group/__snapshots__/collapsible_nav_group.test.tsx.snap +++ b/src/components/collapsible_nav_beta/collapsible_nav_group/__snapshots__/collapsible_nav_group.test.tsx.snap @@ -8,6 +8,7 @@ exports[`EuiCollapsibleNavGroup renders 1`] = ` aria-label="aria-label" class="euiCollapsibleNavLink euiCollapsibleNavItem emotion-euiCollapsibleNavLink-isTopItem-isNotAccordion-euiCollapsibleNavGroup__title" data-test-subj="test subject string" + id="generated-id" >
{isCollapsed && isPush ? ( @@ -72,10 +74,16 @@ export const EuiCollapsibleNavGroup: FunctionComponent< ) : ( <> - + )}
diff --git a/src/components/collapsible_nav_beta/collapsible_nav_item/__snapshots__/collapsible_nav_accordion.test.tsx.snap b/src/components/collapsible_nav_beta/collapsible_nav_item/__snapshots__/collapsible_nav_accordion.test.tsx.snap index 293ba2b0a01..9a05ff15881 100644 --- a/src/components/collapsible_nav_beta/collapsible_nav_item/__snapshots__/collapsible_nav_accordion.test.tsx.snap +++ b/src/components/collapsible_nav_beta/collapsible_nav_item/__snapshots__/collapsible_nav_accordion.test.tsx.snap @@ -43,7 +43,7 @@ exports[`EuiCollapsibleNavAccordion renders as a sub item 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="generated-id" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -113,7 +113,7 @@ exports[`EuiCollapsibleNavAccordion renders as a top level item 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="generated-id" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > diff --git a/src/components/collapsible_nav_beta/collapsible_nav_item/__snapshots__/collapsible_nav_group.test.tsx.snap b/src/components/collapsible_nav_beta/collapsible_nav_item/__snapshots__/collapsible_nav_group.test.tsx.snap index 55fa4548a06..1cd58f934fb 100644 --- a/src/components/collapsible_nav_beta/collapsible_nav_item/__snapshots__/collapsible_nav_group.test.tsx.snap +++ b/src/components/collapsible_nav_beta/collapsible_nav_item/__snapshots__/collapsible_nav_group.test.tsx.snap @@ -6,11 +6,14 @@ exports[`EuiCollapsibleNavGroup renders as a sub item 1`] = ` > Header
Header
diff --git a/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_group.tsx b/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_group.tsx index ebc9cdeb190..b63ac6e82f7 100644 --- a/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_group.tsx +++ b/src/components/collapsible_nav_beta/collapsible_nav_item/collapsible_nav_group.tsx @@ -9,7 +9,7 @@ import React, { FunctionComponent, ReactNode } from 'react'; import classNames from 'classnames'; -import { useEuiTheme } from '../../../services'; +import { useEuiTheme, useGeneratedHtmlId } from '../../../services'; import { EuiCollapsibleNavSubItems, @@ -63,10 +63,13 @@ export const EuiCollapsibleNavGroup: FunctionComponent< } : undefined; // Prevents Emotion from generating a selector if no styles need to be applied + const labelledById = useGeneratedHtmlId(); + return (
); diff --git a/src/components/notification/__snapshots__/notification_event.test.tsx.snap b/src/components/notification/__snapshots__/notification_event.test.tsx.snap index 31c0724e804..2d2a2009c54 100644 --- a/src/components/notification/__snapshots__/notification_event.test.tsx.snap +++ b/src/components/notification/__snapshots__/notification_event.test.tsx.snap @@ -516,7 +516,7 @@ exports[`EuiNotificationEvent props multiple messages are rendered 1`] = ` class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="euiNotificationEventMessagesAccordion_generated-id" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > diff --git a/upcoming_changelogs/7326.md b/upcoming_changelogs/7326.md new file mode 100644 index 00000000000..cd8b7a759b2 --- /dev/null +++ b/upcoming_changelogs/7326.md @@ -0,0 +1,5 @@ +- Added a configurable `role` prop to `EuiAccordion` + +**Accessibility** + +- `EuiAccordion` now defaults to a less screenreader-noisy `group` role instead of `region`. If your accordion contains significant enough content to be a document landmark role, you may re-configure it back to `region`.