diff --git a/packages/modals/src/elements/TooltipDialog/TooltipDialog.spec.tsx b/packages/modals/src/elements/TooltipDialog/TooltipDialog.spec.tsx
index b4dce630d9..881223f12f 100644
--- a/packages/modals/src/elements/TooltipDialog/TooltipDialog.spec.tsx
+++ b/packages/modals/src/elements/TooltipDialog/TooltipDialog.spec.tsx
@@ -232,4 +232,68 @@ describe('TooltipDialog', () => {
expect(onCloseSpy).toHaveBeenCalled();
});
});
+
+ describe('keepMounted', () => {
+ it('keeps dialog mounted in the DOM while visually hidden', () => {
+ const { queryByRole, getByTestId } = render(
+
+ );
+
+ // Closed (hidden) state: still mounted but visually hidden and aria-hidden on backdrop
+ const hiddenDialog = queryByRole('dialog', { hidden: true });
+
+ expect(hiddenDialog).not.toBeNull();
+ expect(getByTestId('backdrop')).toHaveAttribute('aria-hidden', 'true');
+ // Backdrop should have hideVisually styles applied (position: absolute, clip, etc.)
+ const backdropStyles = window.getComputedStyle(getByTestId('backdrop'));
+ expect(backdropStyles.position).toBe('absolute');
+ expect(backdropStyles.width).toBe('1px');
+ expect(backdropStyles.height).toBe('1px');
+ });
+
+ it('toggles visibility and manages focus correctly when reopened', async () => {
+ const { getByText, getByRole, queryByRole, getByTestId } = render(
+
+ );
+
+ const trigger = getByText('open');
+
+ // Initially hidden but mounted
+ const initiallyHiddenDialog = queryByRole('dialog', { hidden: true });
+ expect(initiallyHiddenDialog).not.toBeNull();
+ expect(getByTestId('backdrop')).toHaveAttribute('aria-hidden', 'true');
+
+ // Open
+ await act(async () => {
+ await user.click(trigger);
+ });
+
+ const openDialog = getByRole('dialog');
+ expect(openDialog).toBeInTheDocument();
+ expect(getByTestId('backdrop')).not.toHaveAttribute('aria-hidden');
+ expect(openDialog).toHaveFocus();
+ // Backdrop should NOT have hideVisually styles when visible
+ const visibleBackdropStyles = window.getComputedStyle(getByTestId('backdrop'));
+ expect(visibleBackdropStyles.position).toBe('fixed');
+
+ // Close (toggle button again)
+ await act(async () => {
+ await user.click(trigger);
+ });
+
+ // Dialog remains mounted but visually hidden again
+ const hiddenAgainDialog = queryByRole('dialog', { hidden: true });
+ expect(hiddenAgainDialog).not.toBeNull();
+ await waitFor(() => {
+ expect(getByTestId('backdrop')).toHaveAttribute('aria-hidden', 'true');
+ // Backdrop should have hideVisually styles applied again
+ const hiddenBackdropStyles = window.getComputedStyle(getByTestId('backdrop'));
+ expect(hiddenBackdropStyles.position).toBe('absolute');
+ expect(hiddenBackdropStyles.width).toBe('1px');
+ expect(hiddenBackdropStyles.height).toBe('1px');
+ });
+ // Focus should return to trigger
+ expect(trigger).toHaveFocus();
+ });
+ });
});
diff --git a/packages/modals/src/elements/TooltipDialog/TooltipDialog.tsx b/packages/modals/src/elements/TooltipDialog/TooltipDialog.tsx
index 81691d5bec..d14bed2a9a 100644
--- a/packages/modals/src/elements/TooltipDialog/TooltipDialog.tsx
+++ b/packages/modals/src/elements/TooltipDialog/TooltipDialog.tsx
@@ -36,6 +36,12 @@ import { createPortal } from 'react-dom';
const PLACEMENT_DEFAULT = 'top';
+/**
+ * 1. When content is kept mounted we must manually focus on re-open
+ * 2. Hide only at 'exited' so exit animations finish
+ * and floating-ui sizing/focus logic remain valid during 'exiting'.
+ * Earlier hiding would cut animation and risk focus/layout issues.
+ */
const TooltipDialogComponent = React.forwardRef(
(
{
@@ -46,6 +52,7 @@ const TooltipDialogComponent = React.forwardRef {
+ if (keepMounted && focusOnMount && modalRef.current) {
+ modalRef.current.focus(); // [1]
+ }
+ }}
>
{transitionState => {
+ const isHidden = keepMounted && transitionState === 'exited'; // [2]
+
return (
)}
{...backdropProps}
ref={transitionRef}
+ aria-hidden={isHidden ? true : undefined}
>
props['aria-hidden'] && hideVisually()}
+
${componentStyles};
`;
diff --git a/packages/modals/src/types/index.ts b/packages/modals/src/types/index.ts
index 71583390b6..5bb231660f 100644
--- a/packages/modals/src/types/index.ts
+++ b/packages/modals/src/types/index.ts
@@ -83,6 +83,10 @@ export interface ITooltipDialogProps extends Omit