@automattic/charts 4.2.0 → 4.3.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 (45) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/index.cjs +470 -121
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +23 -2
  5. package/dist/index.d.ts +23 -2
  6. package/dist/index.js +471 -122
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/charts/area-chart/area-chart.tsx +42 -10
  10. package/src/charts/area-chart/private/validate-data.ts +2 -3
  11. package/src/charts/area-chart/test/area-chart.test.tsx +72 -1
  12. package/src/charts/bar-chart/bar-chart.tsx +100 -83
  13. package/src/charts/bar-chart/index.ts +1 -1
  14. package/src/charts/bar-chart/private/band-highlight.tsx +3 -1
  15. package/src/charts/bar-chart/private/band-tooltip.tsx +1 -1
  16. package/src/charts/bar-chart/private/index.ts +1 -0
  17. package/src/charts/bar-chart/private/test/band-highlight.test.tsx +28 -2
  18. package/src/charts/bar-chart/private/test/value-domain.test.ts +100 -0
  19. package/src/charts/bar-chart/private/use-bar-chart-options.ts +12 -39
  20. package/src/charts/bar-chart/private/value-domain.ts +90 -0
  21. package/src/charts/bar-chart/test/bar-chart.test.tsx +484 -9
  22. package/src/charts/bar-chart/types.ts +39 -0
  23. package/src/charts/line-chart/line-chart.tsx +148 -35
  24. package/src/charts/line-chart/private/index.ts +1 -0
  25. package/src/charts/line-chart/private/line-chart-glyph.tsx +17 -3
  26. package/src/charts/line-chart/private/nearest-pointer-events.tsx +155 -0
  27. package/src/charts/line-chart/test/line-chart.test.tsx +604 -9
  28. package/src/charts/line-chart/types.ts +2 -1
  29. package/src/charts/private/readings.ts +34 -0
  30. package/src/charts/private/test/readings.test.ts +29 -0
  31. package/src/charts/private/test/whole-number-ticks.test.tsx +57 -0
  32. package/src/charts/private/whole-number-ticks.tsx +79 -0
  33. package/src/components/legend/hooks/test/use-chart-legend-items.test.tsx +124 -0
  34. package/src/components/legend/hooks/use-chart-legend-items.ts +60 -2
  35. package/src/components/legend/private/base-legend.tsx +36 -21
  36. package/src/components/legend/test/legend.test.tsx +84 -0
  37. package/src/components/legend/types.ts +2 -0
  38. package/src/components/legend/utils/index.ts +0 -1
  39. package/src/components/tooltip/accessible-tooltip.tsx +1 -1
  40. package/src/hooks/test/use-chart-margin.test.tsx +35 -0
  41. package/src/hooks/use-chart-margin.tsx +7 -2
  42. package/src/providers/chart-context/global-charts-provider.tsx +1 -1
  43. package/src/providers/chart-context/test/chart-context.test.tsx +4 -4
  44. package/src/types.ts +8 -0
  45. package/src/components/legend/utils/label-transform-factory.ts +0 -24
@@ -353,6 +353,41 @@ describe( 'useChartMargin', () => {
353
353
 
354
354
  expect( result.current.left ).toBe( 20 );
355
355
  } );
356
+
357
+ it( 'measures ticks from readings that exist', () => {
358
+ const partialData = [
359
+ {
360
+ label: 'Series 1',
361
+ data: [
362
+ { date: new Date( '2024-01-01' ), value: null },
363
+ { date: new Date( '2024-01-02' ), value: 100 },
364
+ { date: new Date( '2024-01-03' ), value: 200 },
365
+ ],
366
+ },
367
+ ];
368
+
369
+ renderHook( () => useChartMargin( 300, optionsBase, partialData, baseTheme ) );
370
+
371
+ const ticks = mockGetLongestTickWidth.mock.calls[ 0 ][ 0 ] as number[];
372
+ expect( Math.min( ...ticks ) ).toBe( 100 );
373
+ } );
374
+
375
+ it( 'measures a unit range when no bucket has a reading', () => {
376
+ const allNullData = [
377
+ {
378
+ label: 'Series 1',
379
+ data: [
380
+ { date: new Date( '2024-01-01' ), value: null },
381
+ { date: new Date( '2024-01-02' ), value: null },
382
+ ],
383
+ },
384
+ ];
385
+
386
+ renderHook( () => useChartMargin( 300, optionsBase, allNullData, baseTheme ) );
387
+
388
+ const ticks = mockGetLongestTickWidth.mock.calls[ 0 ][ 0 ] as number[];
389
+ expect( Math.max( ...ticks ) ).toBe( 1 );
390
+ } );
356
391
  } );
357
392
 
358
393
  describe( 'horizontal y ticks', () => {
@@ -111,8 +111,13 @@ export const useChartMargin = (
111
111
  return options.axis.y.tickValues;
112
112
  }
113
113
 
114
- const minY = Math.min( ...allDataPoints.map( d => d.value ) );
115
- const maxY = Math.max( ...allDataPoints.map( d => d.value ) );
114
+ const values = allDataPoints
115
+ .map( d => d.value )
116
+ .filter(
117
+ ( value ): value is number => typeof value === 'number' && Number.isFinite( value )
118
+ );
119
+ const minY = values.length ? Math.min( ...values ) : 0;
120
+ const maxY = values.length ? Math.max( ...values ) : 1;
116
121
  const yScale = createScale( {
117
122
  ...options.yScale,
118
123
  // A pinned domain is what the axis actually renders, so measure those
@@ -219,7 +219,7 @@ export const GlobalChartsProvider: FC< GlobalChartsProviderProps > = ( {
219
219
 
220
220
  const getElementStyles = useCallback< GlobalChartsContextValue[ 'getElementStyles' ] >(
221
221
  ( { data, index, overrideColor, legendShape } ) => {
222
- const isSeriesData = data && typeof data === 'object' && 'data' in data && 'options' in data;
222
+ const isSeriesData = data && typeof data === 'object' && 'data' in data;
223
223
  // DataPointPercentage has a numeric 'value' directly, unlike SeriesData which has 'data' array
224
224
  const isPointPercentageData =
225
225
  data &&
@@ -1318,11 +1318,11 @@ describe( 'ChartContext', () => {
1318
1318
  expect( styles ).toHaveProperty( 'glyph' );
1319
1319
  expect( styles ).toHaveProperty( 'shapeStyles' );
1320
1320
 
1321
- // Verify types
1321
+ // A series without an `options` key is still a series, so theme styles apply.
1322
1322
  expect( typeof styles.color ).toBe( 'string' );
1323
- expect( typeof styles.lineStyles ).toBe( 'object' );
1324
- expect( typeof styles.glyph ).toBe( 'function' );
1325
- expect( typeof styles.shapeStyles ).toBe( 'object' );
1323
+ expect( styles.lineStyles ).toEqual( { strokeWidth: 2 } );
1324
+ expect( styles.glyph ).toBe( mockGlyph );
1325
+ expect( styles.shapeStyles ).toEqual( { fill: '#SHAPE1' } );
1326
1326
  } );
1327
1327
 
1328
1328
  it( 'returns complete ElementStyles object for DataPointPercentage', () => {
package/src/types.ts CHANGED
@@ -134,6 +134,7 @@ export type ThemeAnnotationStyles = {
134
134
  export type DataPoint = {
135
135
  label: string;
136
136
  value: number;
137
+ /** Per-point fill override for BarChart; other built-in charts ignore it. */
137
138
  color?: string;
138
139
  };
139
140
 
@@ -186,6 +187,7 @@ export type DataPointDate = {
186
187
  dateString?: string;
187
188
  value: number | null;
188
189
  label?: string;
190
+ /** Per-point fill override for BarChart; other built-in charts ignore it. */
189
191
  color?: string;
190
192
  };
191
193
 
@@ -650,6 +652,12 @@ export type SeriesChartLegendConfig = ChartLegendConfig< SeriesData[] > & {
650
652
  * uncollapsed one toggles only its own.
651
653
  */
652
654
  collapseGroups?: boolean;
655
+ /**
656
+ * Append a static, non-interactive item explaining the comparison overlay whenever a series
657
+ * has `options.type === 'comparison'`. Skipped when that series already has its own item.
658
+ * Pass a string to replace the default label.
659
+ */
660
+ comparisonItem?: boolean | string;
653
661
  };
654
662
 
655
663
  /**
@@ -1,24 +0,0 @@
1
- import type { ItemTransformer, LabelFormatter } from '@visx/legend';
2
- import type { AnyD3Scale, ScaleInput } from '@visx/scale';
3
-
4
- /**
5
- * Returns a function which takes a Datum and index as input, and returns a formatted label object.
6
- * @param {object} root0 - The object to return the value of.
7
- * @param {AnyD3Scale} root0.scale - The scale to use.
8
- * @param {LabelFormatter<ScaleInput<Scale>>} root0.labelFormat - The label format to use.
9
- * @return {ItemTransformer<ScaleInput<Scale>, ReturnType<Scale>>} The label transform factory.
10
- */
11
- export function labelTransformFactory< Scale extends AnyD3Scale >( {
12
- scale,
13
- labelFormat,
14
- }: {
15
- scale: Scale;
16
- labelFormat: LabelFormatter< ScaleInput< Scale > >;
17
- } ): ItemTransformer< ScaleInput< Scale >, ReturnType< Scale > > {
18
- return ( d, i ) => ( {
19
- datum: d,
20
- index: i,
21
- text: `${ labelFormat( d, i ) }`,
22
- value: scale( d ),
23
- } );
24
- }