diff --git a/packages/grafana-ui/src/components/Toggletip/Toggletip.test.tsx b/packages/grafana-ui/src/components/Toggletip/Toggletip.test.tsx index b9c84ca8ec8..83f50757161 100644 --- a/packages/grafana-ui/src/components/Toggletip/Toggletip.test.tsx +++ b/packages/grafana-ui/src/components/Toggletip/Toggletip.test.tsx @@ -140,7 +140,7 @@ describe('Toggletip', () => { expect(onOpen).toHaveBeenCalledTimes(1); }); - it('should be able to focus toggletip content next in DOM order - forwards and backwards', async () => { + it('should trap content within the overlay', async () => { const onClose = jest.fn(); const afterInDom = 'Outside of toggletip'; @@ -164,17 +164,39 @@ describe('Toggletip', () => { const closeButton = screen.getByTestId('toggletip-header-close'); expect(closeButton).toHaveFocus(); - // focus after - await userEvent.tab(); - expect(afterButton).toHaveFocus(); + // tab forwards + await userEvent.keyboard('{tab}'); + // need to waitFor here to wait for the floating-ui focus manager to take effect + await waitFor(() => { + expect(closeButton).toHaveFocus(); + }); + expect(afterButton).not.toHaveFocus(); - // focus backwards - await userEvent.tab({ shift: true }); - expect(closeButton).toHaveFocus(); + // tab forwards again + await userEvent.keyboard('{tab}'); + // need to waitFor here to wait for the floating-ui focus manager to take effect + await waitFor(() => { + expect(closeButton).toHaveFocus(); + }); + expect(afterButton).not.toHaveFocus(); - // focus back to togglebutton - await userEvent.tab({ shift: true }); + // tab backwards + await userEvent.keyboard('{shift}{tab}'); + // need to waitFor here to wait for the floating-ui focus manager to take effect + await waitFor(() => { + expect(closeButton).toHaveFocus(); + }); + expect(afterButton).not.toHaveFocus(); + + // close overlay, focus back to togglebutton + await userEvent.keyboard('{escape}'); expect(button).toHaveFocus(); + expect(afterButton).not.toHaveFocus(); + + // tab forwards with overlay closed + await userEvent.tab(); + expect(closeButton).not.toHaveFocus(); + expect(afterButton).toHaveFocus(); }); describe('Focus state', () => { diff --git a/packages/grafana-ui/src/components/Toggletip/Toggletip.tsx b/packages/grafana-ui/src/components/Toggletip/Toggletip.tsx index 84f13eaf112..41cc4e55ab4 100644 --- a/packages/grafana-ui/src/components/Toggletip/Toggletip.tsx +++ b/packages/grafana-ui/src/components/Toggletip/Toggletip.tsx @@ -20,6 +20,7 @@ import { useStyles2, useTheme2 } from '../../themes/ThemeContext'; import { getPositioningMiddleware } from '../../utils/floating'; import { buildTooltipTheme, getPlacement } from '../../utils/tooltipUtils'; import { IconButton } from '../IconButton/IconButton'; +import { Portal } from '../Portal/Portal'; import { ToggletipContent } from './types'; @@ -118,44 +119,46 @@ export const Toggletip = memo( ...getReferenceProps(), })} {isOpen && ( - -
- - {Boolean(title) &&
{title}
} - {closeButton && ( -
- { - setControlledVisible(false); - onClose?.(); - }} - /> + + +
+ + {Boolean(title) &&
{title}
} + {closeButton && ( +
+ { + setControlledVisible(false); + onClose?.(); + }} + /> +
+ )} +
+ {(typeof content === 'string' || isValidElement(content)) && content} + {typeof content === 'function' && content({})}
- )} -
- {(typeof content === 'string' || isValidElement(content)) && content} - {typeof content === 'function' && content({})} + {Boolean(footer) &&
{footer}
}
- {Boolean(footer) &&
{footer}
} -
-
+ +
)} ); diff --git a/packages/grafana-ui/src/utils/tooltipUtils.ts b/packages/grafana-ui/src/utils/tooltipUtils.ts index c02edbece4c..eef90d24392 100644 --- a/packages/grafana-ui/src/utils/tooltipUtils.ts +++ b/packages/grafana-ui/src/utils/tooltipUtils.ts @@ -51,8 +51,8 @@ export function buildTooltipTheme( headerClose: css({ color: theme.colors.text.secondary, position: 'absolute', - right: theme.spacing(1), - top: theme.spacing(1.5), + right: theme.spacing(0.5), + top: theme.spacing(1), backgroundColor: 'transparent', border: 0, }),