@automattic/charts 4.15.0 → 4.15.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 +8 -0
- package/dist/index.cjs +13 -1170
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +27 -1
- package/dist/index.js +14 -886
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
- package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +43 -1
- package/src/charts/leaderboard-chart/leaderboard-chart.tsx +25 -20
- package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +15 -20
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/charts",
|
|
3
|
-
"version": "4.15.
|
|
3
|
+
"version": "4.15.1",
|
|
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": {
|
|
@@ -111,7 +111,6 @@
|
|
|
111
111
|
"@types/react-dom": "19.3.0",
|
|
112
112
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
113
113
|
"@visx/glyph": "4.0.0",
|
|
114
|
-
"@wordpress/components": "44.0.0",
|
|
115
114
|
"@wordpress/element": "10.1.0",
|
|
116
115
|
"@wordpress/private-apis": "3.1.0",
|
|
117
116
|
"@wordpress/theme-build-tools": "1.0.0",
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Room for the chevron an interactive row reveals on hover or focus.
|
|
2
|
+
$hover-reserve: var(--wpds-dimension-gap-2xl);
|
|
3
|
+
|
|
1
4
|
.leaderboardChart {
|
|
2
5
|
transition:
|
|
3
6
|
opacity var(--wpds-motion-duration-lg)
|
|
@@ -28,6 +31,17 @@
|
|
|
28
31
|
}
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
.grid {
|
|
35
|
+
display: grid;
|
|
36
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
37
|
+
row-gap: var(--a8c-charts-dimension-leaderboard-row-gap);
|
|
38
|
+
column-gap: var(--a8c-charts-dimension-leaderboard-column-gap);
|
|
39
|
+
|
|
40
|
+
&--comparison {
|
|
41
|
+
grid-template-columns: minmax(0, 1fr) auto auto;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
31
45
|
.barWithLabelContainer {
|
|
32
46
|
display: grid;
|
|
33
47
|
align-items: center;
|
|
@@ -233,6 +247,33 @@ $focus-ring-width: var(--wpds-border-width-focus);
|
|
|
233
247
|
var(--wpds-motion-easing-subtle);
|
|
234
248
|
}
|
|
235
249
|
|
|
250
|
+
.barWithLabelContainer > :not(.bar, .barTrack) {
|
|
251
|
+
max-inline-size: 100%;
|
|
252
|
+
transition:
|
|
253
|
+
max-inline-size var(--wpds-motion-duration-sm)
|
|
254
|
+
var(--wpds-motion-easing-subtle);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Single-line, so the hover cap below moves the ellipsis instead of
|
|
258
|
+
// rewrapping the label and growing the row.
|
|
259
|
+
.label {
|
|
260
|
+
overflow: hidden;
|
|
261
|
+
text-overflow: ellipsis;
|
|
262
|
+
white-space: nowrap;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// The label stays clear of the slid value on any hover or focus, nested
|
|
266
|
+
// controls included: lifting it while a trailing action is hovered would
|
|
267
|
+
// move that action out from under the pointer.
|
|
268
|
+
&:hover,
|
|
269
|
+
&:focus-visible,
|
|
270
|
+
&:has(:focus-visible) {
|
|
271
|
+
|
|
272
|
+
.barWithLabelContainer > :not(.bar, .barTrack) {
|
|
273
|
+
max-inline-size: calc(100% - #{$hover-reserve});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
236
277
|
.valueContainer {
|
|
237
278
|
transition:
|
|
238
279
|
transform var(--wpds-motion-duration-sm)
|
|
@@ -246,7 +287,7 @@ $focus-ring-width: var(--wpds-border-width-focus);
|
|
|
246
287
|
|
|
247
288
|
// Fixed reserve for the chevron — the value slides by it in full, and the
|
|
248
289
|
// bar width inset (see leaderboard-chart.tsx) scales it by the bar's share.
|
|
249
|
-
--a8c-charts-dimension-leaderboard-bar-hover-inset:
|
|
290
|
+
--a8c-charts-dimension-leaderboard-bar-hover-inset: #{$hover-reserve};
|
|
250
291
|
|
|
251
292
|
.bar {
|
|
252
293
|
opacity: 0.5;
|
|
@@ -343,6 +384,7 @@ $focus-ring-width: var(--wpds-border-width-focus);
|
|
|
343
384
|
.interactiveRow {
|
|
344
385
|
|
|
345
386
|
.bar,
|
|
387
|
+
.barWithLabelContainer > :not(.bar, .barTrack),
|
|
346
388
|
.valueContainer,
|
|
347
389
|
.chevron {
|
|
348
390
|
transition: none;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
/* eslint-disable @wordpress/no-unsafe-wp-apis */
|
|
2
|
-
import { __experimentalGrid as Grid, VisuallyHidden } from '@wordpress/components';
|
|
3
1
|
import { __ } from '@wordpress/i18n';
|
|
4
2
|
import { Icon, chevronRight } from '@wordpress/icons';
|
|
5
|
-
import { Stack, Text } from '@wordpress/ui';
|
|
3
|
+
import { Stack, Text, VisuallyHidden } from '@wordpress/ui';
|
|
6
4
|
import clsx from 'clsx';
|
|
7
5
|
import { useContext, useMemo, type FC, type MouseEvent } from 'react';
|
|
8
6
|
import { Legend } from '../../components/legend';
|
|
@@ -30,12 +28,6 @@ import {
|
|
|
30
28
|
import type { LeaderboardChartProps } from './types';
|
|
31
29
|
import type { LeaderboardEntry } from '../../types';
|
|
32
30
|
|
|
33
|
-
// Handed to `Grid` as a chain rather than set in this component's stylesheet: `Grid` writes
|
|
34
|
-
// its own gaps through an Emotion class, which a module class of equal specificity would not
|
|
35
|
-
// reliably outrank. The chain lands on the element and resolves there.
|
|
36
|
-
const ROW_GAP = 'var(--a8c-charts-dimension-leaderboard-row-gap, 12px)';
|
|
37
|
-
const COLUMN_GAP = 'var(--a8c-charts-dimension-leaderboard-column-gap, 4px)';
|
|
38
|
-
|
|
39
31
|
/** Indexed by `Math.sign( delta ) + 1`, so [negative, neutral, positive]. */
|
|
40
32
|
const DELTA_TREND_CLASS = [ 'deltaValue--down', 'deltaValue--neutral', 'deltaValue--up' ] as const;
|
|
41
33
|
|
|
@@ -107,8 +99,22 @@ function forwardCellClickToRowButton( event: MouseEvent< HTMLDivElement > ) {
|
|
|
107
99
|
event.currentTarget.querySelector< HTMLButtonElement >( ':scope > button' )?.click();
|
|
108
100
|
}
|
|
109
101
|
|
|
110
|
-
const BarLabel = ( {
|
|
111
|
-
|
|
102
|
+
const BarLabel = ( {
|
|
103
|
+
label,
|
|
104
|
+
isInteractiveRow,
|
|
105
|
+
}: {
|
|
106
|
+
label: LeaderboardEntry[ 'label' ];
|
|
107
|
+
isInteractiveRow: boolean;
|
|
108
|
+
} ) => (
|
|
109
|
+
<>
|
|
110
|
+
{ typeof label === 'string' ? (
|
|
111
|
+
<Text className={ styles.label } title={ isInteractiveRow ? label : undefined }>
|
|
112
|
+
{ label }
|
|
113
|
+
</Text>
|
|
114
|
+
) : (
|
|
115
|
+
label
|
|
116
|
+
) }
|
|
117
|
+
</>
|
|
112
118
|
);
|
|
113
119
|
|
|
114
120
|
/** The largest negative and positive share magnitudes, which size each side of zero. */
|
|
@@ -213,7 +219,7 @@ const BarWithLabel = ( {
|
|
|
213
219
|
} ) }
|
|
214
220
|
data-testid={ divergingScale ? 'leaderboard-chart-diverging-row' : undefined }
|
|
215
221
|
>
|
|
216
|
-
<BarLabel label={ entry.label } />
|
|
222
|
+
<BarLabel label={ entry.label } isInteractiveRow={ Boolean( entry.onClick ) } />
|
|
217
223
|
{ bars }
|
|
218
224
|
</div>
|
|
219
225
|
);
|
|
@@ -388,8 +394,7 @@ const LeaderboardChartInternal: FC< LeaderboardChartProps > = ( {
|
|
|
388
394
|
// Comparing adds a delta track so the values and the deltas each line up in a column
|
|
389
395
|
// of their own; with either series hidden the grid keeps its two tracks.
|
|
390
396
|
const showComparisonColumn = withComparison && isComparisonVisible;
|
|
391
|
-
const
|
|
392
|
-
showComparisonColumn && isPrimaryVisible ? 'minmax(0, 1fr) auto auto' : 'minmax(0, 1fr) auto';
|
|
397
|
+
const hasDeltaColumn = showComparisonColumn && isPrimaryVisible;
|
|
393
398
|
|
|
394
399
|
// Handle empty or undefined data
|
|
395
400
|
if ( ! data || data.length === 0 ) {
|
|
@@ -480,13 +485,13 @@ const LeaderboardChartInternal: FC< LeaderboardChartProps > = ( {
|
|
|
480
485
|
{ getAllHiddenMessage( legendInteractive, 'series' ) }
|
|
481
486
|
</div>
|
|
482
487
|
) : (
|
|
483
|
-
<
|
|
488
|
+
<div
|
|
484
489
|
role="list"
|
|
485
490
|
aria-label={ ariaLabel ?? __( 'Leaderboard chart', 'jetpack-charts' ) }
|
|
486
|
-
templateColumns={ templateColumns }
|
|
487
|
-
rowGap={ ROW_GAP }
|
|
488
|
-
columnGap={ COLUMN_GAP }
|
|
489
491
|
data-leaderboard-grid
|
|
492
|
+
className={ clsx( styles.grid, {
|
|
493
|
+
[ styles[ 'grid--comparison' ] ]: hasDeltaColumn,
|
|
494
|
+
} ) }
|
|
490
495
|
>
|
|
491
496
|
{ resolvedData.map( ( entry, rowIndex ) => {
|
|
492
497
|
// visibility, not clipping: hidden rows keep their geometry (so a
|
|
@@ -544,7 +549,7 @@ const LeaderboardChartInternal: FC< LeaderboardChartProps > = ( {
|
|
|
544
549
|
) }
|
|
545
550
|
>
|
|
546
551
|
<span aria-hidden="true">—</span>
|
|
547
|
-
<VisuallyHidden
|
|
552
|
+
<VisuallyHidden render={ <span /> }>
|
|
548
553
|
{ hasPreviousPeriodValue
|
|
549
554
|
? __( 'Percentage change unavailable', 'jetpack-charts' )
|
|
550
555
|
: __( 'No comparison data', 'jetpack-charts' ) }
|
|
@@ -616,7 +621,7 @@ const LeaderboardChartInternal: FC< LeaderboardChartProps > = ( {
|
|
|
616
621
|
</div>
|
|
617
622
|
);
|
|
618
623
|
} ) }
|
|
619
|
-
</
|
|
624
|
+
</div>
|
|
620
625
|
) }
|
|
621
626
|
</div>
|
|
622
627
|
</ChartLayout>
|
|
@@ -802,18 +802,25 @@ describe( 'LeaderboardChart', () => {
|
|
|
802
802
|
);
|
|
803
803
|
expect( screen.getAllByRole( 'button' ) ).toHaveLength( 1 );
|
|
804
804
|
} );
|
|
805
|
+
|
|
806
|
+
it( 'titles only an interactive row’s string label with its full text', () => {
|
|
807
|
+
const [ interactive, inert ] = mockData;
|
|
808
|
+
render( <LeaderboardChart data={ [ { ...interactive, onClick: jest.fn() }, inert ] } /> );
|
|
809
|
+
expect( screen.getByText( String( interactive.label ) ) ).toHaveAttribute(
|
|
810
|
+
'title',
|
|
811
|
+
interactive.label
|
|
812
|
+
);
|
|
813
|
+
expect( screen.getByText( String( inert.label ) ) ).not.toHaveAttribute( 'title' );
|
|
814
|
+
} );
|
|
805
815
|
} );
|
|
806
816
|
|
|
807
|
-
describe( 'grid
|
|
808
|
-
|
|
809
|
-
const grid = () => document.querySelector< HTMLElement >( '[data-leaderboard-grid]' );
|
|
810
|
-
const templateColumns = () =>
|
|
811
|
-
grid().style.getPropertyValue( '--wp-components-grid-template-columns' );
|
|
817
|
+
describe( 'grid columns', () => {
|
|
818
|
+
const grid = () => screen.getByRole( 'list', { name: 'Leaderboard chart' } );
|
|
812
819
|
|
|
813
820
|
it( 'lays the rows out without a delta track when not comparing', () => {
|
|
814
821
|
render( <LeaderboardChart data={ mockData } withComparison={ false } /> );
|
|
815
822
|
|
|
816
|
-
expect(
|
|
823
|
+
expect( grid() ).not.toHaveClass( 'grid--comparison' );
|
|
817
824
|
} );
|
|
818
825
|
|
|
819
826
|
it.each( [ [ 'Previous period' ], [ 'Current period' ] ] )(
|
|
@@ -829,24 +836,12 @@ describe( 'LeaderboardChart', () => {
|
|
|
829
836
|
/>
|
|
830
837
|
);
|
|
831
838
|
|
|
832
|
-
expect(
|
|
839
|
+
expect( grid() ).toHaveClass( 'grid--comparison' );
|
|
833
840
|
|
|
834
841
|
await user.click( screen.getByRole( 'button', { name: new RegExp( legendItem ) } ) );
|
|
835
842
|
|
|
836
|
-
expect(
|
|
843
|
+
expect( grid() ).not.toHaveClass( 'grid--comparison' );
|
|
837
844
|
}
|
|
838
845
|
);
|
|
839
|
-
|
|
840
|
-
it( 'hands the catalog role to the grid', () => {
|
|
841
|
-
render( <LeaderboardChart data={ mockData } /> );
|
|
842
|
-
|
|
843
|
-
// Grid writes its gaps into custom properties and reads them from a stylesheet.
|
|
844
|
-
expect( grid().style.getPropertyValue( '--wp-components-grid-row-gap' ) ).toBe(
|
|
845
|
-
'var(--a8c-charts-dimension-leaderboard-row-gap, 12px)'
|
|
846
|
-
);
|
|
847
|
-
expect( grid().style.getPropertyValue( '--wp-components-grid-column-gap' ) ).toBe(
|
|
848
|
-
'var(--a8c-charts-dimension-leaderboard-column-gap, 4px)'
|
|
849
|
-
);
|
|
850
|
-
} );
|
|
851
846
|
} );
|
|
852
847
|
} );
|