@cdc/core 4.24.5 → 4.24.9
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-gear-multi.svg +23 -0
- package/components/AdvancedEditor/AdvancedEditor.tsx +93 -0
- package/components/AdvancedEditor/advanced-editor-styles.css +3 -0
- package/components/AdvancedEditor/index.ts +1 -0
- package/components/Alert/components/Alert.styles.css +15 -0
- package/components/Alert/components/Alert.tsx +39 -0
- package/components/Alert/index.tsx +3 -0
- package/components/DataTable/DataTable.tsx +127 -32
- package/components/DataTable/DataTableStandAlone.tsx +4 -25
- package/components/DataTable/components/DataTableEditorPanel.tsx +4 -4
- package/components/DataTable/components/ExpandCollapse.tsx +1 -1
- package/components/DataTable/helpers/chartCellMatrix.tsx +6 -12
- package/components/DataTable/helpers/getChartCellValue.ts +9 -5
- package/components/DataTable/helpers/getDataSeriesColumns.ts +10 -7
- package/components/DataTable/helpers/getRowType.ts +6 -0
- package/components/DataTable/helpers/mapCellMatrix.tsx +3 -3
- package/components/DataTable/types/TableConfig.ts +2 -1
- package/components/EditorPanel/ColumnsEditor.tsx +3 -30
- package/components/EditorPanel/DataTableEditor.tsx +66 -22
- package/components/EditorPanel/FieldSetWrapper.tsx +51 -0
- package/components/EditorPanel/FootnotesEditor.tsx +77 -0
- package/components/EditorPanel/Inputs.tsx +13 -4
- package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +268 -0
- package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +306 -0
- package/components/EditorPanel/VizFilterEditor/components/FilterOrder.tsx +40 -0
- package/components/EditorPanel/VizFilterEditor/index.ts +1 -0
- package/components/EditorWrapper/EditorWrapper.tsx +3 -4
- package/components/EditorWrapper/index.ts +1 -0
- package/components/Filters.tsx +520 -0
- package/components/Footnotes/Footnotes.tsx +25 -0
- package/components/Footnotes/FootnotesStandAlone.tsx +45 -0
- package/components/Footnotes/footnotes.css +5 -0
- package/components/Footnotes/index.ts +1 -0
- package/components/Layout/components/Responsive.tsx +14 -4
- package/components/Layout/components/Sidebar/components/Sidebar.tsx +14 -5
- package/components/Layout/components/Sidebar/components/sidebar.styles.scss +23 -20
- package/components/Layout/components/Visualization/index.tsx +19 -6
- package/components/Layout/components/Visualization/visualizations.scss +32 -26
- package/components/Layout/styles/editor.scss +0 -8
- package/components/Legend/Legend.Gradient.tsx +133 -0
- package/components/LegendShape.tsx +28 -0
- package/components/MultiSelect/MultiSelect.tsx +41 -11
- package/components/MultiSelect/multiselect.styles.css +0 -3
- package/components/NestedDropdown/NestedDropdown.tsx +47 -52
- package/components/NestedDropdown/nesteddropdown.styles.css +19 -25
- package/components/Table/Table.tsx +8 -5
- package/components/Table/components/Cell.tsx +2 -2
- package/components/Table/components/Row.tsx +25 -7
- package/components/_stories/Footnotes.stories.tsx +17 -0
- package/components/_stories/Layout.Debug.stories.tsx +91 -0
- package/components/_stories/_mocks/bar-chart-suppressed.json +474 -0
- package/components/_stories/styles.scss +14 -1
- package/components/createBarElement.jsx +4 -4
- package/components/inputs/InputSelect.tsx +17 -6
- package/components/ui/Icon.tsx +22 -16
- package/components/ui/Title/Title.scss +0 -8
- package/helpers/DataTransform.ts +2 -2
- package/helpers/addValuesToFilters.ts +135 -0
- package/helpers/cove/accessibility.ts +17 -4
- package/helpers/cove/fontSettings.ts +2 -0
- package/helpers/coveUpdateWorker.ts +30 -9
- package/helpers/filterVizData.ts +49 -0
- package/helpers/formatConfigBeforeSave.ts +95 -0
- package/helpers/gatherQueryParams.ts +14 -7
- package/helpers/getGradientLegendWidth.ts +15 -0
- package/helpers/getTextWidth.ts +18 -0
- package/helpers/lineChartHelpers.js +2 -1
- package/helpers/pivotData.ts +18 -0
- package/helpers/queryStringUtils.ts +29 -0
- package/helpers/scaling.ts +7 -0
- package/helpers/tests/addValuesToFilters.test.ts +55 -0
- package/helpers/tests/filterVizData.test.ts +31 -0
- package/helpers/tests/invertValue.test.ts +35 -0
- package/helpers/tests/updateFieldFactory.test.ts +1 -0
- package/helpers/updateFieldFactory.ts +1 -1
- package/helpers/updatePaletteNames.ts +19 -0
- package/helpers/{useDataVizClasses.js → useDataVizClasses.ts} +3 -2
- package/helpers/ver/4.24.5.ts +3 -3
- package/helpers/ver/4.24.7.ts +123 -0
- package/helpers/ver/4.24.9.ts +63 -0
- package/helpers/ver/tests/4.24.9.test.ts +22 -0
- package/helpers/ver/versionNeedsUpdate.ts +9 -0
- package/package.json +6 -4
- package/styles/_button-section.scss +7 -2
- package/styles/_data-table.scss +0 -1
- package/styles/_global.scss +6 -2
- package/styles/base.scss +4 -0
- package/styles/filters.scss +4 -0
- package/styles/v2/themes/_color-definitions.scss +1 -0
- package/types/Annotation.ts +46 -0
- package/types/Axis.ts +3 -2
- package/types/ConfigureData.ts +1 -1
- package/types/Dimensions.ts +1 -0
- package/types/Footnotes.ts +17 -0
- package/types/General.ts +5 -0
- package/types/Runtime.ts +2 -7
- package/types/Table.ts +6 -0
- package/types/Visualization.ts +31 -9
- package/types/VizFilter.ts +39 -7
- package/LICENSE +0 -201
- package/components/AdvancedEditor.jsx +0 -74
- package/components/EditorPanel/VizFilterEditor.tsx +0 -234
- package/components/Filters.jsx +0 -461
- package/components/LegendCircle.jsx +0 -17
- package/helpers/queryStringUtils.js +0 -26
- package/helpers/updatePaletteNames.js +0 -16
- package/types/BaseVisualizationType.ts +0 -1
- /package/components/{Waiting.jsx → Waiting.tsx} +0 -0
- /package/helpers/ver/{4.23.4.ts → 4.24.4.ts} +0 -0
package/components/Filters.jsx
DELETED
|
@@ -1,461 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef } from 'react'
|
|
2
|
-
import { useId } from 'react'
|
|
3
|
-
|
|
4
|
-
// CDC
|
|
5
|
-
import Button from '@cdc/core/components/elements/Button'
|
|
6
|
-
import { getQueryParams, updateQueryString } from '@cdc/core/helpers/queryStringUtils'
|
|
7
|
-
|
|
8
|
-
// Third Party
|
|
9
|
-
import PropTypes from 'prop-types'
|
|
10
|
-
|
|
11
|
-
export const filterStyleOptions = ['dropdown', 'pill', 'tab', 'tab bar']
|
|
12
|
-
|
|
13
|
-
export const filterOrderOptions = [
|
|
14
|
-
{
|
|
15
|
-
label: 'Ascending Alphanumeric',
|
|
16
|
-
value: 'asc'
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
label: 'Descending Alphanumeric',
|
|
20
|
-
value: 'desc'
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
label: 'Custom',
|
|
24
|
-
value: 'cust'
|
|
25
|
-
}
|
|
26
|
-
]
|
|
27
|
-
|
|
28
|
-
export const handleSorting = singleFilter => {
|
|
29
|
-
const { order } = singleFilter
|
|
30
|
-
|
|
31
|
-
const sortAsc = (a, b) => {
|
|
32
|
-
return a.toString().localeCompare(b.toString(), 'en', { numeric: true })
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const sortDesc = (a, b) => {
|
|
36
|
-
return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (!order || order === '') {
|
|
40
|
-
singleFilter.order = 'asc'
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (order === 'desc') {
|
|
44
|
-
singleFilter.values = singleFilter.values.sort(sortDesc)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (order === 'asc') {
|
|
48
|
-
singleFilter.values = singleFilter.values.sort(sortAsc)
|
|
49
|
-
}
|
|
50
|
-
return singleFilter
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const hasStandardFilterBehavior = ['chart', 'table']
|
|
54
|
-
|
|
55
|
-
export const useFilters = props => {
|
|
56
|
-
const [showApplyButton, setShowApplyButton] = useState(false)
|
|
57
|
-
|
|
58
|
-
// Desconstructing: notice, adding more descriptive visualizationConfig name over config
|
|
59
|
-
// visualizationConfig feels more robust for all vis types so that its not confused with config/state/etc.
|
|
60
|
-
const { config: visualizationConfig, setConfig, filteredData, setFilteredData, excludedData, filterData, getUniqueValues } = props
|
|
61
|
-
const { type, data } = visualizationConfig
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Re-orders a filter based on two indices and updates the runtime filters array and filters state
|
|
65
|
-
* @param {number} idx1 - The index of the original position of the filter value.
|
|
66
|
-
* @param {number} idx2 - The index of the new position for the filter value.
|
|
67
|
-
* @param {number} filterIndex - The index of the filter item within the array of filter items.
|
|
68
|
-
* @param {object} filter - The filter item itself, which contains an array of filter values.
|
|
69
|
-
* @return {void} None. This function only updates the state of the component.
|
|
70
|
-
*
|
|
71
|
-
* @modifies {object} - The filter object passed in as a parameter
|
|
72
|
-
* @modifies {array} - The filteredData state if visualizationConfig.type equals 'map'
|
|
73
|
-
* @modifies {object} - The visualizationConfig state
|
|
74
|
-
*/
|
|
75
|
-
const handleFilterOrder = (idx1, idx2, filterIndex, filter) => {
|
|
76
|
-
// Create a shallow copy of the filter values array & update position of the values
|
|
77
|
-
const updatedValues = [...filter.values]
|
|
78
|
-
const [movedItem] = updatedValues.splice(idx1, 1)
|
|
79
|
-
updatedValues.splice(idx2, 0, movedItem)
|
|
80
|
-
|
|
81
|
-
const filtersCopy = hasStandardFilterBehavior.includes(visualizationConfig.type) ? [...visualizationConfig.filters] : [...filteredData]
|
|
82
|
-
const filterItem = { ...filtersCopy[filterIndex] }
|
|
83
|
-
|
|
84
|
-
// Overwrite filterItem.values since thats what we map through in the editor panel
|
|
85
|
-
filterItem.values = updatedValues
|
|
86
|
-
filterItem.orderedValues = updatedValues
|
|
87
|
-
filterItem.active = updatedValues[0]
|
|
88
|
-
filterItem.order = 'cust'
|
|
89
|
-
|
|
90
|
-
// Update the filters
|
|
91
|
-
filtersCopy[filterIndex] = filterItem
|
|
92
|
-
|
|
93
|
-
if (visualizationConfig.type === 'map') {
|
|
94
|
-
setFilteredData(filtersCopy)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
setConfig({ ...visualizationConfig, filters: filtersCopy })
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const announceChange = text => {}
|
|
101
|
-
|
|
102
|
-
const changeFilterActive = (index, value) => {
|
|
103
|
-
const newFilters = visualizationConfig.type === 'map' ? [...filteredData] : [...visualizationConfig.filters]
|
|
104
|
-
|
|
105
|
-
if (visualizationConfig.filterBehavior === 'Apply Button') {
|
|
106
|
-
newFilters[index].queuedActive = value
|
|
107
|
-
setShowApplyButton(true)
|
|
108
|
-
} else {
|
|
109
|
-
const newFilter = newFilters[index]
|
|
110
|
-
newFilter.active = value
|
|
111
|
-
|
|
112
|
-
const queryParams = getQueryParams()
|
|
113
|
-
if (newFilter.setByQueryParameter && queryParams[newFilter.setByQueryParameter] !== newFilter.active) {
|
|
114
|
-
queryParams[newFilter.setByQueryParameter] = newFilter.active
|
|
115
|
-
updateQueryString(queryParams)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
setConfig({
|
|
119
|
-
...visualizationConfig,
|
|
120
|
-
filters: newFilters
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
// Used for setting active filter, fromHash breaks the filteredData functionality.
|
|
124
|
-
if (visualizationConfig.type === 'map' && visualizationConfig.filterBehavior === 'Filter Change') {
|
|
125
|
-
setFilteredData(newFilters)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// If we're on a chart and not using the apply button
|
|
129
|
-
if (hasStandardFilterBehavior.includes(visualizationConfig.type) && visualizationConfig.filterBehavior === 'Filter Change') {
|
|
130
|
-
setFilteredData(filterData(newFilters, excludedData))
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const handleApplyButton = newFilters => {
|
|
135
|
-
let needsQueryUpdate = false
|
|
136
|
-
const queryParams = getQueryParams()
|
|
137
|
-
newFilters.forEach(newFilter => {
|
|
138
|
-
if (newFilter.queuedActive) {
|
|
139
|
-
newFilter.active = newFilter.queuedActive
|
|
140
|
-
delete newFilter.queuedActive
|
|
141
|
-
if (newFilter.setByQueryParameter && queryParams[newFilter.setByQueryParameter] !== newFilter.active) {
|
|
142
|
-
queryParams[newFilter.setByQueryParameter] = newFilter.active
|
|
143
|
-
needsQueryUpdate = true
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
})
|
|
147
|
-
if (needsQueryUpdate) {
|
|
148
|
-
updateQueryString(queryParams)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
setConfig({ ...visualizationConfig, filters: newFilters })
|
|
152
|
-
|
|
153
|
-
if (type === 'map') {
|
|
154
|
-
setFilteredData(newFilters, excludedData)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (hasStandardFilterBehavior.includes(visualizationConfig.type)) {
|
|
158
|
-
setFilteredData(filterData(newFilters, excludedData))
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
setShowApplyButton(false)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const handleReset = e => {
|
|
165
|
-
let newFilters = [...visualizationConfig.filters]
|
|
166
|
-
e.preventDefault()
|
|
167
|
-
|
|
168
|
-
// reset to first item in values array.
|
|
169
|
-
let needsQueryUpdate = false
|
|
170
|
-
const queryParams = getQueryParams()
|
|
171
|
-
newFilters.forEach((filter, i) => {
|
|
172
|
-
if(!filter.values || filter.values.length === 0){
|
|
173
|
-
filter.values = getUniqueValues(data, filter.columnName)
|
|
174
|
-
}
|
|
175
|
-
newFilters[i].active = handleSorting(filter).values[0]
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (filter.setByQueryParameter && queryParams[filter.setByQueryParameter] !== filter.active) {
|
|
179
|
-
queryParams[filter.setByQueryParameter] = filter.active
|
|
180
|
-
needsQueryUpdate = true
|
|
181
|
-
}
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
if (needsQueryUpdate) {
|
|
185
|
-
updateQueryString(queryParams)
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
setConfig({ ...visualizationConfig, filters: newFilters })
|
|
189
|
-
|
|
190
|
-
if (type === 'map') {
|
|
191
|
-
setFilteredData(newFilters, excludedData)
|
|
192
|
-
} else {
|
|
193
|
-
setFilteredData(filterData(newFilters, excludedData))
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const filterConstants = {
|
|
199
|
-
buttonText: 'Apply Filters',
|
|
200
|
-
resetText: 'Reset All',
|
|
201
|
-
introText: `Make a selection from the filters to change the visualization information.`,
|
|
202
|
-
applyText: 'Select the apply button to update the visualization information.'
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// prettier-ignore
|
|
206
|
-
return {
|
|
207
|
-
handleApplyButton,
|
|
208
|
-
changeFilterActive,
|
|
209
|
-
announceChange,
|
|
210
|
-
showApplyButton,
|
|
211
|
-
handleReset,
|
|
212
|
-
filterConstants,
|
|
213
|
-
filterStyleOptions,
|
|
214
|
-
filterOrderOptions,
|
|
215
|
-
handleFilterOrder,
|
|
216
|
-
handleSorting
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const Filters = props => {
|
|
221
|
-
const { config: visualizationConfig, filteredData, dimensions, getUniqueValues } = props
|
|
222
|
-
const { filters, type, general, theme, filterBehavior, data } = visualizationConfig
|
|
223
|
-
const [mobileFilterStyle, setMobileFilterStyle] = useState(false)
|
|
224
|
-
const [selectedFilter, setSelectedFilter] = useState('')
|
|
225
|
-
const id = useId()
|
|
226
|
-
|
|
227
|
-
// useFilters hook provides data and logic for handling various filter functions
|
|
228
|
-
// prettier-ignore
|
|
229
|
-
const {
|
|
230
|
-
handleApplyButton,
|
|
231
|
-
changeFilterActive,
|
|
232
|
-
announceChange,
|
|
233
|
-
showApplyButton,
|
|
234
|
-
handleReset,
|
|
235
|
-
filterConstants,
|
|
236
|
-
handleSorting
|
|
237
|
-
} = useFilters(props)
|
|
238
|
-
|
|
239
|
-
useEffect(() => {
|
|
240
|
-
if (!dimensions) return
|
|
241
|
-
if (dimensions[0] < 768 && filters?.length > 0) {
|
|
242
|
-
setMobileFilterStyle(true)
|
|
243
|
-
} else {
|
|
244
|
-
setMobileFilterStyle(false)
|
|
245
|
-
}
|
|
246
|
-
}, [dimensions])
|
|
247
|
-
|
|
248
|
-
useEffect(() => {
|
|
249
|
-
if (selectedFilter) {
|
|
250
|
-
let el = document.getElementById(selectedFilter.id)
|
|
251
|
-
if (el) el.focus()
|
|
252
|
-
}
|
|
253
|
-
}, [changeFilterActive, selectedFilter])
|
|
254
|
-
|
|
255
|
-
const Filters = props => props.children
|
|
256
|
-
|
|
257
|
-
const filterSectionClassList = ['filters-section', type === 'map' ? general.headerColor : visualizationConfig?.visualizationType === 'Spark Line' ? null : theme]
|
|
258
|
-
// Exterior Section Wrapper
|
|
259
|
-
Filters.Section = props => {
|
|
260
|
-
return (
|
|
261
|
-
visualizationConfig?.filters && (
|
|
262
|
-
<section className={filterSectionClassList.join(' ')}>
|
|
263
|
-
<p className='filters-section__intro-text'>
|
|
264
|
-
{filters?.some(f => f.active) ? filterConstants.introText : ''} {visualizationConfig.filterBehavior === 'Apply Button' && filterConstants.applyText}
|
|
265
|
-
</p>
|
|
266
|
-
<div className='filters-section__wrapper'>{props.children}</div>
|
|
267
|
-
</section>
|
|
268
|
-
)
|
|
269
|
-
)
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Apply/Reset Buttons
|
|
273
|
-
Filters.ApplyBehavior = props => {
|
|
274
|
-
if (filterBehavior !== 'Apply Button') return
|
|
275
|
-
const applyButtonClasses = [general?.headerColor ? general.headerColor : theme, 'apply']
|
|
276
|
-
return (
|
|
277
|
-
<div className='filters-section__buttons'>
|
|
278
|
-
<Button onClick={() => handleApplyButton(filters)} disabled={!showApplyButton} className={applyButtonClasses.join(' ')}>
|
|
279
|
-
{filterConstants.buttonText}
|
|
280
|
-
</Button>
|
|
281
|
-
<a href='#!' role='button' onClick={handleReset}>
|
|
282
|
-
{filterConstants.resetText}
|
|
283
|
-
</a>
|
|
284
|
-
</div>
|
|
285
|
-
)
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
Filters.TabBar = props => {
|
|
289
|
-
const { filter: singleFilter, index: outerIndex } = props
|
|
290
|
-
return (
|
|
291
|
-
<section className='single-filters__tab-bar'>
|
|
292
|
-
{singleFilter.values.map((filter, index) => {
|
|
293
|
-
const buttonClassList = ['button__tab-bar', singleFilter.active === filter ? 'button__tab-bar--active' : '']
|
|
294
|
-
return (
|
|
295
|
-
<button
|
|
296
|
-
id={`${filter}-${outerIndex}-${index}-${id}`}
|
|
297
|
-
className={buttonClassList.join(' ')}
|
|
298
|
-
key={filter}
|
|
299
|
-
onClick={e => {
|
|
300
|
-
changeFilterActive(outerIndex, filter)
|
|
301
|
-
setSelectedFilter(e.target)
|
|
302
|
-
}}
|
|
303
|
-
onKeyDown={e => {
|
|
304
|
-
if (e.keyCode === 13) {
|
|
305
|
-
changeFilterActive(outerIndex, filter)
|
|
306
|
-
setSelectedFilter(e.target)
|
|
307
|
-
}
|
|
308
|
-
}}
|
|
309
|
-
>
|
|
310
|
-
{filter}
|
|
311
|
-
</button>
|
|
312
|
-
)
|
|
313
|
-
})}
|
|
314
|
-
</section>
|
|
315
|
-
)
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
Filters.Pills = props => props.pills
|
|
319
|
-
|
|
320
|
-
Filters.Tabs = props => props.tabs
|
|
321
|
-
|
|
322
|
-
Filters.Dropdown = props => {
|
|
323
|
-
const { index: outerIndex, label, active, filters } = props
|
|
324
|
-
return (
|
|
325
|
-
<select
|
|
326
|
-
id={`filter-${outerIndex}`}
|
|
327
|
-
name={label}
|
|
328
|
-
aria-label={`Filter by ${label}`}
|
|
329
|
-
className='filter-select'
|
|
330
|
-
data-index='0'
|
|
331
|
-
value={active}
|
|
332
|
-
onChange={e => {
|
|
333
|
-
changeFilterActive(outerIndex, e.target.value)
|
|
334
|
-
announceChange(`Filter ${label} value has been changed to ${e.target.value}, please reference the data table to see updated values.`)
|
|
335
|
-
}}
|
|
336
|
-
>
|
|
337
|
-
{filters}
|
|
338
|
-
</select>
|
|
339
|
-
)
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Resolve Filter Styles
|
|
343
|
-
Filters.Style = () => {
|
|
344
|
-
if (filters || filteredData) {
|
|
345
|
-
// Here charts is using config.filters where maps is using a runtime value
|
|
346
|
-
let filtersToLoop = type === 'map' ? filteredData : filters
|
|
347
|
-
|
|
348
|
-
// Remove fromHash if it exists on filters to loop so we can loop nicely
|
|
349
|
-
delete filtersToLoop.fromHash
|
|
350
|
-
|
|
351
|
-
return filtersToLoop.map((singleFilter, outerIndex) => {
|
|
352
|
-
if (singleFilter.showDropdown === false) return
|
|
353
|
-
|
|
354
|
-
const values = []
|
|
355
|
-
const pillValues = []
|
|
356
|
-
const tabValues = []
|
|
357
|
-
const tabBarValues = []
|
|
358
|
-
|
|
359
|
-
const { active, queuedActive, label, filterStyle } = singleFilter
|
|
360
|
-
|
|
361
|
-
handleSorting(singleFilter)
|
|
362
|
-
|
|
363
|
-
singleFilter.values.forEach((filterOption, index) => {
|
|
364
|
-
const pillClassList = ['pill', active === filterOption ? 'pill--active' : null, theme && theme]
|
|
365
|
-
const tabClassList = ['tab', active === filterOption && 'tab--active', theme && theme]
|
|
366
|
-
|
|
367
|
-
pillValues.push(
|
|
368
|
-
<div className='pill__wrapper' key={`pill-${index}`}>
|
|
369
|
-
<button
|
|
370
|
-
id={`${filterOption}-${outerIndex}-${index}-${id}`}
|
|
371
|
-
className={pillClassList.join(' ')}
|
|
372
|
-
onKeyDown={e => {
|
|
373
|
-
if (e.keyCode === 13) {
|
|
374
|
-
changeFilterActive(outerIndex, filterOption)
|
|
375
|
-
setSelectedFilter(e.target)
|
|
376
|
-
}
|
|
377
|
-
}}
|
|
378
|
-
onClick={e => {
|
|
379
|
-
changeFilterActive(outerIndex, filterOption)
|
|
380
|
-
setSelectedFilter(e.target)
|
|
381
|
-
}}
|
|
382
|
-
name={label}
|
|
383
|
-
>
|
|
384
|
-
{filterOption}
|
|
385
|
-
</button>
|
|
386
|
-
</div>
|
|
387
|
-
)
|
|
388
|
-
|
|
389
|
-
values.push(
|
|
390
|
-
<option key={index} value={filterOption} aria-label={filterOption}>
|
|
391
|
-
{singleFilter.labels && singleFilter.labels[filterOption] ? singleFilter.labels[filterOption] : filterOption}
|
|
392
|
-
</option>
|
|
393
|
-
)
|
|
394
|
-
|
|
395
|
-
tabValues.push(
|
|
396
|
-
<button
|
|
397
|
-
id={`${filterOption}-${outerIndex}-${index}-${id}`}
|
|
398
|
-
className={tabClassList.join(' ')}
|
|
399
|
-
onClick={e => {
|
|
400
|
-
changeFilterActive(outerIndex, filterOption)
|
|
401
|
-
setSelectedFilter(e.target)
|
|
402
|
-
}}
|
|
403
|
-
onKeyDown={e => {
|
|
404
|
-
if (e.keyCode === 13) {
|
|
405
|
-
changeFilterActive(outerIndex, filterOption)
|
|
406
|
-
setSelectedFilter(e.target)
|
|
407
|
-
}
|
|
408
|
-
}}
|
|
409
|
-
>
|
|
410
|
-
{filterOption}
|
|
411
|
-
</button>
|
|
412
|
-
)
|
|
413
|
-
|
|
414
|
-
tabBarValues.push(filterOption)
|
|
415
|
-
})
|
|
416
|
-
|
|
417
|
-
const classList = ['single-filters', mobileFilterStyle ? 'single-filters--dropdown' : `single-filters--${filterStyle}`]
|
|
418
|
-
|
|
419
|
-
return (
|
|
420
|
-
<div className={classList.join(' ')} key={outerIndex}>
|
|
421
|
-
<>
|
|
422
|
-
{label && <label htmlFor={`filter-${outerIndex}`}>{label}</label>}
|
|
423
|
-
{filterStyle === 'tab' && !mobileFilterStyle && <Filters.Tabs tabs={tabValues} />}
|
|
424
|
-
{filterStyle === 'pill' && !mobileFilterStyle && <Filters.Pills pills={pillValues} />}
|
|
425
|
-
{filterStyle === 'tab bar' && !mobileFilterStyle && <Filters.TabBar filter={singleFilter} index={outerIndex} />}
|
|
426
|
-
{(filterStyle === 'dropdown' || mobileFilterStyle) && <Filters.Dropdown filter={singleFilter} index={outerIndex} label={label} active={queuedActive || active} filters={values} />}
|
|
427
|
-
</>
|
|
428
|
-
</div>
|
|
429
|
-
)
|
|
430
|
-
})
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
if (visualizationConfig?.filters?.length === 0) return
|
|
435
|
-
return (
|
|
436
|
-
<Filters>
|
|
437
|
-
<Filters.Section>
|
|
438
|
-
<Filters.Style />
|
|
439
|
-
<Filters.ApplyBehavior />
|
|
440
|
-
</Filters.Section>
|
|
441
|
-
</Filters>
|
|
442
|
-
)
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
Filters.propTypes = {
|
|
446
|
-
// runtimeFilters in place
|
|
447
|
-
filteredData: PropTypes.array,
|
|
448
|
-
// function for updating the runtime filters
|
|
449
|
-
setFilteredData: PropTypes.func,
|
|
450
|
-
// the full apps config
|
|
451
|
-
config: PropTypes.object,
|
|
452
|
-
// updating function for setting fitlerBehavior
|
|
453
|
-
setConfig: PropTypes.func,
|
|
454
|
-
// exclusions
|
|
455
|
-
excludedData: PropTypes.array,
|
|
456
|
-
// function for filtering the data
|
|
457
|
-
filterData: PropTypes.func,
|
|
458
|
-
dimensions: PropTypes.array
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
export default Filters
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
|
|
3
|
-
export default function LegendCircle({ fill, borderColor, display = 'inline-block', viewport }) {
|
|
4
|
-
const dimensions = ['sm', 'xs', 'xxs'].includes(viewport) ? { width: '0.7em', height: '0.7em' } : { width: '1em', height: '1em' }
|
|
5
|
-
const styles = {
|
|
6
|
-
marginRight: '5px',
|
|
7
|
-
borderRadius: '300px',
|
|
8
|
-
verticalAlign: 'middle',
|
|
9
|
-
display: display,
|
|
10
|
-
height: dimensions.height || '1em',
|
|
11
|
-
width: dimensions.width || '1em',
|
|
12
|
-
border: borderColor ? `${borderColor} 1px solid` : 'rgba(0,0,0,.3) 1px solid',
|
|
13
|
-
backgroundColor: fill
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return <span className='legend-item' style={styles} />
|
|
17
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
export function getQueryStringFilterValue(filter) {
|
|
2
|
-
const urlParams = new URLSearchParams(window.location.search)
|
|
3
|
-
if(filter.setByQueryParameter){ // Only check the query string if the filter is supposed to be set by QS param
|
|
4
|
-
const filterValue = urlParams.get(filter.setByQueryParameter)
|
|
5
|
-
if(filterValue && filter.values){
|
|
6
|
-
for(let i = 0; i < filter.values.length; i++){
|
|
7
|
-
if(filter.values[i] && filter.values[i].toLowerCase() === filterValue.toLowerCase()){
|
|
8
|
-
return filter.values[i]
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function getQueryParams(filter) {
|
|
16
|
-
const queryParams = {};
|
|
17
|
-
for (const [key, value] of (new URLSearchParams(window.location.search)).entries()) {
|
|
18
|
-
queryParams[key] = value
|
|
19
|
-
}
|
|
20
|
-
return queryParams;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function updateQueryString(queryParams) {
|
|
24
|
-
const updateUrl = `${window.location.origin}${window.location.pathname}?${Object.keys(queryParams).map(queryParam => `${queryParam}=${encodeURIComponent(queryParams[queryParam])}`).join('&')}`;
|
|
25
|
-
window.history.pushState({path: updateUrl}, '', updateUrl);
|
|
26
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export function updatePaletteNames(colorPalettes) {
|
|
2
|
-
// this function adds REVERSE keyword to each palette
|
|
3
|
-
delete colorPalettes.qualitative9 // delete palette before reversing
|
|
4
|
-
let palettereversed = {}
|
|
5
|
-
for (const [paletteName, hexCodeArr] of Object.entries(colorPalettes)) {
|
|
6
|
-
const paletteStr = String(paletteName)
|
|
7
|
-
|
|
8
|
-
if (!paletteStr.endsWith('reverse')) {
|
|
9
|
-
let palette = paletteStr.concat('reverse') // add to the end of the string "reverse"
|
|
10
|
-
palettereversed[palette] = [...hexCodeArr].reverse() // reverses arrays elements and create new keys on object
|
|
11
|
-
} else {
|
|
12
|
-
palettereversed = { ...colorPalettes }
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
return { ...palettereversed, ...colorPalettes }
|
|
16
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type BaseVisualizationType = 'dashboard' | 'chart' | 'map' | 'data-bite' | 'waffle-chart' | 'markup-include' | 'filtered-text' | 'filter-dropdowns' | 'table' | 'navigation'
|
|
File without changes
|
|
File without changes
|