diff --git a/packages/polaris-viz/CHANGELOG.md b/packages/polaris-viz/CHANGELOG.md index 4ce2a1c94..91e7aeaaf 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 + +### Changed + +- Limit `` legends from taking up more than 50% of the chart width. ## [12.2.2] - 2024-04-01 diff --git a/packages/polaris-viz/src/components/DonutChart/Chart.tsx b/packages/polaris-viz/src/components/DonutChart/Chart.tsx index 8936f62d5..71ae2731d 100644 --- a/packages/polaris-viz/src/components/DonutChart/Chart.tsx +++ b/packages/polaris-viz/src/components/DonutChart/Chart.tsx @@ -7,6 +7,7 @@ import { useUniqueId, ChartState, useChartContext, + estimateStringWidth, } from '@shopify/polaris-viz-core'; import type { DataPoint, @@ -17,7 +18,6 @@ import type { } from '@shopify/polaris-viz-core'; import {getContainerAlignmentForLegend} from '../../utilities'; -import {estimateLegendItemWidth} from '../Legend'; import type {ComparisonMetricProps} from '../ComparisonMetric'; import {LegendContainer, useLegend} from '../../components/LegendContainer'; import { @@ -40,7 +40,6 @@ import {InnerValue, LegendValues} from './components'; const ERROR_ANIMATION_PADDING = 40; const FULL_CIRCLE = Math.PI * 2; -const MAX_LEGEND_WIDTH_PERCENTAGE = 0.35; const RADIUS_PADDING = 20; export interface ChartProps { @@ -98,28 +97,8 @@ export function Chart({ ? 'vertical' : 'horizontal'; - const longestLegendWidth = data.reduce((previous, current) => { - const estimatedLegendWidth = estimateLegendItemWidth( - showLegendValues === true - ? `${current.name ?? ''} ${current.data[0].value}` - : `${current.name ?? ''}`, - characterWidths, - ); - - if (estimatedLegendWidth > previous) { - return estimatedLegendWidth; - } - - return previous; - }, 0); - const maxLegendWidth = - legendDirection === 'vertical' - ? Math.max( - longestLegendWidth, - dimensions.width * MAX_LEGEND_WIDTH_PERCENTAGE, - ) - : 0; + legendDirection === 'vertical' ? dimensions.width / 2 : 0; const {height, width, legend, setLegendDimensions, isLegendMounted} = useLegend({ @@ -131,6 +110,19 @@ export function Chart({ maxWidth: maxLegendWidth, }); + const longestLegendValueWidth = legend.reduce((previous, current) => { + const estimatedLegendWidth = estimateStringWidth( + `${labelFormatter(`${current.value || ''}`)}`, + characterWidths, + ); + + if (estimatedLegendWidth > previous) { + return estimatedLegendWidth; + } + + return previous; + }, 0); + const shouldUseColorVisionEvents = Boolean( width && height && isLegendMounted, ); @@ -147,7 +139,10 @@ export function Chart({ }, }); - if (!width || !height) return null; + if (!width || !height) { + return null; + } + const diameter = Math.min(height, width); const radius = diameter / 2; @@ -187,6 +182,7 @@ export function Chart({ data={data} activeIndex={activeIndex} labelFormatter={labelFormatter} + longestLegendValueWidth={longestLegendValueWidth} getColorVisionStyles={getColorVisionStyles} getColorVisionEventAttrs={getColorVisionEventAttrs} dimensions={{...dimensions, x: 0, y: 0}} diff --git a/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.scss b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.scss index 1768cf3c1..b435b0c60 100644 --- a/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.scss +++ b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.scss @@ -2,4 +2,5 @@ width: 100%; border-collapse: separate; border-spacing: 0 10px; + table-layout: fixed; } diff --git a/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.tsx b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.tsx index 61f1fa0a8..c5c9a9ceb 100644 --- a/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.tsx +++ b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/LegendValues.tsx @@ -24,6 +24,7 @@ interface LegendContentProps { activeIndex: number; dimensions: BoundingRect; labelFormatter: LabelFormatter; + longestLegendValueWidth: number; renderHiddenLegendLabel?: RenderHiddenLegendLabel; getColorVisionStyles: ColorVisionInteractionMethods['getColorVisionStyles']; getColorVisionEventAttrs: ColorVisionInteractionMethods['getColorVisionEventAttrs']; @@ -33,6 +34,7 @@ export function LegendValues({ data: allData, activeIndex, labelFormatter, + longestLegendValueWidth, renderHiddenLegendLabel = (count) => `+${count} more`, getColorVisionStyles, getColorVisionEventAttrs, @@ -90,6 +92,7 @@ export function LegendValues({ value={value} trend={trend} index={index} + longestLegendValueWidth={longestLegendValueWidth} maxTrendIndicatorWidth={maxTrendIndicatorWidth} seriesColors={seriesColors} onDimensionChange={(dimensions) => { diff --git a/packages/polaris-viz/src/components/DonutChart/components/LegendValues/components/LegendValueItem/LegendValueItem.scss b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/components/LegendValueItem/LegendValueItem.scss index 2b4a4aa20..3788ae1d7 100644 --- a/packages/polaris-viz/src/components/DonutChart/components/LegendValues/components/LegendValueItem/LegendValueItem.scss +++ b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/components/LegendValueItem/LegendValueItem.scss @@ -4,13 +4,10 @@ .Name { overflow: hidden; - text-wrap: nowrap; + white-space: nowrap; text-overflow: ellipsis; padding-left: 4px; -} - -.TableSpacer { - padding-left: 30px; + padding-right: 20px; } .alignLeft { diff --git a/packages/polaris-viz/src/components/DonutChart/components/LegendValues/components/LegendValueItem/LegendValueItem.tsx b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/components/LegendValueItem/LegendValueItem.tsx index bb7420034..102b68767 100644 --- a/packages/polaris-viz/src/components/DonutChart/components/LegendValues/components/LegendValueItem/LegendValueItem.tsx +++ b/packages/polaris-viz/src/components/DonutChart/components/LegendValues/components/LegendValueItem/LegendValueItem.tsx @@ -21,6 +21,7 @@ interface Props { value?: string; trend?: MetaDataTrendIndicator; labelFormatter: LabelFormatter; + longestLegendValueWidth: number; seriesColors: Color[]; maxTrendIndicatorWidth: number; onDimensionChange: (dimensions: Dimensions) => void; @@ -33,6 +34,7 @@ export function LegendValueItem({ value, index, labelFormatter, + longestLegendValueWidth, trend, seriesColors, maxTrendIndicatorWidth, @@ -73,13 +75,12 @@ export function LegendValueItem({ style={{ color: selectedTheme.legend.labelColor, }} + title={name} > {name} - - - + - - {trend && valueExists && } - + {trend && valueExists && ( + + + + + + )} ); } diff --git a/packages/polaris-viz/src/components/DonutChart/stories/DonutChart.chromatic.stories.tsx b/packages/polaris-viz/src/components/DonutChart/stories/DonutChart.chromatic.stories.tsx new file mode 100644 index 000000000..eb6cf30f3 --- /dev/null +++ b/packages/polaris-viz/src/components/DonutChart/stories/DonutChart.chromatic.stories.tsx @@ -0,0 +1,112 @@ +import {storiesOf} from '@storybook/react'; +import type {PropCombinations} from '../../../chromatic/types'; + +import { + addWithPropsCombinations, + renderCombinationSections, +} from '../../../chromatic'; +import {DonutChart} from '../'; +import type {DonutChartProps} from '../DonutChart'; +import {DEFAULT_DATA} from './data'; + +const stories = storiesOf('Chromatic/Components', module).addParameters({ + docs: {page: null}, + chromatic: {disableSnapshot: false}, +}); + +const DEFAULT_PROPS: PropCombinations = { + data: [DEFAULT_DATA], + comparisonMetric: [ + { + metric: '6%', + trend: 'positive', + accessibilityLabel: 'trending up 6%', + }, + ], + legendPosition: ['left'], + isAnimated: [false], +}; + +const CHART_SIZE = {style: {width: 500, height: 300}}; + +const combinations = renderCombinationSections([ + [ + 'Data', + addWithPropsCombinations( + DonutChart, + { + ...DEFAULT_PROPS, + }, + CHART_SIZE, + ), + ], + [ + 'Legend Position', + addWithPropsCombinations( + DonutChart, + { + ...DEFAULT_PROPS, + legendPosition: [ + 'top-left', + 'top-right', + 'bottom-left', + 'bottom-right', + 'top', + 'right', + 'bottom', + 'left', + ], + }, + CHART_SIZE, + ), + ], + [ + 'Truncated Legends', + addWithPropsCombinations( + DonutChart, + { + ...DEFAULT_PROPS, + data: [ + [ + { + name: 'This is a long name that will get truncated', + data: [{key: 'april - march', value: 50000}], + metadata: { + trend: { + value: '5%', + }, + }, + }, + { + name: 'This is another long name that will get truncated', + data: [{key: 'april - march', value: 250000}], + metadata: { + trend: { + value: '50%', + direction: 'downward', + trend: 'negative', + }, + }, + }, + { + name: 'This is the last long name that will get truncated', + data: [{key: 'april - march', value: 10000}], + metadata: { + trend: { + value: '100%', + direction: 'upward', + trend: 'positive', + }, + }, + }, + ], + ], + legendPosition: ['left', 'right'], + showLegendValues: [true, false], + }, + CHART_SIZE, + ), + ], +]); + +stories.add('DonutChart', combinations); diff --git a/packages/polaris-viz/src/components/DonutChart/stories/TruncatedLegends.stories.tsx b/packages/polaris-viz/src/components/DonutChart/stories/TruncatedLegends.stories.tsx new file mode 100644 index 000000000..a6d6390e6 --- /dev/null +++ b/packages/polaris-viz/src/components/DonutChart/stories/TruncatedLegends.stories.tsx @@ -0,0 +1,51 @@ +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 TruncatedLegends: Story = Template.bind({}); + +TruncatedLegends.args = { + ...DEFAULT_PROPS, + showLegendValues: true, + legendPosition: 'right', + legendFullWidth: false, + labelFormatter: (value) => `$${value}`, + isAnimated: false, + data: [ + { + name: 'This is a long name that will get truncated', + data: [{key: 'april - march', value: 50000}], + metadata: { + trend: { + value: '5%', + }, + }, + }, + { + name: 'This is another long name that will get truncated', + data: [{key: 'april - march', value: 250000}], + metadata: { + trend: { + value: '50%', + direction: 'downward', + trend: 'negative', + }, + }, + }, + { + name: 'This is the last long name that will get truncated', + data: [{key: 'april - march', value: 10000}], + metadata: { + trend: { + value: '100%', + direction: 'upward', + trend: 'positive', + }, + }, + }, + ], +}; diff --git a/packages/polaris-viz/src/components/Legend/components/LegendItem/LegendItem.tsx b/packages/polaris-viz/src/components/Legend/components/LegendItem/LegendItem.tsx index f42b282f1..df1766737 100644 --- a/packages/polaris-viz/src/components/Legend/components/LegendItem/LegendItem.tsx +++ b/packages/polaris-viz/src/components/Legend/components/LegendItem/LegendItem.tsx @@ -93,12 +93,13 @@ export function LegendItem({ paddingRight: LEGEND_ITEM_RIGHT_PADDING, gap: LEGEND_ITEM_GAP, // if there is overflow, add a max width and truncate with ellipsis - maxWidth: truncate ? minWidth : undefined, + maxWidth: truncate ? minWidth : '100%', // if the item width is less than the minWidth, don't set a min width minWidth: width < minWidth ? undefined : minWidth, }} className={style.Legend} ref={ref} + title={name} > {renderSeriesIcon == null ? (