Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/core/chart-api/chart-extra-navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -178,6 +180,7 @@ export class ChartExtraNavigation {
].includes(event.keyCode)
) {
event.preventDefault();
event.stopPropagation();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this break other event handlers? E.g. if I am currently in the keyboard navigation, and I have an event listener on the parent element, does it still get triggered?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, intentionally. Standard role="application" behavior, captures all keys to prevent parent handlers (page scrolling, modal closing, etc.) from interfering with chart navigation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if this role is not set? Or can't that happen?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not happen in practice because, the role="application" is hardcoded in the ChartApplication component (it's not configurable). The application element is only rendered when keyboardNavigation={true}.

But if hypothetically it wasn't set: the global Escape handler would run during keyboard navigation, causing tooltip to dismiss when user meant to exit navigation.

}
// 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.
Expand Down
33 changes: 19 additions & 14 deletions src/core/chart-core.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"]')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do wen need to check for target?.closest('[role="application"]')?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prevents double-handling. The navigation handler in chart-extra-navigation.ts already handles Escape for keyboard nav, this check skips the global handler when navigation is active.

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
Expand Down
Loading