Skip to content

Commit 7d480a0

Browse files
committed
feat: add support for programmatically highlight chart items
1 parent e93f51a commit 7d480a0

File tree

4 files changed

+66
-5
lines changed

4 files changed

+66
-5
lines changed

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

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

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

@@ -18,10 +18,27 @@ const series: Highcharts.SeriesOptionsType[] = [
1818
{ type: "line", name: "Line 2", data: [4, 5, 6] },
1919
];
2020

21+
const threeSeries: Highcharts.SeriesOptionsType[] = [
22+
{ type: "line", name: "Line 1", data: [1, 2, 3] },
23+
{ type: "line", name: "Line 2", data: [4, 5, 6] },
24+
{ type: "line", name: "Line 3", data: [7, 8, 9] },
25+
];
26+
27+
const pieSeries: Highcharts.SeriesOptionsType[] = [
28+
{
29+
type: "pie",
30+
name: "Data",
31+
data: [
32+
{ name: "Segment 1", y: 33.3 },
33+
{ name: "Segment 2", y: 33.3 },
34+
{ name: "Segment 3", y: 33.4 },
35+
],
36+
},
37+
];
38+
2139
describe("CoreChart: API tests", () => {
2240
test("passes isApiCall=false to onHighlight when triggered by an user interaction", () => {
2341
const onHighlight = vi.fn();
24-
renderChart({ highcharts, options: { series }, onHighlight });
2542

2643
act(() => hc.highlightChartPoint(0, 0));
2744

@@ -32,7 +49,7 @@ describe("CoreChart: API tests", () => {
3249

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

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

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

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

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

@@ -74,11 +91,49 @@ describe("CoreChart: API tests", () => {
7491

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

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

8198
act(() => chartApi!.setItemsVisible(["Line 1"]));
8299
expect(onVisibleItemsChange).toHaveBeenCalledWith({ items: expect.any(Array), isApiCall: true });
83100
});
101+
102+
test("highlightSeries should only highlight the specified series in a line chart", () => {
103+
let chartApi: CoreChartProps.ChartAPI | null = null;
104+
renderChart({ highcharts, options: { series: threeSeries }, callback: (api) => (chartApi = api) });
105+
106+
act(() => chartApi!.highlightItems([hc.getChartSeries(1).name]));
107+
108+
expect(hc.getChartSeries(0).state).toBe("inactive");
109+
expect(hc.getChartSeries(1).state).not.toBe("inactive");
110+
expect(hc.getChartSeries(2).state).toBe("inactive");
111+
112+
act(() => chartApi!.clearChartHighlight());
113+
act(() => chartApi!.highlightItems([hc.getChartSeries(0).name, hc.getChartSeries(2).name]));
114+
115+
expect(hc.getChartSeries(0).state).not.toBe("inactive");
116+
expect(hc.getChartSeries(1).state).toBe("inactive");
117+
expect(hc.getChartSeries(2).state).not.toBe("inactive");
118+
});
119+
120+
test("highlightChartPoint should only highlight the specified point in a pie chart", () => {
121+
let chartApi: CoreChartProps.ChartAPI | null = null;
122+
renderChart({ highcharts, options: { series: pieSeries }, callback: (api) => (chartApi = api) });
123+
124+
const series = hc.getChartSeries(0);
125+
126+
act(() => chartApi!.highlightItems([series.points[1].name]));
127+
128+
expect(hc.getChartPoint(0, 0).state).not.toBe("inactive");
129+
expect(hc.getChartPoint(0, 1).state).toBe("hover");
130+
expect(hc.getChartPoint(0, 2).state).not.toBe("inactive");
131+
132+
act(() => chartApi!.clearChartHighlight());
133+
act(() => chartApi!.highlightItems([series.points[0].name, series.points[2].name]));
134+
135+
expect(hc.getChartPoint(0, 0).state).toBe("hover");
136+
expect(hc.getChartPoint(0, 1).state).not.toBe("hover");
137+
expect(hc.getChartPoint(0, 2).state).toBe("hover");
138+
});
84139
});

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ describe("CoreChart: rendering", () => {
3838
expect(callback).toHaveBeenCalledWith({
3939
chart: hc.getChart(),
4040
highcharts,
41+
highlightItems: expect.any(Function),
4142
setItemsVisible: expect.any(Function),
4243
highlightChartPoint: expect.any(Function),
4344
highlightChartGroup: expect.any(Function),

src/core/chart-api/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ export class ChartAPI {
208208

209209
// Callbacks used for hover and keyboard navigation, and also exposed to the public API to give the ability
210210
// to highlight and show tooltip for the given point or group manually.
211+
public highlightChartItems = (itemIds: readonly string[]) => {
212+
this.chartExtraHighlight.highlightChartItems(itemIds);
213+
};
211214
public setItemsVisible = (visibleItemsIds: readonly string[], { isApiCall }: { isApiCall: boolean }) => {
212215
this.chartExtraLegend.onItemVisibilityChange(visibleItemsIds, { isApiCall });
213216
};
@@ -228,6 +231,7 @@ export class ChartAPI {
228231
};
229232
public get publicApi() {
230233
return {
234+
highlightItems: (itemIds: readonly string[]) => this.highlightChartItems(itemIds),
231235
setItemsVisible: (visibleItemIds: readonly string[]) => this.setItemsVisible(visibleItemIds, { isApiCall: true }),
232236
highlightChartPoint: (point: Highcharts.Point) => this.highlightChartPoint(point, { isApiCall: true }),
233237
highlightChartGroup: (group: readonly Highcharts.Point[]) => this.highlightChartGroup(group, { isApiCall: true }),

src/core/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ export namespace CoreChartProps {
379379
export interface ChartAPI {
380380
chart: Highcharts.Chart;
381381
highcharts: typeof Highcharts;
382+
highlightItems(itemIds: readonly string[]): void;
382383
setItemsVisible(itemIds: readonly string[]): void;
383384
highlightChartPoint(point: Highcharts.Point): void;
384385
highlightChartGroup(group: readonly Highcharts.Point[]): void;

0 commit comments

Comments
 (0)