Skip to content

Commit a764511

Browse files
authored
feat: add onLegendItemHighlight callback (#66)
* feat: add onLegendHover callback * fix tests * fix: improve naming * rename missing type
1 parent 58fdc23 commit a764511

File tree

6 files changed

+45
-5
lines changed

6 files changed

+45
-5
lines changed

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33

44
import { act } from "react";
55
import highcharts from "highcharts";
6+
import { vi } from "vitest";
67

78
import { KeyCode } from "@cloudscape-design/component-toolkit/internal";
89

9-
import { createChartWrapper, renderChart } from "./common";
10+
import { createChartWrapper, hoverLegendItem, renderChart } from "./common";
1011
import { HighchartsTestHelper } from "./highcharts-utils";
1112

1213
import legendTestClasses from "../../../lib/components/internal/components/chart-legend/test-classes/styles.selectors.js";
@@ -451,4 +452,23 @@ describe("CoreChart: legend", () => {
451452
expect(wrapper.findLegend()!.findItemTooltip()!.findHeader()!.getElement().textContent).toBe("P2");
452453
});
453454
});
455+
456+
test("calls onLegendItemHighlight when hovering over a legend item", () => {
457+
const onLegendItemHighlight = vi.fn();
458+
const { wrapper } = renderChart({ highcharts, options: { series }, onLegendItemHighlight });
459+
460+
hoverLegendItem(0, wrapper);
461+
462+
expect(onLegendItemHighlight).toHaveBeenCalledWith(
463+
expect.objectContaining({
464+
item: expect.objectContaining({
465+
id: "L1",
466+
name: "L1",
467+
marker: expect.any(Object),
468+
visible: expect.any(Boolean),
469+
highlighted: expect.any(Boolean),
470+
}),
471+
}),
472+
);
473+
});
454474
});

src/core/__tests__/common.tsx

Lines changed: 7 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";
@@ -33,6 +33,7 @@ export function StatefulChart(props: CoreChartProps) {
3333
}
3434

3535
type TestProps = Partial<CoreChartProps> & {
36+
onLegendItemHighlight?: () => void;
3637
i18nProvider?: Record<string, Record<string, string>>;
3738
};
3839

@@ -86,3 +87,8 @@ export function toggleLegendItem(index: number, wrapper: BaseChartWrapper = crea
8687
const modifier = Math.random() > 0.5 ? { metaKey: true } : { ctrlKey: true };
8788
act(() => wrapper.findLegend()!.findItems()[index].click(modifier));
8889
}
90+
export function hoverLegendItem(index: number, wrapper: BaseChartWrapper = createChartWrapper()) {
91+
act(() => {
92+
fireEvent.mouseOver(wrapper.findLegend()!.findItems()[index].getElement());
93+
});
94+
}

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+
onLegendItemHighlight,
6162
onClearHighlight,
6263
onVisibleItemsChange,
6364
visibleItems,
@@ -307,6 +308,7 @@ export function InternalCoreChart({
307308
position={legendPosition}
308309
api={api}
309310
i18nStrings={i18nStrings}
311+
onItemHighlight={onLegendItemHighlight}
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+
onItemHighlight,
1718
getLegendTooltipContent,
1819
}: {
1920
api: ChartAPI;
2021
title?: string;
2122
actions?: React.ReactNode;
2223
position: "bottom" | "side";
2324
i18nStrings?: BaseI18nStrings;
25+
onItemHighlight?: (detail: CoreChartProps.LegendItemHighlightDetail) => 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+
onItemHighlight?.({ 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 a legend item is highlighted.
346+
*/
347+
onLegendItemHighlight?: (detail: CoreChartProps.LegendItemHighlightDetail) => void;
344348
/**
345349
* Called when series/points visibility changes due to user interaction with legend or filter.
346350
*/
@@ -441,6 +445,9 @@ export namespace CoreChartProps {
441445
items: TooltipContentItem[];
442446
}
443447

448+
export interface LegendItemHighlightDetail {
449+
item: LegendItem;
450+
}
444451
export interface VisibleItemsChangeDetail {
445452
items: readonly LegendItem[];
446453
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)