diff --git a/e2e/components/Tooltip/Tooltip-test.avt.e2e.js b/e2e/components/Tooltip/Tooltip-test.avt.e2e.js index 1fa765bcd6c9..dd35ae669b3f 100644 --- a/e2e/components/Tooltip/Tooltip-test.avt.e2e.js +++ b/e2e/components/Tooltip/Tooltip-test.avt.e2e.js @@ -44,8 +44,7 @@ test.describe('@avt Tooltip', () => { await expect(page).toHaveNoACViolations('Tooltip - duration'); }); - // Prevent timeout - test.slow('@avt-keyboard-nav - tooltip default', async ({ page }) => { + test('@avt-keyboard-nav - tooltip default', async ({ page }) => { await visitStory(page, { component: 'Tooltip', id: 'components-tooltip--default', @@ -54,7 +53,6 @@ test.describe('@avt Tooltip', () => { }, }); - await page.keyboard.press('Tab'); await expect(page.getByRole('button')).toBeVisible(); // Expect tooltip to be focused await page.keyboard.press('Tab'); @@ -62,4 +60,29 @@ test.describe('@avt Tooltip', () => { // Expect tooltip content to be visible await expect(page.locator('.cds--popover-container')).toBeVisible(); }); + + test('@avt-keyboard-nav - tooltip default Escape key on hover', async ({ + page, + }) => { + await visitStory(page, { + component: 'Tooltip', + id: 'components-tooltip--default', + globals: { + theme: 'white', + }, + }); + + const tooltipTrigger = page.getByRole('button'); + await expect(tooltipTrigger).toBeVisible(); + + await tooltipTrigger.hover(); + + const tooltipContent = page.getByText( + 'Occasionally, services are updated in a specified time window to ensure no down time for customers.' + ); + await expect(tooltipContent).toBeVisible(); + // Press ESCAPE key while hover is active + await page.keyboard.press('Escape'); + await expect(tooltipContent).toBeHidden(); + }); }); diff --git a/packages/react/src/components/Tooltip/Tooltip.tsx b/packages/react/src/components/Tooltip/Tooltip.tsx index ccd00475933b..fd10188cb656 100644 --- a/packages/react/src/components/Tooltip/Tooltip.tsx +++ b/packages/react/src/components/Tooltip/Tooltip.tsx @@ -15,6 +15,7 @@ import { useId } from '../../internal/useId'; import { useNoInteractiveChildren } from '../../internal/useNoInteractiveChildren'; import { usePrefix } from '../../internal/usePrefix'; import { type PolymorphicProps } from '../../types/common'; +import useIsomorphicEffect from '../../internal/useIsomorphicEffect'; /** * Event types that trigger a "drag" to stop. @@ -144,19 +145,40 @@ function Tooltip({ triggerProps['aria-describedby'] = id; } - function onKeyDown(event: React.KeyboardEvent) { - if (open && match(event, keys.Escape)) { - event.stopPropagation(); - setOpen(false); + const onKeyDown = useCallback( + (event: React.SyntheticEvent | Event) => { + if (open && match(event, keys.Escape)) { + event.stopPropagation(); + setOpen(false); + } + if ( + open && + closeOnActivation && + (match(event, keys.Enter) || match(event, keys.Space)) + ) { + setOpen(false); + } + }, + [closeOnActivation, open, setOpen] + ); + + useIsomorphicEffect(() => { + if (!open) { + return undefined; } - if ( - open && - closeOnActivation && - (match(event, keys.Enter) || match(event, keys.Space)) - ) { - setOpen(false); + + function handleKeyDown(event: KeyboardEvent) { + if (match(event, keys.Escape)) { + onKeyDown(event); + } } - } + + document.addEventListener('keydown', handleKeyDown); + + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [open, onKeyDown]); function onMouseEnter() { // Interactive Tags should not support onMouseEnter @@ -221,7 +243,8 @@ function Tooltip({ }, [isDragging, onDragStop]); return ( - + // @ts-ignore-error Popover throws a TS error everytime is imported + ({ aria-hidden={open ? 'false' : 'true'} className={`${prefix}--tooltip-content`} id={id} + onMouseEnter={onMouseEnter} role="tooltip"> {label || description}