@cdc/dashboard 4.26.3 → 4.26.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/CONFIG.md +219 -0
- package/README.md +60 -20
- package/dist/cdcdashboard-CY9IcPSi.es.js +6 -0
- package/dist/cdcdashboard-DlpiY3fQ.es.js +4 -0
- package/dist/cdcdashboard.js +61559 -58048
- package/examples/__data__/data-2.json +6 -0
- package/examples/__data__/data.json +6 -0
- package/examples/dashboard-conditions-filters-incomplete.json +221 -0
- package/examples/dashboard-missing-datasets-multi.json +174 -0
- package/examples/dashboard-missing-datasets-single.json +121 -0
- package/examples/dashboard-multi-dashboard-version-regression.json +146 -0
- package/examples/dashboard-shared-filter-row-delete-cleanup.json +186 -0
- package/examples/dashboard-stale-dataset-keys.json +181 -0
- package/examples/dashboard-tiered-filter-regression.json +190 -0
- package/examples/legend-issue.json +1 -1
- package/examples/minimal-example.json +34 -0
- package/examples/private/cfa-dashboard.json +651 -0
- package/examples/private/data-bite-wrap.json +6936 -0
- package/examples/private/dengue.json +4640 -0
- package/examples/private/link_to_file.json +16662 -0
- package/examples/private/multi-dash-fix.json +16963 -0
- package/examples/private/versions.json +41612 -0
- package/examples/sankey.json +3 -3
- package/examples/test-api-filter-reset.json +4 -4
- package/examples/tp5-test.json +86 -4
- package/examples/us-map-filter-example.json +1074 -0
- package/package.json +9 -9
- package/src/CdcDashboard.tsx +6 -2
- package/src/CdcDashboardComponent.tsx +179 -88
- package/src/DashboardCopyPasteContext.test.tsx +33 -0
- package/src/DashboardCopyPasteContext.tsx +48 -0
- package/src/_stories/Dashboard.EditorRegression.stories.tsx +72 -0
- package/src/_stories/Dashboard.Regression.stories.tsx +196 -0
- package/src/_stories/Dashboard.Zoom.stories.tsx +88 -0
- package/src/_stories/Dashboard.smoke.stories.tsx +33 -0
- package/src/_stories/Dashboard.stories.tsx +337 -2
- package/src/_stories/FilteredTextMigrationComparison.stories.tsx +87 -0
- package/src/_stories/_mock/dashboard-data-driven-colors.json +171 -0
- package/src/_stories/_mock/tp5-test.json +86 -5
- package/src/components/Column.test.tsx +176 -0
- package/src/components/Column.tsx +214 -13
- package/src/components/DashboardConditionModal.test.tsx +420 -0
- package/src/components/DashboardConditionModal.tsx +367 -0
- package/src/components/DashboardConditionSummary.tsx +59 -0
- package/src/components/DashboardEditors.tsx +23 -0
- package/src/components/DashboardFilters/DashboardFilters.test.tsx +267 -0
- package/src/components/DashboardFilters/DashboardFilters.tsx +193 -172
- package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.test.tsx +164 -0
- package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +46 -6
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/APIModal.tsx +5 -3
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/DeleteFilterModal.tsx +59 -58
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.test.tsx +304 -0
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +43 -36
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +2 -2
- package/src/components/DashboardFilters/DashboardFiltersWrapper.test.tsx +142 -0
- package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +32 -27
- package/src/components/DashboardFilters/dashboardfilter.styles.css +42 -27
- package/src/components/DataDesignerModal.tsx +2 -1
- package/src/components/ExpandCollapseButtons.tsx +6 -4
- package/src/components/Grid.tsx +12 -7
- package/src/components/Header/Header.tsx +36 -17
- package/src/components/MultiConfigTabs/MultiConfigTabs.tsx +141 -140
- package/src/components/Row.test.tsx +228 -0
- package/src/components/Row.tsx +104 -28
- package/src/components/VisualizationRow.test.tsx +396 -0
- package/src/components/VisualizationRow.tsx +177 -51
- package/src/components/VisualizationsPanel/VisualizationsPanel.test.tsx +49 -0
- package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +14 -13
- package/src/components/Widget/Widget.test.tsx +218 -0
- package/src/components/Widget/Widget.tsx +123 -20
- package/src/components/Widget/widget.styles.css +58 -14
- package/src/components/dashboard-condition-modal.css +76 -0
- package/src/components/dashboard-condition-summary.css +87 -0
- package/src/data/initial-state.js +1 -0
- package/src/helpers/addValuesToDashboardFilters.ts +3 -5
- package/src/helpers/addVisualization.ts +17 -4
- package/src/helpers/cloneDashboardWidget.ts +127 -0
- package/src/helpers/dashboardColumnWidgets.ts +99 -0
- package/src/helpers/dashboardConditionUi.ts +47 -0
- package/src/helpers/dashboardConditions.ts +200 -0
- package/src/helpers/dashboardFilterTargets.ts +156 -0
- package/src/helpers/filterData.ts +4 -9
- package/src/helpers/filterVisibility.ts +20 -0
- package/src/helpers/formatConfigBeforeSave.ts +2 -2
- package/src/helpers/getFilteredData.ts +18 -5
- package/src/helpers/getUpdateConfig.ts +43 -12
- package/src/helpers/getVizRowColumnLocator.ts +11 -1
- package/src/helpers/iconHash.tsx +9 -3
- package/src/helpers/mapDataToConfig.ts +31 -29
- package/src/helpers/reloadURLHelpers.ts +25 -5
- package/src/helpers/removeDashboardFilter.ts +33 -33
- package/src/helpers/tests/addVisualization.test.ts +53 -9
- package/src/helpers/tests/cloneDashboardWidget.test.ts +136 -0
- package/src/helpers/tests/dashboardColumnWidgets.test.ts +99 -0
- package/src/helpers/tests/dashboardConditionUi.test.ts +41 -0
- package/src/helpers/tests/dashboardConditions.test.ts +428 -0
- package/src/helpers/tests/formatConfigBeforeSave.test.ts +51 -0
- package/src/helpers/tests/getFilteredData.test.ts +265 -86
- package/src/helpers/tests/getUpdateConfig.test.ts +338 -0
- package/src/helpers/tests/reloadURLHelpers.test.ts +394 -238
- package/src/index.tsx +6 -3
- package/src/scss/grid.scss +281 -22
- package/src/scss/main.scss +215 -64
- package/src/store/dashboard.actions.ts +17 -4
- package/src/store/dashboard.reducer.test.ts +538 -0
- package/src/store/dashboard.reducer.ts +136 -22
- package/src/test/CdcDashboard.test.jsx +24 -0
- package/src/test/CdcDashboard.test.tsx +148 -0
- package/src/test/CdcDashboardComponent.test.tsx +935 -2
- package/src/types/ConfigRow.ts +15 -0
- package/src/types/DashboardFilters.ts +4 -0
- package/src/types/SharedFilter.ts +2 -0
- package/tests/fixtures/dashboard-config-with-metadata.json +1 -1
- package/dist/cdcdashboard-vr9HZwRt.es.js +0 -6
- package/examples/DEV-6574.json +0 -2224
- package/examples/api-dashboard-data.json +0 -272
- package/examples/api-dashboard-years.json +0 -11
- package/examples/api-geographies-data.json +0 -11
- package/examples/chart-data.json +0 -5409
- package/examples/custom/css/respiratory.css +0 -236
- package/examples/custom/js/respiratory.js +0 -242
- package/examples/default-data.json +0 -368
- package/examples/default-filter-control.json +0 -209
- package/examples/default-multi-dataset-shared-filter.json +0 -1729
- package/examples/default-multi-dataset.json +0 -506
- package/examples/ed-visits-county-file.json +0 -402
- package/examples/filters/Alabama.json +0 -72
- package/examples/filters/Alaska.json +0 -1737
- package/examples/filters/Arkansas.json +0 -4713
- package/examples/filters/California.json +0 -212
- package/examples/filters/Colorado.json +0 -1500
- package/examples/filters/Connecticut.json +0 -559
- package/examples/filters/Delaware.json +0 -63
- package/examples/filters/DistrictofColumbia.json +0 -63
- package/examples/filters/Florida.json +0 -4217
- package/examples/filters/States.json +0 -146
- package/examples/state-level.json +0 -90136
- package/examples/state-points.json +0 -10474
- package/examples/temp-example-data.json +0 -130
- package/examples/test-dashboard-simple.json +0 -503
- package/examples/test-example.json +0 -752
- package/examples/test-file.json +0 -147
- package/examples/test.json +0 -752
- package/examples/testing.json +0 -94456
- /package/examples/{data → __data__}/data-with-metadata.json +0 -0
- /package/examples/{legend-issue-data.json → __data__/legend-issue-data.json} +0 -0
- /package/examples/api-test/{categories.json → __data__/categories.json} +0 -0
- /package/examples/api-test/{chart-data.json → __data__/chart-data.json} +0 -0
- /package/examples/api-test/{topics.json → __data__/topics.json} +0 -0
- /package/examples/api-test/{years.json → __data__/years.json} +0 -0
- /package/src/_stories/{Dashboard.Pages.stories.tsx → Dashboard.Pages.smoke.stories.tsx} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import DataTableStandAlone from '@cdc/core/components/DataTable/DataTableStandAlone'
|
|
2
|
-
import React, { useContext, useEffect, useMemo, useState } from 'react'
|
|
2
|
+
import React, { useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
|
|
3
3
|
import Toggle from './Toggle'
|
|
4
4
|
import cloneDeep from 'lodash/cloneDeep'
|
|
5
5
|
import { ConfigRow } from '../types/ConfigRow'
|
|
@@ -11,6 +11,12 @@ import CdcFilteredText from '@cdc/filtered-text/src/CdcFilteredText'
|
|
|
11
11
|
import DashboardSharedFilters, { APIFilterDropdowns } from './DashboardFilters'
|
|
12
12
|
import { DashboardContext } from '../DashboardContext'
|
|
13
13
|
import { ViewPort } from '@cdc/core/types/ViewPort'
|
|
14
|
+
import { evaluateDashboardCondition } from '../helpers/dashboardConditions'
|
|
15
|
+
import { dashboardConditionsSupportedForRow } from '../helpers/dashboardFilterTargets'
|
|
16
|
+
import {
|
|
17
|
+
hasAuthoredWidgetEntries,
|
|
18
|
+
resolveColumnWidgetEntry as resolveDashboardColumnWidgetEntry
|
|
19
|
+
} from '../helpers/dashboardColumnWidgets'
|
|
14
20
|
import { getVizConfig } from '../helpers/getVizConfig'
|
|
15
21
|
import { TableConfig } from '@cdc/core/components/DataTable/types/TableConfig'
|
|
16
22
|
import CollapsibleVisualizationRow from './CollapsibleVisualizationRow'
|
|
@@ -21,6 +27,7 @@ import ExpandCollapseButtons from './ExpandCollapseButtons'
|
|
|
21
27
|
import { ChartConfig } from '@cdc/chart/src/types/ChartConfig'
|
|
22
28
|
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
|
|
23
29
|
import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
|
|
30
|
+
import { hasVisibleDashboardFiltersForIndexes } from '../helpers/filterVisibility'
|
|
24
31
|
|
|
25
32
|
type VisualizationWrapperProps = {
|
|
26
33
|
allExpanded: boolean
|
|
@@ -63,6 +70,8 @@ type VizRowProps = {
|
|
|
63
70
|
rowIndex: number
|
|
64
71
|
inNoDataState: boolean
|
|
65
72
|
setSharedFilter: Function
|
|
73
|
+
clearSharedFilter: (key: string) => void
|
|
74
|
+
hasActiveSharedFilter: (key: string) => boolean
|
|
66
75
|
updateChildConfig: Function
|
|
67
76
|
apiFilterDropdowns: APIFilterDropdowns
|
|
68
77
|
currentViewport: ViewPort
|
|
@@ -79,6 +88,8 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
79
88
|
rowIndex: index,
|
|
80
89
|
inNoDataState,
|
|
81
90
|
setSharedFilter,
|
|
91
|
+
clearSharedFilter,
|
|
92
|
+
hasActiveSharedFilter,
|
|
82
93
|
updateChildConfig,
|
|
83
94
|
apiFilterDropdowns,
|
|
84
95
|
currentViewport,
|
|
@@ -87,7 +98,8 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
87
98
|
interactionLabel = ''
|
|
88
99
|
}) => {
|
|
89
100
|
const { config, filteredData: dashboardFilteredData, data: rawData } = useContext(DashboardContext)
|
|
90
|
-
const [toggledRow, setToggled] =
|
|
101
|
+
const [toggledRow, setToggled] = useState<number>(0)
|
|
102
|
+
const rowRef = useRef<HTMLDivElement>(null)
|
|
91
103
|
|
|
92
104
|
useEffect(() => {
|
|
93
105
|
if (row.toggle) setToggled(0)
|
|
@@ -103,15 +115,18 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
103
115
|
}
|
|
104
116
|
}, [toggledRow, row.toggle])
|
|
105
117
|
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
118
|
+
const setupMinHeightEqualizer = (rowElement: HTMLElement, selectors: string[]) => {
|
|
119
|
+
let items: HTMLElement[] = []
|
|
120
|
+
const selector = selectors.join(', ')
|
|
121
|
+
const resizeObserver = new ResizeObserver(() => equalizeHeights())
|
|
109
122
|
|
|
110
123
|
const equalizeHeights = () => {
|
|
111
124
|
items.forEach(item => {
|
|
112
125
|
item.style.minHeight = ''
|
|
113
126
|
})
|
|
114
127
|
|
|
128
|
+
if (items.length <= 1) return
|
|
129
|
+
|
|
115
130
|
let maxHeight = 0
|
|
116
131
|
items.forEach(item => {
|
|
117
132
|
const height = item.offsetHeight
|
|
@@ -125,42 +140,105 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
125
140
|
}
|
|
126
141
|
}
|
|
127
142
|
|
|
128
|
-
|
|
143
|
+
const refreshItems = () => {
|
|
144
|
+
const nextItems = Array.from(rowElement.querySelectorAll(selector)) as HTMLElement[]
|
|
145
|
+
const nextSet = new Set(nextItems)
|
|
146
|
+
|
|
147
|
+
items.forEach(item => {
|
|
148
|
+
if (!nextSet.has(item)) {
|
|
149
|
+
resizeObserver.unobserve(item)
|
|
150
|
+
item.style.minHeight = ''
|
|
151
|
+
}
|
|
152
|
+
})
|
|
129
153
|
|
|
130
|
-
|
|
154
|
+
nextItems.forEach(item => {
|
|
155
|
+
if (!items.includes(item)) {
|
|
156
|
+
resizeObserver.observe(item)
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
items = nextItems
|
|
131
161
|
equalizeHeights()
|
|
132
|
-
}
|
|
162
|
+
}
|
|
133
163
|
|
|
134
|
-
|
|
135
|
-
|
|
164
|
+
const mutationObserver = new MutationObserver(() => {
|
|
165
|
+
refreshItems()
|
|
136
166
|
})
|
|
137
167
|
|
|
138
|
-
|
|
168
|
+
mutationObserver.observe(rowElement, { childList: true, subtree: true })
|
|
169
|
+
refreshItems()
|
|
170
|
+
|
|
171
|
+
return () => {
|
|
172
|
+
mutationObserver.disconnect()
|
|
173
|
+
resizeObserver.disconnect()
|
|
174
|
+
items.forEach(item => {
|
|
175
|
+
item.style.minHeight = ''
|
|
176
|
+
})
|
|
177
|
+
}
|
|
139
178
|
}
|
|
140
179
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
180
|
+
const shouldIgnoreDashboardConditions = !dashboardConditionsSupportedForRow(row)
|
|
181
|
+
const columnDashboardConditionEvaluations = useMemo(
|
|
182
|
+
() =>
|
|
183
|
+
row.columns.map(column =>
|
|
184
|
+
resolveDashboardColumnWidgetEntry(column, {
|
|
185
|
+
evaluateCondition: dashboardCondition => {
|
|
186
|
+
if (shouldIgnoreDashboardConditions || !dashboardCondition) {
|
|
187
|
+
return { matches: true, resolved: true }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return evaluateDashboardCondition(dashboardCondition, dashboardFilteredData[dashboardCondition.id])
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
),
|
|
194
|
+
[dashboardFilteredData, row.columns, shouldIgnoreDashboardConditions]
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
const tp5CountInRow = columnDashboardConditionEvaluations.reduce((count, evaluation) => {
|
|
198
|
+
const widgetKey = evaluation?.widget
|
|
199
|
+
if (!widgetKey) return count
|
|
200
|
+
const viz = config.visualizations[widgetKey]
|
|
201
|
+
if (!viz) return count
|
|
202
|
+
|
|
203
|
+
const isTp5DataBite = viz.type === 'data-bite' && (viz as any).biteStyle === 'tp5'
|
|
204
|
+
const isTp5WaffleOrGauge =
|
|
205
|
+
viz.type === 'waffle-chart' && (viz.visualizationType === 'TP5 Waffle' || viz.visualizationType === 'TP5 Gauge')
|
|
206
|
+
const isTp5MarkupInclude = viz.type === 'markup-include' && (viz as any).contentEditor?.style === 'tp5'
|
|
144
207
|
|
|
145
|
-
|
|
208
|
+
return isTp5DataBite || isTp5WaffleOrGauge || isTp5MarkupInclude ? count + 1 : count
|
|
209
|
+
}, 0)
|
|
210
|
+
const needsTP5AutoEqualization = tp5CountInRow > 1
|
|
211
|
+
const shouldEqualizeRow = !!row.equalHeight || needsTP5AutoEqualization
|
|
212
|
+
|
|
213
|
+
// Layer TP5 equalization for row-level title consistency and same-type internals.
|
|
214
|
+
useLayoutEffect(() => {
|
|
215
|
+
if (!shouldEqualizeRow) return
|
|
216
|
+
|
|
217
|
+
const rowElement = rowRef.current
|
|
146
218
|
if (!rowElement) return
|
|
147
219
|
|
|
148
220
|
const cleanups = [
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
221
|
+
// Cross-type TP5 title alignment in the row.
|
|
222
|
+
setupMinHeightEqualizer(rowElement, [
|
|
223
|
+
'.bite__style--tp5 .cdc-callout__heading',
|
|
224
|
+
'.waffle__style--tp5 .cdc-callout__heading',
|
|
225
|
+
'.gauge__style--tp5 .cdc-callout__heading',
|
|
226
|
+
'.markup-include__style--tp5 .cdc-callout__heading'
|
|
227
|
+
]),
|
|
228
|
+
// Same-type gauge internals alignment so gauge meters line up despite content variance.
|
|
229
|
+
setupMinHeightEqualizer(rowElement, ['.gauge__style--tp5 .cove-gauge-chart__content'])
|
|
230
|
+
]
|
|
154
231
|
|
|
155
232
|
return () => {
|
|
156
233
|
cleanups.forEach(cleanup => cleanup())
|
|
157
234
|
}
|
|
158
|
-
}, [
|
|
235
|
+
}, [shouldEqualizeRow, row.columns, config.activeDashboard, filteredDataOverride, dashboardFilteredData[index]])
|
|
159
236
|
|
|
160
|
-
const isFilterRow =
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
237
|
+
const isFilterRow = columnDashboardConditionEvaluations.some(evaluation => {
|
|
238
|
+
const widgetKey = evaluation?.widget
|
|
239
|
+
return widgetKey && config.visualizations[widgetKey]?.type === 'dashboardFilters'
|
|
240
|
+
})
|
|
241
|
+
const needsEqualHeight = shouldEqualizeRow && !isFilterRow
|
|
164
242
|
|
|
165
243
|
const show = useMemo(() => {
|
|
166
244
|
if (row.toggle) {
|
|
@@ -171,6 +249,33 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
171
249
|
}, [config.activeDashboard, toggledRow])
|
|
172
250
|
|
|
173
251
|
const _data = dashboardFilteredData[index] || row.formattedData || []
|
|
252
|
+
const isMultiVizGroupRow = !!row.originalMultiVizColumn && !!filteredDataOverride
|
|
253
|
+
const rowDashboardCondition = useMemo(() => {
|
|
254
|
+
if (shouldIgnoreDashboardConditions || !row.dashboardCondition) {
|
|
255
|
+
return { matches: true, resolved: true }
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return evaluateDashboardCondition(row.dashboardCondition, dashboardFilteredData[row.dashboardCondition.id])
|
|
259
|
+
}, [dashboardFilteredData, row.dashboardCondition, shouldIgnoreDashboardConditions])
|
|
260
|
+
const hasVisibleWidgetColumn = row.columns.some((_column, columnIndex) => {
|
|
261
|
+
if (!row.columns[columnIndex].width) return false
|
|
262
|
+
|
|
263
|
+
const widgetKey = columnDashboardConditionEvaluations[columnIndex]?.widget
|
|
264
|
+
if (!widgetKey) return false
|
|
265
|
+
|
|
266
|
+
const visualization = config.visualizations[widgetKey]
|
|
267
|
+
if (visualization?.type !== 'dashboardFilters') return true
|
|
268
|
+
|
|
269
|
+
return hasVisibleDashboardFiltersForIndexes(
|
|
270
|
+
config.dashboard.sharedFilters,
|
|
271
|
+
(visualization as DashboardFilters).sharedFilterIndexes
|
|
272
|
+
)
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
if (!rowDashboardCondition.matches || !hasVisibleWidgetColumn) {
|
|
276
|
+
return null
|
|
277
|
+
}
|
|
278
|
+
|
|
174
279
|
const dataGroups =
|
|
175
280
|
row.multiVizColumn &&
|
|
176
281
|
_data.reduce((acc, dataRow) => {
|
|
@@ -187,7 +292,9 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
187
292
|
const applyFilters = dashboardFilters.filter(v => !v.autoLoad).flatMap(v => v.sharedFilterIndexes)
|
|
188
293
|
if (hasDashboardApplyBehavior(config.visualizations) && vizConfig.autoLoad) {
|
|
189
294
|
return applyFilters.some(index => {
|
|
190
|
-
const
|
|
295
|
+
const filter = config.dashboard.sharedFilters[index]
|
|
296
|
+
if (!filter) return false
|
|
297
|
+
const { queuedActive, active, subGrouping } = filter
|
|
191
298
|
if (!active && !queuedActive) return true
|
|
192
299
|
if (!queuedActive) return false
|
|
193
300
|
// for nested dropdowns
|
|
@@ -236,6 +343,7 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
236
343
|
|
|
237
344
|
return (
|
|
238
345
|
<div
|
|
346
|
+
ref={rowRef}
|
|
239
347
|
className={`row${needsEqualHeight ? ' equal-height' : ''}${row.toggle ? ' toggle' : ''}`}
|
|
240
348
|
key={`row__${index}`}
|
|
241
349
|
data-row-index={index}
|
|
@@ -245,11 +353,27 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
245
353
|
)}
|
|
246
354
|
{row.columns.map((col, colIndex) => {
|
|
247
355
|
if (col.width) {
|
|
248
|
-
|
|
249
|
-
|
|
356
|
+
const resolvedWidget = columnDashboardConditionEvaluations[colIndex]?.widget
|
|
357
|
+
const hasAuthoredWidgets = hasAuthoredWidgetEntries(col)
|
|
358
|
+
const hiddenByDashboardCondition = hasAuthoredWidgets && !resolvedWidget
|
|
359
|
+
if (!resolvedWidget) {
|
|
360
|
+
if (!hasAuthoredWidgets && (isMultiVizGroupRow || row.toggle)) {
|
|
361
|
+
return null
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return (
|
|
365
|
+
<div
|
|
366
|
+
key={`row__${index}__col__${colIndex}`}
|
|
367
|
+
className={`col-12 col-md-${col.width}${
|
|
368
|
+
hiddenByDashboardCondition ? ' dashboard-condition-hidden' : ''
|
|
369
|
+
}`}
|
|
370
|
+
data-dashboard-condition-hidden={hiddenByDashboardCondition || undefined}
|
|
371
|
+
></div>
|
|
372
|
+
)
|
|
373
|
+
}
|
|
250
374
|
|
|
251
375
|
const visualizationConfig = getVizConfig(
|
|
252
|
-
|
|
376
|
+
resolvedWidget,
|
|
253
377
|
index,
|
|
254
378
|
config,
|
|
255
379
|
rawData,
|
|
@@ -262,9 +386,9 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
262
386
|
|
|
263
387
|
const setsSharedFilter =
|
|
264
388
|
config.dashboard.sharedFilters &&
|
|
265
|
-
config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy ===
|
|
389
|
+
config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy === resolvedWidget).length > 0
|
|
266
390
|
const setSharedFilterValue = setsSharedFilter
|
|
267
|
-
? config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy ===
|
|
391
|
+
? config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy === resolvedWidget)[0].active
|
|
268
392
|
: undefined
|
|
269
393
|
const tableLink = (
|
|
270
394
|
<a
|
|
@@ -300,9 +424,7 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
300
424
|
|
|
301
425
|
const hiddenDashboardFilters =
|
|
302
426
|
type === 'dashboardFilters' &&
|
|
303
|
-
sharedFilterIndexes
|
|
304
|
-
sharedFilterIndexes.filter(idx => config.dashboard.sharedFilters?.[idx]?.showDropdown === false).length ===
|
|
305
|
-
sharedFilterIndexes.length
|
|
427
|
+
!hasVisibleDashboardFiltersForIndexes(config.dashboard.sharedFilters, sharedFilterIndexes)
|
|
306
428
|
|
|
307
429
|
const vizWrapperClass = `col-12 col-md-${col.width}${!shouldShow ? ' d-none' : ''}${
|
|
308
430
|
hideVisualization ? ' hide-parent-visualization' : ''
|
|
@@ -322,12 +444,12 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
322
444
|
>
|
|
323
445
|
{type === 'chart' && (
|
|
324
446
|
<CdcChart
|
|
325
|
-
key={
|
|
447
|
+
key={resolvedWidget}
|
|
326
448
|
config={visualizationConfig as ChartConfig}
|
|
327
449
|
dashboardConfig={config}
|
|
328
450
|
datasets={config.datasets}
|
|
329
451
|
setConfig={newConfig => {
|
|
330
|
-
updateChildConfig(
|
|
452
|
+
updateChildConfig(resolvedWidget, newConfig)
|
|
331
453
|
}}
|
|
332
454
|
setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
|
|
333
455
|
isDashboard={true}
|
|
@@ -337,13 +459,15 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
337
459
|
)}
|
|
338
460
|
{type === 'map' && (
|
|
339
461
|
<CdcMap
|
|
340
|
-
key={
|
|
462
|
+
key={resolvedWidget}
|
|
341
463
|
config={visualizationConfig}
|
|
342
464
|
setConfig={newConfig => {
|
|
343
|
-
updateChildConfig(
|
|
465
|
+
updateChildConfig(resolvedWidget, newConfig)
|
|
344
466
|
}}
|
|
345
467
|
showLoader={false}
|
|
346
468
|
setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
|
|
469
|
+
clearSharedFilter={setsSharedFilter ? clearSharedFilter : undefined}
|
|
470
|
+
hasActiveSharedFilter={setsSharedFilter ? hasActiveSharedFilter(resolvedWidget) : false}
|
|
347
471
|
setSharedFilterValue={setSharedFilterValue}
|
|
348
472
|
isDashboard={true}
|
|
349
473
|
link={link}
|
|
@@ -353,10 +477,11 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
353
477
|
)}
|
|
354
478
|
{type === 'data-bite' && (
|
|
355
479
|
<CdcDataBite
|
|
356
|
-
key={
|
|
480
|
+
key={resolvedWidget}
|
|
357
481
|
config={visualizationConfig}
|
|
482
|
+
rawData={rawData?.[visualizationConfig.dataKey] || []}
|
|
358
483
|
setConfig={newConfig => {
|
|
359
|
-
updateChildConfig(
|
|
484
|
+
updateChildConfig(resolvedWidget, newConfig)
|
|
360
485
|
}}
|
|
361
486
|
isDashboard={true}
|
|
362
487
|
isEditor={config.editing === true}
|
|
@@ -365,10 +490,11 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
365
490
|
)}
|
|
366
491
|
{type === 'waffle-chart' && (
|
|
367
492
|
<CdcWaffleChart
|
|
368
|
-
key={
|
|
493
|
+
key={resolvedWidget}
|
|
369
494
|
config={visualizationConfig}
|
|
495
|
+
rawData={rawData?.[visualizationConfig.dataKey] || []}
|
|
370
496
|
setConfig={newConfig => {
|
|
371
|
-
updateChildConfig(
|
|
497
|
+
updateChildConfig(resolvedWidget, newConfig)
|
|
372
498
|
}}
|
|
373
499
|
isDashboard={true}
|
|
374
500
|
interactionLabel={interactionLabel}
|
|
@@ -376,22 +502,22 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
376
502
|
)}
|
|
377
503
|
{type === 'markup-include' && (
|
|
378
504
|
<CdcMarkupInclude
|
|
379
|
-
key={
|
|
505
|
+
key={resolvedWidget}
|
|
380
506
|
config={visualizationConfig}
|
|
381
507
|
datasets={config.datasets}
|
|
382
508
|
isDashboard={true}
|
|
383
509
|
setConfig={newConfig => {
|
|
384
|
-
updateChildConfig(
|
|
510
|
+
updateChildConfig(resolvedWidget, newConfig)
|
|
385
511
|
}}
|
|
386
512
|
interactionLabel={interactionLabel}
|
|
387
513
|
/>
|
|
388
514
|
)}
|
|
389
515
|
{type === 'filtered-text' && (
|
|
390
516
|
<CdcFilteredText
|
|
391
|
-
key={
|
|
517
|
+
key={resolvedWidget}
|
|
392
518
|
config={visualizationConfig}
|
|
393
519
|
setConfig={newConfig => {
|
|
394
|
-
updateChildConfig(
|
|
520
|
+
updateChildConfig(resolvedWidget, newConfig)
|
|
395
521
|
}}
|
|
396
522
|
isDashboard={true}
|
|
397
523
|
/>
|
|
@@ -399,9 +525,9 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
399
525
|
{type === 'dashboardFilters' && (
|
|
400
526
|
<DashboardSharedFilters
|
|
401
527
|
setConfig={newConfig => {
|
|
402
|
-
updateChildConfig(
|
|
528
|
+
updateChildConfig(resolvedWidget, newConfig)
|
|
403
529
|
}}
|
|
404
|
-
key={
|
|
530
|
+
key={resolvedWidget}
|
|
405
531
|
visualizationConfig={visualizationConfig as DashboardFilters}
|
|
406
532
|
apiFilterDropdowns={apiFilterDropdowns}
|
|
407
533
|
currentViewport={currentViewport}
|
|
@@ -410,11 +536,11 @@ const VisualizationRow: React.FC<VizRowProps> = ({
|
|
|
410
536
|
)}
|
|
411
537
|
{type === 'table' && (
|
|
412
538
|
<DataTableStandAlone
|
|
413
|
-
key={
|
|
539
|
+
key={resolvedWidget}
|
|
414
540
|
updateConfig={newConfig => {
|
|
415
|
-
updateChildConfig(
|
|
541
|
+
updateChildConfig(resolvedWidget, newConfig)
|
|
416
542
|
}}
|
|
417
|
-
visualizationKey={
|
|
543
|
+
visualizationKey={resolvedWidget}
|
|
418
544
|
config={visualizationConfig as TableConfig}
|
|
419
545
|
viewport={currentViewport}
|
|
420
546
|
interactionLabel={interactionLabel}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { render, screen } from '@testing-library/react'
|
|
3
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
4
|
+
import { DashboardContext, DashboardDispatchContext, initialState } from '../../DashboardContext'
|
|
5
|
+
import VisualizationsPanel from './VisualizationsPanel'
|
|
6
|
+
|
|
7
|
+
vi.mock('../Widget/Widget', () => ({
|
|
8
|
+
default: ({ type }: { type: string }) => <div data-testid='creation-widget'>{type}</div>
|
|
9
|
+
}))
|
|
10
|
+
|
|
11
|
+
vi.mock('@cdc/core/components/AdvancedEditor', () => ({
|
|
12
|
+
default: () => <div data-testid='advanced-editor' />
|
|
13
|
+
}))
|
|
14
|
+
|
|
15
|
+
describe('VisualizationsPanel', () => {
|
|
16
|
+
it('does not expose filtered-text in dashboard creation surfaces', () => {
|
|
17
|
+
render(
|
|
18
|
+
<DashboardContext.Provider
|
|
19
|
+
value={{
|
|
20
|
+
...initialState,
|
|
21
|
+
config: {
|
|
22
|
+
type: 'dashboard',
|
|
23
|
+
dashboard: { sharedFilters: [] },
|
|
24
|
+
datasets: {},
|
|
25
|
+
rows: [],
|
|
26
|
+
visualizations: {}
|
|
27
|
+
} as any,
|
|
28
|
+
outerContainerRef: vi.fn(),
|
|
29
|
+
setParentConfig: vi.fn(),
|
|
30
|
+
isDebug: false,
|
|
31
|
+
isEditor: true,
|
|
32
|
+
reloadURLData: vi.fn(),
|
|
33
|
+
loadAPIFilters: vi.fn(),
|
|
34
|
+
setAPIFilterDropdowns: vi.fn(),
|
|
35
|
+
setAPILoading: vi.fn(),
|
|
36
|
+
data: {}
|
|
37
|
+
}}
|
|
38
|
+
>
|
|
39
|
+
<DashboardDispatchContext.Provider value={vi.fn()}>
|
|
40
|
+
<VisualizationsPanel />
|
|
41
|
+
</DashboardDispatchContext.Provider>
|
|
42
|
+
</DashboardContext.Provider>
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
const creationTypes = screen.getAllByTestId('creation-widget').map(widget => widget.textContent)
|
|
46
|
+
expect(creationTypes).toContain('markup-include')
|
|
47
|
+
expect(creationTypes).not.toContain('filtered-text')
|
|
48
|
+
})
|
|
49
|
+
})
|
|
@@ -12,6 +12,8 @@ const VisualizationsPanel = () => {
|
|
|
12
12
|
const [advancedEditing, setAdvancedEditing] = useState(false)
|
|
13
13
|
const { config, isEditor } = useContext(DashboardContext)
|
|
14
14
|
const dispatch = useContext(DashboardDispatchContext)
|
|
15
|
+
const createVisualization = (type, subType) =>
|
|
16
|
+
addVisualization(type, subType, { existingIds: Object.keys(config.visualizations || {}) })
|
|
15
17
|
const loadConfig = incomingConfig => {
|
|
16
18
|
const newConfig = !incomingConfig.multiDashboards
|
|
17
19
|
? incomingConfig
|
|
@@ -29,25 +31,24 @@ const VisualizationsPanel = () => {
|
|
|
29
31
|
<p style={{ fontSize: '14px' }}>Click and drag an item onto the grid to add it to your dashboard.</p>
|
|
30
32
|
<span className='subheading-3'>Chart</span>
|
|
31
33
|
<div className='drag-grid'>
|
|
32
|
-
<Widget addVisualization={() =>
|
|
33
|
-
<Widget addVisualization={() =>
|
|
34
|
-
<Widget addVisualization={() =>
|
|
35
|
-
<Widget addVisualization={() =>
|
|
34
|
+
<Widget addVisualization={() => createVisualization('chart', 'Bar')} type='Bar' />
|
|
35
|
+
<Widget addVisualization={() => createVisualization('chart', 'Line')} type='Line' />
|
|
36
|
+
<Widget addVisualization={() => createVisualization('chart', 'Pie')} type='Pie' />
|
|
37
|
+
<Widget addVisualization={() => createVisualization('chart', 'Sankey')} type='Sankey' />
|
|
36
38
|
</div>
|
|
37
39
|
<span className='subheading-3'>Map</span>
|
|
38
40
|
<div className='drag-grid'>
|
|
39
|
-
<Widget addVisualization={() =>
|
|
40
|
-
<Widget addVisualization={() =>
|
|
41
|
-
<Widget addVisualization={() =>
|
|
41
|
+
<Widget addVisualization={() => createVisualization('map', 'us')} type='us' />
|
|
42
|
+
<Widget addVisualization={() => createVisualization('map', 'world')} type='world' />
|
|
43
|
+
<Widget addVisualization={() => createVisualization('map', 'single-state')} type='single-state' />
|
|
42
44
|
</div>
|
|
43
45
|
<span className='subheading-3'>Misc.</span>
|
|
44
46
|
<div className='drag-grid'>
|
|
45
|
-
<Widget addVisualization={() =>
|
|
46
|
-
<Widget addVisualization={() =>
|
|
47
|
-
<Widget addVisualization={() =>
|
|
48
|
-
<Widget addVisualization={() =>
|
|
49
|
-
<Widget addVisualization={() =>
|
|
50
|
-
<Widget addVisualization={() => addVisualization('table', '')} type='table' />
|
|
47
|
+
<Widget addVisualization={() => createVisualization('data-bite', '')} type='data-bite' />
|
|
48
|
+
<Widget addVisualization={() => createVisualization('waffle-chart', 'Waffle')} type='waffle-chart' />
|
|
49
|
+
<Widget addVisualization={() => createVisualization('markup-include', '')} type='markup-include' />
|
|
50
|
+
<Widget addVisualization={() => createVisualization('dashboardFilters', '')} type='dashboardFilters' />
|
|
51
|
+
<Widget addVisualization={() => createVisualization('table', '')} type='table' />
|
|
51
52
|
</div>
|
|
52
53
|
<AdvancedEditor
|
|
53
54
|
loadConfig={loadConfig}
|