@cdc/dashboard 4.23.4 → 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/cdcdashboard.js +83294 -85823
- package/index.html +24 -26
- package/package.json +9 -9
- package/src/CdcDashboard.jsx +116 -22
- package/src/components/Header.jsx +191 -67
- package/src/data/initial-state.js +2 -1
package/index.html
CHANGED
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
|
6
|
+
<style>
|
|
7
|
+
body {
|
|
8
|
+
margin: 0 auto !important;
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
min-height: unset !important;
|
|
13
|
+
}
|
|
3
14
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
margin: 0 auto !important;
|
|
10
|
-
display: flex;
|
|
11
|
-
flex-direction: column;
|
|
12
|
-
justify-content: center;
|
|
13
|
-
min-height: unset !important;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
.react-container+.react-container {
|
|
17
|
-
margin-top: 3rem;
|
|
18
|
-
}
|
|
19
|
-
</style>
|
|
20
|
-
</head>
|
|
21
|
-
|
|
22
|
-
<body>
|
|
23
|
-
<!-- <div class="react-container" data-config="/examples/default.json"></div> -->
|
|
24
|
-
<!-- <div class="react-container" data-config="/examples/default-multi-dataset.json"></div> -->
|
|
25
|
-
<div class="react-container" data-config="/examples/default-filter-control.json"></div>
|
|
26
|
-
<!-- <div class="react-container" data-config="/examples/private/totals.json"></div> -->
|
|
27
|
-
<!-- <div class="react-container" data-config="/examples/private/totals-two.json"></div> -->
|
|
28
|
-
<script type="module" src="./src/index.jsx"></script>
|
|
29
|
-
</body>
|
|
15
|
+
.react-container + .react-container {
|
|
16
|
+
margin-top: 3rem;
|
|
17
|
+
}
|
|
18
|
+
</style>
|
|
19
|
+
</head>
|
|
30
20
|
|
|
21
|
+
<body>
|
|
22
|
+
<div class="react-container" data-config="/examples/default.json"></div>
|
|
23
|
+
<!-- <div class="react-container" data-config="/examples/default-multi-dataset.json"></div> -->
|
|
24
|
+
<!-- <div class="react-container" data-config="/examples/default-filter-control.json"></div> -->
|
|
25
|
+
<!-- <div class="react-container" data-config="/examples/private/totals.json"></div> -->
|
|
26
|
+
<!-- <div class="react-container" data-config="/examples/private/totals-two.json"></div> -->
|
|
27
|
+
<script type="module" src="./src/index.jsx"></script>
|
|
28
|
+
</body>
|
|
31
29
|
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/dashboard",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.5",
|
|
4
4
|
"description": "React component for combining multiple visualizations into a single dashboard",
|
|
5
5
|
"moduleName": "CdcDashboard",
|
|
6
6
|
"main": "dist/cdcdashboard",
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
},
|
|
26
26
|
"license": "Apache-2.0",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@cdc/chart": "^4.23.
|
|
29
|
-
"@cdc/core": "^4.23.
|
|
30
|
-
"@cdc/data-bite": "^4.23.
|
|
31
|
-
"@cdc/filtered-text": "^4.23.
|
|
32
|
-
"@cdc/map": "^4.23.
|
|
33
|
-
"@cdc/markup-include": "^4.23.
|
|
34
|
-
"@cdc/waffle-chart": "^4.23.
|
|
28
|
+
"@cdc/chart": "^4.23.5",
|
|
29
|
+
"@cdc/core": "^4.23.5",
|
|
30
|
+
"@cdc/data-bite": "^4.23.5",
|
|
31
|
+
"@cdc/filtered-text": "^4.23.5",
|
|
32
|
+
"@cdc/map": "^4.23.5",
|
|
33
|
+
"@cdc/markup-include": "^4.23.5",
|
|
34
|
+
"@cdc/waffle-chart": "^4.23.5",
|
|
35
35
|
"html-react-parser": "^3.0.8",
|
|
36
36
|
"js-base64": "^2.5.2",
|
|
37
37
|
"papaparse": "^5.3.0",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"react": "^18.2.0",
|
|
48
48
|
"react-dom": "^18.2.0"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "34add3436994ca3cf13e51f313add4d70377f53e"
|
|
51
51
|
}
|
package/src/CdcDashboard.jsx
CHANGED
|
@@ -11,7 +11,6 @@ import { HTML5Backend } from 'react-dnd-html5-backend'
|
|
|
11
11
|
import parse from 'html-react-parser'
|
|
12
12
|
|
|
13
13
|
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
14
|
-
import cacheBustingString from '@cdc/core/helpers/cacheBustingString'
|
|
15
14
|
import { GlobalContextProvider } from '@cdc/core/components/GlobalContext'
|
|
16
15
|
import ConfigContext from './ConfigContext'
|
|
17
16
|
|
|
@@ -115,25 +114,106 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
115
114
|
|
|
116
115
|
const transform = new DataTransform()
|
|
117
116
|
|
|
117
|
+
const getFormattedData = (data, dataDescription) => {
|
|
118
|
+
if (data && dataDescription) {
|
|
119
|
+
try {
|
|
120
|
+
let formattedData = transform.autoStandardize(data)
|
|
121
|
+
formattedData = transform.developerStandardize(data, dataDescription)
|
|
122
|
+
return formattedData
|
|
123
|
+
} catch (e) {
|
|
124
|
+
return data
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return data
|
|
129
|
+
}
|
|
130
|
+
|
|
118
131
|
const processData = async config => {
|
|
119
132
|
let dataset = config.formattedData || config.data
|
|
120
133
|
|
|
121
134
|
if (config.dataUrl) {
|
|
122
|
-
dataset = await fetchRemoteData(`${config.dataUrl}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
dataset = transform.autoStandardize(data)
|
|
127
|
-
dataset = transform.developerStandardize(data, config.dataDescription)
|
|
128
|
-
} catch (e) {
|
|
129
|
-
//Data not able to be standardized, leave as is
|
|
130
|
-
}
|
|
131
|
-
}
|
|
135
|
+
dataset = await fetchRemoteData(`${config.dataUrl}`)
|
|
136
|
+
|
|
137
|
+
dataset = getFormattedData(dataset, config.dataDescription)
|
|
132
138
|
}
|
|
133
139
|
|
|
134
140
|
return dataset
|
|
135
141
|
}
|
|
136
142
|
|
|
143
|
+
const reloadURLData = async () => {
|
|
144
|
+
if (config.datasets) {
|
|
145
|
+
let newData = { ...data }
|
|
146
|
+
let newDatasets = { ...config.datasets }
|
|
147
|
+
let datasetsNeedsUpdate = false
|
|
148
|
+
let datasetKeys = Object.keys(config.datasets)
|
|
149
|
+
for (let i = 0; i < datasetKeys.length; i++) {
|
|
150
|
+
const dataset = config.datasets[datasetKeys[i]]
|
|
151
|
+
if (dataset.dataUrl && config.dashboard && config.dashboard.sharedFilters) {
|
|
152
|
+
const dataUrl = new URL(dataset.runtimeDataUrl || dataset.dataUrl)
|
|
153
|
+
let qsParams = Object.fromEntries(new URLSearchParams(dataUrl.search))
|
|
154
|
+
|
|
155
|
+
let isUpdateNeeded = false
|
|
156
|
+
|
|
157
|
+
config.dashboard.sharedFilters.forEach(filter => {
|
|
158
|
+
if (filter.type === 'url' && qsParams[filter.queryParameter] !== decodeURIComponent(filter.active)) {
|
|
159
|
+
qsParams[filter.queryParameter] = filter.active
|
|
160
|
+
isUpdateNeeded = true
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
if (!isUpdateNeeded) return
|
|
165
|
+
|
|
166
|
+
let dataUrlFinal = `${dataUrl.origin}${dataUrl.pathname}${Object.keys(qsParams)
|
|
167
|
+
.map((param, i) => {
|
|
168
|
+
let qs = i === 0 ? '?' : '&'
|
|
169
|
+
qs += param + '='
|
|
170
|
+
qs += qsParams[param]
|
|
171
|
+
return qs
|
|
172
|
+
})
|
|
173
|
+
.join('')}`
|
|
174
|
+
|
|
175
|
+
let newDataset = await fetchRemoteData(`${dataUrlFinal}`)
|
|
176
|
+
|
|
177
|
+
if (newDataset && dataset.dataDescription) {
|
|
178
|
+
try {
|
|
179
|
+
newDataset = transform.autoStandardize(newDataset)
|
|
180
|
+
newDataset = transform.developerStandardize(newDataset, dataset.dataDescription)
|
|
181
|
+
} catch (e) {
|
|
182
|
+
//Data not able to be standardized, leave as is
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
newDatasets[datasetKeys[i]].runtimeDataUrl = dataUrlFinal
|
|
187
|
+
newData[datasetKeys[i]] = newDataset
|
|
188
|
+
datasetsNeedsUpdate = true
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (datasetsNeedsUpdate) {
|
|
193
|
+
setData(newData)
|
|
194
|
+
|
|
195
|
+
let newFilteredData = {}
|
|
196
|
+
let newConfig = { ...config }
|
|
197
|
+
Object.keys(config.visualizations).forEach(key => {
|
|
198
|
+
let dataKey = config.visualizations[key].dataKey
|
|
199
|
+
|
|
200
|
+
let applicableFilters = config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(key) !== -1)
|
|
201
|
+
if (applicableFilters.length > 0) {
|
|
202
|
+
newFilteredData[key] = filterData(applicableFilters, newData[dataKey])
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (newData[dataKey]) {
|
|
206
|
+
newConfig.visualizations[key].formattedData = newData[dataKey]
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
setFilteredData(newFilteredData)
|
|
210
|
+
|
|
211
|
+
newConfig.datasets = newDatasets
|
|
212
|
+
setConfig(newConfig)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
137
217
|
const loadConfig = async () => {
|
|
138
218
|
let response = configObj || (await (await fetch(configUrl)).json())
|
|
139
219
|
let newConfig = { ...defaults, ...response }
|
|
@@ -201,7 +281,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
201
281
|
let add = true
|
|
202
282
|
|
|
203
283
|
filters.forEach(filter => {
|
|
204
|
-
if (row[filter.columnName]
|
|
284
|
+
if (filter.type !== 'url' && row[filter.columnName] != filter.active) {
|
|
205
285
|
add = false
|
|
206
286
|
}
|
|
207
287
|
})
|
|
@@ -230,7 +310,9 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
230
310
|
if (applicableFilters.length > 0) {
|
|
231
311
|
const visualization = newConfig.visualizations[visualizationKey]
|
|
232
312
|
|
|
233
|
-
|
|
313
|
+
const formattedData = visualization.dataDescription ? getFormattedData(data[visualization.dataKey] || visualization.data, visualization.dataDescription) : undefined
|
|
314
|
+
|
|
315
|
+
newFilteredData[visualizationKey] = filterData(applicableFilters, formattedData || data[visualization.dataKey])
|
|
234
316
|
}
|
|
235
317
|
})
|
|
236
318
|
|
|
@@ -291,7 +373,11 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
291
373
|
let applicableFilters = newConfig.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(visualizationKey) !== -1)
|
|
292
374
|
|
|
293
375
|
if (applicableFilters.length > 0) {
|
|
294
|
-
|
|
376
|
+
const visualization = newConfig.visualizations[visualizationKey]
|
|
377
|
+
|
|
378
|
+
const formattedData = getFormattedData(visualization.data, visualization.dataDescription)
|
|
379
|
+
|
|
380
|
+
newFilteredData[visualizationKey] = filterData(applicableFilters, formattedData || visualization.data || (dataOverride || data)[visualization.dataKey])
|
|
295
381
|
}
|
|
296
382
|
})
|
|
297
383
|
}
|
|
@@ -315,6 +401,10 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
315
401
|
}
|
|
316
402
|
}, [config])
|
|
317
403
|
|
|
404
|
+
useEffect(() => {
|
|
405
|
+
reloadURLData()
|
|
406
|
+
}, [JSON.stringify(config.dashboard ? config.dashboard.sharedFilters : undefined)])
|
|
407
|
+
|
|
318
408
|
const updateChildConfig = (visualizationKey, newConfig) => {
|
|
319
409
|
let updatedConfig = { ...config }
|
|
320
410
|
|
|
@@ -336,7 +426,11 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
336
426
|
Object.keys(config.visualizations).forEach(key => {
|
|
337
427
|
let applicableFilters = dashboardConfig.sharedFilters.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(key) !== -1)
|
|
338
428
|
if (applicableFilters.length > 0) {
|
|
339
|
-
|
|
429
|
+
const visualization = config.visualizations[key]
|
|
430
|
+
|
|
431
|
+
const formattedData = visualization.dataDescription ? getFormattedData(data[config.visualizations[key].dataKey] || visualization.data, visualization.dataDescription) : undefined
|
|
432
|
+
|
|
433
|
+
newFilteredData[key] = filterData(applicableFilters, formattedData || data[config.visualizations[key].dataKey])
|
|
340
434
|
}
|
|
341
435
|
})
|
|
342
436
|
|
|
@@ -346,14 +440,14 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
346
440
|
const announceChange = text => {}
|
|
347
441
|
|
|
348
442
|
return config.dashboard.sharedFilters.map((singleFilter, index) => {
|
|
349
|
-
if (!singleFilter.showDropdown) return <></>
|
|
443
|
+
if (singleFilter.type !== 'url' && !singleFilter.showDropdown) return <></>
|
|
350
444
|
|
|
351
445
|
const values = []
|
|
352
446
|
|
|
353
447
|
singleFilter.values.forEach((filterOption, index) => {
|
|
354
448
|
values.push(
|
|
355
449
|
<option key={`${singleFilter.key}-option-${index}`} value={filterOption}>
|
|
356
|
-
{filterOption}
|
|
450
|
+
{singleFilter.labels ? singleFilter.labels[filterOption] || filterOption : filterOption}
|
|
357
451
|
</option>
|
|
358
452
|
)
|
|
359
453
|
})
|
|
@@ -578,7 +672,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
578
672
|
}}
|
|
579
673
|
setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
|
|
580
674
|
isDashboard={true}
|
|
581
|
-
link={config.table && config.table.show && config.datasets && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
|
|
675
|
+
link={config.table && config.table.show && config.datasets && visualizationConfig.table && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
|
|
582
676
|
/>
|
|
583
677
|
)}
|
|
584
678
|
{visualizationConfig.type === 'map' && (
|
|
@@ -592,7 +686,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
592
686
|
setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
|
|
593
687
|
setSharedFilterValue={setSharedFilterValue}
|
|
594
688
|
isDashboard={true}
|
|
595
|
-
link={config.table && config.table.show && config.datasets && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
|
|
689
|
+
link={config.table && config.table.show && config.datasets && visualizationConfig.table && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
|
|
596
690
|
/>
|
|
597
691
|
)}
|
|
598
692
|
{visualizationConfig.type === 'data-bite' && (
|
|
@@ -615,7 +709,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
615
709
|
updateChildConfig(col.widget, newConfig)
|
|
616
710
|
}}
|
|
617
711
|
isDashboard={true}
|
|
618
|
-
link={config.table && config.table.show && config.datasets && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
|
|
712
|
+
link={config.table && config.table.show && config.datasets && visualizationConfig.table && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
|
|
619
713
|
/>
|
|
620
714
|
)}
|
|
621
715
|
{visualizationConfig.type === 'markup-include' && (
|
|
@@ -678,7 +772,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
678
772
|
config.dashboard.sharedFilters.forEach(sharedFilter => {
|
|
679
773
|
let allMatch = true
|
|
680
774
|
vizKeysUsingDataset.forEach(visualizationKey => {
|
|
681
|
-
if (sharedFilter.usedBy.indexOf(visualizationKey) === -1) {
|
|
775
|
+
if (sharedFilter.usedBy && sharedFilter.usedBy.indexOf(visualizationKey) === -1) {
|
|
682
776
|
allMatch = false
|
|
683
777
|
}
|
|
684
778
|
})
|
|
@@ -697,7 +791,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
697
791
|
|
|
698
792
|
return (
|
|
699
793
|
<div className='multi-table-container' id={`data-table-${datasetKey}`} key={`data-table-${datasetKey}`}>
|
|
700
|
-
<DataTable data={filteredTableData || config.datasets[datasetKey].data} downloadData={config.datasets[datasetKey].data} dataFileSourceType={dataFileSourceType} datasetKey={datasetKey} config={config} imageRef={imageId}></DataTable>
|
|
794
|
+
<DataTable data={filteredTableData || config.datasets[datasetKey].data || []} downloadData={config.datasets[datasetKey].data} dataFileSourceType={dataFileSourceType} datasetKey={datasetKey} config={config} imageRef={imageId}></DataTable>
|
|
701
795
|
</div>
|
|
702
796
|
)
|
|
703
797
|
})}
|
|
@@ -2,6 +2,7 @@ import React, { useState, useEffect, useContext } from 'react'
|
|
|
2
2
|
|
|
3
3
|
import ConfigContext from '../ConfigContext'
|
|
4
4
|
|
|
5
|
+
import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
5
6
|
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
6
7
|
import { useGlobalContext } from '@cdc/core/components/GlobalContext'
|
|
7
8
|
import Modal from '@cdc/core/components/ui/Modal'
|
|
@@ -13,6 +14,8 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
13
14
|
|
|
14
15
|
const [columns, setColumns] = useState([])
|
|
15
16
|
|
|
17
|
+
const transform = new DataTransform()
|
|
18
|
+
|
|
16
19
|
const changeConfigValue = (parentObj, key, value) => {
|
|
17
20
|
let newConfig = { ...config }
|
|
18
21
|
if (!newConfig[parentObj]) newConfig[parentObj] = {}
|
|
@@ -45,6 +48,13 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
45
48
|
|
|
46
49
|
dashboardConfig.sharedFilters.splice(index, 1)
|
|
47
50
|
|
|
51
|
+
// Ensures URL filters refresh after filter removal
|
|
52
|
+
if(dashboardConfig.datasets){
|
|
53
|
+
Object.keys(dashboardConfig.datasets).forEach(datasetKey => {
|
|
54
|
+
delete dashboardConfig.datasets[datasetKey].runtimeDataUrl
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
48
58
|
updateConfig({ ...config, dashboard: dashboardConfig })
|
|
49
59
|
|
|
50
60
|
overlay?.actions.toggleOverlay()
|
|
@@ -90,7 +100,7 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
90
100
|
if (config.datasets[dataKeys[i]].dataDescription) {
|
|
91
101
|
try {
|
|
92
102
|
config.datasets[dataKeys[i]].data = transform.autoStandardize(config.datasets[dataKeys[i]].data)
|
|
93
|
-
config.datasets[dataKeys[i]].data = transform.developerStandardize(config.datasets[dataKeys[i]].data, config.datasets[
|
|
103
|
+
config.datasets[dataKeys[i]].data = transform.developerStandardize(config.datasets[dataKeys[i]].data, config.datasets[dataKeys[i]].dataDescription)
|
|
94
104
|
} catch (e) {
|
|
95
105
|
//Data not able to be standardized, leave as is
|
|
96
106
|
}
|
|
@@ -98,7 +108,7 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
98
108
|
}
|
|
99
109
|
|
|
100
110
|
if (config.datasets[dataKeys[i]].data) {
|
|
101
|
-
config.datasets[dataKeys[i]].data.
|
|
111
|
+
config.datasets[dataKeys[i]].data.forEach(row => {
|
|
102
112
|
Object.keys(row).forEach(columnName => (columns[columnName] = true))
|
|
103
113
|
})
|
|
104
114
|
}
|
|
@@ -128,6 +138,14 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
128
138
|
overlay?.actions.openOverlay(filterModal(newFilter, index))
|
|
129
139
|
}
|
|
130
140
|
|
|
141
|
+
const updateFilterPropByFunction = (index, func) => {
|
|
142
|
+
let newFilter = { ...filter }
|
|
143
|
+
|
|
144
|
+
newFilter = func(newFilter)
|
|
145
|
+
|
|
146
|
+
overlay?.actions.openOverlay(filterModal(newFilter, index))
|
|
147
|
+
}
|
|
148
|
+
|
|
131
149
|
const addFilterUsedBy = (filter, index, value) => {
|
|
132
150
|
if (!filter.usedBy) filter.usedBy = []
|
|
133
151
|
filter.usedBy.push(value)
|
|
@@ -142,6 +160,47 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
142
160
|
}
|
|
143
161
|
}
|
|
144
162
|
|
|
163
|
+
const updateLabel = (e, value) => {
|
|
164
|
+
let newLabels = filter.labels || {}
|
|
165
|
+
|
|
166
|
+
newLabels[value] = e.target.value
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
updateFilterProp('labels', index, newLabels)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const removeValue = (valueIndex) => {
|
|
173
|
+
let newLabels = filter.labels || {}
|
|
174
|
+
let newValues = filter.values || []
|
|
175
|
+
|
|
176
|
+
delete newLabels[filter.values[valueIndex]]
|
|
177
|
+
newValues.splice(valueIndex, 1)
|
|
178
|
+
|
|
179
|
+
updateFilterPropByFunction(index, newFilter => {
|
|
180
|
+
newFilter.labels = newLabels
|
|
181
|
+
newFilter.orderedValue = newValues
|
|
182
|
+
return newFilter
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const addNewValue = (e) => {
|
|
187
|
+
e.preventDefault()
|
|
188
|
+
if (!filter.values || filter.values.indexOf(e.target[0].value) === -1) {
|
|
189
|
+
let newValues = filter.values || []
|
|
190
|
+
newValues.push(e.target[0].value)
|
|
191
|
+
|
|
192
|
+
updateFilterPropByFunction(index, newFilter => {
|
|
193
|
+
newFilter.values = newValues
|
|
194
|
+
if (!newFilter.active) {
|
|
195
|
+
newFilter.active = e.target[0].value
|
|
196
|
+
}
|
|
197
|
+
return newFilter
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
e.target[0].value = ''
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
145
204
|
return (
|
|
146
205
|
<Modal>
|
|
147
206
|
<Modal.Content>
|
|
@@ -157,81 +216,146 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
157
216
|
Remove Filter
|
|
158
217
|
</button>
|
|
159
218
|
<label>
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
<
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
<
|
|
198
|
-
|
|
199
|
-
{
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
<ul>
|
|
209
|
-
{filter.usedBy &&
|
|
210
|
-
filter.usedBy.map(vizKey => (
|
|
211
|
-
<li key={`used-by-list-item-${vizKey}`}>
|
|
212
|
-
<span>{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}</span>{' '}
|
|
219
|
+
<span className='edit-label column-heading'>Filter Type:</span>
|
|
220
|
+
<select defaultValue={filter.type || 'data'} onChange={e => updateFilterProp('type', index, e.target.value)}>
|
|
221
|
+
<option value="url">URL</option>
|
|
222
|
+
<option value="data">Data</option>
|
|
223
|
+
</select>
|
|
224
|
+
</label>
|
|
225
|
+
{filter.type === 'url' && <>
|
|
226
|
+
<label>
|
|
227
|
+
<span className='edit-label column-heading'>Label: </span>
|
|
228
|
+
<input
|
|
229
|
+
type='text'
|
|
230
|
+
value={filter.key}
|
|
231
|
+
onChange={e => {
|
|
232
|
+
updateFilterProp('key', index, e.target.value)
|
|
233
|
+
}}
|
|
234
|
+
/>
|
|
235
|
+
</label>
|
|
236
|
+
<label>
|
|
237
|
+
<span className='edit-label column-heading'>URL to Filter: </span>
|
|
238
|
+
<select defaultValue={filter.datasetKey || ''} onChange={e => updateFilterProp('datasetKey', index, e.target.value)}>
|
|
239
|
+
<option value=''>- Select Option -</option>
|
|
240
|
+
{Object.keys(config.datasets).map(datasetKey => {
|
|
241
|
+
if(config.datasets[datasetKey].dataUrl){
|
|
242
|
+
return <option key={datasetKey} value={datasetKey}>{(config.datasets[datasetKey].dataUrl).substring(0, 50)}</option>
|
|
243
|
+
}
|
|
244
|
+
return <React.Fragment key={datasetKey}></React.Fragment>;
|
|
245
|
+
})}
|
|
246
|
+
</select>
|
|
247
|
+
</label>
|
|
248
|
+
<label>
|
|
249
|
+
<span className='edit-label column-heading'>Query string parameter</span>{' '}
|
|
250
|
+
<input
|
|
251
|
+
type='text'
|
|
252
|
+
defaultValue={filter.queryParameter}
|
|
253
|
+
onChange={e => updateFilterProp('queryParameter', index, e.target.value)}
|
|
254
|
+
/>
|
|
255
|
+
</label>
|
|
256
|
+
<span className='edit-label column-heading'>Values</span>{' '}
|
|
257
|
+
<ul className='value-list'>
|
|
258
|
+
{filter.values &&
|
|
259
|
+
filter.values.map((value, valueIndex) => (
|
|
260
|
+
<li>
|
|
261
|
+
{value}
|
|
262
|
+
<input
|
|
263
|
+
type='text'
|
|
264
|
+
value={filter.labels ? filter.labels[value] : undefined}
|
|
265
|
+
onChange={(e) => updateLabel(e, value)}
|
|
266
|
+
/>
|
|
213
267
|
<button
|
|
214
|
-
onClick={
|
|
215
|
-
e.preventDefault()
|
|
216
|
-
removeFilterUsedBy(filter, index, vizKey)
|
|
217
|
-
}}
|
|
268
|
+
onClick={() => removeValue(valueIndex)}
|
|
218
269
|
>
|
|
219
270
|
X
|
|
220
271
|
</button>
|
|
221
272
|
</li>
|
|
222
273
|
))}
|
|
223
274
|
</ul>
|
|
224
|
-
<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
275
|
+
<form
|
|
276
|
+
onSubmit={addNewValue}
|
|
277
|
+
>
|
|
278
|
+
<input type='text' /> <button type='submit'>Add New Value</button>
|
|
279
|
+
</form>
|
|
280
|
+
</>}
|
|
281
|
+
{filter.type !== 'url' && <>
|
|
282
|
+
<label>
|
|
283
|
+
<span className='edit-label column-heading'>Filter: </span>
|
|
284
|
+
<select
|
|
285
|
+
value={filter.columnName}
|
|
286
|
+
onChange={e => {
|
|
287
|
+
updateFilterProp('columnName', index, e.target.value)
|
|
288
|
+
}}
|
|
289
|
+
>
|
|
290
|
+
<option value=''>- Select Option -</option>
|
|
291
|
+
{columns.map(dataKey => (
|
|
292
|
+
<option value={dataKey} key={`filter-column-select-item-${dataKey}`}>
|
|
293
|
+
{dataKey}
|
|
294
|
+
</option>
|
|
295
|
+
))}
|
|
296
|
+
</select>
|
|
297
|
+
</label>
|
|
298
|
+
<label>
|
|
299
|
+
<span className='edit-label column-heading'>Label: </span>
|
|
300
|
+
<input
|
|
301
|
+
type='text'
|
|
302
|
+
value={filter.key}
|
|
303
|
+
onChange={e => {
|
|
304
|
+
updateFilterProp('key', index, e.target.value)
|
|
305
|
+
}}
|
|
306
|
+
/>
|
|
307
|
+
</label>
|
|
308
|
+
<label>
|
|
309
|
+
<span className='edit-label column-heading'>Show Dropdown</span>
|
|
310
|
+
<input
|
|
311
|
+
type='checkbox'
|
|
312
|
+
defaultChecked={filter.showDropdown === true}
|
|
313
|
+
onChange={e => {
|
|
314
|
+
updateFilterProp('showDropdown', index, !filter.showDropdown)
|
|
315
|
+
}}
|
|
316
|
+
/>
|
|
317
|
+
</label>
|
|
318
|
+
<label>
|
|
319
|
+
<span className='edit-label column-heading'>Set By: </span>
|
|
320
|
+
<select value={filter.setBy} onChange={e => updateFilterProp('setBy', index, e.target.value)}>
|
|
321
|
+
<option value=''>- Select Option -</option>
|
|
322
|
+
{Object.keys(config.visualizations).map(vizKey => (
|
|
323
|
+
<option value={vizKey} key={`set-by-select-item-${vizKey}`}>
|
|
230
324
|
{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}
|
|
231
325
|
</option>
|
|
232
326
|
))}
|
|
233
|
-
|
|
234
|
-
|
|
327
|
+
</select>
|
|
328
|
+
</label>
|
|
329
|
+
<label>
|
|
330
|
+
<span className='edit-label column-heading'>Used By:</span>
|
|
331
|
+
<ul>
|
|
332
|
+
{filter.usedBy &&
|
|
333
|
+
filter.usedBy.map(vizKey => (
|
|
334
|
+
<li key={`used-by-list-item-${vizKey}`}>
|
|
335
|
+
<span>{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}</span>{' '}
|
|
336
|
+
<button
|
|
337
|
+
onClick={e => {
|
|
338
|
+
e.preventDefault()
|
|
339
|
+
removeFilterUsedBy(filter, index, vizKey)
|
|
340
|
+
}}
|
|
341
|
+
>
|
|
342
|
+
X
|
|
343
|
+
</button>
|
|
344
|
+
</li>
|
|
345
|
+
))}
|
|
346
|
+
</ul>
|
|
347
|
+
<select onChange={e => addFilterUsedBy(filter, index, e.target.value)}>
|
|
348
|
+
<option value=''>- Select Option -</option>
|
|
349
|
+
{Object.keys(config.visualizations)
|
|
350
|
+
.filter(vizKey => filter.setBy !== vizKey && (!filter.usedBy || filter.usedBy.indexOf(vizKey) === -1) && !config.visualizations[vizKey].usesSharedFilter)
|
|
351
|
+
.map(vizKey => (
|
|
352
|
+
<option value={vizKey} key={`used-by-select-item-${vizKey}`}>
|
|
353
|
+
{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}
|
|
354
|
+
</option>
|
|
355
|
+
))}
|
|
356
|
+
</select>
|
|
357
|
+
</label>
|
|
358
|
+
</>}
|
|
235
359
|
</fieldset>
|
|
236
360
|
<button type='button' className='btn btn-primary' style={{ display: 'inline-block', 'margin-right': '1em' }} onClick={overlay?.actions.toggleOverlay}>
|
|
237
361
|
Cancel
|