@automattic/charts 0.58.0 → 0.59.0
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/CHANGELOG.md +20 -0
- package/README.md +7 -54
- package/dist/index.cjs +9606 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +20 -25
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +1612 -33
- package/dist/index.d.ts +1612 -33
- package/dist/index.js +9640 -56
- package/dist/index.js.map +1 -1
- package/package.json +8 -125
- package/src/charts/bar-chart/bar-chart.module.scss +0 -5
- package/src/charts/bar-chart/bar-chart.tsx +131 -137
- package/src/charts/leaderboard-chart/leaderboard-chart.tsx +23 -40
- package/src/charts/line-chart/line-chart.module.scss +0 -5
- package/src/charts/line-chart/line-chart.tsx +190 -183
- package/src/charts/line-chart/private/line-chart-annotations-overlay.tsx +1 -2
- package/src/charts/pie-chart/pie-chart.module.scss +2 -10
- package/src/charts/pie-chart/pie-chart.tsx +198 -199
- package/src/charts/pie-chart/test/composition-api.test.tsx +3 -3
- package/src/charts/pie-chart/test/pie-chart.test.tsx +42 -35
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +2 -8
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +155 -155
- package/src/charts/pie-semi-circle-chart/test/pie-semi-circle-chart.test.tsx +25 -25
- package/src/charts/private/chart-layout/chart-layout.module.scss +7 -0
- package/src/charts/private/chart-layout/chart-layout.tsx +106 -0
- package/src/charts/private/chart-layout/index.ts +2 -0
- package/src/charts/private/chart-layout/test/chart-layout.test.tsx +167 -0
- package/src/charts/private/single-chart-context/single-chart-context.tsx +2 -2
- package/src/charts/private/svg-empty-state/index.ts +1 -0
- package/src/charts/private/svg-empty-state/svg-empty-state.module.scss +7 -0
- package/src/charts/private/svg-empty-state/svg-empty-state.tsx +40 -0
- package/src/components/legend/hooks/test/use-chart-legend-items.test.tsx +12 -8
- package/src/components/legend/hooks/use-chart-legend-items.ts +12 -13
- package/src/components/legend/legend.tsx +33 -8
- package/src/components/legend/test/legend.test.tsx +93 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-data-with-percentages.ts +24 -0
- package/src/hooks/use-interactive-legend-data.ts +18 -15
- package/src/index.ts +65 -2
- package/src/providers/chart-context/global-charts-provider.tsx +7 -1
- package/src/providers/chart-context/hooks/use-chart-registration.ts +2 -1
- package/src/providers/chart-context/types.ts +2 -2
- package/src/types.ts +27 -7
- package/src/utils/test/resolve-css-var.test.ts +2 -0
- package/dist/base-tooltip-DOq93wjU.d.cts +0 -38
- package/dist/base-tooltip-DOq93wjU.d.ts +0 -38
- package/dist/charts/bar-chart/index.cjs +0 -17
- package/dist/charts/bar-chart/index.cjs.map +0 -1
- package/dist/charts/bar-chart/index.css +0 -141
- package/dist/charts/bar-chart/index.css.map +0 -1
- package/dist/charts/bar-chart/index.d.cts +0 -36
- package/dist/charts/bar-chart/index.d.ts +0 -36
- package/dist/charts/bar-chart/index.js +0 -17
- package/dist/charts/bar-chart/index.js.map +0 -1
- package/dist/charts/bar-list-chart/index.cjs +0 -18
- package/dist/charts/bar-list-chart/index.cjs.map +0 -1
- package/dist/charts/bar-list-chart/index.css +0 -141
- package/dist/charts/bar-list-chart/index.css.map +0 -1
- package/dist/charts/bar-list-chart/index.d.cts +0 -92
- package/dist/charts/bar-list-chart/index.d.ts +0 -92
- package/dist/charts/bar-list-chart/index.js +0 -18
- package/dist/charts/bar-list-chart/index.js.map +0 -1
- package/dist/charts/conversion-funnel-chart/index.cjs +0 -11
- package/dist/charts/conversion-funnel-chart/index.cjs.map +0 -1
- package/dist/charts/conversion-funnel-chart/index.css +0 -157
- package/dist/charts/conversion-funnel-chart/index.css.map +0 -1
- package/dist/charts/conversion-funnel-chart/index.d.cts +0 -97
- package/dist/charts/conversion-funnel-chart/index.d.ts +0 -97
- package/dist/charts/conversion-funnel-chart/index.js +0 -11
- package/dist/charts/conversion-funnel-chart/index.js.map +0 -1
- package/dist/charts/geo-chart/index.cjs +0 -13
- package/dist/charts/geo-chart/index.cjs.map +0 -1
- package/dist/charts/geo-chart/index.css +0 -23
- package/dist/charts/geo-chart/index.css.map +0 -1
- package/dist/charts/geo-chart/index.d.cts +0 -67
- package/dist/charts/geo-chart/index.d.ts +0 -67
- package/dist/charts/geo-chart/index.js +0 -13
- package/dist/charts/geo-chart/index.js.map +0 -1
- package/dist/charts/leaderboard-chart/index.cjs +0 -21
- package/dist/charts/leaderboard-chart/index.cjs.map +0 -1
- package/dist/charts/leaderboard-chart/index.css +0 -160
- package/dist/charts/leaderboard-chart/index.css.map +0 -1
- package/dist/charts/leaderboard-chart/index.d.cts +0 -46
- package/dist/charts/leaderboard-chart/index.d.ts +0 -46
- package/dist/charts/leaderboard-chart/index.js +0 -21
- package/dist/charts/leaderboard-chart/index.js.map +0 -1
- package/dist/charts/line-chart/index.cjs +0 -17
- package/dist/charts/line-chart/index.cjs.map +0 -1
- package/dist/charts/line-chart/index.css +0 -213
- package/dist/charts/line-chart/index.css.map +0 -1
- package/dist/charts/line-chart/index.d.cts +0 -98
- package/dist/charts/line-chart/index.d.ts +0 -98
- package/dist/charts/line-chart/index.js +0 -17
- package/dist/charts/line-chart/index.js.map +0 -1
- package/dist/charts/pie-chart/index.cjs +0 -19
- package/dist/charts/pie-chart/index.cjs.map +0 -1
- package/dist/charts/pie-chart/index.css +0 -131
- package/dist/charts/pie-chart/index.css.map +0 -1
- package/dist/charts/pie-chart/index.d.cts +0 -91
- package/dist/charts/pie-chart/index.d.ts +0 -91
- package/dist/charts/pie-chart/index.js +0 -19
- package/dist/charts/pie-chart/index.js.map +0 -1
- package/dist/charts/pie-semi-circle-chart/index.cjs +0 -18
- package/dist/charts/pie-semi-circle-chart/index.cjs.map +0 -1
- package/dist/charts/pie-semi-circle-chart/index.css +0 -132
- package/dist/charts/pie-semi-circle-chart/index.css.map +0 -1
- package/dist/charts/pie-semi-circle-chart/index.d.cts +0 -88
- package/dist/charts/pie-semi-circle-chart/index.d.ts +0 -88
- package/dist/charts/pie-semi-circle-chart/index.js +0 -18
- package/dist/charts/pie-semi-circle-chart/index.js.map +0 -1
- package/dist/charts/sparkline/index.cjs +0 -18
- package/dist/charts/sparkline/index.cjs.map +0 -1
- package/dist/charts/sparkline/index.css +0 -230
- package/dist/charts/sparkline/index.css.map +0 -1
- package/dist/charts/sparkline/index.d.cts +0 -113
- package/dist/charts/sparkline/index.d.ts +0 -113
- package/dist/charts/sparkline/index.js +0 -18
- package/dist/charts/sparkline/index.js.map +0 -1
- package/dist/chunk-2A34OA5O.cjs +0 -51
- package/dist/chunk-2A34OA5O.cjs.map +0 -1
- package/dist/chunk-2I67QUIV.js +0 -895
- package/dist/chunk-2I67QUIV.js.map +0 -1
- package/dist/chunk-2ICEEQOC.js +0 -1071
- package/dist/chunk-2ICEEQOC.js.map +0 -1
- package/dist/chunk-4B7BL2DD.js +0 -120
- package/dist/chunk-4B7BL2DD.js.map +0 -1
- package/dist/chunk-4OXMTKAL.js +0 -401
- package/dist/chunk-4OXMTKAL.js.map +0 -1
- package/dist/chunk-ASLARV7L.cjs +0 -81
- package/dist/chunk-ASLARV7L.cjs.map +0 -1
- package/dist/chunk-B6NLZFRW.js +0 -617
- package/dist/chunk-B6NLZFRW.js.map +0 -1
- package/dist/chunk-BBAUQOW6.cjs +0 -120
- package/dist/chunk-BBAUQOW6.cjs.map +0 -1
- package/dist/chunk-BPYKWMI7.js +0 -194
- package/dist/chunk-BPYKWMI7.js.map +0 -1
- package/dist/chunk-CMMHCTBX.cjs +0 -373
- package/dist/chunk-CMMHCTBX.cjs.map +0 -1
- package/dist/chunk-CPPXJATQ.cjs +0 -1071
- package/dist/chunk-CPPXJATQ.cjs.map +0 -1
- package/dist/chunk-DKU775VC.js +0 -219
- package/dist/chunk-DKU775VC.js.map +0 -1
- package/dist/chunk-GRA7Y2ZG.cjs +0 -401
- package/dist/chunk-GRA7Y2ZG.cjs.map +0 -1
- package/dist/chunk-I2276W3I.cjs +0 -66
- package/dist/chunk-I2276W3I.cjs.map +0 -1
- package/dist/chunk-JJIMABHT.js +0 -351
- package/dist/chunk-JJIMABHT.js.map +0 -1
- package/dist/chunk-KJHWXOCZ.js +0 -421
- package/dist/chunk-KJHWXOCZ.js.map +0 -1
- package/dist/chunk-KRWGSOJ2.js +0 -91
- package/dist/chunk-KRWGSOJ2.js.map +0 -1
- package/dist/chunk-KXRWNFQJ.js +0 -51
- package/dist/chunk-KXRWNFQJ.js.map +0 -1
- package/dist/chunk-LTFH7SEG.js +0 -373
- package/dist/chunk-LTFH7SEG.js.map +0 -1
- package/dist/chunk-MUNOKLLE.js +0 -165
- package/dist/chunk-MUNOKLLE.js.map +0 -1
- package/dist/chunk-MXGLYWVP.cjs +0 -351
- package/dist/chunk-MXGLYWVP.cjs.map +0 -1
- package/dist/chunk-OP6PHB2U.js +0 -81
- package/dist/chunk-OP6PHB2U.js.map +0 -1
- package/dist/chunk-OYC34VTO.cjs +0 -3957
- package/dist/chunk-OYC34VTO.cjs.map +0 -1
- package/dist/chunk-PQL5I3F6.cjs +0 -421
- package/dist/chunk-PQL5I3F6.cjs.map +0 -1
- package/dist/chunk-REZTQ4PH.cjs +0 -488
- package/dist/chunk-REZTQ4PH.cjs.map +0 -1
- package/dist/chunk-TZRUHQOH.cjs +0 -91
- package/dist/chunk-TZRUHQOH.cjs.map +0 -1
- package/dist/chunk-UTYVIOWZ.js +0 -3957
- package/dist/chunk-UTYVIOWZ.js.map +0 -1
- package/dist/chunk-W2LDIX26.cjs +0 -165
- package/dist/chunk-W2LDIX26.cjs.map +0 -1
- package/dist/chunk-WSG64BVN.cjs +0 -219
- package/dist/chunk-WSG64BVN.cjs.map +0 -1
- package/dist/chunk-WTQYGUNF.js +0 -400
- package/dist/chunk-WTQYGUNF.js.map +0 -1
- package/dist/chunk-WYK7EL5R.cjs +0 -895
- package/dist/chunk-WYK7EL5R.cjs.map +0 -1
- package/dist/chunk-XC4KHJYX.cjs +0 -617
- package/dist/chunk-XC4KHJYX.cjs.map +0 -1
- package/dist/chunk-XVBH5XHE.cjs +0 -400
- package/dist/chunk-XVBH5XHE.cjs.map +0 -1
- package/dist/chunk-XWYZIFZW.js +0 -66
- package/dist/chunk-XWYZIFZW.js.map +0 -1
- package/dist/chunk-Y3NNQMAX.cjs +0 -194
- package/dist/chunk-Y3NNQMAX.cjs.map +0 -1
- package/dist/chunk-YAFQVVDI.js +0 -488
- package/dist/chunk-YAFQVVDI.js.map +0 -1
- package/dist/components/legend/index.cjs +0 -12
- package/dist/components/legend/index.cjs.map +0 -1
- package/dist/components/legend/index.css +0 -91
- package/dist/components/legend/index.css.map +0 -1
- package/dist/components/legend/index.d.cts +0 -37
- package/dist/components/legend/index.d.ts +0 -37
- package/dist/components/legend/index.js +0 -12
- package/dist/components/legend/index.js.map +0 -1
- package/dist/components/tooltip/index.cjs +0 -12
- package/dist/components/tooltip/index.cjs.map +0 -1
- package/dist/components/tooltip/index.css +0 -13
- package/dist/components/tooltip/index.css.map +0 -1
- package/dist/components/tooltip/index.d.cts +0 -71
- package/dist/components/tooltip/index.d.ts +0 -71
- package/dist/components/tooltip/index.js +0 -12
- package/dist/components/tooltip/index.js.map +0 -1
- package/dist/components/trend-indicator/index.cjs +0 -8
- package/dist/components/trend-indicator/index.cjs.map +0 -1
- package/dist/components/trend-indicator/index.css +0 -27
- package/dist/components/trend-indicator/index.css.map +0 -1
- package/dist/components/trend-indicator/index.d.cts +0 -44
- package/dist/components/trend-indicator/index.d.ts +0 -44
- package/dist/components/trend-indicator/index.js +0 -8
- package/dist/components/trend-indicator/index.js.map +0 -1
- package/dist/format-metric-value-MXm5DtQ_.d.cts +0 -24
- package/dist/format-metric-value-MXm5DtQ_.d.ts +0 -24
- package/dist/hooks/index.cjs +0 -29
- package/dist/hooks/index.cjs.map +0 -1
- package/dist/hooks/index.css +0 -9
- package/dist/hooks/index.css.map +0 -1
- package/dist/hooks/index.d.cts +0 -264
- package/dist/hooks/index.d.ts +0 -264
- package/dist/hooks/index.js +0 -29
- package/dist/hooks/index.js.map +0 -1
- package/dist/leaderboard-chart-BSbg0ufV.d.cts +0 -79
- package/dist/leaderboard-chart-odEYxxEC.d.ts +0 -79
- package/dist/legend-DFkosEvC.d.cts +0 -9
- package/dist/legend-DLswHhOk.d.ts +0 -9
- package/dist/providers/index.cjs +0 -21
- package/dist/providers/index.cjs.map +0 -1
- package/dist/providers/index.css +0 -9
- package/dist/providers/index.css.map +0 -1
- package/dist/providers/index.d.cts +0 -28
- package/dist/providers/index.d.ts +0 -28
- package/dist/providers/index.js +0 -21
- package/dist/providers/index.js.map +0 -1
- package/dist/themes-D0qc5JaW.d.cts +0 -67
- package/dist/themes-itO4Ht5g.d.ts +0 -67
- package/dist/types-B5f6XQ7Q.d.cts +0 -19
- package/dist/types-BsHooDbM.d.ts +0 -19
- package/dist/types-BuSrRM4p.d.ts +0 -49
- package/dist/types-ChOUI9-N.d.cts +0 -545
- package/dist/types-ChOUI9-N.d.ts +0 -545
- package/dist/types-Dfw9VOKI.d.cts +0 -49
- package/dist/utils/index.cjs +0 -44
- package/dist/utils/index.cjs.map +0 -1
- package/dist/utils/index.d.cts +0 -226
- package/dist/utils/index.d.ts +0 -226
- package/dist/utils/index.js +0 -44
- package/dist/utils/index.js.map +0 -1
- package/dist/with-responsive-CNfhzAUu.d.cts +0 -18
- package/dist/with-responsive-CNfhzAUu.d.ts +0 -18
|
@@ -18,13 +18,11 @@ const mockData = [
|
|
|
18
18
|
label: 'Category A',
|
|
19
19
|
value: 30,
|
|
20
20
|
valueDisplay: '30%',
|
|
21
|
-
percentage: 30,
|
|
22
21
|
},
|
|
23
22
|
{
|
|
24
23
|
label: 'Category B',
|
|
25
24
|
value: 70,
|
|
26
25
|
valueDisplay: '70%',
|
|
27
|
-
percentage: 70,
|
|
28
26
|
},
|
|
29
27
|
];
|
|
30
28
|
|
|
@@ -64,9 +62,9 @@ describe( 'PieSemiCircleChart', () => {
|
|
|
64
62
|
it( 'shows tooltip on segment hover when withTooltips is true', async () => {
|
|
65
63
|
const user = userEvent.setup();
|
|
66
64
|
const testData = [
|
|
67
|
-
{ label: 'MacOS', value: 30000, valueDisplay: '30K'
|
|
68
|
-
{ label: 'Linux', value: 22000, valueDisplay: '22K'
|
|
69
|
-
{ label: 'Windows', value: 80000, valueDisplay: '80K'
|
|
65
|
+
{ label: 'MacOS', value: 30000, valueDisplay: '30K' },
|
|
66
|
+
{ label: 'Linux', value: 22000, valueDisplay: '22K' },
|
|
67
|
+
{ label: 'Windows', value: 80000, valueDisplay: '80K' },
|
|
70
68
|
];
|
|
71
69
|
|
|
72
70
|
renderPieChart( { data: testData, withTooltips: true, width: 400 } );
|
|
@@ -86,9 +84,9 @@ describe( 'PieSemiCircleChart', () => {
|
|
|
86
84
|
it( 'hides tooltip on mouse leave', async () => {
|
|
87
85
|
const user = userEvent.setup();
|
|
88
86
|
const testData = [
|
|
89
|
-
{ label: 'MacOS', value: 30000, valueDisplay: '30K'
|
|
90
|
-
{ label: 'Linux', value: 22000, valueDisplay: '22K'
|
|
91
|
-
{ label: 'Windows', value: 80000, valueDisplay: '80K'
|
|
87
|
+
{ label: 'MacOS', value: 30000, valueDisplay: '30K' },
|
|
88
|
+
{ label: 'Linux', value: 22000, valueDisplay: '22K' },
|
|
89
|
+
{ label: 'Windows', value: 80000, valueDisplay: '80K' },
|
|
92
90
|
];
|
|
93
91
|
|
|
94
92
|
renderPieChart( { data: testData, withTooltips: true, width: 400 } );
|
|
@@ -113,9 +111,9 @@ describe( 'PieSemiCircleChart', () => {
|
|
|
113
111
|
it( 'renders custom tooltip when renderTooltip prop is provided', async () => {
|
|
114
112
|
const user = userEvent.setup();
|
|
115
113
|
const testData = [
|
|
116
|
-
{ label: 'MacOS', value: 30000, valueDisplay: '30K'
|
|
117
|
-
{ label: 'Linux', value: 22000, valueDisplay: '22K'
|
|
118
|
-
{ label: 'Windows', value: 80000, valueDisplay: '80K'
|
|
114
|
+
{ label: 'MacOS', value: 30000, valueDisplay: '30K' },
|
|
115
|
+
{ label: 'Linux', value: 22000, valueDisplay: '22K' },
|
|
116
|
+
{ label: 'Windows', value: 80000, valueDisplay: '80K' },
|
|
119
117
|
];
|
|
120
118
|
|
|
121
119
|
const customTooltipRenderer = jest.fn( ( { tooltipData } ) => (
|
|
@@ -148,10 +146,12 @@ describe( 'PieSemiCircleChart', () => {
|
|
|
148
146
|
tooltipData: expect.objectContaining( {
|
|
149
147
|
label: 'MacOS',
|
|
150
148
|
value: 30000,
|
|
151
|
-
percentage: 5,
|
|
152
149
|
} ),
|
|
153
150
|
} )
|
|
154
151
|
);
|
|
152
|
+
// Verify percentage is calculated (approximately 22.73%)
|
|
153
|
+
const callArgs = customTooltipRenderer.mock.calls[ 0 ][ 0 ];
|
|
154
|
+
expect( callArgs.tooltipData.percentage ).toBeCloseTo( 22.73, 1 );
|
|
155
155
|
} );
|
|
156
156
|
|
|
157
157
|
it( 'applies custom className', () => {
|
|
@@ -193,21 +193,21 @@ describe( 'PieSemiCircleChart', () => {
|
|
|
193
193
|
expect( screen.getByText( 'No data available' ) ).toBeInTheDocument();
|
|
194
194
|
} );
|
|
195
195
|
|
|
196
|
-
test( 'handles zero total
|
|
196
|
+
test( 'handles zero total value', () => {
|
|
197
197
|
renderPieChart( {
|
|
198
198
|
data: [
|
|
199
|
-
{ label: 'A', value: 0
|
|
200
|
-
{ label: 'B', value: 0
|
|
199
|
+
{ label: 'A', value: 0 },
|
|
200
|
+
{ label: 'B', value: 0 },
|
|
201
201
|
],
|
|
202
202
|
} );
|
|
203
203
|
expect(
|
|
204
|
-
screen.getByText( 'Invalid
|
|
204
|
+
screen.getByText( 'Invalid data: Total value must be greater than 0' )
|
|
205
205
|
).toBeInTheDocument();
|
|
206
206
|
} );
|
|
207
207
|
|
|
208
208
|
test( 'handles single data point', () => {
|
|
209
209
|
renderPieChart( {
|
|
210
|
-
data: [ { label: 'Single', value: 100
|
|
210
|
+
data: [ { label: 'Single', value: 100 } ],
|
|
211
211
|
} );
|
|
212
212
|
expect( screen.getByTestId( 'pie-segment' ) ).toBeInTheDocument();
|
|
213
213
|
} );
|
|
@@ -215,8 +215,8 @@ describe( 'PieSemiCircleChart', () => {
|
|
|
215
215
|
test( 'handles negative values', () => {
|
|
216
216
|
renderPieChart( {
|
|
217
217
|
data: [
|
|
218
|
-
{ label: 'A', value: -30
|
|
219
|
-
{ label: 'B', value: 130
|
|
218
|
+
{ label: 'A', value: -30 },
|
|
219
|
+
{ label: 'B', value: 130 },
|
|
220
220
|
],
|
|
221
221
|
} );
|
|
222
222
|
expect(
|
|
@@ -288,8 +288,8 @@ describe( 'PieSemiCircleChart', () => {
|
|
|
288
288
|
test( 'filters segments when interactive legend is enabled and segment is toggled', async () => {
|
|
289
289
|
const user = userEvent.setup();
|
|
290
290
|
const testData = [
|
|
291
|
-
{ label: 'Segment A', value: 50
|
|
292
|
-
{ label: 'Segment B', value: 50
|
|
291
|
+
{ label: 'Segment A', value: 50 },
|
|
292
|
+
{ label: 'Segment B', value: 50 },
|
|
293
293
|
];
|
|
294
294
|
|
|
295
295
|
renderPieChart( {
|
|
@@ -320,8 +320,8 @@ describe( 'PieSemiCircleChart', () => {
|
|
|
320
320
|
test( 'shows empty state when all segments are hidden', async () => {
|
|
321
321
|
const user = userEvent.setup();
|
|
322
322
|
const testData = [
|
|
323
|
-
{ label: 'Segment A', value: 50
|
|
324
|
-
{ label: 'Segment B', value: 50
|
|
323
|
+
{ label: 'Segment A', value: 50 },
|
|
324
|
+
{ label: 'Segment B', value: 50 },
|
|
325
325
|
];
|
|
326
326
|
|
|
327
327
|
renderPieChart( {
|
|
@@ -347,8 +347,8 @@ describe( 'PieSemiCircleChart', () => {
|
|
|
347
347
|
|
|
348
348
|
test( 'does not filter segments when legendInteractive is false', () => {
|
|
349
349
|
const testData = [
|
|
350
|
-
{ label: 'Segment A', value: 50
|
|
351
|
-
{ label: 'Segment B', value: 50
|
|
350
|
+
{ label: 'Segment A', value: 50 },
|
|
351
|
+
{ label: 'Segment B', value: 50 },
|
|
352
352
|
];
|
|
353
353
|
|
|
354
354
|
renderPieChart( {
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Stack } from '@wordpress/ui';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { useElementSize } from '../../../hooks';
|
|
4
|
+
import { renderLegendSlot } from '../chart-composition';
|
|
5
|
+
import styles from './chart-layout.module.scss';
|
|
6
|
+
import type { LegendPosition } from '../../../types';
|
|
7
|
+
import type { LegendChild } from '../chart-composition/use-chart-children';
|
|
8
|
+
import type { GapSize } from '@wordpress/theme';
|
|
9
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Measurements provided to the render prop when ChartLayout handles resize listening.
|
|
13
|
+
*/
|
|
14
|
+
export interface ContentMeasurements {
|
|
15
|
+
/** Measured width of the content area in pixels */
|
|
16
|
+
contentWidth: number;
|
|
17
|
+
/** Measured height of the content area in pixels */
|
|
18
|
+
contentHeight: number;
|
|
19
|
+
/** True when a non-zero contentHeight measurement is available */
|
|
20
|
+
isMeasured: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ChartLayoutProps {
|
|
24
|
+
/** Position for the prop-based legend element */
|
|
25
|
+
legendPosition: LegendPosition;
|
|
26
|
+
/** The legend element rendered via the showLegend prop (false when hidden) */
|
|
27
|
+
legendElement?: ReactNode;
|
|
28
|
+
/** Legend children from the composition API */
|
|
29
|
+
legendChildren: LegendChild[];
|
|
30
|
+
/** Chart content — either a ReactNode or a render prop receiving content measurements */
|
|
31
|
+
children: ReactNode | ( ( measurements: ContentMeasurements ) => ReactNode );
|
|
32
|
+
/** Content rendered after the bottom legend (e.g., nonLegendChildren, htmlChildren, tooltips) */
|
|
33
|
+
trailingContent?: ReactNode;
|
|
34
|
+
/** Called when the measured content height changes (for render-prop mode) */
|
|
35
|
+
onContentHeightChange?: ( height: number ) => void;
|
|
36
|
+
/** Gap between Stack items */
|
|
37
|
+
gap?: GapSize;
|
|
38
|
+
/** Additional class names */
|
|
39
|
+
className?: string;
|
|
40
|
+
/** Inline styles (width, height, etc.) */
|
|
41
|
+
style?: CSSProperties;
|
|
42
|
+
/** Test ID for the container */
|
|
43
|
+
'data-testid'?: string;
|
|
44
|
+
/** Chart ID attribute */
|
|
45
|
+
'data-chart-id'?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const ChartLayout = ( {
|
|
49
|
+
legendPosition,
|
|
50
|
+
legendElement,
|
|
51
|
+
legendChildren,
|
|
52
|
+
children,
|
|
53
|
+
trailingContent,
|
|
54
|
+
onContentHeightChange,
|
|
55
|
+
gap,
|
|
56
|
+
className,
|
|
57
|
+
style,
|
|
58
|
+
'data-testid': dataTestId,
|
|
59
|
+
'data-chart-id': dataChartId,
|
|
60
|
+
}: ChartLayoutProps ) => {
|
|
61
|
+
const [ contentRef, contentWidth, contentHeight ] = useElementSize< HTMLDivElement >();
|
|
62
|
+
const isRenderProp = typeof children === 'function';
|
|
63
|
+
const isMeasured = contentHeight > 0;
|
|
64
|
+
|
|
65
|
+
// When using render-prop children, hide the layout until measurement is available
|
|
66
|
+
// to prevent layout shift. Plain ReactNode children don't need this since they
|
|
67
|
+
// don't depend on measured dimensions.
|
|
68
|
+
const visibilityStyle: { visibility?: 'hidden' | 'visible' } =
|
|
69
|
+
isRenderProp && ! isMeasured ? { visibility: 'hidden' } : {};
|
|
70
|
+
|
|
71
|
+
useEffect( () => {
|
|
72
|
+
if ( isRenderProp && onContentHeightChange && isMeasured ) {
|
|
73
|
+
onContentHeightChange( contentHeight );
|
|
74
|
+
}
|
|
75
|
+
}, [ isRenderProp, contentHeight, isMeasured, onContentHeightChange ] );
|
|
76
|
+
const renderedChildren = isRenderProp
|
|
77
|
+
? children( { contentWidth, contentHeight, isMeasured } )
|
|
78
|
+
: children;
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<Stack
|
|
82
|
+
direction="column"
|
|
83
|
+
gap={ gap }
|
|
84
|
+
className={ className }
|
|
85
|
+
style={ { ...style, ...visibilityStyle } }
|
|
86
|
+
data-testid={ dataTestId }
|
|
87
|
+
data-chart-id={ dataChartId }
|
|
88
|
+
>
|
|
89
|
+
{ legendPosition === 'top' && legendElement }
|
|
90
|
+
{ renderLegendSlot( legendChildren, 'top' ) }
|
|
91
|
+
|
|
92
|
+
{ isRenderProp ? (
|
|
93
|
+
<div ref={ contentRef } className={ styles[ 'chart-layout__content' ] }>
|
|
94
|
+
{ renderedChildren }
|
|
95
|
+
</div>
|
|
96
|
+
) : (
|
|
97
|
+
renderedChildren
|
|
98
|
+
) }
|
|
99
|
+
|
|
100
|
+
{ legendPosition === 'bottom' && legendElement }
|
|
101
|
+
{ renderLegendSlot( legendChildren, 'bottom' ) }
|
|
102
|
+
|
|
103
|
+
{ trailingContent }
|
|
104
|
+
</Stack>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { renderLegendSlot } from '../../chart-composition';
|
|
3
|
+
import { ChartLayout } from '../chart-layout';
|
|
4
|
+
import type { LegendChild } from '../../chart-composition/use-chart-children';
|
|
5
|
+
|
|
6
|
+
// Mock renderLegendSlot since we test it separately
|
|
7
|
+
jest.mock( '../../chart-composition', () => ( {
|
|
8
|
+
renderLegendSlot: jest.fn( () => [] ),
|
|
9
|
+
} ) );
|
|
10
|
+
|
|
11
|
+
const mockRenderLegendSlot = renderLegendSlot as jest.Mock;
|
|
12
|
+
|
|
13
|
+
describe( 'ChartLayout', () => {
|
|
14
|
+
beforeEach( () => {
|
|
15
|
+
mockRenderLegendSlot.mockReturnValue( [] );
|
|
16
|
+
} );
|
|
17
|
+
|
|
18
|
+
it( 'renders children inside a column Stack', () => {
|
|
19
|
+
render(
|
|
20
|
+
<ChartLayout legendPosition="bottom" legendChildren={ [] }>
|
|
21
|
+
<div data-testid="chart-content">Chart</div>
|
|
22
|
+
</ChartLayout>
|
|
23
|
+
);
|
|
24
|
+
expect( screen.getByTestId( 'chart-content' ) ).toBeInTheDocument();
|
|
25
|
+
} );
|
|
26
|
+
|
|
27
|
+
it( 'renders legend element at top when legendPosition is top', () => {
|
|
28
|
+
const legendElement = <div data-testid="legend">Legend</div>;
|
|
29
|
+
render(
|
|
30
|
+
<ChartLayout legendPosition="top" legendElement={ legendElement } legendChildren={ [] }>
|
|
31
|
+
<div data-testid="chart-content">Chart</div>
|
|
32
|
+
</ChartLayout>
|
|
33
|
+
);
|
|
34
|
+
const legend = screen.getByTestId( 'legend' );
|
|
35
|
+
const content = screen.getByTestId( 'chart-content' );
|
|
36
|
+
expect( legend.compareDocumentPosition( content ) ).toBe( Node.DOCUMENT_POSITION_FOLLOWING );
|
|
37
|
+
} );
|
|
38
|
+
|
|
39
|
+
it( 'renders legend element at bottom when legendPosition is bottom', () => {
|
|
40
|
+
const legendElement = <div data-testid="legend">Legend</div>;
|
|
41
|
+
render(
|
|
42
|
+
<ChartLayout legendPosition="bottom" legendElement={ legendElement } legendChildren={ [] }>
|
|
43
|
+
<div data-testid="chart-content">Chart</div>
|
|
44
|
+
</ChartLayout>
|
|
45
|
+
);
|
|
46
|
+
const content = screen.getByTestId( 'chart-content' );
|
|
47
|
+
const legend = screen.getByTestId( 'legend' );
|
|
48
|
+
expect( content.compareDocumentPosition( legend ) ).toBe( Node.DOCUMENT_POSITION_FOLLOWING );
|
|
49
|
+
} );
|
|
50
|
+
|
|
51
|
+
it( 'does not render legend element when it is false/null', () => {
|
|
52
|
+
render(
|
|
53
|
+
<ChartLayout legendPosition="top" legendElement={ false } legendChildren={ [] }>
|
|
54
|
+
<div data-testid="chart-content">Chart</div>
|
|
55
|
+
</ChartLayout>
|
|
56
|
+
);
|
|
57
|
+
expect( screen.queryByTestId( 'legend' ) ).not.toBeInTheDocument();
|
|
58
|
+
} );
|
|
59
|
+
|
|
60
|
+
it( 'calls renderLegendSlot for both positions', () => {
|
|
61
|
+
const legendChildren: LegendChild[] = [];
|
|
62
|
+
render(
|
|
63
|
+
<ChartLayout legendPosition="bottom" legendChildren={ legendChildren }>
|
|
64
|
+
<div>Chart</div>
|
|
65
|
+
</ChartLayout>
|
|
66
|
+
);
|
|
67
|
+
expect( mockRenderLegendSlot ).toHaveBeenCalledWith( legendChildren, 'top' );
|
|
68
|
+
expect( mockRenderLegendSlot ).toHaveBeenCalledWith( legendChildren, 'bottom' );
|
|
69
|
+
} );
|
|
70
|
+
|
|
71
|
+
it( 'hides layout until measured when using render-prop children', () => {
|
|
72
|
+
// Override the global getBoundingClientRect mock to return zero (unmeasured state)
|
|
73
|
+
// for elements inside this test. This simulates the initial state before
|
|
74
|
+
// ResizeObserver provides real dimensions in a browser.
|
|
75
|
+
const originalGetBoundingClientRect = Element.prototype.getBoundingClientRect;
|
|
76
|
+
Element.prototype.getBoundingClientRect = function () {
|
|
77
|
+
return { width: 0, height: 0, top: 0, left: 0, bottom: 0, right: 0, x: 0, y: 0 } as DOMRect;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const childFn = jest.fn().mockReturnValue( <div>Chart</div> );
|
|
82
|
+
render(
|
|
83
|
+
<ChartLayout legendPosition="bottom" legendChildren={ [] } data-testid="layout">
|
|
84
|
+
{ childFn }
|
|
85
|
+
</ChartLayout>
|
|
86
|
+
);
|
|
87
|
+
// When contentHeight is 0, layout should be hidden to prevent layout shift
|
|
88
|
+
expect( screen.getByTestId( 'layout' ) ).toHaveStyle( { visibility: 'hidden' } );
|
|
89
|
+
} finally {
|
|
90
|
+
Element.prototype.getBoundingClientRect = originalGetBoundingClientRect;
|
|
91
|
+
}
|
|
92
|
+
} );
|
|
93
|
+
|
|
94
|
+
it( 'does not hide layout when using plain ReactNode children', () => {
|
|
95
|
+
render(
|
|
96
|
+
<ChartLayout legendPosition="bottom" legendChildren={ [] } data-testid="layout">
|
|
97
|
+
<div>Chart</div>
|
|
98
|
+
</ChartLayout>
|
|
99
|
+
);
|
|
100
|
+
const layoutStyle = screen.getByTestId( 'layout' ).getAttribute( 'style' ) ?? '';
|
|
101
|
+
expect( layoutStyle ).not.toContain( 'visibility' );
|
|
102
|
+
} );
|
|
103
|
+
|
|
104
|
+
it( 'passes className and style to Stack', () => {
|
|
105
|
+
render(
|
|
106
|
+
<ChartLayout
|
|
107
|
+
legendPosition="bottom"
|
|
108
|
+
legendChildren={ [] }
|
|
109
|
+
className="my-chart"
|
|
110
|
+
style={ { width: 400, height: 300 } }
|
|
111
|
+
data-testid="layout"
|
|
112
|
+
>
|
|
113
|
+
<div>Chart</div>
|
|
114
|
+
</ChartLayout>
|
|
115
|
+
);
|
|
116
|
+
const layout = screen.getByTestId( 'layout' );
|
|
117
|
+
expect( layout ).toHaveClass( 'my-chart' );
|
|
118
|
+
expect( layout ).toHaveStyle( { width: '400px', height: '300px' } );
|
|
119
|
+
} );
|
|
120
|
+
|
|
121
|
+
it( 'passes gap to Stack', () => {
|
|
122
|
+
render(
|
|
123
|
+
<ChartLayout legendPosition="bottom" legendChildren={ [] } gap="lg" data-testid="layout">
|
|
124
|
+
<div>Chart</div>
|
|
125
|
+
</ChartLayout>
|
|
126
|
+
);
|
|
127
|
+
// Stack renders gap as a CSS class or style — just verify it renders without error
|
|
128
|
+
expect( screen.getByTestId( 'layout' ) ).toBeInTheDocument();
|
|
129
|
+
} );
|
|
130
|
+
|
|
131
|
+
it( 'calls function-as-children with measurement props', () => {
|
|
132
|
+
const childFn = jest.fn().mockReturnValue( <div data-testid="chart-content">Chart</div> );
|
|
133
|
+
|
|
134
|
+
render(
|
|
135
|
+
<ChartLayout legendPosition="bottom" legendChildren={ [] }>
|
|
136
|
+
{ childFn }
|
|
137
|
+
</ChartLayout>
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
expect( childFn ).toHaveBeenCalled();
|
|
141
|
+
const firstCallArg = childFn.mock.calls[ 0 ][ 0 ];
|
|
142
|
+
|
|
143
|
+
expect( firstCallArg ).toEqual(
|
|
144
|
+
expect.objectContaining( {
|
|
145
|
+
contentWidth: expect.any( Number ),
|
|
146
|
+
contentHeight: expect.any( Number ),
|
|
147
|
+
isMeasured: expect.any( Boolean ),
|
|
148
|
+
} )
|
|
149
|
+
);
|
|
150
|
+
} );
|
|
151
|
+
|
|
152
|
+
it( 'renders trailing content after bottom legend', () => {
|
|
153
|
+
render(
|
|
154
|
+
<ChartLayout
|
|
155
|
+
legendPosition="bottom"
|
|
156
|
+
legendElement={ <div data-testid="legend">Legend</div> }
|
|
157
|
+
legendChildren={ [] }
|
|
158
|
+
trailingContent={ <div data-testid="trailing">Extra</div> }
|
|
159
|
+
>
|
|
160
|
+
<div data-testid="chart-content">Chart</div>
|
|
161
|
+
</ChartLayout>
|
|
162
|
+
);
|
|
163
|
+
const legend = screen.getByTestId( 'legend' );
|
|
164
|
+
const trailing = screen.getByTestId( 'trailing' );
|
|
165
|
+
expect( legend.compareDocumentPosition( trailing ) ).toBe( Node.DOCUMENT_POSITION_FOLLOWING );
|
|
166
|
+
} );
|
|
167
|
+
} );
|
|
@@ -13,8 +13,8 @@ export interface ChartInstanceRef {
|
|
|
13
13
|
export interface ChartInstanceContextValue {
|
|
14
14
|
chartId: string;
|
|
15
15
|
chartRef?: React.RefObject< ChartInstanceRef >;
|
|
16
|
-
chartWidth
|
|
17
|
-
chartHeight
|
|
16
|
+
chartWidth?: number;
|
|
17
|
+
chartHeight?: number;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export const ChartInstanceContext = createContext< ChartInstanceContextValue | null >( null );
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SvgEmptyState } from './svg-empty-state';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Stack } from '@wordpress/ui';
|
|
2
|
+
import styles from './svg-empty-state.module.scss';
|
|
3
|
+
import type { FC, ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
interface SvgEmptyStateProps {
|
|
6
|
+
/** X coordinate of the center point */
|
|
7
|
+
x: number;
|
|
8
|
+
/** Y coordinate of the center point */
|
|
9
|
+
y: number;
|
|
10
|
+
/** Available width for the text area */
|
|
11
|
+
width: number;
|
|
12
|
+
/** Available height for the text area */
|
|
13
|
+
height: number;
|
|
14
|
+
/** Text content */
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Renders empty-state text inside an SVG using foreignObject so that the
|
|
20
|
+
* message wraps onto multiple lines instead of being clipped.
|
|
21
|
+
*
|
|
22
|
+
* The component centers the text within the specified area.
|
|
23
|
+
*
|
|
24
|
+
* @param root0 - Component props
|
|
25
|
+
* @param root0.x - X coordinate of the center point
|
|
26
|
+
* @param root0.y - Y coordinate of the center point
|
|
27
|
+
* @param root0.width - Available width for the text area
|
|
28
|
+
* @param root0.height - Available height for the text area
|
|
29
|
+
* @param root0.children - Text content
|
|
30
|
+
* @return {JSX.Element} A foreignObject element containing the centered text.
|
|
31
|
+
*/
|
|
32
|
+
export const SvgEmptyState: FC< SvgEmptyStateProps > = ( { x, y, width, height, children } ) => {
|
|
33
|
+
return (
|
|
34
|
+
<foreignObject x={ x - width / 2 } y={ y - height / 2 } width={ width } height={ height }>
|
|
35
|
+
<Stack align="center" justify="center" className={ styles[ 'svg-empty-state' ] }>
|
|
36
|
+
{ children }
|
|
37
|
+
</Stack>
|
|
38
|
+
</foreignObject>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { renderHook } from '@testing-library/react';
|
|
2
2
|
import { GlobalChartsProvider } from '../../../../providers';
|
|
3
3
|
import { useChartLegendItems } from '../use-chart-legend-items';
|
|
4
|
-
import type {
|
|
4
|
+
import type { DataPointPercentageCalculated, DataPointDate, SeriesData } from '../../../../types';
|
|
5
5
|
import type { ReactNode } from 'react';
|
|
6
6
|
|
|
7
7
|
// Wrapper component to provide GlobalChartsProvider context
|
|
@@ -11,8 +11,8 @@ const wrapper = ( { children }: { children: ReactNode } ) => (
|
|
|
11
11
|
|
|
12
12
|
describe( 'useChartLegendItems', () => {
|
|
13
13
|
describe( 'Number Formatting (i18n)', () => {
|
|
14
|
-
describe( '
|
|
15
|
-
const percentageData:
|
|
14
|
+
describe( 'DataPointPercentageCalculated', () => {
|
|
15
|
+
const percentageData: DataPointPercentageCalculated[] = [
|
|
16
16
|
{ label: 'Item 1', value: 80000, percentage: 60.6 },
|
|
17
17
|
{ label: 'Item 2', value: 30000, percentage: 22.7 },
|
|
18
18
|
{ label: 'Item 3', value: 22000, percentage: 16.7 },
|
|
@@ -50,7 +50,7 @@ describe( 'useChartLegendItems', () => {
|
|
|
50
50
|
} );
|
|
51
51
|
|
|
52
52
|
test( 'uses valueDisplay when provided, falling back to formatted value', () => {
|
|
53
|
-
const dataWithDisplay:
|
|
53
|
+
const dataWithDisplay: DataPointPercentageCalculated[] = [
|
|
54
54
|
{ label: 'Item 1', value: 80000, percentage: 60, valueDisplay: 'Custom 80K' },
|
|
55
55
|
{ label: 'Item 2', value: 30000, percentage: 30 },
|
|
56
56
|
];
|
|
@@ -194,7 +194,9 @@ describe( 'useChartLegendItems', () => {
|
|
|
194
194
|
} );
|
|
195
195
|
|
|
196
196
|
test( 'handles data with zero values', () => {
|
|
197
|
-
const zeroData:
|
|
197
|
+
const zeroData: DataPointPercentageCalculated[] = [
|
|
198
|
+
{ label: 'Zero Value', value: 0, percentage: 0 },
|
|
199
|
+
];
|
|
198
200
|
|
|
199
201
|
const { result } = renderHook(
|
|
200
202
|
() =>
|
|
@@ -209,7 +211,7 @@ describe( 'useChartLegendItems', () => {
|
|
|
209
211
|
} );
|
|
210
212
|
|
|
211
213
|
test( 'handles very large numbers', () => {
|
|
212
|
-
const largeData:
|
|
214
|
+
const largeData: DataPointPercentageCalculated[] = [
|
|
213
215
|
{ label: 'Large', value: 1234567890, percentage: 100 },
|
|
214
216
|
];
|
|
215
217
|
|
|
@@ -227,7 +229,7 @@ describe( 'useChartLegendItems', () => {
|
|
|
227
229
|
} );
|
|
228
230
|
|
|
229
231
|
test( 'handles decimal values', () => {
|
|
230
|
-
const decimalData:
|
|
232
|
+
const decimalData: DataPointPercentageCalculated[] = [
|
|
231
233
|
{ label: 'Decimal', value: 1234.5678, percentage: 100 },
|
|
232
234
|
];
|
|
233
235
|
|
|
@@ -247,7 +249,9 @@ describe( 'useChartLegendItems', () => {
|
|
|
247
249
|
|
|
248
250
|
describe( 'Label and Color', () => {
|
|
249
251
|
test( 'preserves label and color from data', () => {
|
|
250
|
-
const data:
|
|
252
|
+
const data: DataPointPercentageCalculated[] = [
|
|
253
|
+
{ label: 'Test Label', value: 100, percentage: 100 },
|
|
254
|
+
];
|
|
251
255
|
|
|
252
256
|
const { result } = renderHook(
|
|
253
257
|
() =>
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type ElementStyles,
|
|
7
7
|
} from '../../../providers';
|
|
8
8
|
import { formatPercentage } from '../../../utils';
|
|
9
|
-
import type { SeriesData, DataPointDate,
|
|
9
|
+
import type { SeriesData, DataPointDate, DataPointPercentageCalculated } from '../../../types';
|
|
10
10
|
import type { BaseLegendItem } from '../types';
|
|
11
11
|
import type { LegendShape } from '@visx/legend/lib/types';
|
|
12
12
|
import type { GlyphProps } from '@visx/xychart';
|
|
@@ -31,7 +31,7 @@ export interface ChartLegendOptions {
|
|
|
31
31
|
* @return Formatted value string
|
|
32
32
|
*/
|
|
33
33
|
function formatPointValue(
|
|
34
|
-
point: DataPointDate |
|
|
34
|
+
point: DataPointDate | DataPointPercentageCalculated,
|
|
35
35
|
showValues: boolean,
|
|
36
36
|
legendValueDisplay: LegendValueDisplay = 'percentage'
|
|
37
37
|
): string {
|
|
@@ -39,16 +39,15 @@ function formatPointValue(
|
|
|
39
39
|
return '';
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
// Handle
|
|
42
|
+
// Handle DataPointPercentageCalculated (pie chart data with calculated percentage)
|
|
43
43
|
if ( 'percentage' in point ) {
|
|
44
|
-
const percentagePoint = point as DataPointPercentage;
|
|
45
44
|
switch ( legendValueDisplay ) {
|
|
46
45
|
case 'percentage':
|
|
47
|
-
return formatPercentage(
|
|
46
|
+
return formatPercentage( point.percentage );
|
|
48
47
|
case 'value':
|
|
49
|
-
return formatNumber(
|
|
48
|
+
return formatNumber( point.value );
|
|
50
49
|
case 'valueDisplay':
|
|
51
|
-
return
|
|
50
|
+
return point.valueDisplay || formatNumber( point.value );
|
|
52
51
|
default:
|
|
53
52
|
return '';
|
|
54
53
|
}
|
|
@@ -145,7 +144,7 @@ function processSeriesData(
|
|
|
145
144
|
* @return Array of processed legend items
|
|
146
145
|
*/
|
|
147
146
|
function processPointData(
|
|
148
|
-
pointData: ( DataPointDate |
|
|
147
|
+
pointData: ( DataPointDate | DataPointPercentageCalculated )[],
|
|
149
148
|
getElementStyles: ( params: GetElementStylesParams ) => ElementStyles,
|
|
150
149
|
showValues: boolean,
|
|
151
150
|
legendValueDisplay: LegendValueDisplay,
|
|
@@ -154,9 +153,9 @@ function processPointData(
|
|
|
154
153
|
renderGlyph?: < Datum extends object >( props: GlyphProps< Datum > ) => ReactNode,
|
|
155
154
|
legendShape?: LegendShape< SeriesData[], number >
|
|
156
155
|
): BaseLegendItem[] {
|
|
157
|
-
const mapper = ( point: DataPointDate |
|
|
156
|
+
const mapper = ( point: DataPointDate | DataPointPercentageCalculated, index: number ) => {
|
|
158
157
|
const { color, glyph, shapeStyles } = getElementStyles( {
|
|
159
|
-
data: point as
|
|
158
|
+
data: point as DataPointPercentageCalculated,
|
|
160
159
|
index,
|
|
161
160
|
legendShape,
|
|
162
161
|
} );
|
|
@@ -182,7 +181,7 @@ function processPointData(
|
|
|
182
181
|
* @return Array of legend items ready for display
|
|
183
182
|
*/
|
|
184
183
|
export function useChartLegendItems<
|
|
185
|
-
T extends SeriesData[] | DataPointDate[] |
|
|
184
|
+
T extends SeriesData[] | DataPointDate[] | DataPointPercentageCalculated[],
|
|
186
185
|
>(
|
|
187
186
|
data: T,
|
|
188
187
|
options: ChartLegendOptions = {},
|
|
@@ -215,9 +214,9 @@ export function useChartLegendItems<
|
|
|
215
214
|
);
|
|
216
215
|
}
|
|
217
216
|
|
|
218
|
-
// Handle DataPointDate or
|
|
217
|
+
// Handle DataPointDate or DataPointPercentageCalculated (single data points)
|
|
219
218
|
return processPointData(
|
|
220
|
-
data as ( DataPointDate |
|
|
219
|
+
data as ( DataPointDate | DataPointPercentageCalculated )[],
|
|
221
220
|
getElementStyles,
|
|
222
221
|
showValues,
|
|
223
222
|
legendValueDisplay,
|