diff --git a/packages/polaris-viz/CHANGELOG.md b/packages/polaris-viz/CHANGELOG.md
index 0e35e6df7a..c158d8629b 100644
--- a/packages/polaris-viz/CHANGELOG.md
+++ b/packages/polaris-viz/CHANGELOG.md
@@ -5,7 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
-
+## Unreleased
+
+### Added
+
+- Added support for showing values and trends in `` legend with new prop `showLegendValues`.
## [10.1.0] - 2023-12-04
diff --git a/packages/polaris-viz/src/components/DonutChart/Chart.tsx b/packages/polaris-viz/src/components/DonutChart/Chart.tsx
index 921200d0cd..3ca89399d8 100644
--- a/packages/polaris-viz/src/components/DonutChart/Chart.tsx
+++ b/packages/polaris-viz/src/components/DonutChart/Chart.tsx
@@ -1,4 +1,4 @@
-import {Fragment, useState} from 'react';
+import {Fragment, useMemo, useState} from 'react';
import {pie} from 'd3-shape';
import {
clamp,
@@ -16,6 +16,7 @@ import type {
Direction,
} from '@shopify/polaris-viz-core';
+import {getTrendIndicatorData} from '../../utilities/getTrendIndicatorData';
import {getContainerAlignmentForLegend} from '../../utilities';
import {estimateLegendItemWidth} from '../Legend';
import type {ComparisonMetricProps} from '../ComparisonMetric';
@@ -27,6 +28,7 @@ import {
} from '../../hooks';
import {Arc} from '../Arc';
import type {
+ ColorVisionInteractionMethods,
LegendPosition,
RenderInnerValueContent,
RenderLegendContent,
@@ -34,7 +36,7 @@ import type {
import {ChartSkeleton} from '../../components/ChartSkeleton';
import styles from './DonutChart.scss';
-import {InnerValue} from './components';
+import {InnerValue, LegendValues} from './components';
const ERROR_ANIMATION_PADDING = 40;
const FULL_CIRCLE = Math.PI * 2;
@@ -46,6 +48,7 @@ export interface ChartProps {
labelFormatter: LabelFormatter;
legendPosition: LegendPosition;
showLegend: boolean;
+ showLegendValues: boolean;
state: ChartState;
theme: string;
accessibilityLabel?: string;
@@ -63,6 +66,7 @@ export function Chart({
labelFormatter,
legendPosition = 'right',
showLegend,
+ showLegendValues,
state,
theme,
accessibilityLabel = '',
@@ -92,22 +96,34 @@ export function Chart({
? 'vertical'
: 'horizontal';
- const longestLegendWidth = data.reduce((previous, current) => {
- const estimatedLegendWidth = estimateLegendItemWidth(
- current.name ?? '',
- characterWidths,
- );
-
- if (estimatedLegendWidth > previous) {
- return estimatedLegendWidth;
+ const maxTrendIndicatorWidth = data.reduce((maxWidth, {metadata}) => {
+ if (!metadata?.trend) {
+ return maxWidth;
}
- return previous;
+ const {trendIndicatorWidth} = getTrendIndicatorData(metadata.trend);
+
+ return Math.max(maxWidth, trendIndicatorWidth);
}, 0);
+ const longestLegendWidth = useMemo(() => {
+ return data.reduce((previous, current) => {
+ const estimatedLegendWidth = estimateLegendItemWidth(
+ `${current.name ?? ''} ${current.data[0].value} `,
+ characterWidths,
+ );
+
+ if (estimatedLegendWidth > previous) {
+ return estimatedLegendWidth;
+ }
+
+ return previous;
+ }, 0);
+ }, [characterWidths, data]);
+
const maxLegendWidth =
legendDirection === 'vertical'
- ? Math.min(
+ ? Math.max(
longestLegendWidth,
dimensions.width * MAX_LEGEND_WIDTH_PERCENTAGE,
)
@@ -167,6 +183,26 @@ export function Chart({
const containerAlignmentStyle =
getContainerAlignmentForLegend(legendPosition);
+ const renderLegendContentWithValues = ({
+ getColorVisionStyles,
+ getColorVisionEventAttrs,
+ }: ColorVisionInteractionMethods) => {
+ return (
+
+ );
+ };
+
+ const shouldRenderLegendContentWithValues =
+ !renderLegendContent &&
+ showLegendValues &&
+ (legendPosition === 'right' || legendPosition === 'left');
+
return (
@@ -258,7 +294,11 @@ export function Chart({
direction={legendDirection}
position={legendPosition}
maxWidth={maxLegendWidth}
- renderLegendContent={renderLegendContent}
+ renderLegendContent={
+ shouldRenderLegendContentWithValues
+ ? renderLegendContentWithValues
+ : renderLegendContent
+ }
/>
)}
diff --git a/packages/polaris-viz/src/components/DonutChart/DonutChart.tsx b/packages/polaris-viz/src/components/DonutChart/DonutChart.tsx
index 1ffd7124ef..c71206dede 100644
--- a/packages/polaris-viz/src/components/DonutChart/DonutChart.tsx
+++ b/packages/polaris-viz/src/components/DonutChart/DonutChart.tsx
@@ -13,10 +13,13 @@ import type {
} from '../../types';
import {Chart} from './Chart';
+import type {DonutChartDataSeries} from './types';
export type DonutChartProps = {
+ data: DonutChartDataSeries[];
comparisonMetric?: ComparisonMetricProps;
showLegend?: boolean;
+ showLegendValues?: boolean;
labelFormatter?: LabelFormatter;
legendFullWidth?: boolean;
legendPosition?: LegendPosition;
@@ -32,6 +35,7 @@ export function DonutChart(props: DonutChartProps) {
theme = defaultTheme,
comparisonMetric,
showLegend = true,
+ showLegendValues = false,
labelFormatter = (value) => `${value}`,
legendFullWidth,
legendPosition = 'left',
@@ -61,6 +65,7 @@ export function DonutChart(props: DonutChartProps) {
labelFormatter={labelFormatter}
comparisonMetric={comparisonMetric}
showLegend={showLegend}
+ showLegendValues={showLegendValues}
legendFullWidth={legendFullWidth}
legendPosition={legendPosition}
renderInnerValueContent={renderInnerValueContent}
diff --git a/packages/polaris-viz/src/components/DonutChart/components/InnerValue/InnerValue.tsx b/packages/polaris-viz/src/components/DonutChart/components/InnerValue/InnerValue.tsx
index ac213a57eb..08a8c8eb96 100644
--- a/packages/polaris-viz/src/components/DonutChart/components/InnerValue/InnerValue.tsx
+++ b/packages/polaris-viz/src/components/DonutChart/components/InnerValue/InnerValue.tsx
@@ -50,7 +50,9 @@ export function InnerValue({
);
const activeValueExists = activeValue !== null && activeValue !== undefined;
- const valueToDisplay = activeValueExists ? activeValue : animatedTotalValue;
+ const valueToDisplay = activeValueExists
+ ? labelFormatter(activeValue)
+ : animatedTotalValue;
const innerContent = renderInnerValueContent?.({
activeValue,
diff --git a/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.scss b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.scss
new file mode 100644
index 0000000000..7a300ca787
--- /dev/null
+++ b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.scss
@@ -0,0 +1,14 @@
+.Table {
+ min-width: 200px;
+ width: 100%;
+ border-collapse: separate;
+ border-spacing: 0 10px;
+
+ .alignLeft {
+ text-align: left;
+ }
+
+ .alignRight {
+ text-align: right;
+ }
+}
diff --git a/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.tsx b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.tsx
new file mode 100644
index 0000000000..dc4fc58822
--- /dev/null
+++ b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.tsx
@@ -0,0 +1,86 @@
+import type {ColorVisionInteractionMethods, DataSeries} from 'index';
+import type {LabelFormatter} from '@shopify/polaris-viz-core';
+import {useTheme} from '@shopify/polaris-viz-core';
+
+import {TrendIndicator} from '../../../TrendIndicator';
+import {SquareColorPreview} from '../../../../components/SquareColorPreview';
+
+import styles from './LegendValues.scss';
+
+interface LegendContentProps {
+ data: DataSeries[];
+ maxTrendIndicatorWidth: number;
+ labelFormatter: LabelFormatter;
+ getColorVisionStyles: ColorVisionInteractionMethods['getColorVisionStyles'];
+ getColorVisionEventAttrs: ColorVisionInteractionMethods['getColorVisionEventAttrs'];
+}
+
+export function LegendValues({
+ data,
+ maxTrendIndicatorWidth,
+ labelFormatter,
+ getColorVisionStyles,
+ getColorVisionEventAttrs,
+}: LegendContentProps) {
+ const selectedTheme = useTheme();
+
+ return (
+
+ {data.map(({name, data, metadata}, index) => {
+ const value = data[0].value || 0;
+
+ return (
+
+
+
+ |
+
+
+
+ {name}
+
+ |
+
+ |
+
+
+
+ {labelFormatter(value)}
+
+ |
+
+
+
+ {metadata?.trend && }
+
+ |
+
+ );
+ })}
+
+ );
+}
diff --git a/packages/polaris-viz/src/components/DonutChart/components/LegendValues/index.ts b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/index.ts
new file mode 100644
index 0000000000..77c93d52c2
--- /dev/null
+++ b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/index.ts
@@ -0,0 +1 @@
+export {LegendValues} from './LegendValues';
diff --git a/packages/polaris-viz/src/components/DonutChart/components/index.ts b/packages/polaris-viz/src/components/DonutChart/components/index.ts
index f966c32ddb..b28a64f07b 100644
--- a/packages/polaris-viz/src/components/DonutChart/components/index.ts
+++ b/packages/polaris-viz/src/components/DonutChart/components/index.ts
@@ -1 +1,2 @@
export {InnerValue} from './InnerValue';
+export {LegendValues} from './LegendValues';
diff --git a/packages/polaris-viz/src/components/DonutChart/stories/WithLegendValues.stories.tsx b/packages/polaris-viz/src/components/DonutChart/stories/WithLegendValues.stories.tsx
new file mode 100644
index 0000000000..2ca5b082ae
--- /dev/null
+++ b/packages/polaris-viz/src/components/DonutChart/stories/WithLegendValues.stories.tsx
@@ -0,0 +1,60 @@
+import type {Story} from '@storybook/react';
+
+export {META as default} from './meta';
+
+import type {DonutChartProps} from '../../DonutChart';
+
+import {DEFAULT_PROPS, Template} from './data';
+
+export const WithLegendValues: Story
= Template.bind({});
+
+WithLegendValues.args = {
+ ...DEFAULT_PROPS,
+ showLegend: true,
+ showLegendValues: true,
+ legendPosition: 'right',
+ labelFormatter: (value) => `$${value}`,
+ data: [
+ {
+ name: 'Shopify Payments',
+ data: [{key: 'april - march', value: 50000}],
+ metadata: {
+ trend: {
+ value: '5%',
+ },
+ },
+ },
+ {
+ name: 'Paypal',
+ data: [{key: 'april - march', value: 25000}],
+ metadata: {
+ trend: {
+ value: '50%',
+ direction: 'downward',
+ trend: 'negative',
+ },
+ },
+ },
+ {
+ name: 'Other',
+ data: [{key: 'april - march', value: 10000}],
+ metadata: {
+ trend: {
+ value: '100%',
+ direction: 'upward',
+ trend: 'positive',
+ },
+ },
+ },
+ {
+ name: 'Amazon Pay',
+ data: [{key: 'april - march', value: 5000}],
+ metadata: {
+ trend: {
+ direction: 'upward',
+ trend: 'positive',
+ },
+ },
+ },
+ ],
+};
diff --git a/packages/polaris-viz/src/components/DonutChart/stories/meta.tsx b/packages/polaris-viz/src/components/DonutChart/stories/meta.tsx
index 5336ad1a8d..48177204c9 100644
--- a/packages/polaris-viz/src/components/DonutChart/stories/meta.tsx
+++ b/packages/polaris-viz/src/components/DonutChart/stories/meta.tsx
@@ -7,6 +7,8 @@ import {
LEGEND_FULL_WIDTH_ARGS,
LEGEND_POSITION_ARGS,
RENDER_LEGEND_CONTENT_ARGS,
+ SHOW_LEGEND_ARGS,
+ SHOW_LEGEND_VALUES_ARGS,
THEME_CONTROL_ARGS,
} from '../../../storybook/constants';
import type {DonutChartProps} from '../DonutChart';
@@ -29,6 +31,8 @@ export const META: Meta = {
data: DATA_SERIES_ARGS,
legendFullWidth: LEGEND_FULL_WIDTH_ARGS,
legendPosition: LEGEND_POSITION_ARGS,
+ showLegend: SHOW_LEGEND_ARGS,
+ showLegendValues: SHOW_LEGEND_VALUES_ARGS,
renderLegendContent: RENDER_LEGEND_CONTENT_ARGS,
theme: THEME_CONTROL_ARGS,
state: CHART_STATE_CONTROL_ARGS,
diff --git a/packages/polaris-viz/src/components/DonutChart/types.ts b/packages/polaris-viz/src/components/DonutChart/types.ts
new file mode 100644
index 0000000000..f87dea5c8e
--- /dev/null
+++ b/packages/polaris-viz/src/components/DonutChart/types.ts
@@ -0,0 +1,13 @@
+import type {DataSeries} from '@shopify/polaris-viz-core/src/types';
+
+import type {TrendIndicatorProps} from '../TrendIndicator';
+
+export type MetaDataTrendIndicator = Omit;
+
+export interface MetaData {
+ trend?: MetaDataTrendIndicator;
+}
+
+export interface DonutChartDataSeries extends DataSeries {
+ metadata?: MetaData;
+}
diff --git a/packages/polaris-viz/src/components/index.ts b/packages/polaris-viz/src/components/index.ts
index 1399bb30f6..8529e49904 100644
--- a/packages/polaris-viz/src/components/index.ts
+++ b/packages/polaris-viz/src/components/index.ts
@@ -47,5 +47,5 @@ export {ChartSkeleton} from './ChartSkeleton';
export {ComboChart} from './ComboChart';
export type {ComboChartProps} from './ComboChart';
export {XAxis} from './XAxis';
-export {TrendIndicator} from './TrendIndicator';
+export {TrendIndicator, estimateTrendIndicatorWidth} from './TrendIndicator';
export {LineChartRelational} from './LineChartRelational';
diff --git a/packages/polaris-viz/src/components/shared/HorizontalBars/HorizontalBars.tsx b/packages/polaris-viz/src/components/shared/HorizontalBars/HorizontalBars.tsx
index bba739a111..dc7fd479d0 100644
--- a/packages/polaris-viz/src/components/shared/HorizontalBars/HorizontalBars.tsx
+++ b/packages/polaris-viz/src/components/shared/HorizontalBars/HorizontalBars.tsx
@@ -9,6 +9,7 @@ import {
clamp,
} from '@shopify/polaris-viz-core';
+import {getTrendIndicatorData} from '../../../utilities/getTrendIndicatorData';
import {TREND_INDICATOR_HEIGHT, TrendIndicator} from '../../TrendIndicator';
import {getHoverZoneOffset} from '../../../utilities';
import {
@@ -21,7 +22,6 @@ import {getGradientDefId} from '../GradientDefs';
import {Label, Bar, LabelWrapper} from './components';
import styles from './HorizontalBars.scss';
-import {getTrendIndicatorData} from './utilities/getTrendIndicatorData';
const SERIES_DELAY = 150;
diff --git a/packages/polaris-viz/src/storybook/constants.ts b/packages/polaris-viz/src/storybook/constants.ts
index ac4d47d215..87ec0d2602 100644
--- a/packages/polaris-viz/src/storybook/constants.ts
+++ b/packages/polaris-viz/src/storybook/constants.ts
@@ -42,6 +42,21 @@ export const LEGEND_FULL_WIDTH_ARGS = {
},
};
+export const SHOW_LEGEND_ARGS = {
+ description: 'Whether to show the legend or not.',
+ control: {
+ type: 'boolean',
+ },
+};
+
+export const SHOW_LEGEND_VALUES_ARGS = {
+ description:
+ 'Whether to show the values in the legend or not. If `showLegend` is false, or `legendPosition` is not `left`/`right`, this prop will have no effect.',
+ control: {
+ type: 'boolean',
+ },
+};
+
export const RENDER_LEGEND_CONTENT_ARGS = {
description:
'This accepts a function that is called to render the legend content instead of the given legend. If `showLegend` is false, this prop will have no effect.',
diff --git a/packages/polaris-viz/src/types.ts b/packages/polaris-viz/src/types.ts
index dcdb3ec086..313f3724b4 100644
--- a/packages/polaris-viz/src/types.ts
+++ b/packages/polaris-viz/src/types.ts
@@ -8,6 +8,7 @@ import type {
} from '@shopify/polaris-viz-core';
import type {Series, SeriesPoint} from 'd3-shape';
import type {ScaleLinear} from 'd3-scale';
+import type {TrendIndicatorProps} from 'components/TrendIndicator';
export interface YAxisTick {
value: number;
@@ -194,6 +195,8 @@ export type LegendPosition =
| 'bottom'
| 'left';
+export type MetaDataTrendIndicator = Omit;
+
export interface ColorVisionInteractionMethods {
getColorVisionEventAttrs: (
index: number,
diff --git a/packages/polaris-viz/src/components/shared/HorizontalBars/utilities/getTrendIndicatorData.ts b/packages/polaris-viz/src/utilities/getTrendIndicatorData.ts
similarity index 58%
rename from packages/polaris-viz/src/components/shared/HorizontalBars/utilities/getTrendIndicatorData.ts
rename to packages/polaris-viz/src/utilities/getTrendIndicatorData.ts
index 80164b9457..105126fa14 100644
--- a/packages/polaris-viz/src/components/shared/HorizontalBars/utilities/getTrendIndicatorData.ts
+++ b/packages/polaris-viz/src/utilities/getTrendIndicatorData.ts
@@ -1,11 +1,14 @@
-import type {MetaDataTrendIndicator} from '../../../SimpleBarChart';
-import {estimateTrendIndicatorWidth} from '../../../TrendIndicator';
+import type {MetaDataTrendIndicator} from 'types';
+
+import {estimateTrendIndicatorWidth} from '../components';
export function getTrendIndicatorData(
trendMetadata: MetaDataTrendIndicator | undefined,
) {
if (trendMetadata != null) {
- const {totalWidth} = estimateTrendIndicatorWidth(`${trendMetadata.value}`);
+ const {totalWidth} = estimateTrendIndicatorWidth(
+ `${trendMetadata.value || ''}`,
+ );
return {
trendIndicatorProps: trendMetadata,