@cdc/dashboard 4.25.6-2 → 4.25.7
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 +63503 -41005
- package/package.json +9 -9
- package/src/CdcDashboardComponent.tsx +11 -8
- package/src/components/Header/Header.tsx +2 -2
- package/src/components/VisualizationRow.tsx +324 -324
- package/src/helpers/getVizConfig.ts +1 -0
- package/src/helpers/getVizRowColumnLocator.ts +10 -10
- package/src/helpers/reloadURLHelpers.ts +124 -124
- package/src/store/dashboard.reducer.ts +288 -288
- package/src/types/ConfigRow.ts +19 -19
- package/src/types/Dashboard.ts +11 -11
- package/examples/private/DEV-10120.json +0 -1294
- package/examples/private/DEV-10527.json +0 -845
- package/examples/private/DEV-10586.json +0 -54319
- package/examples/private/DEV-10856.json +0 -54319
- package/examples/private/DEV-9199.json +0 -606
- package/examples/private/DEV-9644.json +0 -20092
- package/examples/private/DEV-9684.json +0 -2135
- package/examples/private/DEV-9932.json +0 -95
- package/examples/private/DEV-9989.json +0 -229
- package/examples/private/art-dashboard.json +0 -18174
- package/examples/private/art-scratch.json +0 -2406
- package/examples/private/bird-flu-2.json +0 -440
- package/examples/private/bird-flu.json +0 -413
- package/examples/private/crashing-sidebar.json +0 -975
- package/examples/private/d.json +0 -1561
- package/examples/private/dashboard-config-ehdi.json +0 -29915
- package/examples/private/dashboard-map-filter.json +0 -815
- package/examples/private/dashboard-margins.js +0 -15
- package/examples/private/dataset.json +0 -1452
- package/examples/private/dev-10856-2.json +0 -1348
- package/examples/private/ehdi-data.json +0 -29502
- package/examples/private/exposure-source-h5-data.csv +0 -26
- package/examples/private/fatal-data.csv +0 -3159
- package/examples/private/feelings.json +0 -1
- package/examples/private/gaza-issue.json +0 -1214
- package/examples/private/josh.json +0 -6175
- package/examples/private/map-issue.json +0 -628
- package/examples/private/markup.json +0 -115
- package/examples/private/mpox.json +0 -429
- package/examples/private/nhis.json +0 -1792
- package/examples/private/testing-pie.json +0 -436
- package/examples/private/workforce.json +0 -2041
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { ConfigRow } from '../types/ConfigRow'
|
|
2
|
-
|
|
3
|
-
// returns a dictionary of widget names and their corresponding row and column index
|
|
4
|
-
export const getVizRowColumnLocator = (rows: ConfigRow[]): Record<string, { row: number; column: number }> =>
|
|
5
|
-
rows.reduce((acc, curr, index) => {
|
|
6
|
-
curr.columns?.forEach((column, columnIndex) => {
|
|
7
|
-
if (column.widget !== undefined) acc[column.widget] = { row: index, column: columnIndex }
|
|
8
|
-
})
|
|
9
|
-
return acc
|
|
10
|
-
}, {})
|
|
1
|
+
import { ConfigRow } from '../types/ConfigRow'
|
|
2
|
+
|
|
3
|
+
// returns a dictionary of widget names and their corresponding row and column index
|
|
4
|
+
export const getVizRowColumnLocator = (rows: ConfigRow[]): Record<string, { row: number; column: number }> =>
|
|
5
|
+
rows.reduce((acc, curr, index) => {
|
|
6
|
+
curr.columns?.forEach((column, columnIndex) => {
|
|
7
|
+
if (column.widget !== undefined) acc[column.widget] = { row: index, column: columnIndex }
|
|
8
|
+
})
|
|
9
|
+
return acc
|
|
10
|
+
}, {})
|
|
@@ -1,124 +1,124 @@
|
|
|
1
|
-
import { gatherQueryParams } from '@cdc/core/helpers/gatherQueryParams'
|
|
2
|
-
import { SharedFilter } from '../types/SharedFilter'
|
|
3
|
-
import { capitalizeSplitAndJoin } from '@cdc/core/helpers/cove/string'
|
|
4
|
-
import { AnyVisualization, Visualization } from '@cdc/core/types/Visualization'
|
|
5
|
-
import _ from 'lodash'
|
|
6
|
-
import { DashboardConfig } from '../types/DashboardConfig'
|
|
7
|
-
import { ConfigRow } from '../types/ConfigRow'
|
|
8
|
-
import { getVizRowColumnLocator } from './getVizRowColumnLocator'
|
|
9
|
-
|
|
10
|
-
export const isUpdateNeeded = (
|
|
11
|
-
filters: SharedFilter[],
|
|
12
|
-
currentQueryParams: Record<string, string>,
|
|
13
|
-
newQueryParams: Record<string, string>
|
|
14
|
-
): boolean => {
|
|
15
|
-
let needsUpdate = false
|
|
16
|
-
filters.find(filter => {
|
|
17
|
-
if (filter.type === 'urlfilter' && !Array.isArray(filter.active) && filter.filterBy === 'File Name') {
|
|
18
|
-
needsUpdate = true
|
|
19
|
-
return true
|
|
20
|
-
}
|
|
21
|
-
})
|
|
22
|
-
Object.keys(newQueryParams).forEach(updatedParam => {
|
|
23
|
-
if (decodeURIComponent(newQueryParams[updatedParam]) !== currentQueryParams[updatedParam]) {
|
|
24
|
-
needsUpdate = true
|
|
25
|
-
}
|
|
26
|
-
})
|
|
27
|
-
return needsUpdate
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
type GetDatasetKeysParams = Pick<DashboardConfig, 'visualizations' | 'datasets' | 'rows'>
|
|
31
|
-
export const getDatasetKeys = ({ visualizations, datasets, rows }: GetDatasetKeysParams): string[] => {
|
|
32
|
-
const vizDataKeys = Object.values(visualizations).map(viz => viz.dataKey)
|
|
33
|
-
const rowDataKeys = rows.map(row => row.dataKey)
|
|
34
|
-
const footnoteDataKeys = Object.values(visualizations)
|
|
35
|
-
.map(viz => viz.footnotes?.dataKey)
|
|
36
|
-
.filter(Boolean)
|
|
37
|
-
// ensure to only load datasets for the specific dashboard tab.
|
|
38
|
-
const datasetsUsedByDashboard = _.uniq([...vizDataKeys, ...rowDataKeys, ...footnoteDataKeys])
|
|
39
|
-
return Object.keys(datasets).filter(datasetKey => datasetsUsedByDashboard.includes(datasetKey))
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const getDataURL = (updatedQSParams: Record<string, string | string[]>, dataUrl: URL, newFileName: string) => {
|
|
43
|
-
const _params = Object.keys(updatedQSParams).flatMap(key => {
|
|
44
|
-
const value = updatedQSParams[key]
|
|
45
|
-
if (value === undefined) return []
|
|
46
|
-
if (typeof value === 'string' && (value as String).match(/undefined/)) return []
|
|
47
|
-
if (Array.isArray(value)) return value.map(v => ({ key, value: v }))
|
|
48
|
-
return { key, value }
|
|
49
|
-
})
|
|
50
|
-
const baseURL = dataUrl.origin + dataUrl.pathname
|
|
51
|
-
let dataUrlFinal = `${baseURL}${gatherQueryParams(baseURL, _params)}`
|
|
52
|
-
|
|
53
|
-
if (newFileName !== '') {
|
|
54
|
-
const fileExtension = dataUrl.pathname.split('.').pop()
|
|
55
|
-
const pathWithoutFilename = dataUrl.pathname.substring(0, dataUrl.pathname.lastIndexOf('/'))
|
|
56
|
-
dataUrlFinal = `${dataUrl.origin}${pathWithoutFilename}/${newFileName}.${fileExtension}${gatherQueryParams(
|
|
57
|
-
baseURL,
|
|
58
|
-
_params
|
|
59
|
-
)}`
|
|
60
|
-
}
|
|
61
|
-
return dataUrlFinal
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export const getNewFileName = (newFileName: string, filter: SharedFilter, datasetKey: string) => {
|
|
65
|
-
const replacements = {
|
|
66
|
-
'Remove Spaces': '',
|
|
67
|
-
'Keep Spaces': ' ',
|
|
68
|
-
'Replace With Underscore': '_'
|
|
69
|
-
}
|
|
70
|
-
let fileName = newFileName
|
|
71
|
-
if (filter.datasetKey === datasetKey) {
|
|
72
|
-
if (filter.fileName) {
|
|
73
|
-
// if a file name is found, ie, state_${query}, use that, ie. state_activeFilter.json
|
|
74
|
-
fileName = capitalizeSplitAndJoin.call(
|
|
75
|
-
String(filter.fileName),
|
|
76
|
-
' ',
|
|
77
|
-
replacements[filter.whitespaceReplacement ?? 'Keep Spaces']
|
|
78
|
-
)
|
|
79
|
-
} else {
|
|
80
|
-
// if no file name is entered use the default active filter. ie. /activeFilter.json
|
|
81
|
-
fileName = filter.active as string
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (fileName?.includes('${query}')) {
|
|
86
|
-
fileName = fileName.replace(
|
|
87
|
-
'${query}',
|
|
88
|
-
capitalizeSplitAndJoin.call(
|
|
89
|
-
String(filter.active),
|
|
90
|
-
' ',
|
|
91
|
-
replacements[filter.whitespaceReplacement ?? 'Keep Spaces']
|
|
92
|
-
)
|
|
93
|
-
)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return fileName
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export const getVisualizationsWithFormattedData = (visualizations: Record<string, Visualization>, newData: Object) => {
|
|
100
|
-
return Object.keys(visualizations).reduce((acc, vizKey) => {
|
|
101
|
-
const dataKey = visualizations[vizKey].dataKey
|
|
102
|
-
if (newData[dataKey]) {
|
|
103
|
-
acc[vizKey].formattedData = newData[dataKey]
|
|
104
|
-
}
|
|
105
|
-
return acc
|
|
106
|
-
}, _.cloneDeep(visualizations))
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export const filterUsedByDataUrl = (
|
|
110
|
-
filter: SharedFilter,
|
|
111
|
-
datasetKey: string,
|
|
112
|
-
visualizations: Record<string, AnyVisualization>,
|
|
113
|
-
rows: ConfigRow[]
|
|
114
|
-
) => {
|
|
115
|
-
if (!filter.usedBy || !filter.usedBy.length) return true
|
|
116
|
-
const vizUsingFilters = filter.usedBy?.map(vizOrRowKey => visualizations[vizOrRowKey] || rows[vizOrRowKey])
|
|
117
|
-
|
|
118
|
-
return vizUsingFilters?.some(viz => {
|
|
119
|
-
const usedByViz = viz?.dataKey === datasetKey
|
|
120
|
-
// datasetKey might be a key to a dynamic footnotes URL
|
|
121
|
-
const usedByVizFootnote = viz?.footnotes?.dataKey === datasetKey
|
|
122
|
-
return usedByViz || usedByVizFootnote
|
|
123
|
-
})
|
|
124
|
-
}
|
|
1
|
+
import { gatherQueryParams } from '@cdc/core/helpers/gatherQueryParams'
|
|
2
|
+
import { SharedFilter } from '../types/SharedFilter'
|
|
3
|
+
import { capitalizeSplitAndJoin } from '@cdc/core/helpers/cove/string'
|
|
4
|
+
import { AnyVisualization, Visualization } from '@cdc/core/types/Visualization'
|
|
5
|
+
import _ from 'lodash'
|
|
6
|
+
import { DashboardConfig } from '../types/DashboardConfig'
|
|
7
|
+
import { ConfigRow } from '../types/ConfigRow'
|
|
8
|
+
import { getVizRowColumnLocator } from './getVizRowColumnLocator'
|
|
9
|
+
|
|
10
|
+
export const isUpdateNeeded = (
|
|
11
|
+
filters: SharedFilter[],
|
|
12
|
+
currentQueryParams: Record<string, string>,
|
|
13
|
+
newQueryParams: Record<string, string>
|
|
14
|
+
): boolean => {
|
|
15
|
+
let needsUpdate = false
|
|
16
|
+
filters.find(filter => {
|
|
17
|
+
if (filter.type === 'urlfilter' && !Array.isArray(filter.active) && filter.filterBy === 'File Name') {
|
|
18
|
+
needsUpdate = true
|
|
19
|
+
return true
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
Object.keys(newQueryParams).forEach(updatedParam => {
|
|
23
|
+
if (decodeURIComponent(newQueryParams[updatedParam]) !== currentQueryParams[updatedParam]) {
|
|
24
|
+
needsUpdate = true
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
return needsUpdate
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type GetDatasetKeysParams = Pick<DashboardConfig, 'visualizations' | 'datasets' | 'rows'>
|
|
31
|
+
export const getDatasetKeys = ({ visualizations, datasets, rows }: GetDatasetKeysParams): string[] => {
|
|
32
|
+
const vizDataKeys = Object.values(visualizations).map(viz => viz.dataKey)
|
|
33
|
+
const rowDataKeys = rows.map(row => row.dataKey)
|
|
34
|
+
const footnoteDataKeys = Object.values(visualizations)
|
|
35
|
+
.map(viz => viz.footnotes?.dataKey)
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
// ensure to only load datasets for the specific dashboard tab.
|
|
38
|
+
const datasetsUsedByDashboard = _.uniq([...vizDataKeys, ...rowDataKeys, ...footnoteDataKeys])
|
|
39
|
+
return Object.keys(datasets).filter(datasetKey => datasetsUsedByDashboard.includes(datasetKey))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const getDataURL = (updatedQSParams: Record<string, string | string[]>, dataUrl: URL, newFileName: string) => {
|
|
43
|
+
const _params = Object.keys(updatedQSParams).flatMap(key => {
|
|
44
|
+
const value = updatedQSParams[key]
|
|
45
|
+
if (value === undefined) return []
|
|
46
|
+
if (typeof value === 'string' && (value as String).match(/undefined/)) return []
|
|
47
|
+
if (Array.isArray(value)) return value.map(v => ({ key, value: v }))
|
|
48
|
+
return { key, value }
|
|
49
|
+
})
|
|
50
|
+
const baseURL = dataUrl.origin + dataUrl.pathname
|
|
51
|
+
let dataUrlFinal = `${baseURL}${gatherQueryParams(baseURL, _params)}`
|
|
52
|
+
|
|
53
|
+
if (newFileName !== '') {
|
|
54
|
+
const fileExtension = dataUrl.pathname.split('.').pop()
|
|
55
|
+
const pathWithoutFilename = dataUrl.pathname.substring(0, dataUrl.pathname.lastIndexOf('/'))
|
|
56
|
+
dataUrlFinal = `${dataUrl.origin}${pathWithoutFilename}/${newFileName}.${fileExtension}${gatherQueryParams(
|
|
57
|
+
baseURL,
|
|
58
|
+
_params
|
|
59
|
+
)}`
|
|
60
|
+
}
|
|
61
|
+
return dataUrlFinal
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const getNewFileName = (newFileName: string, filter: SharedFilter, datasetKey: string) => {
|
|
65
|
+
const replacements = {
|
|
66
|
+
'Remove Spaces': '',
|
|
67
|
+
'Keep Spaces': ' ',
|
|
68
|
+
'Replace With Underscore': '_'
|
|
69
|
+
}
|
|
70
|
+
let fileName = newFileName
|
|
71
|
+
if (filter.datasetKey === datasetKey) {
|
|
72
|
+
if (filter.fileName) {
|
|
73
|
+
// if a file name is found, ie, state_${query}, use that, ie. state_activeFilter.json
|
|
74
|
+
fileName = capitalizeSplitAndJoin.call(
|
|
75
|
+
String(filter.fileName),
|
|
76
|
+
' ',
|
|
77
|
+
replacements[filter.whitespaceReplacement ?? 'Keep Spaces']
|
|
78
|
+
)
|
|
79
|
+
} else {
|
|
80
|
+
// if no file name is entered use the default active filter. ie. /activeFilter.json
|
|
81
|
+
fileName = filter.active as string
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (fileName?.includes('${query}')) {
|
|
86
|
+
fileName = fileName.replace(
|
|
87
|
+
'${query}',
|
|
88
|
+
capitalizeSplitAndJoin.call(
|
|
89
|
+
String(filter.active),
|
|
90
|
+
' ',
|
|
91
|
+
replacements[filter.whitespaceReplacement ?? 'Keep Spaces']
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return fileName
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const getVisualizationsWithFormattedData = (visualizations: Record<string, Visualization>, newData: Object) => {
|
|
100
|
+
return Object.keys(visualizations).reduce((acc, vizKey) => {
|
|
101
|
+
const dataKey = visualizations[vizKey].dataKey
|
|
102
|
+
if (newData[dataKey]) {
|
|
103
|
+
acc[vizKey].formattedData = newData[dataKey]
|
|
104
|
+
}
|
|
105
|
+
return acc
|
|
106
|
+
}, _.cloneDeep(visualizations))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const filterUsedByDataUrl = (
|
|
110
|
+
filter: SharedFilter,
|
|
111
|
+
datasetKey: string,
|
|
112
|
+
visualizations: Record<string, AnyVisualization>,
|
|
113
|
+
rows: ConfigRow[]
|
|
114
|
+
) => {
|
|
115
|
+
if (!filter.usedBy || !filter.usedBy.length) return true
|
|
116
|
+
const vizUsingFilters = filter.usedBy?.map(vizOrRowKey => visualizations[vizOrRowKey] || rows[vizOrRowKey])
|
|
117
|
+
|
|
118
|
+
return vizUsingFilters?.some(viz => {
|
|
119
|
+
const usedByViz = viz?.dataKey === datasetKey
|
|
120
|
+
// datasetKey might be a key to a dynamic footnotes URL
|
|
121
|
+
const usedByVizFootnote = viz?.footnotes?.dataKey === datasetKey
|
|
122
|
+
return usedByViz || usedByVizFootnote
|
|
123
|
+
})
|
|
124
|
+
}
|