diff --git a/build-tools/eslint/__tests__/prefer-live-region.test.js b/build-tools/eslint/__tests__/prefer-live-region.test.js index 3b2dda100a..f9a7f41d86 100644 --- a/build-tools/eslint/__tests__/prefer-live-region.test.js +++ b/build-tools/eslint/__tests__/prefer-live-region.test.js @@ -6,7 +6,7 @@ const useLiveRegionOverAriaLive = require('../prefer-live-region'); const ruleTester = new RuleTester({ languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } } }); ruleTester.run('no-aria-live', useLiveRegionOverAriaLive, { - valid: ['
'], + valid: ['
', '
'], invalid: [ { diff --git a/build-tools/eslint/prefer-live-region.js b/build-tools/eslint/prefer-live-region.js index 9f67cb1d67..d386ae7a80 100644 --- a/build-tools/eslint/prefer-live-region.js +++ b/build-tools/eslint/prefer-live-region.js @@ -16,7 +16,11 @@ module.exports = { JSXElement(node) { if ( node.openingElement.attributes.some( - attribute => attribute.type === 'JSXAttribute' && attribute.name.name === 'aria-live' + attribute => + attribute.type === 'JSXAttribute' && + attribute.name.name === 'aria-live' && + // Allow aria-live="off" to disable implicit aria-live behaviors of certain roles. + !(attribute.value && attribute.value.type === 'Literal' && attribute.value.value === 'off') ) ) { context.report({ diff --git a/pages/steps/with-updates.page.tsx b/pages/steps/with-updates.page.tsx index fc75e8b063..559a010e5b 100644 --- a/pages/steps/with-updates.page.tsx +++ b/pages/steps/with-updates.page.tsx @@ -5,6 +5,7 @@ import React, { useRef, useState } from 'react'; import Box from '~components/box'; import Button from '~components/button'; import Header from '~components/header'; +import LiveRegion from '~components/live-region'; import Steps from '~components/steps'; import { @@ -124,13 +125,13 @@ export default function StepsPermutationsWithUpdates() { Steps Component Description - + - +
@@ -140,9 +141,9 @@ export default function StepsPermutationsWithUpdates() { Blocked Execution - + - +
@@ -152,9 +153,9 @@ export default function StepsPermutationsWithUpdates() { Failed Execution - + - +
@@ -164,9 +165,9 @@ export default function StepsPermutationsWithUpdates() { Failed Execution with Retry - + - +
diff --git a/src/flashbar/__tests__/collapsible.test.tsx b/src/flashbar/__tests__/collapsible.test.tsx index 241dbecd48..9dac776908 100644 --- a/src/flashbar/__tests__/collapsible.test.tsx +++ b/src/flashbar/__tests__/collapsible.test.tsx @@ -317,12 +317,14 @@ describe('Collapsible Flashbar', () => { }); it('announces updates to the item counter with aria-live', () => { - const flashbar = renderFlashbar(); - const counter = findOuterCounter(flashbar); - expect(counter).toHaveAttribute('aria-live', 'polite'); - // We add `role="status"` as well, to maximize compatibility - // https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions#roles_with_implicit_live_region_attributes - expect(counter).toHaveAttribute('role', 'status'); + // Fake timers to bypass the live region delay. + const { rerender } = render(); + jest.useFakeTimers(); + rerender(); + jest.runAllTimers(); + jest.useRealTimers(); + const liveRegion = document.querySelector('[aria-live=polite]')!; + expect(liveRegion).toHaveTextContent('Notifications Error 1 Warning 0 Success 1 Information 1 In progress 0'); }); it('renders the toggle element header as H2 element', () => { diff --git a/src/flashbar/collapsible-flashbar.tsx b/src/flashbar/collapsible-flashbar.tsx index 644ac35f1b..ef04cefedb 100644 --- a/src/flashbar/collapsible-flashbar.tsx +++ b/src/flashbar/collapsible-flashbar.tsx @@ -20,6 +20,7 @@ import { useDebounceCallback } from '../internal/hooks/use-debounce-callback'; import { useEffectOnUpdate } from '../internal/hooks/use-effect-on-update'; import { useThrottleCallback } from '../internal/hooks/use-throttle-callback'; import { scrollElementIntoView } from '../internal/utils/scrollable-containers'; +import InternalLiveRegion from '../live-region/internal'; import { GeneratedAnalyticsMetadataFlashbarCollapse, GeneratedAnalyticsMetadataFlashbarExpand, @@ -361,7 +362,7 @@ export default function CollapsibleFlashbar({ items, style, ...restProps }: Inte }, } as GeneratedAnalyticsMetadataFlashbarExpand | GeneratedAnalyticsMetadataFlashbarCollapse)} > - + {notificationBarText &&

{notificationBarText}

} {counterTypes.map(({ type, labelName, iconName }) => ( @@ -374,6 +375,13 @@ export default function CollapsibleFlashbar({ items, style, ...restProps }: Inte ))}
+ [iconAriaLabels[labelName], `${countByType[type]}`]), + ]} + />