@cdc/dashboard 4.25.5-1 → 4.25.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/cdcdashboard.js +80055 -78658
- package/examples/m2.json +32904 -0
- package/examples/special-classes.json +54340 -0
- package/package.json +9 -9
- package/src/CdcDashboardComponent.tsx +36 -214
- package/src/_stories/_mock/api-filter-map.json +1 -0
- package/src/components/CollapsibleVisualizationRow.tsx +2 -4
- package/src/components/DashboardEditors.tsx +143 -0
- package/src/components/DashboardFilters/DashboardFilters.tsx +205 -205
- package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +198 -198
- package/src/components/Header/Header.tsx +7 -9
- package/src/components/Row.tsx +1 -24
- package/src/components/VisualizationRow.tsx +190 -213
- package/src/helpers/getVizConfig.ts +108 -80
- package/src/helpers/getVizRowColumnLocator.ts +0 -1
- package/src/helpers/reloadURLHelpers.ts +10 -13
- package/src/store/dashboard.actions.ts +0 -2
- package/src/store/dashboard.reducer.ts +0 -11
- package/src/types/ConfigRow.ts +0 -1
- package/src/types/Dashboard.ts +1 -1
- package/src/types/DashboardConfig.ts +1 -1
- package/src/types/DataSet.ts +0 -12
|
@@ -1,80 +1,108 @@
|
|
|
1
|
-
import _ from 'lodash'
|
|
2
|
-
import { MultiDashboardConfig } from '../types/MultiDashboard'
|
|
3
|
-
import DataTransform from '@cdc/core/helpers/DataTransform'
|
|
4
|
-
import { getApplicableFilters } from './getFilteredData'
|
|
5
|
-
import { filterData } from './filterData'
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
const transform = new DataTransform()
|
|
9
|
-
|
|
10
|
-
export const getFootnotesVizConfig = (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
1
|
+
import _ from 'lodash'
|
|
2
|
+
import { MultiDashboardConfig } from '../types/MultiDashboard'
|
|
3
|
+
import DataTransform from '@cdc/core/helpers/DataTransform'
|
|
4
|
+
import { getApplicableFilters } from './getFilteredData'
|
|
5
|
+
import { filterData } from './filterData'
|
|
6
|
+
import { AnyVisualization } from '@cdc/core/types/Visualization'
|
|
7
|
+
|
|
8
|
+
const transform = new DataTransform()
|
|
9
|
+
|
|
10
|
+
export const getFootnotesVizConfig = (
|
|
11
|
+
visualizationConfig: AnyVisualization,
|
|
12
|
+
rowNumber: number,
|
|
13
|
+
config: MultiDashboardConfig
|
|
14
|
+
) => {
|
|
15
|
+
if (!visualizationConfig?.footnotes) return visualizationConfig
|
|
16
|
+
const data = _.cloneDeep(config.datasets[visualizationConfig.footnotes.dataKey]?.data)
|
|
17
|
+
const dataColumns = data?.length ? Object.keys(data[0]) : []
|
|
18
|
+
const filters = (getApplicableFilters(config.dashboard, rowNumber) || []).filter(filter =>
|
|
19
|
+
dataColumns.includes(filter.columnName)
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
const newConfig = _.cloneDeep(visualizationConfig)
|
|
23
|
+
|
|
24
|
+
if (filters.length) {
|
|
25
|
+
_.set(newConfig, 'footnotes.data', filterData(filters, data))
|
|
26
|
+
} else {
|
|
27
|
+
_.set(newConfig, 'footnotes.data', data)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return newConfig
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const getVizConfig = (
|
|
34
|
+
visualizationKey: string,
|
|
35
|
+
rowNumber: number,
|
|
36
|
+
config: MultiDashboardConfig,
|
|
37
|
+
data: Object,
|
|
38
|
+
filteredData?: Object,
|
|
39
|
+
filteredDataOverride?: Object[],
|
|
40
|
+
multiVizColumn?: string
|
|
41
|
+
): AnyVisualization => {
|
|
42
|
+
if (rowNumber === undefined) return {} as AnyVisualization
|
|
43
|
+
const visualizationConfig = _.cloneDeep(config.visualizations[visualizationKey])
|
|
44
|
+
const rowData = config.rows[rowNumber]
|
|
45
|
+
if (visualizationConfig.footnotes?.dataKey) {
|
|
46
|
+
_.set(visualizationConfig, 'footnotes.data', config.datasets[visualizationConfig.footnotes.dataKey]?.data)
|
|
47
|
+
}
|
|
48
|
+
if (rowData?.dataKey) {
|
|
49
|
+
// data configured on the row
|
|
50
|
+
Object.assign(visualizationConfig, _.pick(rowData, ['dataKey', 'dataDescription', 'formattedData', 'data']))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (visualizationConfig.table && config.dashboard.sharedFilters.length) {
|
|
54
|
+
// Download CSV button needs to know to include shared filter columns
|
|
55
|
+
const sharedFilterColumns = config.dashboard.sharedFilters.reduce((acc, filter) => {
|
|
56
|
+
if (!filter.usedBy?.length || filter.usedBy?.includes(visualizationKey)) {
|
|
57
|
+
const apiFilter = filter.apiFilter
|
|
58
|
+
const colName = apiFilter?.textSelector || apiFilter?.valueSelector || filter.columnName
|
|
59
|
+
acc.push(colName)
|
|
60
|
+
const subGrouping =
|
|
61
|
+
apiFilter?.subgroupTextSelector || apiFilter?.subgroupValueSelector || filter.subGrouping?.columnName
|
|
62
|
+
if (subGrouping) {
|
|
63
|
+
acc.push(subGrouping)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return acc
|
|
67
|
+
}, [])
|
|
68
|
+
visualizationConfig.table.sharedFilterColumns = sharedFilterColumns
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (visualizationConfig.formattedData) visualizationConfig.originalFormattedData = visualizationConfig.formattedData
|
|
72
|
+
const filteredVizData = filteredData?.[rowNumber] ?? filteredData?.[visualizationKey]
|
|
73
|
+
|
|
74
|
+
if (filteredVizData) {
|
|
75
|
+
visualizationConfig.data = filteredVizData || []
|
|
76
|
+
if (visualizationConfig.formattedData) {
|
|
77
|
+
visualizationConfig.formattedData = visualizationConfig.data
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
const dataKey = visualizationConfig.dataKey || 'backwards-compatibility'
|
|
81
|
+
visualizationConfig.data = data[dataKey] || []
|
|
82
|
+
if (visualizationConfig.formattedData) {
|
|
83
|
+
visualizationConfig.formattedData =
|
|
84
|
+
transform.developerStandardize(visualizationConfig.data, visualizationConfig.dataDescription) ||
|
|
85
|
+
visualizationConfig.data
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (filteredDataOverride) {
|
|
90
|
+
visualizationConfig.data = filteredDataOverride
|
|
91
|
+
if (visualizationConfig.formattedData) {
|
|
92
|
+
visualizationConfig.formattedData = filteredDataOverride
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (visualizationConfig.footnotes) {
|
|
96
|
+
const visConfigWithFootnotes = getFootnotesVizConfig(visualizationConfig, rowNumber, config)
|
|
97
|
+
if (multiVizColumn && filteredDataOverride) {
|
|
98
|
+
const vizCategory = filteredDataOverride[0][multiVizColumn]
|
|
99
|
+
// the multiViz filtering filtering is applied after the dashboard filters
|
|
100
|
+
|
|
101
|
+
const categoryFootnote = visConfigWithFootnotes.footnotes.data.filter(d => d[multiVizColumn] === vizCategory)
|
|
102
|
+
_.set(visConfigWithFootnotes, 'footnotes.data', categoryFootnote)
|
|
103
|
+
}
|
|
104
|
+
return visConfigWithFootnotes
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return visualizationConfig as AnyVisualization
|
|
108
|
+
}
|
|
@@ -6,6 +6,5 @@ export const getVizRowColumnLocator = (rows: ConfigRow[]): Record<string, { row:
|
|
|
6
6
|
curr.columns?.forEach((column, columnIndex) => {
|
|
7
7
|
if (column.widget !== undefined) acc[column.widget] = { row: index, column: columnIndex }
|
|
8
8
|
})
|
|
9
|
-
if (curr.footnotesId) acc[curr.footnotesId] = { row: index, column: 0 }
|
|
10
9
|
return acc
|
|
11
10
|
}, {})
|
|
@@ -31,8 +31,11 @@ type GetDatasetKeysParams = Pick<DashboardConfig, 'visualizations' | 'datasets'
|
|
|
31
31
|
export const getDatasetKeys = ({ visualizations, datasets, rows }: GetDatasetKeysParams): string[] => {
|
|
32
32
|
const vizDataKeys = Object.values(visualizations).map(viz => viz.dataKey)
|
|
33
33
|
const rowDataKeys = rows.map(row => row.dataKey)
|
|
34
|
+
const footnoteDataKeys = Object.values(visualizations)
|
|
35
|
+
.map(viz => viz.footnotes?.dataKey)
|
|
36
|
+
.filter(Boolean)
|
|
34
37
|
// ensure to only load datasets for the specific dashboard tab.
|
|
35
|
-
const datasetsUsedByDashboard = _.uniq([...vizDataKeys, ...rowDataKeys])
|
|
38
|
+
const datasetsUsedByDashboard = _.uniq([...vizDataKeys, ...rowDataKeys, ...footnoteDataKeys])
|
|
36
39
|
return Object.keys(datasets).filter(datasetKey => datasetsUsedByDashboard.includes(datasetKey))
|
|
37
40
|
}
|
|
38
41
|
|
|
@@ -111,17 +114,11 @@ export const filterUsedByDataUrl = (
|
|
|
111
114
|
) => {
|
|
112
115
|
if (!filter.usedBy || !filter.usedBy.length) return true
|
|
113
116
|
const vizUsingFilters = filter.usedBy?.map(vizOrRowKey => visualizations[vizOrRowKey] || rows[vizOrRowKey])
|
|
114
|
-
// push any footnotes which are using the filter also
|
|
115
|
-
const vizRowLookup = getVizRowColumnLocator(rows) // ensure we have the footnotesId in the rows
|
|
116
|
-
filter.usedBy?.forEach(vizOrRowKey => {
|
|
117
|
-
const vizLookup = vizRowLookup[vizOrRowKey] // if found vizOrRowKey is a viz key
|
|
118
|
-
const row = rows[vizLookup?.row || vizOrRowKey]
|
|
119
|
-
if (row?.footnotesId) {
|
|
120
|
-
// if the widget is using the filter and the widget is on this row and the row has a footnoteId
|
|
121
|
-
// then the footnote's endpoint should be filtered by the filter
|
|
122
|
-
vizUsingFilters.push(visualizations[row.footnotesId])
|
|
123
|
-
}
|
|
124
|
-
})
|
|
125
117
|
|
|
126
|
-
return vizUsingFilters?.some(viz =>
|
|
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
|
+
})
|
|
127
124
|
}
|
|
@@ -6,7 +6,6 @@ import { AnyVisualization } from '@cdc/core/types/Visualization'
|
|
|
6
6
|
import Footnotes from '@cdc/core/types/Footnotes'
|
|
7
7
|
import { SharedFilter } from '../types/SharedFilter'
|
|
8
8
|
|
|
9
|
-
type ADD_FOOTNOTE = Action<'ADD_FOOTNOTE', { id: string; rowIndex: number; config: Footnotes }>
|
|
10
9
|
type ADD_VISUALIZATION = Action<'ADD_VISUALIZATION', { rowIdx: number; colIdx: number; newViz: AnyVisualization }>
|
|
11
10
|
type APPLY_CONFIG = Action<'APPLY_CONFIG', [Config, Object?]>
|
|
12
11
|
type DELETE_WIDGET = Action<'DELETE_WIDGET', { uid: string }>
|
|
@@ -36,7 +35,6 @@ type UPDATE_ROW = Action<'UPDATE_ROW', { rowIndex: number; rowData: Partial<Conf
|
|
|
36
35
|
type UPDATE_TOGGLE_NAME = Action<'UPDATE_TOGGLE_NAME', { rowIndex: number; columnIndex: number; toggleName: string }>
|
|
37
36
|
|
|
38
37
|
type DashboardActions =
|
|
39
|
-
| ADD_FOOTNOTE
|
|
40
38
|
| ADD_VISUALIZATION
|
|
41
39
|
| APPLY_CONFIG
|
|
42
40
|
| ADD_NEW_DASHBOARD
|
|
@@ -39,17 +39,6 @@ export type DashboardState = {
|
|
|
39
39
|
|
|
40
40
|
const reducer = (state: DashboardState, action: DashboardActions): DashboardState => {
|
|
41
41
|
switch (action.type) {
|
|
42
|
-
case 'ADD_FOOTNOTE': {
|
|
43
|
-
const { id, rowIndex, config } = action.payload
|
|
44
|
-
const newRows = state.config.rows.map((row, i) => (i === rowIndex ? { ...row, footnotesId: id } : row))
|
|
45
|
-
return {
|
|
46
|
-
...state,
|
|
47
|
-
config: saveMultiChanges(
|
|
48
|
-
{ ...state.config, rows: newRows, visualizations: { ...state.config.visualizations, [id]: config } },
|
|
49
|
-
state.config.activeDashboard
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
42
|
case 'ADD_NEW_DASHBOARD': {
|
|
54
43
|
const currentMultiDashboards = state.config.multiDashboards
|
|
55
44
|
const label = 'New Dashboard ' + (currentMultiDashboards.length + 1)
|
package/src/types/ConfigRow.ts
CHANGED
package/src/types/Dashboard.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Series } from '@cdc/core/types/Series'
|
|
2
2
|
import { Runtime } from '@cdc/core/types/Runtime'
|
|
3
|
-
import { DataSet } from '
|
|
3
|
+
import { DataSet } from '@cdc/core/types/DataSet'
|
|
4
4
|
import { ConfigRow } from './ConfigRow'
|
|
5
5
|
import { AnyVisualization } from '@cdc/core/types/Visualization'
|
|
6
6
|
import { Table } from '@cdc/core/types/Table'
|
package/src/types/DataSet.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { ConfigureData } from '@cdc/core/types/ConfigureData'
|
|
2
|
-
|
|
3
|
-
export type DataSet = ConfigureData & {
|
|
4
|
-
dataFileFormat?: string
|
|
5
|
-
dataFileName?: string
|
|
6
|
-
dataFileSize?: number
|
|
7
|
-
preview?: boolean
|
|
8
|
-
dataUrl: string
|
|
9
|
-
runtimeDataUrl: string
|
|
10
|
-
dataFileSourceType: string
|
|
11
|
-
loadQueryParam?: string // fetched from the browser's query string and appended to the dataUrl on load.
|
|
12
|
-
}
|