@automattic/charts 1.5.3 → 1.7.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.
- package/CHANGELOG.md +17 -0
- package/README.md +2 -2
- package/dist/index.cjs +8981 -9569
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +318 -202
- package/dist/index.d.cts +1319 -1183
- package/dist/index.d.ts +1319 -1183
- package/dist/index.js +8561 -9570
- package/dist/index.js.map +1 -1
- package/dist/visx/group/index.cjs +8 -8
- package/dist/visx/group/index.d.cts +2 -3
- package/dist/visx/group/index.d.ts +2 -3
- package/dist/visx/group/index.js +1 -7
- package/dist/visx/legend/index.cjs +20 -10
- package/dist/visx/legend/index.d.cts +2 -1
- package/dist/visx/legend/index.d.ts +2 -1
- package/dist/visx/legend/index.js +2 -10
- package/dist/visx/text/index.cjs +20 -12
- package/dist/visx/text/index.d.cts +2 -1
- package/dist/visx/text/index.d.ts +2 -1
- package/dist/visx/text/index.js +2 -12
- package/package.json +32 -38
- package/src/charts/area-chart/types.ts +1 -1
- package/src/charts/bar-chart/bar-chart.tsx +1 -1
- package/src/charts/geo-chart/geo-chart.tsx +3 -1
- package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +113 -1
- package/src/charts/leaderboard-chart/leaderboard-chart.tsx +37 -6
- package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +81 -0
- package/src/charts/line-chart/line-chart.tsx +1 -1
- package/src/charts/line-chart/private/line-chart-annotation-label-popover.tsx +18 -2
- package/src/charts/line-chart/private/line-chart-annotation.tsx +1 -1
- package/src/charts/line-chart/types.ts +1 -1
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +2 -2
- package/src/charts/private/chart-layout/chart-layout.tsx +1 -2
- package/src/charts/private/x-zoom.tsx +2 -2
- package/src/components/legend/hooks/use-chart-legend-items.ts +6 -2
- package/src/components/legend/legend.tsx +1 -2
- package/src/components/legend/utils/label-transform-factory.ts +1 -1
- package/src/components/tooltip/accessible-tooltip.tsx +2 -6
- package/src/index.ts +6 -5
- package/src/providers/chart-context/themes.ts +1 -1
- package/src/providers/chart-context/types.ts +7 -2
- package/src/types.ts +118 -11
- package/src/utils/get-styles.ts +1 -2
- package/src/visx/types.ts +30 -1
- package/dist/chunk-5WRI5ZAA.js +0 -31
- package/dist/chunk-5WRI5ZAA.js.map +0 -1
- package/dist/chunk-7OZEQ5HE.cjs +0 -9
- package/dist/chunk-7OZEQ5HE.cjs.map +0 -1
- package/dist/chunk-DZUJEN5N.cjs +0 -31
- package/dist/chunk-DZUJEN5N.cjs.map +0 -1
- package/dist/chunk-NFRB2POF.js +0 -9
- package/dist/chunk-NFRB2POF.js.map +0 -1
- package/dist/index.css.map +0 -1
- package/dist/visx/group/index.cjs.map +0 -1
- package/dist/visx/group/index.js.map +0 -1
- package/dist/visx/legend/index.cjs.map +0 -1
- package/dist/visx/legend/index.js.map +0 -1
- package/dist/visx/text/index.cjs.map +0 -1
- package/dist/visx/text/index.js.map +0 -1
- package/tsup.config.ts +0 -46
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _visx_group = require("@visx/group");
|
|
3
|
+
Object.defineProperty(exports, "Group", {
|
|
4
|
+
enumerable: true,
|
|
5
|
+
get: function() {
|
|
6
|
+
return _visx_group.Group;
|
|
7
|
+
}
|
|
8
|
+
});
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import '@visx/text';
|
|
1
|
+
import { Group } from "@visx/group";
|
|
2
|
+
export { Group };
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import '@visx/text';
|
|
1
|
+
import { Group } from "@visx/group";
|
|
2
|
+
export { Group };
|
package/dist/visx/group/index.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _visx_legend = require("@visx/legend");
|
|
3
|
+
Object.defineProperty(exports, "CircleShape", {
|
|
4
|
+
enumerable: true,
|
|
5
|
+
get: function() {
|
|
6
|
+
return _visx_legend.CircleShape;
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
Object.defineProperty(exports, "LineShape", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: function() {
|
|
12
|
+
return _visx_legend.LineShape;
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(exports, "RectShape", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function() {
|
|
18
|
+
return _visx_legend.RectShape;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { CircleShape, LineShape, RectShape } from "@visx/legend";
|
|
2
|
+
export { CircleShape, LineShape, RectShape };
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { CircleShape, LineShape, RectShape } from "@visx/legend";
|
|
2
|
+
export { CircleShape, LineShape, RectShape };
|
|
@@ -1,10 +1,2 @@
|
|
|
1
|
-
import "
|
|
2
|
-
|
|
3
|
-
// src/visx/legend/index.ts
|
|
4
|
-
import { LineShape, CircleShape, RectShape } from "@visx/legend";
|
|
5
|
-
export {
|
|
6
|
-
CircleShape,
|
|
7
|
-
LineShape,
|
|
8
|
-
RectShape
|
|
9
|
-
};
|
|
10
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import { CircleShape, LineShape, RectShape } from "@visx/legend";
|
|
2
|
+
export { CircleShape, LineShape, RectShape };
|
package/dist/visx/text/index.cjs
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _visx_text = require("@visx/text");
|
|
3
|
+
Object.defineProperty(exports, "Text", {
|
|
4
|
+
enumerable: true,
|
|
5
|
+
get: function() {
|
|
6
|
+
return _visx_text.Text;
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
Object.defineProperty(exports, "getStringWidth", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: function() {
|
|
12
|
+
return _visx_text.getStringWidth;
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(exports, "useText", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function() {
|
|
18
|
+
return _visx_text.useText;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { Text, getStringWidth, useText } from "@visx/text";
|
|
2
|
+
export { Text, getStringWidth, useText };
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { Text, getStringWidth, useText } from "@visx/text";
|
|
2
|
+
export { Text, getStringWidth, useText };
|
package/dist/visx/text/index.js
CHANGED
|
@@ -1,12 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
getStringWidth,
|
|
4
|
-
useText
|
|
5
|
-
} from "../../chunk-NFRB2POF.js";
|
|
6
|
-
import "../../chunk-5WRI5ZAA.js";
|
|
7
|
-
export {
|
|
8
|
-
Text,
|
|
9
|
-
getStringWidth,
|
|
10
|
-
useText
|
|
11
|
-
};
|
|
12
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import { Text, getStringWidth, useText } from "@visx/text";
|
|
2
|
+
export { Text, getStringWidth, useText };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/charts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.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": {
|
|
@@ -56,35 +56,35 @@
|
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "pnpm run build:prod",
|
|
59
|
-
"build:prod": "
|
|
59
|
+
"build:prod": "tsdown",
|
|
60
60
|
"storybook": "cd ../storybook && pnpm run storybook:dev",
|
|
61
61
|
"test": "TZ=UTC jest --config=tests/jest.config.cjs",
|
|
62
62
|
"test-coverage": "pnpm run test --coverage",
|
|
63
63
|
"typecheck": "tsgo --noEmit"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@automattic/number-formatters": "^1.2.
|
|
67
|
-
"@babel/runtime": "7.29.
|
|
68
|
-
"@react-spring/web": "
|
|
69
|
-
"@visx/annotation": "^
|
|
70
|
-
"@visx/axis": "^
|
|
71
|
-
"@visx/curve": "^
|
|
72
|
-
"@visx/event": "^
|
|
73
|
-
"@visx/gradient": "^
|
|
74
|
-
"@visx/grid": "^
|
|
75
|
-
"@visx/group": "^
|
|
76
|
-
"@visx/legend": "^
|
|
77
|
-
"@visx/pattern": "^
|
|
78
|
-
"@visx/responsive": "^
|
|
79
|
-
"@visx/scale": "^
|
|
80
|
-
"@visx/shape": "^
|
|
81
|
-
"@visx/text": "^
|
|
82
|
-
"@visx/tooltip": "^
|
|
83
|
-
"@visx/vendor": "^
|
|
84
|
-
"@visx/xychart": "^
|
|
66
|
+
"@automattic/number-formatters": "^1.2.4",
|
|
67
|
+
"@babel/runtime": "7.29.7",
|
|
68
|
+
"@react-spring/web": "10.1.1",
|
|
69
|
+
"@visx/annotation": "^4.0.0",
|
|
70
|
+
"@visx/axis": "^4.0.0",
|
|
71
|
+
"@visx/curve": "^4.0.0",
|
|
72
|
+
"@visx/event": "^4.0.0",
|
|
73
|
+
"@visx/gradient": "^4.0.0",
|
|
74
|
+
"@visx/grid": "^4.0.0",
|
|
75
|
+
"@visx/group": "^4.0.0",
|
|
76
|
+
"@visx/legend": "^4.0.0",
|
|
77
|
+
"@visx/pattern": "^4.0.0",
|
|
78
|
+
"@visx/responsive": "^4.0.0",
|
|
79
|
+
"@visx/scale": "^4.0.0",
|
|
80
|
+
"@visx/shape": "^4.0.0",
|
|
81
|
+
"@visx/text": "^4.0.0",
|
|
82
|
+
"@visx/tooltip": "^4.0.0",
|
|
83
|
+
"@visx/vendor": "^4.0.0",
|
|
84
|
+
"@visx/xychart": "^4.0.0",
|
|
85
85
|
"@wordpress/i18n": "^6.0.0",
|
|
86
86
|
"@wordpress/icons": "^13.0.0",
|
|
87
|
-
"@wordpress/theme": "0.15.
|
|
87
|
+
"@wordpress/theme": "0.15.1",
|
|
88
88
|
"@wordpress/ui": "0.13.0",
|
|
89
89
|
"clsx": "2.1.1",
|
|
90
90
|
"date-fns": "^4.1.0",
|
|
@@ -96,43 +96,37 @@
|
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
98
98
|
"@automattic/jetpack-webpack-config": "workspace:*",
|
|
99
|
-
"@babel/core": "7.29.
|
|
100
|
-
"@babel/preset-react": "7.28.5",
|
|
101
|
-
"@babel/preset-typescript": "7.28.5",
|
|
99
|
+
"@babel/core": "^7.29.7",
|
|
102
100
|
"@storybook/addon-docs": "10.3.6",
|
|
103
101
|
"@storybook/react": "10.3.6",
|
|
104
102
|
"@testing-library/dom": "^10.0.0",
|
|
105
103
|
"@testing-library/jest-dom": "^6.0.0",
|
|
106
104
|
"@testing-library/react": "^16.0.0",
|
|
107
105
|
"@testing-library/user-event": "^14.4.3",
|
|
106
|
+
"@tsdown/css": "0.22.2",
|
|
108
107
|
"@types/jest": "^30.0.0",
|
|
109
108
|
"@types/react": "18.3.28",
|
|
110
109
|
"@types/react-dom": "18.3.7",
|
|
111
110
|
"@typescript/native-preview": "7.0.0-dev.20260225.1",
|
|
112
|
-
"@visx/glyph": "
|
|
113
|
-
"@wordpress/components": "35.0.
|
|
114
|
-
"@wordpress/element": "8.0.
|
|
115
|
-
"@wordpress/private-apis": "1.48.
|
|
111
|
+
"@visx/glyph": "4.0.0",
|
|
112
|
+
"@wordpress/components": "35.0.1",
|
|
113
|
+
"@wordpress/element": "8.0.1",
|
|
114
|
+
"@wordpress/private-apis": "1.48.1",
|
|
116
115
|
"babel-jest": "30.4.1",
|
|
117
|
-
"babel-plugin-react-remove-properties": "^0.3.1",
|
|
118
|
-
"esbuild": "0.27.4",
|
|
119
|
-
"esbuild-plugin-babel": "^0.2.3",
|
|
120
|
-
"esbuild-sass-plugin": "^3.1.0",
|
|
121
116
|
"identity-obj-proxy": "^3.0.0",
|
|
122
117
|
"jest": "30.4.2",
|
|
123
118
|
"jest-extended": "7.0.0",
|
|
124
|
-
"
|
|
125
|
-
"postcss-modules": "6.0.1",
|
|
119
|
+
"magic-string": "0.30.21",
|
|
126
120
|
"react": "18.3.1",
|
|
127
121
|
"react-dom": "18.3.1",
|
|
128
122
|
"sass-embedded": "1.97.3",
|
|
129
123
|
"storybook": "10.3.6",
|
|
130
|
-
"
|
|
124
|
+
"tsdown": "0.22.2",
|
|
131
125
|
"typescript": "5.9.3"
|
|
132
126
|
},
|
|
133
127
|
"peerDependencies": {
|
|
134
|
-
"react": "^
|
|
135
|
-
"react-dom": "^
|
|
128
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
129
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
136
130
|
},
|
|
137
131
|
"engines": {
|
|
138
132
|
"node": ">=20.11.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BaseChartProps, DataPointDate, SeriesData } from '../../types';
|
|
2
|
+
import type { RenderTooltipParams } from '../../visx/types';
|
|
2
3
|
import type { CurveType } from '../line-chart/types';
|
|
3
|
-
import type { RenderTooltipParams } from '@visx/xychart/lib/components/Tooltip';
|
|
4
4
|
import type { ReactNode } from 'react';
|
|
5
5
|
|
|
6
6
|
export interface AreaChartProps extends BaseChartProps< SeriesData[] > {
|
|
@@ -29,8 +29,8 @@ import { withResponsive } from '../private/with-responsive';
|
|
|
29
29
|
import styles from './bar-chart.module.scss';
|
|
30
30
|
import { useBarChartOptions } from './private';
|
|
31
31
|
import type { BaseChartProps, DataPointDate, SeriesData, Optional } from '../../types';
|
|
32
|
+
import type { RenderTooltipParams } from '../../visx/types';
|
|
32
33
|
import type { ResponsiveConfig } from '../private/with-responsive';
|
|
33
|
-
import type { RenderTooltipParams } from '@visx/xychart/lib/components/Tooltip';
|
|
34
34
|
import type { FC, ReactNode, ComponentType } from 'react';
|
|
35
35
|
|
|
36
36
|
export interface BarChartProps extends BaseChartProps< SeriesData[] > {
|
|
@@ -5,7 +5,7 @@ import { __ } from '@wordpress/i18n';
|
|
|
5
5
|
import { Stack } from '@wordpress/ui';
|
|
6
6
|
import clsx from 'clsx';
|
|
7
7
|
import { FC, useContext, useMemo } from 'react';
|
|
8
|
-
import { Chart
|
|
8
|
+
import { Chart } from 'react-google-charts';
|
|
9
9
|
/**
|
|
10
10
|
* Internal dependencies
|
|
11
11
|
*/
|
|
@@ -20,6 +20,8 @@ import { GeoChartProps } from './types';
|
|
|
20
20
|
const DEFAULT_FEATURE_FILL_COLOR = '#ffffff';
|
|
21
21
|
const DEFAULT_BACKGROUND_COLOR = '#ffffff';
|
|
22
22
|
|
|
23
|
+
type GoogleChartOptions = Record< string, unknown >;
|
|
24
|
+
|
|
23
25
|
/**
|
|
24
26
|
* Renders a geographical chart using Google Charts GeoChart to visualize data.
|
|
25
27
|
*
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
&.is-overlay {
|
|
32
32
|
grid-template: "overlap" 1fr / 1fr;
|
|
33
33
|
|
|
34
|
-
/* contains a single cell
|
|
34
|
+
/* contains a single cell called 'overlap' */
|
|
35
35
|
|
|
36
36
|
>* {
|
|
37
37
|
grid-area: overlap;
|
|
@@ -45,6 +45,9 @@
|
|
|
45
45
|
.bar {
|
|
46
46
|
height: 100%;
|
|
47
47
|
min-height: 6px;
|
|
48
|
+
// Keeps a sliver of bar visible for tiny shares, and stops the interactive
|
|
49
|
+
// row's fixed-pixel hover inset from collapsing a narrow bar to nothing.
|
|
50
|
+
min-width: var(--wpds-dimension-size-5xs, 4px);
|
|
48
51
|
|
|
49
52
|
border-radius: var(--a8c--charts--leaderboard--bar--border-radius, 9999px);
|
|
50
53
|
z-index: -1; /* places it behind the label */
|
|
@@ -80,3 +83,112 @@
|
|
|
80
83
|
font-size: var(--wpds-typography-font-size-md, 13px);
|
|
81
84
|
font-style: italic;
|
|
82
85
|
}
|
|
86
|
+
|
|
87
|
+
.interactiveRow {
|
|
88
|
+
--focus-ring-width: var(--wpds-border-width-focus, var(--wp-admin-border-width-focus, 2px));
|
|
89
|
+
|
|
90
|
+
display: grid;
|
|
91
|
+
grid-column: 1 / -1;
|
|
92
|
+
grid-template-columns: subgrid;
|
|
93
|
+
align-items: center;
|
|
94
|
+
position: relative;
|
|
95
|
+
|
|
96
|
+
// Native button reset.
|
|
97
|
+
box-sizing: border-box;
|
|
98
|
+
appearance: none;
|
|
99
|
+
width: 100%;
|
|
100
|
+
margin: 0;
|
|
101
|
+
// Reserve a ring's worth of space on every edge so the inset focus outline
|
|
102
|
+
// hugs the row without the bar painting over it (see :focus-visible below).
|
|
103
|
+
padding: var(--focus-ring-width);
|
|
104
|
+
border: 0;
|
|
105
|
+
background: none;
|
|
106
|
+
color: inherit;
|
|
107
|
+
font: inherit;
|
|
108
|
+
text-align: inherit;
|
|
109
|
+
cursor: pointer;
|
|
110
|
+
|
|
111
|
+
// On hover/focus the bar dims and its inline-end edge pulls back while the
|
|
112
|
+
// value/delta slides toward the inline start to clear room for the chevron
|
|
113
|
+
// revealed at the inline-end edge. The value always slides the full inset
|
|
114
|
+
// (it must clear the chevron), but the bar pulls back in proportion to its
|
|
115
|
+
// length (see getBarWidth in leaderboard-chart.tsx): the full-length bar —
|
|
116
|
+
// the one that reaches the value — pulls back the whole inset to keep its
|
|
117
|
+
// gap, while shorter bars move proportionally less, down to ~0. The bar is
|
|
118
|
+
// a grid item aligned to the inline start, so its inset mirrors in RTL; the
|
|
119
|
+
// value slide is given an RTL override below. No row background.
|
|
120
|
+
.bar {
|
|
121
|
+
transition: opacity 0.15s ease, width 0.15s ease;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.valueContainer {
|
|
125
|
+
transition: transform 0.15s ease;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
&:hover,
|
|
129
|
+
&:focus-visible {
|
|
130
|
+
|
|
131
|
+
// Fixed reserve for the chevron — the value slides by it in full, and the
|
|
132
|
+
// bar width inset (see leaderboard-chart.tsx) scales it by the bar's share.
|
|
133
|
+
--a8c--charts--leaderboard--bar--hover-inset: var(--wpds-dimension-gap-2xl, 32px);
|
|
134
|
+
|
|
135
|
+
.bar {
|
|
136
|
+
opacity: 0.5;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.valueContainer {
|
|
140
|
+
transform: translateX(calc(var(--a8c--charts--leaderboard--bar--hover-inset) * -1));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.chevron {
|
|
144
|
+
opacity: 1;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// In RTL the value slides toward the inline start the other way (rightward).
|
|
149
|
+
[dir="rtl"] &:hover .valueContainer,
|
|
150
|
+
[dir="rtl"] &:focus-visible .valueContainer {
|
|
151
|
+
transform: translateX(var(--a8c--charts--leaderboard--bar--hover-inset));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
&:focus-visible {
|
|
155
|
+
border-radius: var(--wpds-border-radius-sm, 2px);
|
|
156
|
+
outline:
|
|
157
|
+
var(--focus-ring-width) solid
|
|
158
|
+
var(--wpds-color-stroke-focus-brand, var(--wp-admin-theme-color, #3858e9));
|
|
159
|
+
// Inset by its own width so the outer edge hugs the row boundary: the
|
|
160
|
+
// outline then sits in the row's padding (also a ring width), clear of the
|
|
161
|
+
// bar, and stays inside the overflow:auto container that would clip an
|
|
162
|
+
// outset ring.
|
|
163
|
+
outline-offset: calc(-1 * var(--focus-ring-width));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.chevron {
|
|
168
|
+
position: absolute;
|
|
169
|
+
inset-inline-end: var(--wpds-dimension-padding-xs, 4px);
|
|
170
|
+
top: 0;
|
|
171
|
+
bottom: 0;
|
|
172
|
+
margin-block: auto;
|
|
173
|
+
opacity: 0;
|
|
174
|
+
color: var(--wpds-color-fg-content-neutral-weak, #707070);
|
|
175
|
+
transition: opacity 0.15s ease;
|
|
176
|
+
pointer-events: none;
|
|
177
|
+
|
|
178
|
+
// The glyph points toward the inline end, so flip it to point left in RTL.
|
|
179
|
+
[dir="rtl"] & {
|
|
180
|
+
transform: scaleX(-1);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@media ( prefers-reduced-motion: reduce ) {
|
|
185
|
+
|
|
186
|
+
.interactiveRow {
|
|
187
|
+
|
|
188
|
+
.bar,
|
|
189
|
+
.valueContainer,
|
|
190
|
+
.chevron {
|
|
191
|
+
transition: none;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { __experimentalGrid as Grid } from '@wordpress/components';
|
|
3
3
|
import { Fragment } from '@wordpress/element';
|
|
4
4
|
import { __ } from '@wordpress/i18n';
|
|
5
|
+
import { Icon, chevronRight } from '@wordpress/icons';
|
|
5
6
|
import { Stack, Text } from '@wordpress/ui';
|
|
6
7
|
import clsx from 'clsx';
|
|
7
8
|
import { useContext, useMemo, type FC } from 'react';
|
|
@@ -51,7 +52,20 @@ const defaultDeltaFormatter = ( value: number ): string => {
|
|
|
51
52
|
} );
|
|
52
53
|
};
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Build a bar's width. A hover-inset CSS variable (0 by default) is subtracted
|
|
57
|
+
* on hover, scaled by the bar's share so the pull-back is proportional to its
|
|
58
|
+
* length: the full-length (100%) bar — the one that reaches the value — pulls
|
|
59
|
+
* back the whole inset to keep its gap with the value, while shorter bars pull
|
|
60
|
+
* back proportionally less, down to ~0 for a very short bar.
|
|
61
|
+
*
|
|
62
|
+
* @param share - The bar's share of the row width, as a percentage.
|
|
63
|
+
* @return A CSS width value.
|
|
64
|
+
*/
|
|
65
|
+
const getBarWidth = ( share: number ): string =>
|
|
66
|
+
`calc(${ share }% - var(--a8c--charts--leaderboard--bar--hover-inset, 0px) * ${ share } / 100)`;
|
|
67
|
+
|
|
68
|
+
const BarLabel = ( { label }: { label: LeaderboardEntry[ 'label' ] } ) => (
|
|
55
69
|
<>{ typeof label === 'string' ? <Text className={ styles.label }>{ label }</Text> : label }</>
|
|
56
70
|
);
|
|
57
71
|
|
|
@@ -87,7 +101,7 @@ const BarWithLabel = ( {
|
|
|
87
101
|
[ styles[ 'bar--animated' ] ]: animation,
|
|
88
102
|
} ) }
|
|
89
103
|
style={ {
|
|
90
|
-
width: entry.currentShare
|
|
104
|
+
width: getBarWidth( entry.currentShare ),
|
|
91
105
|
backgroundColor: primaryColor,
|
|
92
106
|
} }
|
|
93
107
|
></div>
|
|
@@ -99,7 +113,7 @@ const BarWithLabel = ( {
|
|
|
99
113
|
[ styles[ 'bar--animated' ] ]: animation,
|
|
100
114
|
} ) }
|
|
101
115
|
style={ {
|
|
102
|
-
width: entry.previousShare
|
|
116
|
+
width: getBarWidth( entry.previousShare ),
|
|
103
117
|
backgroundColor: secondaryColor,
|
|
104
118
|
} }
|
|
105
119
|
></div>
|
|
@@ -324,8 +338,8 @@ const LeaderboardChartInternal: FC< LeaderboardChartProps > = ( {
|
|
|
324
338
|
const colorIndex = Math.sign( entry.delta ) + 1;
|
|
325
339
|
const deltaColor = deltaColors[ colorIndex ];
|
|
326
340
|
|
|
327
|
-
|
|
328
|
-
|
|
341
|
+
const rowCells = (
|
|
342
|
+
<>
|
|
329
343
|
<Stack direction="column" gap={ labelSpacing }>
|
|
330
344
|
<BarWithLabel
|
|
331
345
|
entry={ entry }
|
|
@@ -354,8 +368,25 @@ const LeaderboardChartInternal: FC< LeaderboardChartProps > = ( {
|
|
|
354
368
|
</Text>
|
|
355
369
|
) }
|
|
356
370
|
</Stack>
|
|
357
|
-
|
|
371
|
+
</>
|
|
358
372
|
);
|
|
373
|
+
|
|
374
|
+
if ( entry.onClick ) {
|
|
375
|
+
return (
|
|
376
|
+
<button
|
|
377
|
+
key={ entry.id }
|
|
378
|
+
type="button"
|
|
379
|
+
className={ styles.interactiveRow }
|
|
380
|
+
onClick={ entry.onClick }
|
|
381
|
+
aria-label={ entry.ariaLabel }
|
|
382
|
+
>
|
|
383
|
+
{ rowCells }
|
|
384
|
+
<Icon className={ styles.chevron } icon={ chevronRight } size={ 24 } />
|
|
385
|
+
</button>
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return <Fragment key={ entry.id }>{ rowCells }</Fragment>;
|
|
359
390
|
} ) }
|
|
360
391
|
</Grid>
|
|
361
392
|
) }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
2
3
|
import LeaderboardChart from '../leaderboard-chart';
|
|
3
4
|
import type { LeaderboardEntry } from '../../../types';
|
|
4
5
|
|
|
@@ -367,4 +368,84 @@ describe( 'LeaderboardChart', () => {
|
|
|
367
368
|
expect( chartContainer ).toHaveStyle( { width: '500px', height: '240px' } );
|
|
368
369
|
} );
|
|
369
370
|
} );
|
|
371
|
+
|
|
372
|
+
describe( 'Interactive items', () => {
|
|
373
|
+
it( 'renders a button for an entry with onClick', () => {
|
|
374
|
+
render( <LeaderboardChart data={ [ { ...mockData[ 0 ], onClick: jest.fn() } ] } /> );
|
|
375
|
+
expect( screen.getByRole( 'button' ) ).toBeInTheDocument();
|
|
376
|
+
expect( screen.getByRole( 'button' ).tagName ).toBe( 'BUTTON' );
|
|
377
|
+
} );
|
|
378
|
+
|
|
379
|
+
it( 'calls onClick when the row is clicked', async () => {
|
|
380
|
+
const user = userEvent.setup();
|
|
381
|
+
const onClick = jest.fn();
|
|
382
|
+
render( <LeaderboardChart data={ [ { ...mockData[ 0 ], onClick } ] } /> );
|
|
383
|
+
await user.click( screen.getByRole( 'button' ) );
|
|
384
|
+
expect( onClick ).toHaveBeenCalledTimes( 1 );
|
|
385
|
+
} );
|
|
386
|
+
|
|
387
|
+
it( 'activates onClick via the keyboard (Enter and Space)', async () => {
|
|
388
|
+
const user = userEvent.setup();
|
|
389
|
+
const onClick = jest.fn();
|
|
390
|
+
render( <LeaderboardChart data={ [ { ...mockData[ 0 ], onClick } ] } /> );
|
|
391
|
+
|
|
392
|
+
await user.tab();
|
|
393
|
+
expect( screen.getByRole( 'button' ) ).toHaveFocus();
|
|
394
|
+
|
|
395
|
+
await user.keyboard( '[Enter]' );
|
|
396
|
+
await user.keyboard( '[Space]' );
|
|
397
|
+
expect( onClick ).toHaveBeenCalledTimes( 2 );
|
|
398
|
+
} );
|
|
399
|
+
|
|
400
|
+
it( 'gives the interactive row an accessible name from the label and value', () => {
|
|
401
|
+
render( <LeaderboardChart data={ [ { ...mockData[ 0 ], onClick: jest.fn() } ] } /> );
|
|
402
|
+
// mockData[0] label is 'Direct', currentValue 12500 → '12.5K'
|
|
403
|
+
expect( screen.getByRole( 'button' ) ).toHaveAccessibleName( /Direct.*12\.5K/ );
|
|
404
|
+
} );
|
|
405
|
+
|
|
406
|
+
it( 'derives the accessible name from an image label via its alt text', () => {
|
|
407
|
+
render(
|
|
408
|
+
<LeaderboardChart
|
|
409
|
+
data={ [
|
|
410
|
+
{
|
|
411
|
+
...mockData[ 0 ],
|
|
412
|
+
label: <img src="https://example.com/flag.svg" alt="United States" />,
|
|
413
|
+
onClick: jest.fn(),
|
|
414
|
+
},
|
|
415
|
+
] }
|
|
416
|
+
/>
|
|
417
|
+
);
|
|
418
|
+
expect( screen.getByRole( 'button' ) ).toHaveAccessibleName( /United States.*12\.5K/ );
|
|
419
|
+
} );
|
|
420
|
+
|
|
421
|
+
it( 'uses ariaLabel as the accessible name when provided', () => {
|
|
422
|
+
render(
|
|
423
|
+
<LeaderboardChart
|
|
424
|
+
data={ [
|
|
425
|
+
{
|
|
426
|
+
...mockData[ 0 ],
|
|
427
|
+
label: <img src="https://example.com/flag.svg" alt="" />,
|
|
428
|
+
ariaLabel: 'United States: 12.5K visitors',
|
|
429
|
+
onClick: jest.fn(),
|
|
430
|
+
},
|
|
431
|
+
] }
|
|
432
|
+
/>
|
|
433
|
+
);
|
|
434
|
+
expect( screen.getByRole( 'button' ) ).toHaveAccessibleName(
|
|
435
|
+
'United States: 12.5K visitors'
|
|
436
|
+
);
|
|
437
|
+
} );
|
|
438
|
+
|
|
439
|
+
it( 'does not render a button for entries without onClick', () => {
|
|
440
|
+
render( <LeaderboardChart data={ [ mockData[ 0 ] ] } /> );
|
|
441
|
+
expect( screen.queryByRole( 'button' ) ).not.toBeInTheDocument();
|
|
442
|
+
} );
|
|
443
|
+
|
|
444
|
+
it( 'renders a button only for interactive entries in mixed data', () => {
|
|
445
|
+
render(
|
|
446
|
+
<LeaderboardChart data={ [ { ...mockData[ 0 ], onClick: jest.fn() }, mockData[ 1 ] ] } />
|
|
447
|
+
);
|
|
448
|
+
expect( screen.getAllByRole( 'button' ) ).toHaveLength( 1 );
|
|
449
|
+
} );
|
|
450
|
+
} );
|
|
370
451
|
} );
|
|
@@ -42,10 +42,10 @@ import styles from './line-chart.module.scss';
|
|
|
42
42
|
import { LineChartAnnotation, LineChartAnnotationsOverlay, LineChartGlyph } from './private';
|
|
43
43
|
import type { RenderLineGlyphProps, LineChartProps, TooltipDatum } from './types';
|
|
44
44
|
import type { DataPoint, DataPointDate, SeriesData, Optional } from '../../types';
|
|
45
|
+
import type { RenderTooltipParams } from '../../visx/types';
|
|
45
46
|
import type { ResponsiveConfig } from '../private/with-responsive';
|
|
46
47
|
import type { TickFormatter } from '@visx/axis';
|
|
47
48
|
import type { GlyphProps } from '@visx/xychart';
|
|
48
|
-
import type { RenderTooltipParams } from '@visx/xychart/lib/components/Tooltip';
|
|
49
49
|
import type { FC, Ref } from 'react';
|
|
50
50
|
|
|
51
51
|
const defaultRenderGlyph = < Datum extends object >( props: RenderLineGlyphProps< Datum > ) => {
|