diff --git a/src/core/__tests__/chart-core-navigation-cartesian.test.tsx b/src/core/__tests__/chart-core-navigation-cartesian.test.tsx index 92eb4292..1baea946 100644 --- a/src/core/__tests__/chart-core-navigation-cartesian.test.tsx +++ b/src/core/__tests__/chart-core-navigation-cartesian.test.tsx @@ -334,6 +334,8 @@ describe("CoreChart: navigation, cartesian charts", () => { expect(describeFocusedElement()).toBe("button:1[true,false]"); expect(wrapper.findTooltip()!.getElement().textContent).toBe("1Line series 111Line series 221"); + // First ESC dismisses tooltip, second ESC navigates to chart + keyDown(KeyCode.escape); keyDown(KeyCode.escape); expect(describeFocusedElement()).toBe("application:Test chart"); @@ -560,19 +562,21 @@ describe("CoreChart: navigation, cartesian charts", () => { ); test("moves focus from point to group", () => { - const { wrapper } = renderChart(commonProps(false, seriesLong1)); + const { wrapper } = renderChart(commonProps(false, seriesShort2)); focusApplication(); keyDown(KeyCode.enter); keyDown(KeyCode.down); - expect(describeFocusedElement()).toBe("button:1 -1, Line series 1[true,false]"); - expect(wrapper.findTooltip()!.getElement().textContent).toBe("1Line series 1-1"); + expect(describeFocusedElement()).toBe("button:1 11, Line series 1[true,false]"); + expect(wrapper.findTooltip()!.getElement().textContent).toBe("1Line series 111Line series 221"); + // First ESC dismisses tooltip, second ESC navigates to group + keyDown(KeyCode.escape); keyDown(KeyCode.escape); expect(describeFocusedElement()).toBe("button:1[true,false]"); - expect(wrapper.findTooltip()!.getElement().textContent).toBe("1Line series 1-1"); + expect(wrapper.findTooltip()!.getElement().textContent).toBe("1Line series 111Line series 221"); }); test("pins popover at point", () => { diff --git a/src/core/__tests__/chart-core-navigation-pie.test.tsx b/src/core/__tests__/chart-core-navigation-pie.test.tsx index 853971ba..76251b7f 100644 --- a/src/core/__tests__/chart-core-navigation-pie.test.tsx +++ b/src/core/__tests__/chart-core-navigation-pie.test.tsx @@ -174,6 +174,8 @@ describe("CoreChart: navigation, pie charts", () => { expect(describeFocusedElement()).toBe("button:P1, 10. Pie series[true,false]"); expect(wrapper.findTooltip()!.getElement().textContent).toBe("P1Pie series10"); + // First ESC dismisses tooltip, second ESC navigates to chart + keyDown(KeyCode.escape); keyDown(KeyCode.escape); expect(describeFocusedElement()).toBe("application:Test chart"); diff --git a/src/core/chart-api/chart-extra-navigation.ts b/src/core/chart-api/chart-extra-navigation.ts index 647db75c..e02725e5 100644 --- a/src/core/chart-api/chart-extra-navigation.ts +++ b/src/core/chart-api/chart-extra-navigation.ts @@ -27,6 +27,9 @@ export interface ChartExtraNavigationHandlers { onBlur(): void; onActivatePoint(point: Highcharts.Point, group: readonly Highcharts.Point[]): void; onActivateGroup(group: readonly Highcharts.Point[]): void; + // Returns true if a tooltip was dismissed, false otherwise. + // Used by ESC handlers to decide whether to also navigate. + onDismissHoverTooltip(): boolean; } export type FocusedState = FocusedStateChart | FocusedStateGroup | FocusedStatePoint; @@ -193,6 +196,7 @@ export class ChartExtraNavigation { private onKeyDownChart = (event: KeyboardEvent) => { handleKey(event, { onActivate: () => this.moveToFirstGroup(), + onEscape: () => this.handlers.onDismissHoverTooltip(), onInlineStart: () => this.moveToLastGroup(), onInlineEnd: () => this.moveToFirstGroup(), onBlockStart: () => this.moveToLastGroup(), @@ -206,7 +210,11 @@ export class ChartExtraNavigation { const i = !!this.context.chart().inverted; handleKey(event, { onActivate: () => this.activateGroup(group), - onEscape: () => this.moveToChart(), + onEscape: () => { + if (!this.handlers.onDismissHoverTooltip()) { + this.moveToChart(); + } + }, onInlineStart: () => (i ? this.moveToFirstInGroup(group) : this.moveToPrevGroup(group)), onInlineEnd: () => (i ? this.moveToLastInGroup(group) : this.moveToNextGroup(group)), onBlockStart: () => (i ? this.moveToPrevGroup(group) : this.moveToLastInGroup(group)), @@ -222,7 +230,11 @@ export class ChartExtraNavigation { const i = !!this.context.chart().inverted; handleKey(event, { onActivate: () => this.activatePoint(point, group), - onEscape: () => this.moveToParentGroup(point), + onEscape: () => { + if (!this.handlers.onDismissHoverTooltip()) { + this.moveToParentGroup(point); + } + }, onInlineEnd: () => (i ? this.moveToPrevInGroup(point) : this.moveToNextInSeries(point)), onInlineStart: () => (i ? this.moveToNextInGroup(point) : this.moveToPrevInSeries(point)), onBlockEnd: () => (i ? this.moveToNextInSeries(point) : this.moveToNextInGroup(point)), diff --git a/src/core/chart-api/index.tsx b/src/core/chart-api/index.tsx index cad956f2..294afa2e 100644 --- a/src/core/chart-api/index.tsx +++ b/src/core/chart-api/index.tsx @@ -264,6 +264,17 @@ export class ChartAPI { this.clearChartHighlight({ isApiCall: false }); this.chartExtraNavigation.announceChart(getChartAccessibleDescription(this.context.chart())); }, + onDismissHoverTooltip: () => { + // Dismiss any visible non-pinned tooltip when ESC is pressed. + // Returns true if a tooltip was dismissed, false otherwise. + // Used by ESC handlers at group/point level to decide whether to also navigate. + const tooltipState = this.chartExtraTooltip.get(); + if (tooltipState.visible && !tooltipState.pinned) { + this.clearChartHighlight({ isApiCall: false }); + return true; + } + return false; + }, onFocusGroup: (group: Highcharts.Point[]) => { this.highlightActions(group, { isApiCall: false, overrideTooltipLock: true }); this.chartExtraNavigation.announceElement(getGroupAccessibleDescription(group), false);