@cdc/chart 4.22.10 → 4.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/495.js +3 -0
- package/dist/703.js +1 -0
- package/dist/cdcchart.js +723 -6
- package/examples/age-adjusted-rates.json +1486 -1218
- package/examples/box-plot-data.json +71 -0
- package/examples/box-plot.csv +5 -0
- package/examples/{private/yaxis-test.json → box-plot.json} +46 -54
- package/examples/case-rate-example-config.json +1 -1
- package/examples/covid-confidence-example-config.json +33 -33
- package/examples/covid-example-config.json +34 -34
- package/examples/covid-example-data-confidence.json +30 -30
- package/examples/covid-example-data.json +20 -20
- package/examples/cutoff-example-config.json +36 -36
- package/examples/cutoff-example-data.json +36 -36
- package/examples/date-exclusions-config.json +1 -1
- package/examples/dynamic-legends.json +124 -124
- package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart-with-numbers-on-bar.json +191 -197
- package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart.json +230 -240
- package/examples/gallery/bar-chart-horizontal/horizontal-stacked.json +239 -247
- package/examples/gallery/bar-chart-vertical/combo-line-chart.json +138 -136
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +79 -79
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +80 -80
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-with-confidence.json +67 -67
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart.json +179 -110
- package/examples/gallery/lollipop/lollipop-style-horizontal.json +215 -219
- package/examples/gallery/paired-bar/paired-bar-chart.json +195 -195
- package/examples/horizontal-chart.json +35 -35
- package/examples/horizontal-stacked-bar-chart.json +34 -34
- package/examples/line-chart.json +75 -75
- package/examples/new-data.csv +17 -0
- package/examples/newdata.json +90 -0
- package/examples/paired-bar-data.json +16 -14
- package/examples/paired-bar-example.json +48 -48
- package/examples/paired-bar-formatted.json +36 -36
- package/examples/planet-chart-horizontal-example-config.json +33 -33
- package/examples/planet-combo-example-config.json +34 -31
- package/examples/planet-example-config.json +35 -33
- package/examples/planet-example-data.json +56 -56
- package/examples/planet-pie-example-config.json +28 -28
- package/examples/stacked-vertical-bar-example.json +1 -1
- package/examples/temp-example-config.json +61 -54
- package/examples/temp-example-data.json +1 -1
- package/package.json +3 -2
- package/src/CdcChart.tsx +449 -434
- package/src/components/BarChart.tsx +383 -497
- package/src/components/BoxPlot.js +92 -0
- package/src/components/DataTable.tsx +182 -197
- package/src/components/EditorPanel.js +1068 -722
- package/src/components/Filters.js +131 -0
- package/src/components/Legend.js +286 -329
- package/src/components/LineChart.tsx +143 -81
- package/src/components/LinearChart.tsx +432 -451
- package/src/components/PairedBarChart.tsx +197 -213
- package/src/components/PieChart.tsx +105 -151
- package/src/components/SparkLine.js +179 -201
- package/src/components/useIntersectionObserver.tsx +19 -20
- package/src/context.tsx +3 -3
- package/src/data/initial-state.js +44 -17
- package/src/hooks/useActiveElement.js +13 -13
- package/src/hooks/useChartClasses.js +34 -28
- package/src/hooks/useColorPalette.ts +56 -63
- package/src/hooks/useLegendClasses.js +18 -10
- package/src/hooks/useReduceData.ts +64 -77
- package/src/hooks/useRightAxis.js +25 -0
- package/src/hooks/useTopAxis.js +6 -0
- package/src/index.html +19 -19
- package/src/index.tsx +13 -16
- package/src/scss/DataTable.scss +6 -5
- package/src/scss/editor-panel.scss +71 -69
- package/src/scss/main.scss +188 -114
- package/src/scss/variables.scss +1 -1
- package/examples/private/line-test-data.json +0 -22
- package/examples/private/line-test-two.json +0 -216
- package/examples/private/line-test.json +0 -102
- package/examples/private/newtest.csv +0 -101
- package/examples/private/shawn.json +0 -1296
- package/examples/private/test.json +0 -10124
- package/examples/private/yaxis-testing.csv +0 -27
- package/examples/private/yaxis.json +0 -28
|
@@ -1,55 +1,61 @@
|
|
|
1
|
-
import React, { useContext, useState, useEffect, useRef } from 'react'
|
|
2
|
-
import { animated, useTransition, interpolate } from 'react-spring'
|
|
3
|
-
import ReactTooltip from 'react-tooltip'
|
|
1
|
+
import React, { useContext, useState, useEffect, useRef } from 'react'
|
|
2
|
+
import { animated, useTransition, interpolate } from 'react-spring'
|
|
3
|
+
import ReactTooltip from 'react-tooltip'
|
|
4
4
|
|
|
5
|
-
import Pie, { ProvidedProps, PieArcDatum } from '@visx/shape/lib/shapes/Pie'
|
|
6
|
-
import chroma from
|
|
7
|
-
import { Group } from '@visx/group'
|
|
8
|
-
import { Text } from '@visx/text'
|
|
9
|
-
import useIntersectionObserver from
|
|
5
|
+
import Pie, { ProvidedProps, PieArcDatum } from '@visx/shape/lib/shapes/Pie'
|
|
6
|
+
import chroma from 'chroma-js'
|
|
7
|
+
import { Group } from '@visx/group'
|
|
8
|
+
import { Text } from '@visx/text'
|
|
9
|
+
import useIntersectionObserver from './useIntersectionObserver'
|
|
10
10
|
|
|
11
|
-
import Context from '../context'
|
|
11
|
+
import Context from '../context'
|
|
12
12
|
|
|
13
|
-
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
13
|
+
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
14
14
|
|
|
15
15
|
// react-spring transition definitions
|
|
16
|
-
type PieStyles = { startAngle: number; endAngle: number }
|
|
16
|
+
type PieStyles = { startAngle: number; endAngle: number }
|
|
17
17
|
|
|
18
18
|
const enterUpdateTransition = ({ startAngle, endAngle }: PieArcDatum<any>) => ({
|
|
19
19
|
startAngle,
|
|
20
|
-
endAngle
|
|
21
|
-
})
|
|
20
|
+
endAngle
|
|
21
|
+
})
|
|
22
22
|
|
|
23
23
|
export default function PieChart() {
|
|
24
|
-
const { transformedData: data, config, dimensions, seriesHighlight, colorScale, formatNumber, currentViewport, handleChartAriaLabels } = useContext<any>(Context)
|
|
24
|
+
const { transformedData: data, config, dimensions, seriesHighlight, colorScale, formatNumber, currentViewport, handleChartAriaLabels } = useContext<any>(Context)
|
|
25
25
|
|
|
26
|
-
const [filteredData, setFilteredData] = useState<any>(undefined)
|
|
27
|
-
const [animatedPie, setAnimatePie] = useState<boolean>(false)
|
|
26
|
+
const [filteredData, setFilteredData] = useState<any>(undefined)
|
|
27
|
+
const [animatedPie, setAnimatePie] = useState<boolean>(false)
|
|
28
28
|
|
|
29
|
-
const triggerRef = useRef()
|
|
29
|
+
const triggerRef = useRef()
|
|
30
30
|
const dataRef = useIntersectionObserver(triggerRef, {
|
|
31
31
|
freezeOnceVisible: false
|
|
32
|
-
})
|
|
32
|
+
})
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
// Make sure the chart is visible if in the editor
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const element = document.querySelector('.isEditor')
|
|
37
|
+
if (element) {
|
|
38
|
+
// parent element is visible
|
|
39
|
+
console.log('setAnimation')
|
|
40
|
+
setAnimatePie(prevState => true)
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
35
45
|
if (dataRef?.isIntersecting && config.animate && !animatedPie) {
|
|
36
46
|
setTimeout(() => {
|
|
37
|
-
setAnimatePie(true)
|
|
38
|
-
}, 500)
|
|
47
|
+
setAnimatePie(true)
|
|
48
|
+
}, 500)
|
|
39
49
|
}
|
|
40
50
|
}, [dataRef?.isIntersecting, config.animate])
|
|
41
51
|
|
|
42
52
|
type AnimatedPieProps<Datum> = ProvidedProps<Datum> & {
|
|
43
|
-
animate?: boolean
|
|
44
|
-
getKey: (d: PieArcDatum<Datum>) => string
|
|
45
|
-
delay?: number
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function AnimatedPie<Datum>({
|
|
49
|
-
arcs,
|
|
50
|
-
path,
|
|
51
|
-
getKey,
|
|
52
|
-
}: AnimatedPieProps<Datum>) {
|
|
53
|
+
animate?: boolean
|
|
54
|
+
getKey: (d: PieArcDatum<Datum>) => string
|
|
55
|
+
delay?: number
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function AnimatedPie<Datum>({ arcs, path, getKey }: AnimatedPieProps<Datum>) {
|
|
53
59
|
const transitions = useTransition<PieArcDatum<Datum>, PieStyles>(
|
|
54
60
|
arcs,
|
|
55
61
|
getKey,
|
|
@@ -58,159 +64,107 @@ export default function PieChart() {
|
|
|
58
64
|
from: enterUpdateTransition,
|
|
59
65
|
enter: enterUpdateTransition,
|
|
60
66
|
update: enterUpdateTransition,
|
|
61
|
-
leave: enterUpdateTransition
|
|
62
|
-
}
|
|
63
|
-
)
|
|
67
|
+
leave: enterUpdateTransition
|
|
68
|
+
}
|
|
69
|
+
)
|
|
64
70
|
|
|
65
71
|
return (
|
|
66
72
|
<>
|
|
67
|
-
{transitions.map(
|
|
68
|
-
(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}: {
|
|
73
|
-
item: PieArcDatum<Datum>;
|
|
74
|
-
props: PieStyles;
|
|
75
|
-
key: string;
|
|
76
|
-
}) => {
|
|
77
|
-
let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${formatNumber(arc.data[config.runtime.yAxis.dataKey])}` : formatNumber(arc.data[config.runtime.yAxis.dataKey])
|
|
78
|
-
let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${arc.data[config.runtime.xAxis.dataKey]}` : arc.data[config.runtime.xAxis.dataKey]
|
|
79
|
-
|
|
80
|
-
const tooltip = `<div>
|
|
73
|
+
{transitions.map(({ item: arc, props, key }: { item: PieArcDatum<Datum>; props: PieStyles; key: string }) => {
|
|
74
|
+
let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${formatNumber(arc.data[config.runtime.yAxis.dataKey])}` : formatNumber(arc.data[config.runtime.yAxis.dataKey])
|
|
75
|
+
let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${arc.data[config.runtime.xAxis.dataKey]}` : arc.data[config.runtime.xAxis.dataKey]
|
|
76
|
+
|
|
77
|
+
const tooltip = `<div>
|
|
81
78
|
${yAxisTooltip}<br />
|
|
82
79
|
${xAxisTooltip}<br />
|
|
83
|
-
Percent: ${Math.round(
|
|
84
|
-
(((arc.endAngle - arc.startAngle) * 180) /
|
|
85
|
-
Math.PI /
|
|
86
|
-
360) *
|
|
87
|
-
100
|
|
88
|
-
) + "%"}
|
|
80
|
+
Percent: ${Math.round((((arc.endAngle - arc.startAngle) * 180) / Math.PI / 360) * 100) + '%'}
|
|
89
81
|
`
|
|
90
82
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
return (
|
|
84
|
+
<Group key={key} style={{ opacity: config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(arc.data[config.runtime.xAxis.dataKey]) === -1 ? 0.5 : 1 }}>
|
|
85
|
+
<animated.path
|
|
86
|
+
// compute interpolated path d attribute from intermediate angle values
|
|
87
|
+
d={interpolate([props.startAngle, props.endAngle], (startAngle, endAngle) =>
|
|
88
|
+
path({
|
|
96
89
|
...arc,
|
|
97
90
|
startAngle,
|
|
98
|
-
endAngle
|
|
99
|
-
})
|
|
100
|
-
fill={colorScale(arc.data[config.runtime.xAxis.dataKey])}
|
|
101
|
-
data-tip={tooltip}
|
|
102
|
-
data-for={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
103
|
-
/>
|
|
104
|
-
</Group>
|
|
105
|
-
);
|
|
106
|
-
},
|
|
107
|
-
)}
|
|
108
|
-
{transitions.map(
|
|
109
|
-
({
|
|
110
|
-
item: arc,
|
|
111
|
-
key,
|
|
112
|
-
}: {
|
|
113
|
-
item: PieArcDatum<Datum>;
|
|
114
|
-
props: PieStyles;
|
|
115
|
-
key: string;
|
|
116
|
-
}) => {
|
|
117
|
-
|
|
118
|
-
const [centroidX, centroidY] = path.centroid(arc);
|
|
119
|
-
const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.1;
|
|
120
|
-
|
|
121
|
-
let textColor = "#FFF";
|
|
122
|
-
if (colorScale(arc.data[config.runtime.xAxis.dataKey]) && chroma.contrast(textColor, colorScale(arc.data[config.runtime.xAxis.dataKey])) < 3.5) {
|
|
123
|
-
textColor = "000";
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return (
|
|
127
|
-
<animated.g key={key}>
|
|
128
|
-
{hasSpaceForLabel && (
|
|
129
|
-
<Text
|
|
130
|
-
style={{ fill: textColor }}
|
|
131
|
-
x={centroidX}
|
|
132
|
-
y={centroidY}
|
|
133
|
-
dy=".33em"
|
|
134
|
-
textAnchor="middle"
|
|
135
|
-
pointerEvents="none"
|
|
136
|
-
>
|
|
137
|
-
{Math.round(
|
|
138
|
-
(((arc.endAngle - arc.startAngle) * 180) /
|
|
139
|
-
Math.PI /
|
|
140
|
-
360) *
|
|
141
|
-
100
|
|
142
|
-
) + "%"}
|
|
143
|
-
</Text>
|
|
91
|
+
endAngle
|
|
92
|
+
})
|
|
144
93
|
)}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
94
|
+
fill={colorScale(arc.data[config.runtime.xAxis.dataKey])}
|
|
95
|
+
data-tip={tooltip}
|
|
96
|
+
data-for={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
97
|
+
/>
|
|
98
|
+
</Group>
|
|
99
|
+
)
|
|
100
|
+
})}
|
|
101
|
+
{transitions.map(({ item: arc, key }: { item: PieArcDatum<Datum>; props: PieStyles; key: string }) => {
|
|
102
|
+
const [centroidX, centroidY] = path.centroid(arc)
|
|
103
|
+
const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.1
|
|
104
|
+
|
|
105
|
+
let textColor = '#FFF'
|
|
106
|
+
if (colorScale(arc.data[config.runtime.xAxis.dataKey]) && chroma.contrast(textColor, colorScale(arc.data[config.runtime.xAxis.dataKey])) < 3.5) {
|
|
107
|
+
textColor = '000'
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<animated.g key={key}>
|
|
112
|
+
{hasSpaceForLabel && (
|
|
113
|
+
<Text style={{ fill: textColor }} x={centroidX} y={centroidY} dy='.33em' textAnchor='middle' pointerEvents='none'>
|
|
114
|
+
{Math.round((((arc.endAngle - arc.startAngle) * 180) / Math.PI / 360) * 100) + '%'}
|
|
115
|
+
</Text>
|
|
116
|
+
)}
|
|
117
|
+
</animated.g>
|
|
118
|
+
)
|
|
119
|
+
})}
|
|
149
120
|
</>
|
|
150
|
-
)
|
|
121
|
+
)
|
|
151
122
|
}
|
|
152
123
|
|
|
153
|
-
let [
|
|
124
|
+
let [width] = dimensions
|
|
154
125
|
|
|
155
|
-
if(config && config.legend && !config.legend.hide && currentViewport === 'lg') {
|
|
156
|
-
width = width * 0.73
|
|
126
|
+
if (config && config.legend && !config.legend.hide && currentViewport === 'lg') {
|
|
127
|
+
width = width * 0.73
|
|
157
128
|
}
|
|
158
129
|
|
|
159
|
-
const height = config.
|
|
130
|
+
const height = config.heights.vertical
|
|
160
131
|
|
|
161
|
-
const radius = Math.min(width, height) / 2
|
|
162
|
-
const centerY = height / 2
|
|
163
|
-
const centerX = width / 2
|
|
164
|
-
const donutThickness =
|
|
132
|
+
const radius = Math.min(width, height) / 2
|
|
133
|
+
const centerY = height / 2
|
|
134
|
+
const centerX = width / 2
|
|
135
|
+
const donutThickness = config.pieType === 'Donut' ? 75 : radius
|
|
165
136
|
|
|
166
137
|
useEffect(() => {
|
|
167
|
-
if(seriesHighlight.length > 0 && config.legend.behavior !==
|
|
168
|
-
let newFilteredData = []
|
|
138
|
+
if (seriesHighlight.length > 0 && config.legend.behavior !== 'highlight') {
|
|
139
|
+
let newFilteredData = []
|
|
169
140
|
|
|
170
|
-
data.forEach(
|
|
171
|
-
if(seriesHighlight.indexOf(d[config.runtime.xAxis.dataKey]) !== -1) {
|
|
172
|
-
newFilteredData.push(d)
|
|
141
|
+
data.forEach(d => {
|
|
142
|
+
if (seriesHighlight.indexOf(d[config.runtime.xAxis.dataKey]) !== -1) {
|
|
143
|
+
newFilteredData.push(d)
|
|
173
144
|
}
|
|
174
|
-
})
|
|
145
|
+
})
|
|
175
146
|
|
|
176
|
-
setFilteredData(newFilteredData)
|
|
147
|
+
setFilteredData(newFilteredData)
|
|
177
148
|
} else {
|
|
178
|
-
setFilteredData(undefined)
|
|
149
|
+
setFilteredData(undefined)
|
|
179
150
|
}
|
|
180
|
-
}, [seriesHighlight])
|
|
151
|
+
}, [seriesHighlight])
|
|
181
152
|
|
|
182
153
|
useEffect(() => {
|
|
183
|
-
ReactTooltip.rebuild()
|
|
184
|
-
})
|
|
154
|
+
ReactTooltip.rebuild()
|
|
155
|
+
})
|
|
185
156
|
|
|
186
157
|
return (
|
|
187
|
-
<ErrorBoundary component=
|
|
188
|
-
<svg
|
|
189
|
-
width={width}
|
|
190
|
-
height={height}
|
|
191
|
-
className={`group ${animatedPie ? 'animated' : ''}`}
|
|
192
|
-
role="img"
|
|
193
|
-
aria-label={handleChartAriaLabels(config)}
|
|
194
|
-
>
|
|
158
|
+
<ErrorBoundary component='PieChart'>
|
|
159
|
+
<svg width={width} height={height} className={`animated-pie group ${config.animate === false || animatedPie ? 'animated' : ''}`} role='img' aria-label={handleChartAriaLabels(config)}>
|
|
195
160
|
<Group top={centerY} left={centerX}>
|
|
196
|
-
<Pie
|
|
197
|
-
|
|
198
|
-
pieValue={d => d[config.runtime.yAxis.dataKey]}
|
|
199
|
-
pieSortValues={() => -1}
|
|
200
|
-
innerRadius={radius - donutThickness}
|
|
201
|
-
outerRadius={radius}
|
|
202
|
-
>
|
|
203
|
-
{pie => (
|
|
204
|
-
<AnimatedPie<any>
|
|
205
|
-
{...pie}
|
|
206
|
-
getKey={d => d.data[config.runtime.xAxis.dataKey]}
|
|
207
|
-
/>
|
|
208
|
-
)}
|
|
161
|
+
<Pie data={filteredData || data} pieValue={d => d[config.runtime.yAxis.dataKey]} pieSortValues={() => -1} innerRadius={radius - donutThickness} outerRadius={radius}>
|
|
162
|
+
{pie => <AnimatedPie<any> {...pie} getKey={d => d.data[config.runtime.xAxis.dataKey]} />}
|
|
209
163
|
</Pie>
|
|
210
164
|
</Group>
|
|
211
165
|
</svg>
|
|
212
166
|
<div ref={triggerRef} />
|
|
213
|
-
<ReactTooltip id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`} html={true} type=
|
|
167
|
+
<ReactTooltip id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`} html={true} type='light' arrowColor='rgba(0,0,0,0)' className='tooltip' />
|
|
214
168
|
</ErrorBoundary>
|
|
215
169
|
)
|
|
216
170
|
}
|