@cdc/chart 4.23.3 → 4.23.5
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 +52543 -50830
- package/examples/feature/__data__/area-chart.json +56 -0
- package/examples/{planet-example-data.json → feature/__data__/planet-example-data.json} +3 -8
- package/examples/feature/__data__/planet-logaritmic-data.json +56 -0
- package/examples/feature/area/area-chart-category.json +240 -0
- package/examples/{area-chart.json → feature/area/area-chart-date.json} +70 -13
- package/examples/feature/bar/example-bar-chart.json +558 -0
- 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/feature/bar/planet-chart-logaritmic-config.json +170 -0
- 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/boxplot/valid-boxplot.csv +17 -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 +212 -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/forecasting/random_data.csv +366 -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/feature/test-highlight/test-highlight-2.json +789 -0
- package/examples/feature/test-highlight/test-highlight-vertical.json +561 -0
- package/examples/feature/test-highlight/test-highlight.json +100 -0
- 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/{stacked-vertical-bar-example-nonnumerics.json → feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json} +1 -2
- package/examples/gallery/bar-chart-horizontal/horizontal-highlight.json +345 -0
- 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 +405 -40
- package/src/components/AreaChart.jsx +122 -80
- package/src/components/BarChart.jsx +126 -49
- package/src/components/BoxPlot.jsx +28 -20
- package/src/components/DataTable.jsx +7 -6
- package/src/components/DeviationBar.jsx +34 -34
- package/src/components/EditorPanel.jsx +1332 -352
- package/src/components/Legend.jsx +40 -4
- package/src/components/LineChart.jsx +10 -23
- package/src/components/LinearChart.jsx +133 -286
- 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 +23 -4
- package/src/hooks/useHighlightedBars.js +154 -0
- package/src/hooks/useMinMax.js +92 -0
- package/src/hooks/useReduceData.js +31 -57
- package/src/hooks/useScales.js +202 -0
- 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/example-bar-chart.json +0 -36
- 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/{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/src/CdcChart.jsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect, useCallback } from 'react'
|
|
1
|
+
import React, { useState, useEffect, useCallback, useRef } from 'react'
|
|
2
2
|
|
|
3
3
|
// IE11
|
|
4
4
|
import ResizeObserver from 'resize-observer-polyfill'
|
|
@@ -9,10 +9,10 @@ import * as d3 from 'd3-array'
|
|
|
9
9
|
import { scaleOrdinal } from '@visx/scale'
|
|
10
10
|
import ParentSize from '@visx/responsive/lib/components/ParentSize'
|
|
11
11
|
import { timeParse, timeFormat } from 'd3-time-format'
|
|
12
|
-
import { format } from 'd3-format'
|
|
13
12
|
import Papa from 'papaparse'
|
|
14
13
|
import parse from 'html-react-parser'
|
|
15
14
|
import 'react-tooltip/dist/react-tooltip.css'
|
|
15
|
+
import chroma from 'chroma-js'
|
|
16
16
|
|
|
17
17
|
// Primary Components
|
|
18
18
|
import ConfigContext from './ConfigContext'
|
|
@@ -27,11 +27,10 @@ import useDataVizClasses from '@cdc/core/helpers/useDataVizClasses'
|
|
|
27
27
|
|
|
28
28
|
import SparkLine from './components/SparkLine'
|
|
29
29
|
import Legend from './components/Legend'
|
|
30
|
-
import DataTable from './components/DataTable'
|
|
31
30
|
import defaults from './data/initial-state'
|
|
32
31
|
import EditorPanel from './components/EditorPanel'
|
|
33
32
|
import Loading from '@cdc/core/components/Loading'
|
|
34
|
-
import Filters from '
|
|
33
|
+
import Filters from '@cdc/core/components/Filters'
|
|
35
34
|
import CoveMediaControls from '@cdc/core/components/CoveMediaControls'
|
|
36
35
|
|
|
37
36
|
// Helpers
|
|
@@ -40,11 +39,40 @@ import getViewport from '@cdc/core/helpers/getViewport'
|
|
|
40
39
|
import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
41
40
|
import cacheBustingString from '@cdc/core/helpers/cacheBustingString'
|
|
42
41
|
import isNumber from '@cdc/core/helpers/isNumber'
|
|
43
|
-
import cleanData from '@cdc/core/helpers/cleanData'
|
|
44
42
|
|
|
45
43
|
import './scss/main.scss'
|
|
44
|
+
// load both then config below determines which to use
|
|
45
|
+
import DataTable_horiz from './components/DataTable'
|
|
46
|
+
import DataTable_vert from '@cdc/core/components/DataTable'
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
const generateColorsArray = (color = '#000000', special = false) => {
|
|
49
|
+
let colorObj = chroma(color)
|
|
50
|
+
let hoverColor = special ? colorObj.brighten(0.5).hex() : colorObj.saturate(1.3).hex()
|
|
51
|
+
|
|
52
|
+
return [color, hoverColor, colorObj.darken(0.3).hex()]
|
|
53
|
+
}
|
|
54
|
+
const hashObj = row => {
|
|
55
|
+
try {
|
|
56
|
+
if (!row) throw new Error('No row supplied to hashObj')
|
|
57
|
+
|
|
58
|
+
let str = JSON.stringify(row)
|
|
59
|
+
let hash = 0
|
|
60
|
+
|
|
61
|
+
if (str.length === 0) return hash
|
|
62
|
+
|
|
63
|
+
for (let i = 0; i < str.length; i++) {
|
|
64
|
+
let char = str.charCodeAt(i)
|
|
65
|
+
hash = (hash << 5) - hash + char
|
|
66
|
+
hash = hash & hash
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return hash
|
|
70
|
+
} catch (e) {
|
|
71
|
+
console.error('COVE: ', e) // eslint-disable-line
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default function CdcChart({ configUrl, config: configObj, isEditor = false, isDebug = false, isDashboard = false, setConfig: setParentConfig, setEditing, hostname, link }) {
|
|
48
76
|
const transform = new DataTransform()
|
|
49
77
|
const [loading, setLoading] = useState(true)
|
|
50
78
|
const [colorScale, setColorScale] = useState(null)
|
|
@@ -61,6 +89,13 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
61
89
|
const [dynamicLegendItems, setDynamicLegendItems] = useState([])
|
|
62
90
|
const [imageId] = useState(`cove-${Math.random().toString(16).slice(-4)}`)
|
|
63
91
|
|
|
92
|
+
let legendMemo = useRef(new Map()) // map collection
|
|
93
|
+
let innerContainerRef = useRef()
|
|
94
|
+
|
|
95
|
+
if (isDebug) console.log('Chart config', config)
|
|
96
|
+
|
|
97
|
+
const DataTable = config?.table?.showVertical ? DataTable_vert : DataTable_horiz
|
|
98
|
+
|
|
64
99
|
// Destructure items from config for more readable JSX
|
|
65
100
|
let { legend, title, description, visualizationType } = config
|
|
66
101
|
|
|
@@ -84,7 +119,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
84
119
|
}
|
|
85
120
|
|
|
86
121
|
const handleChartAriaLabels = (state, testing = false) => {
|
|
87
|
-
if (testing) console.log(`handleChartAriaLabels Testing On:`, state)
|
|
122
|
+
if (testing) console.log(`handleChartAriaLabels Testing On:`, state) // eslint-disable-line
|
|
88
123
|
try {
|
|
89
124
|
if (!state.visualizationType) throw Error('handleChartAriaLabels: no visualization type found in state')
|
|
90
125
|
let ariaLabel = ''
|
|
@@ -103,26 +138,121 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
103
138
|
}
|
|
104
139
|
}
|
|
105
140
|
|
|
141
|
+
const reloadURLData = async () => {
|
|
142
|
+
if (config.dataUrl) {
|
|
143
|
+
const dataUrl = new URL(config.runtimeDataUrl || config.dataUrl)
|
|
144
|
+
let qsParams = Object.fromEntries(new URLSearchParams(dataUrl.search))
|
|
145
|
+
|
|
146
|
+
let isUpdateNeeded = false
|
|
147
|
+
config.filters.forEach(filter => {
|
|
148
|
+
if (filter.type === 'url' && qsParams[filter.queryParameter] !== decodeURIComponent(filter.active)) {
|
|
149
|
+
qsParams[filter.queryParameter] = filter.active
|
|
150
|
+
isUpdateNeeded = true
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
if ((!config.formattedData || config.formattedData.urlFiltered) && !isUpdateNeeded) return
|
|
155
|
+
|
|
156
|
+
let dataUrlFinal = `${dataUrl.origin}${dataUrl.pathname}${Object.keys(qsParams)
|
|
157
|
+
.map((param, i) => {
|
|
158
|
+
let qs = i === 0 ? '?' : '&'
|
|
159
|
+
qs += param + '='
|
|
160
|
+
qs += qsParams[param]
|
|
161
|
+
return qs
|
|
162
|
+
})
|
|
163
|
+
.join('')}`
|
|
164
|
+
|
|
165
|
+
let data
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const regex = /(?:\.([^.]+))?$/
|
|
169
|
+
|
|
170
|
+
const ext = regex.exec(dataUrl.pathname)[1]
|
|
171
|
+
if ('csv' === ext) {
|
|
172
|
+
data = await fetch(dataUrlFinal)
|
|
173
|
+
.then(response => response.text())
|
|
174
|
+
.then(responseText => {
|
|
175
|
+
const parsedCsv = Papa.parse(responseText, {
|
|
176
|
+
header: true,
|
|
177
|
+
dynamicTyping: true,
|
|
178
|
+
skipEmptyLines: true
|
|
179
|
+
})
|
|
180
|
+
return parsedCsv.data
|
|
181
|
+
})
|
|
182
|
+
} else if ('json' === ext) {
|
|
183
|
+
data = await fetch(dataUrlFinal).then(response => response.json())
|
|
184
|
+
} else {
|
|
185
|
+
data = []
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
console.error(`Cannot parse URL: ${dataUrlFinal}`)
|
|
189
|
+
data = []
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (config.dataDescription) {
|
|
193
|
+
data = transform.autoStandardize(data)
|
|
194
|
+
data = transform.developerStandardize(data, config.dataDescription)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
Object.assign(data, { urlFiltered: true })
|
|
198
|
+
|
|
199
|
+
updateConfig({ ...config, runtimeDataUrl: dataUrlFinal, data, formattedData: data })
|
|
200
|
+
|
|
201
|
+
if (data) {
|
|
202
|
+
setStateData(data)
|
|
203
|
+
setExcludedData(data)
|
|
204
|
+
setFilteredData(filterData(config.filters, data))
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
106
209
|
const handleLineType = lineType => {
|
|
107
210
|
switch (lineType) {
|
|
108
211
|
case 'dashed-sm':
|
|
109
212
|
return '5 5'
|
|
213
|
+
case 'Dashed Small':
|
|
214
|
+
return '5 5'
|
|
110
215
|
case 'dashed-md':
|
|
111
216
|
return '10 5'
|
|
217
|
+
case 'Dashed Medium':
|
|
218
|
+
return '10 5'
|
|
112
219
|
case 'dashed-lg':
|
|
113
220
|
return '15 5'
|
|
221
|
+
case 'Dashed Large':
|
|
222
|
+
return '15 5'
|
|
114
223
|
default:
|
|
115
224
|
return 0
|
|
116
225
|
}
|
|
117
226
|
}
|
|
118
227
|
|
|
228
|
+
const lineOptions = [
|
|
229
|
+
{
|
|
230
|
+
value: 'Dashed Small',
|
|
231
|
+
key: 'dashed-sm'
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
value: 'Dashed Medium',
|
|
235
|
+
key: 'dashed-md'
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
value: 'Dashed Large',
|
|
239
|
+
key: 'dashed-lg'
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
value: 'Solid Line',
|
|
243
|
+
key: 'solid-line'
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
|
|
119
247
|
const loadConfig = async () => {
|
|
120
248
|
let response = configObj || (await (await fetch(configUrl)).json())
|
|
121
249
|
|
|
122
250
|
// If data is included through a URL, fetch that and store
|
|
123
251
|
let data = response.formattedData || response.data || {}
|
|
124
252
|
|
|
125
|
-
|
|
253
|
+
const urlFilters = response.filters ? (response.filters.filter(filter => filter.type === 'url').length > 0 ? true : false) : false
|
|
254
|
+
|
|
255
|
+
if (response.dataUrl && !urlFilters) {
|
|
126
256
|
try {
|
|
127
257
|
const regex = /(?:\.([^.]+))?$/
|
|
128
258
|
|
|
@@ -144,7 +274,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
144
274
|
data = await fetch(response.dataUrl + `?v=${cacheBustingString()}`).then(response => response.json())
|
|
145
275
|
}
|
|
146
276
|
} catch {
|
|
147
|
-
console.error(`COVE: Cannot parse URL: ${response.dataUrl}`)
|
|
277
|
+
console.error(`COVE: Cannot parse URL: ${response.dataUrl}`) // eslint-disable-line
|
|
148
278
|
data = []
|
|
149
279
|
}
|
|
150
280
|
|
|
@@ -159,11 +289,19 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
159
289
|
setExcludedData(data)
|
|
160
290
|
}
|
|
161
291
|
|
|
292
|
+
// force showVertical for data tables false if it does not exist
|
|
293
|
+
if (response !== undefined && response.table !== undefined) {
|
|
294
|
+
if (!response.table || !response.table.showVertical) {
|
|
295
|
+
response.table = response.table || {}
|
|
296
|
+
response.table.showVertical = false
|
|
297
|
+
}
|
|
298
|
+
}
|
|
162
299
|
let newConfig = { ...defaults, ...response }
|
|
163
300
|
if (newConfig.visualizationType === 'Box Plot') {
|
|
164
301
|
newConfig.legend.hide = true
|
|
165
302
|
}
|
|
166
303
|
if (undefined === newConfig.table.show) newConfig.table.show = !isDashboard
|
|
304
|
+
|
|
167
305
|
updateConfig(newConfig, data)
|
|
168
306
|
}
|
|
169
307
|
|
|
@@ -219,7 +357,9 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
219
357
|
|
|
220
358
|
newConfig.filters[index].values = filterValues
|
|
221
359
|
// Initial filter should be active
|
|
222
|
-
|
|
360
|
+
|
|
361
|
+
newConfig.filters[index].active = newConfig.filters[index].active || filterValues[0]
|
|
362
|
+
newConfig.filters[index].filterStyle = newConfig.filters[index].filterStyle ? newConfig.filters[index].filterStyle : 'dropdown'
|
|
223
363
|
})
|
|
224
364
|
currentData = filterData(newConfig.filters, newExcludedData)
|
|
225
365
|
setFilteredData(currentData)
|
|
@@ -258,6 +398,37 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
258
398
|
let tableData = []
|
|
259
399
|
const plots = []
|
|
260
400
|
|
|
401
|
+
/**
|
|
402
|
+
* Calculates the first quartile (q1) and third quartile (q3) from an array of integers or decimals.
|
|
403
|
+
*
|
|
404
|
+
* @param {Array} arr - The array of integers or decimals.
|
|
405
|
+
* @returns {Object} An object containing the q1 and q3 values.
|
|
406
|
+
*/
|
|
407
|
+
const getQuartiles = arr => {
|
|
408
|
+
arr.sort((a, b) => a - b)
|
|
409
|
+
|
|
410
|
+
// Calculate the index of the median value of the array
|
|
411
|
+
const medianIndex = Math.floor(arr.length / 2)
|
|
412
|
+
|
|
413
|
+
// Check if the length of the array is even or odd
|
|
414
|
+
const isEvenLength = arr.length % 2 === 0
|
|
415
|
+
|
|
416
|
+
// Split the array into two subarrays based on the median index
|
|
417
|
+
const q1Array = isEvenLength ? arr.slice(0, medianIndex) : arr.slice(0, medianIndex + 1)
|
|
418
|
+
const q3Array = isEvenLength ? arr.slice(medianIndex) : arr.slice(medianIndex + 1)
|
|
419
|
+
|
|
420
|
+
// Calculate the median of the first subarray to get the q1 value
|
|
421
|
+
const q1Index = Math.floor(q1Array.length / 2)
|
|
422
|
+
const q1 = isEvenLength ? (q1Array[q1Index - 1] + q1Array[q1Index]) / 2 : q1Array[q1Index]
|
|
423
|
+
|
|
424
|
+
// Calculate the median of the second subarray to get the q3 value
|
|
425
|
+
const q3Index = Math.floor(q3Array.length / 2)
|
|
426
|
+
const q3 = isEvenLength ? (q3Array[q3Index - 1] + q3Array[q3Index]) / 2 : q3Array[q3Index]
|
|
427
|
+
|
|
428
|
+
// Return an object containing the q1 and q3 values
|
|
429
|
+
return { q1, q3 }
|
|
430
|
+
}
|
|
431
|
+
|
|
261
432
|
// group specific statistics
|
|
262
433
|
// prevent re-renders
|
|
263
434
|
if (!groups) return
|
|
@@ -268,10 +439,16 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
268
439
|
// filter data by group
|
|
269
440
|
let filteredData = newExcludedData ? newExcludedData.filter(item => item[newConfig.xAxis.dataKey] === g) : data.filter(item => item[newConfig.xAxis.dataKey] === g)
|
|
270
441
|
let filteredDataValues = filteredData.map(item => Number(item[newConfig?.series[0]?.dataKey]))
|
|
271
|
-
|
|
442
|
+
|
|
443
|
+
// Sort the data for upcoming functions.
|
|
444
|
+
let sortedData = filteredDataValues.sort((a, b) => a - b)
|
|
445
|
+
|
|
446
|
+
// ! - Notice d3.quantile doesn't work here, and we had to take a custom route.
|
|
447
|
+
const quartiles = getQuartiles(sortedData)
|
|
272
448
|
|
|
273
449
|
if (!filteredData) throw new Error('boxplots dont have data yet')
|
|
274
450
|
if (!plots) throw new Error('boxplots dont have plots yet')
|
|
451
|
+
|
|
275
452
|
if (newConfig.boxplot.firstQuartilePercentage === '') {
|
|
276
453
|
newConfig.boxplot.firstQuartilePercentage = 0
|
|
277
454
|
}
|
|
@@ -280,27 +457,30 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
280
457
|
newConfig.boxplot.thirdQuartilePercentage = 0
|
|
281
458
|
}
|
|
282
459
|
|
|
283
|
-
const q1 =
|
|
284
|
-
const q3 =
|
|
460
|
+
const q1 = quartiles.q1
|
|
461
|
+
const q3 = quartiles.q3
|
|
285
462
|
const iqr = q3 - q1
|
|
286
463
|
const lowerBounds = q1 - (q3 - q1) * 1.5
|
|
287
464
|
const upperBounds = q3 + (q3 - q1) * 1.5
|
|
288
|
-
|
|
465
|
+
|
|
466
|
+
const outliers = sortedData.filter(v => v < lowerBounds || v > upperBounds)
|
|
289
467
|
let nonOutliers = filteredDataValues
|
|
290
468
|
|
|
291
469
|
nonOutliers = nonOutliers.filter(item => !outliers.includes(item))
|
|
292
470
|
|
|
293
471
|
plots.push({
|
|
294
472
|
columnCategory: g,
|
|
295
|
-
columnMax:
|
|
473
|
+
columnMax: d3.min([d3.max(filteredDataValues), q1 + 1.5 * iqr]),
|
|
296
474
|
columnThirdQuartile: Number(q3).toFixed(newConfig.dataFormat.roundTo),
|
|
297
475
|
columnMedian: Number(d3.median(filteredDataValues)).toFixed(newConfig.dataFormat.roundTo),
|
|
298
476
|
columnFirstQuartile: q1.toFixed(newConfig.dataFormat.roundTo),
|
|
299
|
-
columnMin:
|
|
477
|
+
columnMin: d3.max([d3.min(filteredDataValues), q1 - 1.5 * iqr]),
|
|
300
478
|
columnTotal: filteredDataValues.reduce((partialSum, a) => partialSum + a, 0),
|
|
301
479
|
columnSd: Number(d3.deviation(filteredDataValues)).toFixed(newConfig.dataFormat.roundTo),
|
|
302
480
|
columnMean: Number(d3.mean(filteredDataValues)).toFixed(newConfig.dataFormat.roundTo),
|
|
303
481
|
columnIqr: Number(iqr).toFixed(newConfig.dataFormat.roundTo),
|
|
482
|
+
columnLowerBounds: d3.max([d3.min(filteredDataValues), q1 - 1.5 * iqr]),
|
|
483
|
+
columnUpperBounds: d3.min([d3.max(sortedData), q1 + 1.5 * iqr]),
|
|
304
484
|
columnOutliers: outliers,
|
|
305
485
|
values: filteredDataValues,
|
|
306
486
|
nonOutlierValues: nonOutliers
|
|
@@ -316,6 +496,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
316
496
|
tableData.map(table => {
|
|
317
497
|
delete table.columnIqr
|
|
318
498
|
delete table.nonOutlierValues
|
|
499
|
+
delete table.columnLowerBounds
|
|
500
|
+
delete table.columnUpperBounds
|
|
319
501
|
return null // resolve eslint
|
|
320
502
|
})
|
|
321
503
|
|
|
@@ -329,7 +511,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
329
511
|
if (newConfig.visualizationType === 'Combo' && newConfig.series) {
|
|
330
512
|
newConfig.runtime.barSeriesKeys = []
|
|
331
513
|
newConfig.runtime.lineSeriesKeys = []
|
|
514
|
+
newConfig.runtime.areaSeriesKeys = []
|
|
515
|
+
|
|
332
516
|
newConfig.series.forEach(series => {
|
|
517
|
+
if (series.type === 'Area Chart') {
|
|
518
|
+
newConfig.runtime.areaSeriesKeys.push(series)
|
|
519
|
+
}
|
|
333
520
|
if (series.type === 'Bar') {
|
|
334
521
|
newConfig.runtime.barSeriesKeys.push(series.dataKey)
|
|
335
522
|
}
|
|
@@ -338,6 +525,15 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
338
525
|
}
|
|
339
526
|
})
|
|
340
527
|
}
|
|
528
|
+
if (newConfig.visualizationType === 'Area Chart' && newConfig.series) {
|
|
529
|
+
newConfig.runtime.areaSeriesKeys = []
|
|
530
|
+
|
|
531
|
+
newConfig.series.forEach(series => {
|
|
532
|
+
if (series.type === 'Area Chart') {
|
|
533
|
+
newConfig.runtime.areaSeriesKeys.push(series)
|
|
534
|
+
}
|
|
535
|
+
})
|
|
536
|
+
}
|
|
341
537
|
|
|
342
538
|
if (((newConfig.visualizationType === 'Bar' || newConfig.visualizationType === 'Deviation Bar') && newConfig.orientation === 'horizontal') || newConfig.visualizationType === 'Paired Bar') {
|
|
343
539
|
newConfig.runtime.xAxis = newConfig.yAxis
|
|
@@ -359,18 +555,21 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
359
555
|
|
|
360
556
|
data.forEach(row => {
|
|
361
557
|
let add = true
|
|
362
|
-
filters
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
558
|
+
filters
|
|
559
|
+
.filter(filter => filter.type !== 'url')
|
|
560
|
+
.forEach(filter => {
|
|
561
|
+
if (row[filter.columnName] != filter.active) {
|
|
562
|
+
add = false
|
|
563
|
+
}
|
|
564
|
+
})
|
|
367
565
|
|
|
368
566
|
if (add) filteredData.push(row)
|
|
369
567
|
})
|
|
568
|
+
|
|
370
569
|
return filteredData
|
|
371
570
|
}
|
|
372
571
|
|
|
373
|
-
// Gets
|
|
572
|
+
// Gets filter values from dataset
|
|
374
573
|
const generateValuesForFilter = (columnName, data = this.state.data) => {
|
|
375
574
|
const values = []
|
|
376
575
|
|
|
@@ -441,6 +640,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
441
640
|
loadConfig()
|
|
442
641
|
}, []) // eslint-disable-line
|
|
443
642
|
|
|
643
|
+
useEffect(() => {
|
|
644
|
+
reloadURLData()
|
|
645
|
+
}, [JSON.stringify(config.filters)])
|
|
646
|
+
|
|
444
647
|
/**
|
|
445
648
|
* When cove has a config and container ref publish the cove_loaded event.
|
|
446
649
|
*/
|
|
@@ -575,10 +778,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
575
778
|
|
|
576
779
|
const section = config.orientation === 'horizontal' ? 'yAxis' : 'xAxis'
|
|
577
780
|
|
|
578
|
-
const parseDate = dateString => {
|
|
781
|
+
const parseDate = (dateString, showError = true) => {
|
|
579
782
|
let date = timeParse(config.runtime[section].dateParseFormat)(dateString)
|
|
580
783
|
if (!date) {
|
|
581
|
-
|
|
784
|
+
if (showError) {
|
|
785
|
+
config.runtime.editorErrorMessage = `Error parsing date "${dateString}". Try reviewing your data and date parse settings in the X Axis section.`
|
|
786
|
+
}
|
|
582
787
|
return new Date()
|
|
583
788
|
} else {
|
|
584
789
|
return date
|
|
@@ -599,8 +804,26 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
599
804
|
return Math.ceil(context.measureText(text).width)
|
|
600
805
|
}
|
|
601
806
|
|
|
807
|
+
const abbreviateNumber = num => {
|
|
808
|
+
let unit = ''
|
|
809
|
+
let absNum = Math.abs(num)
|
|
810
|
+
|
|
811
|
+
if (absNum >= 1e9) {
|
|
812
|
+
unit = 'B'
|
|
813
|
+
num = num / 1e9
|
|
814
|
+
} else if (absNum >= 1e6) {
|
|
815
|
+
unit = 'M'
|
|
816
|
+
num = num / 1e6
|
|
817
|
+
} else if (absNum >= 1e3) {
|
|
818
|
+
unit = 'K'
|
|
819
|
+
num = num / 1e3
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
return num + unit
|
|
823
|
+
}
|
|
824
|
+
|
|
602
825
|
// Format numeric data based on settings in config
|
|
603
|
-
const formatNumber = (num, axis) => {
|
|
826
|
+
const formatNumber = (num, axis, shouldAbbreviate = false) => {
|
|
604
827
|
// if num is NaN return num
|
|
605
828
|
if (isNaN(num) || !num) return num
|
|
606
829
|
// Check if the input number is negative
|
|
@@ -616,8 +839,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
616
839
|
dataFormat: { commas, abbreviated, roundTo, prefix, suffix, rightRoundTo, bottomRoundTo, rightPrefix, rightSuffix, bottomPrefix, bottomSuffix, bottomAbbreviated }
|
|
617
840
|
} = config
|
|
618
841
|
|
|
619
|
-
let formatSuffix = format('.2s')
|
|
620
|
-
|
|
621
842
|
// check if value contains comma and remove it. later will add comma below.
|
|
622
843
|
if (String(num).indexOf(',') !== -1) num = num.replaceAll(',', '')
|
|
623
844
|
|
|
@@ -670,19 +891,20 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
670
891
|
//
|
|
671
892
|
// Edge case for small numbers with decimals
|
|
672
893
|
// - if roundTo undefined which means it is blank, then do not round
|
|
673
|
-
|
|
894
|
+
|
|
895
|
+
if ((axis === 'left' && commas && abbreviated && shouldAbbreviate) || (axis === 'bottom' && commas && abbreviated && shouldAbbreviate)) {
|
|
674
896
|
num = num // eslint-disable-line
|
|
675
897
|
} else {
|
|
676
898
|
num = num.toLocaleString('en-US', stringFormattingOptions)
|
|
677
899
|
}
|
|
678
900
|
let result = ''
|
|
679
901
|
|
|
680
|
-
if (abbreviated && axis === 'left') {
|
|
681
|
-
num =
|
|
902
|
+
if (abbreviated && axis === 'left' && shouldAbbreviate) {
|
|
903
|
+
num = abbreviateNumber(parseFloat(num))
|
|
682
904
|
}
|
|
683
905
|
|
|
684
|
-
if (bottomAbbreviated && axis === 'bottom') {
|
|
685
|
-
num =
|
|
906
|
+
if (bottomAbbreviated && axis === 'bottom' && shouldAbbreviate) {
|
|
907
|
+
num = abbreviateNumber(parseFloat(num))
|
|
686
908
|
}
|
|
687
909
|
|
|
688
910
|
if (prefix && axis === 'left') {
|
|
@@ -748,17 +970,128 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
748
970
|
return false
|
|
749
971
|
}
|
|
750
972
|
|
|
973
|
+
// used for Additional Column
|
|
974
|
+
const displayDataAsText = (value, columnName) => {
|
|
975
|
+
if (value === null || value === '' || value === undefined) {
|
|
976
|
+
return ''
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
if (typeof value === 'string' && value.length > 0 && config.legend.type === 'equalnumber') {
|
|
980
|
+
return value
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
let formattedValue = value
|
|
984
|
+
|
|
985
|
+
let columnObj //= config.columns[columnName]
|
|
986
|
+
// config.columns not an array but a hash of objects
|
|
987
|
+
if (Object.keys(config.columns).length > 0) {
|
|
988
|
+
Object.keys(config.columns).forEach(function (key) {
|
|
989
|
+
var column = config.columns[key]
|
|
990
|
+
// add if not the index AND it is enabled to be added to data table
|
|
991
|
+
if (column.name === columnName) {
|
|
992
|
+
columnObj = column
|
|
993
|
+
}
|
|
994
|
+
})
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
if (columnObj === undefined) {
|
|
998
|
+
// then use left axis config
|
|
999
|
+
columnObj = config.type === 'chart' ? config.dataFormat : config.primary
|
|
1000
|
+
// NOTE: Left Value Axis uses different names
|
|
1001
|
+
// so map them below so the code below works
|
|
1002
|
+
// - copy commas to useCommas to work below
|
|
1003
|
+
columnObj['useCommas'] = columnObj.commas
|
|
1004
|
+
// - copy roundTo to roundToPlace to work below
|
|
1005
|
+
columnObj['roundToPlace'] = columnObj.roundTo ? columnObj.roundTo : ''
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
if (columnObj) {
|
|
1009
|
+
// If value is a number, apply specific formattings
|
|
1010
|
+
let hasDecimal = false
|
|
1011
|
+
let decimalPoint = 0
|
|
1012
|
+
if (Number(value)) {
|
|
1013
|
+
if (columnObj.roundToPlace >= 0) {
|
|
1014
|
+
hasDecimal = columnObj.roundToPlace ? columnObj.roundToPlace !== '' || columnObj.roundToPlace !== null : false
|
|
1015
|
+
decimalPoint = columnObj.roundToPlace ? Number(columnObj.roundToPlace) : 0
|
|
1016
|
+
|
|
1017
|
+
// Rounding
|
|
1018
|
+
if (columnObj.hasOwnProperty('roundToPlace') && hasDecimal) {
|
|
1019
|
+
formattedValue = Number(value).toFixed(decimalPoint)
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
if (columnObj.hasOwnProperty('useCommas') && columnObj.useCommas === true) {
|
|
1024
|
+
// Formats number to string with commas - allows up to 5 decimal places, if rounding is not defined.
|
|
1025
|
+
// Otherwise, uses the rounding value set at 'columnObj.roundToPlace'.
|
|
1026
|
+
formattedValue = Number(value).toLocaleString('en-US', {
|
|
1027
|
+
style: 'decimal',
|
|
1028
|
+
minimumFractionDigits: hasDecimal ? decimalPoint : 0,
|
|
1029
|
+
maximumFractionDigits: hasDecimal ? decimalPoint : 5
|
|
1030
|
+
})
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// add prefix and suffix if set
|
|
1035
|
+
formattedValue = (columnObj.prefix || '') + formattedValue + (columnObj.suffix || '')
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
return formattedValue
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// this is passed DOWN into the various components
|
|
1042
|
+
// then they do a lookup based on the bin number as index into here (TT)
|
|
1043
|
+
const applyLegendToRow = rowObj => {
|
|
1044
|
+
try {
|
|
1045
|
+
if (!rowObj) throw new Error('COVE: No rowObj in applyLegendToRow')
|
|
1046
|
+
// Navigation map
|
|
1047
|
+
if ('navigation' === config.type) {
|
|
1048
|
+
let mapColorPalette = colorPalettes[config.color] || colorPalettes['bluegreenreverse']
|
|
1049
|
+
return generateColorsArray(mapColorPalette[3])
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
let hash = hashObj(rowObj)
|
|
1053
|
+
|
|
1054
|
+
if (legendMemo.current.has(hash)) {
|
|
1055
|
+
let idx = legendMemo.current.get(hash)
|
|
1056
|
+
if (runtimeLegend[idx]?.disabled) return false
|
|
1057
|
+
|
|
1058
|
+
// DEV-784 changed to use bin prop to get color instead of idx
|
|
1059
|
+
// bc we re-order legend when showSpecialClassesLast is checked
|
|
1060
|
+
let legendBinColor = runtimeLegend.find(o => o.bin === idx)?.color
|
|
1061
|
+
return generateColorsArray(legendBinColor, runtimeLegend[idx]?.special)
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
// Fail state
|
|
1065
|
+
return generateColorsArray()
|
|
1066
|
+
} catch (e) {
|
|
1067
|
+
console.error('COVE: ', e) // eslint-disable-line
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
const clean = data => {
|
|
1072
|
+
return config?.xAxis?.dataKey ? transform.cleanData(data, config.xAxis.dataKey) : data
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// required for DataTable
|
|
1076
|
+
const displayGeoName = key => {
|
|
1077
|
+
return key
|
|
1078
|
+
}
|
|
1079
|
+
|
|
751
1080
|
// Prevent render if loading
|
|
752
1081
|
let body = <Loading />
|
|
753
1082
|
|
|
754
1083
|
if (!loading) {
|
|
1084
|
+
const tableLink = (
|
|
1085
|
+
<a href={`#data-table-${config.dataKey}`} className='margin-left-href'>
|
|
1086
|
+
{config.dataKey} (Go to Table)
|
|
1087
|
+
</a>
|
|
1088
|
+
)
|
|
755
1089
|
body = (
|
|
756
1090
|
<>
|
|
757
1091
|
{isEditor && <EditorPanel />}
|
|
758
1092
|
{!missingRequiredSections() && !config.newViz && (
|
|
759
1093
|
<div className='cdc-chart-inner-container'>
|
|
760
1094
|
{/* Title */}
|
|
761
|
-
|
|
762
1095
|
{title && config.showTitle && (
|
|
763
1096
|
<div role='heading' className={`chart-title ${config.theme} cove-component__header`} aria-level={2}>
|
|
764
1097
|
{config && <sup className='superTitle'>{parse(config.superTitle || '')}</sup>}
|
|
@@ -769,11 +1102,11 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
769
1102
|
Skip Over Chart Container
|
|
770
1103
|
</a>
|
|
771
1104
|
{/* Filters */}
|
|
772
|
-
{config.filters && !externalFilters && <Filters />}
|
|
1105
|
+
{config.filters && !externalFilters && <Filters config={config} setConfig={setConfig} setFilteredData={setFilteredData} filteredData={filteredData} excludedData={excludedData} filterData={filterData} isNumber={isNumber} dimensions={dimensions} />}
|
|
773
1106
|
{/* Visualization */}
|
|
774
1107
|
{config?.introText && <section className='introText'>{parse(config.introText)}</section>}
|
|
775
1108
|
<div
|
|
776
|
-
style={{ marginBottom: config.legend.position !== 'bottom' &&
|
|
1109
|
+
style={{ marginBottom: config.legend.position !== 'bottom' && config.orientation === 'horizontal' ? `${config.runtime.xAxis.size}px` : '0px' }}
|
|
777
1110
|
className={`chart-container ${config.legend.position === 'bottom' ? 'bottom' : ''}${config.legend.hide ? ' legend-hidden' : ''}${lineDatapointClass}${barBorderClass} ${contentClasses.join(' ')}`}
|
|
778
1111
|
>
|
|
779
1112
|
{/* All charts except sparkline */}
|
|
@@ -797,7 +1130,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
797
1130
|
{!config.legend.hide && config.visualizationType !== 'Spark Line' && <Legend />}
|
|
798
1131
|
</div>
|
|
799
1132
|
{/* Link */}
|
|
800
|
-
{link && link}
|
|
1133
|
+
{isDashboard && config.table && config.table.show && config.table.showDataTableLink ? tableLink : link && link}
|
|
1134
|
+
|
|
801
1135
|
{/* Description */}
|
|
802
1136
|
{description && config.visualizationType !== 'Spark Line' && <div className='subtext'>{parse(description)}</div>}
|
|
803
1137
|
|
|
@@ -808,7 +1142,36 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
808
1142
|
</CoveMediaControls.Section>
|
|
809
1143
|
|
|
810
1144
|
{/* Data Table */}
|
|
811
|
-
{config.xAxis.dataKey && config.table.show && config.visualizationType !== 'Spark Line' &&
|
|
1145
|
+
{config.xAxis.dataKey && config.table.show && config.visualizationType !== 'Spark Line' && (
|
|
1146
|
+
<DataTable
|
|
1147
|
+
config={config}
|
|
1148
|
+
rawData={config.data}
|
|
1149
|
+
runtimeData={filteredData || excludedData}
|
|
1150
|
+
//navigationHandler={navigationHandler}
|
|
1151
|
+
expandDataTable={config.table.expanded}
|
|
1152
|
+
//headerColor={general.headerColor}
|
|
1153
|
+
columns={config.columns}
|
|
1154
|
+
showDownloadButton={config.general.showDownloadButton}
|
|
1155
|
+
runtimeLegend={dynamicLegendItems}
|
|
1156
|
+
displayDataAsText={displayDataAsText}
|
|
1157
|
+
displayGeoName={displayGeoName}
|
|
1158
|
+
applyLegendToRow={applyLegendToRow}
|
|
1159
|
+
tableTitle={config.table.label}
|
|
1160
|
+
indexTitle={config.table.indexLabel}
|
|
1161
|
+
vizTitle={title}
|
|
1162
|
+
viewport={currentViewport}
|
|
1163
|
+
parseDate={parseDate}
|
|
1164
|
+
formatDate={formatDate}
|
|
1165
|
+
formatNumber={formatNumber}
|
|
1166
|
+
tabbingId={handleChartTabbing}
|
|
1167
|
+
showDownloadImgButton={config.showDownloadImgButton}
|
|
1168
|
+
showDownloadPdfButton={config.showDownloadPdfButton}
|
|
1169
|
+
innerContainerRef={innerContainerRef}
|
|
1170
|
+
outerContainerRef={outerContainerRef}
|
|
1171
|
+
imageRef={imageId}
|
|
1172
|
+
isDebug={isDebug}
|
|
1173
|
+
/>
|
|
1174
|
+
)}
|
|
812
1175
|
{config?.footnotes && <section className='footnotes'>{parse(config.footnotes)}</section>}
|
|
813
1176
|
{/* show pdf or image button */}
|
|
814
1177
|
</div>
|
|
@@ -827,7 +1190,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
827
1190
|
setConfig,
|
|
828
1191
|
rawData: stateData ?? {},
|
|
829
1192
|
excludedData: excludedData,
|
|
830
|
-
transformedData: filteredData || excludedData,
|
|
1193
|
+
transformedData: clean(filteredData || excludedData), // do this right before passing to components
|
|
1194
|
+
tableData: filteredData || excludedData, // do not clean table data
|
|
831
1195
|
unfilteredData: stateData,
|
|
832
1196
|
seriesHighlight,
|
|
833
1197
|
colorScale,
|
|
@@ -854,10 +1218,11 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
854
1218
|
filterData,
|
|
855
1219
|
imageId,
|
|
856
1220
|
handleLineType,
|
|
1221
|
+
lineOptions,
|
|
857
1222
|
isNumber,
|
|
858
|
-
cleanData,
|
|
859
1223
|
getTextWidth,
|
|
860
|
-
twoColorPalette
|
|
1224
|
+
twoColorPalette,
|
|
1225
|
+
isDebug
|
|
861
1226
|
}
|
|
862
1227
|
|
|
863
1228
|
const classes = ['cdc-open-viz-module', 'type-chart', `${currentViewport}`, `font-${config.fontSize}`, `${config.theme}`]
|