@automattic/charts 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/charts",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Display charts within Automattic products.",
5
5
  "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/charts/#readme",
6
6
  "bugs": {
@@ -63,7 +63,7 @@
63
63
  "typecheck": "tsgo --noEmit"
64
64
  },
65
65
  "dependencies": {
66
- "@automattic/number-formatters": "^1.1.5",
66
+ "@automattic/number-formatters": "^1.1.6",
67
67
  "@babel/runtime": "7.29.2",
68
68
  "@react-spring/web": "9.7.5",
69
69
  "@visx/annotation": "^3.12.0",
@@ -85,7 +85,7 @@
85
85
  "@wordpress/i18n": "^6.0.0",
86
86
  "@wordpress/icons": "^12.0.0",
87
87
  "@wordpress/theme": "0.10.0",
88
- "@wordpress/ui": "0.9.0",
88
+ "@wordpress/ui": "0.10.0",
89
89
  "clsx": "2.1.1",
90
90
  "date-fns": "^4.1.0",
91
91
  "deepmerge": "4.3.1",
@@ -7,10 +7,6 @@
7
7
  }
8
8
 
9
9
  .main-metric {
10
- display: flex;
11
- align-items: baseline;
12
- gap: 8px;
13
- margin-bottom: 24px;
14
10
  height: 20px;
15
11
  }
16
12
 
@@ -36,9 +32,6 @@
36
32
  }
37
33
 
38
34
  .funnel-container {
39
- display: flex;
40
- gap: 16px;
41
- align-items: flex-end;
42
35
  flex: 1;
43
36
  min-height: 200px;
44
37
  width: 100%;
@@ -47,8 +40,6 @@
47
40
  .funnel-step {
48
41
  flex: 1 1 0;
49
42
  min-width: 0;
50
- display: flex;
51
- flex-direction: column;
52
43
  height: 100%;
53
44
 
54
45
  &--animated {
@@ -60,10 +51,6 @@
60
51
  }
61
52
  }
62
53
 
63
- .step-header {
64
- margin-bottom: 24px;
65
- }
66
-
67
54
  .step-label {
68
55
  color: #757575;
69
56
  font-size: var(--wpds-font-size-sm, 12px);
@@ -88,8 +75,6 @@
88
75
 
89
76
  .bar-container {
90
77
  flex: 1;
91
- display: flex;
92
- align-items: flex-end;
93
78
  border-radius: 4px;
94
79
  position: relative;
95
80
  cursor: pointer;
@@ -116,12 +101,6 @@
116
101
  }
117
102
 
118
103
  .tooltip-wrapper {
119
- display: inline-flex;
120
- flex-direction: column;
121
- justify-content: center;
122
- align-items: flex-start;
123
- gap: 4px;
124
-
125
104
  border-bottom: 1px solid var(--Gray-Gray-5, #dcdcde);
126
105
  background: var(--black-white-white, #fff);
127
106
 
@@ -263,13 +263,13 @@ const ConversionFunnelChartInternal: FC< ConversionFunnelChartProps > = ( {
263
263
 
264
264
  // Default tooltip rendering function
265
265
  const renderDefaultTooltip = ( step: FunnelStep ) => (
266
- <>
266
+ <Stack direction="column" align="flex-start" gap="xs">
267
267
  <div className={ styles[ 'tooltip-title' ] }>{ step.label }</div>
268
268
  <div className={ styles[ 'tooltip-content' ] }>
269
269
  { formatPercentage( step.rate ) }
270
270
  { ` • ${ step.count ?? 'no' } items` }
271
271
  </div>
272
- </>
272
+ </Stack>
273
273
  );
274
274
 
275
275
  // Validate data
@@ -322,6 +322,7 @@ const ConversionFunnelChartInternal: FC< ConversionFunnelChartProps > = ( {
322
322
  <>
323
323
  <Stack
324
324
  direction="column"
325
+ gap="xl"
325
326
  data-testid="conversion-funnel-chart"
326
327
  ref={ node => {
327
328
  // Set containerRef for @visx coordinate system
@@ -344,27 +345,31 @@ const ConversionFunnelChartInternal: FC< ConversionFunnelChartProps > = ( {
344
345
  changeColor,
345
346
  } )
346
347
  ) : (
347
- <div className={ styles[ 'main-metric' ] }>{ renderDefaultMainMetric() }</div>
348
+ <Stack direction="row" align="baseline" gap="sm" className={ styles[ 'main-metric' ] }>
349
+ { renderDefaultMainMetric() }
350
+ </Stack>
348
351
  ) }
349
352
 
350
353
  { /* Funnel Steps */ }
351
- <div className={ styles[ 'funnel-container' ] }>
354
+ <Stack direction="row" align="flex-end" gap="lg" className={ styles[ 'funnel-container' ] }>
352
355
  { steps.map( ( step, index ) => {
353
356
  const barHeight = ( step.rate / maxRate ) * 100;
354
357
  const { isBlurred } = getStepState( step.id );
355
358
 
356
359
  return (
357
- <div
360
+ <Stack
358
361
  key={ step.id }
362
+ direction="column"
359
363
  data-testid="funnel-step"
360
364
  className={ clsx(
361
365
  styles[ 'funnel-step' ],
362
366
  isColorPaletteResolved && styles[ 'funnel-step--animated' ],
363
367
  isBlurred && styles[ 'funnel-step--blurred' ]
364
368
  ) }
369
+ gap="xl"
365
370
  >
366
371
  { /* Step Label and Rate */ }
367
- <div className={ styles[ 'step-header' ] }>
372
+ <div>
368
373
  { renderStepLabel ? (
369
374
  renderStepLabel( {
370
375
  step,
@@ -388,7 +393,9 @@ const ConversionFunnelChartInternal: FC< ConversionFunnelChartProps > = ( {
388
393
  </div>
389
394
 
390
395
  { /* Funnel Bar */ }
391
- <div
396
+ <Stack
397
+ direction="column"
398
+ justify="flex-end"
392
399
  className={ styles[ 'bar-container' ] }
393
400
  onClick={ stepHandlers.get( step.id )?.onClick }
394
401
  onKeyDown={ stepHandlers.get( step.id )?.onKeyDown }
@@ -407,11 +414,11 @@ const ConversionFunnelChartInternal: FC< ConversionFunnelChartProps > = ( {
407
414
  backgroundColor: barColor,
408
415
  } }
409
416
  />
410
- </div>
411
- </div>
417
+ </Stack>
418
+ </Stack>
412
419
  );
413
420
  } ) }
414
- </div>
421
+ </Stack>
415
422
  </Stack>
416
423
 
417
424
  { /* Tooltip Portal */ }
@@ -1,6 +1,3 @@
1
1
  .container {
2
2
  position: relative;
3
- display: flex;
4
- justify-content: center;
5
- align-items: center;
6
3
  }
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import { __ } from '@wordpress/i18n';
5
+ import { Stack } from '@wordpress/ui';
5
6
  import clsx from 'clsx';
6
7
  import { FC, useContext, useMemo } from 'react';
7
8
  import { Chart, type GoogleChartOptions } from 'react-google-charts';
@@ -57,13 +58,15 @@ const GeoChartInternal: FC< GeoChartProps > = ( {
57
58
 
58
59
  // Render loading placeholder
59
60
  const loadingPlaceholder = (
60
- <div
61
+ <Stack
62
+ align="center"
63
+ justify="center"
61
64
  className={ clsx( 'geo-chart', styles.container, className ) }
62
65
  data-testid="geo-chart-loading"
63
66
  style={ { width, height } }
64
67
  >
65
68
  { renderPlaceholder ? renderPlaceholder() : __( 'Loading map', 'jetpack-charts' ) }
66
- </div>
69
+ </Stack>
67
70
  );
68
71
 
69
72
  // Google charts doesn't accept CSS variables, so we need to convert them to hex colors
@@ -144,7 +147,9 @@ const GeoChartInternal: FC< GeoChartProps > = ( {
144
147
  );
145
148
 
146
149
  return (
147
- <div
150
+ <Stack
151
+ align="center"
152
+ justify="center"
148
153
  className={ clsx( 'geo-chart', styles.container, className ) }
149
154
  data-testid="geo-chart"
150
155
  style={ { width, height, backgroundColor } }
@@ -157,7 +162,7 @@ const GeoChartInternal: FC< GeoChartProps > = ( {
157
162
  options={ options }
158
163
  loader={ loadingPlaceholder }
159
164
  />
160
- </div>
165
+ </Stack>
161
166
  );
162
167
  };
163
168
 
@@ -26,10 +26,7 @@
26
26
  }
27
27
 
28
28
  &__tooltip-row {
29
- display: flex;
30
- align-items: center;
31
29
  padding: 4px 0;
32
- justify-content: space-between;
33
30
  }
34
31
 
35
32
  &__tooltip-label {
@@ -81,13 +78,6 @@
81
78
  }
82
79
  }
83
80
 
84
- &__annotation-label-popover-header {
85
- display: flex;
86
- flex-direction: row;
87
- justify-content: space-between;
88
- align-items: start;
89
- }
90
-
91
81
  &__annotation-label-popover-content {
92
82
  padding: 0.5rem;
93
83
  }
@@ -4,6 +4,7 @@ import { LinearGradient } from '@visx/gradient';
4
4
  import { scaleTime } from '@visx/scale';
5
5
  import { XYChart, AreaSeries, Grid, Axis, DataContext } from '@visx/xychart';
6
6
  import { __ } from '@wordpress/i18n';
7
+ import { Stack } from '@wordpress/ui';
7
8
  import clsx from 'clsx';
8
9
  import { differenceInHours, differenceInYears } from 'date-fns';
9
10
  import {
@@ -103,12 +104,18 @@ const renderDefaultTooltip = ( params: RenderTooltipParams< DataPointDate > ) =>
103
104
  { nearestDatum.date?.toLocaleDateString() }
104
105
  </div>
105
106
  { tooltipPoints.map( point => (
106
- <div key={ point.key } className={ styles[ 'line-chart__tooltip-row' ] }>
107
+ <Stack
108
+ key={ point.key }
109
+ direction="row"
110
+ align="center"
111
+ justify="space-between"
112
+ className={ styles[ 'line-chart__tooltip-row' ] }
113
+ >
107
114
  <span className={ styles[ 'line-chart__tooltip-label' ] }>{ point.key }:</span>
108
115
  <span className={ styles[ 'line-chart__tooltip-value' ] }>
109
116
  { formatNumber( point.value ) }
110
117
  </span>
111
- </div>
118
+ </Stack>
112
119
  ) ) }
113
120
  </div>
114
121
  );
@@ -1,5 +1,6 @@
1
1
  import { __ } from '@wordpress/i18n';
2
2
  import { Icon, close } from '@wordpress/icons';
3
+ import { Stack } from '@wordpress/ui';
3
4
  import clsx from 'clsx';
4
5
  import { useEffect, useId, useRef, useState } from 'react';
5
6
  import { isSafari } from '../../../utils';
@@ -88,7 +89,7 @@ const LineChartAnnotationLabelWithPopover: FC< LineChartAnnotationLabelWithPopov
88
89
  ) }
89
90
  data-testid="line-chart-annotation-label-popover"
90
91
  >
91
- <div className={ styles[ 'line-chart__annotation-label-popover-header' ] }>
92
+ <Stack direction="row" align="flex-start" justify="space-between">
92
93
  <div className={ styles[ 'line-chart__annotation-label-popover-content' ] }>
93
94
  { renderLabelPopover( { title, subtitle } ) }
94
95
  </div>
@@ -102,7 +103,7 @@ const LineChartAnnotationLabelWithPopover: FC< LineChartAnnotationLabelWithPopov
102
103
  >
103
104
  <Icon icon={ close } size={ 16 } />
104
105
  </button>
105
- </div>
106
+ </Stack>
106
107
  </div>
107
108
  </div>
108
109
  );
@@ -1,49 +1,8 @@
1
1
  .legend {
2
2
  align-self: stretch;
3
-
4
- &--horizontal {
5
- display: flex;
6
- flex-direction: row;
7
- flex-wrap: wrap;
8
- gap: 16px;
9
- }
10
-
11
- &--vertical {
12
- display: flex;
13
- flex-direction: column;
14
- gap: 8px;
15
- }
16
-
17
- &--alignment-start {
18
- justify-content: flex-start;
19
- }
20
-
21
- &--alignment-center {
22
- justify-content: center;
23
- }
24
-
25
- &--alignment-end {
26
- justify-content: flex-end;
27
- }
28
-
29
- // Vertical legends align on the cross-axis instead
30
- &--vertical.legend--alignment-start {
31
- align-items: flex-start;
32
- }
33
-
34
- &--vertical.legend--alignment-center {
35
- align-items: center;
36
- }
37
-
38
- &--vertical.legend--alignment-end {
39
- align-items: flex-end;
40
- }
41
-
42
3
  }
43
4
 
44
5
  .legend-item {
45
- display: flex;
46
- align-items: center;
47
6
  font-size: var(--wpds-font-size-md, 13px);
48
7
 
49
8
  &--interactive {
@@ -75,14 +34,6 @@
75
34
  }
76
35
  }
77
36
 
78
- .legend-item-label {
79
- display: flex;
80
- align-items: center;
81
- gap: 0.5rem;
82
-
83
- /* Text wrapping is handled at the text level, not the label container */
84
- }
85
-
86
37
  .legend-item-text {
87
38
 
88
39
  &--wrap {
@@ -1,6 +1,7 @@
1
1
  import { Group } from '@visx/group';
2
2
  import { LegendItem, LegendLabel, LegendOrdinal, LegendShape } from '@visx/legend';
3
3
  import { scaleOrdinal } from '@visx/scale';
4
+ import { Stack } from '@wordpress/ui';
4
5
  import clsx from 'clsx';
5
6
  import {
6
7
  type RefAttributes,
@@ -16,10 +17,11 @@ import { valueOrIdentity, valueOrIdentityString, labelTransformFactory } from '.
16
17
  import styles from './base-legend.module.scss';
17
18
  import type { BaseLegendProps } from '../types';
18
19
 
19
- const orientationToFlexDirection = {
20
- horizontal: 'row' as const,
21
- vertical: 'column' as const,
22
- };
20
+ const ALIGNMENT_TO_FLEX = {
21
+ start: 'flex-start',
22
+ center: 'center',
23
+ end: 'flex-end',
24
+ } as const;
23
25
 
24
26
  // Component for legend text with truncation detection
25
27
  // Moved outside BaseLegend to prevent recreation on every render
@@ -159,6 +161,8 @@ export const BaseLegend: ForwardRefExoticComponent<
159
161
  [ interactive, handleLegendClick ]
160
162
  );
161
163
 
164
+ const flexAlignment = ALIGNMENT_TO_FLEX[ alignment ] ?? 'center';
165
+
162
166
  return render ? (
163
167
  render( items )
164
168
  ) : (
@@ -168,20 +172,17 @@ export const BaseLegend: ForwardRefExoticComponent<
168
172
  labelTransform={ labelTransform }
169
173
  >
170
174
  { labels => (
171
- <div
175
+ <Stack
172
176
  ref={ ref }
177
+ direction={ orientation === 'vertical' ? 'column' : 'row' }
178
+ gap={ orientation === 'vertical' ? 'sm' : 'lg' }
179
+ align={ orientation === 'vertical' ? flexAlignment : undefined }
180
+ justify={ orientation === 'horizontal' ? flexAlignment : undefined }
181
+ wrap={ orientation === 'horizontal' ? 'wrap' : undefined }
173
182
  role="list"
174
183
  data-testid={ `legend-${ orientation }` }
175
- className={ clsx(
176
- styles.legend,
177
- styles[ `legend--${ orientation }` ],
178
- styles[ `legend--alignment-${ alignment }` ],
179
- className
180
- ) }
181
- style={ {
182
- flexDirection: orientationToFlexDirection[ orientation ],
183
- ...theme.legend?.containerStyles,
184
- } }
184
+ className={ clsx( styles.legend, className ) }
185
+ style={ theme.legend?.containerStyles }
185
186
  >
186
187
  { labels.map( ( label, i ) => {
187
188
  const visible = isSeriesVisible( label.text );
@@ -257,28 +258,29 @@ export const BaseLegend: ForwardRefExoticComponent<
257
258
  labelClassName
258
259
  ) }
259
260
  style={ {
260
- justifyContent: labelJustifyContent,
261
261
  flex: labelFlex,
262
262
  margin: labelMargin,
263
263
  ...theme.legend?.labelStyles,
264
264
  } }
265
265
  >
266
- <LegendText
267
- text={ label.text }
268
- textOverflow={ textOverflow }
269
- maxWidth={ maxWidth }
270
- />
271
- { matchedItem?.value != null && matchedItem.value !== '' && (
272
- <span className={ styles[ 'legend-item-value' ] }>
273
- { '\u00A0' }
274
- { matchedItem.value }
275
- </span>
276
- ) }
266
+ <Stack align="center" gap="sm" justify={ labelJustifyContent }>
267
+ <LegendText
268
+ text={ label.text }
269
+ textOverflow={ textOverflow }
270
+ maxWidth={ maxWidth }
271
+ />
272
+ { matchedItem?.value != null && matchedItem.value !== '' && (
273
+ <span className={ styles[ 'legend-item-value' ] }>
274
+ { '\u00A0' }
275
+ { matchedItem.value }
276
+ </span>
277
+ ) }
278
+ </Stack>
277
279
  </LegendLabel>
278
280
  </LegendItem>
279
281
  );
280
282
  } ) }
281
- </div>
283
+ </Stack>
282
284
  ) }
283
285
  </LegendOrdinal>
284
286
  );