@automattic/charts 1.10.0 → 1.10.2

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 (35) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/index.cjs +5917 -5783
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.css +44 -3
  5. package/dist/index.d.cts +66 -13
  6. package/dist/index.d.ts +67 -14
  7. package/dist/index.js +1159 -1015
  8. package/dist/index.js.map +1 -1
  9. package/package.json +7 -7
  10. package/src/charts/heatmap-chart/heatmap-chart.module.scss +27 -4
  11. package/src/charts/heatmap-chart/heatmap-chart.tsx +115 -49
  12. package/src/charts/heatmap-chart/private/build-calendar-data.ts +39 -4
  13. package/src/charts/heatmap-chart/private/heatmap-context.ts +10 -0
  14. package/src/charts/heatmap-chart/private/heatmap-legend.tsx +3 -2
  15. package/src/charts/heatmap-chart/private/index.ts +2 -0
  16. package/src/charts/heatmap-chart/private/use-heatmap-colors.ts +6 -2
  17. package/src/charts/heatmap-chart/test/build-calendar-data.test.ts +57 -1
  18. package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +91 -3
  19. package/src/charts/heatmap-chart/test/use-heatmap-colors.test.ts +4 -0
  20. package/src/charts/heatmap-chart/types.ts +28 -0
  21. package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +12 -0
  22. package/src/charts/leaderboard-chart/leaderboard-chart.tsx +65 -37
  23. package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +46 -6
  24. package/src/charts/pie-semi-circle-chart/test/pie-semi-circle-chart.test.tsx +0 -6
  25. package/src/charts/private/with-responsive/test/with-responsive.test.tsx +229 -18
  26. package/src/charts/private/with-responsive/with-responsive.module.scss +20 -1
  27. package/src/charts/private/with-responsive/with-responsive.tsx +114 -65
  28. package/src/hooks/test/use-chart-margin.test.tsx +8 -5
  29. package/src/hooks/use-chart-margin.tsx +11 -1
  30. package/src/providers/chart-context/global-charts-provider.tsx +7 -0
  31. package/src/providers/chart-context/test/chart-context.test.tsx +40 -0
  32. package/src/providers/chart-context/types.ts +3 -0
  33. package/src/types.ts +9 -6
  34. package/src/utils/color-utils.ts +28 -0
  35. package/src/utils/test/color-utils.test.ts +29 -0
@@ -38,6 +38,9 @@ export interface GlobalChartsContextValue {
38
38
  getChartData: ( id: string ) => ChartRegistration | undefined;
39
39
  theme: CompleteChartTheme;
40
40
  getElementStyles: ( params: GetElementStylesParams ) => ElementStyles;
41
+ // Resolve a theme color or CSS variable to a hex string in the provider's scope.
42
+ // Resolves at call time so it tracks ambient theme changes (e.g. a light/dark switch).
43
+ resolveThemeColor: ( value: string ) => string;
41
44
  // Series visibility management for interactive legends
42
45
  toggleSeriesVisibility: ( chartId: string, seriesLabel: string ) => void;
43
46
  isSeriesVisible: ( chartId: string, seriesLabel: string ) => boolean;
package/src/types.ts CHANGED
@@ -191,9 +191,10 @@ export type LeaderboardEntry = {
191
191
  currentValue: number;
192
192
 
193
193
  /**
194
- * Value of the entry in the previous period
194
+ * Value of the entry in the previous period. Omit when this row has no
195
+ * matching comparison-period value.
195
196
  */
196
- previousValue: number;
197
+ previousValue?: number;
197
198
 
198
199
  /**
199
200
  * Width of current bar, as % of the current value
@@ -201,14 +202,16 @@ export type LeaderboardEntry = {
201
202
  currentShare: number;
202
203
 
203
204
  /**
204
- * Width of previous bar, as % of the current value
205
+ * Width of previous bar, as % of the current value. Omit when this row has
206
+ * no matching comparison-period value.
205
207
  */
206
- previousShare: number;
208
+ previousShare?: number;
207
209
 
208
210
  /**
209
- * Delta of the entry
211
+ * Delta of the entry. Omit when this row has no matching comparison-period
212
+ * value.
210
213
  */
211
- delta: number;
214
+ delta?: number;
212
215
 
213
216
  /**
214
217
  * Optional color for the entry's image/icon
@@ -233,6 +233,34 @@ export const lightenHexColor = ( hex: string, blend: number ): string => {
233
233
  .padStart( 2, '0' ) }${ newB.toString( 16 ).padStart( 2, '0' ) }`;
234
234
  };
235
235
 
236
+ /**
237
+ * Blend one hex color toward another, per-channel in sRGB.
238
+ *
239
+ * @param fromHex - Starting hex color, returned when blend is 0
240
+ * @param toHex - Target hex color, returned when blend is 1
241
+ * @param blend - Amount toward toHex, clamped to [0, 1]
242
+ * @return Blended hex color string
243
+ * @throws {Error} if either hex string is malformed
244
+ */
245
+ export const mixHexColors = ( fromHex: string, toHex: string, blend: number ): string => {
246
+ validateHexColor( fromHex );
247
+ validateHexColor( toHex );
248
+
249
+ const amount = Math.min( 1, Math.max( 0, blend ) );
250
+ const channel = ( start: number, end: number ): string =>
251
+ Math.round( start + ( end - start ) * amount )
252
+ .toString( 16 )
253
+ .padStart( 2, '0' );
254
+
255
+ return `#${ channel(
256
+ parseInt( fromHex.slice( 1, 3 ), 16 ),
257
+ parseInt( toHex.slice( 1, 3 ), 16 )
258
+ ) }${ channel(
259
+ parseInt( fromHex.slice( 3, 5 ), 16 ),
260
+ parseInt( toHex.slice( 3, 5 ), 16 )
261
+ ) }${ channel( parseInt( fromHex.slice( 5, 7 ), 16 ), parseInt( toHex.slice( 5, 7 ), 16 ) ) }`;
262
+ };
263
+
236
264
  /**
237
265
  * WCAG relative luminance of a hex color (0 = black, 1 = white).
238
266
  *
@@ -2,6 +2,7 @@ import { hsl as d3Hsl } from '@visx/vendor/d3-color';
2
2
  import {
3
3
  getColorDistance,
4
4
  lightenHexColor,
5
+ mixHexColors,
5
6
  isValidHexColor,
6
7
  hexToRgba,
7
8
  validateHexColor,
@@ -585,6 +586,34 @@ describe( 'lightenHexColor', () => {
585
586
  } );
586
587
  } );
587
588
 
589
+ describe( 'mixHexColors', () => {
590
+ it( 'returns the from color at blend 0', () => {
591
+ expect( mixHexColors( '#ff0000', '#0000ff', 0 ) ).toBe( '#ff0000' );
592
+ } );
593
+
594
+ it( 'returns the to color at blend 1', () => {
595
+ expect( mixHexColors( '#ff0000', '#0000ff', 1 ) ).toBe( '#0000ff' );
596
+ } );
597
+
598
+ it( 'blends halfway between the two colors', () => {
599
+ expect( mixHexColors( '#000000', '#ffffff', 0.5 ) ).toBe( '#808080' );
600
+ } );
601
+
602
+ it( 'matches lightenHexColor when the target is white', () => {
603
+ expect( mixHexColors( '#98c8df', '#ffffff', 0.8 ) ).toBe( lightenHexColor( '#98c8df', 0.8 ) );
604
+ } );
605
+
606
+ it( 'clamps blend outside [0, 1]', () => {
607
+ expect( mixHexColors( '#123456', '#abcdef', -1 ) ).toBe( '#123456' );
608
+ expect( mixHexColors( '#123456', '#abcdef', 2 ) ).toBe( '#abcdef' );
609
+ } );
610
+
611
+ it( 'throws on a malformed hex', () => {
612
+ expect( () => mixHexColors( '#fff', '#000000', 0.5 ) ).toThrow();
613
+ expect( () => mixHexColors( '#000000', 'nope', 0.5 ) ).toThrow();
614
+ } );
615
+ } );
616
+
588
617
  describe( 'parseHslString', () => {
589
618
  describe( 'Valid HSL strings', () => {
590
619
  it( 'parses hsl(120, 50%, 50%)', () => {