@automattic/charts 4.10.0 → 4.11.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 +19 -0
- package/TOKENS.md +7 -3
- package/dist/index.cjs +337 -708
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +52 -3
- package/dist/index.d.cts +39 -39
- package/dist/index.d.ts +39 -39
- package/dist/index.js +308 -660
- package/dist/index.js.map +1 -1
- package/dist/visx/group/index.js +1 -2
- package/dist/visx/legend/index.js +1 -2
- package/dist/visx/text/index.js +1 -2
- package/package.json +13 -12
- package/src/charts/conversion-funnel-chart/conversion-funnel-chart.module.scss +5 -0
- package/src/charts/geo-chart/geo-chart.module.scss +27 -0
- package/src/charts/geo-chart/geo-chart.tsx +68 -43
- package/src/charts/geo-chart/private/use-tooltip-position.ts +106 -0
- package/src/charts/geo-chart/test/geo-chart.test.tsx +68 -42
- package/src/charts/geo-chart/test/use-tooltip-position.test.ts +43 -0
- package/src/charts/heatmap-chart/heatmap-chart.module.scss +3 -3
- package/src/charts/heatmap-chart/private/heatmap-scale.ts +48 -43
- package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +5 -5
- package/src/charts/heatmap-chart/test/heatmap-scale.test.ts +55 -27
- package/src/charts/heatmap-chart/types.ts +1 -1
- package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +8 -5
- package/src/components/legend/private/base-legend.module.scss +5 -0
- package/src/components/legend/private/base-legend.tsx +8 -2
- package/src/components/tooltip/_surface.scss +19 -0
- package/src/components/tooltip/private/bounded-tooltip.tsx +2 -2
- package/src/components/tooltip/tooltip-box.module.scss +3 -17
- package/src/styles/chart-scope.scss +6 -4
- package/src/styles/test/chart-scope.test.ts +6 -2
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { MIN_BACKGROUND_CONTRAST } from '../../../providers/chart-context/private/palette-generator';
|
|
2
1
|
import { contrastRatio } from '../../../providers/chart-context/private/perceptual-color';
|
|
3
2
|
import { mixHexColors, relativeLuminance } from '../../../utils/color-utils';
|
|
4
3
|
|
|
4
|
+
/** Contrast the lowest step aims for against the background and the empty cell. */
|
|
5
|
+
export const HEATMAP_LOW_CONTRAST = 2.5;
|
|
6
|
+
|
|
7
|
+
/** Contrast every step keeps against the background, and the lowest against the highest. */
|
|
8
|
+
export const HEATMAP_STEP_CONTRAST = 2;
|
|
9
|
+
|
|
5
10
|
/** Contrast the highest step aims for against the background, so the steps stay apart. */
|
|
6
|
-
export const HEATMAP_HIGH_CONTRAST =
|
|
11
|
+
export const HEATMAP_HIGH_CONTRAST = 6;
|
|
7
12
|
|
|
8
13
|
const STEPS = 200;
|
|
9
14
|
|
|
@@ -12,20 +17,13 @@ export type HeatmapScale = {
|
|
|
12
17
|
high: string;
|
|
13
18
|
};
|
|
14
19
|
|
|
15
|
-
//
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
const color = mixHexColors( from, to, step / STEPS );
|
|
19
|
-
if ( meets( color ) ) {
|
|
20
|
-
return color;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return null;
|
|
24
|
-
};
|
|
20
|
+
// Mixed to the rounded hex the browser paints, so an endpoint never lands under its threshold.
|
|
21
|
+
const ramp = ( from: string, to: string ) =>
|
|
22
|
+
Array.from( { length: STEPS + 1 }, ( _, step ) => mixHexColors( from, to, step / STEPS ) );
|
|
25
23
|
|
|
26
24
|
/**
|
|
27
|
-
* The two ends of the heatmap fill scale:
|
|
28
|
-
* low end,
|
|
25
|
+
* The two ends of the heatmap fill scale: toward 2.5:1 against the background and the empty cell at
|
|
26
|
+
* the low end, never under 2:1 against the background or the high end, and toward 6:1 at the high end.
|
|
29
27
|
*
|
|
30
28
|
* @param primary - The resolved primary color, as six-digit hex.
|
|
31
29
|
* @param background - The resolved chart background, as six-digit hex.
|
|
@@ -37,38 +35,45 @@ export const getHeatmapScale = (
|
|
|
37
35
|
background: string,
|
|
38
36
|
emptyCell?: string | null
|
|
39
37
|
): HeatmapScale => {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
const backgroundLuminance = relativeLuminance( background );
|
|
39
|
+
const ownSide = relativeLuminance( primary ) > backgroundLuminance ? '#ffffff' : '#000000';
|
|
40
|
+
const otherSide = ownSide === '#ffffff' ? '#000000' : '#ffffff';
|
|
41
|
+
// The scale deepens on the primary's own side of the background, unless only the other side
|
|
42
|
+
// reaches the high end or contrasts more; it never crosses the background either way.
|
|
43
|
+
const extreme =
|
|
44
|
+
contrastRatio( ownSide, background ) >= HEATMAP_HIGH_CONTRAST ||
|
|
45
|
+
contrastRatio( ownSide, background ) >= contrastRatio( otherSide, background )
|
|
46
|
+
? ownSide
|
|
47
|
+
: otherSide;
|
|
48
|
+
const onExtremeSide = ( color: string ) =>
|
|
49
|
+
extreme === '#ffffff'
|
|
50
|
+
? relativeLuminance( color ) > backgroundLuminance
|
|
51
|
+
: relativeLuminance( color ) < backgroundLuminance;
|
|
47
52
|
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
references.every( reference => contrastRatio( color, reference ) >= MIN_BACKGROUND_CONTRAST );
|
|
51
|
-
return (
|
|
52
|
-
( primaryStandsOut ? firstMeeting( background, primary, meets ) : null ) ??
|
|
53
|
-
firstMeeting( primary, extreme, meets )
|
|
54
|
-
);
|
|
55
|
-
};
|
|
56
|
-
// Only `high` can miss: black or white tops out near 4.58:1 on a mid-tone background.
|
|
53
|
+
const deepening = ramp( primary, extreme );
|
|
54
|
+
// Only `high` can miss its target: black or white tops out near 4.58:1 on a mid-tone background.
|
|
57
55
|
const high =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
extreme,
|
|
61
|
-
color => contrastRatio( color, background ) >= HEATMAP_HIGH_CONTRAST
|
|
62
|
-
) ?? extreme;
|
|
56
|
+
deepening.find( color => contrastRatio( color, background ) >= HEATMAP_HIGH_CONTRAST ) ??
|
|
57
|
+
extreme;
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
// empty
|
|
66
|
-
const
|
|
59
|
+
const path = [ ...ramp( background, primary ), ...deepening ];
|
|
60
|
+
// Never empty: `high` sits at least 4.58:1 out.
|
|
61
|
+
const spread = path
|
|
62
|
+
.slice( 0, path.indexOf( high ) + 1 )
|
|
63
|
+
.filter(
|
|
64
|
+
color =>
|
|
65
|
+
onExtremeSide( color ) &&
|
|
66
|
+
contrastRatio( color, background ) >= HEATMAP_STEP_CONTRAST &&
|
|
67
|
+
contrastRatio( color, high ) >= HEATMAP_STEP_CONTRAST
|
|
68
|
+
);
|
|
69
|
+
const surroundings = emptyCell ? [ background, emptyCell ] : [ background ];
|
|
70
|
+
const weakest = ( color: string ) =>
|
|
71
|
+
Math.min( ...surroundings.map( other => contrastRatio( color, other ) ) );
|
|
72
|
+
// Short of 2.5:1 the low end settles for 2:1, then for the step farthest from its nearest neighbor.
|
|
67
73
|
const low =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
: lowAgainst( [ background ] );
|
|
74
|
+
spread.find( color => weakest( color ) >= HEATMAP_LOW_CONTRAST ) ??
|
|
75
|
+
spread.find( color => weakest( color ) >= HEATMAP_STEP_CONTRAST ) ??
|
|
76
|
+
spread.reduce( ( best, color ) => ( weakest( color ) > weakest( best ) ? color : best ) );
|
|
72
77
|
|
|
73
|
-
return { low
|
|
78
|
+
return { low, high };
|
|
74
79
|
};
|
|
@@ -486,8 +486,8 @@ describe( 'HeatmapChart', () => {
|
|
|
486
486
|
);
|
|
487
487
|
const grid = screen.getByRole( 'grid', { name: /heatmap/i } );
|
|
488
488
|
|
|
489
|
-
expect( grid.style.getPropertyValue( '--a8c-charts-color-heatmap-low' ) ).toBe( '#
|
|
490
|
-
expect( grid.style.getPropertyValue( '--a8c-charts-color-heatmap-high' ) ).toBe( '#
|
|
489
|
+
expect( grid.style.getPropertyValue( '--a8c-charts-color-heatmap-low' ) ).toBe( '#7f93f1' );
|
|
490
|
+
expect( grid.style.getPropertyValue( '--a8c-charts-color-heatmap-high' ) ).toBe( '#3554df' );
|
|
491
491
|
|
|
492
492
|
document.body.removeChild( scope );
|
|
493
493
|
} );
|
|
@@ -586,7 +586,7 @@ const mockRects = () =>
|
|
|
586
586
|
describe( 'HeatmapChart value text contrast', () => {
|
|
587
587
|
// The low end, a mid-tone, and the high end of the scale.
|
|
588
588
|
const scale: HeatmapColumn[] = [
|
|
589
|
-
{ label: 'W1', data: [ { value: 1 }, { value:
|
|
589
|
+
{ label: 'W1', data: [ { value: 1 }, { value: 60 }, { value: 100 } ] },
|
|
590
590
|
];
|
|
591
591
|
|
|
592
592
|
let injectedStyle: HTMLStyleElement | null = null;
|
|
@@ -619,7 +619,7 @@ describe( 'HeatmapChart value text contrast', () => {
|
|
|
619
619
|
test( 'falls back to black on a mid-tone cell where neither role reaches AA', () => {
|
|
620
620
|
renderScale();
|
|
621
621
|
|
|
622
|
-
expect( screen.getByText( '
|
|
622
|
+
expect( screen.getByText( '60' ) ).toHaveClass( 'heatmap-chart__cell-value--black' );
|
|
623
623
|
} );
|
|
624
624
|
|
|
625
625
|
test( 'reads the label roles once data arrives after an empty first render', () => {
|
|
@@ -691,7 +691,7 @@ describe( 'HeatmapChart value text contrast', () => {
|
|
|
691
691
|
|
|
692
692
|
renderScale( 'pinned-heatmap' );
|
|
693
693
|
|
|
694
|
-
[ '1', '
|
|
694
|
+
[ '1', '60', '100' ].forEach( value =>
|
|
695
695
|
expect( screen.getByText( value ) ).toHaveClass( 'heatmap-chart__cell-value', {
|
|
696
696
|
exact: true,
|
|
697
697
|
} )
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { MIN_BACKGROUND_CONTRAST } from '../../../providers/chart-context/private/palette-generator';
|
|
2
1
|
import { contrastRatio } from '../../../providers/chart-context/private/perceptual-color';
|
|
3
2
|
import { mixHexColors } from '../../../utils/color-utils';
|
|
4
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getHeatmapScale,
|
|
5
|
+
HEATMAP_HIGH_CONTRAST,
|
|
6
|
+
HEATMAP_LOW_CONTRAST,
|
|
7
|
+
HEATMAP_STEP_CONTRAST,
|
|
8
|
+
} from '../private/heatmap-scale';
|
|
5
9
|
|
|
6
|
-
// The wp-admin scheme colors, then the pre-7.0 ones
|
|
10
|
+
// The wp-admin scheme colors, then the pre-7.0 ones.
|
|
7
11
|
const PRIMARIES = [
|
|
8
12
|
'#007cba',
|
|
9
13
|
'#3858e9',
|
|
@@ -22,17 +26,17 @@ describe.each( [
|
|
|
22
26
|
[ 'dark', '#242424', '#272727' ],
|
|
23
27
|
] )( 'getHeatmapScale on a %s background', ( _mode, background, emptyCell ) => {
|
|
24
28
|
it.each( PRIMARIES )(
|
|
25
|
-
'keeps the lowest step of %s at
|
|
29
|
+
'keeps the lowest step of %s at 2.5:1 against the background and empty cell, no further',
|
|
26
30
|
primary => {
|
|
27
31
|
const { low } = getHeatmapScale( primary, background, emptyCell );
|
|
28
32
|
const contrasts = [ contrastRatio( low, background ), contrastRatio( low, emptyCell ) ];
|
|
29
33
|
|
|
30
|
-
expect( Math.min( ...contrasts ) ).toBeGreaterThanOrEqual(
|
|
31
|
-
expect( Math.min( ...contrasts ) ).toBeLessThan(
|
|
34
|
+
expect( Math.min( ...contrasts ) ).toBeGreaterThanOrEqual( HEATMAP_LOW_CONTRAST );
|
|
35
|
+
expect( Math.min( ...contrasts ) ).toBeLessThan( HEATMAP_LOW_CONTRAST + 0.1 );
|
|
32
36
|
}
|
|
33
37
|
);
|
|
34
38
|
|
|
35
|
-
it.each( PRIMARIES )( 'runs the scale of %s out to
|
|
39
|
+
it.each( PRIMARIES )( 'runs the scale of %s out to 6:1', primary => {
|
|
36
40
|
const { high } = getHeatmapScale( primary, background );
|
|
37
41
|
|
|
38
42
|
expect( contrastRatio( high, background ) ).toBeGreaterThanOrEqual( HEATMAP_HIGH_CONTRAST );
|
|
@@ -40,39 +44,63 @@ describe.each( [
|
|
|
40
44
|
} );
|
|
41
45
|
|
|
42
46
|
describe( 'getHeatmapScale', () => {
|
|
43
|
-
it( 'keeps a primary already past
|
|
47
|
+
it( 'keeps a primary already past 6:1 as the high end', () => {
|
|
44
48
|
expect( getHeatmapScale( '#1d2327', '#ffffff' ).high ).toBe( '#1d2327' );
|
|
45
49
|
} );
|
|
46
50
|
|
|
47
51
|
it.each( [
|
|
48
|
-
[ '#ffffff', '#808080' ],
|
|
49
|
-
[ '#ffd000', '#767676' ],
|
|
50
|
-
[ '#3858e9', '#808080' ],
|
|
51
|
-
[ '#1e1e1e', '#999999' ],
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
[ '#ffffff', '#808080', null ],
|
|
53
|
+
[ '#ffd000', '#767676', null ],
|
|
54
|
+
[ '#3858e9', '#808080', null ],
|
|
55
|
+
[ '#1e1e1e', '#999999', null ],
|
|
56
|
+
[ '#ffd000', '#8a8a8a', null ],
|
|
57
|
+
[ '#ffd000', '#8a8a8a', '#cccccc' ],
|
|
58
|
+
[ '#1d2327', '#555555', '#272727' ],
|
|
59
|
+
] )(
|
|
60
|
+
'keeps every step of %s at 2:1 on a mid-gray %s with a %s empty cell, ends apart',
|
|
61
|
+
( primary, background, emptyCell ) => {
|
|
62
|
+
const { low, high } = getHeatmapScale( primary, background, emptyCell );
|
|
54
63
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
expect( contrastRatio( low, high ) ).toBeGreaterThanOrEqual( HEATMAP_STEP_CONTRAST );
|
|
65
|
+
for ( let step = 0; step <= 20; step++ ) {
|
|
66
|
+
expect(
|
|
67
|
+
contrastRatio( mixHexColors( low, high, step / 20 ), background )
|
|
68
|
+
).toBeGreaterThanOrEqual( HEATMAP_STEP_CONTRAST );
|
|
69
|
+
}
|
|
59
70
|
}
|
|
60
|
-
|
|
71
|
+
);
|
|
61
72
|
|
|
62
73
|
it.each( [
|
|
63
|
-
[ '
|
|
64
|
-
[ '
|
|
65
|
-
[ '
|
|
74
|
+
[ '#ffffff', '#cccccc' ],
|
|
75
|
+
[ '#ffffff', '#959595' ],
|
|
76
|
+
[ '#1e1e1e', '#6b6b6b' ],
|
|
66
77
|
] )(
|
|
67
|
-
'
|
|
68
|
-
(
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
'moves the low end on %s farther from a %s empty cell than the background alone would',
|
|
79
|
+
( background, emptyCell ) => {
|
|
80
|
+
const { low } = getHeatmapScale( '#3858e9', background, emptyCell );
|
|
81
|
+
const backgroundOnly = getHeatmapScale( '#3858e9', background ).low;
|
|
82
|
+
|
|
83
|
+
expect( contrastRatio( low, emptyCell ) ).toBeGreaterThan(
|
|
84
|
+
contrastRatio( backgroundOnly, emptyCell )
|
|
71
85
|
);
|
|
72
86
|
}
|
|
73
87
|
);
|
|
74
88
|
|
|
75
|
-
it(
|
|
89
|
+
it.each( [
|
|
90
|
+
[ '#ffffff', '#959595' ],
|
|
91
|
+
[ '#ffffff', '#cccccc' ],
|
|
92
|
+
[ '#1e1e1e', '#555555' ],
|
|
93
|
+
] )(
|
|
94
|
+
'keeps the ends apart and in order on %s with a %s empty cell',
|
|
95
|
+
( background, emptyCell ) => {
|
|
96
|
+
const { low, high } = getHeatmapScale( '#3858e9', background, emptyCell );
|
|
97
|
+
|
|
98
|
+
expect( contrastRatio( low, high ) ).toBeGreaterThanOrEqual( HEATMAP_STEP_CONTRAST );
|
|
99
|
+
expect( contrastRatio( low, background ) ).toBeLessThan( contrastRatio( high, background ) );
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
it( 'ends on black or white when the background leaves no room for 6:1', () => {
|
|
76
104
|
expect( getHeatmapScale( '#3858e9', '#808080' ).high ).toBe( '#000000' );
|
|
77
105
|
} );
|
|
78
106
|
} );
|
|
@@ -102,7 +102,7 @@ export interface HeatmapChartProps extends Omit<
|
|
|
102
102
|
/** Floor a cell's height (px) in non-compact mode; see `minCellWidth`. */
|
|
103
103
|
minCellHeight?: number;
|
|
104
104
|
/**
|
|
105
|
-
* Color the cell scale is built from, deepened at the highest value toward
|
|
105
|
+
* Color the cell scale is built from, deepened at the highest value toward 6:1
|
|
106
106
|
* against the background. Defaults to the first series palette slot,
|
|
107
107
|
* `--a8c-charts-color-series-1`.
|
|
108
108
|
*/
|
|
@@ -747,15 +747,18 @@ describe( 'LeaderboardChart', () => {
|
|
|
747
747
|
|
|
748
748
|
describe( 'grid gaps', () => {
|
|
749
749
|
// eslint-disable-next-line testing-library/no-node-access -- The grid is rendered by `Grid` from @wordpress/components, which takes no test id.
|
|
750
|
-
const grid = () => document.querySelector( '[data-leaderboard-grid]' );
|
|
750
|
+
const grid = () => document.querySelector< HTMLElement >( '[data-leaderboard-grid]' );
|
|
751
751
|
|
|
752
752
|
it( 'hands the catalog role to the grid', () => {
|
|
753
753
|
render( <LeaderboardChart data={ mockData } /> );
|
|
754
754
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
755
|
+
// Grid writes its gaps into custom properties and reads them from a stylesheet.
|
|
756
|
+
expect( grid().style.getPropertyValue( '--wp-components-grid-row-gap' ) ).toBe(
|
|
757
|
+
'var(--a8c-charts-dimension-leaderboard-row-gap, 12px)'
|
|
758
|
+
);
|
|
759
|
+
expect( grid().style.getPropertyValue( '--wp-components-grid-column-gap' ) ).toBe(
|
|
760
|
+
'var(--a8c-charts-dimension-leaderboard-column-gap, 4px)'
|
|
761
|
+
);
|
|
759
762
|
} );
|
|
760
763
|
} );
|
|
761
764
|
} );
|
|
@@ -257,13 +257,19 @@ export const BaseLegend: ForwardRefExoticComponent<
|
|
|
257
257
|
<Stack
|
|
258
258
|
ref={ ref }
|
|
259
259
|
direction={ orientation === 'vertical' ? 'column' : 'row' }
|
|
260
|
-
gap
|
|
260
|
+
// Stack's gap is inline; horizontal gaps live in CSS so consumers can override them.
|
|
261
|
+
gap={ orientation === 'vertical' ? 'sm' : undefined }
|
|
261
262
|
align={ orientation === 'vertical' ? flexAlignment : undefined }
|
|
262
263
|
justify={ orientation === 'horizontal' ? flexAlignment : undefined }
|
|
263
264
|
wrap={ orientation === 'horizontal' ? 'wrap' : undefined }
|
|
264
265
|
role={ interactive ? undefined : 'list' }
|
|
265
266
|
data-testid={ `legend-${ orientation }` }
|
|
266
|
-
className={ clsx(
|
|
267
|
+
className={ clsx(
|
|
268
|
+
standaloneScopeClass,
|
|
269
|
+
styles.legend,
|
|
270
|
+
orientation === 'horizontal' && styles[ 'legend--horizontal' ],
|
|
271
|
+
className
|
|
272
|
+
) }
|
|
267
273
|
style={ theme.legend?.containerStyles }
|
|
268
274
|
>
|
|
269
275
|
{ labels.map( ( label, i ) => {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// The chart tooltip box: the look every chart tooltip shares.
|
|
2
|
+
@mixin surface {
|
|
3
|
+
padding: var(--wpds-dimension-padding-xs) var(--wpds-dimension-padding-sm);
|
|
4
|
+
background-color: var(--a8c-charts-color-surface);
|
|
5
|
+
color: var(--a8c-charts-color-label);
|
|
6
|
+
border-radius: var(--wpds-border-radius-md);
|
|
7
|
+
font-family: var(--wpds-typography-font-family-body);
|
|
8
|
+
font-size: var(--wpds-typography-font-size-sm);
|
|
9
|
+
line-height: 1.4;
|
|
10
|
+
box-shadow: var(--a8c-charts-elevation-sm, 0 1px 2px 0 #0000000d, 0 2px 3px 0 #0000000a, 0 6px 6px 0 #00000008, 0 8px 8px 0 #00000005);
|
|
11
|
+
pointer-events: none;
|
|
12
|
+
// Beats a host `max-width` on `.visx-tooltip`, even an `!important`
|
|
13
|
+
// one: WordPress.com's Help Center caps it at 150px.
|
|
14
|
+
min-width: fit-content;
|
|
15
|
+
|
|
16
|
+
@media (forced-colors: active) {
|
|
17
|
+
border: var(--wpds-border-width-xs) solid CanvasText;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -17,7 +17,7 @@ export type BoundedTooltipProps = Omit< TooltipBoxProps, 'children' > & {
|
|
|
17
17
|
placement?: TooltipPlacement;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
const DEFAULT_OFFSET = 10;
|
|
20
|
+
export const DEFAULT_OFFSET = 10;
|
|
21
21
|
// Above anything else layered inside a chart, such as the zoom reset button (2).
|
|
22
22
|
// Chart wrappers isolate their stacking context, so it never reaches page chrome.
|
|
23
23
|
export const TOOLTIP_Z_INDEX = 3;
|
|
@@ -43,7 +43,7 @@ const findLayoutParent = ( node: Element ): Element | null => {
|
|
|
43
43
|
// The first ancestor that cuts its overflow off. The box may leave the chart
|
|
44
44
|
// wrapper but never this element, which is what a body-level portal used to
|
|
45
45
|
// guarantee by never being inside one.
|
|
46
|
-
const findClippingAncestor = ( wrapper: Element ): Element | null => {
|
|
46
|
+
export const findClippingAncestor = ( wrapper: Element ): Element | null => {
|
|
47
47
|
let element = wrapper.parentElement;
|
|
48
48
|
while ( element && element !== document.body ) {
|
|
49
49
|
if ( isClipping( element ) ) {
|
|
@@ -1,21 +1,7 @@
|
|
|
1
1
|
// `.surface` is the look alone, for a chart that positions the box itself.
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
@use "surface";
|
|
3
|
+
|
|
4
4
|
.surface {
|
|
5
|
-
padding: var(--wpds-dimension-padding-xs) var(--wpds-dimension-padding-sm);
|
|
6
|
-
background-color: var(--a8c-charts-color-surface);
|
|
7
|
-
color: var(--a8c-charts-color-label);
|
|
8
|
-
border-radius: var(--wpds-border-radius-md);
|
|
9
|
-
font-family: var(--wpds-typography-font-family-body);
|
|
10
|
-
font-size: var(--wpds-typography-font-size-sm);
|
|
11
|
-
line-height: 1.4;
|
|
12
|
-
box-shadow: var(--a8c-charts-elevation-sm, 0 1px 2px 0 #0000000d, 0 2px 3px 0 #0000000a, 0 6px 6px 0 #00000008, 0 8px 8px 0 #00000005);
|
|
13
|
-
pointer-events: none;
|
|
14
|
-
// Beats a host `max-width` on `.visx-tooltip`, even an `!important`
|
|
15
|
-
// one: WordPress.com's Help Center caps it at 150px.
|
|
16
|
-
min-width: fit-content;
|
|
17
5
|
|
|
18
|
-
@
|
|
19
|
-
border: var(--wpds-border-width-xs) solid CanvasText;
|
|
20
|
-
}
|
|
6
|
+
@include surface.surface;
|
|
21
7
|
}
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
// the same element wins. Chart roots deliberately do NOT re-declare these: an
|
|
11
11
|
// element's own declaration beats an inherited one, so re-declaring lower down
|
|
12
12
|
// would shadow a consumer override set inside the provider tree.
|
|
13
|
-
// The tooltip
|
|
14
|
-
// .a8c-charts-scope does not land on the dark box.
|
|
15
|
-
:where(.a8c-charts-scope, .a8c-charts-tooltip-scope) {
|
|
13
|
+
// The tooltip boxes (ours and Google's GeoChart one) re-declare the catalog on
|
|
14
|
+
// themselves, so a rule on .a8c-charts-scope does not land on the dark box.
|
|
15
|
+
:where(.a8c-charts-scope, .a8c-charts-tooltip-scope, .geo-chart .google-visualization-tooltip) {
|
|
16
16
|
// The series palette. GlobalChartsProvider resolves these five slots at its
|
|
17
17
|
// wrapper and seeds ColorCache with whatever resolves; a slot that resolves
|
|
18
18
|
// to nothing is skipped, so setting only slots 1 and 3 gives a two-color
|
|
@@ -87,11 +87,13 @@
|
|
|
87
87
|
|
|
88
88
|
--a8c-charts-dimension-leaderboard-row-gap: var(--wpds-dimension-gap-md);
|
|
89
89
|
--a8c-charts-dimension-leaderboard-column-gap: var(--wpds-dimension-gap-xs);
|
|
90
|
+
--a8c-charts-dimension-legend-column-gap: var(--wpds-dimension-gap-lg);
|
|
91
|
+
--a8c-charts-dimension-legend-row-gap: var(--wpds-dimension-gap-sm);
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
@media (forced-colors: active) {
|
|
93
95
|
|
|
94
|
-
:where(.a8c-charts-scope, .a8c-charts-tooltip-scope) {
|
|
96
|
+
:where(.a8c-charts-scope, .a8c-charts-tooltip-scope, .geo-chart .google-visualization-tooltip) {
|
|
95
97
|
--a8c-charts-color-label-axis: CanvasText;
|
|
96
98
|
--a8c-charts-color-grid: GrayText;
|
|
97
99
|
--a8c-charts-color-axis-x: GrayText;
|
|
@@ -127,7 +127,9 @@ describe( 'chart scope catalog', () => {
|
|
|
127
127
|
expect( mediaRules[ 0 ].cssRules ).toHaveLength( 1 );
|
|
128
128
|
|
|
129
129
|
const rule = mediaRules[ 0 ].cssRules[ 0 ] as CSSStyleRule;
|
|
130
|
-
expect( rule.selectorText ).toBe(
|
|
130
|
+
expect( rule.selectorText ).toBe(
|
|
131
|
+
':where(.a8c-charts-scope, .a8c-charts-tooltip-scope, .geo-chart .google-visualization-tooltip)'
|
|
132
|
+
);
|
|
131
133
|
expect(
|
|
132
134
|
Object.fromEntries(
|
|
133
135
|
Array.from( rule.style ).map( property => [
|
|
@@ -172,7 +174,9 @@ describe( 'chart scope catalog', () => {
|
|
|
172
174
|
} );
|
|
173
175
|
|
|
174
176
|
it( 'scopes the catalog to the chart and tooltip scope classes rather than :root', () => {
|
|
175
|
-
expect( stylesheet ).toMatch(
|
|
177
|
+
expect( stylesheet ).toMatch(
|
|
178
|
+
/:where\(\.a8c-charts-scope, \.a8c-charts-tooltip-scope, \.geo-chart \.google-visualization-tooltip\)\s*{/
|
|
179
|
+
);
|
|
176
180
|
expect( stylesheet ).not.toMatch( /(^|\s):root\s*{/ );
|
|
177
181
|
} );
|
|
178
182
|
|