@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.
Files changed (151) hide show
  1. package/CONFIG.md +219 -0
  2. package/README.md +60 -20
  3. package/dist/cdcdashboard-CY9IcPSi.es.js +6 -0
  4. package/dist/cdcdashboard-DlpiY3fQ.es.js +4 -0
  5. package/dist/cdcdashboard.js +61559 -58048
  6. package/examples/__data__/data-2.json +6 -0
  7. package/examples/__data__/data.json +6 -0
  8. package/examples/dashboard-conditions-filters-incomplete.json +221 -0
  9. package/examples/dashboard-missing-datasets-multi.json +174 -0
  10. package/examples/dashboard-missing-datasets-single.json +121 -0
  11. package/examples/dashboard-multi-dashboard-version-regression.json +146 -0
  12. package/examples/dashboard-shared-filter-row-delete-cleanup.json +186 -0
  13. package/examples/dashboard-stale-dataset-keys.json +181 -0
  14. package/examples/dashboard-tiered-filter-regression.json +190 -0
  15. package/examples/legend-issue.json +1 -1
  16. package/examples/minimal-example.json +34 -0
  17. package/examples/private/cfa-dashboard.json +651 -0
  18. package/examples/private/data-bite-wrap.json +6936 -0
  19. package/examples/private/dengue.json +4640 -0
  20. package/examples/private/link_to_file.json +16662 -0
  21. package/examples/private/multi-dash-fix.json +16963 -0
  22. package/examples/private/versions.json +41612 -0
  23. package/examples/sankey.json +3 -3
  24. package/examples/test-api-filter-reset.json +4 -4
  25. package/examples/tp5-test.json +86 -4
  26. package/examples/us-map-filter-example.json +1074 -0
  27. package/package.json +9 -9
  28. package/src/CdcDashboard.tsx +6 -2
  29. package/src/CdcDashboardComponent.tsx +179 -88
  30. package/src/DashboardCopyPasteContext.test.tsx +33 -0
  31. package/src/DashboardCopyPasteContext.tsx +48 -0
  32. package/src/_stories/Dashboard.EditorRegression.stories.tsx +72 -0
  33. package/src/_stories/Dashboard.Regression.stories.tsx +196 -0
  34. package/src/_stories/Dashboard.Zoom.stories.tsx +88 -0
  35. package/src/_stories/Dashboard.smoke.stories.tsx +33 -0
  36. package/src/_stories/Dashboard.stories.tsx +337 -2
  37. package/src/_stories/FilteredTextMigrationComparison.stories.tsx +87 -0
  38. package/src/_stories/_mock/dashboard-data-driven-colors.json +171 -0
  39. package/src/_stories/_mock/tp5-test.json +86 -5
  40. package/src/components/Column.test.tsx +176 -0
  41. package/src/components/Column.tsx +214 -13
  42. package/src/components/DashboardConditionModal.test.tsx +420 -0
  43. package/src/components/DashboardConditionModal.tsx +367 -0
  44. package/src/components/DashboardConditionSummary.tsx +59 -0
  45. package/src/components/DashboardEditors.tsx +23 -0
  46. package/src/components/DashboardFilters/DashboardFilters.test.tsx +267 -0
  47. package/src/components/DashboardFilters/DashboardFilters.tsx +193 -172
  48. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.test.tsx +164 -0
  49. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +46 -6
  50. package/src/components/DashboardFilters/DashboardFiltersEditor/components/APIModal.tsx +5 -3
  51. package/src/components/DashboardFilters/DashboardFiltersEditor/components/DeleteFilterModal.tsx +59 -58
  52. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.test.tsx +304 -0
  53. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +43 -36
  54. package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +2 -2
  55. package/src/components/DashboardFilters/DashboardFiltersWrapper.test.tsx +142 -0
  56. package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +32 -27
  57. package/src/components/DashboardFilters/dashboardfilter.styles.css +42 -27
  58. package/src/components/DataDesignerModal.tsx +2 -1
  59. package/src/components/ExpandCollapseButtons.tsx +6 -4
  60. package/src/components/Grid.tsx +12 -7
  61. package/src/components/Header/Header.tsx +36 -17
  62. package/src/components/MultiConfigTabs/MultiConfigTabs.tsx +141 -140
  63. package/src/components/Row.test.tsx +228 -0
  64. package/src/components/Row.tsx +104 -28
  65. package/src/components/VisualizationRow.test.tsx +396 -0
  66. package/src/components/VisualizationRow.tsx +177 -51
  67. package/src/components/VisualizationsPanel/VisualizationsPanel.test.tsx +49 -0
  68. package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +14 -13
  69. package/src/components/Widget/Widget.test.tsx +218 -0
  70. package/src/components/Widget/Widget.tsx +123 -20
  71. package/src/components/Widget/widget.styles.css +58 -14
  72. package/src/components/dashboard-condition-modal.css +76 -0
  73. package/src/components/dashboard-condition-summary.css +87 -0
  74. package/src/data/initial-state.js +1 -0
  75. package/src/helpers/addValuesToDashboardFilters.ts +3 -5
  76. package/src/helpers/addVisualization.ts +17 -4
  77. package/src/helpers/cloneDashboardWidget.ts +127 -0
  78. package/src/helpers/dashboardColumnWidgets.ts +99 -0
  79. package/src/helpers/dashboardConditionUi.ts +47 -0
  80. package/src/helpers/dashboardConditions.ts +200 -0
  81. package/src/helpers/dashboardFilterTargets.ts +156 -0
  82. package/src/helpers/filterData.ts +4 -9
  83. package/src/helpers/filterVisibility.ts +20 -0
  84. package/src/helpers/formatConfigBeforeSave.ts +2 -2
  85. package/src/helpers/getFilteredData.ts +18 -5
  86. package/src/helpers/getUpdateConfig.ts +43 -12
  87. package/src/helpers/getVizRowColumnLocator.ts +11 -1
  88. package/src/helpers/iconHash.tsx +9 -3
  89. package/src/helpers/mapDataToConfig.ts +31 -29
  90. package/src/helpers/reloadURLHelpers.ts +25 -5
  91. package/src/helpers/removeDashboardFilter.ts +33 -33
  92. package/src/helpers/tests/addVisualization.test.ts +53 -9
  93. package/src/helpers/tests/cloneDashboardWidget.test.ts +136 -0
  94. package/src/helpers/tests/dashboardColumnWidgets.test.ts +99 -0
  95. package/src/helpers/tests/dashboardConditionUi.test.ts +41 -0
  96. package/src/helpers/tests/dashboardConditions.test.ts +428 -0
  97. package/src/helpers/tests/formatConfigBeforeSave.test.ts +51 -0
  98. package/src/helpers/tests/getFilteredData.test.ts +265 -86
  99. package/src/helpers/tests/getUpdateConfig.test.ts +338 -0
  100. package/src/helpers/tests/reloadURLHelpers.test.ts +394 -238
  101. package/src/index.tsx +6 -3
  102. package/src/scss/grid.scss +281 -22
  103. package/src/scss/main.scss +215 -64
  104. package/src/store/dashboard.actions.ts +17 -4
  105. package/src/store/dashboard.reducer.test.ts +538 -0
  106. package/src/store/dashboard.reducer.ts +136 -22
  107. package/src/test/CdcDashboard.test.jsx +24 -0
  108. package/src/test/CdcDashboard.test.tsx +148 -0
  109. package/src/test/CdcDashboardComponent.test.tsx +935 -2
  110. package/src/types/ConfigRow.ts +15 -0
  111. package/src/types/DashboardFilters.ts +4 -0
  112. package/src/types/SharedFilter.ts +2 -0
  113. package/tests/fixtures/dashboard-config-with-metadata.json +1 -1
  114. package/dist/cdcdashboard-vr9HZwRt.es.js +0 -6
  115. package/examples/DEV-6574.json +0 -2224
  116. package/examples/api-dashboard-data.json +0 -272
  117. package/examples/api-dashboard-years.json +0 -11
  118. package/examples/api-geographies-data.json +0 -11
  119. package/examples/chart-data.json +0 -5409
  120. package/examples/custom/css/respiratory.css +0 -236
  121. package/examples/custom/js/respiratory.js +0 -242
  122. package/examples/default-data.json +0 -368
  123. package/examples/default-filter-control.json +0 -209
  124. package/examples/default-multi-dataset-shared-filter.json +0 -1729
  125. package/examples/default-multi-dataset.json +0 -506
  126. package/examples/ed-visits-county-file.json +0 -402
  127. package/examples/filters/Alabama.json +0 -72
  128. package/examples/filters/Alaska.json +0 -1737
  129. package/examples/filters/Arkansas.json +0 -4713
  130. package/examples/filters/California.json +0 -212
  131. package/examples/filters/Colorado.json +0 -1500
  132. package/examples/filters/Connecticut.json +0 -559
  133. package/examples/filters/Delaware.json +0 -63
  134. package/examples/filters/DistrictofColumbia.json +0 -63
  135. package/examples/filters/Florida.json +0 -4217
  136. package/examples/filters/States.json +0 -146
  137. package/examples/state-level.json +0 -90136
  138. package/examples/state-points.json +0 -10474
  139. package/examples/temp-example-data.json +0 -130
  140. package/examples/test-dashboard-simple.json +0 -503
  141. package/examples/test-example.json +0 -752
  142. package/examples/test-file.json +0 -147
  143. package/examples/test.json +0 -752
  144. package/examples/testing.json +0 -94456
  145. /package/examples/{data → __data__}/data-with-metadata.json +0 -0
  146. /package/examples/{legend-issue-data.json → __data__/legend-issue-data.json} +0 -0
  147. /package/examples/api-test/{categories.json → __data__/categories.json} +0 -0
  148. /package/examples/api-test/{chart-data.json → __data__/chart-data.json} +0 -0
  149. /package/examples/api-test/{topics.json → __data__/topics.json} +0 -0
  150. /package/examples/api-test/{years.json → __data__/years.json} +0 -0
  151. /package/src/_stories/{Dashboard.Pages.stories.tsx → Dashboard.Pages.smoke.stories.tsx} +0 -0
@@ -0,0 +1,367 @@
1
+ import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
2
+ import DataTransform from '@cdc/core/helpers/DataTransform'
3
+ import Button from '@cdc/core/components/elements/Button'
4
+ import Loader from '@cdc/core/components/Loader'
5
+ import Modal from '@cdc/core/components/ui/Modal'
6
+ import MultiSelect from '@cdc/core/components/MultiSelect'
7
+ import { Select } from '@cdc/core/components/EditorPanel/Inputs'
8
+ import { useGlobalContext } from '@cdc/core/components/GlobalContext'
9
+ import Tooltip from '@cdc/core/components/ui/Tooltip'
10
+ import Icon from '@cdc/core/components/ui/Icon'
11
+ import { createCoveId } from '@cdc/core/helpers/createCoveId'
12
+ import { useContext, useEffect, useMemo, useState } from 'react'
13
+ import { DashboardContext, DashboardDispatchContext } from '../DashboardContext'
14
+ import { hasConditionalWidgets, normalizeConditionalColumn } from '../helpers/dashboardColumnWidgets'
15
+ import { getDashboardConditionIds } from '../helpers/dashboardConditions'
16
+ import { DASHBOARD_CONDITION_TYPE_OPTIONS, DashboardConditionTypeOption } from '../helpers/dashboardConditionUi'
17
+ import { dashboardConditionsSupportedForRow } from '../helpers/dashboardFilterTargets'
18
+ import { DashboardCondition } from '../types/ConfigRow'
19
+
20
+ import './dashboard-condition-modal.css'
21
+
22
+ type DashboardConditionModalProps = {
23
+ rowIndex: number
24
+ columnIndex?: number
25
+ entryIndex?: number
26
+ }
27
+
28
+ type DashboardConditionFormState = {
29
+ datasetKey: string
30
+ operator: DashboardConditionTypeOption
31
+ columnName: string
32
+ values: string[]
33
+ }
34
+
35
+ const getDashboardConditionFormState = (dashboardCondition?: DashboardCondition): DashboardConditionFormState => ({
36
+ datasetKey: dashboardCondition?.datasetKey || '',
37
+ operator: dashboardCondition?.operator || '',
38
+ columnName: dashboardCondition?.columnName || '',
39
+ values: dashboardCondition?.values || []
40
+ })
41
+
42
+ const tooltipIcon = (label: string) => (
43
+ <Tooltip style={{ textTransform: 'none' }}>
44
+ <Tooltip.Target>
45
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} alt={label} />
46
+ </Tooltip.Target>
47
+ <Tooltip.Content>
48
+ <p className='dashboard-condition-modal__tooltip-text'>{label}</p>
49
+ </Tooltip.Content>
50
+ </Tooltip>
51
+ )
52
+
53
+ export const DashboardConditionModal: React.FC<DashboardConditionModalProps> = ({
54
+ rowIndex,
55
+ columnIndex,
56
+ entryIndex
57
+ }) => {
58
+ const { config } = useContext(DashboardContext)
59
+ const dispatch = useContext(DashboardDispatchContext)
60
+ const { overlay } = useGlobalContext()
61
+ const transform = new DataTransform()
62
+
63
+ const row = config.rows[rowIndex]
64
+ const column = columnIndex === undefined ? undefined : row.columns[columnIndex]
65
+ const isConditionalEntryEditor =
66
+ columnIndex !== undefined && entryIndex !== undefined && hasConditionalWidgets(column)
67
+ const existingDashboardCondition =
68
+ columnIndex === undefined
69
+ ? row.dashboardCondition
70
+ : isConditionalEntryEditor
71
+ ? column?.conditionalWidgets?.[entryIndex]?.dashboardCondition
72
+ : undefined
73
+ const [formState, setFormState] = useState<DashboardConditionFormState>(
74
+ getDashboardConditionFormState(existingDashboardCondition)
75
+ )
76
+ const [datasetRows, setDatasetRows] = useState<Record<string, any>[]>([])
77
+ const [columns, setColumns] = useState<string[]>([])
78
+ const [loadingColumns, setLoadingColumns] = useState(false)
79
+ const [errorMessage, setErrorMessage] = useState('')
80
+ const supportsDashboardConditions = dashboardConditionsSupportedForRow(row)
81
+
82
+ const title =
83
+ columnIndex === undefined
84
+ ? `Row ${rowIndex + 1} Dashboard Condition`
85
+ : isConditionalEntryEditor
86
+ ? `Row ${rowIndex + 1} Column ${columnIndex + 1} Component ${entryIndex + 1} Dashboard Condition`
87
+ : `Row ${rowIndex + 1} Column ${columnIndex + 1} Dashboard Condition`
88
+ const targetLabel = columnIndex === undefined ? 'row' : 'component'
89
+
90
+ const availableDatasets = Object.keys(config.datasets || {})
91
+ const needsValueMatch = formState.operator === 'columnHasAnyValue'
92
+ const usesDashboardFilterState = formState.operator === 'filtersIncomplete'
93
+ const hasCondition = !!formState.operator
94
+ const shouldShowDatasetSelect = hasCondition && !usesDashboardFilterState
95
+ const shouldShowColumnSelect = shouldShowDatasetSelect && needsValueMatch && !!formState.datasetKey
96
+ const shouldShowValueSelect = shouldShowColumnSelect && !!formState.columnName
97
+
98
+ const canSave = useMemo(() => {
99
+ if (!hasCondition) return true
100
+ if (usesDashboardFilterState) return true
101
+ if (!formState.datasetKey || !formState.operator) return false
102
+ if (!needsValueMatch) return true
103
+
104
+ return !!formState.columnName && formState.values.length > 0
105
+ }, [formState, hasCondition, needsValueMatch, usesDashboardFilterState])
106
+
107
+ const updateDashboardCondition = (dashboardCondition?: DashboardCondition) => {
108
+ if (columnIndex === undefined) {
109
+ dispatch({ type: 'UPDATE_ROW', payload: { rowIndex, rowData: { dashboardCondition } } })
110
+ return
111
+ }
112
+
113
+ const columns = row.columns.map((currentColumn, currentColumnIndex) => {
114
+ if (currentColumnIndex !== columnIndex) return currentColumn
115
+
116
+ if (isConditionalEntryEditor) {
117
+ const conditionalWidgets = [...(currentColumn.conditionalWidgets || [])]
118
+ conditionalWidgets[entryIndex] = {
119
+ ...conditionalWidgets[entryIndex],
120
+ dashboardCondition
121
+ }
122
+
123
+ return normalizeConditionalColumn({
124
+ ...currentColumn,
125
+ conditionalWidgets
126
+ })
127
+ }
128
+
129
+ if (!dashboardCondition) {
130
+ return currentColumn
131
+ }
132
+
133
+ return normalizeConditionalColumn({
134
+ ...currentColumn,
135
+ widget: undefined,
136
+ conditionalWidgets: [
137
+ {
138
+ widget: currentColumn.widget,
139
+ dashboardCondition
140
+ }
141
+ ]
142
+ })
143
+ })
144
+ dispatch({ type: 'UPDATE_ROW', payload: { rowIndex, rowData: { columns } } })
145
+ }
146
+
147
+ const closeModal = () => {
148
+ overlay?.actions.toggleOverlay(false)
149
+ }
150
+
151
+ const loadColumns = async (datasetKey: string) => {
152
+ if (!datasetKey) {
153
+ setDatasetRows([])
154
+ setColumns([])
155
+ return
156
+ }
157
+
158
+ const dataset = config.datasets[datasetKey]
159
+ if (!dataset) {
160
+ setDatasetRows([])
161
+ setColumns([])
162
+ return
163
+ }
164
+
165
+ setLoadingColumns(true)
166
+ setErrorMessage('')
167
+
168
+ try {
169
+ let nextData = dataset.data
170
+ if (!nextData && dataset.dataUrl) {
171
+ const response = await fetchRemoteData(dataset.dataUrl)
172
+ nextData = transform.autoStandardize(response.data)
173
+ if (dataset.dataDescription) {
174
+ nextData = transform.developerStandardize(nextData, dataset.dataDescription)
175
+ }
176
+ }
177
+
178
+ setDatasetRows(nextData || [])
179
+ setColumns(Object.keys(nextData?.[0] || {}))
180
+ } catch (_error) {
181
+ setDatasetRows([])
182
+ setColumns([])
183
+ setErrorMessage('There was an issue loading the condition dataset. Please try again.')
184
+ } finally {
185
+ setLoadingColumns(false)
186
+ }
187
+ }
188
+
189
+ useEffect(() => {
190
+ loadColumns(formState.datasetKey)
191
+ }, [formState.datasetKey])
192
+
193
+ const selectedColumnValues = useMemo(() => {
194
+ if (!needsValueMatch || !formState.columnName) return []
195
+
196
+ const distinctValues = datasetRows.reduce((acc, row) => {
197
+ const value = row?.[formState.columnName]
198
+ if (value === undefined || value === null) return acc
199
+
200
+ const normalizedValue = String(value)
201
+ if (!acc.includes(normalizedValue)) {
202
+ acc.push(normalizedValue)
203
+ }
204
+ return acc
205
+ }, [] as string[])
206
+
207
+ return distinctValues
208
+ }, [datasetRows, formState.columnName, needsValueMatch])
209
+
210
+ if (!supportsDashboardConditions) {
211
+ return (
212
+ <Modal>
213
+ <Modal.Content>
214
+ <p>{title} editing is not available for toggle or multi-visualization rows in v1.</p>
215
+ <Button variant='primary' onClick={closeModal}>
216
+ Close
217
+ </Button>
218
+ </Modal.Content>
219
+ </Modal>
220
+ )
221
+ }
222
+
223
+ return (
224
+ <Modal>
225
+ <Modal.Content>
226
+ {loadingColumns && <Loader fullScreen />}
227
+ <div className='dashboard-condition-modal'>
228
+ <h3>{title}</h3>
229
+
230
+ <div className='dashboard-condition-modal__fields'>
231
+ <Select
232
+ className='dashboard-condition-modal__select py-2 ps-2 w-100 d-block'
233
+ fieldName='operator'
234
+ label='Condition Type'
235
+ options={DASHBOARD_CONDITION_TYPE_OPTIONS}
236
+ tooltip={tooltipIcon(
237
+ `Choose whether this ${targetLabel} should appear when the filtered condition dataset has data, has no data, contains one of the selected column values, or when targeted filters are incomplete. Use "Show when filters are incomplete" for static helper content, such as a markup include message explaining that filters must be selected to proceed.`
238
+ )}
239
+ value={formState.operator}
240
+ onChange={event => {
241
+ const operator = event.target.value as DashboardConditionTypeOption
242
+ setFormState(currentState => ({
243
+ ...currentState,
244
+ operator,
245
+ datasetKey: operator && operator !== 'filtersIncomplete' ? currentState.datasetKey : '',
246
+ columnName: operator === 'columnHasAnyValue' ? currentState.columnName : '',
247
+ values: operator === 'columnHasAnyValue' ? currentState.values : []
248
+ }))
249
+ }}
250
+ />
251
+
252
+ {shouldShowDatasetSelect && (
253
+ <>
254
+ <Select
255
+ className='dashboard-condition-modal__select py-2 ps-2 w-100 d-block'
256
+ fieldName='datasetKey'
257
+ label='Condition Dataset'
258
+ options={[
259
+ { value: '', label: '- Select Option -' },
260
+ ...availableDatasets.map(key => ({ value: key, label: key }))
261
+ ]}
262
+ value={formState.datasetKey}
263
+ onChange={event => {
264
+ const datasetKey = event.target.value
265
+ setFormState(currentState => ({
266
+ ...currentState,
267
+ datasetKey,
268
+ columnName: datasetKey === currentState.datasetKey ? currentState.columnName : '',
269
+ values: datasetKey === currentState.datasetKey ? currentState.values : []
270
+ }))
271
+ }}
272
+ />
273
+
274
+ {shouldShowColumnSelect && (
275
+ <>
276
+ <Select
277
+ className='dashboard-condition-modal__select py-2 ps-2 w-100 d-block'
278
+ fieldName='columnName'
279
+ label='Column'
280
+ options={[
281
+ { value: '', label: '- Select Option -' },
282
+ ...columns.map(columnName => ({ value: columnName, label: columnName }))
283
+ ]}
284
+ tooltip={tooltipIcon('Select the dataset column to inspect for this condition.')}
285
+ value={formState.columnName}
286
+ onChange={event => {
287
+ const columnName = event.target.value
288
+ setFormState(currentState => ({
289
+ ...currentState,
290
+ columnName,
291
+ values: columnName === currentState.columnName ? currentState.values : []
292
+ }))
293
+ }}
294
+ />
295
+
296
+ {shouldShowValueSelect && (
297
+ <div className='dashboard-condition-modal__multiselect-field'>
298
+ <span className='edit-label column-heading'>
299
+ Column Values
300
+ {tooltipIcon(
301
+ 'Choose one or more matching values from the selected column. This condition passes when the filtered dataset contains at least one row with one of these values.'
302
+ )}
303
+ </span>
304
+ <MultiSelect
305
+ fieldName='values'
306
+ options={selectedColumnValues.map(value => ({ value, label: value }))}
307
+ selected={formState.values}
308
+ updateField={(_section, _subSection, _fieldName, values) => {
309
+ setFormState(currentState => ({
310
+ ...currentState,
311
+ values
312
+ }))
313
+ }}
314
+ />
315
+ </div>
316
+ )}
317
+ </>
318
+ )}
319
+ </>
320
+ )}
321
+ </div>
322
+
323
+ {errorMessage && <p className='text-danger'>{errorMessage}</p>}
324
+
325
+ <div className='d-flex gap-2 mt-3'>
326
+ <Button
327
+ disabled={!canSave}
328
+ onClick={() => {
329
+ if (!hasCondition || !formState.operator) {
330
+ updateDashboardCondition(undefined)
331
+ closeModal()
332
+ return
333
+ }
334
+
335
+ const nextCondition: DashboardCondition = {
336
+ id:
337
+ existingDashboardCondition?.id ||
338
+ createCoveId('condition', { existingIds: getDashboardConditionIds(config.rows) }),
339
+ operator: formState.operator
340
+ }
341
+
342
+ if (!usesDashboardFilterState) {
343
+ nextCondition.datasetKey = formState.datasetKey
344
+ }
345
+
346
+ if (needsValueMatch) {
347
+ nextCondition.columnName = formState.columnName
348
+ nextCondition.values = formState.values
349
+ }
350
+
351
+ updateDashboardCondition(nextCondition)
352
+ closeModal()
353
+ }}
354
+ variant='primary'
355
+ >
356
+ Save
357
+ </Button>
358
+
359
+ <Button onClick={closeModal} variant='secondary'>
360
+ Cancel
361
+ </Button>
362
+ </div>
363
+ </div>
364
+ </Modal.Content>
365
+ </Modal>
366
+ )
367
+ }
@@ -0,0 +1,59 @@
1
+ import React from 'react'
2
+ import Button from '@cdc/core/components/elements/Button'
3
+ import { useGlobalContext } from '@cdc/core/components/GlobalContext'
4
+ import { iconHash } from '../helpers/iconHash'
5
+ import { getColumnHasAnyValueSummaryParts, getDashboardConditionSummary } from '../helpers/dashboardConditionUi'
6
+ import { DashboardCondition } from '../types/ConfigRow'
7
+ import { DashboardConditionModal } from './DashboardConditionModal'
8
+
9
+ import './dashboard-condition-summary.css'
10
+
11
+ type DashboardConditionSummaryProps = {
12
+ dashboardCondition: DashboardCondition
13
+ rowIndex: number
14
+ columnIndex?: number
15
+ entryIndex?: number
16
+ className?: string
17
+ }
18
+
19
+ export const DashboardConditionSummary: React.FC<DashboardConditionSummaryProps> = ({
20
+ dashboardCondition,
21
+ rowIndex,
22
+ columnIndex,
23
+ entryIndex,
24
+ className = ''
25
+ }) => {
26
+ const { overlay } = useGlobalContext()
27
+ const summary = getDashboardConditionSummary(dashboardCondition)
28
+ const columnHasAnyValueSummaryParts =
29
+ dashboardCondition.operator === 'columnHasAnyValue'
30
+ ? getColumnHasAnyValueSummaryParts(dashboardCondition.columnName)
31
+ : undefined
32
+ const summaryContent = columnHasAnyValueSummaryParts ? (
33
+ <>
34
+ {columnHasAnyValueSummaryParts.prefix}
35
+ <strong>{columnHasAnyValueSummaryParts.columnName}</strong>
36
+ {columnHasAnyValueSummaryParts.suffix}
37
+ </>
38
+ ) : (
39
+ summary
40
+ )
41
+
42
+ return (
43
+ <Button
44
+ aria-label={`Configure Dashboard Condition: ${summary}`}
45
+ className={['dashboard-condition-summary', className].filter(Boolean).join(' ')}
46
+ title={summary}
47
+ onClick={() => {
48
+ overlay?.actions.openOverlay(
49
+ <DashboardConditionModal rowIndex={rowIndex} columnIndex={columnIndex} entryIndex={entryIndex} />
50
+ )
51
+ }}
52
+ >
53
+ <span className='dashboard-condition-summary__icon' aria-hidden='true'>
54
+ {iconHash['condition']}
55
+ </span>
56
+ <span className='dashboard-condition-summary__text'>{summaryContent}</span>
57
+ </Button>
58
+ )
59
+ }
@@ -17,6 +17,8 @@ type DashboardEditorProps = {
17
17
  _updateConfig: (config: any) => void
18
18
  isDebug?: boolean
19
19
  setSharedFilter?: Function
20
+ clearSharedFilter?: (key: string) => void
21
+ hasActiveSharedFilter?: (key: string) => boolean
20
22
  apiFilterDropdowns?: APIFilterDropdowns
21
23
  state: DashboardState
22
24
  interactionLabel: string
@@ -28,6 +30,8 @@ const DashboardEditors: React.FC<DashboardEditorProps> = ({
28
30
  _updateConfig,
29
31
  isDebug,
30
32
  setSharedFilter,
33
+ clearSharedFilter,
34
+ hasActiveSharedFilter,
31
35
  apiFilterDropdowns,
32
36
  state,
33
37
  interactionLabel = ''
@@ -63,6 +67,10 @@ const DashboardEditors: React.FC<DashboardEditorProps> = ({
63
67
  isDebug={isDebug}
64
68
  setConfig={_updateConfig}
65
69
  setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
70
+ clearSharedFilter={setsSharedFilter ? clearSharedFilter : undefined}
71
+ hasActiveSharedFilter={
72
+ setsSharedFilter && hasActiveSharedFilter ? hasActiveSharedFilter(visualizationKey) : false
73
+ }
66
74
  setSharedFilterValue={setSharedFilterValue}
67
75
  isDashboard={true}
68
76
  showLoader={false}
@@ -77,6 +85,11 @@ const DashboardEditors: React.FC<DashboardEditorProps> = ({
77
85
  <CdcDataBite
78
86
  key={visualizationKey}
79
87
  config={{ ...visualizationConfig, newViz: true }}
88
+ rawData={
89
+ state.data?.[visualizationConfig.dataKey] ||
90
+ state.config.datasets?.[visualizationConfig.dataKey]?.data ||
91
+ []
92
+ }
80
93
  isEditor={true}
81
94
  setConfig={_updateConfig}
82
95
  isDashboard={true}
@@ -89,6 +102,11 @@ const DashboardEditors: React.FC<DashboardEditorProps> = ({
89
102
  <CdcWaffleChart
90
103
  key={visualizationKey}
91
104
  config={visualizationConfig}
105
+ rawData={
106
+ state.data?.[visualizationConfig.dataKey] ||
107
+ state.config.datasets?.[visualizationConfig.dataKey]?.data ||
108
+ []
109
+ }
92
110
  isEditor={true}
93
111
  setConfig={_updateConfig}
94
112
  isDashboard={true}
@@ -101,6 +119,11 @@ const DashboardEditors: React.FC<DashboardEditorProps> = ({
101
119
  <CdcMarkupInclude
102
120
  key={visualizationKey}
103
121
  config={visualizationConfig}
122
+ rawData={
123
+ state.data?.[visualizationConfig.dataKey] ||
124
+ state.config.datasets?.[visualizationConfig.dataKey]?.data ||
125
+ []
126
+ }
104
127
  isEditor={true}
105
128
  setConfig={_updateConfig}
106
129
  isDashboard={true}