@cdc/dashboard 4.25.8 → 4.25.11

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 (88) hide show
  1. package/dist/{cdcdashboard-fce76882.es.js → cdcdashboard-BnB1QM5d.es.js} +6 -13
  2. package/dist/{cdcdashboard-c55ac1ea.es.js → cdcdashboard-D6CG2-Hb.es.js} +5 -12
  3. package/dist/{cdcdashboard-31a33da1.es.js → cdcdashboard-MXgURbdZ.es.js} +6 -13
  4. package/dist/{cdcdashboard-1a1724a1.es.js → cdcdashboard-dgT_1dIT.es.js} +136 -151
  5. package/dist/cdcdashboard.js +80040 -75976
  6. package/examples/api-test/categories.json +18 -0
  7. package/examples/api-test/chart-data.json +602 -0
  8. package/examples/api-test/topics.json +47 -0
  9. package/examples/api-test/years.json +22 -0
  10. package/examples/markup-axis-label.json +4167 -0
  11. package/examples/private/DEV-10538.json +407 -0
  12. package/examples/private/DEV-11405.json +39112 -0
  13. package/examples/private/big-dashboard.json +39112 -0
  14. package/examples/private/brfs-2.json +1532 -0
  15. package/examples/private/brfs.json +2128 -2138
  16. package/examples/private/clade-2.json +430 -0
  17. package/examples/private/delete.json +32919 -0
  18. package/examples/private/diabetes.json +5582 -0
  19. package/examples/private/example-2.json +49796 -0
  20. package/examples/private/group-legend-test.json +328 -0
  21. package/examples/private/map.json +1211 -0
  22. package/examples/private/markup-footer/burden_toolkit_mortality_diabetes_attributable_deaths_data.csv +14041 -0
  23. package/examples/private/markup-footer/burden_toolkit_mortality_diabetes_attributable_deaths_per_100000_data.csv +14041 -0
  24. package/examples/private/markup-footer/burden_toolkit_mortality_qaly_data.csv +18721 -0
  25. package/examples/private/markup-footer/burden_toolkit_mortality_yll_data.csv +18721 -0
  26. package/examples/private/markup-footer/mortality-deaths-footnotes-age.csv +3 -0
  27. package/examples/private/markup-variables.json +1451 -0
  28. package/examples/private/markup.json +5471 -0
  29. package/examples/private/mpox.json +38128 -0
  30. package/examples/private/north-dakota.json +1132 -0
  31. package/examples/private/ophdst.json +38754 -0
  32. package/examples/private/pedro.json +1 -0
  33. package/examples/private/pivot.json +683 -0
  34. package/examples/private/reset.json +32920 -0
  35. package/examples/private/sewershed.json +435 -0
  36. package/examples/private/tobacco.json +1938 -0
  37. package/examples/test-api-filter-reset.json +132 -0
  38. package/index.html +2 -2
  39. package/package.json +16 -10
  40. package/src/CdcDashboard.tsx +1 -3
  41. package/src/CdcDashboardComponent.tsx +34 -16
  42. package/src/DashboardContext.tsx +5 -1
  43. package/src/_stories/Dashboard.API.stories.tsx +62 -0
  44. package/src/_stories/Dashboard.stories.tsx +492 -472
  45. package/src/_stories/_mock/api/cessation.json +1 -0
  46. package/src/_stories/_mock/api/data-explorer.json +1 -0
  47. package/src/_stories/_mock/api/explore-by-location.json +1 -0
  48. package/src/_stories/_mock/api/explore-by-topic.json +1 -0
  49. package/src/_stories/_mock/api/legislation.json +1 -0
  50. package/src/_stories/_mock/api/oral-health-data.json +1 -0
  51. package/src/_stories/_mock/custom-order-new-values.json +116 -0
  52. package/src/components/CollapsibleVisualizationRow.tsx +1 -1
  53. package/src/components/DashboardFilters/DashboardFilters.tsx +34 -23
  54. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +29 -12
  55. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +81 -112
  56. package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +82 -52
  57. package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +130 -31
  58. package/src/components/DashboardFilters/_stories/DashboardFilters.stories.tsx +80 -21
  59. package/src/components/DataDesignerModal.tsx +227 -210
  60. package/src/components/Header/Header.tsx +13 -12
  61. package/src/components/Toggle/Toggle.tsx +48 -47
  62. package/src/components/VisualizationRow.tsx +13 -6
  63. package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +2 -0
  64. package/src/components/Widget/Widget.tsx +47 -18
  65. package/src/helpers/addValuesToDashboardFilters.ts +111 -60
  66. package/src/helpers/apiFilterHelpers.ts +190 -166
  67. package/src/helpers/filterData.ts +52 -7
  68. package/src/helpers/filterResetHelpers.ts +102 -0
  69. package/src/helpers/formatConfigBeforeSave.ts +137 -0
  70. package/src/helpers/getVizConfig.ts +36 -18
  71. package/src/helpers/loadAPIFilters.ts +109 -99
  72. package/src/helpers/reloadURLHelpers.ts +1 -1
  73. package/src/helpers/tests/filterResetHelpers.test.ts +532 -0
  74. package/src/helpers/tests/formatConfigBeforeSave.test.ts +69 -0
  75. package/src/index.tsx +1 -1
  76. package/src/scss/editor-panel.scss +3 -431
  77. package/src/scss/grid.scss +7 -5
  78. package/src/scss/main.scss +1 -24
  79. package/src/store/errorMessage/errorMessage.reducer.ts +1 -1
  80. package/src/types/DashboardFilters.ts +9 -8
  81. package/src/types/InitialState.ts +12 -12
  82. package/vite.config.js +1 -1
  83. package/vitest.config.ts +16 -0
  84. package/src/coreStyles_dashboard.scss +0 -3
  85. package/src/helpers/getAutoLoadVisualization.ts +0 -11
  86. package/src/scss/mixins.scss +0 -47
  87. package/src/scss/variables.scss +0 -5
  88. /package/dist/{cdcdashboard-548642e6.es.js → cdcdashboard-Ct2SB0vL.es.js} +0 -0
@@ -1,210 +1,227 @@
1
- import { useGlobalContext } from '@cdc/core/components/GlobalContext'
2
- import DataDesigner from '@cdc/core/components/managers/DataDesigner'
3
- import { useContext, useMemo, useState } from 'react'
4
- import { DashboardContext, DashboardDispatchContext } from '../DashboardContext'
5
- import Modal from '@cdc/core/components/ui/Modal'
6
- import Loader from '@cdc/core/components/Loader'
7
- import { CheckBox } from '@cdc/core/components/EditorPanel/Inputs'
8
- import Tooltip from '@cdc/core/components/ui/Tooltip'
9
- import _ from 'lodash'
10
- import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
11
- import DataTransform from '@cdc/core/helpers/DataTransform'
12
- import { ConfigureData } from '@cdc/core/types/ConfigureData'
13
- import Icon from '@cdc/core/components/ui/Icon'
14
- import InputSelect from '@cdc/core/components/inputs/InputSelect'
15
-
16
- type DataDesignerModalProps = {
17
- rowIndex: number
18
- vizKey?: string
19
- }
20
-
21
- export const DataDesignerModal: React.FC<DataDesignerModalProps> = ({ vizKey, rowIndex }) => {
22
- const { config } = useContext(DashboardContext)
23
- const { overlay } = useGlobalContext()
24
- const transform = new DataTransform()
25
- const dispatch = useContext(DashboardDispatchContext)
26
- const [canContinue, setCanContinue] = useState(false)
27
- const [useRow, setUseRow] = useState(!vizKey)
28
- const [multiViz, setMultiViz] = useState(!!config.rows[rowIndex].multiVizColumn)
29
- const [errorMessage, setErrorMessage] = useState('')
30
- const [loadingAPIData, setLoadingAPIData] = useState(false)
31
-
32
- const isUpdatingVisualization = useMemo(() => {
33
- return !!vizKey && !useRow
34
- }, [vizKey, useRow])
35
-
36
- const configureData = useMemo(() => {
37
- if (isUpdatingVisualization) {
38
- return config.visualizations[vizKey]
39
- }
40
- return config.rows[rowIndex]
41
- }, [config.visualizations, config.rows, rowIndex, isUpdatingVisualization])
42
-
43
- const fetchData = async datasetKey => {
44
- const { data, dataUrl } = config.datasets[datasetKey]
45
- if (!dataUrl) return data
46
- let newData = data
47
- const noCachedData = dataUrl && !data
48
- const dataSetChanged = datasetKey !== configureData.dataKey
49
- setErrorMessage('')
50
- if (dataSetChanged || noCachedData) {
51
- setLoadingAPIData(true)
52
- try {
53
- newData = await fetchRemoteData(dataUrl)
54
- newData = transform.autoStandardize(newData)
55
- } catch (e) {
56
- setErrorMessage('There was an issue loading the data source. Please check the datasource URL and try again.')
57
- }
58
-
59
- setLoadingAPIData(false)
60
- }
61
- return newData
62
- }
63
-
64
- const updateConfigureData = (newConfigureData: Partial<ConfigureData>) => {
65
- if (isUpdatingVisualization) {
66
- dispatch({ type: 'UPDATE_VISUALIZATION', payload: { vizKey, configureData: newConfigureData } })
67
- } else {
68
- dispatch({ type: 'UPDATE_ROW', payload: { rowIndex, rowData: newConfigureData } })
69
- }
70
- }
71
-
72
- const removeDatasetsFromVisualizations = () => {
73
- const columnVisualizations = config.rows[rowIndex].columns.map(column => column.widget).filter(Boolean)
74
- columnVisualizations.forEach(currentVisualizationKey => {
75
- dispatch({ type: 'RESET_VISUALIZATION', payload: { vizKey: currentVisualizationKey } })
76
- })
77
- }
78
-
79
- const changeDataset = async ({ target: { value } }) => {
80
- if (!isUpdatingVisualization) removeDatasetsFromVisualizations()
81
- const newData = value === '' ? {} : await fetchData(value)
82
- const newConfigureData = {
83
- dataDescription: {
84
- horizontal: false
85
- },
86
- formattedData: undefined,
87
- dataKey: value,
88
- data: newData
89
- } as ConfigureData
90
- updateConfigureData(newConfigureData)
91
- }
92
-
93
- const updateDescriptionProp = async (key, value) => {
94
- const datasetKey = configureData.dataKey
95
- const newData = configureData.data || (await fetchData(datasetKey))
96
-
97
- const dataDescription = { ...configureData.dataDescription, [key]: value }
98
-
99
- const newConfigureData = {
100
- data: newData,
101
- dataDescription,
102
- formattedData: transform.developerStandardize(newData, dataDescription)
103
- }
104
-
105
- updateConfigureData(newConfigureData)
106
- setCanContinue(true)
107
- }
108
-
109
- const setMultiVizColumn = (column: string) => {
110
- if (column !== '') {
111
- dispatch({ type: 'UPDATE_ROW', payload: { rowIndex, rowData: { multiVizColumn: column } } })
112
- setCanContinue(true)
113
- }
114
- }
115
-
116
- const setExpandCollapseAllButtons = (selection: boolean) => {
117
- dispatch({ type: 'UPDATE_ROW', payload: { rowIndex, rowData: { expandCollapseAllButtons: selection } } })
118
- setCanContinue(true)
119
- }
120
-
121
- return (
122
- <Modal>
123
- <Modal.Content>
124
- {loadingAPIData && <Loader fullScreen />}
125
- <div className='dataset-selector-container'>
126
- Select a dataset:&nbsp;
127
- <select className='dataset-selector' value={configureData.dataKey || ''} onChange={changeDataset}>
128
- <option value=''>Select a dataset</option>
129
- {config.datasets &&
130
- Object.keys(config.datasets).map(datasetKey => <option key={datasetKey}>{datasetKey}</option>)}
131
- </select>
132
- {vizKey && (
133
- // only shows for visualizations
134
- <CheckBox
135
- label='Apply To Row'
136
- value={useRow}
137
- updateField={(section, subsection, fieldName, value) => {
138
- setUseRow(value)
139
- changeDataset({ target: { value: configureData.dataKey } })
140
- }}
141
- />
142
- )}
143
- </div>
144
- {errorMessage && <p className='text-danger'>{errorMessage}</p>}
145
- {configureData.dataKey && (
146
- <DataDesigner
147
- {...{
148
- configureData,
149
- visualizationKey: vizKey,
150
- updateDescriptionProp
151
- }}
152
- />
153
- )}
154
- {useRow && !!configureData.dataKey ? (
155
- !multiViz ? (
156
- <CheckBox
157
- label='Configure Multiple Visualizations'
158
- value={multiViz}
159
- tooltip={
160
- <Tooltip style={{ textTransform: 'none' }}>
161
- <Tooltip.Target>
162
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
163
- </Tooltip.Target>
164
- <Tooltip.Content>
165
- <p>
166
- You can select a column where for each unique value in the column the configuration for the row
167
- will be repeated in to the data preview.
168
- </p>
169
- </Tooltip.Content>
170
- </Tooltip>
171
- }
172
- updateField={(section, subsection, fieldName, value) => {
173
- if (canContinue && value === true) setCanContinue(false)
174
- setMultiViz(value)
175
- }}
176
- />
177
- ) : (
178
- <>
179
- <InputSelect
180
- options={Object.keys(config.datasets[configureData.dataKey]?.data[0] || {})}
181
- value={config.rows[rowIndex].multiVizColumn}
182
- label='Multi-Visualization Column'
183
- initial='--Select--'
184
- updateField={(section, subsection, fieldName, value) => setMultiVizColumn(value)}
185
- required
186
- />
187
- <CheckBox
188
- value={config.rows[rowIndex].expandCollapseAllButtons}
189
- label=' Add Expand/Collapse All buttons'
190
- fieldName=''
191
- updateField={(section, subsection, fieldName, value) => setExpandCollapseAllButtons(value)}
192
- />
193
- </>
194
- )
195
- ) : (
196
- <></>
197
- )}
198
- {canContinue && (
199
- <button
200
- style={{ margin: '1em', display: 'block' }}
201
- className='cove-button'
202
- onClick={() => overlay?.actions.toggleOverlay()}
203
- >
204
- Continue
205
- </button>
206
- )}
207
- </Modal.Content>
208
- </Modal>
209
- )
210
- }
1
+ import { useGlobalContext } from '@cdc/core/components/GlobalContext'
2
+ import DataDesigner from '@cdc/core/components/managers/DataDesigner'
3
+ import { useContext, useMemo, useState } from 'react'
4
+ import { DashboardContext, DashboardDispatchContext } from '../DashboardContext'
5
+ import Modal from '@cdc/core/components/ui/Modal'
6
+ import Loader from '@cdc/core/components/Loader'
7
+ import { CheckBox, Select } from '@cdc/core/components/EditorPanel/Inputs'
8
+ import Tooltip from '@cdc/core/components/ui/Tooltip'
9
+ import _ from 'lodash'
10
+ import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
11
+ import DataTransform from '@cdc/core/helpers/DataTransform'
12
+ import { ConfigureData } from '@cdc/core/types/ConfigureData'
13
+ import Icon from '@cdc/core/components/ui/Icon'
14
+
15
+ type DataDesignerModalProps = {
16
+ rowIndex: number
17
+ vizKey?: string
18
+ }
19
+
20
+ export const DataDesignerModal: React.FC<DataDesignerModalProps> = ({ vizKey, rowIndex }) => {
21
+ const { config } = useContext(DashboardContext)
22
+ const { overlay } = useGlobalContext()
23
+ const transform = new DataTransform()
24
+ const dispatch = useContext(DashboardDispatchContext)
25
+ const [canContinue, setCanContinue] = useState(false)
26
+ const [useRow, setUseRow] = useState(!vizKey)
27
+ const [multiViz, setMultiViz] = useState(!!config.rows[rowIndex].multiVizColumn)
28
+ const [errorMessage, setErrorMessage] = useState('')
29
+ const [loadingAPIData, setLoadingAPIData] = useState(false)
30
+
31
+ const isUpdatingVisualization = useMemo(() => {
32
+ return !!vizKey && !useRow
33
+ }, [vizKey, useRow])
34
+
35
+ const configureData = useMemo(() => {
36
+ let baseConfig
37
+ if (isUpdatingVisualization) {
38
+ baseConfig = config.visualizations[vizKey]
39
+ } else {
40
+ baseConfig = config.rows[rowIndex]
41
+ }
42
+
43
+ // Directly attach data from datasets
44
+ const data = baseConfig?.dataKey ? config.datasets[baseConfig.dataKey]?.data : undefined
45
+
46
+ return {
47
+ ...baseConfig,
48
+ data
49
+ }
50
+ }, [config.visualizations, config.datasets, config.rows, rowIndex, isUpdatingVisualization])
51
+
52
+ const fetchData = async datasetKey => {
53
+ const { data, dataUrl } = config.datasets[datasetKey]
54
+ if (!dataUrl) return data
55
+ let newData = data
56
+ const noCachedData = dataUrl && !data
57
+ const dataSetChanged = datasetKey !== configureData.dataKey
58
+ setErrorMessage('')
59
+ if (dataSetChanged || noCachedData) {
60
+ setLoadingAPIData(true)
61
+ try {
62
+ newData = await fetchRemoteData(dataUrl)
63
+ newData = transform.autoStandardize(newData)
64
+ } catch (e) {
65
+ setErrorMessage('There was an issue loading the data source. Please check the datasource URL and try again.')
66
+ }
67
+
68
+ setLoadingAPIData(false)
69
+ }
70
+ return newData
71
+ }
72
+
73
+ const updateConfigureData = (newConfigureData: Partial<ConfigureData>) => {
74
+ if (isUpdatingVisualization) {
75
+ dispatch({ type: 'UPDATE_VISUALIZATION', payload: { vizKey, configureData: newConfigureData } })
76
+ } else {
77
+ dispatch({ type: 'UPDATE_ROW', payload: { rowIndex, rowData: newConfigureData } })
78
+ }
79
+ }
80
+
81
+ const removeDatasetsFromVisualizations = () => {
82
+ const columnVisualizations = config.rows[rowIndex].columns.map(column => column.widget).filter(Boolean)
83
+ columnVisualizations.forEach(currentVisualizationKey => {
84
+ dispatch({ type: 'RESET_VISUALIZATION', payload: { vizKey: currentVisualizationKey } })
85
+ })
86
+ }
87
+
88
+ const changeDataset = async ({ target: { value } }) => {
89
+ if (!isUpdatingVisualization) removeDatasetsFromVisualizations()
90
+ const newData = value === '' ? {} : await fetchData(value)
91
+ const newConfigureData = {
92
+ dataDescription: {
93
+ horizontal: false
94
+ },
95
+ formattedData: undefined,
96
+ dataKey: value,
97
+ data: newData
98
+ } as ConfigureData
99
+ updateConfigureData(newConfigureData)
100
+ }
101
+
102
+ const updateDescriptionProp = async (key, value) => {
103
+ const datasetKey = configureData.dataKey
104
+ const newData = configureData.data || (await fetchData(datasetKey))
105
+
106
+ const dataDescription = { ...configureData.dataDescription, [key]: value }
107
+
108
+ const newConfigureData = {
109
+ data: newData,
110
+ dataDescription,
111
+ formattedData: transform.developerStandardize(newData, dataDescription)
112
+ }
113
+
114
+ updateConfigureData(newConfigureData)
115
+ setCanContinue(true)
116
+ }
117
+
118
+ const setMultiVizColumn = (column: string) => {
119
+ if (column !== '') {
120
+ dispatch({ type: 'UPDATE_ROW', payload: { rowIndex, rowData: { multiVizColumn: column } } })
121
+ setCanContinue(true)
122
+ }
123
+ }
124
+
125
+ const setExpandCollapseAllButtons = (selection: boolean) => {
126
+ dispatch({ type: 'UPDATE_ROW', payload: { rowIndex, rowData: { expandCollapseAllButtons: selection } } })
127
+ setCanContinue(true)
128
+ }
129
+
130
+ return (
131
+ <Modal>
132
+ <Modal.Content>
133
+ {loadingAPIData && <Loader fullScreen />}
134
+ <div className='dataset-selector-container'>
135
+ Select a dataset:&nbsp;
136
+ <select
137
+ className='dataset-selector cove-form-select'
138
+ value={configureData.dataKey || ''}
139
+ onChange={changeDataset}
140
+ >
141
+ <option value=''>Select a dataset</option>
142
+ {config.datasets &&
143
+ Object.keys(config.datasets).map(datasetKey => (
144
+ <option key={datasetKey} value={datasetKey}>
145
+ {datasetKey}
146
+ </option>
147
+ ))}
148
+ </select>
149
+ {vizKey && (
150
+ // only shows for visualizations
151
+ <CheckBox
152
+ label='Apply To Row'
153
+ value={useRow}
154
+ updateField={(section, subsection, fieldName, value) => {
155
+ setUseRow(value)
156
+ changeDataset({ target: { value: configureData.dataKey } })
157
+ }}
158
+ />
159
+ )}
160
+ </div>
161
+ {errorMessage && <p className='text-danger'>{errorMessage}</p>}
162
+ {configureData.dataKey && (
163
+ <DataDesigner
164
+ {...{
165
+ configureData,
166
+ visualizationKey: vizKey,
167
+ updateDescriptionProp
168
+ }}
169
+ />
170
+ )}
171
+ {useRow && !!configureData.dataKey ? (
172
+ !multiViz ? (
173
+ <CheckBox
174
+ label='Configure Multiple Visualizations'
175
+ value={multiViz}
176
+ tooltip={
177
+ <Tooltip style={{ textTransform: 'none' }}>
178
+ <Tooltip.Target>
179
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
180
+ </Tooltip.Target>
181
+ <Tooltip.Content>
182
+ <p>
183
+ You can select a column where for each unique value in the column the configuration for the row
184
+ will be repeated in to the data preview.
185
+ </p>
186
+ </Tooltip.Content>
187
+ </Tooltip>
188
+ }
189
+ updateField={(section, subsection, fieldName, value) => {
190
+ if (canContinue && value === true) setCanContinue(false)
191
+ setMultiViz(value)
192
+ }}
193
+ />
194
+ ) : (
195
+ <>
196
+ <Select
197
+ options={Object.keys(config.datasets[configureData.dataKey]?.data[0] || {})}
198
+ value={config.rows[rowIndex].multiVizColumn}
199
+ label='Multi-Visualization Column'
200
+ initial='--Select--'
201
+ updateField={(section, subsection, fieldName, value) => setMultiVizColumn(value)}
202
+ required
203
+ />
204
+ <CheckBox
205
+ value={config.rows[rowIndex].expandCollapseAllButtons}
206
+ label=' Add Expand/Collapse All buttons'
207
+ fieldName=''
208
+ updateField={(section, subsection, fieldName, value) => setExpandCollapseAllButtons(value)}
209
+ />
210
+ </>
211
+ )
212
+ ) : (
213
+ <></>
214
+ )}
215
+ {canContinue && (
216
+ <button
217
+ style={{ margin: '1em', display: 'block' }}
218
+ className='cove-button'
219
+ onClick={() => overlay?.actions.toggleOverlay()}
220
+ >
221
+ Continue
222
+ </button>
223
+ )}
224
+ </Modal.Content>
225
+ </Modal>
226
+ )
227
+ }
@@ -1,5 +1,5 @@
1
- import { useEffect, useContext } from 'react'
2
-
1
+ import { useContext, useRef } from 'react'
2
+ import cloneConfig from '@cdc/core/helpers/cloneConfig'
3
3
  import { DashboardContext, DashboardDispatchContext } from '../../DashboardContext'
4
4
 
5
5
  import './index.scss'
@@ -20,7 +20,7 @@ const Header = (props: HeaderProps) => {
20
20
  const dispatch = useContext(DashboardDispatchContext)
21
21
  const back = () => {
22
22
  if (!visualizationKey) return
23
- const newConfig = _.cloneDeep(config)
23
+ const newConfig = cloneConfig(config)
24
24
  newConfig.visualizations[visualizationKey].editing = false
25
25
  dispatch({ type: 'SET_CONFIG', payload: newConfig })
26
26
 
@@ -49,28 +49,29 @@ const Header = (props: HeaderProps) => {
49
49
  }
50
50
 
51
51
  const convertStateToConfig = () => {
52
- const strippedState = _.cloneDeep(config)
52
+ const strippedState = cloneConfig(config)
53
53
  delete strippedState.newViz
54
54
  delete strippedState.runtime
55
55
 
56
56
  return strippedState
57
57
  }
58
58
 
59
- useEffect(() => {
60
- const parsedData = convertStateToConfig()
59
+ const configStringRef = useRef<string>()
61
60
 
62
- // Emit the data in a regular JS event so it can be consumed by anything.
63
- const event = new CustomEvent('updateVizConfig', { detail: JSON.stringify(parsedData) })
61
+ // Only update parent when config content actually changes (not just reference)
62
+ const configString = JSON.stringify(convertStateToConfig())
63
+ if (configStringRef.current !== configString) {
64
+ configStringRef.current = configString
64
65
 
66
+ // Emit the data in a regular JS event so it can be consumed by anything.
67
+ const event = new CustomEvent('updateVizConfig', { detail: configString })
65
68
  window.dispatchEvent(event)
66
69
 
67
70
  // Pass up to Editor if needed
68
71
  if (setParentConfig) {
69
- setParentConfig(parsedData)
72
+ setParentConfig(JSON.parse(configString))
70
73
  }
71
-
72
- // eslint-disable-next-line react-hooks/exhaustive-deps
73
- }, [config])
74
+ }
74
75
 
75
76
  const handleCheck = e => {
76
77
  const { checked } = e.currentTarget
@@ -1,47 +1,48 @@
1
- import { ConfigRow } from '../../types/ConfigRow'
2
- import { AnyVisualization } from '@cdc/core/types/Visualization'
3
- import { getIcon } from '../../helpers/iconHash'
4
- import { labelHash } from '@cdc/core/helpers/labelHash'
5
- import './toggle-style.css'
6
- import _ from 'lodash'
7
-
8
- type ToggleProps = {
9
- active: number
10
- row: ConfigRow
11
- visualizations: Record<string, AnyVisualization>
12
- setToggled: (colIndex: number) => void
13
- }
14
- const Toggle: React.FC<ToggleProps> = ({ active, row, visualizations, setToggled, text }) => {
15
- const selectItem = (colIndex, e = null) => {
16
- if (e?.key && e.key !== 'Enter') return
17
- setToggled(colIndex)
18
- }
19
-
20
- return (
21
- <div className='toggle-component'>
22
- {row.columns.map((col, colIndex) => {
23
- if (!col.widget) return null
24
- const type = visualizations[col.widget].type
25
- // Get the column toggele Text or default to the type
26
- const text = col.toggleName ? col.toggleName : labelHash[type]
27
- const selected = colIndex === active
28
- return (
29
- <div
30
- role='radio'
31
- className={selected ? 'selected' : ''}
32
- key={colIndex}
33
- onClick={() => selectItem(colIndex)}
34
- onKeyUp={e => selectItem(colIndex, e)}
35
- aria-checked={selected}
36
- tabIndex={0}
37
- aria-label={`Toggle ${type}`}
38
- >
39
- {getIcon(visualizations[col.widget])} <span>{text}</span>
40
- </div>
41
- )
42
- })}
43
- </div>
44
- )
45
- }
46
-
47
- export default Toggle
1
+ import { ConfigRow } from '../../types/ConfigRow'
2
+ import { AnyVisualization } from '@cdc/core/types/Visualization'
3
+ import { getIcon } from '../../helpers/iconHash'
4
+ import { labelHash } from '@cdc/core/helpers/labelHash'
5
+ import './toggle-style.css'
6
+ import _ from 'lodash'
7
+
8
+ type ToggleProps = {
9
+ active: number
10
+ row: ConfigRow
11
+ visualizations: Record<string, AnyVisualization>
12
+ setToggled: (colIndex: number) => void
13
+ }
14
+ const Toggle: React.FC<ToggleProps> = ({ active, row, visualizations, setToggled, text }) => {
15
+ const selectItem = (colIndex, e = null) => {
16
+ if (e?.key && e.key !== 'Enter' && e.key !== ' ') return
17
+ if (e?.key === ' ') e.preventDefault() // Prevent page scroll
18
+ setToggled(colIndex)
19
+ }
20
+
21
+ return (
22
+ <div className='toggle-component' role='radiogroup' aria-label='Visualization options'>
23
+ {row.columns.map((col, colIndex) => {
24
+ if (!col.widget) return null
25
+ const type = visualizations[col.widget].type
26
+ // Get the column toggele Text or default to the type
27
+ const text = col.toggleName ? col.toggleName : labelHash[type]
28
+ const selected = colIndex === active
29
+ return (
30
+ <div
31
+ role='radio'
32
+ className={selected ? 'selected' : ''}
33
+ key={colIndex}
34
+ onClick={() => selectItem(colIndex)}
35
+ onKeyUp={e => selectItem(colIndex, e)}
36
+ aria-checked={selected}
37
+ tabIndex={0}
38
+ aria-label={`Toggle ${text}`}
39
+ >
40
+ <span aria-hidden='true'>{getIcon(visualizations[col.widget])}</span> <span>{text}</span>
41
+ </div>
42
+ )
43
+ })}
44
+ </div>
45
+ )
46
+ }
47
+
48
+ export default Toggle
@@ -20,6 +20,7 @@ import CdcChart from '@cdc/chart/src/CdcChartComponent'
20
20
  import ExpandCollapseButtons from './ExpandCollapseButtons'
21
21
  import { ChartConfig } from '@cdc/chart/src/types/ChartConfig'
22
22
  import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
23
+ import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
23
24
 
24
25
  type VisualizationWrapperProps = {
25
26
  allExpanded: boolean
@@ -196,12 +197,15 @@ const VisualizationRow: React.FC<VizRowProps> = ({
196
197
  href={`#data-table-${dataKey}`}
197
198
  className='margin-left-href'
198
199
  onClick={() => {
199
- publishAnalyticsEvent(
200
- `link_to_data_table_click`,
201
- 'click',
202
- `${interactionLabel}|#data-table-${dataKey}`,
203
- visualizationConfig.type
204
- )
200
+ publishAnalyticsEvent({
201
+ vizType: visualizationConfig.type,
202
+ vizSubType: getVizSubType(visualizationConfig),
203
+ eventType: `link_to_data_table_click`,
204
+ eventAction: 'click',
205
+ eventLabel: `${interactionLabel}`,
206
+ vizTitle: getVizTitle(visualizationConfig),
207
+ specifics: `table: #data-table-${dataKey}`
208
+ })
205
209
  }}
206
210
  >
207
211
  {dataKey} (Go to Table)
@@ -277,6 +281,7 @@ const VisualizationRow: React.FC<VizRowProps> = ({
277
281
  updateChildConfig(col.widget, newConfig)
278
282
  }}
279
283
  isDashboard={true}
284
+ interactionLabel={interactionLabel}
280
285
  />
281
286
  )}
282
287
  {type === 'waffle-chart' && (
@@ -294,10 +299,12 @@ const VisualizationRow: React.FC<VizRowProps> = ({
294
299
  <CdcMarkupInclude
295
300
  key={col.widget}
296
301
  config={visualizationConfig}
302
+ datasets={config.datasets}
297
303
  isDashboard={true}
298
304
  setConfig={newConfig => {
299
305
  updateChildConfig(col.widget, newConfig)
300
306
  }}
307
+ interactionLabel={interactionLabel}
301
308
  />
302
309
  )}
303
310
  {type === 'filtered-text' && (