diff --git a/.changeset/easy-suits-mate.md b/.changeset/easy-suits-mate.md
new file mode 100644
index 00000000000..f4e8f19d4c1
--- /dev/null
+++ b/.changeset/easy-suits-mate.md
@@ -0,0 +1,5 @@
+---
+'@primer/react': patch
+---
+
+Replaces 'aria-live' usage and removes internal LiveRegion component
diff --git a/packages/react/src/DataTable/Pagination.tsx b/packages/react/src/DataTable/Pagination.tsx
index c58bf6375ec..950838a5638 100644
--- a/packages/react/src/DataTable/Pagination.tsx
+++ b/packages/react/src/DataTable/Pagination.tsx
@@ -2,7 +2,7 @@ import {ChevronLeftIcon, ChevronRightIcon} from '@primer/octicons-react'
import type React from 'react'
import {useCallback, useMemo, useState} from 'react'
import {Button} from '../internal/components/ButtonReset'
-import {LiveRegion, LiveRegionOutlet, Message} from '../internal/components/LiveRegion'
+import {AriaStatus} from '../live-region'
import {VisuallyHidden} from '../VisuallyHidden'
import {warning} from '../utils/warning'
import type {ResponsiveValue} from '../hooks/useResponsiveValue'
@@ -99,72 +99,69 @@ export function Pagination({
}, [pageCount, pageIndex, showPages])
return (
-
-
-
-
+
)
}
@@ -179,7 +176,11 @@ function Range({pageStart, pageEnd, totalCount}: RangeProps) {
const end = pageEnd
return (
<>
-
+
+
+ Showing {start} through {end} of {totalCount}
+
+
{start}
through
diff --git a/packages/react/src/Skeleton/Skeleton.examples.stories.tsx b/packages/react/src/Skeleton/Skeleton.examples.stories.tsx
index 641c863b6b1..acc83622da8 100644
--- a/packages/react/src/Skeleton/Skeleton.examples.stories.tsx
+++ b/packages/react/src/Skeleton/Skeleton.examples.stories.tsx
@@ -7,6 +7,7 @@ import {SkeletonAvatar} from '../SkeletonAvatar'
import {VisuallyHidden} from '../VisuallyHidden'
import {KebabHorizontalIcon} from '@primer/octicons-react'
import classes from './Skeleton.examples.stories.module.css'
+import {AriaStatus} from '../experimental'
export default {
title: 'Components/Skeleton/Examples',
@@ -42,7 +43,9 @@ export const CommentsLoading = () => {
{/** read by screen readers in place of the comments in a skeleton loading state */}
{loading ? Comments are loading : null}
{/** when loading is completed, it should be announced by the screen-reader */}
- {loadingFinished ? 'Comments are loaded' : null}
+
+ {loadingFinished ? 'Comments are loaded' : null}
+
{Array.from({length: COMMENT_LIST_LENGTH}, (_, index) => (
@@ -101,7 +104,9 @@ export const CommentsLoadingWithSuspense = () => {
{/** read by screen readers in place of the comments in a skeleton loading state */}
{loadingStatus === 'pending' ? Comments are loading : null}
{/** when loading is completed, it should be announced by the screen-reader */}
- {loadingStatus === 'fulfilled' ? 'Comments are loaded' : null}
+
+ {loadingStatus === 'fulfilled' ? 'Comments are loaded' : null}
+
{/* aria-busy is passed so the screenreader doesn't announce the skeleton state */}
diff --git a/packages/react/src/TreeView/TreeView.test.tsx b/packages/react/src/TreeView/TreeView.test.tsx
index 240d67b078c..f0d2d77f8ad 100644
--- a/packages/react/src/TreeView/TreeView.test.tsx
+++ b/packages/react/src/TreeView/TreeView.test.tsx
@@ -5,6 +5,7 @@ import React from 'react'
import type {SubTreeState} from './TreeView'
import {TreeView} from './TreeView'
import {GearIcon} from '@primer/octicons-react'
+import {getLiveRegion} from '../live-region/__tests__/test-helpers'
// TODO: Move this function into a shared location
function renderWithTheme(
@@ -1391,7 +1392,14 @@ describe('State', () => {
})
describe('Asynchronous loading', () => {
- it('updates aria live region when loading is done', () => {
+ afterEach(() => {
+ const liveRegion = document.querySelector('live-region')
+ if (liveRegion) {
+ document.body.removeChild(liveRegion)
+ }
+ })
+
+ it('updates aria live region when loading is done', async () => {
function TestTree() {
const [state, setState] = React.useState('initial')
@@ -1423,29 +1431,33 @@ describe('Asynchronous loading', () => {
)
}
+ const user = userEvent.setup()
const {getByRole} = renderWithTheme()
const doneButton = getByRole('button', {name: 'Load'})
- const liveRegion = getByRole('status')
+ const liveRegion = getLiveRegion()
// Live region should be empty
- expect(liveRegion).toHaveTextContent('')
+ expect(liveRegion.getMessage('polite')).toBe('')
// Click load button to mimic async loading
- fireEvent.click(doneButton)
+ await act(async () => {
+ await user.click(doneButton)
+ })
- expect(liveRegion).toHaveTextContent('Parent content loading')
+ expect(liveRegion.getMessage('polite')).toBe('Parent content loading')
// Click done button to mimic the completion of async loading
- fireEvent.click(doneButton)
+ await act(async () => {
+ await user.click(doneButton)
+ })
act(() => {
vi.runAllTimers()
})
// Live region should be updated
- expect(liveRegion).not.toHaveTextContent('Child 2 is empty')
- expect(liveRegion).toHaveTextContent('Parent content loaded')
+ expect(liveRegion.getMessage('polite')).toBe('Parent content loaded')
})
it('moves focus from loading item to first child', async () => {
@@ -1810,7 +1822,8 @@ describe('CSS Module Migration', () => {
)
- // Testing on the second child element because the first child element is visually hidden
- expect(render().container.children[1]).toHaveClass('test-class-name')
+ // Find the TreeView ul element (which should have the className)
+ const treeElement = render().getByRole('tree')
+ expect(treeElement).toHaveClass('test-class-name')
})
})
diff --git a/packages/react/src/TreeView/TreeView.tsx b/packages/react/src/TreeView/TreeView.tsx
index 86100c9cbce..c10d740513a 100644
--- a/packages/react/src/TreeView/TreeView.tsx
+++ b/packages/react/src/TreeView/TreeView.tsx
@@ -29,6 +29,7 @@ import {useIsMacOS} from '../hooks'
import {Tooltip} from '../TooltipV2'
import {isSlot} from '../utils/is-slot'
import type {FCWithSlotMarker} from '../utils/types'
+import {AriaStatus} from '../live-region'
// ----------------------------------------------------------------------------
// Context
@@ -144,8 +145,8 @@ const Root: React.FC = ({
}}
>
<>
-
- {ariaLiveMessage}
+
+ {ariaLiveMessage}