Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/announce-skip-empty-reflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': patch
---

perf(Announce): skip getComputedStyle when there is no text content to announce
7 changes: 3 additions & 4 deletions packages/react/src/TreeView/TreeView.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1440,16 +1440,15 @@ describe('Asynchronous loading', () => {
const {getByRole} = renderWithTheme(<TestTree />)

const doneButton = getByRole('button', {name: 'Load'})
const liveRegion = getLiveRegion()

// Live region should be empty
expect(liveRegion.getMessage('polite')).toBe('')

// Click load button to mimic async loading
await act(async () => {
await user.click(doneButton)
})

// Get live region after the first announcement creates it
const liveRegion = getLiveRegion()

expect(liveRegion.getMessage('polite')).toBe('Parent content loading')

// Click done button to mimic the completion of async loading
Expand Down
18 changes: 13 additions & 5 deletions packages/react/src/live-region/Announce.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,25 @@ export function Announce<As extends React.ElementType = 'div'>(props: AnnouncePr
return
}

const style = window.getComputedStyle(element)
if (style.display === 'none') {
// PERFORMANCE: Check text content before getComputedStyle to avoid forcing
// a style recalculation when there's nothing to announce. getComputedStyle
// triggers a synchronous reflow that can cost hundreds of milliseconds on
// large DOM trees (e.g. TreeView with 14K+ nodes).
const textContent = getTextContent(element)
if (!textContent) {
return
}

if (style.visibility === 'hidden') {
if (textContent === previousAnnouncementText) {
return
}

const textContent = getTextContent(element)
if (textContent === previousAnnouncementText) {
const style = window.getComputedStyle(element)
if (style.display === 'none') {
return
}

if (style.visibility === 'hidden') {
return
}

Expand Down
Loading