@automattic/charts 4.8.0 → 4.10.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 +28 -0
- package/dist/index.cjs +766 -277
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +72 -8
- package/dist/index.d.cts +54 -15
- package/dist/index.d.ts +54 -15
- package/dist/index.js +770 -281
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/charts/area-chart/area-chart.tsx +7 -3
- package/src/charts/area-chart/test/area-chart.test.tsx +15 -14
- package/src/charts/area-chart/test/tooltip-resolution.test.tsx +2 -2
- package/src/charts/bar-chart/bar-chart.tsx +12 -4
- package/src/charts/bar-chart/test/bar-chart.test.tsx +74 -63
- package/src/charts/bar-list-chart/bar-list-chart.tsx +3 -0
- package/src/charts/bar-list-chart/test/bar-list-chart.test.tsx +3 -1
- package/src/charts/conversion-funnel-chart/conversion-funnel-chart.tsx +23 -7
- package/src/charts/conversion-funnel-chart/test/conversion-funnel-chart.test.tsx +22 -0
- package/src/charts/conversion-funnel-chart/types.ts +1 -1
- package/src/charts/geo-chart/geo-chart.tsx +17 -2
- package/src/charts/geo-chart/test/geo-chart.test.tsx +9 -4
- package/src/charts/geo-chart/types.ts +1 -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 -7
- package/src/charts/heatmap-chart/types.ts +2 -2
- package/src/charts/leaderboard-chart/hooks/use-fitted-row-count.ts +2 -2
- package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +52 -4
- package/src/charts/leaderboard-chart/leaderboard-chart.tsx +60 -9
- package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +80 -1
- package/src/charts/leaderboard-chart/types.ts +7 -1
- package/src/charts/line-chart/line-chart.tsx +8 -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 +37 -35
- package/src/charts/line-chart/test/tooltip-resolution.test.tsx +2 -2
- package/src/charts/line-chart/types.ts +3 -3
- package/src/charts/pie-chart/pie-chart.module.scss +3 -0
- package/src/charts/pie-chart/pie-chart.tsx +144 -139
- package/src/charts/pie-chart/test/pie-chart.test.tsx +167 -0
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +3 -0
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +117 -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 +49 -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/charts/test/accessible-name.test.tsx +181 -0
- package/src/components/legend/hooks/test/use-chart-legend-items.test.tsx +63 -3
- package/src/components/legend/hooks/use-chart-legend-items.ts +63 -1
- package/src/components/legend/private/base-legend.tsx +27 -15
- package/src/components/legend/test/legend.test.tsx +42 -0
- package/src/components/legend/types.ts +2 -0
- package/src/components/tooltip/test/xy-chart-tooltip.test.tsx +20 -19
- package/src/components/tooltip/xy-chart-tooltip.tsx +3 -3
- package/src/hooks/test/use-xychart-theme.test.tsx +22 -0
- package/src/hooks/use-xychart-theme.ts +33 -2
- package/src/index.ts +1 -0
- package/src/types.ts +37 -3
- package/src/utils/resolve-font-size.ts +9 -10
- package/src/utils/test/resolve-font-size.test.ts +11 -2
|
@@ -4,7 +4,6 @@ import { BaseChartProps, LeaderboardEntry } from '../../types';
|
|
|
4
4
|
export interface LeaderboardChartProps extends Pick<
|
|
5
5
|
BaseChartProps< LeaderboardEntry >,
|
|
6
6
|
| 'className'
|
|
7
|
-
| 'data'
|
|
8
7
|
| 'showLegend'
|
|
9
8
|
| 'legend'
|
|
10
9
|
| 'chartId'
|
|
@@ -13,7 +12,14 @@ export interface LeaderboardChartProps extends Pick<
|
|
|
13
12
|
| 'size'
|
|
14
13
|
| 'gap'
|
|
15
14
|
| 'animation'
|
|
15
|
+
| 'ariaLabel'
|
|
16
16
|
> {
|
|
17
|
+
/**
|
|
18
|
+
* Entries to display. Declared here rather than picked, since the base type
|
|
19
|
+
* would split the entry union into one array type per member.
|
|
20
|
+
*/
|
|
21
|
+
data: LeaderboardEntry[];
|
|
22
|
+
|
|
17
23
|
/**
|
|
18
24
|
* Whether to show comparison data
|
|
19
25
|
*/
|
|
@@ -232,6 +232,7 @@ const LineChartInternal = forwardRef< ChartInstanceRef, LineChartProps >(
|
|
|
232
232
|
(
|
|
233
233
|
{
|
|
234
234
|
data,
|
|
235
|
+
ariaLabel,
|
|
235
236
|
chartId: providedChartId,
|
|
236
237
|
width,
|
|
237
238
|
height,
|
|
@@ -271,6 +272,7 @@ const LineChartInternal = forwardRef< ChartInstanceRef, LineChartProps >(
|
|
|
271
272
|
const legendInteractive = legend.interactive ?? false;
|
|
272
273
|
const legendCollapseGroups = legend.collapseGroups ?? false;
|
|
273
274
|
const legendComparisonItem = legend.comparisonItem ?? false;
|
|
275
|
+
const legendComparisonSeriesItems = legend.comparisonSeriesItems;
|
|
274
276
|
const legendShape = legend.shape ?? 'line';
|
|
275
277
|
const legendPosition = legend.position ?? 'bottom';
|
|
276
278
|
|
|
@@ -522,6 +524,7 @@ const LineChartInternal = forwardRef< ChartInstanceRef, LineChartProps >(
|
|
|
522
524
|
glyphSize: Math.max( 0, toNumber( glyphStyle?.radius ) ?? 4 ),
|
|
523
525
|
collapseGroups: legendCollapseGroups,
|
|
524
526
|
comparisonItem: legendComparisonItem,
|
|
527
|
+
comparisonSeriesItems: legendComparisonSeriesItems,
|
|
525
528
|
renderGlyph,
|
|
526
529
|
} ),
|
|
527
530
|
[
|
|
@@ -529,6 +532,7 @@ const LineChartInternal = forwardRef< ChartInstanceRef, LineChartProps >(
|
|
|
529
532
|
glyphStyle?.radius,
|
|
530
533
|
legendCollapseGroups,
|
|
531
534
|
legendComparisonItem,
|
|
535
|
+
legendComparisonSeriesItems,
|
|
532
536
|
renderGlyph,
|
|
533
537
|
]
|
|
534
538
|
);
|
|
@@ -624,10 +628,11 @@ const LineChartInternal = forwardRef< ChartInstanceRef, LineChartProps >(
|
|
|
624
628
|
const chartHeight = contentHeight > 0 ? contentHeight : height;
|
|
625
629
|
|
|
626
630
|
return (
|
|
631
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- the application role hands arrow keys to the chart's point navigation.
|
|
627
632
|
<div
|
|
628
633
|
ref={ chartRef }
|
|
629
|
-
role="
|
|
630
|
-
aria-label={ __( 'Line chart', 'jetpack-charts' ) }
|
|
634
|
+
role="application"
|
|
635
|
+
aria-label={ ariaLabel ?? __( 'Line chart', 'jetpack-charts' ) }
|
|
631
636
|
tabIndex={ 0 }
|
|
632
637
|
onKeyDown={ onChartKeyDown }
|
|
633
638
|
onFocus={ onChartFocus }
|
|
@@ -637,6 +642,7 @@ const LineChartInternal = forwardRef< ChartInstanceRef, LineChartProps >(
|
|
|
637
642
|
<div className={ plotStyles[ 'xy-plot' ] }>
|
|
638
643
|
{ zoomable && zoom.domain && <ZoomResetButton onClick={ zoom.reset } /> }
|
|
639
644
|
<XYChart
|
|
645
|
+
accessibilityLabel=""
|
|
640
646
|
theme={ theme }
|
|
641
647
|
width={ width }
|
|
642
648
|
height={ chartHeight }
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { __ } from '@wordpress/i18n';
|
|
2
2
|
import { Stack } from '@wordpress/ui';
|
|
3
3
|
import clsx from 'clsx';
|
|
4
|
-
import { useEffect, useId, useRef, useState } from 'react';
|
|
4
|
+
import { useEffect, useId, useRef, useState, version as reactVersion } from 'react';
|
|
5
5
|
import { isSafari } from '../../../utils';
|
|
6
6
|
import styles from '../line-chart.module.scss';
|
|
7
7
|
import type { ButtonWithPopover, PopoverElement, ToggleEvent } from '../../../types';
|
|
8
|
-
import type { FC } from 'react';
|
|
8
|
+
import type { FC, ReactNode } from 'react';
|
|
9
9
|
|
|
10
10
|
export const POPOVER_BUTTON_SIZE = 44;
|
|
11
11
|
|
|
12
|
+
// @todo Remove this (and uses of it below) when we drop support for WordPress versions shipping React 18.
|
|
13
|
+
const isReact18 = reactVersion.startsWith( '18.' );
|
|
14
|
+
|
|
12
15
|
const CloseIcon = () => (
|
|
13
16
|
<svg
|
|
14
17
|
width="16"
|
|
@@ -29,8 +32,8 @@ const CloseIcon = () => (
|
|
|
29
32
|
interface LineChartAnnotationLabelWithPopoverProps {
|
|
30
33
|
title: string;
|
|
31
34
|
subtitle?: string;
|
|
32
|
-
renderLabel:
|
|
33
|
-
renderLabelPopover:
|
|
35
|
+
renderLabel: ( props: { title: string; subtitle?: string } ) => ReactNode;
|
|
36
|
+
renderLabelPopover: ( props: { title: string; subtitle?: string } ) => ReactNode;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
const LineChartAnnotationLabelWithPopover: FC< LineChartAnnotationLabelWithPopoverProps > = ( {
|
|
@@ -83,7 +86,9 @@ const LineChartAnnotationLabelWithPopover: FC< LineChartAnnotationLabelWithPopov
|
|
|
83
86
|
<div className={ styles[ 'line-chart__annotation-label' ] }>
|
|
84
87
|
<button
|
|
85
88
|
ref={ buttonRef }
|
|
86
|
-
{ ...(
|
|
89
|
+
{ ...( isReact18
|
|
90
|
+
? ( { popovertarget: popoverId } as ButtonWithPopover )
|
|
91
|
+
: { popoverTarget: popoverId } ) }
|
|
87
92
|
className={ styles[ 'line-chart__annotation-label-trigger-button' ] }
|
|
88
93
|
style={ {
|
|
89
94
|
width: `${ POPOVER_BUTTON_SIZE }px`,
|
|
@@ -110,10 +115,15 @@ const LineChartAnnotationLabelWithPopover: FC< LineChartAnnotationLabelWithPopov
|
|
|
110
115
|
{ renderLabelPopover( { title, subtitle } ) }
|
|
111
116
|
</div>
|
|
112
117
|
<button
|
|
113
|
-
{ ...(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
{ ...( isReact18
|
|
119
|
+
? ( {
|
|
120
|
+
popovertarget: popoverId,
|
|
121
|
+
popovertargetaction: 'hide',
|
|
122
|
+
} as ButtonWithPopover )
|
|
123
|
+
: {
|
|
124
|
+
popoverTarget: popoverId,
|
|
125
|
+
popoverTargetAction: 'hide',
|
|
126
|
+
} ) }
|
|
117
127
|
className={ styles[ 'line-chart__annotation-label-popover-close-button' ] }
|
|
118
128
|
aria-label={ __( 'Close', 'jetpack-charts' ) }
|
|
119
129
|
>
|
|
@@ -66,7 +66,7 @@ describe( 'renderDefaultTooltip date heading', () => {
|
|
|
66
66
|
</GlobalChartsProvider>
|
|
67
67
|
);
|
|
68
68
|
|
|
69
|
-
screen.getByRole( '
|
|
69
|
+
screen.getByRole( 'application', { name: /line chart/i } ).focus();
|
|
70
70
|
await user.keyboard( '{ArrowRight}' );
|
|
71
71
|
|
|
72
72
|
expect( screen.getByTestId( 'chart-tooltip-0' ) ).toHaveTextContent( '3.8.2026' );
|
|
@@ -120,7 +120,7 @@ describe( 'LineChart', () => {
|
|
|
120
120
|
tooltipPlacement,
|
|
121
121
|
tooltipStyle: { backgroundColor: 'black', color: 'white', boxShadow: 'none' },
|
|
122
122
|
} );
|
|
123
|
-
screen.getByRole( '
|
|
123
|
+
screen.getByRole( 'application', { name: /line chart/i } ).focus();
|
|
124
124
|
await user.keyboard( '{ArrowRight}' );
|
|
125
125
|
|
|
126
126
|
const vertical = screen.getByTestId( 'xy-chart-tooltip-crosshair-vertical' );
|
|
@@ -143,7 +143,7 @@ describe( 'LineChart', () => {
|
|
|
143
143
|
test( 'passes a color-only tooltipStyle through with no background added', async () => {
|
|
144
144
|
const user = userEvent.setup();
|
|
145
145
|
renderWithTheme( { tooltipStyle: { color: 'white' } } );
|
|
146
|
-
screen.getByRole( '
|
|
146
|
+
screen.getByRole( 'application', { name: /line chart/i } ).focus();
|
|
147
147
|
await user.keyboard( '{ArrowRight}' );
|
|
148
148
|
|
|
149
149
|
const box = screen.getByTestId( 'bounded-tooltip' );
|
|
@@ -154,7 +154,7 @@ describe( 'LineChart', () => {
|
|
|
154
154
|
test( 'paints a tooltipStyle background on the box, not on the default content', async () => {
|
|
155
155
|
const user = userEvent.setup();
|
|
156
156
|
renderWithTheme( { tooltipStyle: { background: 'black' } } );
|
|
157
|
-
screen.getByRole( '
|
|
157
|
+
screen.getByRole( 'application', { name: /line chart/i } ).focus();
|
|
158
158
|
await user.keyboard( '{ArrowRight}' );
|
|
159
159
|
|
|
160
160
|
expect( screen.getByTestId( 'bounded-tooltip' ) ).toHaveStyle( { background: 'black' } );
|
|
@@ -164,7 +164,7 @@ describe( 'LineChart', () => {
|
|
|
164
164
|
test( 'preserves the existing tooltip colors for noncolor overrides', async () => {
|
|
165
165
|
const user = userEvent.setup();
|
|
166
166
|
renderWithTheme( { tooltipStyle: { boxShadow: 'none' } } );
|
|
167
|
-
screen.getByRole( '
|
|
167
|
+
screen.getByRole( 'application', { name: /line chart/i } ).focus();
|
|
168
168
|
await user.keyboard( '{ArrowRight}' );
|
|
169
169
|
|
|
170
170
|
const content = screen.getByTestId( 'line-chart-tooltip-content' );
|
|
@@ -179,13 +179,13 @@ describe( 'LineChart', () => {
|
|
|
179
179
|
} );
|
|
180
180
|
|
|
181
181
|
test.each( [ [ 'Escape', '{Escape}' ] ] )(
|
|
182
|
-
'returns focus to the
|
|
182
|
+
'returns focus to the chart after %s',
|
|
183
183
|
async ( _name, keys ) => {
|
|
184
184
|
jest.useFakeTimers();
|
|
185
185
|
try {
|
|
186
186
|
const user = userEvent.setup( { advanceTimers: jest.advanceTimersByTime } );
|
|
187
187
|
renderWithTheme();
|
|
188
|
-
const chart = screen.getByRole( '
|
|
188
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
189
189
|
|
|
190
190
|
await user.tab();
|
|
191
191
|
expect( chart ).toHaveFocus();
|
|
@@ -225,7 +225,7 @@ describe( 'LineChart', () => {
|
|
|
225
225
|
],
|
|
226
226
|
} );
|
|
227
227
|
// Should render without crashing and show the single point
|
|
228
|
-
expect( screen.getByRole( '
|
|
228
|
+
expect( screen.getByRole( 'application', { name: /line chart/i } ) ).toBeInTheDocument();
|
|
229
229
|
} );
|
|
230
230
|
|
|
231
231
|
test( 'renders a bucket with no reading instead of failing the whole chart', () => {
|
|
@@ -241,7 +241,7 @@ describe( 'LineChart', () => {
|
|
|
241
241
|
],
|
|
242
242
|
} );
|
|
243
243
|
expect( screen.queryByText( /invalid data/i ) ).not.toBeInTheDocument();
|
|
244
|
-
expect( screen.getByRole( '
|
|
244
|
+
expect( screen.getByRole( 'application', { name: /line chart/i } ) ).toBeInTheDocument();
|
|
245
245
|
} );
|
|
246
246
|
|
|
247
247
|
test( 'tooltip reads No data for a bucket with no reading', async () => {
|
|
@@ -258,7 +258,7 @@ describe( 'LineChart', () => {
|
|
|
258
258
|
],
|
|
259
259
|
} );
|
|
260
260
|
|
|
261
|
-
screen.getByRole( '
|
|
261
|
+
screen.getByRole( 'application', { name: /line chart/i } ).focus();
|
|
262
262
|
await user.keyboard( '{ArrowRight}' );
|
|
263
263
|
|
|
264
264
|
const tooltip = screen.getByTestId( 'chart-tooltip-0' );
|
|
@@ -282,7 +282,7 @@ describe( 'LineChart', () => {
|
|
|
282
282
|
|
|
283
283
|
expect( screen.queryByText( /invalid data/i ) ).not.toBeInTheDocument();
|
|
284
284
|
|
|
285
|
-
const chart = screen.getByRole( '
|
|
285
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
286
286
|
const ticks = within( chart )
|
|
287
287
|
.getAllByText( /^-?[\d.,]+$/ )
|
|
288
288
|
.map( el => el.textContent ?? '' );
|
|
@@ -305,7 +305,7 @@ describe( 'LineChart', () => {
|
|
|
305
305
|
],
|
|
306
306
|
} );
|
|
307
307
|
|
|
308
|
-
const chart = screen.getByRole( '
|
|
308
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
309
309
|
const ticks = within( chart )
|
|
310
310
|
.getAllByText( /^-?[\d.,]+$/ )
|
|
311
311
|
.map( el => el.textContent );
|
|
@@ -333,7 +333,7 @@ describe( 'LineChart', () => {
|
|
|
333
333
|
],
|
|
334
334
|
} );
|
|
335
335
|
|
|
336
|
-
const chart = screen.getByRole( '
|
|
336
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
337
337
|
const ticks = within( chart )
|
|
338
338
|
.getAllByText( /^-?[\d.,]+$/ )
|
|
339
339
|
.map( el => el.textContent );
|
|
@@ -353,7 +353,7 @@ describe( 'LineChart', () => {
|
|
|
353
353
|
],
|
|
354
354
|
} );
|
|
355
355
|
|
|
356
|
-
const chart = screen.getByRole( '
|
|
356
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
357
357
|
expect( within( chart ).getAllByText( /^-?[\d.,]+$/ ).length ).toBeGreaterThan( 2 );
|
|
358
358
|
} );
|
|
359
359
|
|
|
@@ -368,7 +368,7 @@ describe( 'LineChart', () => {
|
|
|
368
368
|
options: { axis: { y: { tickValues: [ 0, 0.5, 1 ] } } },
|
|
369
369
|
} );
|
|
370
370
|
|
|
371
|
-
const chart = screen.getByRole( '
|
|
371
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
372
372
|
expect( within( chart ).getAllByText( /^-?[\d.,]+$/ ) ).toHaveLength( 3 );
|
|
373
373
|
} );
|
|
374
374
|
|
|
@@ -389,7 +389,7 @@ describe( 'LineChart', () => {
|
|
|
389
389
|
},
|
|
390
390
|
} );
|
|
391
391
|
|
|
392
|
-
const chart = screen.getByRole( '
|
|
392
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
393
393
|
expect( within( chart ).getAllByText( /^\d+%$/ ) ).toHaveLength( 6 );
|
|
394
394
|
} );
|
|
395
395
|
|
|
@@ -544,9 +544,9 @@ describe( 'LineChart', () => {
|
|
|
544
544
|
],
|
|
545
545
|
} );
|
|
546
546
|
|
|
547
|
-
const
|
|
547
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
548
548
|
// eslint-disable-next-line testing-library/no-node-access -- visx's stroke is a <path class="visx-line">; the axis and grid lines are plain <line> elements sharing that class.
|
|
549
|
-
const linePath =
|
|
549
|
+
const linePath = chart.querySelector( 'path.visx-line' );
|
|
550
550
|
const gapCount = linePath?.getAttribute( 'd' )?.match( /M/g )?.length;
|
|
551
551
|
expect( gapCount ).toBe( 2 );
|
|
552
552
|
} );
|
|
@@ -615,7 +615,7 @@ describe( 'LineChart', () => {
|
|
|
615
615
|
expect( screen.getAllByTestId( 'legend-item' ) ).toHaveLength( 2 );
|
|
616
616
|
const html = document.body.innerHTML;
|
|
617
617
|
expect( html.indexOf( 'data-testid="legend-horizontal"' ) ).toBeLessThan(
|
|
618
|
-
html.indexOf( 'role="
|
|
618
|
+
html.indexOf( 'role="application"' )
|
|
619
619
|
);
|
|
620
620
|
} );
|
|
621
621
|
} );
|
|
@@ -805,7 +805,7 @@ describe( 'LineChart', () => {
|
|
|
805
805
|
expect( gradient ).toBeInTheDocument();
|
|
806
806
|
|
|
807
807
|
// Verify that the chart container exists
|
|
808
|
-
const chart = screen.getByRole( '
|
|
808
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
809
809
|
expect( chart ).toBeInTheDocument();
|
|
810
810
|
} );
|
|
811
811
|
} );
|
|
@@ -821,7 +821,7 @@ describe( 'LineChart', () => {
|
|
|
821
821
|
},
|
|
822
822
|
} );
|
|
823
823
|
// The chart should render with the custom axis configuration
|
|
824
|
-
expect( screen.getByRole( '
|
|
824
|
+
expect( screen.getByRole( 'application', { name: /line chart/i } ) ).toBeInTheDocument();
|
|
825
825
|
} );
|
|
826
826
|
} );
|
|
827
827
|
|
|
@@ -1067,7 +1067,7 @@ describe( 'LineChart', () => {
|
|
|
1067
1067
|
|
|
1068
1068
|
// Instead of checking styles, verify the chart renders
|
|
1069
1069
|
expect( screen.getByTestId( 'line-chart' ) ).toBeInTheDocument();
|
|
1070
|
-
expect( screen.getByRole( '
|
|
1070
|
+
expect( screen.getByRole( 'application', { name: /line chart/i } ) ).toBeInTheDocument();
|
|
1071
1071
|
} );
|
|
1072
1072
|
} );
|
|
1073
1073
|
|
|
@@ -1605,17 +1605,18 @@ describe( 'LineChart', () => {
|
|
|
1605
1605
|
describe( 'Chart Focus and Accessibility Attributes', () => {
|
|
1606
1606
|
test( 'chart container has proper accessibility attributes', () => {
|
|
1607
1607
|
renderWithTheme();
|
|
1608
|
-
const chart = screen.getByRole( '
|
|
1608
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
1609
1609
|
|
|
1610
1610
|
expect( chart ).toHaveAttribute( 'tabIndex', '0' );
|
|
1611
|
-
expect( chart ).toHaveAttribute( 'role', '
|
|
1611
|
+
expect( chart ).toHaveAttribute( 'role', 'application' );
|
|
1612
1612
|
expect( chart ).toHaveAttribute( 'aria-label', 'Line chart' );
|
|
1613
|
+
expect( screen.queryByLabelText( 'XYChart' ) ).not.toBeInTheDocument();
|
|
1613
1614
|
} );
|
|
1614
1615
|
|
|
1615
1616
|
test( 'chart container can receive focus', async () => {
|
|
1616
1617
|
const user = userEvent.setup();
|
|
1617
1618
|
renderWithTheme();
|
|
1618
|
-
const chart = screen.getByRole( '
|
|
1619
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
1619
1620
|
|
|
1620
1621
|
await user.tab();
|
|
1621
1622
|
expect( chart ).toHaveFocus();
|
|
@@ -1646,7 +1647,7 @@ describe( 'LineChart', () => {
|
|
|
1646
1647
|
const onDatumActivate = jest.fn();
|
|
1647
1648
|
renderWithTheme( { data: [ SERIES_A, SERIES_B ], onDatumActivate } );
|
|
1648
1649
|
|
|
1649
|
-
screen.getByRole( '
|
|
1650
|
+
screen.getByRole( 'application', { name: /line chart/i } ).focus();
|
|
1650
1651
|
await user.keyboard( '{ArrowRight}{ArrowRight}{Enter}' );
|
|
1651
1652
|
|
|
1652
1653
|
expect( onDatumActivate ).toHaveBeenCalledTimes( 1 );
|
|
@@ -1662,7 +1663,7 @@ describe( 'LineChart', () => {
|
|
|
1662
1663
|
const onDatumActivate = jest.fn();
|
|
1663
1664
|
renderWithTheme( { data: [ SERIES_A ], onDatumActivate } );
|
|
1664
1665
|
|
|
1665
|
-
screen.getByRole( '
|
|
1666
|
+
screen.getByRole( 'application', { name: /line chart/i } ).focus();
|
|
1666
1667
|
await user.keyboard( '{Enter}' );
|
|
1667
1668
|
|
|
1668
1669
|
expect( onDatumActivate ).not.toHaveBeenCalled();
|
|
@@ -1696,7 +1697,7 @@ describe( 'LineChart', () => {
|
|
|
1696
1697
|
],
|
|
1697
1698
|
} );
|
|
1698
1699
|
|
|
1699
|
-
const chart = screen.getByRole( '
|
|
1700
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
1700
1701
|
chart.focus();
|
|
1701
1702
|
|
|
1702
1703
|
// Single tab should focus on the first tooltip.
|
|
@@ -1741,7 +1742,7 @@ describe( 'LineChart', () => {
|
|
|
1741
1742
|
],
|
|
1742
1743
|
} );
|
|
1743
1744
|
|
|
1744
|
-
const chart = screen.getByRole( '
|
|
1745
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
1745
1746
|
chart.focus();
|
|
1746
1747
|
|
|
1747
1748
|
// Right arrow key should focus on the first tooltip.
|
|
@@ -1788,7 +1789,7 @@ describe( 'LineChart', () => {
|
|
|
1788
1789
|
],
|
|
1789
1790
|
} );
|
|
1790
1791
|
|
|
1791
|
-
const chart = screen.getByRole( '
|
|
1792
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
1792
1793
|
chart.focus();
|
|
1793
1794
|
|
|
1794
1795
|
// Chart should be in focus.
|
|
@@ -1819,7 +1820,7 @@ describe( 'LineChart', () => {
|
|
|
1819
1820
|
|
|
1820
1821
|
renderWithTheme( { renderTooltip: customTooltipRenderer } );
|
|
1821
1822
|
|
|
1822
|
-
const chart = screen.getByRole( '
|
|
1823
|
+
const chart = screen.getByRole( 'application', { name: /line chart/i } );
|
|
1823
1824
|
chart.focus();
|
|
1824
1825
|
|
|
1825
1826
|
// Click on right arrow key to focus on the first tooltip.
|
|
@@ -2049,8 +2050,9 @@ describe( 'LineChart', () => {
|
|
|
2049
2050
|
|
|
2050
2051
|
const pointer = ( type: string, clientX: number, clientY: number ) => {
|
|
2051
2052
|
// visx owns the pointer capture rect and does not expose an attribute prop for it.
|
|
2053
|
+
const chart = screen.getByRole( 'application' );
|
|
2052
2054
|
// eslint-disable-next-line testing-library/no-node-access
|
|
2053
|
-
const target =
|
|
2055
|
+
const target = chart.querySelector( 'svg > rect[fill="transparent"]' );
|
|
2054
2056
|
fireEvent( target, new MouseEvent( type, { bubbles: true, clientX, clientY } ) );
|
|
2055
2057
|
};
|
|
2056
2058
|
|
|
@@ -2398,7 +2400,7 @@ describe( 'LineChart', () => {
|
|
|
2398
2400
|
options: {},
|
|
2399
2401
|
},
|
|
2400
2402
|
];
|
|
2401
|
-
// Bare numbers inside the plot (scoped via the chart's
|
|
2403
|
+
// Bare numbers inside the plot (scoped via the chart's application role, away from visx's
|
|
2402
2404
|
// off-screen text-measurement SVGs) are value-axis ticks: the series labels carry no digits
|
|
2403
2405
|
// and the empty-state message is text-only.
|
|
2404
2406
|
const numericTick = /^[\d,]+$/;
|
|
@@ -2414,7 +2416,7 @@ describe( 'LineChart', () => {
|
|
|
2414
2416
|
data: twoSeries,
|
|
2415
2417
|
} );
|
|
2416
2418
|
|
|
2417
|
-
const chart = screen.getByRole( '
|
|
2419
|
+
const chart = screen.getByRole( 'application' );
|
|
2418
2420
|
const ticksBefore = within( chart )
|
|
2419
2421
|
.getAllByText( numericTick )
|
|
2420
2422
|
.map( el => el.textContent )
|
|
@@ -2441,7 +2443,7 @@ describe( 'LineChart', () => {
|
|
|
2441
2443
|
data: twoSeries,
|
|
2442
2444
|
} );
|
|
2443
2445
|
|
|
2444
|
-
const chart = screen.getByRole( '
|
|
2446
|
+
const chart = screen.getByRole( 'application' );
|
|
2445
2447
|
const ticksBefore = within( chart )
|
|
2446
2448
|
.getAllByText( numericTick )
|
|
2447
2449
|
.map( el => el.textContent )
|
|
@@ -2469,7 +2471,7 @@ describe( 'LineChart', () => {
|
|
|
2469
2471
|
data: twoSeries,
|
|
2470
2472
|
} );
|
|
2471
2473
|
|
|
2472
|
-
const chart = screen.getByRole( '
|
|
2474
|
+
const chart = screen.getByRole( 'application' );
|
|
2473
2475
|
expect( within( chart ).getAllByText( numericTick ).length ).toBeGreaterThan( 0 );
|
|
2474
2476
|
|
|
2475
2477
|
const buttons = screen.getAllByRole( 'button' );
|
|
@@ -93,7 +93,7 @@ describe( 'default tooltip at hourly resolution', () => {
|
|
|
93
93
|
</GlobalChartsProvider>
|
|
94
94
|
);
|
|
95
95
|
|
|
96
|
-
screen.getByRole( '
|
|
96
|
+
screen.getByRole( 'application', { name: /line chart/i } ).focus();
|
|
97
97
|
await user.keyboard( '{ArrowRight}' );
|
|
98
98
|
|
|
99
99
|
expect( screen.getByTestId( 'chart-tooltip-0' ) ).toHaveTextContent( '2.8.2026, 09 Uhr' );
|
|
@@ -130,7 +130,7 @@ describe( 'default tooltip at hourly resolution', () => {
|
|
|
130
130
|
</GlobalChartsProvider>
|
|
131
131
|
);
|
|
132
132
|
|
|
133
|
-
screen.getByRole( '
|
|
133
|
+
screen.getByRole( 'application', { name: /line chart/i } ).focus();
|
|
134
134
|
await user.keyboard( '{ArrowRight}' );
|
|
135
135
|
|
|
136
136
|
const tooltip = screen.getByTestId( 'chart-tooltip-0' );
|
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
} from '../../types';
|
|
11
11
|
import type { CrosshairStyle, RenderTooltipParams } from '../../visx/types';
|
|
12
12
|
import type { GlyphProps } from '@visx/xychart';
|
|
13
|
-
import type { ReactNode, SVGProps,
|
|
13
|
+
import type { ReactNode, SVGProps, CSSProperties } from 'react';
|
|
14
14
|
|
|
15
15
|
export type LineChartAnnotationProps = {
|
|
16
16
|
datum: DataPointDate;
|
|
@@ -19,8 +19,8 @@ export type LineChartAnnotationProps = {
|
|
|
19
19
|
subjectType?: 'circle' | 'line-vertical' | 'line-horizontal';
|
|
20
20
|
styles?: AnnotationStyles;
|
|
21
21
|
testId?: string;
|
|
22
|
-
renderLabel?:
|
|
23
|
-
renderLabelPopover?:
|
|
22
|
+
renderLabel?: ( props: { title: string; subtitle?: string } ) => ReactNode;
|
|
23
|
+
renderLabelPopover?: ( props: { title: string; subtitle?: string } ) => ReactNode;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
export type CurveType = 'smooth' | 'linear' | 'monotone';
|