@automattic/charts 0.1.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 +27 -0
- package/LICENSE.txt +357 -0
- package/README.md +32 -0
- package/SECURITY.md +47 -0
- package/index.ts +19 -0
- package/package.json +63 -0
- package/src/components/bar-chart/bar-chart.module.scss +7 -0
- package/src/components/bar-chart/bar-chart.tsx +155 -0
- package/src/components/bar-chart/index.tsx +1 -0
- package/src/components/legend/base-legend.tsx +71 -0
- package/src/components/legend/index.ts +2 -0
- package/src/components/legend/legend.module.scss +36 -0
- package/src/components/legend/types.ts +14 -0
- package/src/components/line-chart/index.tsx +1 -0
- package/src/components/line-chart/line-chart.module.scss +25 -0
- package/src/components/line-chart/line-chart.tsx +159 -0
- package/src/components/pie-chart/index.tsx +1 -0
- package/src/components/pie-chart/pie-chart.module.scss +3 -0
- package/src/components/pie-chart/pie-chart.tsx +135 -0
- package/src/components/pie-semi-circle-chart/index.tsx +1 -0
- package/src/components/pie-semi-circle-chart/pie-semi-circle-chart.module.scss +19 -0
- package/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx +187 -0
- package/src/components/shared/types.d.ts +109 -0
- package/src/components/tooltip/base-tooltip.module.scss +11 -0
- package/src/components/tooltip/base-tooltip.tsx +57 -0
- package/src/components/tooltip/index.ts +2 -0
- package/src/components/tooltip/types.ts +8 -0
- package/src/hooks/use-chart-mouse-handler.ts +90 -0
- package/src/index.ts +19 -0
- package/src/providers/theme/index.ts +2 -0
- package/src/providers/theme/theme-provider.tsx +36 -0
- package/src/providers/theme/themes.ts +51 -0
- package/tests/index.test.js +18 -0
- package/tests/jest.config.cjs +7 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { localPoint } from '@visx/event';
|
|
2
|
+
import { Group } from '@visx/group';
|
|
3
|
+
import Pie, { PieArcDatum } from '@visx/shape/lib/shapes/Pie';
|
|
4
|
+
import { Text } from '@visx/text';
|
|
5
|
+
import { useTooltip } from '@visx/tooltip';
|
|
6
|
+
import clsx from 'clsx';
|
|
7
|
+
import { FC, useCallback } from 'react';
|
|
8
|
+
import { useChartTheme } from '../../providers/theme/theme-provider';
|
|
9
|
+
import { Legend } from '../legend';
|
|
10
|
+
import { BaseTooltip } from '../tooltip';
|
|
11
|
+
import styles from './pie-semi-circle-chart.module.scss';
|
|
12
|
+
import type { BaseChartProps, DataPointPercentage } from '../shared/types';
|
|
13
|
+
|
|
14
|
+
interface PieSemiCircleChartProps extends BaseChartProps< DataPointPercentage[] > {
|
|
15
|
+
/**
|
|
16
|
+
* Label text to display above the chart
|
|
17
|
+
*/
|
|
18
|
+
label: string;
|
|
19
|
+
/**
|
|
20
|
+
* Note text to display below the label
|
|
21
|
+
*/
|
|
22
|
+
note: string;
|
|
23
|
+
/**
|
|
24
|
+
* Direction of chart rendering
|
|
25
|
+
* true for clockwise, false for counter-clockwise
|
|
26
|
+
*/
|
|
27
|
+
clockwise?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Thickness of the pie chart. A value between 0 and 1
|
|
30
|
+
*/
|
|
31
|
+
thickness?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type ArcData = PieArcDatum< DataPointPercentage >;
|
|
35
|
+
|
|
36
|
+
const PieSemiCircleChart: FC< PieSemiCircleChartProps > = ( {
|
|
37
|
+
data,
|
|
38
|
+
width,
|
|
39
|
+
label,
|
|
40
|
+
note,
|
|
41
|
+
className,
|
|
42
|
+
withTooltips = false,
|
|
43
|
+
clockwise = true,
|
|
44
|
+
thickness = 0.4,
|
|
45
|
+
showLegend,
|
|
46
|
+
legendOrientation,
|
|
47
|
+
} ) => {
|
|
48
|
+
const providerTheme = useChartTheme();
|
|
49
|
+
const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } =
|
|
50
|
+
useTooltip< DataPointPercentage >();
|
|
51
|
+
|
|
52
|
+
const centerX = width / 2;
|
|
53
|
+
const height = width / 2;
|
|
54
|
+
const radius = width / 2;
|
|
55
|
+
const pad = 0.03;
|
|
56
|
+
const innerRadius = radius * ( 1 - thickness + pad );
|
|
57
|
+
|
|
58
|
+
// Map the data to include index for color assignment
|
|
59
|
+
const dataWithIndex = data.map( ( d, index ) => ( {
|
|
60
|
+
...d,
|
|
61
|
+
index,
|
|
62
|
+
} ) );
|
|
63
|
+
|
|
64
|
+
// Set the clockwise direction based on the prop
|
|
65
|
+
const startAngle = clockwise ? -Math.PI / 2 : Math.PI / 2;
|
|
66
|
+
const endAngle = clockwise ? Math.PI / 2 : -Math.PI / 2;
|
|
67
|
+
|
|
68
|
+
const accessors = {
|
|
69
|
+
value: ( d: DataPointPercentage & { index: number } ) => d.value,
|
|
70
|
+
sort: (
|
|
71
|
+
a: DataPointPercentage & { index: number },
|
|
72
|
+
b: DataPointPercentage & { index: number }
|
|
73
|
+
) => b.value - a.value,
|
|
74
|
+
// Use the color property from the data object as a last resort. The theme provides colours by default.
|
|
75
|
+
fill: ( d: DataPointPercentage & { index: number } ) =>
|
|
76
|
+
d.color || providerTheme.colors[ d.index % providerTheme.colors.length ],
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const handleMouseMove = useCallback(
|
|
80
|
+
( event: React.MouseEvent, arc: ArcData ) => {
|
|
81
|
+
const coords = localPoint( event );
|
|
82
|
+
if ( ! coords ) return;
|
|
83
|
+
|
|
84
|
+
showTooltip( {
|
|
85
|
+
tooltipData: arc.data,
|
|
86
|
+
tooltipLeft: coords.x,
|
|
87
|
+
tooltipTop: coords.y - 10,
|
|
88
|
+
} );
|
|
89
|
+
},
|
|
90
|
+
[ showTooltip ]
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const handleMouseLeave = useCallback( () => {
|
|
94
|
+
hideTooltip();
|
|
95
|
+
}, [ hideTooltip ] );
|
|
96
|
+
|
|
97
|
+
const handleArcMouseMove = useCallback(
|
|
98
|
+
( arc: ArcData ) => ( event: React.MouseEvent ) => {
|
|
99
|
+
handleMouseMove( event, arc );
|
|
100
|
+
},
|
|
101
|
+
[ handleMouseMove ]
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Create legend items
|
|
105
|
+
const legendItems = data.map( ( item, index ) => ( {
|
|
106
|
+
label: item.label,
|
|
107
|
+
value: item.valueDisplay || item.value.toString(),
|
|
108
|
+
color: accessors.fill( { ...item, index } ),
|
|
109
|
+
} ) );
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<div
|
|
113
|
+
className={ clsx( 'pie-semi-circle-chart', styles[ 'pie-semi-circle-chart' ], className ) }
|
|
114
|
+
>
|
|
115
|
+
<svg width={ width } height={ height }>
|
|
116
|
+
{ /* Main chart group that contains both the pie and text elements */ }
|
|
117
|
+
<Group top={ centerX } left={ centerX }>
|
|
118
|
+
{ /* Pie chart */ }
|
|
119
|
+
<Pie< DataPointPercentage & { index: number } >
|
|
120
|
+
data={ dataWithIndex }
|
|
121
|
+
pieValue={ accessors.value }
|
|
122
|
+
outerRadius={ radius }
|
|
123
|
+
innerRadius={ innerRadius }
|
|
124
|
+
cornerRadius={ 3 }
|
|
125
|
+
padAngle={ pad }
|
|
126
|
+
startAngle={ startAngle }
|
|
127
|
+
endAngle={ endAngle }
|
|
128
|
+
pieSort={ accessors.sort }
|
|
129
|
+
>
|
|
130
|
+
{ pie => {
|
|
131
|
+
return pie.arcs.map( arc => (
|
|
132
|
+
<g
|
|
133
|
+
key={ arc.data.label }
|
|
134
|
+
onMouseMove={ handleArcMouseMove( arc ) }
|
|
135
|
+
onMouseLeave={ handleMouseLeave }
|
|
136
|
+
>
|
|
137
|
+
<path d={ pie.path( arc ) || '' } fill={ accessors.fill( arc.data ) } />
|
|
138
|
+
</g>
|
|
139
|
+
) );
|
|
140
|
+
} }
|
|
141
|
+
</Pie>
|
|
142
|
+
|
|
143
|
+
<Group>
|
|
144
|
+
<Text
|
|
145
|
+
textAnchor="middle"
|
|
146
|
+
verticalAnchor="start"
|
|
147
|
+
y={ -40 } // double font size to make room for a note
|
|
148
|
+
className={ styles.label }
|
|
149
|
+
>
|
|
150
|
+
{ label }
|
|
151
|
+
</Text>
|
|
152
|
+
<Text
|
|
153
|
+
textAnchor="middle"
|
|
154
|
+
verticalAnchor="start"
|
|
155
|
+
y={ -20 } // font size with padding
|
|
156
|
+
className={ styles.note }
|
|
157
|
+
>
|
|
158
|
+
{ note }
|
|
159
|
+
</Text>
|
|
160
|
+
</Group>
|
|
161
|
+
</Group>
|
|
162
|
+
</svg>
|
|
163
|
+
|
|
164
|
+
{ withTooltips && tooltipOpen && tooltipData && (
|
|
165
|
+
<BaseTooltip
|
|
166
|
+
data={ {
|
|
167
|
+
label: tooltipData.label,
|
|
168
|
+
value: tooltipData.value,
|
|
169
|
+
valueDisplay: tooltipData.valueDisplay,
|
|
170
|
+
} }
|
|
171
|
+
top={ tooltipTop }
|
|
172
|
+
left={ tooltipLeft }
|
|
173
|
+
/>
|
|
174
|
+
) }
|
|
175
|
+
|
|
176
|
+
{ showLegend && (
|
|
177
|
+
<Legend
|
|
178
|
+
items={ legendItems }
|
|
179
|
+
orientation={ legendOrientation }
|
|
180
|
+
className={ styles[ 'pie-semi-circle-chart-legend' ] }
|
|
181
|
+
/>
|
|
182
|
+
) }
|
|
183
|
+
</div>
|
|
184
|
+
);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
export default PieSemiCircleChart;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
|
|
3
|
+
export type DataPoint = {
|
|
4
|
+
label: string;
|
|
5
|
+
value: number;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type DataPointDate = {
|
|
9
|
+
date: Date;
|
|
10
|
+
label?: string;
|
|
11
|
+
value: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type SeriesData = {
|
|
15
|
+
group?: string;
|
|
16
|
+
label: string;
|
|
17
|
+
data: DataPointDate[] | DataPoint[];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type MultipleDataPointsDate = {
|
|
21
|
+
label: string;
|
|
22
|
+
data: DataPointDate[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type DataPointPercentage = {
|
|
26
|
+
/**
|
|
27
|
+
* Label for the data point
|
|
28
|
+
*/
|
|
29
|
+
label: string;
|
|
30
|
+
/**
|
|
31
|
+
* Numerical value
|
|
32
|
+
*/
|
|
33
|
+
value: number;
|
|
34
|
+
/**
|
|
35
|
+
* Formatted value for display
|
|
36
|
+
*/
|
|
37
|
+
valueDisplay?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Percentage value
|
|
40
|
+
*/
|
|
41
|
+
percentage: number;
|
|
42
|
+
/**
|
|
43
|
+
* Color code for the segment, by default colours are taken from the theme but this property can overrides it
|
|
44
|
+
*/
|
|
45
|
+
color?: string;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Theme configuration for chart components
|
|
50
|
+
*/
|
|
51
|
+
export type ChartTheme = {
|
|
52
|
+
/** Background color for chart components */
|
|
53
|
+
backgroundColor: string;
|
|
54
|
+
/** Background color for labels */
|
|
55
|
+
labelBackgroundColor?: string;
|
|
56
|
+
/** Array of colors used for data visualization */
|
|
57
|
+
colors: string[];
|
|
58
|
+
/** Optional CSS styles for grid lines */
|
|
59
|
+
gridStyles?: CSSProperties;
|
|
60
|
+
/** Length of axis ticks in pixels */
|
|
61
|
+
tickLength: number;
|
|
62
|
+
/** Color of the grid lines */
|
|
63
|
+
gridColor: string;
|
|
64
|
+
/** Color of the grid lines in dark mode */
|
|
65
|
+
gridColorDark: string;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Base properties shared across all chart components
|
|
70
|
+
*/
|
|
71
|
+
export type BaseChartProps< T = DataPoint | DataPointDate > = {
|
|
72
|
+
/**
|
|
73
|
+
* Array of data points to display in the chart
|
|
74
|
+
*/
|
|
75
|
+
data: T extends DataPoint | DataPointDate ? T[] : T;
|
|
76
|
+
/**
|
|
77
|
+
* Additional CSS class name for the chart container
|
|
78
|
+
*/
|
|
79
|
+
className?: string;
|
|
80
|
+
/**
|
|
81
|
+
* Width of the chart in pixels
|
|
82
|
+
*/
|
|
83
|
+
width: number;
|
|
84
|
+
/**
|
|
85
|
+
* Height of the chart in pixels
|
|
86
|
+
*/
|
|
87
|
+
height?: number;
|
|
88
|
+
/**
|
|
89
|
+
* Chart margins
|
|
90
|
+
*/
|
|
91
|
+
margin?: {
|
|
92
|
+
top: number;
|
|
93
|
+
right: number;
|
|
94
|
+
bottom: number;
|
|
95
|
+
left: number;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Whether to show tooltips on hover. False by default.
|
|
99
|
+
*/
|
|
100
|
+
withTooltips?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Whether to show legend
|
|
103
|
+
*/
|
|
104
|
+
showLegend?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Legend orientation
|
|
107
|
+
*/
|
|
108
|
+
legendOrientation?: 'horizontal' | 'vertical';
|
|
109
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
.tooltip {
|
|
2
|
+
padding: 0.5rem;
|
|
3
|
+
background-color: rgba(0, 0, 0, 0.85);
|
|
4
|
+
color: white;
|
|
5
|
+
border-radius: 4px;
|
|
6
|
+
font-size: 14px;
|
|
7
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
8
|
+
position: absolute;
|
|
9
|
+
pointer-events: none;
|
|
10
|
+
transform: translate(-50%, -100%);
|
|
11
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import styles from './base-tooltip.module.scss';
|
|
2
|
+
import type { CSSProperties, ComponentType, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type TooltipData = {
|
|
5
|
+
label: string;
|
|
6
|
+
value: number;
|
|
7
|
+
valueDisplay?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type TooltipComponentProps = {
|
|
11
|
+
data: TooltipData;
|
|
12
|
+
className?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type TooltipCommonProps = {
|
|
16
|
+
top: number;
|
|
17
|
+
left: number;
|
|
18
|
+
style?: CSSProperties;
|
|
19
|
+
className?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type DefaultDataTooltip = {
|
|
23
|
+
data: TooltipData;
|
|
24
|
+
component?: ComponentType< TooltipComponentProps >;
|
|
25
|
+
children?: never;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type CustomTooltip = {
|
|
29
|
+
children: ReactNode;
|
|
30
|
+
data?: never;
|
|
31
|
+
component?: never;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type BaseTooltipProps = TooltipCommonProps & ( DefaultDataTooltip | CustomTooltip );
|
|
35
|
+
|
|
36
|
+
const DefaultTooltipContent = ( { data }: TooltipComponentProps ) => (
|
|
37
|
+
<>
|
|
38
|
+
{ data?.label }: { data?.valueDisplay || data?.value }
|
|
39
|
+
</>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
export const BaseTooltip = ( {
|
|
43
|
+
data,
|
|
44
|
+
top,
|
|
45
|
+
left,
|
|
46
|
+
component: Component = DefaultTooltipContent,
|
|
47
|
+
children,
|
|
48
|
+
className,
|
|
49
|
+
}: BaseTooltipProps ) => {
|
|
50
|
+
return (
|
|
51
|
+
<div className={ styles.tooltip } style={ { top, left } } role="tooltip">
|
|
52
|
+
{ children || <Component data={ data } className={ className } /> }
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type { BaseTooltipProps, TooltipData };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { localPoint } from '@visx/event';
|
|
2
|
+
import { useTooltip } from '@visx/tooltip';
|
|
3
|
+
import { useCallback, type MouseEvent } from 'react';
|
|
4
|
+
import type { DataPoint } from '../components/shared/types';
|
|
5
|
+
|
|
6
|
+
type UseChartMouseHandlerProps = {
|
|
7
|
+
/**
|
|
8
|
+
* Whether tooltips are enabled
|
|
9
|
+
*/
|
|
10
|
+
withTooltips: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type UseChartMouseHandlerReturn = {
|
|
14
|
+
/**
|
|
15
|
+
* Handler for mouse move events
|
|
16
|
+
*/
|
|
17
|
+
onMouseMove: ( event: React.MouseEvent< SVGElement >, data: DataPoint ) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Handler for mouse leave events
|
|
20
|
+
*/
|
|
21
|
+
onMouseLeave: () => void;
|
|
22
|
+
/**
|
|
23
|
+
* Whether the tooltip is currently open
|
|
24
|
+
*/
|
|
25
|
+
tooltipOpen: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* The current tooltip data
|
|
28
|
+
*/
|
|
29
|
+
tooltipData: DataPoint | null;
|
|
30
|
+
/**
|
|
31
|
+
* The current tooltip left position
|
|
32
|
+
*/
|
|
33
|
+
tooltipLeft: number | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* The current tooltip top position
|
|
36
|
+
*/
|
|
37
|
+
tooltipTop: number | undefined;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Hook to handle mouse interactions for chart components
|
|
42
|
+
*
|
|
43
|
+
* @param {UseChartMouseHandlerProps} props - Hook configuration
|
|
44
|
+
* @return {UseChartMouseHandlerReturn} Object containing handlers and tooltip state
|
|
45
|
+
*/
|
|
46
|
+
const useChartMouseHandler = ( {
|
|
47
|
+
withTooltips,
|
|
48
|
+
}: UseChartMouseHandlerProps ): UseChartMouseHandlerReturn => {
|
|
49
|
+
const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } =
|
|
50
|
+
useTooltip< DataPoint >();
|
|
51
|
+
|
|
52
|
+
// TODO: either debounce/throttle or use useTooltipInPortal with built-in debounce
|
|
53
|
+
const onMouseMove = useCallback(
|
|
54
|
+
( event: MouseEvent< SVGElement >, data: DataPoint ) => {
|
|
55
|
+
if ( ! withTooltips ) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const coords = localPoint( event );
|
|
60
|
+
if ( ! coords ) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
showTooltip( {
|
|
65
|
+
tooltipData: data,
|
|
66
|
+
tooltipLeft: coords.x,
|
|
67
|
+
tooltipTop: coords.y - 10,
|
|
68
|
+
} );
|
|
69
|
+
},
|
|
70
|
+
[ withTooltips, showTooltip ]
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const onMouseLeave = useCallback( () => {
|
|
74
|
+
if ( ! withTooltips ) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
hideTooltip();
|
|
78
|
+
}, [ withTooltips, hideTooltip ] );
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
onMouseMove,
|
|
82
|
+
onMouseLeave,
|
|
83
|
+
tooltipOpen,
|
|
84
|
+
tooltipData,
|
|
85
|
+
tooltipLeft,
|
|
86
|
+
tooltipTop,
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export default useChartMouseHandler;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Charts
|
|
2
|
+
export { BarChart } from './components/bar-chart';
|
|
3
|
+
export { LineChart } from './components/line-chart';
|
|
4
|
+
export { PieChart } from './components/pie-chart';
|
|
5
|
+
export { PieSemiCircleChart } from './components/pie-semi-circle-chart';
|
|
6
|
+
|
|
7
|
+
// Chart components
|
|
8
|
+
export { BaseTooltip } from './components/tooltip';
|
|
9
|
+
export { Legend } from './components/legend';
|
|
10
|
+
|
|
11
|
+
// Providers
|
|
12
|
+
export { ThemeProvider } from './providers/theme';
|
|
13
|
+
|
|
14
|
+
// Hooks
|
|
15
|
+
export { default as useChartMouseHandler } from './hooks/use-chart-mouse-handler';
|
|
16
|
+
|
|
17
|
+
// Types
|
|
18
|
+
export type * from './components/shared/types';
|
|
19
|
+
export type { BaseTooltipProps } from './components/tooltip';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createContext, useContext, FC, ReactNode } from 'react';
|
|
2
|
+
import { defaultTheme } from './themes';
|
|
3
|
+
import type { ChartTheme } from '../../components/shared/types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Context for sharing theme configuration across components
|
|
7
|
+
*/
|
|
8
|
+
const ThemeContext = createContext< ChartTheme >( defaultTheme );
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Hook to access chart theme
|
|
12
|
+
* @return {object} A built theme configuration compatible with visx charts
|
|
13
|
+
*/
|
|
14
|
+
const useChartTheme = () => {
|
|
15
|
+
const theme = useContext( ThemeContext );
|
|
16
|
+
return theme;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Props for the ThemeProvider component
|
|
21
|
+
*/
|
|
22
|
+
type ThemeProviderProps = {
|
|
23
|
+
/** Optional partial theme override */
|
|
24
|
+
theme?: Partial< ChartTheme >;
|
|
25
|
+
/** Child components that will have access to the theme */
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Provider component for chart theming
|
|
30
|
+
// Allows theme customization through props while maintaining default values
|
|
31
|
+
const ThemeProvider: FC< ThemeProviderProps > = ( { theme = {}, children } ) => {
|
|
32
|
+
const mergedTheme = { ...defaultTheme, ...theme };
|
|
33
|
+
return <ThemeContext.Provider value={ mergedTheme }>{ children }</ThemeContext.Provider>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export { ThemeProvider, useChartTheme };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ChartTheme } from '../../components/shared/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default theme configuration
|
|
5
|
+
*/
|
|
6
|
+
const defaultTheme: ChartTheme = {
|
|
7
|
+
backgroundColor: '#FFFFFF', // chart background color
|
|
8
|
+
labelBackgroundColor: '#FFFFFF', // label background color
|
|
9
|
+
colors: [ '#98C8DF', '#006DAB', '#A6DC80', '#1F9828', '#FF8C8F' ],
|
|
10
|
+
gridStyles: {
|
|
11
|
+
stroke: '#787C82',
|
|
12
|
+
strokeWidth: 1,
|
|
13
|
+
},
|
|
14
|
+
tickLength: 0,
|
|
15
|
+
gridColor: '',
|
|
16
|
+
gridColorDark: '',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Jetpack theme configuration
|
|
21
|
+
*/
|
|
22
|
+
const jetpackTheme: ChartTheme = {
|
|
23
|
+
backgroundColor: '#FFFFFF', // chart background color
|
|
24
|
+
labelBackgroundColor: '#FFFFFF', // label background color
|
|
25
|
+
colors: [ '#98C8DF', '#006DAB', '#A6DC80', '#1F9828', '#FF8C8F' ],
|
|
26
|
+
gridStyles: {
|
|
27
|
+
stroke: '#787C82',
|
|
28
|
+
strokeWidth: 1,
|
|
29
|
+
},
|
|
30
|
+
tickLength: 0,
|
|
31
|
+
gridColor: '',
|
|
32
|
+
gridColorDark: '',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Woo theme configuration
|
|
37
|
+
*/
|
|
38
|
+
const wooTheme: ChartTheme = {
|
|
39
|
+
backgroundColor: '#FFFFFF', // chart background color
|
|
40
|
+
labelBackgroundColor: '#FFFFFF', // label background color
|
|
41
|
+
colors: [ '#80C8FF', '#B999FF', '#3858E9' ],
|
|
42
|
+
gridStyles: {
|
|
43
|
+
stroke: '#787C82',
|
|
44
|
+
strokeWidth: 1,
|
|
45
|
+
},
|
|
46
|
+
tickLength: 0,
|
|
47
|
+
gridColor: '',
|
|
48
|
+
gridColorDark: '',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export { defaultTheme, jetpackTheme, wooTheme };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// We recommend using `jest` for testing. If you're testing React code, we recommend `@testing-library/react` and related packages.
|
|
2
|
+
// Please match the versions used elsewhere in the monorepo.
|
|
3
|
+
//
|
|
4
|
+
// Please don't add new uses of `mocha`, `chai`, `sinon`, `enzyme`, and so on. We're trying to standardize on one testing framework.
|
|
5
|
+
//
|
|
6
|
+
// The default setup is to have files named like "name.test.js" (or .jsx, .ts, or .tsx) in this `tests/` directory.
|
|
7
|
+
// But you could instead put them in `src/`, or put files like "name.js" (or .jsx, .ts, or .tsx) in `test` or `__tests__` directories somewhere.
|
|
8
|
+
|
|
9
|
+
// This is a placeholder test, new tests will be added soon
|
|
10
|
+
const placeholderFunction = () => {
|
|
11
|
+
return true;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
describe( 'placeholderTest', () => {
|
|
15
|
+
it( 'should be a function', () => {
|
|
16
|
+
expect( typeof placeholderFunction ).toBe( 'function' );
|
|
17
|
+
} );
|
|
18
|
+
} );
|