@cdc/dashboard 4.24.3 → 4.24.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 +148123 -105018
- package/examples/chart-data.json +5409 -0
- package/examples/full-dash-test.json +14643 -0
- package/examples/full-dashboard.json +10036 -0
- package/index.html +2 -2
- package/package.json +9 -9
- package/src/CdcDashboard.tsx +8 -3
- package/src/CdcDashboardComponent.tsx +141 -290
- package/src/_stories/Dashboard.stories.tsx +52 -7
- package/src/_stories/_mock/markup-include.json +78 -0
- package/src/_stories/_mock/multi-dashboards.json +914 -0
- package/src/_stories/_mock/multi-viz.json +378 -0
- package/src/_stories/_mock/pivot-filter.json +2 -2
- package/src/_stories/_mock/standalone-table.json +2 -0
- package/src/components/DataDesignerModal.tsx +145 -0
- package/src/components/Grid.tsx +3 -1
- package/src/components/Header/FilterModal.tsx +63 -33
- package/src/components/MultiConfigTabs/MultiTabs.tsx +3 -2
- package/src/components/Row.tsx +50 -25
- package/src/components/Toggle/Toggle.tsx +6 -7
- package/src/components/VisualizationRow.tsx +183 -0
- package/src/components/VisualizationsPanel.tsx +26 -3
- package/src/components/Widget.tsx +21 -103
- package/src/helpers/filterData.ts +1 -1
- package/src/helpers/getFilteredData.ts +39 -0
- package/src/helpers/getUpdateConfig.ts +15 -0
- package/src/helpers/getVizConfig.ts +31 -0
- package/src/helpers/getVizRowColumnLocator.ts +9 -0
- package/src/scss/grid.scss +9 -2
- package/src/scss/main.scss +5 -0
- package/src/store/dashboard.actions.ts +6 -0
- package/src/store/dashboard.reducer.ts +33 -8
- package/src/types/ConfigRow.ts +12 -3
- package/src/types/DataSet.ts +11 -8
- package/src/types/SharedFilter.ts +1 -1
- package/src/components/EditorWrapper/EditorWrapper.tsx +0 -52
- package/src/components/EditorWrapper/editor-wrapper.style.css +0 -13
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useContext } from 'react'
|
|
2
2
|
import { useDrag } from 'react-dnd'
|
|
3
3
|
|
|
4
4
|
import { useGlobalContext } from '@cdc/core/components/GlobalContext'
|
|
5
5
|
import { DashboardContext, DashboardDispatchContext } from '../DashboardContext'
|
|
6
6
|
|
|
7
7
|
import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
8
|
-
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
9
|
-
|
|
10
|
-
import DataDesigner from '@cdc/core/components/managers/DataDesigner'
|
|
11
8
|
import Icon from '@cdc/core/components/ui/Icon'
|
|
12
9
|
import Modal from '@cdc/core/components/ui/Modal'
|
|
13
10
|
import { Visualization } from '@cdc/core/types/Visualization'
|
|
14
11
|
import { iconHash } from '../helpers/iconHash'
|
|
12
|
+
import _ from 'lodash'
|
|
13
|
+
import { DataDesignerModal } from './DataDesignerModal'
|
|
15
14
|
|
|
16
15
|
const labelHash = {
|
|
17
16
|
'data-bite': 'Data Bite',
|
|
@@ -45,8 +44,6 @@ const Widget = ({ data, addVisualization, type }: WidgetProps) => {
|
|
|
45
44
|
const visualizations = config.visualizations
|
|
46
45
|
const dispatch = useContext(DashboardDispatchContext)
|
|
47
46
|
const updateConfig = config => dispatch({ type: 'UPDATE_CONFIG', payload: [config] })
|
|
48
|
-
const dataRef = useRef<WidgetData>()
|
|
49
|
-
dataRef.current = data
|
|
50
47
|
|
|
51
48
|
const transform = new DataTransform()
|
|
52
49
|
|
|
@@ -58,14 +55,14 @@ const Widget = ({ data, addVisualization, type }: WidgetProps) => {
|
|
|
58
55
|
const { rowIdx, colIdx } = result
|
|
59
56
|
|
|
60
57
|
if (undefined !== data?.rowIdx) {
|
|
61
|
-
rows[data.rowIdx][data.colIdx].widget = null // Wipe from old position
|
|
58
|
+
rows[data.rowIdx].columns[data.colIdx].widget = null // Wipe from old position
|
|
62
59
|
|
|
63
|
-
rows[rowIdx][colIdx].widget = data.uid // Add to new row and col
|
|
60
|
+
rows[rowIdx].columns[colIdx].widget = data.uid // Add to new row and col
|
|
64
61
|
} else if (!!addVisualization) {
|
|
65
62
|
// Item does not exist, instantiate a new one
|
|
66
63
|
const newViz = addVisualization()
|
|
67
64
|
visualizations[newViz.uid] = newViz // Add to widgets collection
|
|
68
|
-
rows[rowIdx][colIdx].widget = newViz.uid // Store reference in rows collection under the specific column
|
|
65
|
+
rows[rowIdx].columns[colIdx].widget = newViz.uid // Store reference in rows collection under the specific column
|
|
69
66
|
}
|
|
70
67
|
|
|
71
68
|
updateConfig({ ...config, rows, visualizations })
|
|
@@ -84,7 +81,7 @@ const Widget = ({ data, addVisualization, type }: WidgetProps) => {
|
|
|
84
81
|
|
|
85
82
|
const deleteWidget = () => {
|
|
86
83
|
if (!data) return
|
|
87
|
-
rows[data.rowIdx][data.colIdx].widget = null
|
|
84
|
+
rows[data.rowIdx].columns[data.colIdx].widget = null
|
|
88
85
|
|
|
89
86
|
delete visualizations[data.uid]
|
|
90
87
|
|
|
@@ -106,78 +103,6 @@ const Widget = ({ data, addVisualization, type }: WidgetProps) => {
|
|
|
106
103
|
updateConfig({ ...config, visualizations })
|
|
107
104
|
}
|
|
108
105
|
|
|
109
|
-
const changeDataset = (uid, value) => {
|
|
110
|
-
visualizations[uid].dataDescription = {}
|
|
111
|
-
visualizations[uid].formattedData = undefined
|
|
112
|
-
|
|
113
|
-
visualizations[uid].dataKey = value
|
|
114
|
-
|
|
115
|
-
updateConfig({ ...config, visualizations })
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const updateDescriptionProp = async (visualizationKey, datasetKey, key, value) => {
|
|
119
|
-
let dataDescription = { ...(dataRef.current?.dataDescription as Object), [key]: value }
|
|
120
|
-
|
|
121
|
-
let newData
|
|
122
|
-
if (!config.datasets[datasetKey].data && config.datasets[datasetKey].dataUrl) {
|
|
123
|
-
newData = await fetchRemoteData(config.datasets[datasetKey].dataUrl)
|
|
124
|
-
newData = transform.autoStandardize(newData)
|
|
125
|
-
} else {
|
|
126
|
-
newData = config.datasets[datasetKey].data
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
let formattedData = transform.developerStandardize(newData, dataDescription)
|
|
130
|
-
|
|
131
|
-
let newVisualizations = { ...config.visualizations }
|
|
132
|
-
newVisualizations[visualizationKey] = { ...newVisualizations[visualizationKey], data: newData, dataDescription, formattedData }
|
|
133
|
-
|
|
134
|
-
updateConfig({ ...config, visualizations: newVisualizations })
|
|
135
|
-
|
|
136
|
-
overlay?.actions.openOverlay(dataDesignerModal(newVisualizations[visualizationKey]))
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const dataDesignerModal = (configureData, dataKeyOverride?) => {
|
|
140
|
-
const dataKey = !dataKeyOverride && dataKeyOverride !== '' ? data?.dataKey || dataRef.current?.dataKey : dataKeyOverride
|
|
141
|
-
|
|
142
|
-
overlay?.actions.toggleOverlay()
|
|
143
|
-
|
|
144
|
-
return (
|
|
145
|
-
<Modal>
|
|
146
|
-
<Modal.Content>
|
|
147
|
-
<div className='dataset-selector-container'>
|
|
148
|
-
Select a dataset:
|
|
149
|
-
<select
|
|
150
|
-
className='dataset-selector'
|
|
151
|
-
defaultValue={dataKey}
|
|
152
|
-
onChange={e => {
|
|
153
|
-
changeDataset(data?.uid, e.target.value)
|
|
154
|
-
overlay?.actions.openOverlay(dataDesignerModal(data, e.target.value || ''))
|
|
155
|
-
}}
|
|
156
|
-
>
|
|
157
|
-
<option value=''>Select a dataset</option>
|
|
158
|
-
{config.datasets && Object.keys(config.datasets).map(datasetKey => <option key={datasetKey}>{datasetKey}</option>)}
|
|
159
|
-
</select>
|
|
160
|
-
</div>
|
|
161
|
-
{dataKey && (
|
|
162
|
-
<DataDesigner
|
|
163
|
-
{...{
|
|
164
|
-
configureData,
|
|
165
|
-
visualizationKey: data?.uid,
|
|
166
|
-
dataKey: dataKey,
|
|
167
|
-
updateDescriptionProp
|
|
168
|
-
}}
|
|
169
|
-
/>
|
|
170
|
-
)}
|
|
171
|
-
{configureData.formattedData && (
|
|
172
|
-
<button style={{ margin: '1em' }} className='cove-button' onClick={() => overlay?.actions.toggleOverlay()}>
|
|
173
|
-
Continue
|
|
174
|
-
</button>
|
|
175
|
-
)}
|
|
176
|
-
</Modal.Content>
|
|
177
|
-
</Modal>
|
|
178
|
-
)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
106
|
const FilterHideModal = configureData => {
|
|
182
107
|
const currentVizKey = Object.keys(visualizations).find(vizKey => vizKey === configureData.uid) || ''
|
|
183
108
|
const currentViz = config.visualizations && config.visualizations[currentVizKey]
|
|
@@ -208,8 +133,6 @@ const Widget = ({ data, addVisualization, type }: WidgetProps) => {
|
|
|
208
133
|
}
|
|
209
134
|
}
|
|
210
135
|
|
|
211
|
-
overlay?.actions.toggleOverlay()
|
|
212
|
-
|
|
213
136
|
const showAutoLoadCheckbox = !vizWithAutoLoad || vizWithAutoLoad === currentVizKey
|
|
214
137
|
return (
|
|
215
138
|
<Modal>
|
|
@@ -243,29 +166,24 @@ const Widget = ({ data, addVisualization, type }: WidgetProps) => {
|
|
|
243
166
|
)
|
|
244
167
|
}
|
|
245
168
|
|
|
246
|
-
useEffect(() => {
|
|
247
|
-
if (data?.openModal) {
|
|
248
|
-
overlay?.actions.openOverlay(type === 'filter-dropdowns' ? FilterHideModal(dataRef.current) : dataDesignerModal(dataRef.current))
|
|
249
|
-
|
|
250
|
-
visualizations[data.uid].openModal = false
|
|
251
|
-
|
|
252
|
-
updateConfig({ ...config, visualizations })
|
|
253
|
-
}
|
|
254
|
-
}, [data?.openModal])
|
|
255
|
-
|
|
256
169
|
let isConfigurationReady = false
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
} else if (data && data.formattedData) {
|
|
170
|
+
const dataConfiguredForRow = !!rows[data?.rowIdx]?.dataKey
|
|
171
|
+
if (dataConfiguredForRow || ['filter-dropdowns', 'markup-include'].includes(type)) {
|
|
260
172
|
isConfigurationReady = true
|
|
261
|
-
} else
|
|
262
|
-
|
|
263
|
-
formattedDataAttempt = transform.developerStandardize(formattedDataAttempt, data.dataDescription)
|
|
264
|
-
if (formattedDataAttempt) {
|
|
173
|
+
} else {
|
|
174
|
+
if (data?.formattedData) {
|
|
265
175
|
isConfigurationReady = true
|
|
176
|
+
} else if (data?.dataKey && data?.dataDescription && config.datasets[data.dataKey]) {
|
|
177
|
+
const formattedDataAttempt = transform.autoStandardize(config.datasets[data.dataKey].data)
|
|
178
|
+
const canFormatData = !!transform.developerStandardize(formattedDataAttempt, data.dataDescription)
|
|
179
|
+
if (canFormatData) {
|
|
180
|
+
isConfigurationReady = true
|
|
181
|
+
}
|
|
266
182
|
}
|
|
267
183
|
}
|
|
268
184
|
|
|
185
|
+
const needsDataConfiguration = !dataConfiguredForRow
|
|
186
|
+
|
|
269
187
|
return (
|
|
270
188
|
<>
|
|
271
189
|
<div className='widget' ref={drag} style={{ opacity: isDragging ? 0.5 : 1 }} {...collected}>
|
|
@@ -278,12 +196,12 @@ const Widget = ({ data, addVisualization, type }: WidgetProps) => {
|
|
|
278
196
|
{iconHash['tools']}
|
|
279
197
|
</button>
|
|
280
198
|
)}
|
|
281
|
-
{
|
|
199
|
+
{needsDataConfiguration && (
|
|
282
200
|
<button
|
|
283
201
|
title='Configure Data'
|
|
284
202
|
className='btn btn-configure'
|
|
285
203
|
onClick={() => {
|
|
286
|
-
overlay?.actions.openOverlay(type === 'filter-dropdowns' ? FilterHideModal(data) :
|
|
204
|
+
overlay?.actions.openOverlay(type === 'filter-dropdowns' ? FilterHideModal(data) : <DataDesignerModal rowIndex={data.rowIdx} vizKey={data.uid} />)
|
|
287
205
|
}}
|
|
288
206
|
>
|
|
289
207
|
{iconHash['gear']}
|
|
@@ -66,7 +66,7 @@ const pivotData = (data, pivotFilter: SharedFilter) => {
|
|
|
66
66
|
const row = newData[index] || {}
|
|
67
67
|
if (!inactive.includes(key)) row[key] = val[valueColumn]
|
|
68
68
|
const toAdd = _.omit(val, [pivotColumn, valueColumn, ...inactive])
|
|
69
|
-
newData[index] = { ...
|
|
69
|
+
newData[index] = { ...toAdd, ...row }
|
|
70
70
|
})
|
|
71
71
|
}
|
|
72
72
|
return newData
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DashboardState } from '../store/dashboard.reducer'
|
|
2
|
+
import { Dashboard } from '../types/Dashboard'
|
|
3
|
+
import { SharedFilter } from '../types/SharedFilter'
|
|
4
|
+
import { filterData } from './filterData'
|
|
5
|
+
import { getFormattedData } from './getFormattedData'
|
|
6
|
+
import { getVizKeys } from './getVizKeys'
|
|
7
|
+
|
|
8
|
+
export const getApplicableFilters = (dashboard: Dashboard, key: string | number): false | SharedFilter[] => {
|
|
9
|
+
const c = dashboard.sharedFilters?.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(`${key}`) !== -1)
|
|
10
|
+
return c?.length > 0 ? c : false
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const getFilteredData = (state: DashboardState, initialFilteredData = {}, dataOverride?: Object) => {
|
|
14
|
+
const newFilteredData = initialFilteredData
|
|
15
|
+
const { config } = state
|
|
16
|
+
getVizKeys(config).forEach(key => {
|
|
17
|
+
const applicableFilters = getApplicableFilters(config.dashboard, key)
|
|
18
|
+
if (applicableFilters) {
|
|
19
|
+
const { dataKey, data, dataDescription } = config.visualizations[key]
|
|
20
|
+
const _data = state.data[dataKey] || data
|
|
21
|
+
const formattedData = dataOverride?.[dataKey] || (dataDescription ? getFormattedData(_data, dataDescription) : _data)
|
|
22
|
+
|
|
23
|
+
newFilteredData[key] = filterData(applicableFilters, formattedData)
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
config.rows.forEach((row, index) => {
|
|
27
|
+
if (row.dataKey) {
|
|
28
|
+
const applicableFilters = getApplicableFilters(config.dashboard, index)
|
|
29
|
+
if (applicableFilters) {
|
|
30
|
+
const { dataKey, data, dataDescription } = row
|
|
31
|
+
const _data = state.data[dataKey] || data
|
|
32
|
+
const formattedData = dataOverride?.[dataKey] ?? dataDescription ? getFormattedData(_data, dataDescription) : _data
|
|
33
|
+
|
|
34
|
+
newFilteredData[index] = filterData(applicableFilters, formattedData)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
return newFilteredData
|
|
39
|
+
}
|
|
@@ -4,6 +4,7 @@ import { filterData } from './filterData'
|
|
|
4
4
|
import { generateValuesForFilter } from './generateValuesForFilter'
|
|
5
5
|
import { getFormattedData } from './getFormattedData'
|
|
6
6
|
import { getVizKeys } from './getVizKeys'
|
|
7
|
+
import { getVizRowColumnLocator } from './getVizRowColumnLocator'
|
|
7
8
|
|
|
8
9
|
import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
|
|
9
10
|
|
|
@@ -17,6 +18,8 @@ export const getUpdateConfig =
|
|
|
17
18
|
let newFilteredData = {}
|
|
18
19
|
let visualizationKeys = getVizKeys(newConfig)
|
|
19
20
|
|
|
21
|
+
const vizRowColumnLocator = getVizRowColumnLocator(newConfig.rows)
|
|
22
|
+
|
|
20
23
|
if (newConfig.dashboard.sharedFilters) {
|
|
21
24
|
newConfig.dashboard.sharedFilters.forEach((filter, i) => {
|
|
22
25
|
const filterIsSetByVizData = !!visualizationKeys.find(key => key === filter.setBy)
|
|
@@ -52,6 +55,8 @@ export const getUpdateConfig =
|
|
|
52
55
|
})
|
|
53
56
|
|
|
54
57
|
visualizationKeys.forEach(visualizationKey => {
|
|
58
|
+
const row = vizRowColumnLocator[visualizationKey]
|
|
59
|
+
if (newConfig.rows[row]?.datakey) return // data configured on the row level
|
|
55
60
|
const applicableFilters = newConfig.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(visualizationKey) !== -1)
|
|
56
61
|
|
|
57
62
|
if (applicableFilters.length > 0) {
|
|
@@ -62,6 +67,16 @@ export const getUpdateConfig =
|
|
|
62
67
|
newFilteredData[visualizationKey] = filterData(applicableFilters, _data)
|
|
63
68
|
}
|
|
64
69
|
})
|
|
70
|
+
|
|
71
|
+
newConfig.rows.forEach((row, rowIndex) => {
|
|
72
|
+
const applicableFilters = newConfig.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(rowIndex) !== -1)
|
|
73
|
+
|
|
74
|
+
if (applicableFilters.length > 0) {
|
|
75
|
+
const formattedData = getFormattedData(row.data, row.dataDescription)
|
|
76
|
+
const _data = formattedData || (dataOverride || state.data)[rowIndex]
|
|
77
|
+
newFilteredData[rowIndex] = filterData(applicableFilters, _data)
|
|
78
|
+
}
|
|
79
|
+
})
|
|
65
80
|
}
|
|
66
81
|
//Enforce default values that need to be calculated at runtime
|
|
67
82
|
newConfig.runtime = {}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import _ from 'lodash'
|
|
2
|
+
import { MultiDashboardConfig } from '../types/MultiDashboard'
|
|
3
|
+
import DataTransform from '@cdc/core/helpers/DataTransform'
|
|
4
|
+
|
|
5
|
+
const transform = new DataTransform()
|
|
6
|
+
|
|
7
|
+
export const getVizConfig = (visualizationKey: string, rowNumber: number, config: MultiDashboardConfig, data: Object, filteredData?: Object) => {
|
|
8
|
+
const visualizationConfig = _.cloneDeep(config.visualizations[visualizationKey])
|
|
9
|
+
const rowData = config.rows[rowNumber]
|
|
10
|
+
if (rowData?.dataKey) {
|
|
11
|
+
// data configured on the row
|
|
12
|
+
Object.assign(visualizationConfig, _.pick(rowData, ['dataKey', 'dataDescription', 'formattedData', 'data']))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (visualizationConfig.formattedData) visualizationConfig.originalFormattedData = visualizationConfig.formattedData
|
|
16
|
+
const filteredVizData = filteredData?.[rowNumber] ?? filteredData?.[visualizationKey]
|
|
17
|
+
|
|
18
|
+
if (filteredVizData) {
|
|
19
|
+
visualizationConfig.data = filteredVizData
|
|
20
|
+
if (visualizationConfig.formattedData) {
|
|
21
|
+
visualizationConfig.formattedData = visualizationConfig.data
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
const dataKey = visualizationConfig.dataKey || 'backwards-compatibility'
|
|
25
|
+
visualizationConfig.data = data[dataKey]
|
|
26
|
+
if (visualizationConfig.formattedData) {
|
|
27
|
+
visualizationConfig.formattedData = transform.developerStandardize(visualizationConfig.data, visualizationConfig.dataDescription) || visualizationConfig.data
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return visualizationConfig
|
|
31
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ConfigRow } from '../types/ConfigRow'
|
|
2
|
+
|
|
3
|
+
export const getVizRowColumnLocator = (rows: ConfigRow[]) =>
|
|
4
|
+
rows.reduce((acc, curr, index) => {
|
|
5
|
+
curr.columns?.forEach((column, columnIndex) => {
|
|
6
|
+
if (column.widget !== undefined) acc[column.widget] = { row: index, column: columnIndex }
|
|
7
|
+
})
|
|
8
|
+
return acc
|
|
9
|
+
}, {})
|
package/src/scss/grid.scss
CHANGED
|
@@ -35,8 +35,7 @@ $red: #f74242;
|
|
|
35
35
|
flex-flow: row;
|
|
36
36
|
width: 100%;
|
|
37
37
|
position: relative;
|
|
38
|
-
|
|
39
|
-
padding: 1em;
|
|
38
|
+
padding: 2em 1em 1em;
|
|
40
39
|
border: 1px solid #c2c2c2;
|
|
41
40
|
transition: border 300ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
42
41
|
background-color: #f2f2f2;
|
|
@@ -330,6 +329,14 @@ $red: #f74242;
|
|
|
330
329
|
.builder-row {
|
|
331
330
|
position: relative;
|
|
332
331
|
|
|
332
|
+
.btn-configure-row {
|
|
333
|
+
background: none;
|
|
334
|
+
display: block;
|
|
335
|
+
position: absolute;
|
|
336
|
+
right: 1em;
|
|
337
|
+
top: 0;
|
|
338
|
+
}
|
|
339
|
+
|
|
333
340
|
.widget__content {
|
|
334
341
|
padding: 0 2em;
|
|
335
342
|
|
package/src/scss/main.scss
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { DashboardConfig as Config } from '../types/DashboardConfig'
|
|
2
2
|
import { type Action } from '@cdc/core/types/Action'
|
|
3
3
|
import { Tab } from '../types/Tab'
|
|
4
|
+
import { ConfigureData } from '@cdc/core/types/ConfigureData'
|
|
5
|
+
import { ConfigRow } from '../types/ConfigRow'
|
|
4
6
|
|
|
5
7
|
type SET_CONFIG = Action<'SET_CONFIG', Config>
|
|
6
8
|
type UPDATE_CONFIG = Action<'UPDATE_CONFIG', [Config, Object?]>
|
|
@@ -17,6 +19,8 @@ type ADD_NEW_DASHBOARD = Action<'ADD_NEW_DASHBOARD', undefined>
|
|
|
17
19
|
type SAVE_CURRENT_CHANGES = Action<'SAVE_CURRENT_CHANGES', undefined>
|
|
18
20
|
type SWITCH_CONFIG = Action<'SWITCH_CONFIG', number>
|
|
19
21
|
type TOGGLE_ROW = Action<'TOGGLE_ROW', { rowIndex: number; colIndex: number }>
|
|
22
|
+
type UPDATE_VISUALIZATION = Action<'UPDATE_VISUALIZATION', { vizKey: string; configureData: Partial<ConfigureData> }>
|
|
23
|
+
type UPDATE_ROW = Action<'UPDATE_ROW', { rowIndex: number; rowData: Partial<ConfigRow> }>
|
|
20
24
|
|
|
21
25
|
type DashboardActions =
|
|
22
26
|
| ADD_NEW_DASHBOARD
|
|
@@ -34,4 +38,6 @@ type DashboardActions =
|
|
|
34
38
|
| SWITCH_CONFIG
|
|
35
39
|
| INITIALIZE_MULTIDASHBOARDS
|
|
36
40
|
| TOGGLE_ROW
|
|
41
|
+
| UPDATE_VISUALIZATION
|
|
42
|
+
| UPDATE_ROW
|
|
37
43
|
export default DashboardActions
|
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
import _ from 'lodash'
|
|
2
2
|
import { getUpdateConfig } from '../helpers/getUpdateConfig'
|
|
3
|
-
import { MultiDashboardConfig } from '../types/MultiDashboard'
|
|
3
|
+
import { MultiDashboard, MultiDashboardConfig } from '../types/MultiDashboard'
|
|
4
4
|
import DashboardActions from './dashboard.actions'
|
|
5
5
|
import { devToolsWrapper } from '@cdc/core/helpers/withDevTools'
|
|
6
6
|
import { Tab } from '../types/Tab'
|
|
7
|
+
import { DashboardConfig } from '../types/DashboardConfig'
|
|
8
|
+
import { ConfigRow } from '../types/ConfigRow'
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
dashboard:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
type BlankMultiConfig = {
|
|
11
|
+
dashboard: Partial<DashboardConfig>
|
|
12
|
+
rows: Partial<ConfigRow>[]
|
|
13
|
+
visualizations: Record<string, Object>
|
|
14
|
+
table: Object
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const createBlankDashboard: () => BlankMultiConfig = () => ({
|
|
18
|
+
dashboard: {},
|
|
19
|
+
rows: [{ columns: [{ width: 12 }] }],
|
|
13
20
|
visualizations: {},
|
|
14
21
|
table: {
|
|
15
22
|
label: 'Data Table',
|
|
16
|
-
show:
|
|
23
|
+
show: false,
|
|
17
24
|
showDownloadUrl: false,
|
|
18
25
|
showVertical: true
|
|
19
26
|
}
|
|
@@ -107,7 +114,25 @@ const reducer = (state: DashboardState, action: DashboardActions): DashboardStat
|
|
|
107
114
|
case 'TOGGLE_ROW': {
|
|
108
115
|
const { rowIndex, colIndex } = action.payload
|
|
109
116
|
const newRows = state.config.rows.map((row, index) => {
|
|
110
|
-
|
|
117
|
+
if (index === rowIndex) {
|
|
118
|
+
const newColumns = row.columns.map((col, i) => ({ ...col, hide: i === colIndex }))
|
|
119
|
+
return { ...row, columns: newColumns }
|
|
120
|
+
}
|
|
121
|
+
return row
|
|
122
|
+
})
|
|
123
|
+
return { ...state, config: { ...state.config, rows: newRows } }
|
|
124
|
+
}
|
|
125
|
+
case 'UPDATE_VISUALIZATION': {
|
|
126
|
+
const { vizKey, configureData } = action.payload
|
|
127
|
+
return { ...state, config: { ...state.config, visualizations: { ...state.config.visualizations, [vizKey]: { ...state.config.visualizations[vizKey], ...configureData } } } }
|
|
128
|
+
}
|
|
129
|
+
case 'UPDATE_ROW': {
|
|
130
|
+
const { rowIndex, rowData } = action.payload
|
|
131
|
+
const newRows = state.config.rows.map((row, index) => {
|
|
132
|
+
if (index === rowIndex) {
|
|
133
|
+
return { ...row, ...rowData }
|
|
134
|
+
}
|
|
135
|
+
return row
|
|
111
136
|
})
|
|
112
137
|
return { ...state, config: { ...state.config, rows: newRows } }
|
|
113
138
|
}
|
package/src/types/ConfigRow.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
import { ConfigureData } from '@cdc/core/types/ConfigureData'
|
|
2
|
+
|
|
3
|
+
type Col = {
|
|
2
4
|
equalHeight?: boolean
|
|
3
5
|
width: number | null
|
|
4
|
-
toggle?: boolean
|
|
5
6
|
hide?: boolean
|
|
6
7
|
widget?: string
|
|
7
8
|
uuid?: string | number
|
|
8
|
-
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type ConfigRow = {
|
|
12
|
+
columns: Col[]
|
|
13
|
+
uuid?: string | number
|
|
14
|
+
toggle?: boolean
|
|
15
|
+
equalHeight?: boolean
|
|
16
|
+
multiVizColumn?: string
|
|
17
|
+
} & ConfigureData
|
package/src/types/DataSet.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
+
}
|
|
@@ -7,7 +7,7 @@ export type SharedFilter = {
|
|
|
7
7
|
setByQueryParameter?: string
|
|
8
8
|
active?: string | string[]
|
|
9
9
|
queuedActive?: string
|
|
10
|
-
usedBy?: string[]
|
|
10
|
+
usedBy?: (string | number)[] // if number used by whole row, else used by specific viz
|
|
11
11
|
parents?: string[]
|
|
12
12
|
pivot?: string
|
|
13
13
|
setBy?: string
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { Accordion } from 'react-accessible-accordion'
|
|
3
|
-
import Header from '../Header'
|
|
4
|
-
import { Visualization } from '@cdc/core/types/Visualization'
|
|
5
|
-
import { ViewPort } from '@cdc/core/types/ViewPort'
|
|
6
|
-
import './editor-wrapper.style.css'
|
|
7
|
-
|
|
8
|
-
type StandAloneComponentProps = {
|
|
9
|
-
visualizationKey: string
|
|
10
|
-
config: Visualization
|
|
11
|
-
isEditor: boolean
|
|
12
|
-
setConfig: Function
|
|
13
|
-
isDashboard: boolean
|
|
14
|
-
configUrl: string
|
|
15
|
-
setEditing: Function
|
|
16
|
-
hostname: string
|
|
17
|
-
viewport?: ViewPort
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
type EditorProps = {
|
|
21
|
-
component: React.JSXElementConstructor<StandAloneComponentProps>
|
|
22
|
-
type: string
|
|
23
|
-
visualizationKey: string
|
|
24
|
-
visualizationConfig: Visualization
|
|
25
|
-
updateConfig: Function
|
|
26
|
-
viewport?: ViewPort
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const EditorWrapper: React.FC<React.PropsWithChildren<EditorProps>> = ({ children, visualizationKey, visualizationConfig, type, component: Component, updateConfig, viewport }) => {
|
|
30
|
-
const [displayPanel, setDisplayPanel] = React.useState(true)
|
|
31
|
-
return (
|
|
32
|
-
<>
|
|
33
|
-
<Header visualizationKey={visualizationKey} subEditor={type} />
|
|
34
|
-
<div className='editor-wrapper'>
|
|
35
|
-
<button className={`editor-toggle ${displayPanel ? '' : 'collapsed'}`} title={displayPanel ? `Collapse Editor` : `Expand Editor`} onClick={() => setDisplayPanel(!displayPanel)} />
|
|
36
|
-
<section className={`${displayPanel ? '' : 'hidden'} editor-panel cove`}>
|
|
37
|
-
<div aria-level={2} role='heading' className='heading-2'>
|
|
38
|
-
Configure {type}
|
|
39
|
-
</div>
|
|
40
|
-
<form>
|
|
41
|
-
<Accordion allowZeroExpanded={true}>{children}</Accordion>
|
|
42
|
-
</form>
|
|
43
|
-
</section>
|
|
44
|
-
<div className='preview-wrapper'>
|
|
45
|
-
<Component visualizationKey={visualizationKey} config={visualizationConfig} isEditor={true} setConfig={updateConfig} isDashboard={true} configUrl={undefined} setEditing={undefined} hostname={undefined} viewport={viewport} />
|
|
46
|
-
</div>
|
|
47
|
-
</div>
|
|
48
|
-
</>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export default EditorWrapper
|