@cdc/dashboard 4.25.7-2 → 4.25.8
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 +36373 -35957
- package/examples/no-data-markup.json +206 -0
- package/examples/private/DEV-11072.json +7591 -0
- package/examples/private/brfs.json +2174 -0
- package/examples/private/example-1.json +1553 -0
- package/examples/private/filters.json +390 -0
- package/examples/private/footnote-issue.json +20943 -0
- package/examples/private/mv-columns.json +506 -0
- package/examples/private/oral-health.json +6171 -0
- package/examples/private/test-breaking-dash.json +942 -0
- package/examples/private/vehicle-issue.json +683 -0
- package/index.html +26 -24
- package/package.json +9 -9
- package/src/CdcDashboard.tsx +13 -3
- package/src/CdcDashboardComponent.tsx +113 -56
- package/src/DashboardContext.tsx +2 -1
- package/src/components/DashboardEditors.tsx +10 -2
- package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +54 -9
- package/src/components/VisualizationRow.tsx +25 -4
- package/src/helpers/getFilteredData.ts +49 -50
- package/src/helpers/getVizConfig.ts +32 -15
- package/src/index.tsx +1 -0
- package/src/store/dashboard.actions.ts +2 -0
- package/src/store/dashboard.reducer.ts +4 -0
- package/src/types/ConfigRow.ts +1 -0
- package/src/types/InitialState.ts +1 -0
|
@@ -19,6 +19,7 @@ import { hasDashboardApplyBehavior } from '../helpers/hasDashboardApplyBehavior'
|
|
|
19
19
|
import CdcChart from '@cdc/chart/src/CdcChartComponent'
|
|
20
20
|
import ExpandCollapseButtons from './ExpandCollapseButtons'
|
|
21
21
|
import { ChartConfig } from '@cdc/chart/src/types/ChartConfig'
|
|
22
|
+
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
|
|
22
23
|
|
|
23
24
|
type VisualizationWrapperProps = {
|
|
24
25
|
allExpanded: boolean
|
|
@@ -66,6 +67,7 @@ type VizRowProps = {
|
|
|
66
67
|
currentViewport: ViewPort
|
|
67
68
|
isLastRow: boolean
|
|
68
69
|
setAllExpanded?: (expanded: boolean) => void
|
|
70
|
+
interactionLabel: string
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
const VisualizationRow: React.FC<VizRowProps> = ({
|
|
@@ -80,7 +82,8 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
80
82
|
apiFilterDropdowns,
|
|
81
83
|
currentViewport,
|
|
82
84
|
isLastRow,
|
|
83
|
-
setAllExpanded
|
|
85
|
+
setAllExpanded,
|
|
86
|
+
interactionLabel = ''
|
|
84
87
|
}) => {
|
|
85
88
|
const { config, filteredData: dashboardFilteredData, data: rawData } = useContext(DashboardContext)
|
|
86
89
|
const [toggledRow, setToggled] = React.useState<number>(0)
|
|
@@ -136,7 +139,9 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
136
139
|
{Object.keys(dataGroups).map(groupName => {
|
|
137
140
|
const dataValue = dataGroups[groupName]
|
|
138
141
|
const _row = _.cloneDeep(row) // clone the row to avoid mutating the original row
|
|
142
|
+
const originalMultiVizColumn = _row.multiVizColumn // store original value before clearing
|
|
139
143
|
_row.multiVizColumn = undefined // reset the multiVizColumn to avoid passing it to the child components
|
|
144
|
+
_row.originalMultiVizColumn = originalMultiVizColumn // store for footnote filtering
|
|
140
145
|
return (
|
|
141
146
|
<VisualizationRow
|
|
142
147
|
key={`row__${index}__${groupName}`}
|
|
@@ -151,6 +156,7 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
151
156
|
currentViewport={currentViewport}
|
|
152
157
|
inNoDataState={inNoDataState}
|
|
153
158
|
isLastRow={isLastRow}
|
|
159
|
+
interactionLabel={interactionLabel}
|
|
154
160
|
/>
|
|
155
161
|
)
|
|
156
162
|
})}
|
|
@@ -174,7 +180,7 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
174
180
|
rawData,
|
|
175
181
|
dashboardFilteredData,
|
|
176
182
|
filteredDataOverride,
|
|
177
|
-
row.multiVizColumn
|
|
183
|
+
row.multiVizColumn || row.originalMultiVizColumn
|
|
178
184
|
)
|
|
179
185
|
|
|
180
186
|
const { type, sharedFilterIndexes, filterBehavior, table, dataKey } = visualizationConfig
|
|
@@ -186,7 +192,18 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
186
192
|
? config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy === col.widget)[0].active
|
|
187
193
|
: undefined
|
|
188
194
|
const tableLink = (
|
|
189
|
-
<a
|
|
195
|
+
<a
|
|
196
|
+
href={`#data-table-${dataKey}`}
|
|
197
|
+
className='margin-left-href'
|
|
198
|
+
onClick={() => {
|
|
199
|
+
publishAnalyticsEvent(
|
|
200
|
+
`link_to_data_table_click`,
|
|
201
|
+
'click',
|
|
202
|
+
`${interactionLabel}|#data-table-${dataKey}`,
|
|
203
|
+
visualizationConfig.type
|
|
204
|
+
)
|
|
205
|
+
}}
|
|
206
|
+
>
|
|
190
207
|
{dataKey} (Go to Table)
|
|
191
208
|
</a>
|
|
192
209
|
)
|
|
@@ -233,6 +250,7 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
233
250
|
setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
|
|
234
251
|
isDashboard={true}
|
|
235
252
|
link={link}
|
|
253
|
+
interactionLabel={interactionLabel}
|
|
236
254
|
/>
|
|
237
255
|
)}
|
|
238
256
|
{type === 'map' && (
|
|
@@ -248,6 +266,7 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
248
266
|
isDashboard={true}
|
|
249
267
|
link={link}
|
|
250
268
|
dataset={config.datasets}
|
|
269
|
+
interactionLabel={interactionLabel}
|
|
251
270
|
/>
|
|
252
271
|
)}
|
|
253
272
|
{type === 'data-bite' && (
|
|
@@ -268,7 +287,7 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
268
287
|
updateChildConfig(col.widget, newConfig)
|
|
269
288
|
}}
|
|
270
289
|
isDashboard={true}
|
|
271
|
-
|
|
290
|
+
interactionLabel={link}
|
|
272
291
|
/>
|
|
273
292
|
)}
|
|
274
293
|
{type === 'markup-include' && (
|
|
@@ -300,6 +319,7 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
300
319
|
visualizationConfig={visualizationConfig as DashboardFilters}
|
|
301
320
|
apiFilterDropdowns={apiFilterDropdowns}
|
|
302
321
|
currentViewport={currentViewport}
|
|
322
|
+
interactionLabel={interactionLabel}
|
|
303
323
|
/>
|
|
304
324
|
)}
|
|
305
325
|
{type === 'table' && (
|
|
@@ -311,6 +331,7 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
311
331
|
visualizationKey={col.widget}
|
|
312
332
|
config={visualizationConfig as TableConfig}
|
|
313
333
|
viewport={currentViewport}
|
|
334
|
+
interactionLabel={interactionLabel}
|
|
314
335
|
/>
|
|
315
336
|
)}
|
|
316
337
|
</VisualizationWrapper>
|
|
@@ -1,50 +1,49 @@
|
|
|
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(
|
|
10
|
-
sharedFilter =>
|
|
11
|
-
(sharedFilter.usedBy && sharedFilter.usedBy.indexOf(`${key}`) !== -1) || sharedFilter.usedBy?.indexOf(key) !== -1
|
|
12
|
-
)
|
|
13
|
-
return c?.length > 0 ? c : false
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
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(
|
|
10
|
+
sharedFilter =>
|
|
11
|
+
(sharedFilter.usedBy && sharedFilter.usedBy.indexOf(`${key}`) !== -1) || sharedFilter.usedBy?.indexOf(key) !== -1
|
|
12
|
+
)
|
|
13
|
+
return c?.length > 0 ? c : false
|
|
14
|
+
}
|
|
15
|
+
export const getFilteredData = (
|
|
16
|
+
state: DashboardState,
|
|
17
|
+
initialFilteredData?: Record<string, any>,
|
|
18
|
+
dataOverride?: Object
|
|
19
|
+
) => {
|
|
20
|
+
const newFilteredData = initialFilteredData || {}
|
|
21
|
+
const { config } = state
|
|
22
|
+
getVizKeys(config).forEach(key => {
|
|
23
|
+
const applicableFilters = getApplicableFilters(config.dashboard, key)
|
|
24
|
+
if (applicableFilters) {
|
|
25
|
+
const { dataKey, data, dataDescription } = config.visualizations[key]
|
|
26
|
+
const _data = (dataOverride || state.data)[dataKey] || data
|
|
27
|
+
const formattedData =
|
|
28
|
+
dataOverride?.[dataKey] || (dataDescription ? getFormattedData(_data, dataDescription) : _data)
|
|
29
|
+
|
|
30
|
+
newFilteredData[key] = filterData(applicableFilters, formattedData)
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
config.rows.forEach((row, index) => {
|
|
34
|
+
if (row.dataKey) {
|
|
35
|
+
const applicableFilters = getApplicableFilters(config.dashboard, index)
|
|
36
|
+
const { dataKey, data, dataDescription } = row
|
|
37
|
+
const _data = (dataOverride || state.data)[dataKey] || data
|
|
38
|
+
if (applicableFilters) {
|
|
39
|
+
const formattedData =
|
|
40
|
+
dataOverride?.[dataKey] ?? dataDescription ? getFormattedData(_data, dataDescription) : _data
|
|
41
|
+
|
|
42
|
+
newFilteredData[index] = filterData(applicableFilters, formattedData)
|
|
43
|
+
} else {
|
|
44
|
+
newFilteredData[index] = _data || []
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
return newFilteredData
|
|
49
|
+
}
|
|
@@ -1,21 +1,37 @@
|
|
|
1
1
|
import _ from 'lodash'
|
|
2
2
|
import { MultiDashboardConfig } from '../types/MultiDashboard'
|
|
3
3
|
import DataTransform from '@cdc/core/helpers/DataTransform'
|
|
4
|
+
import { getApplicableFilters } from './getFilteredData'
|
|
4
5
|
import { filterData } from './filterData'
|
|
5
6
|
import { AnyVisualization } from '@cdc/core/types/Visualization'
|
|
6
7
|
|
|
7
8
|
const transform = new DataTransform()
|
|
8
9
|
|
|
9
|
-
export const getFootnotesVizConfig = (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export const getFootnotesVizConfig = (
|
|
11
|
+
visualizationConfig: AnyVisualization,
|
|
12
|
+
rowNumber: number,
|
|
13
|
+
config: MultiDashboardConfig,
|
|
14
|
+
visualizationKey: string
|
|
15
|
+
) => {
|
|
16
|
+
if (!visualizationConfig?.footnotes) return visualizationConfig
|
|
17
|
+
const data = _.cloneDeep(config.datasets[visualizationConfig.footnotes.dataKey]?.data)
|
|
18
|
+
const dataColumns = data?.length ? Object.keys(data[0]) : []
|
|
19
|
+
const filters = (getApplicableFilters(config.dashboard, rowNumber) || []).filter(filter =>
|
|
20
|
+
dataColumns.includes(filter.columnName)
|
|
21
|
+
)
|
|
22
|
+
// check if shared filters has viz key
|
|
23
|
+
const sharedFilters = config.dashboard.sharedFilters
|
|
24
|
+
const matchingFilters = sharedFilters.filter(f => f.usedBy?.includes(visualizationKey))
|
|
13
25
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
26
|
+
if (matchingFilters.length) {
|
|
27
|
+
visualizationConfig.footnotes.data = filterData(matchingFilters, data)
|
|
28
|
+
} else {
|
|
29
|
+
if (visualizationConfig.footnotes.data) {
|
|
30
|
+
visualizationConfig.footnotes.data = data
|
|
31
|
+
}
|
|
32
|
+
}
|
|
17
33
|
|
|
18
|
-
return
|
|
34
|
+
return visualizationConfig
|
|
19
35
|
}
|
|
20
36
|
|
|
21
37
|
export const getVizConfig = (
|
|
@@ -76,16 +92,17 @@ export const getVizConfig = (
|
|
|
76
92
|
|
|
77
93
|
if (filteredDataOverride) {
|
|
78
94
|
visualizationConfig.data = filteredDataOverride
|
|
79
|
-
visualizationConfig.formattedData = filteredDataOverride
|
|
80
95
|
}
|
|
81
96
|
|
|
82
97
|
if (visualizationConfig.footnotes) {
|
|
83
|
-
const visConfigWithFootnotes = getFootnotesVizConfig(visualizationConfig, config)
|
|
84
|
-
if (multiVizColumn && filteredDataOverride) {
|
|
85
|
-
const vizCategory = filteredDataOverride[0][multiVizColumn]
|
|
86
|
-
// the multiViz filtering
|
|
87
|
-
|
|
88
|
-
|
|
98
|
+
const visConfigWithFootnotes = getFootnotesVizConfig(visualizationConfig, rowNumber, config, visualizationKey)
|
|
99
|
+
if (multiVizColumn && filteredDataOverride && filteredDataOverride.length > 0) {
|
|
100
|
+
const vizCategory = filteredDataOverride?.[0]?.[multiVizColumn]
|
|
101
|
+
// the multiViz filtering is applied after the dashboard filters
|
|
102
|
+
if (vizCategory !== undefined && visConfigWithFootnotes.footnotes.data) {
|
|
103
|
+
const categoryFootnote = visConfigWithFootnotes.footnotes.data.filter(d => d[multiVizColumn] === vizCategory)
|
|
104
|
+
visConfigWithFootnotes.footnotes.data = categoryFootnote
|
|
105
|
+
}
|
|
89
106
|
}
|
|
90
107
|
return visConfigWithFootnotes
|
|
91
108
|
}
|
package/src/index.tsx
CHANGED
|
@@ -15,6 +15,7 @@ ReactDOM.createRoot(domContainer).render(
|
|
|
15
15
|
<React.StrictMode>
|
|
16
16
|
<MultiDashboardWrapper
|
|
17
17
|
configUrl={domContainer.attributes['data-config'].value}
|
|
18
|
+
interactionLabel={domContainer.attributes['data-config'].value}
|
|
18
19
|
isEditor={isEditor}
|
|
19
20
|
isDebug={isDebug}
|
|
20
21
|
/>
|
|
@@ -32,6 +32,7 @@ type RESET_VISUALIZATION = Action<'RESET_VISUALIZATION', { vizKey: string }>
|
|
|
32
32
|
type UPDATE_VISUALIZATION = Action<'UPDATE_VISUALIZATION', { vizKey: string; configureData: Partial<AnyVisualization> }>
|
|
33
33
|
type UPDATE_ROW = Action<'UPDATE_ROW', { rowIndex: number; rowData: Partial<ConfigRow> }>
|
|
34
34
|
type UPDATE_TOGGLE_NAME = Action<'UPDATE_TOGGLE_NAME', { rowIndex: number; columnIndex: number; toggleName: string }>
|
|
35
|
+
type SET_FILTERS_APPLIED = Action<'SET_FILTERS_APPLIED', boolean>
|
|
35
36
|
|
|
36
37
|
type DashboardActions =
|
|
37
38
|
| ADD_VISUALIZATION
|
|
@@ -58,4 +59,5 @@ type DashboardActions =
|
|
|
58
59
|
| UPDATE_VISUALIZATION
|
|
59
60
|
| UPDATE_ROW
|
|
60
61
|
| UPDATE_TOGGLE_NAME
|
|
62
|
+
| SET_FILTERS_APPLIED
|
|
61
63
|
export default DashboardActions
|
|
@@ -35,6 +35,7 @@ export type DashboardState = {
|
|
|
35
35
|
loading: boolean
|
|
36
36
|
preview: boolean
|
|
37
37
|
tabSelected: Tab
|
|
38
|
+
filtersApplied: boolean
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
const reducer = (state: DashboardState, action: DashboardActions): DashboardState => {
|
|
@@ -266,6 +267,9 @@ const reducer = (state: DashboardState, action: DashboardActions): DashboardStat
|
|
|
266
267
|
})
|
|
267
268
|
return { ...state, config: { ...state.config, rows: newRows } }
|
|
268
269
|
}
|
|
270
|
+
case 'SET_FILTERS_APPLIED': {
|
|
271
|
+
return { ...state, filtersApplied: action.payload }
|
|
272
|
+
}
|
|
269
273
|
default:
|
|
270
274
|
return state
|
|
271
275
|
}
|
package/src/types/ConfigRow.ts
CHANGED