diff --git a/projects/js-packages/charts/changelog/charts-175-use-stack-for-layout b/projects/js-packages/charts/changelog/charts-175-use-stack-for-layout
new file mode 100644
index 000000000000..45519913e244
--- /dev/null
+++ b/projects/js-packages/charts/changelog/charts-175-use-stack-for-layout
@@ -0,0 +1,4 @@
+Significance: patch
+Type: changed
+
+Charts: Replace ad-hoc flexbox layouts with @wordpress/ui Stack across legend, conversion funnel, line chart, geo chart, conversion funnel tooltip, and donut story.
diff --git a/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss b/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss
index 16da365f498b..52b4891936f0 100644
--- a/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss
+++ b/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss
@@ -7,10 +7,6 @@
}
.main-metric {
- display: flex;
- align-items: baseline;
- gap: 8px;
- margin-bottom: 24px;
height: 20px;
}
@@ -36,9 +32,6 @@
}
.funnel-container {
- display: flex;
- gap: 16px;
- align-items: flex-end;
flex: 1;
min-height: 200px;
width: 100%;
@@ -47,8 +40,6 @@
.funnel-step {
flex: 1 1 0;
min-width: 0;
- display: flex;
- flex-direction: column;
height: 100%;
&--animated {
@@ -60,10 +51,6 @@
}
}
-.step-header {
- margin-bottom: 24px;
-}
-
.step-label {
color: #757575;
font-size: var(--wpds-font-size-sm, 12px);
@@ -88,8 +75,6 @@
.bar-container {
flex: 1;
- display: flex;
- align-items: flex-end;
border-radius: 4px;
position: relative;
cursor: pointer;
@@ -116,12 +101,6 @@
}
.tooltip-wrapper {
- display: inline-flex;
- flex-direction: column;
- justify-content: center;
- align-items: flex-start;
- gap: 4px;
-
border-bottom: 1px solid var(--Gray-Gray-5, #dcdcde);
background: var(--black-white-white, #fff);
diff --git a/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx b/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx
index 610aba37c5d3..05fb970b5523 100644
--- a/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx
+++ b/projects/js-packages/charts/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx
@@ -263,13 +263,13 @@ const ConversionFunnelChartInternal: FC< ConversionFunnelChartProps > = ( {
// Default tooltip rendering function
const renderDefaultTooltip = ( step: FunnelStep ) => (
- <>
+
{ step.label }
{ formatPercentage( step.rate ) }
{ ` • ${ step.count ?? 'no' } items` }
- >
+
);
// Validate data
@@ -322,6 +322,7 @@ const ConversionFunnelChartInternal: FC< ConversionFunnelChartProps > = ( {
<>
{
// Set containerRef for @visx coordinate system
@@ -344,27 +345,31 @@ const ConversionFunnelChartInternal: FC< ConversionFunnelChartProps > = ( {
changeColor,
} )
) : (
- { renderDefaultMainMetric() }
+
+ { renderDefaultMainMetric() }
+
) }
{ /* Funnel Steps */ }
-
+
{ steps.map( ( step, index ) => {
const barHeight = ( step.rate / maxRate ) * 100;
const { isBlurred } = getStepState( step.id );
return (
-
{ /* Step Label and Rate */ }
-
+
{ renderStepLabel ? (
renderStepLabel( {
step,
@@ -388,7 +393,9 @@ const ConversionFunnelChartInternal: FC< ConversionFunnelChartProps > = ( {
{ /* Funnel Bar */ }
-
= ( {
backgroundColor: barColor,
} }
/>
-
-
+
+
);
} ) }
-
+
{ /* Tooltip Portal */ }
diff --git a/projects/js-packages/charts/src/charts/geo-chart/geo-chart.module.scss b/projects/js-packages/charts/src/charts/geo-chart/geo-chart.module.scss
index a69330bbc55a..b4a51cf6b002 100644
--- a/projects/js-packages/charts/src/charts/geo-chart/geo-chart.module.scss
+++ b/projects/js-packages/charts/src/charts/geo-chart/geo-chart.module.scss
@@ -1,6 +1,3 @@
.container {
position: relative;
- display: flex;
- justify-content: center;
- align-items: center;
}
diff --git a/projects/js-packages/charts/src/charts/geo-chart/geo-chart.tsx b/projects/js-packages/charts/src/charts/geo-chart/geo-chart.tsx
index b2e9dc5d998e..db0e1464ff1f 100644
--- a/projects/js-packages/charts/src/charts/geo-chart/geo-chart.tsx
+++ b/projects/js-packages/charts/src/charts/geo-chart/geo-chart.tsx
@@ -2,6 +2,7 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
+import { Stack } from '@wordpress/ui';
import clsx from 'clsx';
import { FC, useContext, useMemo } from 'react';
import { Chart, type GoogleChartOptions } from 'react-google-charts';
@@ -57,13 +58,15 @@ const GeoChartInternal: FC< GeoChartProps > = ( {
// Render loading placeholder
const loadingPlaceholder = (
-
{ renderPlaceholder ? renderPlaceholder() : __( 'Loading map', 'jetpack-charts' ) }
-
+
);
// Google charts doesn't accept CSS variables, so we need to convert them to hex colors
@@ -144,7 +147,9 @@ const GeoChartInternal: FC< GeoChartProps > = ( {
);
return (
-
= ( {
options={ options }
loader={ loadingPlaceholder }
/>
-
+
);
};
diff --git a/projects/js-packages/charts/src/charts/line-chart/line-chart.module.scss b/projects/js-packages/charts/src/charts/line-chart/line-chart.module.scss
index 027e9d18faac..67d40905763a 100644
--- a/projects/js-packages/charts/src/charts/line-chart/line-chart.module.scss
+++ b/projects/js-packages/charts/src/charts/line-chart/line-chart.module.scss
@@ -26,10 +26,7 @@
}
&__tooltip-row {
- display: flex;
- align-items: center;
padding: 4px 0;
- justify-content: space-between;
}
&__tooltip-label {
@@ -81,13 +78,6 @@
}
}
- &__annotation-label-popover-header {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: start;
- }
-
&__annotation-label-popover-content {
padding: 0.5rem;
}
diff --git a/projects/js-packages/charts/src/charts/line-chart/line-chart.tsx b/projects/js-packages/charts/src/charts/line-chart/line-chart.tsx
index 43b846e5db27..8757e34fb190 100644
--- a/projects/js-packages/charts/src/charts/line-chart/line-chart.tsx
+++ b/projects/js-packages/charts/src/charts/line-chart/line-chart.tsx
@@ -4,6 +4,7 @@ import { LinearGradient } from '@visx/gradient';
import { scaleTime } from '@visx/scale';
import { XYChart, AreaSeries, Grid, Axis, DataContext } from '@visx/xychart';
import { __ } from '@wordpress/i18n';
+import { Stack } from '@wordpress/ui';
import clsx from 'clsx';
import { differenceInHours, differenceInYears } from 'date-fns';
import {
@@ -103,12 +104,18 @@ const renderDefaultTooltip = ( params: RenderTooltipParams< DataPointDate > ) =>
{ nearestDatum.date?.toLocaleDateString() }
{ tooltipPoints.map( point => (
-
+
{ point.key }:
{ formatNumber( point.value ) }
-
+
) ) }
);
diff --git a/projects/js-packages/charts/src/charts/line-chart/private/line-chart-annotation-label-popover.tsx b/projects/js-packages/charts/src/charts/line-chart/private/line-chart-annotation-label-popover.tsx
index 6b5f15e9a88f..787fa729e54b 100644
--- a/projects/js-packages/charts/src/charts/line-chart/private/line-chart-annotation-label-popover.tsx
+++ b/projects/js-packages/charts/src/charts/line-chart/private/line-chart-annotation-label-popover.tsx
@@ -1,5 +1,6 @@
import { __ } from '@wordpress/i18n';
import { Icon, close } from '@wordpress/icons';
+import { Stack } from '@wordpress/ui';
import clsx from 'clsx';
import { useEffect, useId, useRef, useState } from 'react';
import { isSafari } from '../../../utils';
@@ -88,7 +89,7 @@ const LineChartAnnotationLabelWithPopover: FC< LineChartAnnotationLabelWithPopov
) }
data-testid="line-chart-annotation-label-popover"
>
-
+
{ renderLabelPopover( { title, subtitle } ) }
@@ -102,7 +103,7 @@ const LineChartAnnotationLabelWithPopover: FC< LineChartAnnotationLabelWithPopov
>
-
+
);
diff --git a/projects/js-packages/charts/src/charts/pie-chart/stories/donut.stories.tsx b/projects/js-packages/charts/src/charts/pie-chart/stories/donut.stories.tsx
index 7ea4afecb3c5..2732e0a210fa 100644
--- a/projects/js-packages/charts/src/charts/pie-chart/stories/donut.stories.tsx
+++ b/projects/js-packages/charts/src/charts/pie-chart/stories/donut.stories.tsx
@@ -1,6 +1,4 @@
-/* eslint-disable @wordpress/no-unsafe-wp-apis */
-import { __experimentalHStack as HStack } from '@wordpress/components';
-import { Text } from '@wordpress/ui';
+import { Stack, Text } from '@wordpress/ui';
import { Fragment } from 'react';
import { BaseLegendItem } from '../../../components/legend/types';
import {
@@ -267,7 +265,7 @@ const CustomPieLegend = ( {
return (
-
+
{ item.label }
-
+
{ item.formattedValue }
diff --git a/projects/js-packages/charts/src/components/legend/private/base-legend.module.scss b/projects/js-packages/charts/src/components/legend/private/base-legend.module.scss
index 64ced1a401ec..d29410dcc58c 100644
--- a/projects/js-packages/charts/src/components/legend/private/base-legend.module.scss
+++ b/projects/js-packages/charts/src/components/legend/private/base-legend.module.scss
@@ -1,49 +1,8 @@
.legend {
align-self: stretch;
-
- &--horizontal {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- gap: 16px;
- }
-
- &--vertical {
- display: flex;
- flex-direction: column;
- gap: 8px;
- }
-
- &--alignment-start {
- justify-content: flex-start;
- }
-
- &--alignment-center {
- justify-content: center;
- }
-
- &--alignment-end {
- justify-content: flex-end;
- }
-
- // Vertical legends align on the cross-axis instead
- &--vertical.legend--alignment-start {
- align-items: flex-start;
- }
-
- &--vertical.legend--alignment-center {
- align-items: center;
- }
-
- &--vertical.legend--alignment-end {
- align-items: flex-end;
- }
-
}
.legend-item {
- display: flex;
- align-items: center;
font-size: var(--wpds-font-size-md, 13px);
&--interactive {
@@ -75,14 +34,6 @@
}
}
-.legend-item-label {
- display: flex;
- align-items: center;
- gap: 0.5rem;
-
- /* Text wrapping is handled at the text level, not the label container */
-}
-
.legend-item-text {
&--wrap {
diff --git a/projects/js-packages/charts/src/components/legend/private/base-legend.tsx b/projects/js-packages/charts/src/components/legend/private/base-legend.tsx
index df56e784f968..125c9c6cf437 100644
--- a/projects/js-packages/charts/src/components/legend/private/base-legend.tsx
+++ b/projects/js-packages/charts/src/components/legend/private/base-legend.tsx
@@ -1,6 +1,7 @@
import { Group } from '@visx/group';
import { LegendItem, LegendLabel, LegendOrdinal, LegendShape } from '@visx/legend';
import { scaleOrdinal } from '@visx/scale';
+import { Stack } from '@wordpress/ui';
import clsx from 'clsx';
import {
type RefAttributes,
@@ -16,10 +17,11 @@ import { valueOrIdentity, valueOrIdentityString, labelTransformFactory } from '.
import styles from './base-legend.module.scss';
import type { BaseLegendProps } from '../types';
-const orientationToFlexDirection = {
- horizontal: 'row' as const,
- vertical: 'column' as const,
-};
+const ALIGNMENT_TO_FLEX = {
+ start: 'flex-start',
+ center: 'center',
+ end: 'flex-end',
+} as const;
// Component for legend text with truncation detection
// Moved outside BaseLegend to prevent recreation on every render
@@ -159,6 +161,8 @@ export const BaseLegend: ForwardRefExoticComponent<
[ interactive, handleLegendClick ]
);
+ const flexAlignment = ALIGNMENT_TO_FLEX[ alignment ] ?? 'center';
+
return render ? (
render( items )
) : (
@@ -168,20 +172,17 @@ export const BaseLegend: ForwardRefExoticComponent<
labelTransform={ labelTransform }
>
{ labels => (
-
{ labels.map( ( label, i ) => {
const visible = isSeriesVisible( label.text );
@@ -257,28 +258,29 @@ export const BaseLegend: ForwardRefExoticComponent<
labelClassName
) }
style={ {
- justifyContent: labelJustifyContent,
flex: labelFlex,
margin: labelMargin,
...theme.legend?.labelStyles,
} }
>
-
- { matchedItem?.value != null && matchedItem.value !== '' && (
-
- { '\u00A0' }
- { matchedItem.value }
-
- ) }
+
+
+ { matchedItem?.value != null && matchedItem.value !== '' && (
+
+ { '\u00A0' }
+ { matchedItem.value }
+
+ ) }
+
);
} ) }
-
+
) }
);