diff --git a/src/Tooltip.tsx b/src/Tooltip.tsx index 52a5aae..ac1ea87 100644 --- a/src/Tooltip.tsx +++ b/src/Tooltip.tsx @@ -120,8 +120,12 @@ const Tooltip = React.forwardRef((props, ref) => { // ======================== Children ======================== const getChildren: TriggerProps['children'] = ({ open }) => { const child = React.Children.only(children); + const childAriaDescribedBy = (child.props as React.AriaAttributes)['aria-describedby']; + const ariaDescribedBy = [childAriaDescribedBy, overlay && open ? mergedId : undefined] + .filter(Boolean) + .join(' '); const ariaProps: React.AriaAttributes = { - 'aria-describedby': overlay && open ? mergedId : undefined, + 'aria-describedby': ariaDescribedBy || undefined, }; return React.cloneElement(child, ariaProps); }; diff --git a/tests/index.test.tsx b/tests/index.test.tsx index 50b4dbb..019854d 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -575,6 +575,28 @@ describe('rc-tooltip', () => { expect(container.querySelector('button')).not.toHaveAttribute('aria-describedby'); }); + it('should preserve the child aria-describedby across visibility changes', () => { + const overlay = 'tooltip content'; + const renderTooltip = (visible: boolean) => ( + + + + ); + const { container, rerender } = render(renderTooltip(false)); + const trigger = container.querySelector('button'); + + expect(trigger).toHaveAttribute('aria-describedby', 'existing-description'); + + rerender(renderTooltip(true)); + expect(trigger).toHaveAttribute( + 'aria-describedby', + 'existing-description tooltip-description', + ); + + rerender(renderTooltip(false)); + expect(trigger).toHaveAttribute('aria-describedby', 'existing-description'); + }); + it('should preserve original props of children', () => { const onMouseEnter = jest.fn();