@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.
@@ -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 href={`#data-table-${dataKey}`} className='margin-left-href'>
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
- configUrl={link}
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
- export const getFilteredData = (
17
- state: DashboardState,
18
- initialFilteredData?: Record<string, any>,
19
- dataOverride?: Object
20
- ) => {
21
- const newFilteredData = initialFilteredData || {}
22
- const { config } = state
23
- getVizKeys(config).forEach(key => {
24
- const applicableFilters = getApplicableFilters(config.dashboard, key)
25
- if (applicableFilters) {
26
- const { dataKey, data, dataDescription } = config.visualizations[key]
27
- const _data = (dataOverride || state.data)[dataKey] || data
28
- const formattedData =
29
- dataOverride?.[dataKey] || (dataDescription ? getFormattedData(_data, dataDescription) : _data)
30
-
31
- newFilteredData[key] = filterData(applicableFilters, formattedData)
32
- }
33
- })
34
- config.rows.forEach((row, index) => {
35
- if (row.dataKey) {
36
- const applicableFilters = getApplicableFilters(config.dashboard, index)
37
- const { dataKey, data, dataDescription } = row
38
- const _data = (dataOverride || state.data)[dataKey] || data
39
- if (applicableFilters) {
40
- const formattedData =
41
- dataOverride?.[dataKey] ?? dataDescription ? getFormattedData(_data, dataDescription) : _data
42
-
43
- newFilteredData[index] = filterData(applicableFilters, formattedData)
44
- } else {
45
- newFilteredData[index] = _data || []
46
- }
47
- }
48
- })
49
- return newFilteredData
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 = (vizConfig: AnyVisualization, config: MultiDashboardConfig) => {
10
- if (!vizConfig?.footnotes) return vizConfig
11
-
12
- const data = config.datasets[vizConfig.footnotes.dataKey]?.data ?? []
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
- const filters = config?.dashboard?.sharedFilters
15
- if (filters.length === 0) return vizConfig
16
- vizConfig.footnotes.data = filters.length ? filterData(filters, data) : data
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 vizConfig
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 filtering is applied after the dashboard filters
87
- const categoryFootnote = visConfigWithFootnotes.footnotes.data.filter(d => d[multiVizColumn] === vizCategory)
88
- visConfigWithFootnotes.footnotes.data = categoryFootnote
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
  }
@@ -16,4 +16,5 @@ export type ConfigRow = {
16
16
  toggle?: boolean
17
17
  equalHeight?: boolean
18
18
  multiVizColumn?: string
19
+ originalMultiVizColumn?: string
19
20
  } & ConfigureData
@@ -8,4 +8,5 @@ export type InitialState = {
8
8
  filteredData: Object
9
9
  preview: boolean
10
10
  tabSelected: Tab
11
+ filtersApplied: boolean
11
12
  }