@automattic/charts 1.10.0 → 1.10.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/CHANGELOG.md +9 -0
- package/dist/index.cjs +5700 -5584
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +15 -3
- package/dist/index.d.cts +26 -12
- package/dist/index.d.ts +27 -13
- package/dist/index.js +1026 -912
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/charts/heatmap-chart/heatmap-chart.module.scss +4 -4
- package/src/charts/heatmap-chart/heatmap-chart.tsx +37 -36
- package/src/charts/heatmap-chart/private/build-calendar-data.ts +17 -1
- package/src/charts/heatmap-chart/private/heatmap-context.ts +10 -0
- package/src/charts/heatmap-chart/private/heatmap-legend.tsx +3 -2
- package/src/charts/heatmap-chart/private/index.ts +2 -0
- package/src/charts/heatmap-chart/test/build-calendar-data.test.ts +23 -1
- package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +10 -3
- package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +12 -0
- package/src/charts/leaderboard-chart/leaderboard-chart.tsx +65 -37
- package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +46 -0
- package/src/hooks/test/use-chart-margin.test.tsx +8 -5
- package/src/hooks/use-chart-margin.tsx +11 -1
- package/src/providers/chart-context/global-charts-provider.tsx +7 -0
- package/src/providers/chart-context/test/chart-context.test.tsx +40 -0
- package/src/providers/chart-context/types.ts +3 -0
- package/src/types.ts +9 -6
- package/src/utils/color-utils.ts +28 -0
- package/src/utils/test/color-utils.test.ts +29 -0
|
@@ -2536,6 +2536,46 @@ describe( 'ChartContext', () => {
|
|
|
2536
2536
|
expect( afterRerenderColor ).toBe( '#ff0000' );
|
|
2537
2537
|
} );
|
|
2538
2538
|
} );
|
|
2539
|
+
|
|
2540
|
+
describe( 'resolveThemeColor', () => {
|
|
2541
|
+
let contextValue: GlobalChartsContextValue;
|
|
2542
|
+
|
|
2543
|
+
const TestComponent = () => {
|
|
2544
|
+
contextValue = useGlobalChartsContext();
|
|
2545
|
+
return <div>Test</div>;
|
|
2546
|
+
};
|
|
2547
|
+
|
|
2548
|
+
const mountProvider = () =>
|
|
2549
|
+
render(
|
|
2550
|
+
<GlobalChartsProvider>
|
|
2551
|
+
<TestComponent />
|
|
2552
|
+
</GlobalChartsProvider>
|
|
2553
|
+
);
|
|
2554
|
+
|
|
2555
|
+
it( 'resolves a CSS variable against the provider scope, not the document root', () => {
|
|
2556
|
+
window.getComputedStyle = jest.fn( ( element: Element ) => ( {
|
|
2557
|
+
getPropertyValue: ( prop: string ) =>
|
|
2558
|
+
prop === '--surface' && element !== document.documentElement ? '#1e1e1e' : '#ffffff',
|
|
2559
|
+
} ) ) as unknown as typeof window.getComputedStyle;
|
|
2560
|
+
|
|
2561
|
+
mountProvider();
|
|
2562
|
+
|
|
2563
|
+
expect( contextValue.resolveThemeColor( 'var(--surface, #fff)' ) ).toBe( '#1e1e1e' );
|
|
2564
|
+
} );
|
|
2565
|
+
|
|
2566
|
+
it( 'passes a resolved color through to hex', () => {
|
|
2567
|
+
mountProvider();
|
|
2568
|
+
|
|
2569
|
+
expect( contextValue.resolveThemeColor( '#abcdef' ) ).toBe( '#abcdef' );
|
|
2570
|
+
expect( contextValue.resolveThemeColor( 'rgb(255, 0, 0)' ) ).toBe( '#ff0000' );
|
|
2571
|
+
} );
|
|
2572
|
+
|
|
2573
|
+
it( 'returns an empty string for an empty value', () => {
|
|
2574
|
+
mountProvider();
|
|
2575
|
+
|
|
2576
|
+
expect( contextValue.resolveThemeColor( '' ) ).toBe( '' );
|
|
2577
|
+
} );
|
|
2578
|
+
} );
|
|
2539
2579
|
} );
|
|
2540
2580
|
|
|
2541
2581
|
describe( 'defaultTheme', () => {
|
|
@@ -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
|
|
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
|
|
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
|
|
214
|
+
delta?: number;
|
|
212
215
|
|
|
213
216
|
/**
|
|
214
217
|
* Optional color for the entry's image/icon
|
package/src/utils/color-utils.ts
CHANGED
|
@@ -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%)', () => {
|