diff --git a/src/core/chart-api/chart-extra-navigation.ts b/src/core/chart-api/chart-extra-navigation.ts index 647db75c..5ef8201d 100644 --- a/src/core/chart-api/chart-extra-navigation.ts +++ b/src/core/chart-api/chart-extra-navigation.ts @@ -162,7 +162,9 @@ export class ChartExtraNavigation { private onKeyDown = (event: KeyboardEvent) => { // The `handleKey` helper we use does not prevent the default keys behavior, so we do it here for - // all used keyboard commands. + // all used keyboard commands. We also stop propagation to prevent the global escape handler in + // chart-core.tsx from interfering with navigation - when keyboard navigation is active, this + // handler is the authoritative one for all navigation keys. if ( [ KeyCode.up, @@ -178,6 +180,7 @@ export class ChartExtraNavigation { ].includes(event.keyCode) ) { event.preventDefault(); + event.stopPropagation(); } // We execute different actions depending on the current focus target (chart, group, or point). // It is expected, that the focused element is still available in the chart. diff --git a/src/core/chart-core.tsx b/src/core/chart-core.tsx index eb0092c9..0e4f0547 100644 --- a/src/core/chart-core.tsx +++ b/src/core/chart-core.tsx @@ -110,23 +110,28 @@ export function InternalCoreChart({ // AWSUI-61678 // Add global Escape key listener to dismiss hover tooltips for WCAG Content on Hover/Focus compliance - // Only enabled when keyboard navigation is disabled; when enabled, navigation handles Escape + // This listener handles Escape when using mouse hover. When keyboard navigation is active (application + // element is focused), the navigation handler deals with Escape, so we skip to avoid conflicts. useEffect(() => { - if (!context.keyboardNavigationEnabled) { - const handleKeyDown = (event: KeyboardEvent) => { - if (event.keyCode === KeyCode.escape && context.tooltipEnabled) { - const tooltipState = api.tooltipStore.get(); - if (tooltipState.visible && !tooltipState.pinned) { - api.hideTooltip(); - } + const handleKeyDown = (event: KeyboardEvent) => { + // Skip if keyboard navigation is handling this (event originated from application element) + const target = event.target as Element; + if (context.keyboardNavigationEnabled && target?.closest('[role="application"]')) { + return; + } + + if (event.keyCode === KeyCode.escape && context.tooltipEnabled) { + const tooltipState = api.tooltipStore.get(); + if (tooltipState.visible && !tooltipState.pinned) { + api.hideTooltip(); } - }; + } + }; - document.addEventListener("keydown", handleKeyDown); - return () => { - document.removeEventListener("keydown", handleKeyDown); - }; - } + document.addEventListener("keydown", handleKeyDown); + return () => { + document.removeEventListener("keydown", handleKeyDown); + }; }, [api, context.tooltipEnabled, context.keyboardNavigationEnabled]); // Render fallback using the same root and container props as for the chart to ensure consistent