diff --git a/pages/03-core/core-legend-horizontal-alignment.page.tsx b/pages/03-core/core-legend-horizontal-alignment.page.tsx new file mode 100644 index 00000000..19367574 --- /dev/null +++ b/pages/03-core/core-legend-horizontal-alignment.page.tsx @@ -0,0 +1,165 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { omit } from "lodash"; + +import ColumnLayout from "@cloudscape-design/components/column-layout"; +import SpaceBetween from "@cloudscape-design/components/space-between"; + +import { CoreChartProps } from "../../lib/components/core/interfaces"; +import CoreChart from "../../lib/components/internal-do-not-use/core-chart"; +import { PageSettingsForm, useChartSettings } from "../common/page-settings"; +import { Page } from "../common/templates"; +import pseudoRandom from "../utils/pseudo-random"; + +function randomInt(min: number, max: number) { + return min + Math.floor(pseudoRandom() * (max - min)); +} + +const lineChartData = [ + { x: 1600984800000, y: 58020 }, + { x: 1600985700000, y: 102402 }, + { x: 1600986600000, y: 104920 }, + { x: 1600987500000, y: 94031 }, + { x: 1600988400000, y: 125021 }, + { x: 1600989300000, y: 159219 }, + { x: 1600990200000, y: 193082 }, + { x: 1600991100000, y: 162592 }, + { x: 1600992000000, y: 274021 }, + { x: 1600992900000, y: 264286 }, + { x: 1600993800000, y: 289210 }, + { x: 1600994700000, y: 256362 }, + { x: 1600995600000, y: 257306 }, + { x: 1600996500000, y: 186776 }, + { x: 1600997400000, y: 294020 }, + { x: 1600998300000, y: 385975 }, + { x: 1600999200000, y: 486039 }, + { x: 1601000100000, y: 490447 }, + { x: 1601001000000, y: 361845 }, + { x: 1601001900000, y: 339058 }, + { x: 1601002800000, y: 298028 }, + { x: 1601003400000, y: 255555 }, + { x: 1601003700000, y: 231902 }, + { x: 1601004600000, y: 224558 }, + { x: 1601005500000, y: 253901 }, + { x: 1601006400000, y: 102839 }, + { x: 1601007300000, y: 234943 }, + { x: 1601008200000, y: 204405 }, + { x: 1601009100000, y: 190391 }, + { x: 1601010000000, y: 183570 }, + { x: 1601010900000, y: 162592 }, + { x: 1601011800000, y: 148910 }, +]; + +export default function () { + const { chartProps } = useChartSettings({ solidgauge: true }); + + const charts: ((horizontalAlignment: CoreChartProps.LegendOptionsHorizontalAlignment) => CoreChartProps)[] = [ + (horizontalAlignment) => ({ + ...omit(chartProps.cartesian, "ref"), + legend: { + horizontalAlignment, + title: chartProps.cartesian.legend?.title, + actions: chartProps.cartesian.legend?.actions, + }, + options: { + chart: { + type: "pie", + }, + title: { + text: `${horizontalAlignment} aligned Pie`, + }, + yAxis: { + min: 0, + max: 100, + title: { + text: "Usage", + }, + }, + series: [ + { + name: "Storage Distribution", + type: "pie", + data: [ + { y: randomInt(30, 40), name: "Documents" }, + { y: randomInt(20, 30), name: "Images" }, + { y: randomInt(15, 25), name: "Videos" }, + { y: randomInt(10, 15), name: "Other" }, + ], + }, + ], + }, + }), + (horizontalAlignment) => ({ + ...omit(chartProps.cartesian, "ref"), + legend: { + horizontalAlignment, + title: chartProps.cartesian.legend?.title, + actions: chartProps.cartesian.legend?.actions, + }, + options: { + chart: { + type: "solidgauge", + }, + title: { + text: `${horizontalAlignment} aligned Gauge`, + }, + yAxis: { + min: 0, + max: 100, + }, + series: [ + { + type: "solidgauge", + name: "Download speed", + data: [randomInt(50, 100)], + showInLegend: true, + dataLabels: { + format: "{y} MB/s", + }, + }, + ], + }, + }), + (horizontalAlignment) => ({ + ...omit(chartProps.cartesian, "ref"), + legend: { + horizontalAlignment, + title: chartProps.cartesian.legend?.title, + actions: chartProps.cartesian.legend?.actions, + }, + options: { + chart: { + type: "line", + }, + title: { + text: `${horizontalAlignment} aligned Line`, + }, + series: [ + { + type: "line", + name: "Download speed", + data: lineChartData, + }, + ], + }, + }), + ]; + + return ( + } + > + + {charts.map((chart, i) => ( + + + + + ))} + + + ); +} diff --git a/pages/03-core/core-line-chart.page.tsx b/pages/03-core/core-line-chart.page.tsx index a7d8bb6d..b2864dbd 100644 --- a/pages/03-core/core-line-chart.page.tsx +++ b/pages/03-core/core-line-chart.page.tsx @@ -91,7 +91,14 @@ export default function () { subtitle="The page demonstrates the use of the core chart, including additional legend settings." settings={ } > diff --git a/pages/common/page-settings.tsx b/pages/common/page-settings.tsx index dafd6e16..9c575b92 100644 --- a/pages/common/page-settings.tsx +++ b/pages/common/page-settings.tsx @@ -15,6 +15,7 @@ import Select from "@cloudscape-design/components/select"; import SpaceBetween from "@cloudscape-design/components/space-between"; import { CartesianChartProps, PieChartProps } from "../../lib/components"; +import { CoreChartProps } from "../../src/core/interfaces"; import AppContext, { AppContextType } from "../app/app-context"; import { useHighcharts } from "./use-highcharts"; @@ -36,6 +37,7 @@ export interface PageSettings { showLegendActions: boolean; legendBottomMaxHeight?: number; legendPosition: "bottom" | "side"; + legendHorizontalAlign: CoreChartProps.LegendOptionsHorizontalAlignment; showCustomHeader: boolean; showHeaderFilter: boolean; showCustomFooter: boolean; @@ -60,6 +62,7 @@ const DEFAULT_SETTINGS: PageSettings = { showLegend: true, showLegendTitle: false, legendPosition: "bottom", + legendHorizontalAlign: "start", showLegendActions: false, showCustomHeader: false, showHeaderFilter: false, @@ -150,7 +153,8 @@ export function useChartSettings : undefined, position: settings.legendPosition, bottomMaxHeight: settings.legendBottomMaxHeight, - }; + horizontalAlignment: settings.legendHorizontalAlign, + } satisfies CoreChartProps.LegendOptions; return { settings, setSettings, @@ -388,6 +392,32 @@ export function PageSettingsForm({ } /> ); + case "legendHorizontalAlign": + return ( + + setSettings({ + legendHorizontalAlign: detail.selectedId as CoreChartProps.LegendOptionsHorizontalAlignment, + }) + } + /> + ); case "showCustomHeader": return ( ; getLegendTooltipContent?: CoreChartProps.GetLegendTooltipContent; @@ -39,6 +41,7 @@ export function ChartLegend({ ariaLabel={ariaLabel} legendTitle={title} items={legendItems} + horizontalAlignment={horizontalAlignment} actions={actions} position={position} onItemVisibilityChange={api.onItemVisibilityChange} diff --git a/src/core/interfaces.ts b/src/core/interfaces.ts index e299768e..9401bd7b 100644 --- a/src/core/interfaces.ts +++ b/src/core/interfaces.ts @@ -420,7 +420,14 @@ export namespace CoreChartProps { export interface LegendOptions extends BaseLegendOptions { bottomMaxHeight?: number; position?: "bottom" | "side"; + /** + * This property only applies when `position="bottom"`. + */ + horizontalAlignment?: LegendOptionsHorizontalAlignment; } + + export type LegendOptionsHorizontalAlignment = "start" | "center"; + export type LegendItem = InternalComponentTypes.LegendItem; export type LegendTooltipContent = InternalComponentTypes.LegendTooltipContent; export type GetLegendTooltipContent = InternalComponentTypes.GetLegendTooltipContent; @@ -488,6 +495,7 @@ export interface CoreLegendProps { ariaLabel?: string; actions?: React.ReactNode; alignment?: "horizontal" | "vertical"; + horizontalAlignment?: CoreChartProps.LegendOptionsHorizontalAlignment; onClearHighlight?: NonCancelableEventHandler; onItemHighlight?: NonCancelableEventHandler; onVisibleItemsChange?: NonCancelableEventHandler; diff --git a/src/internal-do-not-use/core-legend/index.tsx b/src/internal-do-not-use/core-legend/index.tsx index 43663abc..263856e7 100644 --- a/src/internal-do-not-use/core-legend/index.tsx +++ b/src/internal-do-not-use/core-legend/index.tsx @@ -15,6 +15,7 @@ export const CoreLegend = ({ onClearHighlight, onVisibleItemsChange, getLegendTooltipContent, + horizontalAlignment = "start", }: CoreLegendProps) => { const position = alignment === "horizontal" ? "bottom" : "side"; @@ -28,6 +29,7 @@ export const CoreLegend = ({ actions={actions} legendTitle={title} position={position} + horizontalAlignment={horizontalAlignment} ariaLabel={ariaLabel} getTooltipContent={(props) => getLegendTooltipContent?.(props) ?? null} onItemHighlightExit={() => fireNonCancelableEvent(onClearHighlight)} diff --git a/src/internal/components/chart-legend/index.tsx b/src/internal/components/chart-legend/index.tsx index d6b4e148..93cead88 100644 --- a/src/internal/components/chart-legend/index.tsx +++ b/src/internal/components/chart-legend/index.tsx @@ -16,6 +16,7 @@ import { import Box from "@cloudscape-design/components/box"; import { InternalChartTooltip } from "@cloudscape-design/components/internal/do-not-use/chart-tooltip"; +import { CoreChartProps } from "../../../core/interfaces"; import { DebouncedCall } from "../../utils/utils"; import { GetLegendTooltipContentProps, LegendItem, LegendTooltipContent } from "../interfaces"; @@ -32,6 +33,7 @@ export interface ChartLegendProps { ariaLabel?: string; actions?: React.ReactNode; position: "bottom" | "side"; + horizontalAlignment: CoreChartProps.LegendOptionsHorizontalAlignment; onItemHighlightEnter: (item: LegendItem) => void; onItemHighlightExit: () => void; onItemVisibilityChange: (hiddenItems: string[]) => void; @@ -48,6 +50,7 @@ export const ChartLegend = ({ onItemHighlightEnter, onItemHighlightExit, getTooltipContent, + horizontalAlignment, }: ChartLegendProps) => { const containerRef = useRef(null); const elementsByIndexRef = useRef>([]); @@ -246,7 +249,12 @@ export const ChartLegend = ({ onMouseLeave={() => (isMouseInContainer.current = false)} > {legendTitle && ( - + {legendTitle} )} @@ -256,6 +264,7 @@ export const ChartLegend = ({ // Setting the tab index to -1 does fix the problem. tabIndex={-1} className={clsx(styles.list, { + [styles[`list-bottom-${horizontalAlignment}`]]: position === "bottom", [styles["list-bottom"]]: position === "bottom", [styles["list-side"]]: position === "side", })} @@ -280,6 +289,7 @@ export const ChartLegend = ({ )}