@automattic/charts 4.8.0 → 4.9.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 +14 -0
- package/dist/index.cjs +624 -228
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +58 -8
- package/dist/index.d.cts +42 -8
- package/dist/index.d.ts +42 -8
- package/dist/index.js +628 -232
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/charts/area-chart/area-chart.tsx +5 -2
- package/src/charts/area-chart/test/area-chart.test.tsx +23 -14
- package/src/charts/area-chart/test/tooltip-resolution.test.tsx +2 -2
- package/src/charts/area-chart/types.ts +2 -0
- package/src/charts/bar-chart/bar-chart.tsx +5 -2
- package/src/charts/bar-chart/test/bar-chart.test.tsx +82 -63
- package/src/charts/bar-chart/types.ts +2 -0
- package/src/charts/bar-list-chart/test/bar-list-chart.test.tsx +3 -1
- package/src/charts/heatmap-chart/heatmap-chart.module.scss +10 -4
- package/src/charts/heatmap-chart/heatmap-chart.tsx +144 -37
- package/src/charts/heatmap-chart/private/column-groups.ts +79 -8
- package/src/charts/heatmap-chart/private/fit-compact-cells.ts +72 -0
- package/src/charts/heatmap-chart/private/heatmap-legend.tsx +14 -2
- package/src/charts/heatmap-chart/private/index.ts +4 -2
- package/src/charts/heatmap-chart/test/column-groups.test.ts +41 -4
- package/src/charts/heatmap-chart/test/fit-compact-cells.test.ts +59 -0
- package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +132 -1
- package/src/charts/heatmap-chart/types.ts +2 -0
- package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +46 -4
- package/src/charts/leaderboard-chart/leaderboard-chart.tsx +37 -1
- package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +53 -0
- package/src/charts/leaderboard-chart/types.ts +6 -1
- package/src/charts/line-chart/line-chart.tsx +5 -2
- package/src/charts/line-chart/private/line-chart-annotation-label-popover.tsx +19 -9
- package/src/charts/line-chart/test/default-tooltip-formatting.test.tsx +1 -1
- package/src/charts/line-chart/test/line-chart.test.tsx +45 -35
- package/src/charts/line-chart/test/tooltip-resolution.test.tsx +2 -2
- package/src/charts/line-chart/types.ts +5 -3
- package/src/charts/pie-chart/pie-chart.tsx +149 -139
- package/src/charts/pie-chart/test/pie-chart.test.tsx +171 -0
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +122 -120
- package/src/charts/pie-semi-circle-chart/test/pie-semi-circle-chart.test.tsx +54 -0
- package/src/charts/private/chart-composition/use-chart-children.ts +11 -6
- package/src/charts/private/pie-keyboard-navigation/index.ts +4 -0
- package/src/charts/private/pie-keyboard-navigation/order-arcs-for-navigation.ts +10 -0
- package/src/charts/private/pie-keyboard-navigation/pie-keyboard-navigation.module.scss +16 -0
- package/src/charts/private/pie-keyboard-navigation/pie-selection-announcement.tsx +14 -0
- package/src/charts/private/pie-keyboard-navigation/pie-selection-output.tsx +82 -0
- package/src/charts/private/pie-keyboard-navigation/pie-tooltip.tsx +44 -0
- package/src/charts/private/pie-keyboard-navigation/selected-segment-ring.tsx +47 -0
- package/src/charts/private/pie-keyboard-navigation/test/order-arcs-for-navigation.test.ts +34 -0
- package/src/charts/private/pie-keyboard-navigation/test/selected-segment-ring.test.ts +14 -0
- package/src/charts/private/pie-keyboard-navigation/use-pie-keyboard-navigation.ts +139 -0
- package/src/charts/private/x-zoom/test/x-zoom.test.tsx +3 -2
- package/src/components/tooltip/test/xy-chart-tooltip.test.tsx +20 -19
- package/src/components/tooltip/xy-chart-tooltip.tsx +3 -3
- package/src/types.ts +18 -2
|
@@ -42,12 +42,12 @@ describe( 'BarChart', () => {
|
|
|
42
42
|
// visx renders bars and grid lines without accessible roles or configurable test IDs.
|
|
43
43
|
const getBarRects = () => {
|
|
44
44
|
// eslint-disable-next-line testing-library/no-node-access -- See the visx node constraint above.
|
|
45
|
-
return screen.getByRole( '
|
|
45
|
+
return screen.getByRole( 'application' ).querySelectorAll( '.visx-bar-group rect' );
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
const getGridLines = () => {
|
|
49
49
|
// eslint-disable-next-line testing-library/no-node-access -- visx grid lines cannot receive test IDs.
|
|
50
|
-
return screen.getByRole( '
|
|
50
|
+
return screen.getByRole( 'application' ).querySelectorAll( '.visx-rows line' );
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
const renderWithTheme = ( props = {}, children = undefined ) => {
|
|
@@ -151,20 +151,21 @@ describe( 'BarChart', () => {
|
|
|
151
151
|
|
|
152
152
|
const pointer = ( type: string, clientX: number, clientY: number ) => {
|
|
153
153
|
// visx owns the pointer capture rect and does not expose an attribute prop for it.
|
|
154
|
+
const chart = screen.getByRole( 'application' );
|
|
154
155
|
// eslint-disable-next-line testing-library/no-node-access
|
|
155
|
-
const target =
|
|
156
|
+
const target = chart.querySelector( 'svg > rect[fill="transparent"]' );
|
|
156
157
|
fireEvent( target, new MouseEvent( type, { bubbles: true, clientX, clientY } ) );
|
|
157
158
|
};
|
|
158
159
|
|
|
159
160
|
const hover = ( clientX: number, clientY: number ) =>
|
|
160
161
|
pointer( 'pointermove', clientX, clientY );
|
|
161
162
|
|
|
162
|
-
test( 'Escape returns focus to the
|
|
163
|
+
test( 'Escape returns focus to the chart without reopening either tooltip', async () => {
|
|
163
164
|
jest.useFakeTimers();
|
|
164
165
|
try {
|
|
165
166
|
const user = userEvent.setup( { advanceTimers: jest.advanceTimersByTime } );
|
|
166
167
|
renderWithTheme( { withTooltips: true } );
|
|
167
|
-
const chart = screen.getByRole( '
|
|
168
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
168
169
|
await user.tab();
|
|
169
170
|
await user.keyboard( '{ArrowRight}' );
|
|
170
171
|
expect( screen.getByTestId( 'chart-tooltip-0' ) ).toHaveFocus();
|
|
@@ -369,7 +370,7 @@ describe( 'BarChart', () => {
|
|
|
369
370
|
unmount();
|
|
370
371
|
renderWithTheme( { withBandHighlight: true, onBandHighlightChange } );
|
|
371
372
|
expect( warn ).toHaveBeenCalledTimes( 1 );
|
|
372
|
-
expect( screen.getByRole( '
|
|
373
|
+
expect( screen.getByRole( 'application' ) ).toBeInTheDocument();
|
|
373
374
|
expect( warn ).toHaveBeenCalledWith(
|
|
374
375
|
'[Charts] BarChart: withBandHighlight and onBandHighlightChange require withTooltips.'
|
|
375
376
|
);
|
|
@@ -473,7 +474,7 @@ describe( 'BarChart', () => {
|
|
|
473
474
|
|
|
474
475
|
test( 'labels a whole-number range smaller than the tick count once per whole number', () => {
|
|
475
476
|
renderWithTheme( { data: wholeNumberData } );
|
|
476
|
-
const chart = screen.getByRole( '
|
|
477
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
477
478
|
const ticks = within( chart )
|
|
478
479
|
.getAllByText( /^-?[\d.,]+$/ )
|
|
479
480
|
.map( el => el.textContent );
|
|
@@ -482,7 +483,7 @@ describe( 'BarChart', () => {
|
|
|
482
483
|
|
|
483
484
|
test( 'labels a horizontal whole-number range once per whole number', () => {
|
|
484
485
|
renderWithTheme( { data: wholeNumberData, orientation: 'horizontal' } );
|
|
485
|
-
const chart = screen.getByRole( '
|
|
486
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
486
487
|
const ticks = within( chart )
|
|
487
488
|
.getAllByText( /^-?[\d.,]+$/ )
|
|
488
489
|
.map( el => el.textContent );
|
|
@@ -501,7 +502,7 @@ describe( 'BarChart', () => {
|
|
|
501
502
|
},
|
|
502
503
|
],
|
|
503
504
|
} );
|
|
504
|
-
const chart = screen.getByRole( '
|
|
505
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
505
506
|
const ticks = within( chart )
|
|
506
507
|
.getAllByText( /^-?[\d.,]+$/ )
|
|
507
508
|
.map( el => el.textContent );
|
|
@@ -530,7 +531,7 @@ describe( 'BarChart', () => {
|
|
|
530
531
|
},
|
|
531
532
|
},
|
|
532
533
|
} );
|
|
533
|
-
const chart = screen.getByRole( '
|
|
534
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
534
535
|
expect( within( chart ).getAllByText( /^\d+%$/ ) ).toHaveLength( 6 );
|
|
535
536
|
}
|
|
536
537
|
);
|
|
@@ -540,7 +541,7 @@ describe( 'BarChart', () => {
|
|
|
540
541
|
data: wholeNumberData,
|
|
541
542
|
options: { axis: { y: { tickValues: [ 0, 0.5, 1 ] } } },
|
|
542
543
|
} );
|
|
543
|
-
const chart = screen.getByRole( '
|
|
544
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
544
545
|
expect( within( chart ).getAllByText( /^-?[\d.,]+$/ ) ).toHaveLength( 3 );
|
|
545
546
|
} );
|
|
546
547
|
} );
|
|
@@ -584,7 +585,7 @@ describe( 'BarChart', () => {
|
|
|
584
585
|
},
|
|
585
586
|
],
|
|
586
587
|
} );
|
|
587
|
-
expect( screen.getByRole( '
|
|
588
|
+
expect( screen.getByRole( 'application', { name: /bar chart/i } ) ).toBeInTheDocument();
|
|
588
589
|
} );
|
|
589
590
|
|
|
590
591
|
test( 'handles negative values', () => {
|
|
@@ -600,7 +601,7 @@ describe( 'BarChart', () => {
|
|
|
600
601
|
},
|
|
601
602
|
],
|
|
602
603
|
} );
|
|
603
|
-
expect( screen.getByRole( '
|
|
604
|
+
expect( screen.getByRole( 'application', { name: /bar chart/i } ) ).toBeInTheDocument();
|
|
604
605
|
} );
|
|
605
606
|
|
|
606
607
|
test( 'renders a bucket with no reading instead of failing the whole chart', () => {
|
|
@@ -617,7 +618,7 @@ describe( 'BarChart', () => {
|
|
|
617
618
|
],
|
|
618
619
|
} );
|
|
619
620
|
expect( screen.queryByText( /invalid data/i ) ).not.toBeInTheDocument();
|
|
620
|
-
expect( screen.getByRole( '
|
|
621
|
+
expect( screen.getByRole( 'application', { name: /bar chart/i } ) ).toBeInTheDocument();
|
|
621
622
|
} );
|
|
622
623
|
|
|
623
624
|
test( 'draws no bar for a bucket with no reading', () => {
|
|
@@ -736,7 +737,7 @@ describe( 'BarChart', () => {
|
|
|
736
737
|
expect( screen.getAllByTestId( 'legend-item' ) ).toHaveLength( 2 );
|
|
737
738
|
const html = document.body.innerHTML;
|
|
738
739
|
expect( html.indexOf( 'data-testid="legend-horizontal"' ) ).toBeLessThan(
|
|
739
|
-
html.indexOf( 'role="
|
|
740
|
+
html.indexOf( 'role="application"' )
|
|
740
741
|
);
|
|
741
742
|
} );
|
|
742
743
|
} );
|
|
@@ -744,7 +745,7 @@ describe( 'BarChart', () => {
|
|
|
744
745
|
describe( 'Grid tick counts', () => {
|
|
745
746
|
const getPositions = ( selector: string, coordinate: string ) => {
|
|
746
747
|
// eslint-disable-next-line testing-library/no-node-access -- See the visx node constraint above.
|
|
747
|
-
const lines = screen.getByRole( '
|
|
748
|
+
const lines = screen.getByRole( 'application' ).querySelectorAll( selector );
|
|
748
749
|
return Array.from( lines, line => Number( line.getAttribute( coordinate ) ) );
|
|
749
750
|
};
|
|
750
751
|
|
|
@@ -822,7 +823,7 @@ describe( 'BarChart', () => {
|
|
|
822
823
|
},
|
|
823
824
|
},
|
|
824
825
|
} );
|
|
825
|
-
const chart = screen.getByRole( '
|
|
826
|
+
const chart = screen.getByRole( 'application' );
|
|
826
827
|
|
|
827
828
|
// eslint-disable-next-line testing-library/no-node-access -- See the visx node constraint above.
|
|
828
829
|
expect( chart.querySelector( '.test-hidden-axis' ) ).toBeNull();
|
|
@@ -834,21 +835,21 @@ describe( 'BarChart', () => {
|
|
|
834
835
|
describe( 'Grid Visibility', () => {
|
|
835
836
|
test( 'renders with different grid visibility options', () => {
|
|
836
837
|
const { rerender } = renderWithTheme( { gridVisibility: 'x' } );
|
|
837
|
-
expect( screen.getByRole( '
|
|
838
|
+
expect( screen.getByRole( 'application', { name: /bar chart/i } ) ).toBeInTheDocument();
|
|
838
839
|
|
|
839
840
|
rerender(
|
|
840
841
|
<GlobalChartsProvider>
|
|
841
842
|
<BarChart { ...defaultProps } gridVisibility="y" />
|
|
842
843
|
</GlobalChartsProvider>
|
|
843
844
|
);
|
|
844
|
-
expect( screen.getByRole( '
|
|
845
|
+
expect( screen.getByRole( 'application', { name: /bar chart/i } ) ).toBeInTheDocument();
|
|
845
846
|
|
|
846
847
|
rerender(
|
|
847
848
|
<GlobalChartsProvider>
|
|
848
849
|
<BarChart { ...defaultProps } gridVisibility="xy" />
|
|
849
850
|
</GlobalChartsProvider>
|
|
850
851
|
);
|
|
851
|
-
expect( screen.getByRole( '
|
|
852
|
+
expect( screen.getByRole( 'application', { name: /bar chart/i } ) ).toBeInTheDocument();
|
|
852
853
|
} );
|
|
853
854
|
} );
|
|
854
855
|
|
|
@@ -930,7 +931,7 @@ describe( 'BarChart', () => {
|
|
|
930
931
|
|
|
931
932
|
// Scoped to the chart: @visx/text parks a measuring node on document.body,
|
|
932
933
|
// which a negative assertion would otherwise match.
|
|
933
|
-
const inChart = () => within( screen.getByRole( '
|
|
934
|
+
const inChart = () => within( screen.getByRole( 'application', { name: /bar chart/i } ) );
|
|
934
935
|
|
|
935
936
|
test( 'leaves tick values alone when a bucket is labelled rather than dated', () => {
|
|
936
937
|
// visx builds the band domain from `label || date`, so a labelled bucket
|
|
@@ -1134,7 +1135,7 @@ describe( 'BarChart', () => {
|
|
|
1134
1135
|
// on document.body, which outlives RTL's cleanup still holding the last
|
|
1135
1136
|
// string measured. Query inside the chart so a previous test's measurement
|
|
1136
1137
|
// can't answer for this one's axis.
|
|
1137
|
-
const inChart = () => within( screen.getByRole( '
|
|
1138
|
+
const inChart = () => within( screen.getByRole( 'application', { name: /bar chart/i } ) );
|
|
1138
1139
|
|
|
1139
1140
|
test( 'renders distinct date ticks for daily buckets within a year', () => {
|
|
1140
1141
|
renderWithTheme( {
|
|
@@ -1570,7 +1571,7 @@ describe( 'BarChart', () => {
|
|
|
1570
1571
|
],
|
|
1571
1572
|
} );
|
|
1572
1573
|
|
|
1573
|
-
screen.getByRole( '
|
|
1574
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
1574
1575
|
await user.keyboard( '{ArrowRight}' );
|
|
1575
1576
|
|
|
1576
1577
|
// The month tick reads "2024" here; the tooltip names the bucket itself.
|
|
@@ -1629,7 +1630,7 @@ describe( 'BarChart', () => {
|
|
|
1629
1630
|
|
|
1630
1631
|
test( 'renders with patterns', () => {
|
|
1631
1632
|
renderWithTheme( { withPatterns: true } );
|
|
1632
|
-
expect( screen.getByRole( '
|
|
1633
|
+
expect( screen.getByRole( 'application', { name: /bar chart/i } ) ).toBeInTheDocument();
|
|
1633
1634
|
|
|
1634
1635
|
// Check that pattern definitions container is present
|
|
1635
1636
|
expect( screen.getByTestId( 'bar-chart-patterns' ) ).toBeInTheDocument();
|
|
@@ -1637,7 +1638,7 @@ describe( 'BarChart', () => {
|
|
|
1637
1638
|
|
|
1638
1639
|
test( 'renders without patterns by default', () => {
|
|
1639
1640
|
renderWithTheme( { withPatterns: false } );
|
|
1640
|
-
expect( screen.getByRole( '
|
|
1641
|
+
expect( screen.getByRole( 'application', { name: /bar chart/i } ) ).toBeInTheDocument();
|
|
1641
1642
|
|
|
1642
1643
|
// Check that no pattern definitions container is present
|
|
1643
1644
|
expect( screen.queryByTestId( 'bar-chart-patterns' ) ).not.toBeInTheDocument();
|
|
@@ -1679,17 +1680,26 @@ describe( 'BarChart', () => {
|
|
|
1679
1680
|
describe( 'Chart Focus and Accessibility Attributes', () => {
|
|
1680
1681
|
test( 'chart container has proper accessibility attributes', () => {
|
|
1681
1682
|
renderWithTheme();
|
|
1682
|
-
const chart = screen.getByRole( '
|
|
1683
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
1683
1684
|
|
|
1684
1685
|
expect( chart ).toHaveAttribute( 'tabIndex', '0' );
|
|
1685
|
-
expect( chart ).toHaveAttribute( 'role', '
|
|
1686
|
+
expect( chart ).toHaveAttribute( 'role', 'application' );
|
|
1686
1687
|
expect( chart ).toHaveAttribute( 'aria-label', 'Bar chart' );
|
|
1687
1688
|
} );
|
|
1688
1689
|
|
|
1690
|
+
test.each( [
|
|
1691
|
+
[ undefined, 'Bar chart' ],
|
|
1692
|
+
[ 'Views over time', 'Views over time' ],
|
|
1693
|
+
] )( 'names the chart container from ariaLabel %p', ( ariaLabel, name ) => {
|
|
1694
|
+
renderWithTheme( { ariaLabel } );
|
|
1695
|
+
expect( screen.getByRole( 'application', { name } ) ).toBeInTheDocument();
|
|
1696
|
+
expect( screen.queryByLabelText( 'XYChart' ) ).not.toBeInTheDocument();
|
|
1697
|
+
} );
|
|
1698
|
+
|
|
1689
1699
|
test( 'chart container can receive focus', async () => {
|
|
1690
1700
|
const user = userEvent.setup();
|
|
1691
1701
|
renderWithTheme();
|
|
1692
|
-
const chart = screen.getByRole( '
|
|
1702
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
1693
1703
|
|
|
1694
1704
|
await user.tab();
|
|
1695
1705
|
expect( chart ).toHaveFocus();
|
|
@@ -1719,7 +1729,7 @@ describe( 'BarChart', () => {
|
|
|
1719
1729
|
const onDatumActivate = jest.fn();
|
|
1720
1730
|
renderWithTheme( { withTooltips: true, data: [ SERIES_A ], onDatumActivate } );
|
|
1721
1731
|
|
|
1722
|
-
screen.getByRole( '
|
|
1732
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
1723
1733
|
await user.keyboard( '{ArrowRight}{ArrowRight}{Enter}' );
|
|
1724
1734
|
|
|
1725
1735
|
expect( onDatumActivate ).toHaveBeenCalledTimes( 1 );
|
|
@@ -1735,7 +1745,7 @@ describe( 'BarChart', () => {
|
|
|
1735
1745
|
const onDatumActivate = jest.fn();
|
|
1736
1746
|
renderWithTheme( { withTooltips: true, data: [ SERIES_A ], onDatumActivate } );
|
|
1737
1747
|
|
|
1738
|
-
screen.getByRole( '
|
|
1748
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
1739
1749
|
await user.keyboard( '{ArrowRight}[Space]' );
|
|
1740
1750
|
|
|
1741
1751
|
expect( onDatumActivate ).toHaveBeenCalledWith( {
|
|
@@ -1753,7 +1763,7 @@ describe( 'BarChart', () => {
|
|
|
1753
1763
|
const onDatumActivate = jest.fn();
|
|
1754
1764
|
renderWithTheme( { withTooltips: true, data: [ SERIES_A, SERIES_B ], onDatumActivate } );
|
|
1755
1765
|
|
|
1756
|
-
screen.getByRole( '
|
|
1766
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
1757
1767
|
await user.keyboard( '{ArrowRight}{ArrowRight}{Enter}' );
|
|
1758
1768
|
|
|
1759
1769
|
expect( onDatumActivate ).toHaveBeenCalledWith( {
|
|
@@ -1768,7 +1778,7 @@ describe( 'BarChart', () => {
|
|
|
1768
1778
|
const onDatumActivate = jest.fn();
|
|
1769
1779
|
renderWithTheme( { withTooltips: true, data: [ SERIES_A ], onDatumActivate } );
|
|
1770
1780
|
|
|
1771
|
-
screen.getByRole( '
|
|
1781
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
1772
1782
|
await user.keyboard( '{Enter}' );
|
|
1773
1783
|
|
|
1774
1784
|
expect( onDatumActivate ).not.toHaveBeenCalled();
|
|
@@ -1802,7 +1812,7 @@ describe( 'BarChart', () => {
|
|
|
1802
1812
|
],
|
|
1803
1813
|
} );
|
|
1804
1814
|
|
|
1805
|
-
const chart = screen.getByRole( '
|
|
1815
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
1806
1816
|
chart.focus();
|
|
1807
1817
|
|
|
1808
1818
|
// Single tab should focus on the first tooltip.
|
|
@@ -1853,7 +1863,7 @@ describe( 'BarChart', () => {
|
|
|
1853
1863
|
],
|
|
1854
1864
|
} );
|
|
1855
1865
|
|
|
1856
|
-
const chart = screen.getByRole( '
|
|
1866
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
1857
1867
|
chart.focus();
|
|
1858
1868
|
|
|
1859
1869
|
await user.keyboard( '{ArrowRight}' );
|
|
@@ -1902,7 +1912,7 @@ describe( 'BarChart', () => {
|
|
|
1902
1912
|
],
|
|
1903
1913
|
} );
|
|
1904
1914
|
|
|
1905
|
-
const chart = screen.getByRole( '
|
|
1915
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
1906
1916
|
chart.focus();
|
|
1907
1917
|
|
|
1908
1918
|
// Right arrow key should focus on the first tooltip.
|
|
@@ -1925,7 +1935,7 @@ describe( 'BarChart', () => {
|
|
|
1925
1935
|
} );
|
|
1926
1936
|
} );
|
|
1927
1937
|
|
|
1928
|
-
describe( '
|
|
1938
|
+
describe( 'Keyboard navigation boundaries', () => {
|
|
1929
1939
|
// Three points, so a clamp at a boundary lands on a different index than a wrap would.
|
|
1930
1940
|
const threePointData = [
|
|
1931
1941
|
{
|
|
@@ -1943,7 +1953,7 @@ describe( 'BarChart', () => {
|
|
|
1943
1953
|
const user = userEvent.setup();
|
|
1944
1954
|
renderWithTheme( { withTooltips: true, data: threePointData } );
|
|
1945
1955
|
|
|
1946
|
-
const chart = screen.getByRole( '
|
|
1956
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
1947
1957
|
chart.focus();
|
|
1948
1958
|
|
|
1949
1959
|
// Move to the first, then on to the last (third) point.
|
|
@@ -1953,7 +1963,7 @@ describe( 'BarChart', () => {
|
|
|
1953
1963
|
expect( screen.getByTestId( 'chart-tooltip-2' ) ).toHaveFocus();
|
|
1954
1964
|
|
|
1955
1965
|
// Right arrow at the last point must not move focus: the highlighted
|
|
1956
|
-
// bar/tooltip stays visible and focused (
|
|
1966
|
+
// bar/tooltip stays visible and focused (focus does not move).
|
|
1957
1967
|
await user.keyboard( '{ArrowRight}' );
|
|
1958
1968
|
expect( screen.getByTestId( 'chart-tooltip-2' ) ).toBeInTheDocument();
|
|
1959
1969
|
expect( screen.getByTestId( 'chart-tooltip-2' ) ).toHaveFocus();
|
|
@@ -1968,7 +1978,7 @@ describe( 'BarChart', () => {
|
|
|
1968
1978
|
const user = userEvent.setup();
|
|
1969
1979
|
renderWithTheme( { withTooltips: true, data: threePointData } );
|
|
1970
1980
|
|
|
1971
|
-
screen.getByRole( '
|
|
1981
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
1972
1982
|
await user.keyboard( '{ArrowLeft}' );
|
|
1973
1983
|
|
|
1974
1984
|
expect( screen.getByTestId( 'chart-tooltip-0' ) ).toHaveFocus();
|
|
@@ -2002,7 +2012,7 @@ describe( 'BarChart', () => {
|
|
|
2002
2012
|
],
|
|
2003
2013
|
} );
|
|
2004
2014
|
|
|
2005
|
-
const chart = screen.getByRole( '
|
|
2015
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
2006
2016
|
chart.focus();
|
|
2007
2017
|
|
|
2008
2018
|
await user.keyboard( '{ArrowRight}' );
|
|
@@ -2037,7 +2047,7 @@ describe( 'BarChart', () => {
|
|
|
2037
2047
|
],
|
|
2038
2048
|
} );
|
|
2039
2049
|
|
|
2040
|
-
const chart = screen.getByRole( '
|
|
2050
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
2041
2051
|
chart.focus();
|
|
2042
2052
|
|
|
2043
2053
|
// Slot 0 is the first primary bar (Mon); slot 1 must be the SECOND primary
|
|
@@ -2069,7 +2079,7 @@ describe( 'BarChart', () => {
|
|
|
2069
2079
|
],
|
|
2070
2080
|
} );
|
|
2071
2081
|
|
|
2072
|
-
screen.getByRole( '
|
|
2082
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
2073
2083
|
await user.keyboard( '{ArrowRight}' );
|
|
2074
2084
|
|
|
2075
2085
|
const tooltip = screen.getByTestId( 'chart-tooltip-0' );
|
|
@@ -2102,7 +2112,7 @@ describe( 'BarChart', () => {
|
|
|
2102
2112
|
],
|
|
2103
2113
|
} );
|
|
2104
2114
|
|
|
2105
|
-
screen.getByRole( '
|
|
2115
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
2106
2116
|
await user.keyboard( '{ArrowRight}' );
|
|
2107
2117
|
|
|
2108
2118
|
const tooltip = screen.getByTestId( 'chart-tooltip-0' );
|
|
@@ -2162,7 +2172,7 @@ describe( 'BarChart', () => {
|
|
|
2162
2172
|
],
|
|
2163
2173
|
} );
|
|
2164
2174
|
|
|
2165
|
-
const chart = screen.getByRole( '
|
|
2175
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
2166
2176
|
chart.focus();
|
|
2167
2177
|
|
|
2168
2178
|
// Chart should be in focus.
|
|
@@ -2202,7 +2212,7 @@ describe( 'BarChart', () => {
|
|
|
2202
2212
|
],
|
|
2203
2213
|
} );
|
|
2204
2214
|
|
|
2205
|
-
const chart = screen.getByRole( '
|
|
2215
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
2206
2216
|
chart.focus();
|
|
2207
2217
|
|
|
2208
2218
|
// Navigate to the first bar
|
|
@@ -2237,7 +2247,7 @@ describe( 'BarChart', () => {
|
|
|
2237
2247
|
],
|
|
2238
2248
|
} );
|
|
2239
2249
|
|
|
2240
|
-
const chart = screen.getByRole( '
|
|
2250
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
2241
2251
|
chart.focus();
|
|
2242
2252
|
|
|
2243
2253
|
// Navigate to the first bar (Series A, Jan 1)
|
|
@@ -2269,7 +2279,7 @@ describe( 'BarChart', () => {
|
|
|
2269
2279
|
const user = userEvent.setup();
|
|
2270
2280
|
renderWithTheme( { withTooltips: true, data } );
|
|
2271
2281
|
|
|
2272
|
-
screen.getByRole( '
|
|
2282
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
2273
2283
|
await user.keyboard( '{ArrowRight}' );
|
|
2274
2284
|
|
|
2275
2285
|
expect( screen.getByTestId( 'chart-tooltip-0' ) ).toHaveTextContent( 'No data' );
|
|
@@ -2280,7 +2290,7 @@ describe( 'BarChart', () => {
|
|
|
2280
2290
|
const user = userEvent.setup();
|
|
2281
2291
|
renderWithTheme( { withTooltips: true, data } );
|
|
2282
2292
|
|
|
2283
|
-
screen.getByRole( '
|
|
2293
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
2284
2294
|
await user.keyboard( '{ArrowRight}{ArrowRight}' );
|
|
2285
2295
|
|
|
2286
2296
|
expect( screen.getByTestId( 'chart-tooltip-1' ) ).toHaveTextContent( '20' );
|
|
@@ -2313,7 +2323,7 @@ describe( 'BarChart', () => {
|
|
|
2313
2323
|
];
|
|
2314
2324
|
renderWithTheme( { withTooltips: true, data: twoSeries } );
|
|
2315
2325
|
|
|
2316
|
-
screen.getByRole( '
|
|
2326
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
2317
2327
|
await user.keyboard( '{ArrowRight}{ArrowRight}{ArrowRight}{ArrowRight}' );
|
|
2318
2328
|
|
|
2319
2329
|
expect( screen.getByTestId( 'chart-tooltip-3' ) ).toHaveTextContent( '15' );
|
|
@@ -2340,7 +2350,7 @@ describe( 'BarChart', () => {
|
|
|
2340
2350
|
|
|
2341
2351
|
renderWithTheme( { withTooltips: true, renderTooltip: customTooltipRenderer } );
|
|
2342
2352
|
|
|
2343
|
-
const chart = screen.getByRole( '
|
|
2353
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
2344
2354
|
chart.focus();
|
|
2345
2355
|
|
|
2346
2356
|
// Click on right arrow key to focus on the first tooltip.
|
|
@@ -2372,7 +2382,9 @@ describe( 'BarChart', () => {
|
|
|
2372
2382
|
],
|
|
2373
2383
|
} );
|
|
2374
2384
|
|
|
2375
|
-
const svgElement = screen
|
|
2385
|
+
const svgElement = screen
|
|
2386
|
+
.getByRole( 'application', { name: /bar chart/i } )
|
|
2387
|
+
.querySelector( 'svg' );
|
|
2376
2388
|
const bars = svgElement?.querySelectorAll( '.visx-bar-group rect' );
|
|
2377
2389
|
|
|
2378
2390
|
// Should have 2 bars
|
|
@@ -2399,7 +2411,9 @@ describe( 'BarChart', () => {
|
|
|
2399
2411
|
],
|
|
2400
2412
|
} );
|
|
2401
2413
|
|
|
2402
|
-
const svgElement = screen
|
|
2414
|
+
const svgElement = screen
|
|
2415
|
+
.getByRole( 'application', { name: /bar chart/i } )
|
|
2416
|
+
.querySelector( 'svg' );
|
|
2403
2417
|
const bars = svgElement?.querySelectorAll( '.visx-bar-group rect' );
|
|
2404
2418
|
|
|
2405
2419
|
// Should have 2 bars
|
|
@@ -2432,7 +2446,9 @@ describe( 'BarChart', () => {
|
|
|
2432
2446
|
orientation: 'horizontal',
|
|
2433
2447
|
} );
|
|
2434
2448
|
|
|
2435
|
-
const svgElement = screen
|
|
2449
|
+
const svgElement = screen
|
|
2450
|
+
.getByRole( 'application', { name: /bar chart/i } )
|
|
2451
|
+
.querySelector( 'svg' );
|
|
2436
2452
|
const bars = svgElement?.querySelectorAll( '.visx-bar-group rect' );
|
|
2437
2453
|
|
|
2438
2454
|
// Both bars should be visible in horizontal mode
|
|
@@ -2461,7 +2477,9 @@ describe( 'BarChart', () => {
|
|
|
2461
2477
|
],
|
|
2462
2478
|
} );
|
|
2463
2479
|
|
|
2464
|
-
const svgElement = screen
|
|
2480
|
+
const svgElement = screen
|
|
2481
|
+
.getByRole( 'application', { name: /bar chart/i } )
|
|
2482
|
+
.querySelector( 'svg' );
|
|
2465
2483
|
const bars = svgElement?.querySelectorAll( '.visx-bar-group rect' );
|
|
2466
2484
|
|
|
2467
2485
|
expect( bars?.length ).toBe( 2 );
|
|
@@ -2501,7 +2519,7 @@ describe( 'BarChart', () => {
|
|
|
2501
2519
|
},
|
|
2502
2520
|
} );
|
|
2503
2521
|
|
|
2504
|
-
expect( screen.getByRole( '
|
|
2522
|
+
expect( screen.getByRole( 'application', { name: /bar chart/i } ) ).toBeInTheDocument();
|
|
2505
2523
|
} );
|
|
2506
2524
|
|
|
2507
2525
|
test( 'truncates labels with CSS text-overflow ellipsis', () => {
|
|
@@ -2594,7 +2612,7 @@ describe( 'BarChart', () => {
|
|
|
2594
2612
|
} );
|
|
2595
2613
|
|
|
2596
2614
|
// Chart should still render without errors
|
|
2597
|
-
expect( screen.getByRole( '
|
|
2615
|
+
expect( screen.getByRole( 'application', { name: /bar chart/i } ) ).toBeInTheDocument();
|
|
2598
2616
|
|
|
2599
2617
|
// Labels should still be present and have minimum width applied
|
|
2600
2618
|
const label = screen.getByText( /Very Long Category Label One/i );
|
|
@@ -2890,7 +2908,7 @@ describe( 'BarChart', () => {
|
|
|
2890
2908
|
</GlobalChartsProvider>
|
|
2891
2909
|
);
|
|
2892
2910
|
|
|
2893
|
-
const chart = screen.getByRole( '
|
|
2911
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
2894
2912
|
chart.focus();
|
|
2895
2913
|
|
|
2896
2914
|
// Navigate through all 4 primary slots (2 series × 2 data points)
|
|
@@ -3176,8 +3194,9 @@ describe( 'BarChart', () => {
|
|
|
3176
3194
|
</GlobalChartsProvider>
|
|
3177
3195
|
);
|
|
3178
3196
|
|
|
3197
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
3179
3198
|
// eslint-disable-next-line testing-library/no-node-access
|
|
3180
|
-
const svgElement =
|
|
3199
|
+
const svgElement = chart.querySelector( 'svg' );
|
|
3181
3200
|
// eslint-disable-next-line testing-library/no-node-access
|
|
3182
3201
|
expect( svgElement?.querySelectorAll( '.visx-bar-group rect' ) ).toHaveLength( 2 );
|
|
3183
3202
|
|
|
@@ -3382,7 +3401,7 @@ describe( 'BarChart', () => {
|
|
|
3382
3401
|
const user = userEvent.setup();
|
|
3383
3402
|
const toggle = mountWithVisibilityToggle();
|
|
3384
3403
|
|
|
3385
|
-
screen.getByRole( '
|
|
3404
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
3386
3405
|
for ( let i = 0; i < 6; i++ ) {
|
|
3387
3406
|
await user.keyboard( '{ArrowRight}' );
|
|
3388
3407
|
}
|
|
@@ -3427,7 +3446,7 @@ describe( 'BarChart', () => {
|
|
|
3427
3446
|
],
|
|
3428
3447
|
} );
|
|
3429
3448
|
|
|
3430
|
-
screen.getByRole( '
|
|
3449
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
3431
3450
|
for ( let i = 0; i < 6; i++ ) {
|
|
3432
3451
|
await user.keyboard( '{ArrowRight}' );
|
|
3433
3452
|
}
|
|
@@ -3484,12 +3503,12 @@ describe( 'BarChart', () => {
|
|
|
3484
3503
|
</GlobalChartsProvider>
|
|
3485
3504
|
);
|
|
3486
3505
|
|
|
3487
|
-
screen.getByRole( '
|
|
3506
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).focus();
|
|
3488
3507
|
await user.keyboard( '{ArrowRight}{ArrowRight}' );
|
|
3489
3508
|
expect( screen.getByTestId( 'chart-tooltip-1' ) ).toHaveTextContent( 'Series A' );
|
|
3490
3509
|
|
|
3491
3510
|
// Move focus out of the chart, then swap which series is hidden without changing the count.
|
|
3492
|
-
screen.getByRole( '
|
|
3511
|
+
screen.getByRole( 'application', { name: /bar chart/i } ).blur();
|
|
3493
3512
|
act( () => context.setChartHiddenSeries( 'test-equal-count-swap', [ 'Series A' ] ) );
|
|
3494
3513
|
|
|
3495
3514
|
expect( screen.queryAllByTestId( /^chart-tooltip-/ ) ).toHaveLength( 0 );
|
|
@@ -3528,7 +3547,7 @@ describe( 'BarChart', () => {
|
|
|
3528
3547
|
|
|
3529
3548
|
await user.click( screen.getByRole( 'button', { name: /Series B: visible/i } ) );
|
|
3530
3549
|
|
|
3531
|
-
const chart = screen.getByRole( '
|
|
3550
|
+
const chart = screen.getByRole( 'application', { name: /bar chart/i } );
|
|
3532
3551
|
chart.focus();
|
|
3533
3552
|
|
|
3534
3553
|
for ( let i = 0; i < 5; i++ ) {
|
|
@@ -19,6 +19,8 @@ export type BandHighlightSelection = {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
export interface BarChartProps extends BaseChartProps< SeriesData[] >, SeriesVisibilityProps {
|
|
22
|
+
/** Accessible name of the chart. Defaults to a localized "Bar chart". */
|
|
23
|
+
ariaLabel?: string;
|
|
22
24
|
/**
|
|
23
25
|
* Legend configuration. Supports `collapseGroups` on top of the shared options.
|
|
24
26
|
*/
|
|
@@ -72,7 +72,9 @@ describe( 'BarListChart', () => {
|
|
|
72
72
|
/* eslint-disable testing-library/no-node-access */
|
|
73
73
|
const barFills = () =>
|
|
74
74
|
Array.from(
|
|
75
|
-
screen
|
|
75
|
+
screen
|
|
76
|
+
.getByRole( 'application' )
|
|
77
|
+
.querySelectorAll< SVGRectElement >( '.visx-bar-group rect' )
|
|
76
78
|
).map( bar => bar.getAttribute( 'fill' ) );
|
|
77
79
|
/* eslint-enable testing-library/no-node-access */
|
|
78
80
|
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// module, so it needs no runtime cascade and never lands on the DOM.
|
|
4
4
|
$focus-ring-width: var(--wpds-border-width-focus);
|
|
5
5
|
|
|
6
|
+
$heatmap-empty-fill: var(--a8c-charts-color-track);
|
|
7
|
+
|
|
6
8
|
// JS sets the ends (getHeatmapScale): 3:1 against background and empty cell at
|
|
7
9
|
// the low end. Unresolved, the floor is 15% of the primary.
|
|
8
10
|
$heatmap-fill: color-mix(in sRGB, var(--a8c-charts-color-heatmap-high, var(--a8c-charts-color-heatmap-primary)) calc(var(--a8c-charts-heatmap-cell-intensity) * 100%), var(--a8c-charts-color-heatmap-low, color-mix(in sRGB, var(--a8c-charts-color-heatmap-primary) 15%, var(--a8c-charts-color-background, #fff))));
|
|
@@ -96,9 +98,9 @@ $heatmap-fill: color-mix(in sRGB, var(--a8c-charts-color-heatmap-high, var(--a8c
|
|
|
96
98
|
|
|
97
99
|
// Beneath its columns, set off from the last row by more than the cell gap.
|
|
98
100
|
.heatmap-chart__group-label {
|
|
99
|
-
color: var(--a8c-charts-color-label
|
|
100
|
-
font-size: var(--wpds-typography-font-size-
|
|
101
|
-
line-height: var(--wpds-typography-line-height-
|
|
101
|
+
color: var(--a8c-charts-color-label);
|
|
102
|
+
font-size: var(--wpds-typography-font-size-md);
|
|
103
|
+
line-height: var(--wpds-typography-line-height-sm);
|
|
102
104
|
white-space: nowrap;
|
|
103
105
|
text-align: center;
|
|
104
106
|
padding-block-start: var(--wpds-dimension-padding-xs);
|
|
@@ -119,7 +121,7 @@ $heatmap-fill: color-mix(in sRGB, var(--a8c-charts-color-heatmap-high, var(--a8c
|
|
|
119
121
|
min-height: 0;
|
|
120
122
|
border-radius: var(--a8c-charts-border-radius-cell);
|
|
121
123
|
// Empty (no-data) default; cells with a value override it below.
|
|
122
|
-
background:
|
|
124
|
+
background: $heatmap-empty-fill;
|
|
123
125
|
}
|
|
124
126
|
|
|
125
127
|
// An empty grid slot (calendar ragged edges): keeps its track but paints and
|
|
@@ -209,6 +211,10 @@ $heatmap-fill: color-mix(in sRGB, var(--a8c-charts-color-heatmap-high, var(--a8c
|
|
|
209
211
|
background: $heatmap-fill;
|
|
210
212
|
}
|
|
211
213
|
|
|
214
|
+
.heatmap-chart__legend-swatch--empty {
|
|
215
|
+
background: $heatmap-empty-fill;
|
|
216
|
+
}
|
|
217
|
+
|
|
212
218
|
// One band: the steps butt against each other and only the band's ends round.
|
|
213
219
|
.heatmap-chart__legend-scale--bar {
|
|
214
220
|
overflow: hidden;
|