@cdc/core 4.23.4 → 4.23.6
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/assets/external-link.svg +1 -0
- package/assets/icon-linear-gauge.svg +1 -0
- package/components/AdvancedEditor.jsx +7 -1
- package/components/DataTable.jsx +657 -0
- package/components/Filters.jsx +62 -7
- package/components/LegendCircle.jsx +2 -2
- package/components/{CoveMediaControls.jsx → MediaControls.jsx} +6 -6
- package/components/RepeatableGroup.jsx +37 -0
- package/components/inputs/InputSelect.jsx +1 -1
- package/components/ui/Accordion.jsx +3 -0
- package/data/colorPalettes.js +3 -3
- package/helpers/DataTransform.js +65 -65
- package/helpers/cove/date.js +9 -0
- package/helpers/cove/number.js +139 -0
- package/helpers/coveUpdateWorker.js +15 -0
- package/helpers/fetchRemoteData.js +12 -2
- package/helpers/validateFipsCodeLength.js +1 -1
- package/helpers/ver/4.23.js +10 -0
- package/package.json +2 -2
- package/styles/_data-table.scss +10 -0
- package/styles/v2/components/input/_input.scss +3 -3
- package/styles/v2/layout/_component.scss +1 -1
package/components/Filters.jsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react'
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react'
|
|
2
|
+
import { useId } from 'react'
|
|
2
3
|
|
|
3
4
|
// CDC
|
|
4
5
|
import Button from '@cdc/core/components/elements/Button'
|
|
@@ -72,7 +73,9 @@ export const useFilters = props => {
|
|
|
72
73
|
|
|
73
74
|
const changeFilterActive = (index, value) => {
|
|
74
75
|
let newFilters = visualizationConfig.type === 'map' ? [...filteredData] : [...visualizationConfig.filters]
|
|
76
|
+
|
|
75
77
|
newFilters[index].active = value
|
|
78
|
+
setConfig({ ...visualizationConfig })
|
|
76
79
|
|
|
77
80
|
// If this is a button filter type show the button.
|
|
78
81
|
if (visualizationConfig.filterBehavior === 'Apply Button') {
|
|
@@ -183,6 +186,8 @@ const Filters = props => {
|
|
|
183
186
|
const { config: visualizationConfig, filteredData, dimensions } = props
|
|
184
187
|
const { filters, type, general, theme, filterBehavior } = visualizationConfig
|
|
185
188
|
const [mobileFilterStyle, setMobileFilterStyle] = useState(false)
|
|
189
|
+
const [selectedFilter, setSelectedFilter] = useState('')
|
|
190
|
+
const id = useId()
|
|
186
191
|
|
|
187
192
|
// useFilters hook provides data and logic for handling various filter functions
|
|
188
193
|
// prettier-ignore
|
|
@@ -205,6 +210,13 @@ const Filters = props => {
|
|
|
205
210
|
}
|
|
206
211
|
}, [dimensions])
|
|
207
212
|
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
if (selectedFilter) {
|
|
215
|
+
let el = document.getElementById(selectedFilter.id)
|
|
216
|
+
if (el) el.focus()
|
|
217
|
+
}
|
|
218
|
+
}, [changeFilterActive, selectedFilter])
|
|
219
|
+
|
|
208
220
|
const Filters = props => props.children
|
|
209
221
|
|
|
210
222
|
const filterSectionClassList = ['filters-section', type === 'map' ? general.headerColor : theme]
|
|
@@ -241,10 +253,24 @@ const Filters = props => {
|
|
|
241
253
|
const { filter: singleFilter, index: outerIndex } = props
|
|
242
254
|
return (
|
|
243
255
|
<section className='single-filters__tab-bar'>
|
|
244
|
-
{singleFilter.values.map(filter => {
|
|
256
|
+
{singleFilter.values.map((filter, index) => {
|
|
245
257
|
const buttonClassList = ['button__tab-bar', singleFilter.active === filter ? 'button__tab-bar--active' : '']
|
|
246
258
|
return (
|
|
247
|
-
<button
|
|
259
|
+
<button
|
|
260
|
+
id={`${filter}-${outerIndex}-${index}-${id}`}
|
|
261
|
+
className={buttonClassList.join(' ')}
|
|
262
|
+
key={filter}
|
|
263
|
+
onClick={e => {
|
|
264
|
+
changeFilterActive(outerIndex, filter)
|
|
265
|
+
setSelectedFilter(e.target)
|
|
266
|
+
}}
|
|
267
|
+
onKeyDown={e => {
|
|
268
|
+
if (e.keyCode === 13) {
|
|
269
|
+
changeFilterActive(outerIndex, filter)
|
|
270
|
+
setSelectedFilter(e.target)
|
|
271
|
+
}
|
|
272
|
+
}}
|
|
273
|
+
>
|
|
248
274
|
{filter}
|
|
249
275
|
</button>
|
|
250
276
|
)
|
|
@@ -286,6 +312,8 @@ const Filters = props => {
|
|
|
286
312
|
delete filtersToLoop.fromHash
|
|
287
313
|
|
|
288
314
|
return filtersToLoop.map((singleFilter, outerIndex) => {
|
|
315
|
+
if(singleFilter.showDropdown === false) return
|
|
316
|
+
|
|
289
317
|
const values = []
|
|
290
318
|
const pillValues = []
|
|
291
319
|
const tabValues = []
|
|
@@ -300,8 +328,22 @@ const Filters = props => {
|
|
|
300
328
|
const tabClassList = ['tab', active === filterOption && 'tab--active', theme && theme]
|
|
301
329
|
|
|
302
330
|
pillValues.push(
|
|
303
|
-
<div className='pill__wrapper'>
|
|
304
|
-
<button
|
|
331
|
+
<div className='pill__wrapper' key={`pill-${index}`}>
|
|
332
|
+
<button
|
|
333
|
+
id={`${filterOption}-${outerIndex}-${index}-${id}`}
|
|
334
|
+
className={pillClassList.join(' ')}
|
|
335
|
+
onKeyDown={e => {
|
|
336
|
+
if (e.keyCode === 13) {
|
|
337
|
+
changeFilterActive(outerIndex, filterOption)
|
|
338
|
+
setSelectedFilter(e.target)
|
|
339
|
+
}
|
|
340
|
+
}}
|
|
341
|
+
onClick={e => {
|
|
342
|
+
changeFilterActive(outerIndex, filterOption)
|
|
343
|
+
setSelectedFilter(e.target)
|
|
344
|
+
}}
|
|
345
|
+
name={label}
|
|
346
|
+
>
|
|
305
347
|
{filterOption}
|
|
306
348
|
</button>
|
|
307
349
|
</div>
|
|
@@ -309,12 +351,25 @@ const Filters = props => {
|
|
|
309
351
|
|
|
310
352
|
values.push(
|
|
311
353
|
<option key={index} value={filterOption}>
|
|
312
|
-
{filterOption}
|
|
354
|
+
{singleFilter.labels && singleFilter.labels[filterOption] ? singleFilter.labels[filterOption] : filterOption}
|
|
313
355
|
</option>
|
|
314
356
|
)
|
|
315
357
|
|
|
316
358
|
tabValues.push(
|
|
317
|
-
<button
|
|
359
|
+
<button
|
|
360
|
+
id={`${filterOption}-${outerIndex}-${index}-${id}`}
|
|
361
|
+
className={tabClassList.join(' ')}
|
|
362
|
+
onClick={e => {
|
|
363
|
+
changeFilterActive(outerIndex, filterOption)
|
|
364
|
+
setSelectedFilter(e.target)
|
|
365
|
+
}}
|
|
366
|
+
onKeyDown={e => {
|
|
367
|
+
if (e.keyCode === 13) {
|
|
368
|
+
changeFilterActive(outerIndex, filterOption)
|
|
369
|
+
setSelectedFilter(e.target)
|
|
370
|
+
}
|
|
371
|
+
}}
|
|
372
|
+
>
|
|
318
373
|
{filterOption}
|
|
319
374
|
</button>
|
|
320
375
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
-
export default function LegendCircle({ fill }) {
|
|
3
|
+
export default function LegendCircle({ fill, borderColor }) {
|
|
4
4
|
const styles = {
|
|
5
5
|
marginRight: '5px',
|
|
6
6
|
borderRadius: '300px',
|
|
@@ -8,7 +8,7 @@ export default function LegendCircle({ fill }) {
|
|
|
8
8
|
display: 'inline-block',
|
|
9
9
|
height: '1em',
|
|
10
10
|
width: '1em',
|
|
11
|
-
border: 'rgba(0,0,0,.3) 1px solid',
|
|
11
|
+
border: borderColor ? `${borderColor} 1px solid` : 'rgba(0,0,0,.3) 1px solid',
|
|
12
12
|
backgroundColor: fill
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -134,11 +134,11 @@ const Section = ({ children, classes }) => {
|
|
|
134
134
|
)
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
const
|
|
137
|
+
const MediaControls = () => null
|
|
138
138
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
MediaControls.Section = Section
|
|
140
|
+
MediaControls.Link = Link
|
|
141
|
+
MediaControls.Button = Button
|
|
142
|
+
MediaControls.generateMedia = generateMedia
|
|
143
143
|
|
|
144
|
-
export default
|
|
144
|
+
export default MediaControls
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Accordion, AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
|
|
3
|
+
import { Draggable } from '@hello-pangea/dnd'
|
|
4
|
+
import Icon from '@cdc/core/components/ui/Icon'
|
|
5
|
+
|
|
6
|
+
const RepeatableGroupSection = props => {
|
|
7
|
+
return <div className='repeatable-group'>{props.children}</div>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const RepeatableGroupItem = props => {
|
|
11
|
+
const { item, index, title } = props
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<Draggable key={item} draggableId={`draggable-repeatable-item-${item}`} index={index}>
|
|
15
|
+
{(provided, snapshot) => (
|
|
16
|
+
<div key={index} className={snapshot.isDragging ? 'currently-dragging' : ''} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
|
|
17
|
+
<Accordion allowZeroExpanded>
|
|
18
|
+
<AccordionItem className='series-item series-item--chart'>
|
|
19
|
+
<AccordionItemHeading className='series-item__title'>
|
|
20
|
+
<AccordionItemButton className={''}>
|
|
21
|
+
<Icon display='move' size={15} style={{ cursor: 'default' }} />
|
|
22
|
+
Title Section
|
|
23
|
+
</AccordionItemButton>
|
|
24
|
+
</AccordionItemHeading>
|
|
25
|
+
<AccordionItemPanel>panel section to be built</AccordionItemPanel>
|
|
26
|
+
</AccordionItem>
|
|
27
|
+
</Accordion>
|
|
28
|
+
</div>
|
|
29
|
+
)}
|
|
30
|
+
</Draggable>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const RepeatableGroup = {
|
|
35
|
+
Section: RepeatableGroupSection,
|
|
36
|
+
Item: RepeatableGroupItem
|
|
37
|
+
}
|
|
@@ -33,7 +33,7 @@ const InputSelect = memo(({ label, value, options, fieldName, section = null, su
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
return (
|
|
36
|
-
<label>
|
|
36
|
+
<label style={{ width: '100%', display: 'block' }}>
|
|
37
37
|
{label && <span className='edit-label cove-input__label'>{label}</span>}
|
|
38
38
|
<select
|
|
39
39
|
className={required && !value ? 'warning' : ''}
|
|
@@ -21,6 +21,7 @@ const Accordion = ({ children }) => {
|
|
|
21
21
|
<AccordionItem className='cove-accordion__item' key={index}>
|
|
22
22
|
<AccordionItemHeading className='cove-accordion__heading'>
|
|
23
23
|
<AccordionItemButton className='cove-accordion__button'>
|
|
24
|
+
{section.props.icon}
|
|
24
25
|
{section.props.title}
|
|
25
26
|
{section.props.tooltipText ? (
|
|
26
27
|
<Tooltip position='bottom'>
|
|
@@ -43,6 +44,8 @@ const Accordion = ({ children }) => {
|
|
|
43
44
|
Accordion.Section = AccordionSection
|
|
44
45
|
|
|
45
46
|
Accordion.Section.propTypes = {
|
|
47
|
+
/* Icon for the accordion label */
|
|
48
|
+
icon: PropTypes.node,
|
|
46
49
|
/* Title for the accordion label*/
|
|
47
50
|
title: PropTypes.string,
|
|
48
51
|
/* Tooltip for the accordion label*/
|
package/data/colorPalettes.js
CHANGED
|
@@ -51,16 +51,16 @@ export const colorPalettes3 = {
|
|
|
51
51
|
'monochrome-4': ['#C2C0FC', '#6a3d9a'],
|
|
52
52
|
'monochrome-5': ['#fedab8', '#bf5b17'],
|
|
53
53
|
'cool-1': ['#b2df8a', '#1f78b4'],
|
|
54
|
-
'cool-2': ['#a6cee3', '#
|
|
54
|
+
'cool-2': ['#a6cee3', '#72D66B'],
|
|
55
55
|
'cool-3': ['#C2C0FC', '#386cb0'],
|
|
56
|
-
'cool-4': ['#
|
|
56
|
+
'cool-4': ['#72D66B', '#6a3d9a'],
|
|
57
57
|
'cool-5': ['#a6cee3', '#6a3d9a'],
|
|
58
58
|
'warm-1': ['#e31a1c', '#fedab8'],
|
|
59
59
|
'complementary-1': ['#1f78b4', '#e6ab02'],
|
|
60
60
|
'complementary-2': ['#1f78b4', '#ff7f00'],
|
|
61
61
|
'complementary-3': ['#6a3d9a', '#ff7f00'],
|
|
62
62
|
'complementary-4': ['#6a3d9a', '#e6ab02'],
|
|
63
|
-
'complementary-5': ['#
|
|
63
|
+
'complementary-5': ['#df168c', '#1EB386']
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
export const colorPalettesChart = updatePaletteNames(colorPalettes2)
|
package/helpers/DataTransform.js
CHANGED
|
@@ -11,109 +11,109 @@ export class DataTransform {
|
|
|
11
11
|
|
|
12
12
|
//Performs standardizations that can be completed automatically without use input
|
|
13
13
|
autoStandardize(data) {
|
|
14
|
-
const errorsFound = []
|
|
14
|
+
const errorsFound = []
|
|
15
15
|
|
|
16
16
|
// Empty data
|
|
17
17
|
if (0 === data.length) {
|
|
18
|
-
errorsFound.push(this.constants.errorMessageEmptyData)
|
|
18
|
+
errorsFound.push(this.constants.errorMessageEmptyData)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// Does it have the correct data structure?
|
|
22
22
|
if (!data.filter || data.filter(row => typeof row !== 'object').length > 0) {
|
|
23
|
-
errorsFound.push(this.constants.errorMessageFormat)
|
|
23
|
+
errorsFound.push(this.constants.errorMessageFormat)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
if (errorsFound.length > 0) {
|
|
27
|
-
console.error(errorsFound)
|
|
28
|
-
return undefined
|
|
27
|
+
console.error(errorsFound)
|
|
28
|
+
return undefined
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
//Convert array of arrays, to array of objects
|
|
32
32
|
if (data.filter(row => row.constructor !== Object).length > 0) {
|
|
33
|
-
let standardizedData = []
|
|
33
|
+
let standardizedData = []
|
|
34
34
|
for (let row = 1; row < data.length; row++) {
|
|
35
|
-
let standardizedRow = {}
|
|
35
|
+
let standardizedRow = {}
|
|
36
36
|
data[row].forEach((datum, col) => {
|
|
37
|
-
standardizedRow[data[0][col]] = datum
|
|
37
|
+
standardizedRow[data[0][col]] = datum
|
|
38
38
|
})
|
|
39
|
-
standardizedData.push(standardizedRow)
|
|
39
|
+
standardizedData.push(standardizedRow)
|
|
40
40
|
}
|
|
41
|
-
data = standardizedData
|
|
41
|
+
data = standardizedData
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
return data
|
|
44
|
+
return data
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
//Performs standardizations based on developer provided description of the data
|
|
48
48
|
developerStandardize(data, description) {
|
|
49
49
|
//Validate the description object
|
|
50
50
|
if (!description) {
|
|
51
|
-
return undefined
|
|
51
|
+
return undefined
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
if (description.horizontal === undefined || description.series === undefined) {
|
|
55
|
-
return undefined
|
|
55
|
+
return undefined
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
if (description.series === true && description.horizontal === false && description.singleRow === undefined) {
|
|
59
|
-
return undefined
|
|
59
|
+
return undefined
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
if (description.horizontal === true) {
|
|
63
63
|
if (description.series === true) {
|
|
64
64
|
if (!description.seriesKey) {
|
|
65
|
-
return undefined
|
|
65
|
+
return undefined
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
let standardizedMapped = {}
|
|
69
|
-
let standardized = []
|
|
68
|
+
let standardizedMapped = {}
|
|
69
|
+
let standardized = []
|
|
70
70
|
data.forEach(row => {
|
|
71
|
-
let nonNumericKeys = []
|
|
71
|
+
let nonNumericKeys = []
|
|
72
72
|
Object.keys(row).forEach(key => {
|
|
73
73
|
if (key !== description.seriesKey && isNaN(parseFloat(row[key]))) {
|
|
74
|
-
nonNumericKeys.push(key)
|
|
74
|
+
nonNumericKeys.push(key)
|
|
75
75
|
}
|
|
76
76
|
})
|
|
77
77
|
|
|
78
78
|
Object.keys(row).forEach(key => {
|
|
79
79
|
if (key !== description.seriesKey && nonNumericKeys.indexOf(key) === -1) {
|
|
80
|
-
let uniqueKey = key + '|' + nonNumericKeys.map(nonNumericKey => nonNumericKey + '=' + row[nonNumericKey])
|
|
80
|
+
let uniqueKey = key + '|' + nonNumericKeys.map(nonNumericKey => nonNumericKey + '=' + row[nonNumericKey])
|
|
81
81
|
if (!standardizedMapped[uniqueKey]) {
|
|
82
|
-
standardizedMapped[uniqueKey] = { [row[description.seriesKey]]: row[key], key }
|
|
82
|
+
standardizedMapped[uniqueKey] = { [row[description.seriesKey]]: row[key], key }
|
|
83
83
|
nonNumericKeys.forEach(nonNumericKey => {
|
|
84
|
-
standardizedMapped[uniqueKey][nonNumericKey] = row[nonNumericKey]
|
|
84
|
+
standardizedMapped[uniqueKey][nonNumericKey] = row[nonNumericKey]
|
|
85
85
|
})
|
|
86
86
|
}
|
|
87
|
-
standardizedMapped[uniqueKey][row[description.seriesKey]] = row[key]
|
|
87
|
+
standardizedMapped[uniqueKey][row[description.seriesKey]] = row[key]
|
|
88
88
|
}
|
|
89
89
|
})
|
|
90
90
|
})
|
|
91
91
|
|
|
92
92
|
Object.keys(standardizedMapped).forEach(key => {
|
|
93
|
-
standardized.push(standardizedMapped[key])
|
|
93
|
+
standardized.push(standardizedMapped[key])
|
|
94
94
|
})
|
|
95
95
|
|
|
96
|
-
return standardized
|
|
96
|
+
return standardized
|
|
97
97
|
} else {
|
|
98
|
-
let standardized = []
|
|
98
|
+
let standardized = []
|
|
99
99
|
|
|
100
100
|
data.forEach(row => {
|
|
101
|
-
let nonNumericKeys = []
|
|
101
|
+
let nonNumericKeys = []
|
|
102
102
|
Object.keys(row).forEach(key => {
|
|
103
103
|
if (isNaN(parseFloat(row[key]))) {
|
|
104
|
-
nonNumericKeys.push(key)
|
|
104
|
+
nonNumericKeys.push(key)
|
|
105
105
|
}
|
|
106
106
|
})
|
|
107
107
|
|
|
108
108
|
Object.keys(row).forEach(key => {
|
|
109
109
|
if (nonNumericKeys.indexOf(key) === -1) {
|
|
110
|
-
let newRow = { key, value: row[key] }
|
|
110
|
+
let newRow = { key, value: row[key] }
|
|
111
111
|
|
|
112
112
|
nonNumericKeys.forEach(nonNumericKey => {
|
|
113
|
-
newRow[nonNumericKey] = row[nonNumericKey]
|
|
113
|
+
newRow[nonNumericKey] = row[nonNumericKey]
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
-
standardized.push(newRow)
|
|
116
|
+
standardized.push(newRow)
|
|
117
117
|
}
|
|
118
118
|
})
|
|
119
119
|
})
|
|
@@ -122,43 +122,43 @@ export class DataTransform {
|
|
|
122
122
|
}
|
|
123
123
|
} else if (description.series === true && description.singleRow === false) {
|
|
124
124
|
if (description.seriesKey !== undefined && description.xKey !== undefined && (description.valueKey !== undefined || (description.valueKeys !== undefined && description.valueKeys.length > 0))) {
|
|
125
|
-
if(description.valueKeys !== undefined){
|
|
126
|
-
let standardizedMapped = {}
|
|
127
|
-
let standardized = []
|
|
128
|
-
let valueKeys = description.valueKeys
|
|
129
|
-
if(description.ignoredKeys && description.ignoredKeys.length > 0){
|
|
130
|
-
valueKeys = valueKeys.concat(description.ignoredKeys)
|
|
125
|
+
if (description.valueKeys !== undefined) {
|
|
126
|
+
let standardizedMapped = {}
|
|
127
|
+
let standardized = []
|
|
128
|
+
let valueKeys = description.valueKeys
|
|
129
|
+
if (description.ignoredKeys && description.ignoredKeys.length > 0) {
|
|
130
|
+
valueKeys = valueKeys.concat(description.ignoredKeys)
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
data.forEach(row => {
|
|
134
134
|
valueKeys.forEach(valueKey => {
|
|
135
|
-
let extraKeys = []
|
|
136
|
-
let uniqueKey = row[description.xKey] + '|' + valueKey
|
|
135
|
+
let extraKeys = []
|
|
136
|
+
let uniqueKey = row[description.xKey] + '|' + valueKey
|
|
137
137
|
Object.keys(row).forEach(key => {
|
|
138
138
|
if (key !== description.xKey && key !== description.seriesKey && valueKeys.indexOf(key) === -1) {
|
|
139
|
-
uniqueKey += '|' + key + '=' + row[key]
|
|
140
|
-
extraKeys.push(key)
|
|
139
|
+
uniqueKey += '|' + key + '=' + row[key]
|
|
140
|
+
extraKeys.push(key)
|
|
141
141
|
}
|
|
142
142
|
})
|
|
143
143
|
|
|
144
|
-
if(!standardizedMapped[uniqueKey]){
|
|
145
|
-
standardizedMapped[uniqueKey] = { [description.xKey]: row[description.xKey], '**Numeric Value Property**': valueKey }
|
|
144
|
+
if (!standardizedMapped[uniqueKey]) {
|
|
145
|
+
standardizedMapped[uniqueKey] = { [description.xKey]: row[description.xKey], '**Numeric Value Property**': valueKey }
|
|
146
146
|
extraKeys.forEach(key => {
|
|
147
|
-
standardizedMapped[uniqueKey][key] = row[key]
|
|
147
|
+
standardizedMapped[uniqueKey][key] = row[key]
|
|
148
148
|
})
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
standardizedMapped[uniqueKey][row[description.seriesKey]] = row[valueKey]
|
|
152
|
-
})
|
|
151
|
+
standardizedMapped[uniqueKey][row[description.seriesKey]] = row[valueKey]
|
|
152
|
+
})
|
|
153
153
|
})
|
|
154
154
|
|
|
155
155
|
Object.keys(standardizedMapped).forEach(key => {
|
|
156
|
-
if(!description.ignoredKeys || description.ignoredKeys.indexOf(standardizedMapped[key]['**Numeric Value Property**']) === -1){
|
|
157
|
-
standardized.push(standardizedMapped[key])
|
|
156
|
+
if (!description.ignoredKeys || description.ignoredKeys.indexOf(standardizedMapped[key]['**Numeric Value Property**']) === -1) {
|
|
157
|
+
standardized.push(standardizedMapped[key])
|
|
158
158
|
}
|
|
159
159
|
})
|
|
160
160
|
|
|
161
|
-
return standardized
|
|
161
|
+
return standardized
|
|
162
162
|
} else {
|
|
163
163
|
let standardizedMapped = {}
|
|
164
164
|
let standardized = []
|
|
@@ -190,14 +190,14 @@ export class DataTransform {
|
|
|
190
190
|
return standardized
|
|
191
191
|
}
|
|
192
192
|
} else {
|
|
193
|
-
return undefined
|
|
193
|
+
return undefined
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
return data
|
|
197
|
+
return data
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
/**
|
|
201
201
|
* cleanData
|
|
202
202
|
*
|
|
203
203
|
// This cleans a data set by:
|
|
@@ -207,21 +207,21 @@ export class DataTransform {
|
|
|
207
207
|
*
|
|
208
208
|
* Inputs: data as array, excludeKey indicates which key to use to NOT clean
|
|
209
209
|
* Example: "Date" should not be cleaned if part of the data
|
|
210
|
-
*
|
|
210
|
+
*
|
|
211
211
|
* Output: returns the cleanedData
|
|
212
|
-
*
|
|
212
|
+
*
|
|
213
213
|
* Set testing = true if you need to see before and after data
|
|
214
|
-
*
|
|
214
|
+
*
|
|
215
215
|
*/
|
|
216
|
-
cleanData
|
|
216
|
+
cleanData(data, excludeKey, testing = false) {
|
|
217
217
|
let cleanedupData = []
|
|
218
218
|
if (testing) console.log('## Data to clean=', data)
|
|
219
219
|
if (excludeKey === undefined) {
|
|
220
220
|
console.log('COVE: cleanData excludeKey undefined')
|
|
221
|
-
return data // because no excludeKey
|
|
221
|
+
return data // because no excludeKey
|
|
222
222
|
}
|
|
223
223
|
data.forEach(function (d, i) {
|
|
224
|
-
if (testing) console.log(
|
|
224
|
+
if (testing) console.log('clean', i, ' d', d)
|
|
225
225
|
let cleaned = {}
|
|
226
226
|
Object.keys(d).forEach(function (key) {
|
|
227
227
|
if (key === excludeKey) {
|
|
@@ -229,27 +229,27 @@ export class DataTransform {
|
|
|
229
229
|
cleaned[key] = d[key]
|
|
230
230
|
} else {
|
|
231
231
|
// remove comma and dollar signs
|
|
232
|
-
if (testing) console.log(
|
|
233
|
-
let tmp =
|
|
232
|
+
if (testing) console.log('typeof d[key] is ', typeof d[key])
|
|
233
|
+
let tmp = ''
|
|
234
234
|
if (typeof d[key] === 'string') {
|
|
235
235
|
tmp = d[key] !== null && d[key] !== '' ? d[key].replace(/[,\$]/g, '') : ''
|
|
236
236
|
} else {
|
|
237
|
-
tmp =
|
|
237
|
+
tmp = d[key] !== null && d[key] !== '' ? d[key] : ''
|
|
238
238
|
}
|
|
239
239
|
if ((tmp !== '' && tmp !== null && !isNaN(tmp)) || (tmp !== '' && tmp !== null && /\d+\.?\d*/.test(tmp))) {
|
|
240
240
|
cleaned[key] = tmp
|
|
241
|
-
} else {
|
|
241
|
+
} else {
|
|
242
|
+
cleaned[key] = ''
|
|
243
|
+
}
|
|
242
244
|
// if you get here, then return nothing to skip bad data point
|
|
243
245
|
}
|
|
244
246
|
})
|
|
245
|
-
if (testing) console.log(
|
|
247
|
+
if (testing) console.log('cleaned=', cleaned)
|
|
246
248
|
cleanedupData.push(cleaned)
|
|
247
249
|
})
|
|
248
250
|
if (testing) console.log('## cleanedData =', cleanedupData)
|
|
249
251
|
return cleanedupData
|
|
250
252
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
export default DataTransform
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { timeFormat, timeParse } from 'd3-time-format'
|
|
2
|
+
|
|
3
|
+
export function formatDate(format = undefined, date) {
|
|
4
|
+
return timeFormat(format)(date)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function parseDate(format = undefined, dateString) {
|
|
8
|
+
return timeParse(format)(dateString) || new Date()
|
|
9
|
+
}
|