@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/AGENTS.md +14 -1
- package/CHANGELOG.md +7 -0
- package/dist/index.cjs +1162 -1137
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +0 -67
- package/dist/index.css.map +1 -1
- package/dist/index.js +1187 -1162
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss +0 -21
- package/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx +17 -10
- package/src/charts/geo-chart/geo-chart.module.scss +0 -3
- package/src/charts/geo-chart/geo-chart.tsx +9 -4
- package/src/charts/line-chart/line-chart.module.scss +0 -10
- package/src/charts/line-chart/line-chart.tsx +9 -2
- package/src/charts/line-chart/private/line-chart-annotation-label-popover.tsx +3 -2
- package/src/components/legend/private/base-legend.module.scss +0 -49
- package/src/components/legend/private/base-legend.tsx +30 -28
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/charts",
|
|
3
|
-
"version": "1.1.
|
|
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.
|
|
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.
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
|
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
|
-
<
|
|
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
|
-
</
|
|
411
|
-
</
|
|
417
|
+
</Stack>
|
|
418
|
+
</Stack>
|
|
412
419
|
);
|
|
413
420
|
} ) }
|
|
414
|
-
</
|
|
421
|
+
</Stack>
|
|
415
422
|
</Stack>
|
|
416
423
|
|
|
417
424
|
{ /* Tooltip Portal */ }
|
|
@@ -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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
<
|
|
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
|
-
|
|
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
|
-
<
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
{ '
|
|
274
|
-
|
|
275
|
-
|
|
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
|
-
</
|
|
283
|
+
</Stack>
|
|
282
284
|
) }
|
|
283
285
|
</LegendOrdinal>
|
|
284
286
|
);
|