Skip to content

Commit 978cda8

Browse files
committed
feat: set the legend horizontal alignment
1 parent 067f70d commit 978cda8

File tree

9 files changed

+300
-43
lines changed

9 files changed

+300
-43
lines changed

pages/03-core/core-line-chart.page.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,14 @@ export default function () {
9191
subtitle="The page demonstrates the use of the core chart, including additional legend settings."
9292
settings={
9393
<PageSettingsForm
94-
selectedSettings={["showLegend", "legendPosition", "showLegendTitle", "showLegendActions", "useFallback"]}
94+
selectedSettings={[
95+
"showLegend",
96+
"legendPosition",
97+
"legendHorizontalAlign",
98+
"showLegendTitle",
99+
"showLegendActions",
100+
"useFallback",
101+
]}
95102
/>
96103
}
97104
>
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import Highcharts from "highcharts";
5+
import { omit } from "lodash";
6+
7+
import CoreChart from "../../lib/components/internal-do-not-use/core-chart";
8+
import { dateFormatter, numberFormatter } from "../common/formatters";
9+
import { PageSettingsForm, useChartSettings } from "../common/page-settings";
10+
import { Page } from "../common/templates";
11+
12+
const baseline = [
13+
{ x: 1600984800000, y1: 58020, y2: 45 },
14+
{ x: 1600985700000, y1: 102402, y2: 52 },
15+
{ x: 1600986600000, y1: 104920, y2: 48 },
16+
{ x: 1600987500000, y1: 94031, y2: 55 },
17+
{ x: 1600988400000, y1: 125021, y2: 62 },
18+
{ x: 1600989300000, y1: 159219, y2: 58 },
19+
{ x: 1600990200000, y1: 193082, y2: 71 },
20+
{ x: 1600991100000, y1: 162592, y2: 65 },
21+
{ x: 1600992000000, y1: 274021, y2: 78 },
22+
{ x: 1600992900000, y1: 264286, y2: 82 },
23+
{ x: 1600993800000, y1: 289210, y2: 75 },
24+
{ x: 1600994700000, y1: 256362, y2: 68 },
25+
{ x: 1600995600000, y1: 257306, y2: 72 },
26+
{ x: 1600996500000, y1: 186776, y2: 58 },
27+
{ x: 1600997400000, y1: 294020, y2: 85 },
28+
{ x: 1600998300000, y1: 385975, y2: 92 },
29+
{ x: 1600999200000, y1: 486039, y2: 88 },
30+
{ x: 1601000100000, y1: 490447, y2: 95 },
31+
{ x: 1601001000000, y1: 361845, y2: 78 },
32+
{ x: 1601001900000, y1: 339058, y2: 72 },
33+
{ x: 1601002800000, y1: 298028, y2: 65 },
34+
{ x: 1601003400000, y1: 255555, y2: 58 },
35+
{ x: 1601003700000, y1: 231902, y2: 52 },
36+
{ x: 1601004600000, y1: 224558, y2: 48 },
37+
{ x: 1601005500000, y1: 253901, y2: 55 },
38+
{ x: 1601006400000, y1: 102839, y2: 42 },
39+
{ x: 1601007300000, y1: 234943, y2: 62 },
40+
{ x: 1601008200000, y1: 204405, y2: 58 },
41+
{ x: 1601009100000, y1: 190391, y2: 52 },
42+
{ x: 1601010000000, y1: 183570, y2: 48 },
43+
{ x: 1601010900000, y1: 162592, y2: 45 },
44+
{ x: 1601011800000, y1: 148910, y2: 38 },
45+
{ x: 1601012700000, y1: null, y2: null },
46+
{ x: 1601013600000, y1: 293910, y2: 72 },
47+
];
48+
49+
const series: Highcharts.SeriesOptionsType[] = [
50+
{
51+
name: "Network Traffic",
52+
type: "line",
53+
data: baseline.map(({ x, y1 }) => ({ x, y: y1 })),
54+
yAxis: "events",
55+
},
56+
{
57+
name: "Request Count",
58+
type: "line",
59+
data: baseline.map(({ x, y1 }) => ({ x, y: y1 === null ? null : y1 * 0.8 })),
60+
yAxis: "events",
61+
},
62+
{
63+
name: "CPU Usage",
64+
type: "line",
65+
data: baseline.map(({ x, y2 }) => ({ x, y: y2 })),
66+
yAxis: "percentage",
67+
},
68+
{
69+
name: "Memory Usage",
70+
type: "line",
71+
data: baseline.map(({ x, y2 }) => ({ x, y: y2 === null ? null : y2 * 0.9 })),
72+
yAxis: "percentage",
73+
},
74+
];
75+
76+
export default function () {
77+
const { chartProps } = useChartSettings();
78+
return (
79+
<Page
80+
title="Dual axis chart demo"
81+
subtitle="Chart with multiple legends and two y-axes."
82+
settings={
83+
<PageSettingsForm
84+
selectedSettings={[
85+
"showLegend",
86+
"legendPosition",
87+
"legendHorizontalAlign",
88+
"showLegendTitle",
89+
"showLegendActions",
90+
"useFallback",
91+
]}
92+
/>
93+
}
94+
>
95+
<CoreChart
96+
{...omit(chartProps.cartesian, "ref")}
97+
highcharts={Highcharts}
98+
options={{
99+
lang: {
100+
accessibility: {
101+
chartContainerLabel: "Dual axis line chart",
102+
},
103+
},
104+
series: series,
105+
xAxis: [
106+
{
107+
type: "datetime",
108+
title: { text: "Time (UTC)" },
109+
valueFormatter: dateFormatter,
110+
},
111+
],
112+
yAxis: [
113+
{
114+
title: { text: "Events" },
115+
id: "events",
116+
},
117+
{
118+
title: { text: "Percentage (%)" },
119+
opposite: true,
120+
id: "percentage",
121+
},
122+
],
123+
chart: {
124+
zooming: {
125+
type: "x",
126+
},
127+
},
128+
}}
129+
chartHeight={400}
130+
tooltip={{ placement: "outside" }}
131+
getTooltipContent={() => ({
132+
point({ item }) {
133+
const value = item ? (item.point.y ?? null) : null;
134+
return {
135+
value: <div>{numberFormatter(value)}</div>,
136+
};
137+
},
138+
})}
139+
/>
140+
</Page>
141+
);
142+
}

pages/03-core/pie.page.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { omit } from "lodash";
5+
6+
import CoreChart from "../../lib/components/internal-do-not-use/core-chart";
7+
import { PageSettingsForm, useChartSettings } from "../common/page-settings";
8+
import { Page } from "../common/templates";
9+
import pseudoRandom from "../utils/pseudo-random";
10+
11+
function randomInt(min: number, max: number) {
12+
return min + Math.floor(pseudoRandom() * (max - min));
13+
}
14+
15+
const series: Highcharts.SeriesOptionsType[] = [
16+
{
17+
name: "Storage Distribution",
18+
type: "pie",
19+
data: [
20+
{ y: randomInt(30, 40), name: "Documents" },
21+
{ y: randomInt(20, 30), name: "Images" },
22+
{ y: randomInt(15, 25), name: "Videos" },
23+
{ y: randomInt(10, 15), name: "Other" },
24+
],
25+
},
26+
];
27+
28+
export default function () {
29+
const { chartProps } = useChartSettings({ solidgauge: true });
30+
return (
31+
<Page
32+
title="Core Pie Chart Demo"
33+
subtitle="This page demonstrates the use of pie charts."
34+
settings={
35+
<PageSettingsForm
36+
selectedSettings={[
37+
"showLegend",
38+
"legendPosition",
39+
"legendHorizontalAlign",
40+
"showLegendTitle",
41+
"showLegendActions",
42+
"useFallback",
43+
]}
44+
/>
45+
}
46+
>
47+
<CoreChart
48+
{...omit(chartProps.cartesian, "ref")}
49+
options={{
50+
series,
51+
chart: {
52+
type: "pie",
53+
},
54+
title: {
55+
text: "Storage Distribution by Type",
56+
},
57+
yAxis: {
58+
min: 0,
59+
max: 100,
60+
title: {
61+
text: "Usage",
62+
},
63+
},
64+
}}
65+
/>
66+
</Page>
67+
);
68+
}

pages/common/page-settings.tsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import Select from "@cloudscape-design/components/select";
1515
import SpaceBetween from "@cloudscape-design/components/space-between";
1616

1717
import { CartesianChartProps, PieChartProps } from "../../lib/components";
18+
import { CoreChartProps } from "../../src/core/interfaces";
1819
import AppContext, { AppContextType } from "../app/app-context";
1920
import { useHighcharts } from "./use-highcharts";
2021

@@ -36,6 +37,7 @@ export interface PageSettings {
3637
showLegendActions: boolean;
3738
legendBottomMaxHeight?: number;
3839
legendPosition: "bottom" | "side";
40+
legendHorizontalAlign: CoreChartProps.LegendOptionsHorizontalAlignment;
3941
showCustomHeader: boolean;
4042
showHeaderFilter: boolean;
4143
showCustomFooter: boolean;
@@ -60,6 +62,7 @@ const DEFAULT_SETTINGS: PageSettings = {
6062
showLegend: true,
6163
showLegendTitle: false,
6264
legendPosition: "bottom",
65+
legendHorizontalAlign: "start",
6366
showLegendActions: false,
6467
showCustomHeader: false,
6568
showHeaderFilter: false,
@@ -149,7 +152,8 @@ export function useChartSettings<SettingsType extends PageSettings = PageSetting
149152
actions: settings.showLegendActions ? <Button variant="icon" iconName="search" /> : undefined,
150153
position: settings.legendPosition,
151154
bottomMaxHeight: settings.legendBottomMaxHeight,
152-
};
155+
horizontalAlignment: settings.legendHorizontalAlign,
156+
} satisfies CoreChartProps.LegendOptions;
153157
return {
154158
settings,
155159
setSettings,
@@ -387,6 +391,32 @@ export function PageSettingsForm({
387391
}
388392
/>
389393
);
394+
case "legendHorizontalAlign":
395+
return (
396+
<SegmentedControl
397+
label="Legend Horizontal Align"
398+
selectedId={
399+
settings.showLegend && settings.legendPosition === "bottom" ? settings.legendHorizontalAlign : null
400+
}
401+
options={[
402+
{
403+
text: "Start",
404+
id: "start",
405+
disabled: !settings.showLegend || settings.legendPosition !== "bottom",
406+
},
407+
{
408+
text: "Center",
409+
id: "center",
410+
disabled: !settings.showLegend || settings.legendPosition !== "bottom",
411+
},
412+
]}
413+
onChange={({ detail }) =>
414+
setSettings({
415+
legendHorizontalAlign: detail.selectedId as CoreChartProps.LegendOptionsHorizontalAlignment,
416+
})
417+
}
418+
/>
419+
);
390420
case "showCustomHeader":
391421
return (
392422
<Checkbox

src/__tests__/__snapshots__/documenter.test.ts.snap

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,43 +1573,12 @@ Supported Highcharts versions: 12.",
15731573
"i18nTag": undefined,
15741574
"inlineType": {
15751575
"name": "CoreChartProps.LegendOptions",
1576-
"properties": [
1577-
{
1578-
"name": "actions",
1579-
"optional": true,
1580-
"type": "React.ReactNode",
1581-
},
1582-
{
1583-
"name": "bottomMaxHeight",
1584-
"optional": true,
1585-
"type": "number",
1586-
},
1587-
{
1588-
"name": "enabled",
1589-
"optional": true,
1590-
"type": "boolean",
1591-
},
1592-
{
1593-
"inlineType": {
1594-
"name": ""side" | "bottom"",
1595-
"type": "union",
1596-
"valueDescriptions": undefined,
1597-
"values": [
1598-
"side",
1599-
"bottom",
1600-
],
1601-
},
1602-
"name": "position",
1603-
"optional": true,
1604-
"type": "string",
1605-
},
1606-
{
1607-
"name": "title",
1608-
"optional": true,
1609-
"type": "string",
1610-
},
1576+
"type": "union",
1577+
"valueDescriptions": undefined,
1578+
"values": [
1579+
"BaseLegendOptions & { bottomMaxHeight?: number | undefined; } & CoreChartProps.LegendOptionsBottom",
1580+
"BaseLegendOptions & { bottomMaxHeight?: number | undefined; } & CoreChartProps.LegendOptionsSide",
16111581
],
1612-
"type": "object",
16131582
},
16141583
"name": "legend",
16151584
"optional": true,

src/core/components/core-legend.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ export function ChartLegend({
1717
i18nStrings,
1818
onItemHighlight,
1919
getLegendTooltipContent,
20+
horizontalAlignment,
2021
}: {
2122
api: ChartAPI;
2223
title?: string;
2324
actions?: React.ReactNode;
2425
position: "bottom" | "side";
26+
horizontalAlignment?: CoreChartProps.LegendOptionsHorizontalAlignment;
2527
i18nStrings?: BaseI18nStrings;
2628
onItemHighlight?: NonCancelableEventHandler<CoreChartProps.LegendItemHighlightDetail>;
2729
getLegendTooltipContent?: CoreChartProps.GetLegendTooltipContent;
@@ -39,6 +41,7 @@ export function ChartLegend({
3941
ariaLabel={ariaLabel}
4042
legendTitle={title}
4143
items={legendItems}
44+
horizontalAlignment={horizontalAlignment}
4245
actions={actions}
4346
position={position}
4447
onItemVisibilityChange={api.onItemVisibilityChange}

src/core/interfaces.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,10 +411,24 @@ export namespace CoreChartProps {
411411
content: React.ReactNode;
412412
}
413413

414-
export interface LegendOptions extends BaseLegendOptions {
414+
export type LegendOptions = BaseLegendOptions & {
415415
bottomMaxHeight?: number;
416-
position?: "bottom" | "side";
416+
} & (LegendOptionsBottom | LegendOptionsSide);
417+
418+
export interface LegendOptionsSide {
419+
position?: "side";
420+
}
421+
export interface LegendOptionsBottom {
422+
position: "bottom";
423+
/**
424+
* When {@link position} is set to "bottom", horizontalAlignment sets the legend horizontal alignment.
425+
*/
426+
horizontalAlignment?: LegendOptionsHorizontalAlignment;
417427
}
428+
429+
export const LegendOptionsAlignments = ["start", "center"] as const;
430+
export type LegendOptionsHorizontalAlignment = (typeof LegendOptionsAlignments)[number];
431+
418432
export type LegendItem = InternalComponentTypes.LegendItem;
419433
export type LegendTooltipContent = InternalComponentTypes.LegendTooltipContent;
420434
export type GetLegendTooltipContent = InternalComponentTypes.GetLegendTooltipContent;

0 commit comments

Comments
 (0)