Skip to content

Commit 6cee7e8

Browse files
committed
fix: Fixes legend sync for standalone legend exit
1 parent d03e827 commit 6cee7e8

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { useRef, useState } from "react";
5+
import Highcharts from "highcharts";
6+
import { omit } from "lodash";
7+
8+
import ColumnLayout from "@cloudscape-design/components/column-layout";
9+
import {
10+
colorChartsPaletteCategorical1,
11+
colorChartsPaletteCategorical2,
12+
colorChartsPaletteCategorical3,
13+
} from "@cloudscape-design/design-tokens";
14+
15+
import { ChartSeriesMarker } from "../../lib/components/internal/components/series-marker";
16+
import CoreChart from "../../lib/components/internal-do-not-use/core-chart";
17+
import { CoreLegend } from "../../lib/components/internal-do-not-use/core-legend";
18+
import { CoreChartProps } from "../../src/core/interfaces";
19+
import { LegendItem } from "../../src/internal/components/interfaces";
20+
import { dateFormatter } from "../common/formatters";
21+
import { useChartSettings } from "../common/page-settings";
22+
import { Page } from "../common/templates";
23+
import pseudoRandom from "../utils/pseudo-random";
24+
25+
function randomInt(min: number, max: number) {
26+
return min + Math.floor(pseudoRandom() * (max - min));
27+
}
28+
29+
const baseline = [
30+
{ x: 1600984800000, y: 58020 },
31+
{ x: 1600985700000, y: 102402 },
32+
{ x: 1600986600000, y: 104920 },
33+
{ x: 1600987500000, y: 94031 },
34+
{ x: 1600988400000, y: 125021 },
35+
{ x: 1600989300000, y: 159219 },
36+
{ x: 1600990200000, y: 193082 },
37+
{ x: 1600991100000, y: 162592 },
38+
{ x: 1600992000000, y: 274021 },
39+
{ x: 1600992900000, y: 264286 },
40+
];
41+
42+
const dataA = baseline.map(({ x, y }) => ({ x, y }));
43+
const dataB = baseline.map(({ x, y }) => ({ x, y: y === null ? null : y + randomInt(-100000, 100000) }));
44+
const dataC = baseline.map(({ x, y }) => ({ x, y: y === null ? null : y + randomInt(-150000, 50000) }));
45+
46+
const series = [
47+
{
48+
name: "Series A",
49+
type: "line" as const,
50+
data: dataA,
51+
color: colorChartsPaletteCategorical1,
52+
},
53+
{
54+
name: "Series B",
55+
type: "line" as const,
56+
data: dataB,
57+
color: colorChartsPaletteCategorical2,
58+
},
59+
{
60+
name: "Series C",
61+
type: "line" as const,
62+
data: dataC,
63+
color: colorChartsPaletteCategorical3,
64+
},
65+
];
66+
67+
export default function LegendEventsDemo() {
68+
const [visibleItems, setVisibleItems] = useState(new Set<string>(["Series A", "Series B", "Series C"]));
69+
const [highlightedItem, setHighlightedItem] = useState<null | string>(null);
70+
71+
const { chartProps } = useChartSettings();
72+
const chart1API = useRef<CoreChartProps.ChartAPI>(null) as React.MutableRefObject<CoreChartProps.ChartAPI>;
73+
const chart2API = useRef<CoreChartProps.ChartAPI>(null) as React.MutableRefObject<CoreChartProps.ChartAPI>;
74+
75+
const createOnItemHighlight =
76+
(referrer: "legend" | "chart1" | "chart2") =>
77+
({ detail }: { detail: { item: LegendItem } }) => {
78+
setHighlightedItem(detail.item.name);
79+
if (referrer !== "chart1") {
80+
chart1API.current.highlightItems([detail.item.name]);
81+
}
82+
if (referrer !== "chart2") {
83+
chart2API.current.highlightItems([detail.item.name]);
84+
}
85+
};
86+
const createOnClearHighlight = (referrer: "legend" | "chart1" | "chart2") => () => {
87+
setHighlightedItem(null);
88+
if (referrer !== "chart1") {
89+
chart1API.current.clearChartHighlight();
90+
}
91+
if (referrer !== "chart2") {
92+
chart2API.current.clearChartHighlight();
93+
}
94+
};
95+
96+
const legendItems: LegendItem[] = series.map((s) => ({
97+
id: s.name,
98+
name: s.name,
99+
marker: <ChartSeriesMarker color={s.color} type="line" />,
100+
visible: visibleItems.has(s.name),
101+
highlighted: highlightedItem === s.name,
102+
}));
103+
104+
return (
105+
<Page
106+
title="Legend events sync"
107+
subtitle="Demonstrates the highlight synchronization between standalone legends and charts with legends"
108+
>
109+
<ColumnLayout columns={2}>
110+
<CoreLegend
111+
items={legendItems}
112+
title="Standalone legend 1"
113+
onItemHighlight={createOnItemHighlight("legend")}
114+
onClearHighlight={createOnClearHighlight("legend")}
115+
onVisibleItemsChange={({ detail }) => setVisibleItems(new Set(detail.items))}
116+
/>
117+
118+
<CoreLegend
119+
items={legendItems}
120+
title="Standalone legend 2"
121+
onItemHighlight={createOnItemHighlight("legend")}
122+
onClearHighlight={createOnClearHighlight("legend")}
123+
onVisibleItemsChange={({ detail }) => setVisibleItems(new Set(detail.items))}
124+
/>
125+
126+
<CoreChart
127+
{...omit(chartProps.cartesian, "ref")}
128+
highcharts={Highcharts}
129+
callback={(chartApi) => (chart1API.current = chartApi)}
130+
ariaLabel="Chart 1"
131+
options={{
132+
series: series,
133+
xAxis: [{ type: "datetime", title: { text: "Time (UTC)" }, valueFormatter: dateFormatter }],
134+
yAxis: [{ title: { text: "Events" } }],
135+
}}
136+
legend={{ title: "Chart legend 1" }}
137+
onLegendItemHighlight={createOnItemHighlight("chart1")}
138+
onClearHighlight={createOnClearHighlight("chart1")}
139+
visibleItems={[...visibleItems]}
140+
onVisibleItemsChange={({ detail }) =>
141+
setVisibleItems(new Set(detail.items.filter((i) => i.visible).map((i) => i.name)))
142+
}
143+
/>
144+
145+
<CoreChart
146+
{...omit(chartProps.cartesian, "ref")}
147+
highcharts={Highcharts}
148+
callback={(chartApi) => (chart2API.current = chartApi)}
149+
ariaLabel="Chart 2"
150+
options={{
151+
series: series,
152+
xAxis: [{ type: "datetime", title: { text: "Time (UTC)" }, valueFormatter: dateFormatter }],
153+
yAxis: [{ title: { text: "Events" } }],
154+
}}
155+
legend={{ title: "Chart legend 2" }}
156+
onLegendItemHighlight={createOnItemHighlight("chart2")}
157+
onClearHighlight={createOnClearHighlight("chart2")}
158+
visibleItems={[...visibleItems]}
159+
onVisibleItemsChange={({ detail }) =>
160+
setVisibleItems(new Set(detail.items.filter((i) => i.visible).map((i) => i.name)))
161+
}
162+
/>
163+
</ColumnLayout>
164+
</Page>
165+
);
166+
}

src/core/chart-api/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ export class ChartAPI {
214214
if (!this.isTooltipPinned) {
215215
this.chartExtraHighlight.clearChartItemsHighlight();
216216
this.chartExtraLegend.onClearHighlight();
217+
218+
// Notify the consumer that a clear-highlight action was made.
219+
fireNonCancelableEvent(this.context.handlers.onClearHighlight, { isApiCall: false });
217220
}
218221
};
219222

0 commit comments

Comments
 (0)