@cdc/chart 4.23.3 → 4.23.4
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/dist/cdcchart.js +24397 -24193
- package/examples/feature/__data__/area-chart.json +56 -0
- package/examples/{planet-example-data.json → feature/__data__/planet-example-data.json} +16 -4
- package/examples/{area-chart.json → feature/area/area-chart.json} +70 -13
- package/examples/{horizontal-chart-max-increase.json → feature/bar/horizontal-chart-max-increase.json} +10 -4
- package/examples/{horizontal-chart.json → feature/bar/horizontal-chart.json} +10 -4
- package/examples/{horizontal-stacked-bar-chart.json → feature/bar/horizontal-stacked-bar-chart.json} +7 -3
- package/examples/{planet-chart-horizontal-example-config.json → feature/bar/planet-chart-horizontal-example-config.json} +8 -3
- package/examples/{planet-example-config.json → feature/bar/planet-example-config.json} +2 -2
- package/examples/{box-plot.json → feature/boxplot/boxplot.json} +7 -7
- package/examples/feature/boxplot/testing.csv +38 -0
- package/examples/feature/combo/combochart-categories_are_numbers .json +18 -0
- package/examples/{planet-combo-example-config.json → feature/combo/planet-combo-example-config.json} +1 -1
- package/examples/{planet-deviation-config.json → feature/deviation/planet-deviation-config.json} +2 -2
- package/examples/{planet-deviation-data.json → feature/deviation/planet-deviation-data.json} +9 -9
- package/examples/feature/filters/filter-testing.json +178 -0
- package/examples/feature/forecasting/case_date_example.csv +130 -0
- package/examples/feature/forecasting/effective_reproduction.json +202 -0
- package/examples/feature/forecasting/r_data.csv +130 -0
- package/examples/feature/line/line-chart.json +124 -0
- package/examples/{paired-bar-example.json → feature/paired-bar/paired-bar-example.json} +10 -4
- package/examples/{planet-pie-example-config.json → feature/pie/planet-pie-example-config.json} +2 -2
- package/examples/{scatterplot.json → feature/scatterplot/scatterplot.json} +1 -1
- package/examples/{case-rate-example-config.json → feature/tests-case-rate/case-rate-example-config.json} +2 -2
- package/examples/{covid-confidence-example-config.json → feature/tests-covid/covid-confidence-example-config.json} +8 -3
- package/examples/{covid-example-config.json → feature/tests-covid/covid-example-config.json} +7 -3
- package/examples/{cutoff-example-config.json → feature/tests-cutoff/cutoff-example-config.json} +7 -3
- package/examples/{date-exclusions-config.json → feature/tests-date-exclusions/date-exclusions-config.json} +2 -2
- package/examples/{example-bar-chart-nonnumeric.json → feature/tests-non-numerics/example-bar-chart-nonnumeric.json} +1 -1
- package/examples/{planet-pie-example-config-nonnumeric.json → feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json} +2 -2
- package/examples/{sparkline-chart-nonnumeric.json → feature/tests-non-numerics/sparkline-chart-nonnumeric.json} +1 -1
- package/examples/gallery/bar-chart-vertical/combo-line-chart.json +145 -7
- package/examples/gallery/paired-bar/paired-bar-chart.json +1 -0
- package/index.html +73 -49
- package/package.json +2 -2
- package/src/CdcChart.jsx +111 -26
- package/src/components/AreaChart.jsx +105 -70
- package/src/components/BarChart.jsx +45 -28
- package/src/components/BoxPlot.jsx +28 -20
- package/src/components/DataTable.jsx +7 -6
- package/src/components/DeviationBar.jsx +2 -2
- package/src/components/EditorPanel.jsx +252 -193
- package/src/components/Legend.jsx +1 -1
- package/src/components/LineChart.jsx +10 -16
- package/src/components/LinearChart.jsx +30 -34
- package/src/components/PairedBarChart.jsx +6 -6
- package/src/components/PieChart.jsx +2 -4
- package/src/components/SparkLine.jsx +6 -42
- package/src/data/initial-state.js +7 -3
- package/src/index.jsx +2 -1
- package/src/scss/editor-panel.scss +15 -0
- package/src/scss/main.scss +8 -6
- package/examples/box-plot.csv +0 -5
- package/examples/dynamic-legends.json +0 -125
- package/examples/line-chart.json +0 -34
- package/examples/temp-example-config.json +0 -64
- package/examples/temp-example-data.json +0 -130
- package/src/components/Filters.jsx +0 -126
- /package/examples/{age-adjusted-rates.json → feature/__data__/age-adjusted-rates.json} +0 -0
- /package/examples/{new-data.csv → feature/__data__/new-data.csv} +0 -0
- /package/examples/{planet-example-data-max-increase.json → feature/__data__/planet-example-data-max-increase.json} +0 -0
- /package/examples/{Barchart_with_negative.json → feature/bar/Barchart_with_negative.json} +0 -0
- /package/examples/{example-bar-chart.json → feature/bar/example-bar-chart.json} +0 -0
- /package/examples/{stacked-vertical-bar-example-negative.json → feature/bar/stacked-vertical-bar-example-negative.json} +0 -0
- /package/examples/{stacked-vertical-bar-example.json → feature/bar/stacked-vertical-bar-example.json} +0 -0
- /package/examples/{box-plot-data.json → feature/boxplot/box-plot-data.json} +0 -0
- /package/examples/{newdata.json → feature/boxplot/boxplot-data.json} +0 -0
- /package/examples/{line-chart-max-increase.json → feature/line/line-chart-max-increase.json} +0 -0
- /package/examples/{paired-bar-data.json → feature/paired-bar/paired-bar-data.json} +0 -0
- /package/examples/{paired-bar-formatted.json → feature/paired-bar/paired-bar-formatted.json} +0 -0
- /package/examples/{scatterplot-continuous.csv → feature/scatterplot/scatterplot-continuous.csv} +0 -0
- /package/examples/{example-sparkline.json → feature/sparkline/example-sparkline.json} +0 -0
- /package/examples/{big-small-test-bar.json → feature/tests-big-small/big-small-test-bar.json} +0 -0
- /package/examples/{big-small-test-line.json → feature/tests-big-small/big-small-test-line.json} +0 -0
- /package/examples/{big-small-test-negative.json → feature/tests-big-small/big-small-test-negative.json} +0 -0
- /package/examples/{case-rate-example-data.json → feature/tests-case-rate/case-rate-example-data.json} +0 -0
- /package/examples/{covid-example-data-confidence.json → feature/tests-covid/covid-example-data-confidence.json} +0 -0
- /package/examples/{covid-example-data.json → feature/tests-covid/covid-example-data.json} +0 -0
- /package/examples/{cutoff-example-data.json → feature/tests-cutoff/cutoff-example-data.json} +0 -0
- /package/examples/{date-exclusions-data.json → feature/tests-date-exclusions/date-exclusions-data.json} +0 -0
- /package/examples/{example-combo-bar-nonnumeric.json → feature/tests-non-numerics/example-combo-bar-nonnumeric.json} +0 -0
- /package/examples/{line-chart-nonnumeric.json → feature/tests-non-numerics/line-chart-nonnumeric.json} +0 -0
- /package/examples/{planet-example-data-nonnumeric.json → feature/tests-non-numerics/planet-example-data-nonnumeric.json} +0 -0
- /package/examples/{stacked-vertical-bar-example-nonnumerics.json → feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json} +0 -0
|
@@ -128,7 +128,7 @@ const Legend = () => {
|
|
|
128
128
|
return defaultLabels
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
const isBottomOrSmallViewport = config.legend.position === 'bottom' || currentViewport === 'sm' || currentViewport === 'xs'
|
|
131
|
+
const isBottomOrSmallViewport = config.legend.position === 'bottom' || currentViewport === 'sm' || currentViewport === 'xs' || currentViewport === 'xxs'
|
|
132
132
|
const isHorizontal = config.orientation === 'horizontal'
|
|
133
133
|
const marginTop = isBottomOrSmallViewport && isHorizontal ? `${config.runtime.xAxis.size}px` : '0px'
|
|
134
134
|
const marginBottom = isBottomOrSmallViewport ? '15px' : '0px'
|
|
@@ -6,16 +6,13 @@ import { LinePath } from '@visx/shape'
|
|
|
6
6
|
import { Text } from '@visx/text'
|
|
7
7
|
|
|
8
8
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
9
|
-
|
|
10
9
|
import ConfigContext from '../ConfigContext'
|
|
11
|
-
|
|
12
10
|
import useRightAxis from '../hooks/useRightAxis'
|
|
11
|
+
import isNumber from '@cdc/core/helpers/isNumber'
|
|
13
12
|
|
|
14
13
|
export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData, xMax, yMax, seriesStyle = 'Line' }) {
|
|
15
|
-
const { colorPalettes, transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, isNumber,
|
|
16
|
-
|
|
17
|
-
// calling clean several times on same set of data (TT)
|
|
18
|
-
const cleanedData = cleanData(data, config.xAxis.dataKey)
|
|
14
|
+
const { colorPalettes, transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, isNumber, updateConfig, handleLineType } = useContext(ConfigContext)
|
|
15
|
+
|
|
19
16
|
const { yScaleRight } = useRightAxis({ config, yMax, data, updateConfig })
|
|
20
17
|
|
|
21
18
|
const handleAxisFormating = (axis = 'left', label, value) => {
|
|
@@ -45,14 +42,13 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
|
|
|
45
42
|
opacity={config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
|
|
46
43
|
display={config.legend.behavior === 'highlight' || (seriesHighlight.length === 0 && !config.legend.dynamicLegend) || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
|
|
47
44
|
>
|
|
48
|
-
{
|
|
45
|
+
{data.map((d, dataIndex) => {
|
|
49
46
|
// Find the series object from the config.series array that has a dataKey matching the seriesKey variable.
|
|
50
47
|
const series = config.series.find(({ dataKey }) => dataKey === seriesKey)
|
|
51
48
|
const { axis } = series
|
|
52
49
|
|
|
53
50
|
const xAxisValue = config.runtime.xAxis.type === 'date' ? formatDate(parseDate(d[config.runtime.xAxis.dataKey])) : d[config.runtime.xAxis.dataKey]
|
|
54
51
|
const yAxisValue = getYAxisData(d, seriesKey)
|
|
55
|
-
|
|
56
52
|
const hasMultipleSeries = Object.keys(config.runtime.seriesLabels).length > 1
|
|
57
53
|
const labeltype = axis === 'Right' ? 'rightLabel' : 'label'
|
|
58
54
|
let label = config.runtime.yAxis[labeltype]
|
|
@@ -72,7 +68,8 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
|
|
|
72
68
|
return (
|
|
73
69
|
d[seriesKey] !== undefined &&
|
|
74
70
|
d[seriesKey] !== '' &&
|
|
75
|
-
d[seriesKey] !== null &&
|
|
71
|
+
d[seriesKey] !== null &&
|
|
72
|
+
isNumber(d[seriesKey]) && (
|
|
76
73
|
<Group key={`series-${seriesKey}-point-${dataIndex}`}>
|
|
77
74
|
{/* Render legend */}
|
|
78
75
|
<Text
|
|
@@ -99,10 +96,9 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
|
|
|
99
96
|
)
|
|
100
97
|
)
|
|
101
98
|
})}
|
|
102
|
-
|
|
103
99
|
<LinePath
|
|
104
|
-
curve={allCurves.
|
|
105
|
-
data={
|
|
100
|
+
curve={allCurves[seriesData[0].lineType]}
|
|
101
|
+
data={data}
|
|
106
102
|
x={d => xScale(getXAxisData(d))}
|
|
107
103
|
y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey)))}
|
|
108
104
|
stroke={
|
|
@@ -116,7 +112,6 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
|
|
|
116
112
|
}
|
|
117
113
|
strokeWidth={2}
|
|
118
114
|
strokeOpacity={1}
|
|
119
|
-
shapeRendering='geometricPrecision'
|
|
120
115
|
strokeDasharray={lineType ? handleLineType(lineType) : 0}
|
|
121
116
|
defined={(item, i) => {
|
|
122
117
|
return item[config.runtime.seriesLabels[seriesKey]] !== '' && item[config.runtime.seriesLabels[seriesKey]] !== null && item[config.runtime.seriesLabels[seriesKey]] !== undefined
|
|
@@ -125,8 +120,8 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
|
|
|
125
120
|
{config.animate && (
|
|
126
121
|
<LinePath
|
|
127
122
|
className='animation'
|
|
128
|
-
curve={
|
|
129
|
-
data={
|
|
123
|
+
curve={seriesData.lineType}
|
|
124
|
+
data={data}
|
|
130
125
|
x={d => xScale(getXAxisData(d))}
|
|
131
126
|
y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey)))}
|
|
132
127
|
stroke='#fff'
|
|
@@ -139,7 +134,6 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
|
|
|
139
134
|
}}
|
|
140
135
|
/>
|
|
141
136
|
)}
|
|
142
|
-
|
|
143
137
|
{/* Render series labels at end if each line if selected in the editor */}
|
|
144
138
|
{config.showLineSeriesLabels &&
|
|
145
139
|
(config.runtime.lineSeriesKeys || config.runtime.seriesKeys).map(seriesKey => {
|
|
@@ -25,7 +25,7 @@ import { DeviationBar } from './DeviationBar'
|
|
|
25
25
|
|
|
26
26
|
// TODO: Move scaling functions into hooks to manage complexity
|
|
27
27
|
export default function LinearChart() {
|
|
28
|
-
const { transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, formatNumber, handleChartAriaLabels, updateConfig
|
|
28
|
+
const { transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, formatNumber, handleChartAriaLabels, updateConfig } = useContext(ConfigContext)
|
|
29
29
|
|
|
30
30
|
let [width] = dimensions
|
|
31
31
|
const { minValue, maxValue, existPositiveValue, isAllLine } = useReduceData(config, data)
|
|
@@ -83,7 +83,7 @@ export default function LinearChart() {
|
|
|
83
83
|
min = enteredMinValue && isMinValid ? enteredMinValue : minValue
|
|
84
84
|
max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
|
|
85
85
|
|
|
86
|
-
//
|
|
86
|
+
// If Confidence Intervals in data, then need to account for increased height in max for YScale
|
|
87
87
|
if (config.visualizationType === 'Bar' || config.visualizationType === 'Combo' || config.visualizationType === 'Deviation Bar') {
|
|
88
88
|
let ciYMax = 0
|
|
89
89
|
if (config.hasOwnProperty('confidenceKeys')) {
|
|
@@ -95,19 +95,7 @@ export default function LinearChart() {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
if (config.visualizationType === 'Bar' || config.visualizationType === 'Combo') {
|
|
100
|
-
let ciYMax = 0
|
|
101
|
-
if (config.hasOwnProperty('confidenceKeys')) {
|
|
102
|
-
let upperCIValues = data.map(function (d) {
|
|
103
|
-
return d[config.confidenceKeys.upper]
|
|
104
|
-
})
|
|
105
|
-
ciYMax = Math.max.apply(Math, upperCIValues)
|
|
106
|
-
if (ciYMax > max) max = ciYMax // bump up the max
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if ((config.visualizationType === 'Bar' || config.visualizationType === 'Deviation Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && min > 0) {
|
|
98
|
+
if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && min > 0) {
|
|
111
99
|
min = 0
|
|
112
100
|
}
|
|
113
101
|
if (config.visualizationType === 'Combo' && isAllLine) {
|
|
@@ -120,6 +108,11 @@ export default function LinearChart() {
|
|
|
120
108
|
}
|
|
121
109
|
}
|
|
122
110
|
|
|
111
|
+
if (config.visualizationType === 'Deviation Bar' && min > 0) {
|
|
112
|
+
const isMinValid = Number(enteredMinValue) < Math.min(minValue, Number(config.xAxis.target))
|
|
113
|
+
min = enteredMinValue && isMinValid ? enteredMinValue : 0
|
|
114
|
+
}
|
|
115
|
+
|
|
123
116
|
if (config.visualizationType === 'Line') {
|
|
124
117
|
const isMinValid = enteredMinValue < minValue
|
|
125
118
|
min = enteredMinValue && isMinValid ? enteredMinValue : minValue
|
|
@@ -161,6 +154,7 @@ export default function LinearChart() {
|
|
|
161
154
|
if (min < 0) {
|
|
162
155
|
// sets with negative data need more padding on the max
|
|
163
156
|
max *= 1.2
|
|
157
|
+
min *= 1.2
|
|
164
158
|
} else {
|
|
165
159
|
max *= 1.1
|
|
166
160
|
}
|
|
@@ -168,7 +162,7 @@ export default function LinearChart() {
|
|
|
168
162
|
|
|
169
163
|
if (config.runtime.horizontal) {
|
|
170
164
|
xScale = scaleLinear({
|
|
171
|
-
domain: [min, max],
|
|
165
|
+
domain: [min * 1.03, max],
|
|
172
166
|
range: [0, xMax]
|
|
173
167
|
})
|
|
174
168
|
|
|
@@ -255,7 +249,8 @@ export default function LinearChart() {
|
|
|
255
249
|
xScale = scaleLinear({
|
|
256
250
|
domain: [min * leftOffset, Math.max(Number(config.xAxis.target), max)],
|
|
257
251
|
range: [0, xMax],
|
|
258
|
-
round: true
|
|
252
|
+
round: true,
|
|
253
|
+
nice: true
|
|
259
254
|
})
|
|
260
255
|
}
|
|
261
256
|
// Handle Box Plots
|
|
@@ -274,8 +269,8 @@ export default function LinearChart() {
|
|
|
274
269
|
}
|
|
275
270
|
|
|
276
271
|
// check fences for max/min
|
|
277
|
-
let lowestFence = Math.min(...config.boxplot.plots.map(item => item.
|
|
278
|
-
let highestFence = Math.max(...config.boxplot.plots.map(item => item.
|
|
272
|
+
let lowestFence = Math.min(...config.boxplot.plots.map(item => item.columnLowerBounds))
|
|
273
|
+
let highestFence = Math.max(...config.boxplot.plots.map(item => item.columnUpperBounds))
|
|
279
274
|
|
|
280
275
|
if (lowestFence < min) min = lowestFence
|
|
281
276
|
if (highestFence > max) max = highestFence
|
|
@@ -296,16 +291,18 @@ export default function LinearChart() {
|
|
|
296
291
|
}
|
|
297
292
|
}
|
|
298
293
|
|
|
294
|
+
const shouldAbbreviate = true
|
|
295
|
+
|
|
299
296
|
const handleLeftTickFormatting = tick => {
|
|
300
297
|
if (config.runtime.yAxis.type === 'date') return formatDate(parseDate(tick))
|
|
301
|
-
if (config.orientation === 'vertical') return formatNumber(tick, 'left')
|
|
298
|
+
if (config.orientation === 'vertical') return formatNumber(tick, 'left', shouldAbbreviate)
|
|
302
299
|
return tick
|
|
303
300
|
}
|
|
304
301
|
|
|
305
302
|
const handleBottomTickFormatting = tick => {
|
|
306
303
|
if (config.runtime.xAxis.type === 'date') return formatDate(tick)
|
|
307
|
-
if (config.orientation === 'horizontal') return formatNumber(tick, 'left')
|
|
308
|
-
if (config.xAxis.type === 'continuous') return formatNumber(tick, 'bottom')
|
|
304
|
+
if (config.orientation === 'horizontal') return formatNumber(tick, 'left', shouldAbbreviate)
|
|
305
|
+
if (config.xAxis.type === 'continuous') return formatNumber(tick, 'bottom', shouldAbbreviate)
|
|
309
306
|
return tick
|
|
310
307
|
}
|
|
311
308
|
|
|
@@ -347,11 +344,13 @@ export default function LinearChart() {
|
|
|
347
344
|
return tickCount
|
|
348
345
|
}
|
|
349
346
|
|
|
347
|
+
const svgRef = useRef()
|
|
348
|
+
|
|
350
349
|
return isNaN(width) ? (
|
|
351
350
|
<></>
|
|
352
351
|
) : (
|
|
353
352
|
<ErrorBoundary component='LinearChart'>
|
|
354
|
-
<svg width={width} height={height} className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''}`} role='img' aria-label={handleChartAriaLabels(config)} tabIndex={0}>
|
|
353
|
+
<svg width={width} height={height} className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''}`} role='img' aria-label={handleChartAriaLabels(config)} tabIndex={0} ref={svgRef}>
|
|
355
354
|
{/* Higlighted regions */}
|
|
356
355
|
{config.regions
|
|
357
356
|
? config.regions.map(region => {
|
|
@@ -457,6 +456,7 @@ export default function LinearChart() {
|
|
|
457
456
|
})}
|
|
458
457
|
{!config.yAxis.hideAxis && <Line from={props.axisFromPoint} to={config.runtime.horizontal ? { x: 0, y: Number(heightHorizontal) } : props.axisToPoint} stroke='#000' />}
|
|
459
458
|
{yScale.domain()[0] < 0 && <Line from={{ x: props.axisFromPoint.x, y: yScale(0) }} to={{ x: xMax, y: yScale(0) }} stroke='#333' />}
|
|
459
|
+
{config.visualizationType === 'Bar' && config.orientation === 'horizontal' && xScale.domain()[0] < 0 && <Line from={{ x: xScale(0), y: 0 }} to={{ x: xScale(0), y: yMax }} stroke='#333' strokeWidth={2} />}
|
|
460
460
|
<Text className='y-label' textAnchor='middle' verticalAnchor='start' transform={`translate(${-1 * config.runtime.yAxis.size}, ${axisCenter}) rotate(-90)`} fontWeight='bold' fill={config.yAxis.labelColor}>
|
|
461
461
|
{props.label}
|
|
462
462
|
</Text>
|
|
@@ -571,7 +571,7 @@ export default function LinearChart() {
|
|
|
571
571
|
{!config.runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
|
|
572
572
|
{!config.runtime.yAxis.hideLabel && (
|
|
573
573
|
<Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
|
|
574
|
-
{formatNumber(tick.
|
|
574
|
+
{formatNumber(tick.value, 'left')}
|
|
575
575
|
</Text>
|
|
576
576
|
)}
|
|
577
577
|
</Group>
|
|
@@ -604,7 +604,7 @@ export default function LinearChart() {
|
|
|
604
604
|
{!config.runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
|
|
605
605
|
{!config.runtime.yAxis.hideLabel && (
|
|
606
606
|
<Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
|
|
607
|
-
{tick.
|
|
607
|
+
{formatNumber(tick.value, 'left')}
|
|
608
608
|
</Text>
|
|
609
609
|
)}
|
|
610
610
|
</Group>
|
|
@@ -625,11 +625,13 @@ export default function LinearChart() {
|
|
|
625
625
|
)}
|
|
626
626
|
|
|
627
627
|
{config.visualizationType === 'Deviation Bar' && <DeviationBar xScale={xScale} yScale={yScale} width={xMax} height={yMax} />}
|
|
628
|
-
|
|
629
|
-
{/* Paired Bar chart */}
|
|
630
628
|
{config.visualizationType === 'Paired Bar' && <PairedBarChart originalWidth={width} width={xMax} height={yMax} />}
|
|
629
|
+
{config.visualizationType === 'Scatter Plot' && <CoveScatterPlot xScale={xScale} yScale={yScale} getXAxisData={getXAxisData} getYAxisData={getYAxisData} />}
|
|
630
|
+
{config.visualizationType === 'Box Plot' && <CoveBoxPlot xScale={xScale} yScale={yScale} />}
|
|
631
|
+
{(config.visualizationType === 'Area Chart' || config.visualizationType === 'Combo') && <CoveAreaChart xScale={xScale} yScale={yScale} yMax={yMax} xMax={xMax} chartRef={svgRef} />}
|
|
631
632
|
|
|
632
633
|
{/* Bar chart */}
|
|
634
|
+
{/* TODO: Make this just bar or combo? */}
|
|
633
635
|
{config.visualizationType !== 'Line' && config.visualizationType !== 'Paired Bar' && config.visualizationType !== 'Box Plot' && config.visualizationType !== 'Area Chart' && config.visualizationType !== 'Scatter Plot' && config.visualizationType !== 'Deviation Bar' && (
|
|
634
636
|
<>
|
|
635
637
|
<BarChart xScale={xScale} yScale={yScale} seriesScale={seriesScale} xMax={xMax} yMax={yMax} getXAxisData={getXAxisData} getYAxisData={getYAxisData} animatedChart={animatedChart} visible={animatedChart} />
|
|
@@ -637,18 +639,12 @@ export default function LinearChart() {
|
|
|
637
639
|
)}
|
|
638
640
|
|
|
639
641
|
{/* Line chart */}
|
|
642
|
+
{/* TODO: Make this just line or combo? */}
|
|
640
643
|
{config.visualizationType !== 'Bar' && config.visualizationType !== 'Paired Bar' && config.visualizationType !== 'Box Plot' && config.visualizationType !== 'Area Chart' && config.visualizationType !== 'Scatter Plot' && config.visualizationType !== 'Deviation Bar' && (
|
|
641
644
|
<>
|
|
642
645
|
<LineChart xScale={xScale} yScale={yScale} getXAxisData={getXAxisData} getYAxisData={getYAxisData} xMax={xMax} yMax={yMax} seriesStyle={config.series} />
|
|
643
646
|
</>
|
|
644
647
|
)}
|
|
645
|
-
|
|
646
|
-
{/* Scatter Plot chart */}
|
|
647
|
-
{config.visualizationType === 'Scatter Plot' && <CoveScatterPlot xScale={xScale} yScale={yScale} getXAxisData={getXAxisData} getYAxisData={getYAxisData} />}
|
|
648
|
-
|
|
649
|
-
{/* Box Plot chart */}
|
|
650
|
-
{config.visualizationType === 'Box Plot' && <CoveBoxPlot xScale={xScale} yScale={yScale} />}
|
|
651
|
-
{config.visualizationType === 'Area Chart' && <CoveAreaChart xScale={xScale} yScale={yScale} yMax={yMax} xMax={xMax} />}
|
|
652
648
|
</svg>
|
|
653
649
|
<ReactTooltip id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`} variant='light' arrowColor='rgba(0,0,0,0)' className='tooltip' />
|
|
654
650
|
<div className='animation-trigger' ref={triggerRef} />
|
|
@@ -61,7 +61,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
61
61
|
return `<p>
|
|
62
62
|
${config.dataDescription.seriesKey}: ${groupOne.dataKey}<br/>
|
|
63
63
|
${config.xAxis.dataKey}: ${d[config.xAxis.dataKey]}<br/>
|
|
64
|
-
${label}${formatNumber(d[groupOne.dataKey])}
|
|
64
|
+
${label}${formatNumber(d[groupOne.dataKey], 'left')}
|
|
65
65
|
</p>`
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -69,7 +69,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
69
69
|
return `<p>
|
|
70
70
|
${config.dataDescription.seriesKey}: ${groupTwo.dataKey}<br/>
|
|
71
71
|
${config.xAxis.dataKey}: ${d[config.xAxis.dataKey]}<br/>
|
|
72
|
-
${label}${formatNumber(d[groupTwo.dataKey])}
|
|
72
|
+
${label}${formatNumber(d[groupTwo.dataKey], 'left')}
|
|
73
73
|
</p>`
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -99,7 +99,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
99
99
|
const totalheight = (Number(config.barSpace) + barHeight + borderWidth) * data.length
|
|
100
100
|
config.heights.horizontal = totalheight
|
|
101
101
|
// check if text fits inside of the bar including suffix/prefix,comma,fontSize ..etc
|
|
102
|
-
const textWidth = getTextWidth(formatNumber(d[groupOne.dataKey]), `normal ${fontSize[config.fontSize]}px sans-serif`)
|
|
102
|
+
const textWidth = getTextWidth(formatNumber(d[groupOne.dataKey], 'left'), `normal ${fontSize[config.fontSize]}px sans-serif`)
|
|
103
103
|
const textFits = textWidth < barWidth - 5 // minus padding dx(5)
|
|
104
104
|
|
|
105
105
|
return (
|
|
@@ -123,7 +123,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
123
123
|
/>
|
|
124
124
|
{config.yAxis.displayNumbersOnBar && displayBar && (
|
|
125
125
|
<Text textAnchor={textFits ? 'start' : 'end'} dx={textFits ? 5 : -5} verticalAnchor='middle' x={halfWidth - barWidth} y={y + config.barHeight / 2} fill={textFits ? groupOne.labelColor : '#000'}>
|
|
126
|
-
{formatNumber(d[groupOne.dataKey])}
|
|
126
|
+
{formatNumber(d[groupOne.dataKey], 'left')}
|
|
127
127
|
</Text>
|
|
128
128
|
)}
|
|
129
129
|
</Group>
|
|
@@ -143,7 +143,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
143
143
|
const totalheight = (Number(config.barSpace) + barHeight + borderWidth) * data.length
|
|
144
144
|
config.heights.horizontal = totalheight
|
|
145
145
|
// check if text fits inside of the bar including suffix/prefix,comma,fontSize ..etc
|
|
146
|
-
const textWidth = getTextWidth(formatNumber(d[groupTwo.dataKey]), `normal ${fontSize[config.fontSize]}px sans-serif`)
|
|
146
|
+
const textWidth = getTextWidth(formatNumber(d[groupTwo.dataKey], 'left'), `normal ${fontSize[config.fontSize]}px sans-serif`)
|
|
147
147
|
const isTextFits = textWidth < barWidth - 5 // minus padding dx(5)
|
|
148
148
|
|
|
149
149
|
return (
|
|
@@ -174,7 +174,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
174
174
|
/>
|
|
175
175
|
{config.yAxis.displayNumbersOnBar && displayBar && (
|
|
176
176
|
<Text textAnchor={isTextFits ? 'end' : 'start'} dx={isTextFits ? -5 : 5} verticalAnchor='middle' x={halfWidth + barWidth} y={y + config.barHeight / 2} fill={isTextFits ? groupTwo.labelColor : '#000'}>
|
|
177
|
-
{formatNumber(d[groupTwo.dataKey])}
|
|
177
|
+
{formatNumber(d[groupTwo.dataKey], 'left')}
|
|
178
178
|
</Text>
|
|
179
179
|
)}
|
|
180
180
|
</Group>
|
|
@@ -18,9 +18,7 @@ const enterUpdateTransition = ({ startAngle, endAngle }) => ({
|
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
export default function PieChart() {
|
|
21
|
-
const { transformedData: data, config, dimensions, seriesHighlight, colorScale, formatNumber, currentViewport, handleChartAriaLabels
|
|
22
|
-
|
|
23
|
-
const cleanedData = cleanData(data, config.xAxis.dataKey)
|
|
21
|
+
const { transformedData: data, config, dimensions, seriesHighlight, colorScale, formatNumber, currentViewport, handleChartAriaLabels } = useContext(ConfigContext)
|
|
24
22
|
|
|
25
23
|
const [filteredData, setFilteredData] = useState(undefined)
|
|
26
24
|
const [animatedPie, setAnimatePie] = useState(false)
|
|
@@ -140,7 +138,7 @@ export default function PieChart() {
|
|
|
140
138
|
<ErrorBoundary component='PieChart'>
|
|
141
139
|
<svg width={width} height={height} className={`animated-pie group ${config.animate === false || animatedPie ? 'animated' : ''}`} role='img' aria-label={handleChartAriaLabels(config)}>
|
|
142
140
|
<Group top={centerY} left={centerX}>
|
|
143
|
-
<Pie data={filteredData ||
|
|
141
|
+
<Pie data={filteredData || data} pieValue={d => d[config.runtime.yAxis.dataKey]} pieSortValues={() => -1} innerRadius={radius - donutThickness} outerRadius={radius}>
|
|
144
142
|
{pie => <AnimatedPie {...pie} getKey={d => d.data[config.runtime.xAxis.dataKey]} />}
|
|
145
143
|
</Pie>
|
|
146
144
|
</Group>
|
|
@@ -35,43 +35,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
35
35
|
const isMaxValid = Number(enteredMaxValue) >= Number(maxValue)
|
|
36
36
|
const isMinValid = Number(enteredMinValue) <= Number(minValue)
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
// Examples: NA, N/A, "1,234", "anystring"
|
|
40
|
-
// - if you dont call this on data into LineGroup below, for example
|
|
41
|
-
// then entire data series are removed because of the defined statement
|
|
42
|
-
// i.e. if a series has any bad data points the entire series wont plot
|
|
43
|
-
const cleanData = (data, testing = false) => {
|
|
44
|
-
let cleanedup = []
|
|
45
|
-
if (testing) console.log('## Data to clean=', data)
|
|
46
|
-
data.forEach(function (d, i) {
|
|
47
|
-
let cleanedSeries = {}
|
|
48
|
-
Object.keys(d).forEach(function (key) {
|
|
49
|
-
if (key === 'Date') {
|
|
50
|
-
// pass thru the dates
|
|
51
|
-
cleanedSeries[key] = d[key]
|
|
52
|
-
} else {
|
|
53
|
-
// remove comma and dollar signs
|
|
54
|
-
let tmp = d[key] !== null && d[key] !== '' ? d[key].replace(/[,$]/g, '') : ''
|
|
55
|
-
if (testing) console.log('tmp no comma or $', tmp)
|
|
56
|
-
if ((tmp !== '' && tmp !== null && !isNaN(tmp)) || (tmp !== '' && tmp !== null && /\d+\.?\d*/.test(tmp))) {
|
|
57
|
-
cleanedSeries[key] = tmp
|
|
58
|
-
} else {
|
|
59
|
-
// return nothing to skip bad data point
|
|
60
|
-
cleanedSeries[key] = '' // returning blank fixes broken chart draw
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
cleanedup.push(cleanedSeries)
|
|
65
|
-
})
|
|
66
|
-
if (testing) console.log('## cleanedData =', cleanedup)
|
|
67
|
-
return cleanedup
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Just do this once up front otherwise we end up
|
|
71
|
-
// calling clean several times on same set of data (TT)
|
|
72
|
-
const cleanedData = cleanData(data, config.xAxis.dataKey)
|
|
73
|
-
|
|
74
|
-
if (cleanedData) {
|
|
38
|
+
if (data) {
|
|
75
39
|
let min = enteredMinValue && isMinValid ? enteredMinValue : minValue
|
|
76
40
|
let max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
|
|
77
41
|
|
|
@@ -86,7 +50,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
86
50
|
max += paddingValue
|
|
87
51
|
}
|
|
88
52
|
|
|
89
|
-
let xAxisDataMapped =
|
|
53
|
+
let xAxisDataMapped = data.map(d => getXAxisData(d))
|
|
90
54
|
|
|
91
55
|
if (config.runtime.horizontal) {
|
|
92
56
|
xScale = scaleLinear({
|
|
@@ -128,7 +92,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
128
92
|
return (
|
|
129
93
|
<ErrorBoundary component='SparkLine'>
|
|
130
94
|
<svg role='img' aria-label={handleChartAriaLabels(config)} width={width} height={height} className={'sparkline'} tabIndex={0}>
|
|
131
|
-
{config.runtime.lineSeriesKeys
|
|
95
|
+
{config.runtime.lineSeriesKeys?.length > 0
|
|
132
96
|
? config.runtime.lineSeriesKeys
|
|
133
97
|
: config.runtime.seriesKeys.map((seriesKey, index) => (
|
|
134
98
|
<>
|
|
@@ -141,7 +105,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
141
105
|
opacity={config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
|
|
142
106
|
display={config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
|
|
143
107
|
>
|
|
144
|
-
{
|
|
108
|
+
{data.map((d, dataIndex) => {
|
|
145
109
|
let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${formatNumber(getYAxisData(d, seriesKey))}` : formatNumber(getYAxisData(d, seriesKey))
|
|
146
110
|
let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${d[config.runtime.xAxis.dataKey]}` : d[config.runtime.xAxis.dataKey]
|
|
147
111
|
|
|
@@ -175,7 +139,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
175
139
|
})}
|
|
176
140
|
<LinePath
|
|
177
141
|
curve={allCurves.curveLinear}
|
|
178
|
-
data={
|
|
142
|
+
data={data}
|
|
179
143
|
x={d => xScale(getXAxisData(d))}
|
|
180
144
|
y={d => yScale(getYAxisData(d, seriesKey))}
|
|
181
145
|
stroke={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
|
|
@@ -201,7 +165,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
201
165
|
hideTicks
|
|
202
166
|
scale={xScale}
|
|
203
167
|
tickValues={handleSparkLineTicks}
|
|
204
|
-
tickFormat={formatDate}
|
|
168
|
+
tickFormat={tick => (config.xAxis.type === 'date' ? formatDate(tick) : null)}
|
|
205
169
|
stroke={'black'}
|
|
206
170
|
tickStroke={'black'}
|
|
207
171
|
tickLabelProps={() => ({
|
|
@@ -67,7 +67,9 @@ export default {
|
|
|
67
67
|
iqr: 'Interquartile Range',
|
|
68
68
|
total: 'Total',
|
|
69
69
|
outliers: 'Outliers',
|
|
70
|
-
values: 'Values'
|
|
70
|
+
values: 'Values',
|
|
71
|
+
lowerBounds: 'Lower Bounds',
|
|
72
|
+
upperBounds: 'Upper Bounds'
|
|
71
73
|
}
|
|
72
74
|
},
|
|
73
75
|
topAxis: {
|
|
@@ -106,7 +108,8 @@ export default {
|
|
|
106
108
|
limitHeight: false,
|
|
107
109
|
height: '',
|
|
108
110
|
caption: '',
|
|
109
|
-
showDownloadUrl: false
|
|
111
|
+
showDownloadUrl: false,
|
|
112
|
+
showDataTableLink: true
|
|
110
113
|
},
|
|
111
114
|
orientation: 'vertical',
|
|
112
115
|
legend: {
|
|
@@ -146,5 +149,6 @@ export default {
|
|
|
146
149
|
border: true,
|
|
147
150
|
accent: true,
|
|
148
151
|
background: true
|
|
149
|
-
}
|
|
152
|
+
},
|
|
153
|
+
filterBehavior: 'Filter Change'
|
|
150
154
|
}
|
package/src/index.jsx
CHANGED
|
@@ -6,11 +6,12 @@ import CdcChart from './CdcChart'
|
|
|
6
6
|
import 'react-tooltip/dist/react-tooltip.css'
|
|
7
7
|
|
|
8
8
|
let isEditor = window.location.href.includes('editor=true')
|
|
9
|
+
let isDebug = window.location.href.includes('debug=true')
|
|
9
10
|
|
|
10
11
|
let domContainer = document.getElementsByClassName('react-container')[0]
|
|
11
12
|
|
|
12
13
|
ReactDOM.createRoot(domContainer).render(
|
|
13
14
|
<React.StrictMode>
|
|
14
|
-
<CdcChart configUrl={domContainer.attributes['data-config'].value} isEditor={isEditor} />
|
|
15
|
+
<CdcChart configUrl={domContainer.attributes['data-config'].value} isEditor={isEditor} isDebug={isDebug} />
|
|
15
16
|
</React.StrictMode>
|
|
16
17
|
)
|
|
@@ -75,11 +75,16 @@
|
|
|
75
75
|
align-items: center;
|
|
76
76
|
justify-content: space-between;
|
|
77
77
|
font-size: 0.9em;
|
|
78
|
+
position: relative;
|
|
78
79
|
|
|
79
80
|
&:hover {
|
|
80
81
|
background-color: $lightestGray;
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
div {
|
|
85
|
+
width: 100%;
|
|
86
|
+
}
|
|
87
|
+
|
|
83
88
|
.series-list__name {
|
|
84
89
|
position: relative;
|
|
85
90
|
user-select: none;
|
|
@@ -132,7 +137,13 @@
|
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
.series-list__dropdown {
|
|
140
|
+
width: 100%;
|
|
141
|
+
display: block;
|
|
135
142
|
font-size: 0.8em;
|
|
143
|
+
margin-bottom: 10px;
|
|
144
|
+
select {
|
|
145
|
+
width: 100%;
|
|
146
|
+
}
|
|
136
147
|
}
|
|
137
148
|
|
|
138
149
|
.series-list__remove {
|
|
@@ -140,6 +151,9 @@
|
|
|
140
151
|
font-size: 1.125rem;
|
|
141
152
|
color: #f00;
|
|
142
153
|
cursor: pointer;
|
|
154
|
+
position: absolute;
|
|
155
|
+
top: 5px;
|
|
156
|
+
right: 5px;
|
|
143
157
|
}
|
|
144
158
|
|
|
145
159
|
+ li {
|
|
@@ -150,6 +164,7 @@
|
|
|
150
164
|
|
|
151
165
|
.series-list__name-text {
|
|
152
166
|
max-width: 150px;
|
|
167
|
+
margin-bottom: 10px;
|
|
153
168
|
white-space: nowrap;
|
|
154
169
|
text-overflow: ellipsis;
|
|
155
170
|
overflow: hidden;
|
package/src/scss/main.scss
CHANGED
|
@@ -231,9 +231,13 @@
|
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
.visx-tooltip {
|
|
235
|
+
z-index: 100000;
|
|
236
|
+
}
|
|
237
|
+
|
|
234
238
|
.tooltip {
|
|
235
|
-
border: rgba(0,0,0
|
|
236
|
-
box-shadow: rgba(0,0,0
|
|
239
|
+
border: rgba(0, 0, 0, 0.3) 1px solid;
|
|
240
|
+
box-shadow: rgba(0, 0, 0, 0.1) 3px 3px 7px;
|
|
237
241
|
opacity: 1;
|
|
238
242
|
line-height: 1.4em;
|
|
239
243
|
font-size: 1em;
|
|
@@ -242,8 +246,8 @@
|
|
|
242
246
|
z-index: 1;
|
|
243
247
|
|
|
244
248
|
.react-tooltip-arrow {
|
|
245
|
-
border-bottom: rgba(0,0,0
|
|
246
|
-
border-right: rgba(0,0,0
|
|
249
|
+
border-bottom: rgba(0, 0, 0, 0.3) 1px solid;
|
|
250
|
+
border-right: rgba(0, 0, 0, 0.3) 1px solid;
|
|
247
251
|
backface-visibility: hidden;
|
|
248
252
|
}
|
|
249
253
|
}
|
|
@@ -337,8 +341,6 @@
|
|
|
337
341
|
}
|
|
338
342
|
|
|
339
343
|
&__wrapper {
|
|
340
|
-
margin-bottom: 40px;
|
|
341
|
-
|
|
342
344
|
hr {
|
|
343
345
|
margin-bottom: 20px;
|
|
344
346
|
}
|
package/examples/box-plot.csv
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
x,min,firstQuartile,median,thirdQuartile,max,outliers
|
|
2
|
-
Group 1,-3.529439249291613,2.498913996933074,6,6.517816161082865,12.546169407307552,-4.222649440089676,-3.7995254226812145,12.669680018702707,,,,,,,,,,,,
|
|
3
|
-
Group 2,-2.961278855962981,3.1553763355078277,5.31642875968012,7.233146463155033,13.349801654625843,-4.5962772985737645,14.42798091207488,14.832072412460995,,,,,,,,,,,,
|
|
4
|
-
Group 3,-0.784362943544294,5.600233888319485,7.606034555385235,9.85663177622867,16.24122860809245,-2.0568265981730285,-2.036299998010181,-1.634595257757523,-0.9751707921193091,-0.9256799494292718,-0.813852054679872,16.255225428689318,19.221712546396496,,,,,,,
|
|
5
|
-
Group 4,-3.4976011611041598,2.362493132101971,4.364242960871863,6.269222660906058,12.129316954112188,-5.912277243480174,-4.535668956980487,-4.255719319016919,-4.175200716132927,-4.1021204775116455,-3.7913796362224352,-3.6909919981778567,-3.6261129831962697,12.169135739844744,12.1724073804239,12.191268834215071,12.236896118210165,12.34513716605812,12.826785108558722,13.048968511771164
|