@automattic/charts 4.8.0 → 4.10.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/index.cjs +766 -277
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.css +72 -8
  5. package/dist/index.d.cts +54 -15
  6. package/dist/index.d.ts +54 -15
  7. package/dist/index.js +770 -281
  8. package/dist/index.js.map +1 -1
  9. package/package.json +1 -1
  10. package/src/charts/area-chart/area-chart.tsx +7 -3
  11. package/src/charts/area-chart/test/area-chart.test.tsx +15 -14
  12. package/src/charts/area-chart/test/tooltip-resolution.test.tsx +2 -2
  13. package/src/charts/bar-chart/bar-chart.tsx +12 -4
  14. package/src/charts/bar-chart/test/bar-chart.test.tsx +74 -63
  15. package/src/charts/bar-list-chart/bar-list-chart.tsx +3 -0
  16. package/src/charts/bar-list-chart/test/bar-list-chart.test.tsx +3 -1
  17. package/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx +23 -7
  18. package/src/charts/conversion-funnel-chart/test/conversion-funnel-chart.test.tsx +22 -0
  19. package/src/charts/conversion-funnel-chart/types.ts +1 -1
  20. package/src/charts/geo-chart/geo-chart.tsx +17 -2
  21. package/src/charts/geo-chart/test/geo-chart.test.tsx +9 -4
  22. package/src/charts/geo-chart/types.ts +1 -1
  23. package/src/charts/heatmap-chart/heatmap-chart.module.scss +10 -4
  24. package/src/charts/heatmap-chart/heatmap-chart.tsx +144 -37
  25. package/src/charts/heatmap-chart/private/column-groups.ts +79 -8
  26. package/src/charts/heatmap-chart/private/fit-compact-cells.ts +72 -0
  27. package/src/charts/heatmap-chart/private/heatmap-legend.tsx +14 -2
  28. package/src/charts/heatmap-chart/private/index.ts +4 -2
  29. package/src/charts/heatmap-chart/test/column-groups.test.ts +41 -4
  30. package/src/charts/heatmap-chart/test/fit-compact-cells.test.ts +59 -0
  31. package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +132 -7
  32. package/src/charts/heatmap-chart/types.ts +2 -2
  33. package/src/charts/leaderboard-chart/hooks/use-fitted-row-count.ts +2 -2
  34. package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +52 -4
  35. package/src/charts/leaderboard-chart/leaderboard-chart.tsx +60 -9
  36. package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +80 -1
  37. package/src/charts/leaderboard-chart/types.ts +7 -1
  38. package/src/charts/line-chart/line-chart.tsx +8 -2
  39. package/src/charts/line-chart/private/line-chart-annotation-label-popover.tsx +19 -9
  40. package/src/charts/line-chart/test/default-tooltip-formatting.test.tsx +1 -1
  41. package/src/charts/line-chart/test/line-chart.test.tsx +37 -35
  42. package/src/charts/line-chart/test/tooltip-resolution.test.tsx +2 -2
  43. package/src/charts/line-chart/types.ts +3 -3
  44. package/src/charts/pie-chart/pie-chart.module.scss +3 -0
  45. package/src/charts/pie-chart/pie-chart.tsx +144 -139
  46. package/src/charts/pie-chart/test/pie-chart.test.tsx +167 -0
  47. package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +3 -0
  48. package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +117 -120
  49. package/src/charts/pie-semi-circle-chart/test/pie-semi-circle-chart.test.tsx +54 -0
  50. package/src/charts/private/chart-composition/use-chart-children.ts +11 -6
  51. package/src/charts/private/pie-keyboard-navigation/index.ts +4 -0
  52. package/src/charts/private/pie-keyboard-navigation/order-arcs-for-navigation.ts +10 -0
  53. package/src/charts/private/pie-keyboard-navigation/pie-keyboard-navigation.module.scss +16 -0
  54. package/src/charts/private/pie-keyboard-navigation/pie-selection-announcement.tsx +14 -0
  55. package/src/charts/private/pie-keyboard-navigation/pie-selection-output.tsx +82 -0
  56. package/src/charts/private/pie-keyboard-navigation/pie-tooltip.tsx +49 -0
  57. package/src/charts/private/pie-keyboard-navigation/selected-segment-ring.tsx +47 -0
  58. package/src/charts/private/pie-keyboard-navigation/test/order-arcs-for-navigation.test.ts +34 -0
  59. package/src/charts/private/pie-keyboard-navigation/test/selected-segment-ring.test.ts +14 -0
  60. package/src/charts/private/pie-keyboard-navigation/use-pie-keyboard-navigation.ts +139 -0
  61. package/src/charts/private/x-zoom/test/x-zoom.test.tsx +3 -2
  62. package/src/charts/test/accessible-name.test.tsx +181 -0
  63. package/src/components/legend/hooks/test/use-chart-legend-items.test.tsx +63 -3
  64. package/src/components/legend/hooks/use-chart-legend-items.ts +63 -1
  65. package/src/components/legend/private/base-legend.tsx +27 -15
  66. package/src/components/legend/test/legend.test.tsx +42 -0
  67. package/src/components/legend/types.ts +2 -0
  68. package/src/components/tooltip/test/xy-chart-tooltip.test.tsx +20 -19
  69. package/src/components/tooltip/xy-chart-tooltip.tsx +3 -3
  70. package/src/hooks/test/use-xychart-theme.test.tsx +22 -0
  71. package/src/hooks/use-xychart-theme.ts +33 -2
  72. package/src/index.ts +1 -0
  73. package/src/types.ts +37 -3
  74. package/src/utils/resolve-font-size.ts +9 -10
  75. package/src/utils/test/resolve-font-size.test.ts +11 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/charts",
3
- "version": "4.8.0",
3
+ "version": "4.10.0",
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": {
@@ -52,6 +52,7 @@ const AreaChartInternal = forwardRef< ChartInstanceRef, AreaChartProps >(
52
52
  (
53
53
  {
54
54
  data,
55
+ ariaLabel,
55
56
  chartId: providedChartId,
56
57
  width,
57
58
  height,
@@ -261,8 +262,9 @@ const AreaChartInternal = forwardRef< ChartInstanceRef, AreaChartProps >(
261
262
  glyphSize: 0,
262
263
  collapseGroups: legend.collapseGroups ?? false,
263
264
  comparisonItem: legend.comparisonItem ?? false,
265
+ comparisonSeriesItems: legend.comparisonSeriesItems,
264
266
  } ),
265
- [ legend.collapseGroups, legend.comparisonItem ]
267
+ [ legend.collapseGroups, legend.comparisonItem, legend.comparisonSeriesItems ]
266
268
  );
267
269
  const legendItems = useChartLegendItems( dataSorted, legendOptions, legendShape );
268
270
 
@@ -421,10 +423,11 @@ const AreaChartInternal = forwardRef< ChartInstanceRef, AreaChartProps >(
421
423
  const chartHeight = contentHeight > 0 ? contentHeight : height;
422
424
 
423
425
  return (
426
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- the application role hands arrow keys to the chart's point navigation.
424
427
  <div
425
428
  ref={ chartRef }
426
- role="grid"
427
- aria-label={ __( 'Area chart', 'jetpack-charts' ) }
429
+ role="application"
430
+ aria-label={ ariaLabel ?? __( 'Area chart', 'jetpack-charts' ) }
428
431
  tabIndex={ 0 }
429
432
  onKeyDown={ onChartKeyDown }
430
433
  onFocus={ onChartFocus }
@@ -434,6 +437,7 @@ const AreaChartInternal = forwardRef< ChartInstanceRef, AreaChartProps >(
434
437
  <div className={ plotStyles[ 'xy-plot' ] }>
435
438
  { zoomable && zoom.domain && <ZoomResetButton onClick={ zoom.reset } /> }
436
439
  <XYChart
440
+ accessibilityLabel=""
437
441
  theme={ theme }
438
442
  width={ width }
439
443
  height={ chartHeight }
@@ -53,13 +53,13 @@ describe( 'AreaChart', () => {
53
53
  };
54
54
 
55
55
  test.each( [ [ 'Escape', '{Escape}' ] ] )(
56
- 'returns focus to the grid after %s',
56
+ 'returns focus to the chart after %s',
57
57
  async ( _name, keys ) => {
58
58
  jest.useFakeTimers();
59
59
  try {
60
60
  const user = userEvent.setup( { advanceTimers: jest.advanceTimersByTime } );
61
61
  renderWithProvider();
62
- const chart = screen.getByRole( 'grid', { name: /area chart/i } );
62
+ const chart = screen.getByRole( 'application', { name: /area chart/i } );
63
63
 
64
64
  await user.tab();
65
65
  expect( chart ).toHaveFocus();
@@ -139,7 +139,7 @@ describe( 'AreaChart', () => {
139
139
 
140
140
  test( 'renders with valid data', () => {
141
141
  renderWithProvider();
142
- expect( screen.getByRole( 'grid', { name: /area chart/i } ) ).toBeInTheDocument();
142
+ expect( screen.getByRole( 'application', { name: /area chart/i } ) ).toBeInTheDocument();
143
143
  } );
144
144
  } );
145
145
 
@@ -203,7 +203,7 @@ describe( 'AreaChart', () => {
203
203
  ],
204
204
  } );
205
205
 
206
- const chart = screen.getByRole( 'grid', { name: /area chart/i } );
206
+ const chart = screen.getByRole( 'application', { name: /area chart/i } );
207
207
  const ticks = within( chart )
208
208
  .getAllByText( /^-?[\d.,]+$/ )
209
209
  .map( el => el.textContent );
@@ -228,7 +228,7 @@ describe( 'AreaChart', () => {
228
228
  },
229
229
  } );
230
230
 
231
- const chart = screen.getByRole( 'grid', { name: /area chart/i } );
231
+ const chart = screen.getByRole( 'application', { name: /area chart/i } );
232
232
  expect( within( chart ).getAllByText( /^\d+%$/ ) ).toHaveLength( 6 );
233
233
  } );
234
234
 
@@ -254,7 +254,7 @@ describe( 'AreaChart', () => {
254
254
  ],
255
255
  } );
256
256
 
257
- const chart = screen.getByRole( 'grid', { name: /area chart/i } );
257
+ const chart = screen.getByRole( 'application', { name: /area chart/i } );
258
258
  expect( within( chart ).getAllByText( /^-?[\d.,]+$/ ).length ).toBeGreaterThan( 2 );
259
259
  } );
260
260
  } );
@@ -275,7 +275,7 @@ describe( 'AreaChart', () => {
275
275
 
276
276
  test( 'accepts custom stackOffset', () => {
277
277
  renderWithProvider( { stackOffset: 'expand' } );
278
- expect( screen.getByRole( 'grid', { name: /area chart/i } ) ).toBeInTheDocument();
278
+ expect( screen.getByRole( 'application', { name: /area chart/i } ) ).toBeInTheDocument();
279
279
  } );
280
280
  } );
281
281
 
@@ -344,7 +344,7 @@ describe( 'AreaChart', () => {
344
344
 
345
345
  // Open a tooltip via keyboard navigation, then verify the hidden
346
346
  // series' label is absent from the rendered tooltip rows.
347
- const chart = screen.getByRole( 'grid', { name: /area chart/i } );
347
+ const chart = screen.getByRole( 'application', { name: /area chart/i } );
348
348
  chart.focus();
349
349
  await user.keyboard( '{ArrowRight}' );
350
350
 
@@ -364,7 +364,7 @@ describe( 'AreaChart', () => {
364
364
  } );
365
365
 
366
366
  await user.click( screen.getByText( 'Series A' ) );
367
- const chart = screen.getByRole( 'grid', { name: /area chart/i } );
367
+ const chart = screen.getByRole( 'application', { name: /area chart/i } );
368
368
  chart.focus();
369
369
  await user.keyboard( '{ArrowRight}' );
370
370
 
@@ -762,7 +762,7 @@ describe( 'AreaChart', () => {
762
762
  context?.toggleSeriesVisibility( chartId, 'Series A' );
763
763
  } );
764
764
 
765
- const chart = screen.getByRole( 'grid', { name: /area chart/i } );
765
+ const chart = screen.getByRole( 'application', { name: /area chart/i } );
766
766
  chart.focus();
767
767
  await user.keyboard( '{ArrowRight}' );
768
768
 
@@ -775,16 +775,17 @@ describe( 'AreaChart', () => {
775
775
  describe( 'Without GlobalChartsProvider', () => {
776
776
  test( 'self-wraps in a provider when none is present', () => {
777
777
  render( <AreaChartUnresponsive { ...defaultProps } /> );
778
- expect( screen.getByRole( 'grid', { name: /area chart/i } ) ).toBeInTheDocument();
778
+ expect( screen.getByRole( 'application', { name: /area chart/i } ) ).toBeInTheDocument();
779
779
  } );
780
780
  } );
781
781
 
782
782
  describe( 'Accessibility', () => {
783
783
  test( 'chart container has expected ARIA attributes', () => {
784
784
  renderWithProvider();
785
- const chart = screen.getByRole( 'grid', { name: /area chart/i } );
785
+ const chart = screen.getByRole( 'application', { name: /area chart/i } );
786
786
  expect( chart ).toHaveAttribute( 'tabIndex', '0' );
787
787
  expect( chart ).toHaveAttribute( 'aria-label', 'Area chart' );
788
+ expect( screen.queryByLabelText( 'XYChart' ) ).not.toBeInTheDocument();
788
789
  } );
789
790
  } );
790
791
 
@@ -822,7 +823,7 @@ describe( 'AreaChart', () => {
822
823
  // surface the visx TooltipContext state in a jsdom environment.
823
824
  const focusFirstDatum = async () => {
824
825
  const user = userEvent.setup();
825
- const chart = screen.getByRole( 'grid', { name: /area chart/i } );
826
+ const chart = screen.getByRole( 'application', { name: /area chart/i } );
826
827
  chart.focus();
827
828
  await user.keyboard( '{ArrowRight}' );
828
829
  };
@@ -972,7 +973,7 @@ describe( 'AreaChart', () => {
972
973
 
973
974
  // The visx grid has no role/testid, so query its internal class from within the plot.
974
975
  /* eslint-disable testing-library/no-node-access */
975
- const plot = screen.getByRole( 'grid' );
976
+ const plot = screen.getByRole( 'application' );
976
977
  expect( plot.querySelector( '.visx-rows' ) ).toBeInTheDocument();
977
978
 
978
979
  const buttons = screen.getAllByRole( 'button' );
@@ -28,7 +28,7 @@ describe( 'area chart tooltip bucket info', () => {
28
28
  </GlobalChartsProvider>
29
29
  );
30
30
 
31
- screen.getByRole( 'grid', { name: /area chart/i } ).focus();
31
+ screen.getByRole( 'application', { name: /area chart/i } ).focus();
32
32
  await user.keyboard( '{ArrowRight}' );
33
33
 
34
34
  expect( screen.getByTestId( 'chart-tooltip-0' ) ).toHaveTextContent( '2.8.2026, 09 Uhr' );
@@ -61,7 +61,7 @@ describe( 'area chart tooltip bucket info', () => {
61
61
  </GlobalChartsProvider>
62
62
  );
63
63
 
64
- screen.getByRole( 'grid', { name: /area chart/i } ).focus();
64
+ screen.getByRole( 'application', { name: /area chart/i } ).focus();
65
65
  await user.keyboard( '{ArrowRight}' );
66
66
 
67
67
  expect( screen.getByTestId( 'chart-tooltip-0' ) ).not.toHaveTextContent( 'Uhr' );
@@ -100,6 +100,7 @@ const renderTooltipRow = ( label: string | undefined, value: string ) => (
100
100
 
101
101
  const BarChartInternal: FC< BarChartProps > = ( {
102
102
  data,
103
+ ariaLabel,
103
104
  chartId: providedChartId,
104
105
  width,
105
106
  height,
@@ -138,6 +139,7 @@ const BarChartInternal: FC< BarChartProps > = ( {
138
139
  const legendInteractive = legend.interactive ?? false;
139
140
  const legendCollapseGroups = legend.collapseGroups ?? false;
140
141
  const legendComparisonItem = legend.comparisonItem ?? false;
142
+ const legendComparisonSeriesItems = legend.comparisonSeriesItems;
141
143
  const horizontal = orientation === 'horizontal';
142
144
  const chartId = useChartId( providedChartId );
143
145
  const hiddenSeries = useDefaultHiddenSeries( chartId, defaultHiddenSeries );
@@ -158,8 +160,12 @@ const BarChartInternal: FC< BarChartProps > = ( {
158
160
 
159
161
  // Create legend items using the reusable hook
160
162
  const legendOptions = useMemo(
161
- () => ( { collapseGroups: legendCollapseGroups, comparisonItem: legendComparisonItem } ),
162
- [ legendCollapseGroups, legendComparisonItem ]
163
+ () => ( {
164
+ collapseGroups: legendCollapseGroups,
165
+ comparisonItem: legendComparisonItem,
166
+ comparisonSeriesItems: legendComparisonSeriesItems,
167
+ } ),
168
+ [ legendCollapseGroups, legendComparisonItem, legendComparisonSeriesItems ]
163
169
  );
164
170
  const legendItems = useChartLegendItems( dataSorted, legendOptions );
165
171
 
@@ -603,10 +609,11 @@ const BarChartInternal: FC< BarChartProps > = ( {
603
609
  const chartHeight = contentHeight > 0 ? contentHeight : height;
604
610
 
605
611
  return (
612
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- the application role hands arrow keys to the chart's point navigation.
606
613
  <div
607
- role="grid"
614
+ role="application"
608
615
  ref={ chartRef }
609
- aria-label={ __( 'Bar chart', 'jetpack-charts' ) }
616
+ aria-label={ ariaLabel ?? __( 'Bar chart', 'jetpack-charts' ) }
610
617
  tabIndex={ 0 }
611
618
  onKeyDown={ onChartKeyDown }
612
619
  onFocus={ onChartFocus }
@@ -615,6 +622,7 @@ const BarChartInternal: FC< BarChartProps > = ( {
615
622
  { chartHeight > 0 && (
616
623
  <div className={ plotStyles[ 'xy-plot' ] }>
617
624
  <XYChart
625
+ accessibilityLabel=""
618
626
  theme={ theme }
619
627
  width={ width }
620
628
  height={ chartHeight }