Skip to content

Commit 006b6a7

Browse files
committed
feat: add onLegendHover callback
1 parent e93f51a commit 006b6a7

File tree

6 files changed

+47
-9
lines changed

6 files changed

+47
-9
lines changed

src/core/__tests__/chart-core-api.test.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { act } from "react";
55
import highcharts from "highcharts";
66
import { vi } from "vitest";
77

8-
import { CoreChartAPI } from "../../../lib/components/core/interfaces";
9-
import { renderChart, selectLegendItem } from "./common";
8+
import { CoreChartProps } from "../interfaces";
9+
import { hoverLegendItem, renderChart, selectLegendItem } from "./common";
1010
import { HighchartsTestHelper } from "./highcharts-utils";
1111

1212
const clearHighlightPause = () => new Promise((resolve) => setTimeout(resolve, 100));
@@ -32,7 +32,7 @@ describe("CoreChart: API tests", () => {
3232

3333
test("passes isApiCall=true to onHighlight when triggered programmatically through API", () => {
3434
const onHighlight = vi.fn();
35-
let chartApi: CoreChartAPI | null = null;
35+
let chartApi: CoreChartProps.ChartAPI | null = null;
3636

3737
renderChart({ highcharts, onHighlight, options: { series }, callback: (api) => (chartApi = api) });
3838

@@ -55,7 +55,7 @@ describe("CoreChart: API tests", () => {
5555

5656
test("passes isApiCall=true to onClearHighlight when triggered programmatically through API", () => {
5757
const onClearHighlight = vi.fn();
58-
let chartApi: CoreChartAPI | null = null;
58+
let chartApi: CoreChartProps.ChartAPI | null = null;
5959

6060
renderChart({ highcharts, onClearHighlight, options: { series }, callback: (api) => (chartApi = api) });
6161

@@ -74,11 +74,30 @@ describe("CoreChart: API tests", () => {
7474

7575
test("passes isApiCall=true to onVisibleItemsChange when triggered programmatically through API", () => {
7676
const onVisibleItemsChange = vi.fn();
77-
let chartApi: CoreChartAPI | null = null;
77+
let chartApi: CoreChartProps.ChartAPI | null = null;
7878

7979
renderChart({ highcharts, options: { series }, onVisibleItemsChange, callback: (api) => (chartApi = api) });
8080

8181
act(() => chartApi!.setItemsVisible(["Line 1"]));
8282
expect(onVisibleItemsChange).toHaveBeenCalledWith({ items: expect.any(Array), isApiCall: true });
8383
});
84+
85+
test("calls onLegendHover when hovering over a legend item", () => {
86+
const onLegendHover = vi.fn();
87+
const { wrapper } = renderChart({ highcharts, options: { series }, onLegendHover });
88+
89+
hoverLegendItem(0, wrapper);
90+
91+
expect(onLegendHover).toHaveBeenCalledWith(
92+
expect.objectContaining({
93+
item: expect.objectContaining({
94+
id: "Line 1",
95+
name: "Line 1",
96+
marker: expect.any(Object),
97+
visible: expect.any(Boolean),
98+
highlighted: expect.any(Boolean),
99+
}),
100+
}),
101+
);
102+
});
84103
});

src/core/__tests__/common.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import { act, useState } from "react";
5-
import { render } from "@testing-library/react";
5+
import { fireEvent, render } from "@testing-library/react";
66

77
import "@cloudscape-design/components/test-utils/dom";
88
import { CoreChartProps } from "../../../lib/components/core/interfaces";
@@ -86,3 +86,8 @@ export function toggleLegendItem(index: number, wrapper: BaseChartWrapper = crea
8686
const modifier = Math.random() > 0.5 ? { metaKey: true } : { ctrlKey: true };
8787
act(() => wrapper.findLegend()!.findItems()[index].click(modifier));
8888
}
89+
export function hoverLegendItem(index: number, wrapper: BaseChartWrapper = createChartWrapper()) {
90+
act(() => {
91+
fireEvent.mouseOver(wrapper.findLegend()!.findItems()[index].getElement());
92+
});
93+
}

src/core/chart-core.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export function InternalCoreChart({
5858
filter,
5959
keyboardNavigation = true,
6060
onHighlight,
61+
onLegendHover,
6162
onClearHighlight,
6263
onVisibleItemsChange,
6364
visibleItems,
@@ -307,6 +308,7 @@ export function InternalCoreChart({
307308
position={legendPosition}
308309
api={api}
309310
i18nStrings={i18nStrings}
311+
onItemHover={onLegendHover}
310312
getLegendTooltipContent={rest.getLegendTooltipContent}
311313
/>
312314
) : null

src/core/components/core-legend.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ export function ChartLegend({
1414
actions,
1515
position,
1616
i18nStrings,
17+
onItemHover,
1718
getLegendTooltipContent,
1819
}: {
1920
api: ChartAPI;
2021
title?: string;
2122
actions?: React.ReactNode;
2223
position: "bottom" | "side";
2324
i18nStrings?: BaseI18nStrings;
25+
onItemHover?: (detail: CoreChartProps.LegendHoverDetail) => void;
2426
getLegendTooltipContent?: CoreChartProps.GetLegendTooltipContent;
2527
}) {
2628
const i18n = useInternalI18n("[charts]");
@@ -39,8 +41,11 @@ export function ChartLegend({
3941
actions={actions}
4042
position={position}
4143
onItemVisibilityChange={api.onItemVisibilityChange}
42-
onItemHighlightEnter={(itemId) => api.onHighlightChartItems([itemId])}
4344
onItemHighlightExit={api.onClearChartItemsHighlight}
45+
onItemHighlightEnter={(item) => {
46+
api.onHighlightChartItems([item.id]);
47+
onItemHover?.({ item });
48+
}}
4449
getTooltipContent={(props) => {
4550
if (isChartTooltipPinned) {
4651
return null;

src/core/interfaces.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,10 @@ export interface CoreChartProps
341341
* IDs of visible series or points.
342342
*/
343343
visibleItems?: readonly string[];
344+
/**
345+
* Called when the user hovers over a legend item.
346+
*/
347+
onLegendHover?: (detail: CoreChartProps.LegendHoverDetail) => void;
344348
/**
345349
* Called when series/points visibility changes due to user interaction with legend or filter.
346350
*/
@@ -440,6 +444,9 @@ export namespace CoreChartProps {
440444
items: TooltipContentItem[];
441445
}
442446

447+
export interface LegendHoverDetail {
448+
item: LegendItem;
449+
}
443450
export interface VisibleItemsChangeDetail {
444451
items: readonly LegendItem[];
445452
isApiCall: boolean;

src/internal/components/chart-legend/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export interface ChartLegendProps {
3232
ariaLabel?: string;
3333
actions?: React.ReactNode;
3434
position: "bottom" | "side";
35-
onItemHighlightEnter: (itemId: string) => void;
35+
onItemHighlightEnter: (item: LegendItem) => void;
3636
onItemHighlightExit: () => void;
3737
onItemVisibilityChange: (hiddenItems: string[]) => void;
3838
getTooltipContent: (props: GetLegendTooltipContentProps) => null | LegendTooltipContent;
@@ -120,7 +120,7 @@ export const ChartLegend = ({
120120
const item = items.find((item) => item.id === itemId);
121121
if (item?.visible) {
122122
highlightControl.cancelPrevious();
123-
onItemHighlightEnter(itemId);
123+
onItemHighlightEnter(item);
124124
}
125125
};
126126
const clearHighlight = () => {

0 commit comments

Comments
 (0)