diff --git a/.changeset/perfect-fishes-camp.md b/.changeset/perfect-fishes-camp.md
new file mode 100644
index 00000000000..7bc8ee2b346
--- /dev/null
+++ b/.changeset/perfect-fishes-camp.md
@@ -0,0 +1,5 @@
+---
+"@primer/react": patch
+---
+
+fix(TooltipV2): always add aria-hidden
diff --git a/packages/react/src/TooltipV2/Tooltip.tsx b/packages/react/src/TooltipV2/Tooltip.tsx
index 3f613d340a2..4c574664b31 100644
--- a/packages/react/src/TooltipV2/Tooltip.tsx
+++ b/packages/react/src/TooltipV2/Tooltip.tsx
@@ -328,8 +328,8 @@ export const Tooltip = React.forwardRef(
{...rest}
// Only need tooltip role if the tooltip is a description for supplementary information
role={type === 'description' ? 'tooltip' : undefined}
- // stop AT from announcing the tooltip twice when it is a label type because it will be announced with "aria-labelledby"
- aria-hidden={type === 'label' ? true : undefined}
+ // stop AT from announcing the tooltip twice: when it is a label type it will be announced with "aria-labelledby",when it is a description type it will be announced with "aria-describedby"
+ aria-hidden={true}
id={tooltipId}
// mouse leave and enter on the tooltip itself is needed to keep the tooltip open when the mouse is over the tooltip
onMouseEnter={openTooltip}
diff --git a/packages/react/src/TooltipV2/__tests__/Tooltip.test.tsx b/packages/react/src/TooltipV2/__tests__/Tooltip.test.tsx
index 0bf8c8b3149..a088914a114 100644
--- a/packages/react/src/TooltipV2/__tests__/Tooltip.test.tsx
+++ b/packages/react/src/TooltipV2/__tests__/Tooltip.test.tsx
@@ -51,6 +51,10 @@ describe('Tooltip', () => {
const {getByText} = HTMLRender()
expect(getByText('Tooltip text')).toHaveAttribute('aria-hidden', 'true')
})
+ it('should render aria-hidden on the tooltip element when the tooltip is description type', () => {
+ const {getByText} = HTMLRender()
+ expect(getByText('Tooltip text')).toHaveAttribute('aria-hidden', 'true')
+ })
it('should describe the trigger element by its tooltip when the tooltip type is description (by default)', () => {
const {getByRole, getByText} = HTMLRender()
const triggerEL = getByRole('button')
diff --git a/packages/react/src/__tests__/ActionMenu.test.tsx b/packages/react/src/__tests__/ActionMenu.test.tsx
index 5abc967ed55..8c8d372e403 100644
--- a/packages/react/src/__tests__/ActionMenu.test.tsx
+++ b/packages/react/src/__tests__/ActionMenu.test.tsx
@@ -402,7 +402,7 @@ describe('ActionMenu', () => {
button.focus()
})
- expect(component.getByRole('tooltip')).toBeInTheDocument()
+ expect(component.getByRole('tooltip', {hidden: true})).toBeInTheDocument()
})
it('should open menu on menu anchor click and it is wrapped with tooltip v2', async () => {
@@ -438,7 +438,7 @@ describe('ActionMenu', () => {
button.focus()
})
- expect(component.getByRole('tooltip')).toBeInTheDocument()
+ expect(component.getByRole('tooltip', {hidden: true})).toBeInTheDocument()
})
it('should pass the "id" prop from ActionMenu.Button to the HTML button', async () => {
diff --git a/packages/react/src/__tests__/__snapshots__/TextInput.test.tsx.snap b/packages/react/src/__tests__/__snapshots__/TextInput.test.tsx.snap
index f5f5c3e315b..e7d70033076 100644
--- a/packages/react/src/__tests__/__snapshots__/TextInput.test.tsx.snap
+++ b/packages/react/src/__tests__/__snapshots__/TextInput.test.tsx.snap
@@ -3401,6 +3401,7 @@ exports[`TextInput renders trailingAction text button with a tooltip 1`] = `