@cdc/core 4.23.2 → 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/assets/icon-area-chart.svg +1 -0
- package/assets/icon-chart-box-whisker.svg +1 -0
- package/assets/icon-chart-scatterplot.svg +1 -0
- package/components/CoveMediaControls.jsx +6 -2
- package/components/Filters.jsx +368 -0
- package/components/inputs/InputToggle.jsx +7 -1
- package/components/ui/Icon.jsx +2 -1
- package/data/colorPalettes.js +22 -2
- package/helpers/DataTransform.js +54 -0
- package/helpers/isNumberLog.js +3 -3
- package/package.json +2 -2
- package/styles/_global.scss +8 -0
- package/styles/_series-list.scss +92 -0
- package/styles/base.scss +6 -0
- package/styles/filters.scss +122 -0
- package/styles/v2/themes/_color-definitions.scss +49 -0
- package/helpers/cleanData.js +0 -50
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96.31 82.55"><path d="M88.49,65.65H15.29V9.04c0-1.1-.9-2-2-2H7.82c-1.1,0-2,.9-2,2V73.13c0,1.1,.9,2,2,2H88.49c1.1,0,2-.9,2-2v-5.48c0-1.1-.9-2-2-2Z"/><path d="M87.91,60.65c-.02-.12-2.46-12.63-5.53-24.97-5.97-23.99-8.8-26-10.99-26-2.54,0-4.17,3.55-7.92,12.31-.67,1.56-1.4,3.27-2.18,5.02-1.66-4.14-3.14-5.9-4.84-5.9-2.39,0-4.05,3.42-7.07,9.64-5.56,11.44-13.97,28.72-29.12,28.72-.8,0-1.46,.65-1.46,1.45s.65,1.45,1.46,1.45H86.48c.43,0,.85-.19,1.12-.53,.28-.34,.39-.78,.31-1.2Zm-46.88-1.25c8.93-4.92,15.3-15.3,19.83-24.54,2.62,8.88,4.97,20.28,5.83,24.58l-25.66-.04Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96.31 82.55"><path d="M54.68,29.14h-4.1V8.43h2.68c.5,0,.9-.15,1.19-.46,.46-.48,.44-1.11,.42-1.62,0-.11,0-.22,0-.34s0-.24,0-.34c.02-.5,.05-1.13-.42-1.62-.29-.3-.69-.46-1.19-.46h-10.2c-.5,0-.9,.15-1.19,.46-.47,.48-.44,1.11-.42,1.62,0,.11,0,.23,0,.34s0,.23,0,.34c-.02,.5-.05,1.13,.42,1.62,.29,.3,.69,.46,1.19,.46h2.68V29.14h-4.1c-1.33,0-2.42,1.09-2.42,2.42v31.31c0,1.33,1.09,2.42,2.42,2.42h4.1v7.97h-2.68c-.5,0-.9,.15-1.19,.46-.46,.48-.44,1.11-.42,1.62,0,.11,0,.22,0,.34s0,.24,0,.34c-.02,.5-.05,1.13,.42,1.62,.29,.3,.69,.46,1.19,.46h10.2c.5,0,.9-.15,1.19-.46,.47-.48,.44-1.11,.42-1.62,0-.11,0-.23,0-.34s0-.23,0-.34c.02-.5,.05-1.13-.42-1.62-.29-.3-.69-.46-1.19-.46h-2.68v-7.97h4.1c1.33,0,2.42-1.09,2.42-2.42V31.56c0-1.33-1.09-2.42-2.42-2.42Zm-2.42,20.5v10.81h-8.2v-10.81h8.2Zm-8.2-4.84v-10.81h8.2v10.81h-8.2Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96.31 82.55"><path d="M88.49,65.65H15.29V9.04c0-1.1-.9-2-2-2H7.82c-1.1,0-2,.9-2,2V73.13c0,1.1,.9,2,2,2H88.49c1.1,0,2-.9,2-2v-5.48c0-1.1-.9-2-2-2Z"/><g><path d="M30.22,49.19c-1.79,0-3.24,1.45-3.24,3.24s1.45,3.24,3.24,3.24,3.24-1.45,3.24-3.24-1.45-3.24-3.24-3.24Z"/><path d="M33.06,35.59c-1.79,0-3.24,1.45-3.24,3.24s1.45,3.24,3.24,3.24,3.24-1.45,3.24-3.24-1.45-3.24-3.24-3.24Z"/><path d="M48.12,37.83c-1.79,0-3.24,1.45-3.24,3.24s1.45,3.24,3.24,3.24,3.24-1.45,3.24-3.24-1.45-3.24-3.24-3.24Z"/><circle cx="49.64" cy="22.73" r="3.24"/><path d="M64.53,19.49c-1.79,0-3.24,1.45-3.24,3.24s1.45,3.24,3.24,3.24,3.24-1.45,3.24-3.24-1.45-3.24-3.24-3.24Z"/><path d="M57.95,31.11c-1.79,0-3.24,1.45-3.24,3.24s1.45,3.24,3.24,3.24,3.24-1.45,3.24-3.24-1.45-3.24-3.24-3.24Z"/><path d="M74.53,11.54c-1.79,0-3.24,1.45-3.24,3.24s1.45,3.24,3.24,3.24,3.24-1.45,3.24-3.24-1.45-3.24-3.24-3.24Z"/></g></svg>
|
|
@@ -83,7 +83,7 @@ const generateMedia = (state, type, elementToCapture) => {
|
|
|
83
83
|
console.warn('COVE: pdf downloads disabled')
|
|
84
84
|
break
|
|
85
85
|
default:
|
|
86
|
-
console.warn("generateMedia param 2 type must be 'image' or 'pdf'")
|
|
86
|
+
console.warn("COVE: generateMedia param 2 type must be 'image' or 'pdf'")
|
|
87
87
|
break
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -127,7 +127,11 @@ const Link = ({ config }) => {
|
|
|
127
127
|
|
|
128
128
|
// TODO: convert to standardized COVE section
|
|
129
129
|
const Section = ({ children, classes }) => {
|
|
130
|
-
return
|
|
130
|
+
return (
|
|
131
|
+
<section className={classes.join(' ')}>
|
|
132
|
+
<span>{children}</span>
|
|
133
|
+
</section>
|
|
134
|
+
)
|
|
131
135
|
}
|
|
132
136
|
|
|
133
137
|
const CoveMediaControls = () => null
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
// CDC
|
|
4
|
+
import Button from '@cdc/core/components/elements/Button'
|
|
5
|
+
|
|
6
|
+
// Third Party
|
|
7
|
+
import PropTypes from 'prop-types'
|
|
8
|
+
|
|
9
|
+
export const useFilters = props => {
|
|
10
|
+
const [showApplyButton, setShowApplyButton] = useState(false)
|
|
11
|
+
|
|
12
|
+
// Desconstructing: notice, adding more descriptive visualizationConfig name over config
|
|
13
|
+
// visualizationConfig feels more robust for all vis types so that its not confused with config/state/etc.
|
|
14
|
+
const { config: visualizationConfig, setConfig, filteredData, setFilteredData, excludedData, filterData } = props
|
|
15
|
+
const { type, filterBehavior, filters } = visualizationConfig
|
|
16
|
+
|
|
17
|
+
const filterStyleOptions = ['dropdown', 'pill', 'tab', 'tab bar']
|
|
18
|
+
|
|
19
|
+
const filterOrderOptions = [
|
|
20
|
+
{
|
|
21
|
+
label: 'Ascending Alphanumeric',
|
|
22
|
+
value: 'asc'
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: 'Descending Alphanumeric',
|
|
26
|
+
value: 'desc'
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: 'Custom',
|
|
30
|
+
value: 'cust'
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Re-orders a filter based on two indices and updates the runtime filters array and filters state
|
|
36
|
+
* @param {number} idx1 - The index of the original position of the filter value.
|
|
37
|
+
* @param {number} idx2 - The index of the new position for the filter value.
|
|
38
|
+
* @param {number} filterIndex - The index of the filter item within the array of filter items.
|
|
39
|
+
* @param {object} filter - The filter item itself, which contains an array of filter values.
|
|
40
|
+
* @return {void} None. This function only updates the state of the component.
|
|
41
|
+
*
|
|
42
|
+
* @modifies {object} - The filter object passed in as a parameter
|
|
43
|
+
* @modifies {array} - The filteredData state if visualizationConfig.type equals 'map'
|
|
44
|
+
* @modifies {object} - The visualizationConfig state
|
|
45
|
+
*/
|
|
46
|
+
const handleFilterOrder = (idx1, idx2, filterIndex, filter) => {
|
|
47
|
+
// Create a shallow copy of the filter values array & update position of the values
|
|
48
|
+
const updatedValues = [...filter.values]
|
|
49
|
+
const [movedItem] = updatedValues.splice(idx1, 1)
|
|
50
|
+
updatedValues.splice(idx2, 0, movedItem)
|
|
51
|
+
|
|
52
|
+
const filtersCopy = visualizationConfig.type === 'chart' ? [...visualizationConfig.filters] : [...filteredData]
|
|
53
|
+
const filterItem = { ...filtersCopy[filterIndex] }
|
|
54
|
+
|
|
55
|
+
// Overwrite filterItem.values since thats what we map through in the editor panel
|
|
56
|
+
filterItem.values = updatedValues
|
|
57
|
+
filterItem.orderedValues = updatedValues
|
|
58
|
+
filterItem.active = updatedValues[0]
|
|
59
|
+
filterItem.order = 'cust'
|
|
60
|
+
|
|
61
|
+
// Update the filters
|
|
62
|
+
filtersCopy[filterIndex] = filterItem
|
|
63
|
+
|
|
64
|
+
if (visualizationConfig.type === 'map') {
|
|
65
|
+
setFilteredData(filtersCopy)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
setConfig({ ...visualizationConfig, filters: filtersCopy })
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const announceChange = text => {}
|
|
72
|
+
|
|
73
|
+
const changeFilterActive = (index, value) => {
|
|
74
|
+
let newFilters = visualizationConfig.type === 'map' ? [...filteredData] : [...visualizationConfig.filters]
|
|
75
|
+
newFilters[index].active = value
|
|
76
|
+
|
|
77
|
+
// If this is a button filter type show the button.
|
|
78
|
+
if (visualizationConfig.filterBehavior === 'Apply Button') {
|
|
79
|
+
setShowApplyButton(true)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// If we're not using the apply button we can set the filters right away.
|
|
83
|
+
if (visualizationConfig.filterBehavior !== 'Apply Button') {
|
|
84
|
+
setConfig({
|
|
85
|
+
...visualizationConfig,
|
|
86
|
+
filters: newFilters
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Used for setting active filter, fromHash breaks the filteredData functionality.
|
|
91
|
+
if (visualizationConfig.type === 'map' && visualizationConfig.filterBehavior === 'Filter Change') {
|
|
92
|
+
setFilteredData(newFilters)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// If we're on a chart and not using the apply button
|
|
96
|
+
if (visualizationConfig.type === 'chart' && visualizationConfig.filterBehavior === 'Filter Change') {
|
|
97
|
+
setFilteredData(filterData(newFilters, excludedData))
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const handleApplyButton = newFilters => {
|
|
102
|
+
setConfig({ ...visualizationConfig, filters: newFilters })
|
|
103
|
+
|
|
104
|
+
if (type === 'map') {
|
|
105
|
+
setFilteredData(newFilters, excludedData)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (type === 'chart') {
|
|
109
|
+
setFilteredData(filterData(newFilters, excludedData))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
setShowApplyButton(false)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const handleReset = e => {
|
|
116
|
+
let newFilters = [...visualizationConfig.filters]
|
|
117
|
+
e.preventDefault()
|
|
118
|
+
|
|
119
|
+
// reset to first item in values array.
|
|
120
|
+
newFilters.map(filter => {
|
|
121
|
+
filter = handleSorting(filter)
|
|
122
|
+
filter.active = filter.values[0]
|
|
123
|
+
return filter
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
if (type === 'map') {
|
|
127
|
+
setFilteredData(newFilters, excludedData)
|
|
128
|
+
} else {
|
|
129
|
+
setFilteredData(filterData(newFilters, excludedData))
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
setConfig({ ...visualizationConfig, filters: newFilters })
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const filterConstants = {
|
|
136
|
+
buttonText: 'Apply Filters',
|
|
137
|
+
resetText: 'Reset All',
|
|
138
|
+
introText: `Make a selection from the filters to change the visualization information.`,
|
|
139
|
+
applyText: 'Select the apply button to update the visualization information.'
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const handleSorting = singleFilter => {
|
|
143
|
+
const { order } = singleFilter
|
|
144
|
+
|
|
145
|
+
const sortAsc = (a, b) => {
|
|
146
|
+
return a.toString().localeCompare(b.toString(), 'en', { numeric: true })
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const sortDesc = (a, b) => {
|
|
150
|
+
return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!order || order === '') {
|
|
154
|
+
singleFilter.order = 'asc'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (order === 'desc') {
|
|
158
|
+
singleFilter.values = singleFilter.values.sort(sortDesc)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (order === 'asc') {
|
|
162
|
+
singleFilter.values = singleFilter.values.sort(sortAsc)
|
|
163
|
+
}
|
|
164
|
+
return singleFilter
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// prettier-ignore
|
|
168
|
+
return {
|
|
169
|
+
handleApplyButton,
|
|
170
|
+
changeFilterActive,
|
|
171
|
+
announceChange,
|
|
172
|
+
showApplyButton,
|
|
173
|
+
handleReset,
|
|
174
|
+
filterConstants,
|
|
175
|
+
filterStyleOptions,
|
|
176
|
+
filterOrderOptions,
|
|
177
|
+
handleFilterOrder,
|
|
178
|
+
handleSorting
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const Filters = props => {
|
|
183
|
+
const { config: visualizationConfig, filteredData, dimensions } = props
|
|
184
|
+
const { filters, type, general, theme, filterBehavior } = visualizationConfig
|
|
185
|
+
const [mobileFilterStyle, setMobileFilterStyle] = useState(false)
|
|
186
|
+
|
|
187
|
+
// useFilters hook provides data and logic for handling various filter functions
|
|
188
|
+
// prettier-ignore
|
|
189
|
+
const {
|
|
190
|
+
handleApplyButton,
|
|
191
|
+
changeFilterActive,
|
|
192
|
+
announceChange,
|
|
193
|
+
showApplyButton,
|
|
194
|
+
handleReset,
|
|
195
|
+
filterConstants,
|
|
196
|
+
handleSorting
|
|
197
|
+
} = useFilters(props)
|
|
198
|
+
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
if (!dimensions) return
|
|
201
|
+
if (dimensions[0] < 768 && filters?.length > 0) {
|
|
202
|
+
setMobileFilterStyle(true)
|
|
203
|
+
} else {
|
|
204
|
+
setMobileFilterStyle(false)
|
|
205
|
+
}
|
|
206
|
+
}, [dimensions])
|
|
207
|
+
|
|
208
|
+
const Filters = props => props.children
|
|
209
|
+
|
|
210
|
+
const filterSectionClassList = ['filters-section', type === 'map' ? general.headerColor : theme]
|
|
211
|
+
|
|
212
|
+
// Exterior Section Wrapper
|
|
213
|
+
Filters.Section = props => {
|
|
214
|
+
return (
|
|
215
|
+
<section className={filterSectionClassList.join(' ')}>
|
|
216
|
+
<p className='filters-section__intro-text'>
|
|
217
|
+
{filterConstants.introText} {visualizationConfig.filterBehavior === 'Apply Button' && filterConstants.applyText}
|
|
218
|
+
</p>
|
|
219
|
+
<div className='filters-section__wrapper'>{props.children}</div>
|
|
220
|
+
</section>
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Apply/Reset Buttons
|
|
225
|
+
Filters.ApplyBehavior = props => {
|
|
226
|
+
if (filterBehavior !== 'Apply Button') return
|
|
227
|
+
const applyButtonClasses = [general?.headerColor ? general.headerColor : theme, 'apply']
|
|
228
|
+
return (
|
|
229
|
+
<div className='filters-section__buttons'>
|
|
230
|
+
<Button onClick={() => handleApplyButton(filters)} disabled={!showApplyButton} className={applyButtonClasses.join(' ')}>
|
|
231
|
+
{filterConstants.buttonText}
|
|
232
|
+
</Button>
|
|
233
|
+
<a href='#!' role='button' onClick={handleReset}>
|
|
234
|
+
{filterConstants.resetText}
|
|
235
|
+
</a>
|
|
236
|
+
</div>
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
Filters.TabBar = props => {
|
|
241
|
+
const { filter: singleFilter, index: outerIndex } = props
|
|
242
|
+
return (
|
|
243
|
+
<section className='single-filters__tab-bar'>
|
|
244
|
+
{singleFilter.values.map(filter => {
|
|
245
|
+
const buttonClassList = ['button__tab-bar', singleFilter.active === filter ? 'button__tab-bar--active' : '']
|
|
246
|
+
return (
|
|
247
|
+
<button className={buttonClassList.join(' ')} key={filter} onClick={e => changeFilterActive(outerIndex, filter)}>
|
|
248
|
+
{filter}
|
|
249
|
+
</button>
|
|
250
|
+
)
|
|
251
|
+
})}
|
|
252
|
+
</section>
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
Filters.Pills = props => props.pills
|
|
257
|
+
|
|
258
|
+
Filters.Tabs = props => props.tabs
|
|
259
|
+
|
|
260
|
+
Filters.Dropdown = props => {
|
|
261
|
+
const { index: outerIndex, label, active, filters } = props
|
|
262
|
+
return (
|
|
263
|
+
<select
|
|
264
|
+
id={`filter-${outerIndex}`}
|
|
265
|
+
name={label}
|
|
266
|
+
className='filter-select'
|
|
267
|
+
data-index='0'
|
|
268
|
+
value={active}
|
|
269
|
+
onChange={e => {
|
|
270
|
+
changeFilterActive(outerIndex, e.target.value)
|
|
271
|
+
announceChange(`Filter ${label} value has been changed to ${e.target.value}, please reference the data table to see updated values.`)
|
|
272
|
+
}}
|
|
273
|
+
>
|
|
274
|
+
{filters}
|
|
275
|
+
</select>
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Resolve Filter Styles
|
|
280
|
+
Filters.Style = () => {
|
|
281
|
+
if (filters || filteredData) {
|
|
282
|
+
// Here charts is using config.filters where maps is using a runtime value
|
|
283
|
+
let filtersToLoop = type === 'map' ? filteredData : filters
|
|
284
|
+
|
|
285
|
+
// Remove fromHash if it exists on filters to loop so we can loop nicely
|
|
286
|
+
delete filtersToLoop.fromHash
|
|
287
|
+
|
|
288
|
+
return filtersToLoop.map((singleFilter, outerIndex) => {
|
|
289
|
+
const values = []
|
|
290
|
+
const pillValues = []
|
|
291
|
+
const tabValues = []
|
|
292
|
+
const tabBarValues = []
|
|
293
|
+
|
|
294
|
+
const { active, label, filterStyle } = singleFilter
|
|
295
|
+
|
|
296
|
+
handleSorting(singleFilter)
|
|
297
|
+
|
|
298
|
+
singleFilter.values.forEach((filterOption, index) => {
|
|
299
|
+
const pillClassList = ['pill', active === filterOption ? 'pill--active' : null, theme && theme]
|
|
300
|
+
const tabClassList = ['tab', active === filterOption && 'tab--active', theme && theme]
|
|
301
|
+
|
|
302
|
+
pillValues.push(
|
|
303
|
+
<div className='pill__wrapper'>
|
|
304
|
+
<button className={pillClassList.join(' ')} onClick={e => changeFilterActive(outerIndex, filterOption)} name={label}>
|
|
305
|
+
{filterOption}
|
|
306
|
+
</button>
|
|
307
|
+
</div>
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
values.push(
|
|
311
|
+
<option key={index} value={filterOption}>
|
|
312
|
+
{filterOption}
|
|
313
|
+
</option>
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
tabValues.push(
|
|
317
|
+
<button className={tabClassList.join(' ')} onClick={e => changeFilterActive(outerIndex, filterOption)}>
|
|
318
|
+
{filterOption}
|
|
319
|
+
</button>
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
tabBarValues.push(filterOption)
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
const classList = ['single-filters', mobileFilterStyle ? 'single-filters--dropdown' : `single-filters--${filterStyle}`]
|
|
326
|
+
|
|
327
|
+
return (
|
|
328
|
+
<div className={classList.join(' ')} key={outerIndex}>
|
|
329
|
+
<>
|
|
330
|
+
{label && <label htmlFor={label}>{label}</label>}
|
|
331
|
+
{filterStyle === 'tab' && !mobileFilterStyle && <Filters.Tabs tabs={tabValues} />}
|
|
332
|
+
{filterStyle === 'pill' && !mobileFilterStyle && <Filters.Pills pills={pillValues} />}
|
|
333
|
+
{filterStyle === 'tab bar' && !mobileFilterStyle && <Filters.TabBar filter={singleFilter} index={outerIndex} />}
|
|
334
|
+
{(filterStyle === 'dropdown' || mobileFilterStyle) && <Filters.Dropdown index={outerIndex} label={label} active={active} filters={values} />}
|
|
335
|
+
</>
|
|
336
|
+
</div>
|
|
337
|
+
)
|
|
338
|
+
})
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (visualizationConfig?.filters?.length === 0 || props?.filteredData?.length === 0) return
|
|
343
|
+
return (
|
|
344
|
+
<Filters>
|
|
345
|
+
<Filters.Section>
|
|
346
|
+
<Filters.Style />
|
|
347
|
+
<Filters.ApplyBehavior />
|
|
348
|
+
</Filters.Section>
|
|
349
|
+
</Filters>
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
Filters.propTypes = {
|
|
354
|
+
// runtimeFilters in place
|
|
355
|
+
filteredData: PropTypes.array,
|
|
356
|
+
// function for updating the runtime filters
|
|
357
|
+
setFilteredData: PropTypes.func,
|
|
358
|
+
// the full apps config
|
|
359
|
+
config: PropTypes.object,
|
|
360
|
+
// updating function for setting fitlerBehavior
|
|
361
|
+
setConfig: PropTypes.func,
|
|
362
|
+
// exclusions
|
|
363
|
+
excludedData: PropTypes.array,
|
|
364
|
+
// function for filtering the data
|
|
365
|
+
filterData: PropTypes.func
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export default Filters
|
|
@@ -17,6 +17,7 @@ const InputSlider = ({
|
|
|
17
17
|
i = null,
|
|
18
18
|
min = null,
|
|
19
19
|
max = null,
|
|
20
|
+
onClick,
|
|
20
21
|
...attributes
|
|
21
22
|
}) => {
|
|
22
23
|
const [value, setValue] = useState(stateValue)
|
|
@@ -51,10 +52,15 @@ const InputSlider = ({
|
|
|
51
52
|
}
|
|
52
53
|
}, [value])
|
|
53
54
|
|
|
55
|
+
const clickHandler = e => {
|
|
56
|
+
setValue(!value)
|
|
57
|
+
if (onClick) onClick(e)
|
|
58
|
+
}
|
|
59
|
+
|
|
54
60
|
return (
|
|
55
61
|
<div className='input-group'>
|
|
56
62
|
{label && <label>{label}</label>}
|
|
57
|
-
<div className={'cove-input__slider' + (size === 'small' ? '--small' : size === 'large' ? '--large' : '') + sliderTypeClass() + (value ? ' active' : '')} onClick={
|
|
63
|
+
<div className={'cove-input__slider' + (size === 'small' ? '--small' : size === 'large' ? '--large' : '') + sliderTypeClass() + (value ? ' active' : '')} onClick={clickHandler}>
|
|
58
64
|
<div className='cove-input__slider-button' />
|
|
59
65
|
<div className='cove-input__slider-track' style={value && activeColor ? { backgroundColor: activeColor } : null} />
|
|
60
66
|
<input className='cove-input--hidden' type='checkbox' name={name()} checked={value || false} readOnly />
|
package/components/ui/Icon.jsx
CHANGED
|
@@ -74,7 +74,8 @@ const Icon = ({ display = null, base, alt = '', size, color, style, ...attribute
|
|
|
74
74
|
const styles = {
|
|
75
75
|
...style,
|
|
76
76
|
color: color ? color : null,
|
|
77
|
-
width: size ? size + 'px' : null
|
|
77
|
+
width: size ? size + 'px' : null,
|
|
78
|
+
cursor: display === 'move' ? 'move' : 'default'
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
return (
|
package/data/colorPalettes.js
CHANGED
|
@@ -43,7 +43,27 @@ const colorPalettes2 = {
|
|
|
43
43
|
'sequential-orange-(MPX)': ['#FFEFCF', '#FFD49C', '#F7A866', '#EB7723', '#B95117', '#862B0B'],
|
|
44
44
|
'sequential-green': ['#C7E9C0', '#A1D99B', '#74C476', '#41AB5D', '#238B45', '#005A32']
|
|
45
45
|
}
|
|
46
|
+
// * ============= Palettes for Paired bar Chart ========== * //
|
|
47
|
+
export const colorPalettes3 = {
|
|
48
|
+
'monochrome-1': ['#A6CEE3', '#15017A'],
|
|
49
|
+
'monochrome-2': ['#C2C0FC', '#15017A'],
|
|
50
|
+
'monochrome-3': ['#cab2d6', '#6a3d9a'],
|
|
51
|
+
'monochrome-4': ['#C2C0FC', '#6a3d9a'],
|
|
52
|
+
'monochrome-5': ['#fedab8', '#bf5b17'],
|
|
53
|
+
'cool-1': ['#b2df8a', '#1f78b4'],
|
|
54
|
+
'cool-2': ['#a6cee3', '#33A02C'],
|
|
55
|
+
'cool-3': ['#C2C0FC', '#386cb0'],
|
|
56
|
+
'cool-4': ['#33A02c', '#6a3d9a'],
|
|
57
|
+
'cool-5': ['#a6cee3', '#6a3d9a'],
|
|
58
|
+
'warm-1': ['#e31a1c', '#fedab8'],
|
|
59
|
+
'complementary-1': ['#1f78b4', '#e6ab02'],
|
|
60
|
+
'complementary-2': ['#1f78b4', '#ff7f00'],
|
|
61
|
+
'complementary-3': ['#6a3d9a', '#ff7f00'],
|
|
62
|
+
'complementary-4': ['#6a3d9a', '#e6ab02'],
|
|
63
|
+
'complementary-5': ['#e31a90', '#1b9e77']
|
|
64
|
+
}
|
|
46
65
|
|
|
47
|
-
export const colorPalettesChart = updatePaletteNames(colorPalettes2)
|
|
48
|
-
const colorPalettes = updatePaletteNames(colorPalettesMap)
|
|
66
|
+
export const colorPalettesChart = updatePaletteNames(colorPalettes2)
|
|
67
|
+
const colorPalettes = updatePaletteNames(colorPalettesMap)
|
|
68
|
+
export const twoColorPalette = updatePaletteNames(colorPalettes3)
|
|
49
69
|
export default colorPalettes
|
package/helpers/DataTransform.js
CHANGED
|
@@ -196,6 +196,60 @@ export class DataTransform {
|
|
|
196
196
|
|
|
197
197
|
return data;
|
|
198
198
|
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* cleanData
|
|
202
|
+
*
|
|
203
|
+
// This cleans a data set by:
|
|
204
|
+
// - removing commas and $ signs from any numbers to try to plot the point
|
|
205
|
+
// - removing any data points that are NOT composed of of all digits (but allow a decimal point)
|
|
206
|
+
// Without this the charts "break" and do not render
|
|
207
|
+
*
|
|
208
|
+
* Inputs: data as array, excludeKey indicates which key to use to NOT clean
|
|
209
|
+
* Example: "Date" should not be cleaned if part of the data
|
|
210
|
+
*
|
|
211
|
+
* Output: returns the cleanedData
|
|
212
|
+
*
|
|
213
|
+
* Set testing = true if you need to see before and after data
|
|
214
|
+
*
|
|
215
|
+
*/
|
|
216
|
+
cleanData (data, excludeKey, testing = false) {
|
|
217
|
+
let cleanedupData = []
|
|
218
|
+
if (testing) console.log('## Data to clean=', data)
|
|
219
|
+
if (excludeKey === undefined) {
|
|
220
|
+
console.log('COVE: cleanData excludeKey undefined')
|
|
221
|
+
return data // because no excludeKey
|
|
222
|
+
}
|
|
223
|
+
data.forEach(function (d, i) {
|
|
224
|
+
if (testing) console.log("clean", i, " d", d);
|
|
225
|
+
let cleaned = {}
|
|
226
|
+
Object.keys(d).forEach(function (key) {
|
|
227
|
+
if (key === excludeKey) {
|
|
228
|
+
// pass thru
|
|
229
|
+
cleaned[key] = d[key]
|
|
230
|
+
} else {
|
|
231
|
+
// remove comma and dollar signs
|
|
232
|
+
if (testing) console.log("typeof d[key] is ", typeof d[key]);
|
|
233
|
+
let tmp = "";
|
|
234
|
+
if (typeof d[key] === 'string') {
|
|
235
|
+
tmp = d[key] !== null && d[key] !== '' ? d[key].replace(/[,\$]/g, '') : ''
|
|
236
|
+
} else {
|
|
237
|
+
tmp = d[key] !== null && d[key] !== '' ? d[key] : ''
|
|
238
|
+
}
|
|
239
|
+
if ((tmp !== '' && tmp !== null && !isNaN(tmp)) || (tmp !== '' && tmp !== null && /\d+\.?\d*/.test(tmp))) {
|
|
240
|
+
cleaned[key] = tmp
|
|
241
|
+
} else { cleaned[key] = '' }
|
|
242
|
+
// if you get here, then return nothing to skip bad data point
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
if (testing) console.log("cleaned=", cleaned)
|
|
246
|
+
cleanedupData.push(cleaned)
|
|
247
|
+
})
|
|
248
|
+
if (testing) console.log('## cleanedData =', cleanedupData)
|
|
249
|
+
return cleanedupData
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
|
|
199
253
|
}
|
|
200
254
|
|
|
201
255
|
export default DataTransform
|
package/helpers/isNumberLog.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default function isNumberLog(value = '', state = null) {
|
|
2
2
|
// if you need to check data to see if there is junk in there that can't be handled
|
|
3
3
|
// you can run the points through this and see values on the console
|
|
4
|
-
console.log("entering
|
|
4
|
+
console.log("entering isNumberLog value, valuetype:",value,typeof value);
|
|
5
5
|
var test;
|
|
6
6
|
if (typeof value === 'number') {
|
|
7
7
|
test = !Number.isNaN(value)
|
|
@@ -10,9 +10,9 @@ export default function isNumberLog(value = '', state = null) {
|
|
|
10
10
|
test = value !== null && value !== '' && /\d+\.?\d*/.test(value)
|
|
11
11
|
}
|
|
12
12
|
if (test === false) {
|
|
13
|
-
console.log('#
|
|
13
|
+
console.log('# isNumberLog FALSE on value, result', value, test)
|
|
14
14
|
} else {
|
|
15
|
-
console.log('#
|
|
15
|
+
console.log('# isNumberLog TRUE on value, result', value, test)
|
|
16
16
|
}
|
|
17
17
|
return test
|
|
18
18
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/core",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.4",
|
|
4
4
|
"description": "Core components, styles, hooks, and helpers, for the CDC Open Visualization project",
|
|
5
5
|
"moduleName": "CdcCore",
|
|
6
6
|
"main": "dist/cdccore",
|
|
@@ -30,5 +30,5 @@
|
|
|
30
30
|
"react": "^18.2.0",
|
|
31
31
|
"react-dom": "^18.2.0"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "dcd395d76f70b2d113f2b4c6fe50a52522655cd1"
|
|
34
34
|
}
|
package/styles/_global.scss
CHANGED
|
@@ -80,6 +80,9 @@ strong {
|
|
|
80
80
|
}
|
|
81
81
|
&.danger {
|
|
82
82
|
background-color: $red;
|
|
83
|
+
padding: 0em 0.6em 0.0em;
|
|
84
|
+
height: 1.6em;
|
|
85
|
+
font-size: 0.8 em;
|
|
83
86
|
color: #fff;
|
|
84
87
|
&:hover {
|
|
85
88
|
background-color: darken($red, 5%);
|
|
@@ -186,3 +189,8 @@ section.footnotes {
|
|
|
186
189
|
.cdc-chart-inner-container .subtext {
|
|
187
190
|
padding: 15px;
|
|
188
191
|
}
|
|
192
|
+
|
|
193
|
+
.margin-left-href {
|
|
194
|
+
margin-left: 15px;
|
|
195
|
+
}
|
|
196
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
&.cdc-map-outer-container .editor-panel .series-item,
|
|
2
|
+
&.type-chart .editor-panel .series-item {
|
|
3
|
+
list-style: none;
|
|
4
|
+
position: relative;
|
|
5
|
+
|
|
6
|
+
$border: 1px solid #ccc;
|
|
7
|
+
|
|
8
|
+
@at-root .accordion:first-of-type .series-item__title {
|
|
9
|
+
border-top: $border;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@at-root .accordion:last-of-type .series-item__title {
|
|
13
|
+
border-bottom: $border;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.accordion__button {
|
|
17
|
+
border-bottom: unset;
|
|
18
|
+
|
|
19
|
+
&.hide-arrow:before {
|
|
20
|
+
display: none;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.accordion__panel {
|
|
25
|
+
border-left: $border;
|
|
26
|
+
border-right: $border;
|
|
27
|
+
padding: 10px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.series-list__remove {
|
|
31
|
+
margin-left: auto;
|
|
32
|
+
margin-right: 20px;
|
|
33
|
+
border: 1px solid red;
|
|
34
|
+
border-radius: 10px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.hide-arrow .series-list__remove {
|
|
38
|
+
margin-right: 0px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
label {
|
|
42
|
+
font-size: 0.8rem;
|
|
43
|
+
font-weight: normal;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.series-item__title {
|
|
47
|
+
width: 100%;
|
|
48
|
+
background-color: #f5f5f5;
|
|
49
|
+
border-right: $border;
|
|
50
|
+
border-left: $border;
|
|
51
|
+
display: block;
|
|
52
|
+
padding: 5px;
|
|
53
|
+
|
|
54
|
+
.accordion__button {
|
|
55
|
+
border-bottom: none !important;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
li {
|
|
60
|
+
padding: 5px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.series_item__title {
|
|
64
|
+
font-size: 12px;
|
|
65
|
+
font-weight: bold;
|
|
66
|
+
text-transform: uppercase;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.series-item__dropdown {
|
|
70
|
+
display: block;
|
|
71
|
+
margin-bottom: 8px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// chart series list
|
|
75
|
+
&--chart {
|
|
76
|
+
padding: 5px 5px 0;
|
|
77
|
+
|
|
78
|
+
.accordion__button {
|
|
79
|
+
display: flex;
|
|
80
|
+
padding-left: 5px !important;
|
|
81
|
+
|
|
82
|
+
.cove-icon {
|
|
83
|
+
padding-right: 5px;
|
|
84
|
+
margin-right: 10px;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
&:last-child {
|
|
89
|
+
padding-bottom: 5px;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
package/styles/base.scss
CHANGED
|
@@ -52,6 +52,11 @@
|
|
|
52
52
|
@import 'reset';
|
|
53
53
|
@import 'variables';
|
|
54
54
|
@import 'mixins';
|
|
55
|
+
@import 'filters';
|
|
56
|
+
|
|
57
|
+
body.post-type-cdc_visualization .visx-tooltip {
|
|
58
|
+
z-index: 1000000;
|
|
59
|
+
}
|
|
55
60
|
|
|
56
61
|
.cdc-open-viz-module {
|
|
57
62
|
position: relative;
|
|
@@ -64,6 +69,7 @@
|
|
|
64
69
|
@import 'data-table';
|
|
65
70
|
@import 'global';
|
|
66
71
|
@import 'button-section';
|
|
72
|
+
@import 'series-list';
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
$theme: (
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
.filters-section {
|
|
2
|
+
&__wrapper {
|
|
3
|
+
flex-wrap: wrap;
|
|
4
|
+
display: flex;
|
|
5
|
+
gap: 7px 15px;
|
|
6
|
+
margin-top: 15px;
|
|
7
|
+
margin-bottom: 15px;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
&__intro-text {
|
|
11
|
+
display: block;
|
|
12
|
+
width: 100%;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
&__buttons {
|
|
16
|
+
width: 100%;
|
|
17
|
+
.apply {
|
|
18
|
+
margin-right: 10px;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// note the diff of section and no section above.
|
|
24
|
+
// Can't use nested selectors with top level section overwrites
|
|
25
|
+
section.filters-section {
|
|
26
|
+
// border: 1px solid #c7c7c7;
|
|
27
|
+
// padding: 10px;
|
|
28
|
+
border-radius: 10px;
|
|
29
|
+
margin-bottom: 10px;
|
|
30
|
+
display: block !important;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
div.single-filters {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-wrap: wrap;
|
|
36
|
+
margin: 15px 0;
|
|
37
|
+
|
|
38
|
+
select {
|
|
39
|
+
width: auto !important;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
label {
|
|
43
|
+
width: 100%;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&__tab-bar {
|
|
47
|
+
&:first-child {
|
|
48
|
+
border-top-left-radius: 15px;
|
|
49
|
+
border-bottom-left-radius: 15px;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.single-filters--dropdown {
|
|
55
|
+
width: auto;
|
|
56
|
+
display: flex;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.single-filters--tab {
|
|
60
|
+
width: 100%;
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-wrap: wrap;
|
|
63
|
+
|
|
64
|
+
.tab {
|
|
65
|
+
width: 25%;
|
|
66
|
+
min-height: 40px;
|
|
67
|
+
border: none;
|
|
68
|
+
padding: 0.5em 0;
|
|
69
|
+
background: none;
|
|
70
|
+
border-top-left-radius: 5px;
|
|
71
|
+
border-top-right-radius: 5px;
|
|
72
|
+
margin-top: 5px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.tab--active {
|
|
76
|
+
border-bottom: none;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.single-filters--pill {
|
|
81
|
+
width: 100%;
|
|
82
|
+
display: flex;
|
|
83
|
+
flex-wrap: wrap;
|
|
84
|
+
|
|
85
|
+
.pill {
|
|
86
|
+
border: none;
|
|
87
|
+
width: 95%;
|
|
88
|
+
height: 60px;
|
|
89
|
+
background: none;
|
|
90
|
+
border-radius: 30px;
|
|
91
|
+
box-shadow: 0 2px 5px #d3d3d3;
|
|
92
|
+
vertical-align: middle;
|
|
93
|
+
margin-bottom: 15px;
|
|
94
|
+
margin-right: 10px;
|
|
95
|
+
margin-left: 10px;
|
|
96
|
+
white-space: break-spaces;
|
|
97
|
+
word-break: break-word;
|
|
98
|
+
|
|
99
|
+
&__wrapper {
|
|
100
|
+
width: 25%;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.single-filters__tab-bar {
|
|
106
|
+
background: #f2f2f2;
|
|
107
|
+
display: flex !important;
|
|
108
|
+
flex-wrap: wrap;
|
|
109
|
+
width: 100%;
|
|
110
|
+
border-radius: 15px;
|
|
111
|
+
|
|
112
|
+
button.button__tab-bar {
|
|
113
|
+
flex: 1 1 0px;
|
|
114
|
+
padding: 10px;
|
|
115
|
+
background: none;
|
|
116
|
+
transition: background 2s;
|
|
117
|
+
&--active {
|
|
118
|
+
background: #fff;
|
|
119
|
+
border-radius: 15px;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -147,6 +147,55 @@ $theme: (
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
// Charts filter styles
|
|
151
|
+
.cdc-open-viz-module {
|
|
152
|
+
@each $theme-name, $theme-colors in $theme {
|
|
153
|
+
&.theme-#{$theme-name} {
|
|
154
|
+
.single-filters--tab .tab--active {
|
|
155
|
+
border: 1px solid string.unquote(nth($theme-colors, 1));
|
|
156
|
+
border-top: 3px solid string.unquote(nth($theme-colors, 1));
|
|
157
|
+
border-bottom: none;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.single-filters--tab .tab:not(.tab--active) {
|
|
161
|
+
border-bottom: 1px solid string.unquote(nth($theme-colors, 1));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.single-filters--pill .pill--active {
|
|
165
|
+
background-color: string.unquote(nth($theme-colors, 1));
|
|
166
|
+
color: #fff;
|
|
167
|
+
}
|
|
168
|
+
.filters-section button.cove-button:not([disabled]) {
|
|
169
|
+
background-color: string.unquote(nth($theme-colors, 1)) !important;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.theme-#{$theme-name} {
|
|
174
|
+
.single-filters--tab .tab--active {
|
|
175
|
+
border: 1px solid string.unquote(nth($theme-colors, 1));
|
|
176
|
+
border-top: 3px solid string.unquote(nth($theme-colors, 1));
|
|
177
|
+
border-bottom: none;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.single-filters--tab .tab:not(.tab--active) {
|
|
181
|
+
border-bottom: 1px solid string.unquote(nth($theme-colors, 1));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.single-filters--pill .pill--active {
|
|
185
|
+
background-color: string.unquote(nth($theme-colors, 1));
|
|
186
|
+
color: #fff;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.button__tab-bar--active {
|
|
190
|
+
outline: 2px solid string.unquote(nth($theme-colors, 1));
|
|
191
|
+
}
|
|
192
|
+
.apply:not([disabled]) {
|
|
193
|
+
background-color: string.unquote(nth($theme-colors, 1)) !important;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
150
199
|
$baseColor: #333;
|
|
151
200
|
$blue: #005eaa;
|
|
152
201
|
$lightestGray: #f2f2f2;
|
package/helpers/cleanData.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cleanData
|
|
3
|
-
*
|
|
4
|
-
// This cleans a data set by:
|
|
5
|
-
// - removing commas and $ signs from any numbers to try to plot the point
|
|
6
|
-
// - removing any data points that are NOT composed of of all digits (but allow a decimal point)
|
|
7
|
-
// Without this the charts "break" and do not render
|
|
8
|
-
*
|
|
9
|
-
* Inputs: data as array, excludeKey indicates which key to use to NOT clean
|
|
10
|
-
* Example: "Date" should not be cleaned if part of the data
|
|
11
|
-
*
|
|
12
|
-
* Output: returns the cleanedData
|
|
13
|
-
*
|
|
14
|
-
* Set testing = true if you need to see before and after data
|
|
15
|
-
*
|
|
16
|
-
*/
|
|
17
|
-
export default function cleanData (data, excludeKey, testing = false) {
|
|
18
|
-
let cleanedupData = []
|
|
19
|
-
if (testing) console.log('## Data to clean=', data)
|
|
20
|
-
if (excludeKey === undefined) {
|
|
21
|
-
excludeKey = "Date" // have a default value
|
|
22
|
-
}
|
|
23
|
-
data.forEach(function (d, i) {
|
|
24
|
-
if (testing) console.log("clean", i, " d", d);
|
|
25
|
-
let cleanedBar = {}
|
|
26
|
-
Object.keys(d).forEach(function (key) {
|
|
27
|
-
if (key === excludeKey) {
|
|
28
|
-
// pass thru
|
|
29
|
-
cleanedBar[key] = d[key]
|
|
30
|
-
} else {
|
|
31
|
-
// remove comma and dollar signs
|
|
32
|
-
if (testing) console.log("typeof d[key] is ", typeof d[key]);
|
|
33
|
-
let tmp = "";
|
|
34
|
-
if (typeof d[key] === 'string') {
|
|
35
|
-
tmp = d[key] !== null && d[key] !== '' ? d[key].replace(/[,\$]/g, '') : ''
|
|
36
|
-
} else {
|
|
37
|
-
tmp = d[key] !== null && d[key] !== '' ? d[key] : ''
|
|
38
|
-
}
|
|
39
|
-
if ((tmp !== '' && tmp !== null && !isNaN(tmp)) || (tmp !== '' && tmp !== null && /\d+\.?\d*/.test(tmp))) {
|
|
40
|
-
cleanedBar[key] = tmp
|
|
41
|
-
} else { cleanedBar[key] = '' }
|
|
42
|
-
// if you get here, then return nothing to skip bad data point
|
|
43
|
-
}
|
|
44
|
-
})
|
|
45
|
-
if (testing) console.log("cleanedBar=", cleanedBar);
|
|
46
|
-
cleanedupData.push(cleanedBar)
|
|
47
|
-
})
|
|
48
|
-
if (testing) console.log('## cleanedData =', cleanedupData)
|
|
49
|
-
return cleanedupData
|
|
50
|
-
}
|