@automattic/charts 1.6.0 → 1.8.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 +380 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +12 -7
- package/dist/index.d.cts +119 -30
- package/dist/index.d.ts +122 -33
- package/dist/index.js +380 -60
- package/dist/index.js.map +1 -1
- package/package.json +22 -21
- package/src/charts/area-chart/types.ts +1 -1
- package/src/charts/bar-chart/bar-chart.tsx +197 -43
- package/src/charts/bar-chart/private/comparison-bars-geometry.ts +70 -0
- package/src/charts/bar-chart/private/comparison-bars.tsx +154 -0
- package/src/charts/bar-chart/private/comparison-constants.ts +33 -0
- package/src/charts/bar-chart/private/index.ts +9 -0
- package/src/charts/bar-chart/private/test/comparison-bars-geometry.test.ts +47 -0
- package/src/charts/bar-chart/private/test/comparison-bars.test.tsx +183 -0
- package/src/charts/bar-chart/private/use-bar-chart-options.ts +46 -5
- package/src/charts/bar-chart/test/bar-chart.test.tsx +191 -1
- package/src/charts/geo-chart/geo-chart.tsx +8 -10
- package/src/charts/leaderboard-chart/leaderboard-chart.module.scss +27 -14
- package/src/charts/leaderboard-chart/leaderboard-chart.tsx +7 -4
- package/src/charts/leaderboard-chart/test/leaderboard-chart.test.tsx +51 -4
- 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-chart/pie-chart.module.scss +0 -4
- package/src/charts/pie-chart/pie-chart.tsx +3 -8
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +0 -5
- package/src/charts/pie-semi-circle-chart/pie-semi-circle-chart.tsx +5 -10
- package/src/charts/private/center/center.module.scss +4 -0
- package/src/charts/private/center/center.tsx +33 -0
- package/src/charts/private/center/index.ts +2 -0
- package/src/charts/private/center/test/center.test.tsx +60 -0
- package/src/charts/private/chart-layout/chart-layout.tsx +1 -2
- package/src/charts/private/svg-empty-state/svg-empty-state.module.scss +0 -2
- package/src/charts/private/svg-empty-state/svg-empty-state.tsx +2 -4
- 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/global-charts-provider.tsx +2 -0
- package/src/providers/chart-context/test/chart-context.test.tsx +13 -1
- package/src/providers/chart-context/themes.ts +9 -1
- package/src/providers/chart-context/types.ts +9 -2
- package/src/types.ts +120 -11
- package/src/utils/get-styles.ts +36 -14
- package/src/utils/index.ts +6 -1
- package/src/utils/test/get-styles.test.ts +47 -1
- package/src/visx/types.ts +30 -1
|
@@ -54,16 +54,18 @@ const defaultDeltaFormatter = ( value: number ): string => {
|
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Build a bar's width. A hover-inset CSS variable (0 by default) is subtracted
|
|
57
|
-
*
|
|
58
|
-
*
|
|
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.
|
|
59
61
|
*
|
|
60
62
|
* @param share - The bar's share of the row width, as a percentage.
|
|
61
63
|
* @return A CSS width value.
|
|
62
64
|
*/
|
|
63
65
|
const getBarWidth = ( share: number ): string =>
|
|
64
|
-
`calc(${ share }% - var(--a8c--charts--leaderboard--bar--hover-inset, 0px))`;
|
|
66
|
+
`calc(${ share }% - var(--a8c--charts--leaderboard--bar--hover-inset, 0px) * ${ share } / 100)`;
|
|
65
67
|
|
|
66
|
-
const BarLabel = ( { label }: { label:
|
|
68
|
+
const BarLabel = ( { label }: { label: LeaderboardEntry[ 'label' ] } ) => (
|
|
67
69
|
<>{ typeof label === 'string' ? <Text className={ styles.label }>{ label }</Text> : label }</>
|
|
68
70
|
);
|
|
69
71
|
|
|
@@ -376,6 +378,7 @@ const LeaderboardChartInternal: FC< LeaderboardChartProps > = ( {
|
|
|
376
378
|
type="button"
|
|
377
379
|
className={ styles.interactiveRow }
|
|
378
380
|
onClick={ entry.onClick }
|
|
381
|
+
aria-label={ entry.ariaLabel }
|
|
379
382
|
>
|
|
380
383
|
{ rowCells }
|
|
381
384
|
<Icon className={ styles.chevron } icon={ chevronRight } size={ 24 } />
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { render, screen
|
|
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
|
|
|
@@ -375,20 +376,66 @@ describe( 'LeaderboardChart', () => {
|
|
|
375
376
|
expect( screen.getByRole( 'button' ).tagName ).toBe( 'BUTTON' );
|
|
376
377
|
} );
|
|
377
378
|
|
|
378
|
-
it( 'calls onClick when the row is clicked', () => {
|
|
379
|
+
it( 'calls onClick when the row is clicked', async () => {
|
|
380
|
+
const user = userEvent.setup();
|
|
379
381
|
const onClick = jest.fn();
|
|
380
382
|
render( <LeaderboardChart data={ [ { ...mockData[ 0 ], onClick } ] } /> );
|
|
381
|
-
|
|
382
|
-
fireEvent.click( screen.getByRole( 'button' ) );
|
|
383
|
+
await user.click( screen.getByRole( 'button' ) );
|
|
383
384
|
expect( onClick ).toHaveBeenCalledTimes( 1 );
|
|
384
385
|
} );
|
|
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
|
+
|
|
386
400
|
it( 'gives the interactive row an accessible name from the label and value', () => {
|
|
387
401
|
render( <LeaderboardChart data={ [ { ...mockData[ 0 ], onClick: jest.fn() } ] } /> );
|
|
388
402
|
// mockData[0] label is 'Direct', currentValue 12500 → '12.5K'
|
|
389
403
|
expect( screen.getByRole( 'button' ) ).toHaveAccessibleName( /Direct.*12\.5K/ );
|
|
390
404
|
} );
|
|
391
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
|
+
|
|
392
439
|
it( 'does not render a button for entries without onClick', () => {
|
|
393
440
|
render( <LeaderboardChart data={ [ mockData[ 0 ] ] } /> );
|
|
394
441
|
expect( screen.queryByRole( 'button' ) ).not.toBeInTheDocument();
|
|
@@ -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 > ) => {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { __ } from '@wordpress/i18n';
|
|
2
|
-
import { Icon, close } from '@wordpress/icons';
|
|
3
2
|
import { Stack } from '@wordpress/ui';
|
|
4
3
|
import clsx from 'clsx';
|
|
5
4
|
import { useEffect, useId, useRef, useState } from 'react';
|
|
@@ -10,6 +9,23 @@ import type { FC } from 'react';
|
|
|
10
9
|
|
|
11
10
|
export const POPOVER_BUTTON_SIZE = 44;
|
|
12
11
|
|
|
12
|
+
const CloseIcon = () => (
|
|
13
|
+
<svg
|
|
14
|
+
width="16"
|
|
15
|
+
height="16"
|
|
16
|
+
viewBox="0 0 24 24"
|
|
17
|
+
fill="none"
|
|
18
|
+
stroke="currentColor"
|
|
19
|
+
strokeWidth="2"
|
|
20
|
+
strokeLinecap="round"
|
|
21
|
+
strokeLinejoin="round"
|
|
22
|
+
aria-hidden="true"
|
|
23
|
+
focusable="false"
|
|
24
|
+
>
|
|
25
|
+
<path d="M6 6l12 12M18 6L6 18" />
|
|
26
|
+
</svg>
|
|
27
|
+
);
|
|
28
|
+
|
|
13
29
|
interface LineChartAnnotationLabelWithPopoverProps {
|
|
14
30
|
title: string;
|
|
15
31
|
subtitle?: string;
|
|
@@ -101,7 +117,7 @@ const LineChartAnnotationLabelWithPopover: FC< LineChartAnnotationLabelWithPopov
|
|
|
101
117
|
className={ styles[ 'line-chart__annotation-label-popover-close-button' ] }
|
|
102
118
|
aria-label={ __( 'Close', 'jetpack-charts' ) }
|
|
103
119
|
>
|
|
104
|
-
<
|
|
120
|
+
<CloseIcon />
|
|
105
121
|
</button>
|
|
106
122
|
</Stack>
|
|
107
123
|
</div>
|
|
@@ -15,7 +15,7 @@ import LineChartAnnotationLabelWithPopover, {
|
|
|
15
15
|
POPOVER_BUTTON_SIZE,
|
|
16
16
|
} from './line-chart-annotation-label-popover';
|
|
17
17
|
import type { LineChartAnnotationProps } from '../types';
|
|
18
|
-
import type { LabelProps } from '@visx/annotation
|
|
18
|
+
import type { LabelProps } from '@visx/annotation';
|
|
19
19
|
import type { TextProps } from '@visx/text';
|
|
20
20
|
import type { FC } from 'react';
|
|
21
21
|
|
|
@@ -5,8 +5,8 @@ import type {
|
|
|
5
5
|
AnnotationStyles,
|
|
6
6
|
DataPoint,
|
|
7
7
|
} from '../../types';
|
|
8
|
+
import type { RenderTooltipParams } from '../../visx/types';
|
|
8
9
|
import type { GlyphProps } from '@visx/xychart';
|
|
9
|
-
import type { RenderTooltipParams } from '@visx/xychart/lib/components/Tooltip';
|
|
10
10
|
import type { ReactNode, SVGProps, FC } from 'react';
|
|
11
11
|
|
|
12
12
|
export type LineChartAnnotationProps = {
|
|
@@ -2,7 +2,6 @@ import { Group } from '@visx/group';
|
|
|
2
2
|
import { Pie } from '@visx/shape';
|
|
3
3
|
import { useTooltip, useTooltipInPortal } from '@visx/tooltip';
|
|
4
4
|
import { __ } from '@wordpress/i18n';
|
|
5
|
-
import { Stack } from '@wordpress/ui';
|
|
6
5
|
import clsx from 'clsx';
|
|
7
6
|
import { useCallback, useContext, useMemo } from 'react';
|
|
8
7
|
import { Legend, useChartLegendItems } from '../../components/legend';
|
|
@@ -22,6 +21,7 @@ import {
|
|
|
22
21
|
} from '../../providers';
|
|
23
22
|
import { attachSubComponents, resolveFontSize } from '../../utils';
|
|
24
23
|
import { getStringWidth } from '../../visx/text';
|
|
24
|
+
import { Center } from '../private/center';
|
|
25
25
|
import { ChartSVG, ChartHTML, useChartChildren } from '../private/chart-composition';
|
|
26
26
|
import { ChartLayout } from '../private/chart-layout';
|
|
27
27
|
import { RadialWipeAnimation } from '../private/radial-wipe-animation/';
|
|
@@ -358,12 +358,7 @@ const PieChartInternal = ( {
|
|
|
358
358
|
: 0;
|
|
359
359
|
|
|
360
360
|
return (
|
|
361
|
-
<
|
|
362
|
-
ref={ containerRef }
|
|
363
|
-
align="center"
|
|
364
|
-
justify="center"
|
|
365
|
-
className={ styles[ 'pie-chart__centering' ] }
|
|
366
|
-
>
|
|
361
|
+
<Center ref={ containerRef }>
|
|
367
362
|
<svg
|
|
368
363
|
viewBox={ `0 0 ${ width } ${ height }` }
|
|
369
364
|
preserveAspectRatio="xMidYMid meet"
|
|
@@ -493,7 +488,7 @@ const PieChartInternal = ( {
|
|
|
493
488
|
{ ! allSegmentsHidden && svgChildren }
|
|
494
489
|
</Group>
|
|
495
490
|
</svg>
|
|
496
|
-
</
|
|
491
|
+
</Center>
|
|
497
492
|
);
|
|
498
493
|
} }
|
|
499
494
|
</ChartLayout>
|
|
@@ -3,7 +3,6 @@ import { Pie } from '@visx/shape';
|
|
|
3
3
|
import { Text } from '@visx/text';
|
|
4
4
|
import { useTooltip, useTooltipInPortal } from '@visx/tooltip';
|
|
5
5
|
import { __ } from '@wordpress/i18n';
|
|
6
|
-
import { Stack } from '@wordpress/ui';
|
|
7
6
|
import clsx from 'clsx';
|
|
8
7
|
import { useCallback, useContext, useMemo } from 'react';
|
|
9
8
|
import { Legend, useChartLegendItems } from '../../components/legend';
|
|
@@ -21,6 +20,7 @@ import {
|
|
|
21
20
|
GlobalChartsContext,
|
|
22
21
|
} from '../../providers';
|
|
23
22
|
import { attachSubComponents } from '../../utils';
|
|
23
|
+
import { Center } from '../private/center';
|
|
24
24
|
import { ChartSVG, ChartHTML, useChartChildren } from '../private/chart-composition';
|
|
25
25
|
import { ChartLayout } from '../private/chart-layout';
|
|
26
26
|
import { RadialWipeAnimation } from '../private/radial-wipe-animation';
|
|
@@ -37,7 +37,7 @@ import type {
|
|
|
37
37
|
} from '../../types';
|
|
38
38
|
import type { ChartComponentWithComposition } from '../private/chart-composition';
|
|
39
39
|
import type { ResponsiveConfig } from '../private/with-responsive';
|
|
40
|
-
import type {
|
|
40
|
+
import type { PieProvidedProps } from '@visx/shape';
|
|
41
41
|
import type { FC, MouseEvent, ReactNode } from 'react';
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -135,7 +135,7 @@ type PieSemiCircleChartResponsiveComponent = ChartComponentWithComposition<
|
|
|
135
135
|
PieSemiCircleChartBaseProps & ResponsiveConfig
|
|
136
136
|
>;
|
|
137
137
|
|
|
138
|
-
export type ArcData =
|
|
138
|
+
export type ArcData = PieProvidedProps< DataPointPercentageCalculated >[ 'arcs' ][ number ];
|
|
139
139
|
|
|
140
140
|
/**
|
|
141
141
|
* Validates the semi-circle pie chart data
|
|
@@ -397,12 +397,7 @@ const PieSemiCircleChartInternal: FC< PieSemiCircleChartProps > = ( {
|
|
|
397
397
|
const innerRadius = radius * ( 1 - thickness );
|
|
398
398
|
|
|
399
399
|
return (
|
|
400
|
-
<
|
|
401
|
-
ref={ containerRef }
|
|
402
|
-
align="center"
|
|
403
|
-
justify="center"
|
|
404
|
-
className={ styles[ 'pie-semi-circle-chart__centering' ] }
|
|
405
|
-
>
|
|
400
|
+
<Center ref={ containerRef }>
|
|
406
401
|
<svg
|
|
407
402
|
width={ width }
|
|
408
403
|
height={ height }
|
|
@@ -491,7 +486,7 @@ const PieSemiCircleChartInternal: FC< PieSemiCircleChartProps > = ( {
|
|
|
491
486
|
) }
|
|
492
487
|
</Group>
|
|
493
488
|
</svg>
|
|
494
|
-
</
|
|
489
|
+
</Center>
|
|
495
490
|
);
|
|
496
491
|
} }
|
|
497
492
|
</ChartLayout>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Stack } from '@wordpress/ui';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { forwardRef, type ComponentPropsWithoutRef } from 'react';
|
|
4
|
+
import styles from './center.module.scss';
|
|
5
|
+
|
|
6
|
+
export type CenterProps = ComponentPropsWithoutRef< typeof Stack >;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Centers its children on both axes and fills its parent.
|
|
10
|
+
*
|
|
11
|
+
* A thin wrapper around `Stack` with `align="center"` and `justify="center"`
|
|
12
|
+
* defaults (both overridable) plus `width: 100%; height: 100%`. Reads more
|
|
13
|
+
* honestly than a `Stack` with both axes centered, and lets call sites drop
|
|
14
|
+
* ad-hoc `*__centering` classes. Forwards its ref and spreads remaining props
|
|
15
|
+
* onto the underlying `Stack`.
|
|
16
|
+
*
|
|
17
|
+
* @param props - Stack props; `align`/`justify` default to `"center"`.
|
|
18
|
+
* @param ref - Forwarded to the underlying element.
|
|
19
|
+
* @return The centered layout element.
|
|
20
|
+
*/
|
|
21
|
+
export const Center = forwardRef< HTMLDivElement, CenterProps >(
|
|
22
|
+
( { align = 'center', justify = 'center', className, ...props }, ref ) => (
|
|
23
|
+
<Stack
|
|
24
|
+
ref={ ref }
|
|
25
|
+
align={ align }
|
|
26
|
+
justify={ justify }
|
|
27
|
+
className={ clsx( styles.center, className ) }
|
|
28
|
+
{ ...props }
|
|
29
|
+
/>
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
Center.displayName = 'Center';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { createRef } from 'react';
|
|
3
|
+
import { Center } from '../index';
|
|
4
|
+
|
|
5
|
+
describe( 'Center', () => {
|
|
6
|
+
test( 'renders its children', () => {
|
|
7
|
+
render(
|
|
8
|
+
<Center data-testid="center">
|
|
9
|
+
<span>Centered content</span>
|
|
10
|
+
</Center>
|
|
11
|
+
);
|
|
12
|
+
expect( screen.getByTestId( 'center' ) ).toHaveTextContent( 'Centered content' );
|
|
13
|
+
} );
|
|
14
|
+
|
|
15
|
+
test( 'merges a custom className with the center class', () => {
|
|
16
|
+
render(
|
|
17
|
+
<Center data-testid="center" className="custom-class">
|
|
18
|
+
child
|
|
19
|
+
</Center>
|
|
20
|
+
);
|
|
21
|
+
const el = screen.getByTestId( 'center' );
|
|
22
|
+
expect( el ).toHaveClass( 'center' );
|
|
23
|
+
expect( el ).toHaveClass( 'custom-class' );
|
|
24
|
+
} );
|
|
25
|
+
|
|
26
|
+
test( 'forwards its ref to the underlying element', () => {
|
|
27
|
+
const ref = createRef< HTMLDivElement >();
|
|
28
|
+
render( <Center ref={ ref }>child</Center> );
|
|
29
|
+
expect( ref.current ).toBeInstanceOf( HTMLElement );
|
|
30
|
+
} );
|
|
31
|
+
|
|
32
|
+
test( 'spreads arbitrary props through to the element', () => {
|
|
33
|
+
render(
|
|
34
|
+
<Center data-testid="center" style={ { width: 120 } }>
|
|
35
|
+
child
|
|
36
|
+
</Center>
|
|
37
|
+
);
|
|
38
|
+
expect( screen.getByTestId( 'center' ) ).toHaveStyle( { width: '120px' } );
|
|
39
|
+
} );
|
|
40
|
+
|
|
41
|
+
test( 'centers on both axes by default', () => {
|
|
42
|
+
render( <Center data-testid="center">child</Center> );
|
|
43
|
+
expect( screen.getByTestId( 'center' ) ).toHaveStyle( {
|
|
44
|
+
alignItems: 'center',
|
|
45
|
+
justifyContent: 'center',
|
|
46
|
+
} );
|
|
47
|
+
} );
|
|
48
|
+
|
|
49
|
+
test( 'allows overriding the align and justify defaults', () => {
|
|
50
|
+
render(
|
|
51
|
+
<Center data-testid="center" align="flex-start" justify="flex-end">
|
|
52
|
+
child
|
|
53
|
+
</Center>
|
|
54
|
+
);
|
|
55
|
+
expect( screen.getByTestId( 'center' ) ).toHaveStyle( {
|
|
56
|
+
alignItems: 'flex-start',
|
|
57
|
+
justifyContent: 'flex-end',
|
|
58
|
+
} );
|
|
59
|
+
} );
|
|
60
|
+
} );
|
|
@@ -3,9 +3,8 @@ import { useEffect } from 'react';
|
|
|
3
3
|
import { useElementSize } from '../../../hooks';
|
|
4
4
|
import { renderLegendSlot } from '../chart-composition';
|
|
5
5
|
import styles from './chart-layout.module.scss';
|
|
6
|
-
import type { LegendPosition } from '../../../types';
|
|
6
|
+
import type { GapSize, LegendPosition } from '../../../types';
|
|
7
7
|
import type { LegendChild } from '../chart-composition/use-chart-children';
|
|
8
|
-
import type { GapSize } from '@wordpress/theme';
|
|
9
8
|
import type { CSSProperties, ReactNode } from 'react';
|
|
10
9
|
|
|
11
10
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Center } from '../center';
|
|
2
2
|
import styles from './svg-empty-state.module.scss';
|
|
3
3
|
import type { FC, ReactNode } from 'react';
|
|
4
4
|
|
|
@@ -32,9 +32,7 @@ interface SvgEmptyStateProps {
|
|
|
32
32
|
export const SvgEmptyState: FC< SvgEmptyStateProps > = ( { x, y, width, height, children } ) => {
|
|
33
33
|
return (
|
|
34
34
|
<foreignObject x={ x - width / 2 } y={ y - height / 2 } width={ width } height={ height }>
|
|
35
|
-
<
|
|
36
|
-
{ children }
|
|
37
|
-
</Stack>
|
|
35
|
+
<Center className={ styles[ 'svg-empty-state' ] }>{ children }</Center>
|
|
38
36
|
</foreignObject>
|
|
39
37
|
);
|
|
40
38
|
};
|
|
@@ -5,7 +5,7 @@ import styles from './x-zoom.module.scss';
|
|
|
5
5
|
import type { SingleChartRef } from './single-chart-context';
|
|
6
6
|
import type { AxisScale } from '@visx/axis';
|
|
7
7
|
import type { EventHandlerParams } from '@visx/xychart';
|
|
8
|
-
import type {
|
|
8
|
+
import type { ReactNode, RefObject } from 'react';
|
|
9
9
|
|
|
10
10
|
const MIN_DRAG_PIXELS = 6;
|
|
11
11
|
|
|
@@ -37,7 +37,7 @@ export function useXZoom< T extends Date | number = Date >( {
|
|
|
37
37
|
userHandlers,
|
|
38
38
|
}: {
|
|
39
39
|
enabled: boolean;
|
|
40
|
-
chartRef:
|
|
40
|
+
chartRef: RefObject< SingleChartRef | null >;
|
|
41
41
|
userHandlers?: {
|
|
42
42
|
onPointerDown?: PointerHandler;
|
|
43
43
|
onPointerMove?: PointerHandler;
|
|
@@ -6,9 +6,13 @@ import {
|
|
|
6
6
|
type ElementStyles,
|
|
7
7
|
} from '../../../providers';
|
|
8
8
|
import { formatPercentage } from '../../../utils';
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
SeriesData,
|
|
11
|
+
DataPointDate,
|
|
12
|
+
DataPointPercentageCalculated,
|
|
13
|
+
LegendShape,
|
|
14
|
+
} from '../../../types';
|
|
10
15
|
import type { BaseLegendItem } from '../types';
|
|
11
|
-
import type { LegendShape } from '@visx/legend/lib/types';
|
|
12
16
|
import type { GlyphProps } from '@visx/xychart';
|
|
13
17
|
import type { ReactNode } from 'react';
|
|
14
18
|
|
|
@@ -3,8 +3,7 @@ import { SingleChartContext } from '../../charts/private/single-chart-context';
|
|
|
3
3
|
import { GlobalChartsContext } from '../../providers';
|
|
4
4
|
import { BaseLegend } from './private';
|
|
5
5
|
import type { LegendProps } from './types';
|
|
6
|
-
import type { ChartType } from '../../types';
|
|
7
|
-
import type { LegendShape } from '@visx/legend/lib/types';
|
|
6
|
+
import type { ChartType, LegendShape } from '../../types';
|
|
8
7
|
|
|
9
8
|
const defaultShapeByChartType: Partial<
|
|
10
9
|
Record< ChartType, Extract< LegendShape< unknown, unknown >, string > >
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { Tooltip, TooltipContext } from '@visx/xychart';
|
|
2
2
|
import { useContext, useEffect, useCallback, useMemo } from 'react';
|
|
3
3
|
import type { SeriesData, DataPointDate } from '../../types';
|
|
4
|
-
import type {
|
|
5
|
-
TooltipProps as BaseTooltipProps,
|
|
6
|
-
RenderTooltipParams,
|
|
7
|
-
} from '@visx/xychart/lib/components/Tooltip';
|
|
4
|
+
import type { RenderTooltipParams, XyChartTooltipProps } from '../../visx/types';
|
|
8
5
|
import type { ReactNode } from 'react';
|
|
9
6
|
|
|
10
7
|
// Type for flattened tooltip data used in individual mode
|
|
@@ -17,7 +14,7 @@ export type FlattenedTooltipData = {
|
|
|
17
14
|
|
|
18
15
|
// Enhanced tooltip with keyboard navigation and accessibility
|
|
19
16
|
interface AccessibleTooltipProps
|
|
20
|
-
extends Omit<
|
|
17
|
+
extends Omit< XyChartTooltipProps< DataPointDate >, 'renderTooltip' > {
|
|
21
18
|
renderTooltip?: ( params: RenderTooltipParams< DataPointDate > ) => ReactNode;
|
|
22
19
|
selectedIndex?: number | undefined;
|
|
23
20
|
tooltipRef?: ( element: HTMLDivElement | null ) => void;
|
|
@@ -242,4 +239,3 @@ export const useKeyboardNavigation = ( {
|
|
|
242
239
|
|
|
243
240
|
// Re-export the base Tooltip for backwards compatibility
|
|
244
241
|
export { Tooltip };
|
|
245
|
-
export type { BaseTooltipProps };
|
package/src/index.ts
CHANGED
|
@@ -54,6 +54,12 @@ export type {
|
|
|
54
54
|
ChartLegendConfig,
|
|
55
55
|
BaseChartProps,
|
|
56
56
|
GridProps,
|
|
57
|
+
GoogleDataTableColumn,
|
|
58
|
+
GoogleDataTableColumnRoleType,
|
|
59
|
+
GoogleDataTableRow,
|
|
60
|
+
LegendShape,
|
|
61
|
+
LegendShapeLabel,
|
|
62
|
+
LegendShapeRenderProps,
|
|
57
63
|
} from './types';
|
|
58
64
|
export type * from './visx/types';
|
|
59
65
|
export type { PieChartProps, PieChartRenderTooltipParams } from './charts/pie-chart';
|
|
@@ -65,11 +71,6 @@ export type { GeoChartProps, GeoRegion, GeoResolution } from './charts/geo-chart
|
|
|
65
71
|
export type { LegendValueDisplay, BaseLegendItem } from './components/legend';
|
|
66
72
|
export type { TrendIndicatorProps, TrendDirection } from './components/trend-indicator';
|
|
67
73
|
export type { LineStyles, GridStyles, EventHandlerParams } from '@visx/xychart';
|
|
68
|
-
export type {
|
|
69
|
-
GoogleDataTableColumn,
|
|
70
|
-
GoogleDataTableRow,
|
|
71
|
-
GoogleDataTableColumnRoleType,
|
|
72
|
-
} from 'react-google-charts';
|
|
73
74
|
|
|
74
75
|
// Re-exports from removed individual entry points
|
|
75
76
|
export { useLeaderboardLegendItems } from './charts/leaderboard-chart/hooks';
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from 'react';
|
|
11
11
|
import {
|
|
12
12
|
getItemShapeStyles,
|
|
13
|
+
getSeriesBarStyles,
|
|
13
14
|
getSeriesLineStyles,
|
|
14
15
|
mergeThemes,
|
|
15
16
|
resolveCssVariable,
|
|
@@ -206,6 +207,7 @@ export const GlobalChartsProvider: FC< GlobalChartsProviderProps > = ( { childre
|
|
|
206
207
|
( isPointPercentageData && data?.color ),
|
|
207
208
|
} ),
|
|
208
209
|
lineStyles: isSeriesData ? getSeriesLineStyles( data, index, providerTheme ) : {},
|
|
210
|
+
barStyles: isSeriesData ? getSeriesBarStyles( data, index, providerTheme ) : {},
|
|
209
211
|
glyph: providerTheme.glyphs?.[ index ],
|
|
210
212
|
shapeStyles: isSeriesData
|
|
211
213
|
? getItemShapeStyles( data, index, providerTheme, legendShape )
|
|
@@ -4,6 +4,7 @@ import { GlobalChartsProvider } from '../global-charts-provider';
|
|
|
4
4
|
import { useChartId } from '../hooks/use-chart-id';
|
|
5
5
|
import { useChartRegistration } from '../hooks/use-chart-registration';
|
|
6
6
|
import { useGlobalChartsContext } from '../hooks/use-global-charts-context';
|
|
7
|
+
import { defaultTheme } from '../themes';
|
|
7
8
|
import type { BaseLegendItem } from '../../../components/legend';
|
|
8
9
|
import type { ChartTheme, SeriesData } from '../../../types';
|
|
9
10
|
import type { GlobalChartsContextValue } from '../types';
|
|
@@ -1165,10 +1166,12 @@ describe( 'ChartContext', () => {
|
|
|
1165
1166
|
legendShape: 'rect',
|
|
1166
1167
|
} );
|
|
1167
1168
|
|
|
1168
|
-
// Should get theme legend shape styles
|
|
1169
|
+
// Should get theme legend shape styles (not line styles), with the comparison bar
|
|
1170
|
+
// opacity layered on so the swatch matches the translucent comparison bar.
|
|
1169
1171
|
expect( styles.shapeStyles ).toEqual( {
|
|
1170
1172
|
fill: '#LEGEND1',
|
|
1171
1173
|
stroke: '#BORDER1',
|
|
1174
|
+
opacity: 0.5,
|
|
1172
1175
|
} );
|
|
1173
1176
|
} );
|
|
1174
1177
|
} );
|
|
@@ -2534,4 +2537,13 @@ describe( 'ChartContext', () => {
|
|
|
2534
2537
|
} );
|
|
2535
2538
|
} );
|
|
2536
2539
|
} );
|
|
2540
|
+
|
|
2541
|
+
describe( 'defaultTheme', () => {
|
|
2542
|
+
it( 'exposes default barChart comparison styles', () => {
|
|
2543
|
+
expect( defaultTheme.barChart.barStyles.comparison ).toEqual( {
|
|
2544
|
+
widthFactor: 1.5,
|
|
2545
|
+
opacity: 0.5,
|
|
2546
|
+
} );
|
|
2547
|
+
} );
|
|
2548
|
+
} );
|
|
2537
2549
|
} );
|
|
@@ -53,7 +53,7 @@ const defaultTheme: CompleteChartTheme = {
|
|
|
53
53
|
leaderboardChart: {
|
|
54
54
|
rowGap: 12,
|
|
55
55
|
columnGap: 4,
|
|
56
|
-
labelSpacing:
|
|
56
|
+
labelSpacing: 'xs',
|
|
57
57
|
deltaColors: [ '#FF8C8F', '#757575', '#1F9828' ], // [negative, neutral, positive]
|
|
58
58
|
},
|
|
59
59
|
conversionFunnelChart: {
|
|
@@ -69,6 +69,14 @@ const defaultTheme: CompleteChartTheme = {
|
|
|
69
69
|
},
|
|
70
70
|
},
|
|
71
71
|
},
|
|
72
|
+
barChart: {
|
|
73
|
+
barStyles: {
|
|
74
|
+
comparison: {
|
|
75
|
+
widthFactor: 1.5,
|
|
76
|
+
opacity: 0.5,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
72
80
|
sparkline: {
|
|
73
81
|
margin: { top: 2, right: 2, bottom: 2, left: 2 },
|
|
74
82
|
strokeWidth: 1.5,
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { CSSProperties, ReactNode } from 'react';
|
|
2
2
|
import type { BaseLegendItem } from '../../components/legend';
|
|
3
|
-
import type {
|
|
4
|
-
|
|
3
|
+
import type {
|
|
4
|
+
BarStyles,
|
|
5
|
+
ChartType,
|
|
6
|
+
CompleteChartTheme,
|
|
7
|
+
DataPointPercentage,
|
|
8
|
+
LegendShape,
|
|
9
|
+
SeriesData,
|
|
10
|
+
} from '../../types';
|
|
5
11
|
import type { GlyphProps, LineStyles } from '@visx/xychart';
|
|
6
12
|
|
|
7
13
|
export interface ChartRegistration {
|
|
@@ -20,6 +26,7 @@ export type GetElementStylesParams = {
|
|
|
20
26
|
export type ElementStyles = {
|
|
21
27
|
color: string;
|
|
22
28
|
lineStyles: LineStyles;
|
|
29
|
+
barStyles: BarStyles;
|
|
23
30
|
glyph: < Datum extends object >( props: GlyphProps< Datum > ) => ReactNode;
|
|
24
31
|
shapeStyles: CSSProperties & LineStyles;
|
|
25
32
|
};
|