@cdc/dashboard 4.25.5-1 → 4.25.6

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.
@@ -1,80 +1,108 @@
1
- import _ from 'lodash'
2
- import { MultiDashboardConfig } from '../types/MultiDashboard'
3
- import DataTransform from '@cdc/core/helpers/DataTransform'
4
- import { getApplicableFilters } from './getFilteredData'
5
- import { filterData } from './filterData'
6
- import Footnotes from '@cdc/core/types/Footnotes'
7
-
8
- const transform = new DataTransform()
9
-
10
- export const getFootnotesVizConfig = (vizKey: string, rowNumber: number, config: MultiDashboardConfig) => {
11
- const visualizationConfig = _.cloneDeep(config.visualizations[vizKey])
12
-
13
- const data = config.datasets[visualizationConfig.dataKey]?.data
14
- const dataColumns = data?.length ? Object.keys(data[0]) : []
15
- const filters = (getApplicableFilters(config.dashboard, rowNumber) || []).filter(filter =>
16
- dataColumns.includes(filter.columnName)
17
- )
18
- if (filters.length) {
19
- visualizationConfig.formattedData = filterData(filters, data)
20
- }
21
- visualizationConfig.data = data
22
- return visualizationConfig as Footnotes
23
- }
24
-
25
- export const getVizConfig = (
26
- visualizationKey: string,
27
- rowNumber: number,
28
- config: MultiDashboardConfig,
29
- data: Object,
30
- filteredData?: Object
31
- ) => {
32
- if (rowNumber === undefined) return {}
33
- const visualizationConfig = _.cloneDeep(config.visualizations[visualizationKey])
34
- const rowData = config.rows[rowNumber]
35
- if (rowData.footnotesId && rowData.footnotesId === visualizationKey) {
36
- // return the footnotes visualization config with filtered data
37
- return getFootnotesVizConfig(visualizationKey, rowNumber, config)
38
- }
39
- if (rowData?.dataKey) {
40
- // data configured on the row
41
- Object.assign(visualizationConfig, _.pick(rowData, ['dataKey', 'dataDescription', 'formattedData', 'data']))
42
- }
43
-
44
- if (visualizationConfig.table && config.dashboard.sharedFilters.length) {
45
- // Download CSV button needs to know to include shared filter columns
46
- const sharedFilterColumns = config.dashboard.sharedFilters.reduce((acc, filter) => {
47
- if (!filter.usedBy?.length || filter.usedBy?.includes(visualizationKey)) {
48
- const apiFilter = filter.apiFilter
49
- const colName = apiFilter?.textSelector || apiFilter?.valueSelector || filter.columnName
50
- acc.push(colName)
51
- const subGrouping =
52
- apiFilter?.subgroupTextSelector || apiFilter?.subgroupValueSelector || filter.subGrouping?.columnName
53
- if (subGrouping) {
54
- acc.push(subGrouping)
55
- }
56
- }
57
- return acc
58
- }, [])
59
- visualizationConfig.table.sharedFilterColumns = sharedFilterColumns
60
- }
61
-
62
- if (visualizationConfig.formattedData) visualizationConfig.originalFormattedData = visualizationConfig.formattedData
63
- const filteredVizData = filteredData?.[rowNumber] ?? filteredData?.[visualizationKey]
64
-
65
- if (filteredVizData) {
66
- visualizationConfig.data = filteredVizData || []
67
- if (visualizationConfig.formattedData) {
68
- visualizationConfig.formattedData = visualizationConfig.data
69
- }
70
- } else {
71
- const dataKey = visualizationConfig.dataKey || 'backwards-compatibility'
72
- visualizationConfig.data = data[dataKey] || []
73
- if (visualizationConfig.formattedData) {
74
- visualizationConfig.formattedData =
75
- transform.developerStandardize(visualizationConfig.data, visualizationConfig.dataDescription) ||
76
- visualizationConfig.data
77
- }
78
- }
79
- return visualizationConfig
80
- }
1
+ import _ from 'lodash'
2
+ import { MultiDashboardConfig } from '../types/MultiDashboard'
3
+ import DataTransform from '@cdc/core/helpers/DataTransform'
4
+ import { getApplicableFilters } from './getFilteredData'
5
+ import { filterData } from './filterData'
6
+ import { AnyVisualization } from '@cdc/core/types/Visualization'
7
+
8
+ const transform = new DataTransform()
9
+
10
+ export const getFootnotesVizConfig = (
11
+ visualizationConfig: AnyVisualization,
12
+ rowNumber: number,
13
+ config: MultiDashboardConfig
14
+ ) => {
15
+ if (!visualizationConfig?.footnotes) return visualizationConfig
16
+ const data = _.cloneDeep(config.datasets[visualizationConfig.footnotes.dataKey]?.data)
17
+ const dataColumns = data?.length ? Object.keys(data[0]) : []
18
+ const filters = (getApplicableFilters(config.dashboard, rowNumber) || []).filter(filter =>
19
+ dataColumns.includes(filter.columnName)
20
+ )
21
+
22
+ const newConfig = _.cloneDeep(visualizationConfig)
23
+
24
+ if (filters.length) {
25
+ _.set(newConfig, 'footnotes.data', filterData(filters, data))
26
+ } else {
27
+ _.set(newConfig, 'footnotes.data', data)
28
+ }
29
+
30
+ return newConfig
31
+ }
32
+
33
+ export const getVizConfig = (
34
+ visualizationKey: string,
35
+ rowNumber: number,
36
+ config: MultiDashboardConfig,
37
+ data: Object,
38
+ filteredData?: Object,
39
+ filteredDataOverride?: Object[],
40
+ multiVizColumn?: string
41
+ ): AnyVisualization => {
42
+ if (rowNumber === undefined) return {} as AnyVisualization
43
+ const visualizationConfig = _.cloneDeep(config.visualizations[visualizationKey])
44
+ const rowData = config.rows[rowNumber]
45
+ if (visualizationConfig.footnotes?.dataKey) {
46
+ _.set(visualizationConfig, 'footnotes.data', config.datasets[visualizationConfig.footnotes.dataKey]?.data)
47
+ }
48
+ if (rowData?.dataKey) {
49
+ // data configured on the row
50
+ Object.assign(visualizationConfig, _.pick(rowData, ['dataKey', 'dataDescription', 'formattedData', 'data']))
51
+ }
52
+
53
+ if (visualizationConfig.table && config.dashboard.sharedFilters.length) {
54
+ // Download CSV button needs to know to include shared filter columns
55
+ const sharedFilterColumns = config.dashboard.sharedFilters.reduce((acc, filter) => {
56
+ if (!filter.usedBy?.length || filter.usedBy?.includes(visualizationKey)) {
57
+ const apiFilter = filter.apiFilter
58
+ const colName = apiFilter?.textSelector || apiFilter?.valueSelector || filter.columnName
59
+ acc.push(colName)
60
+ const subGrouping =
61
+ apiFilter?.subgroupTextSelector || apiFilter?.subgroupValueSelector || filter.subGrouping?.columnName
62
+ if (subGrouping) {
63
+ acc.push(subGrouping)
64
+ }
65
+ }
66
+ return acc
67
+ }, [])
68
+ visualizationConfig.table.sharedFilterColumns = sharedFilterColumns
69
+ }
70
+
71
+ if (visualizationConfig.formattedData) visualizationConfig.originalFormattedData = visualizationConfig.formattedData
72
+ const filteredVizData = filteredData?.[rowNumber] ?? filteredData?.[visualizationKey]
73
+
74
+ if (filteredVizData) {
75
+ visualizationConfig.data = filteredVizData || []
76
+ if (visualizationConfig.formattedData) {
77
+ visualizationConfig.formattedData = visualizationConfig.data
78
+ }
79
+ } else {
80
+ const dataKey = visualizationConfig.dataKey || 'backwards-compatibility'
81
+ visualizationConfig.data = data[dataKey] || []
82
+ if (visualizationConfig.formattedData) {
83
+ visualizationConfig.formattedData =
84
+ transform.developerStandardize(visualizationConfig.data, visualizationConfig.dataDescription) ||
85
+ visualizationConfig.data
86
+ }
87
+ }
88
+
89
+ if (filteredDataOverride) {
90
+ visualizationConfig.data = filteredDataOverride
91
+ if (visualizationConfig.formattedData) {
92
+ visualizationConfig.formattedData = filteredDataOverride
93
+ }
94
+ }
95
+ if (visualizationConfig.footnotes) {
96
+ const visConfigWithFootnotes = getFootnotesVizConfig(visualizationConfig, rowNumber, config)
97
+ if (multiVizColumn && filteredDataOverride) {
98
+ const vizCategory = filteredDataOverride[0][multiVizColumn]
99
+ // the multiViz filtering filtering is applied after the dashboard filters
100
+
101
+ const categoryFootnote = visConfigWithFootnotes.footnotes.data.filter(d => d[multiVizColumn] === vizCategory)
102
+ _.set(visConfigWithFootnotes, 'footnotes.data', categoryFootnote)
103
+ }
104
+ return visConfigWithFootnotes
105
+ }
106
+
107
+ return visualizationConfig as AnyVisualization
108
+ }
@@ -6,6 +6,5 @@ export const getVizRowColumnLocator = (rows: ConfigRow[]): Record<string, { row:
6
6
  curr.columns?.forEach((column, columnIndex) => {
7
7
  if (column.widget !== undefined) acc[column.widget] = { row: index, column: columnIndex }
8
8
  })
9
- if (curr.footnotesId) acc[curr.footnotesId] = { row: index, column: 0 }
10
9
  return acc
11
10
  }, {})
@@ -31,8 +31,11 @@ type GetDatasetKeysParams = Pick<DashboardConfig, 'visualizations' | 'datasets'
31
31
  export const getDatasetKeys = ({ visualizations, datasets, rows }: GetDatasetKeysParams): string[] => {
32
32
  const vizDataKeys = Object.values(visualizations).map(viz => viz.dataKey)
33
33
  const rowDataKeys = rows.map(row => row.dataKey)
34
+ const footnoteDataKeys = Object.values(visualizations)
35
+ .map(viz => viz.footnotes?.dataKey)
36
+ .filter(Boolean)
34
37
  // ensure to only load datasets for the specific dashboard tab.
35
- const datasetsUsedByDashboard = _.uniq([...vizDataKeys, ...rowDataKeys])
38
+ const datasetsUsedByDashboard = _.uniq([...vizDataKeys, ...rowDataKeys, ...footnoteDataKeys])
36
39
  return Object.keys(datasets).filter(datasetKey => datasetsUsedByDashboard.includes(datasetKey))
37
40
  }
38
41
 
@@ -111,17 +114,11 @@ export const filterUsedByDataUrl = (
111
114
  ) => {
112
115
  if (!filter.usedBy || !filter.usedBy.length) return true
113
116
  const vizUsingFilters = filter.usedBy?.map(vizOrRowKey => visualizations[vizOrRowKey] || rows[vizOrRowKey])
114
- // push any footnotes which are using the filter also
115
- const vizRowLookup = getVizRowColumnLocator(rows) // ensure we have the footnotesId in the rows
116
- filter.usedBy?.forEach(vizOrRowKey => {
117
- const vizLookup = vizRowLookup[vizOrRowKey] // if found vizOrRowKey is a viz key
118
- const row = rows[vizLookup?.row || vizOrRowKey]
119
- if (row?.footnotesId) {
120
- // if the widget is using the filter and the widget is on this row and the row has a footnoteId
121
- // then the footnote's endpoint should be filtered by the filter
122
- vizUsingFilters.push(visualizations[row.footnotesId])
123
- }
124
- })
125
117
 
126
- return vizUsingFilters?.some(viz => viz?.dataKey === datasetKey)
118
+ return vizUsingFilters?.some(viz => {
119
+ const usedByViz = viz?.dataKey === datasetKey
120
+ // datasetKey might be a key to a dynamic footnotes URL
121
+ const usedByVizFootnote = viz?.footnotes?.dataKey === datasetKey
122
+ return usedByViz || usedByVizFootnote
123
+ })
127
124
  }
@@ -6,7 +6,6 @@ import { AnyVisualization } from '@cdc/core/types/Visualization'
6
6
  import Footnotes from '@cdc/core/types/Footnotes'
7
7
  import { SharedFilter } from '../types/SharedFilter'
8
8
 
9
- type ADD_FOOTNOTE = Action<'ADD_FOOTNOTE', { id: string; rowIndex: number; config: Footnotes }>
10
9
  type ADD_VISUALIZATION = Action<'ADD_VISUALIZATION', { rowIdx: number; colIdx: number; newViz: AnyVisualization }>
11
10
  type APPLY_CONFIG = Action<'APPLY_CONFIG', [Config, Object?]>
12
11
  type DELETE_WIDGET = Action<'DELETE_WIDGET', { uid: string }>
@@ -36,7 +35,6 @@ type UPDATE_ROW = Action<'UPDATE_ROW', { rowIndex: number; rowData: Partial<Conf
36
35
  type UPDATE_TOGGLE_NAME = Action<'UPDATE_TOGGLE_NAME', { rowIndex: number; columnIndex: number; toggleName: string }>
37
36
 
38
37
  type DashboardActions =
39
- | ADD_FOOTNOTE
40
38
  | ADD_VISUALIZATION
41
39
  | APPLY_CONFIG
42
40
  | ADD_NEW_DASHBOARD
@@ -39,17 +39,6 @@ export type DashboardState = {
39
39
 
40
40
  const reducer = (state: DashboardState, action: DashboardActions): DashboardState => {
41
41
  switch (action.type) {
42
- case 'ADD_FOOTNOTE': {
43
- const { id, rowIndex, config } = action.payload
44
- const newRows = state.config.rows.map((row, i) => (i === rowIndex ? { ...row, footnotesId: id } : row))
45
- return {
46
- ...state,
47
- config: saveMultiChanges(
48
- { ...state.config, rows: newRows, visualizations: { ...state.config.visualizations, [id]: config } },
49
- state.config.activeDashboard
50
- )
51
- }
52
- }
53
42
  case 'ADD_NEW_DASHBOARD': {
54
43
  const currentMultiDashboards = state.config.multiDashboards
55
44
  const label = 'New Dashboard ' + (currentMultiDashboards.length + 1)
@@ -16,5 +16,4 @@ export type ConfigRow = {
16
16
  toggle?: boolean
17
17
  equalHeight?: boolean
18
18
  multiVizColumn?: string
19
- footnotesId?: string // id for the footnotes in the vizConfig section
20
19
  } & ConfigureData
@@ -1,4 +1,4 @@
1
- import { DataSet } from './DataSet'
1
+ import { DataSet } from '@cdc/core/types/DataSet'
2
2
  import { SharedFilter } from './SharedFilter'
3
3
 
4
4
  export type Dashboard = {
@@ -1,6 +1,6 @@
1
1
  import { Series } from '@cdc/core/types/Series'
2
2
  import { Runtime } from '@cdc/core/types/Runtime'
3
- import { DataSet } from './DataSet'
3
+ import { DataSet } from '@cdc/core/types/DataSet'
4
4
  import { ConfigRow } from './ConfigRow'
5
5
  import { AnyVisualization } from '@cdc/core/types/Visualization'
6
6
  import { Table } from '@cdc/core/types/Table'
@@ -1,12 +0,0 @@
1
- import { ConfigureData } from '@cdc/core/types/ConfigureData'
2
-
3
- export type DataSet = ConfigureData & {
4
- dataFileFormat?: string
5
- dataFileName?: string
6
- dataFileSize?: number
7
- preview?: boolean
8
- dataUrl: string
9
- runtimeDataUrl: string
10
- dataFileSourceType: string
11
- loadQueryParam?: string // fetched from the browser's query string and appended to the dataUrl on load.
12
- }