@cdc/core 4.24.4 → 4.24.7

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 (80) hide show
  1. package/components/AdvancedEditor/AdvancedEditor.tsx +93 -0
  2. package/components/AdvancedEditor/advanced-editor-styles.css +3 -0
  3. package/components/AdvancedEditor/index.ts +1 -0
  4. package/components/DataTable/DataTable.tsx +33 -15
  5. package/components/DataTable/DataTableStandAlone.tsx +30 -4
  6. package/components/DataTable/components/ChartHeader.tsx +3 -2
  7. package/components/DataTable/components/DataTableEditorPanel.tsx +23 -6
  8. package/components/DataTable/components/ExpandCollapse.tsx +1 -1
  9. package/components/DataTable/helpers/chartCellMatrix.tsx +5 -10
  10. package/components/DataTable/helpers/getChartCellValue.ts +26 -2
  11. package/components/DataTable/helpers/getDataSeriesColumns.ts +40 -16
  12. package/components/DataTable/helpers/getRowType.ts +6 -0
  13. package/components/DataTable/helpers/getSeriesName.ts +2 -1
  14. package/components/DataTable/helpers/{customColumns.ts → removeNullColumns.ts} +3 -3
  15. package/components/DataTable/types/TableConfig.ts +12 -22
  16. package/components/EditorPanel/ColumnsEditor.tsx +278 -262
  17. package/components/EditorPanel/DataTableEditor.tsx +159 -60
  18. package/components/EditorPanel/FieldSetWrapper.tsx +51 -0
  19. package/components/EditorPanel/FootnotesEditor.tsx +77 -0
  20. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +227 -0
  21. package/components/EditorPanel/VizFilterEditor/components/FilterOrder.tsx +54 -0
  22. package/components/EditorPanel/VizFilterEditor/index.ts +1 -0
  23. package/components/EditorWrapper/EditorWrapper.tsx +47 -0
  24. package/components/EditorWrapper/editor-wrapper.style.css +14 -0
  25. package/components/EditorWrapper/index.ts +1 -0
  26. package/components/{Filters.jsx → Filters.tsx} +102 -70
  27. package/components/Footnotes/Footnotes.tsx +25 -0
  28. package/components/Footnotes/FootnotesStandAlone.tsx +45 -0
  29. package/components/Footnotes/footnotes.css +5 -0
  30. package/components/Footnotes/index.ts +1 -0
  31. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +8 -4
  32. package/components/Layout/components/Visualization/index.tsx +14 -5
  33. package/components/MediaControls.jsx +1 -1
  34. package/components/MultiSelect/MultiSelect.tsx +36 -9
  35. package/components/MultiSelect/multiselect.styles.css +0 -3
  36. package/components/_stories/DataTable.stories.tsx +1 -2
  37. package/components/_stories/EditorPanel.stories.tsx +1 -0
  38. package/components/_stories/Footnotes.stories.tsx +17 -0
  39. package/components/inputs/InputSelect.tsx +17 -6
  40. package/components/ui/Icon.tsx +1 -2
  41. package/helpers/DataTransform.ts +9 -32
  42. package/helpers/addValuesToFilters.ts +56 -0
  43. package/helpers/cove/accessibility.ts +1 -0
  44. package/helpers/cove/fontSettings.ts +2 -0
  45. package/helpers/coveUpdateWorker.ts +11 -2
  46. package/helpers/filterVizData.ts +30 -0
  47. package/helpers/footnoteSymbols.ts +11 -0
  48. package/helpers/formatConfigBeforeSave.ts +90 -0
  49. package/helpers/gatherQueryParams.ts +14 -7
  50. package/helpers/lineChartHelpers.js +2 -1
  51. package/helpers/pivotData.ts +18 -0
  52. package/helpers/queryStringUtils.ts +29 -0
  53. package/helpers/tests/updateFieldFactory.test.ts +1 -0
  54. package/helpers/updateFieldFactory.ts +1 -1
  55. package/helpers/useDataVizClasses.js +0 -4
  56. package/helpers/ver/4.23.4.ts +27 -0
  57. package/helpers/ver/4.24.5.ts +32 -0
  58. package/helpers/ver/4.24.7.ts +92 -0
  59. package/package.json +6 -4
  60. package/styles/_button-section.scss +6 -1
  61. package/styles/_data-table.scss +0 -1
  62. package/styles/_reset.scss +7 -6
  63. package/styles/base.scss +4 -0
  64. package/styles/v2/themes/_color-definitions.scss +1 -0
  65. package/types/Annotation.ts +46 -0
  66. package/types/Column.ts +1 -0
  67. package/types/ConfigureData.ts +1 -1
  68. package/types/Footnotes.ts +17 -0
  69. package/types/General.ts +5 -0
  70. package/types/Legend.ts +1 -0
  71. package/types/MarkupInclude.ts +26 -0
  72. package/types/Runtime.ts +3 -7
  73. package/types/Series.ts +1 -1
  74. package/types/Table.ts +21 -14
  75. package/types/Visualization.ts +40 -11
  76. package/types/VizFilter.ts +24 -0
  77. package/LICENSE +0 -201
  78. package/components/AdvancedEditor.jsx +0 -74
  79. package/helpers/queryStringUtils.js +0 -26
  80. package/types/BaseVisualizationType.ts +0 -1
@@ -51,13 +51,12 @@ export const CityState: Story = {
51
51
 
52
52
  export const Grouped: Story = {
53
53
  args: {
54
- config: Example_1,
54
+ config: { ...Example_1, table: { ...Example_1.table, groupBy: 'TimeZone' } },
55
55
  dataConfig: Example_1.datasets[datasetKey],
56
56
  rawData: Example_1.datasets[datasetKey].data,
57
57
  runtimeData: Example_1.datasets[datasetKey].data,
58
58
  expandDataTable: true,
59
59
  tableTitle: 'COVE DataTable',
60
- groupBy: 'TimeZone',
61
60
  viewport: 'lg',
62
61
  tabbingId: datasetKey
63
62
  }
@@ -46,6 +46,7 @@ export const Primary: Story = {
46
46
  label: 'Data Table',
47
47
  show: true
48
48
  },
49
+ columns: {},
49
50
  visualizationType: 'Pie'
50
51
  },
51
52
  isDashboard: true
@@ -0,0 +1,17 @@
1
+ import { Meta, StoryObj } from '@storybook/react'
2
+ import Footnotes from '../Footnotes'
3
+
4
+ const meta: Meta<typeof Footnotes> = {
5
+ title: 'Components/Organisms/Footnotes',
6
+ component: Footnotes
7
+ }
8
+
9
+ export default meta
10
+
11
+ type Story = StoryObj<typeof Footnotes>
12
+
13
+ export const Primary: Story = {
14
+ args: {
15
+ footnotes: [{ symbol: '*', text: 'This is a footnote' }, { symbol: '†', text: 'This is another footnote' }, { text: 'This is a third footnote' }]
16
+ }
17
+ }
@@ -3,7 +3,7 @@ import '../../styles/v2/components/input/index.scss'
3
3
  interface InputProps {
4
4
  label?
5
5
  value?
6
- options: string[] | { [key: string]: string }
6
+ options: string[] | Record<string, any> | [any, string][]
7
7
  fieldName
8
8
  section?
9
9
  subsection?
@@ -17,11 +17,22 @@ const InputSelect = ({ label, value, options, fieldName, section = null, subsect
17
17
 
18
18
  if (Array.isArray(options)) {
19
19
  //Handle basic array
20
- optionsJsx = options.map(optionName => (
21
- <option value={optionName} key={optionName}>
22
- {optionName}
23
- </option>
24
- ))
20
+ optionsJsx = options.map(option => {
21
+ if (typeof option === 'string') {
22
+ return (
23
+ <option value={option} key={option}>
24
+ {option}
25
+ </option>
26
+ )
27
+ } else {
28
+ const [value, name] = option
29
+ return (
30
+ <option value={value} key={name}>
31
+ {name}
32
+ </option>
33
+ )
34
+ }
35
+ })
25
36
  } else {
26
37
  //Handle object with value/name pairs
27
38
  optionsJsx = []
@@ -1,4 +1,3 @@
1
- import React from 'react'
2
1
  import PropTypes from 'prop-types'
3
2
 
4
3
  import iconCaretUp from '../../assets/icon-caret-up.svg'
@@ -68,7 +67,7 @@ const iconHash = {
68
67
  plus: iconPlus,
69
68
  minus: iconMinus,
70
69
  'filtered-text': iconText,
71
- 'filter-dropdowns': iconDropdowns,
70
+ dashboardFilters: iconDropdowns,
72
71
  table: iconTable,
73
72
  sankey: iconSankey,
74
73
  rotateLeft: iconRotateLeft,
@@ -134,27 +134,27 @@ export class DataTransform {
134
134
  let standardized: string[] = []
135
135
 
136
136
  data.forEach(row => {
137
- let uniqueKey = row[description.xKey];
137
+ let uniqueKey = row[description.xKey]
138
138
  Object.keys(row).forEach(key => {
139
- if(key !== description.xKey && key !== description.seriesKey && description.valueKeysTallSupport.indexOf(key) === -1 && (!description.ignoredKeys || description.ignoredKeys.indexOf(key) === -1)){
140
- uniqueKey += "|" + row[key];
139
+ if (key !== description.xKey && key !== description.seriesKey && description.valueKeysTallSupport.indexOf(key) === -1 && (!description.ignoredKeys || description.ignoredKeys.indexOf(key) === -1)) {
140
+ uniqueKey += '|' + row[key]
141
141
  }
142
142
  })
143
143
 
144
- if(!standardizedMapped[uniqueKey]){
145
- standardizedMapped[uniqueKey] = {[description.xKey]: row[description.xKey]}
144
+ if (!standardizedMapped[uniqueKey]) {
145
+ standardizedMapped[uniqueKey] = { [description.xKey]: row[description.xKey] }
146
146
  }
147
147
  Object.keys(row).forEach(key => {
148
- if(key !== description.xKey && key !== description.seriesKey && description.valueKeysTallSupport.indexOf(key) === -1 && (!description.ignoredKeys || description.ignoredKeys.indexOf(key) === -1)){
149
- standardizedMapped[uniqueKey][key] = row[key];
148
+ if (key !== description.xKey && key !== description.seriesKey && description.valueKeysTallSupport.indexOf(key) === -1 && (!description.ignoredKeys || description.ignoredKeys.indexOf(key) === -1)) {
149
+ standardizedMapped[uniqueKey][key] = row[key]
150
150
  }
151
151
  })
152
152
  description.valueKeysTallSupport.forEach(valueKey => {
153
- standardizedMapped[uniqueKey][row[description.seriesKey] + '-' + valueKey] = row[valueKey];
153
+ standardizedMapped[uniqueKey][row[description.seriesKey] + '-' + valueKey] = row[valueKey]
154
154
  })
155
155
  })
156
156
 
157
- standardized = Object.keys(standardizedMapped).map(key => standardizedMapped[key]);
157
+ standardized = Object.keys(standardizedMapped).map(key => standardizedMapped[key])
158
158
 
159
159
  return standardized
160
160
  } else if (description.valueKeys !== undefined) {
@@ -291,29 +291,6 @@ export class DataTransform {
291
291
  return cleanedupData
292
292
  }
293
293
 
294
- applySuppression = (data, suppressedData) => {
295
- if (!suppressedData || suppressedData.length === 0) return data
296
-
297
- // Create a new array to store the result without modifying the original data
298
- const result = data.map(item => {
299
- // Create a new object to store the updated item without modifying the original item
300
- const newItem = { ...item }
301
-
302
- // For each suppressedData item
303
- for (let i = 0; i < suppressedData.length; i++) {
304
- // If the object contains the column and its value matches the suppressed one
305
- if (newItem[suppressedData[i].column] && newItem[suppressedData[i].column] === suppressedData[i].value) {
306
- // Replace the value with the label in the new object
307
- newItem[suppressedData[i].column] = suppressedData[i].label
308
- }
309
- }
310
-
311
- return newItem
312
- })
313
-
314
- return result
315
- }
316
-
317
294
  // clean out %, $, commas from numbers when needing to do sorting!
318
295
  cleanDataPoint(data, testing = false) {
319
296
  if (testing) console.log('clean', data)
@@ -0,0 +1,56 @@
1
+ import _ from 'lodash'
2
+ import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
3
+
4
+ type Filter = {
5
+ columnName: string
6
+ values: string[]
7
+ filterStyle?: string
8
+ active?: string | string[]
9
+ }
10
+
11
+ // Gets filter values from dataset
12
+ const generateValuesForFilter = (columnName, data: any[] | Record<string, any[]>) => {
13
+ const values: string[] = []
14
+
15
+ if (Array.isArray(data)) {
16
+ data.forEach(row => {
17
+ const value = row[columnName]
18
+ if (!values.includes(value)) {
19
+ values.push(value)
20
+ }
21
+ })
22
+ } else {
23
+ // data is a dataset this loops through ALL datasets to find matching values
24
+ // not sure if this is desired behavior
25
+ Object.values(data).forEach((rows: any[]) => {
26
+ rows.forEach(row => {
27
+ const value = row[columnName]
28
+ if (!values.includes(value)) {
29
+ values.push(value)
30
+ }
31
+ })
32
+ })
33
+ }
34
+
35
+ return values
36
+ }
37
+
38
+ export const addValuesToFilters = <T>(filters: Filter[], data: any[] | Record<string, any[]>): Array<T> => {
39
+ return filters?.map(filter => {
40
+ const filterCopy = _.cloneDeep(filter)
41
+
42
+ const filterValues = generateValuesForFilter(filter.columnName, data)
43
+ filterCopy.values = filterValues
44
+ if (filterValues.length > 0) {
45
+ const defaultValues = filterCopy.filterStyle === 'multi-select' ? filterCopy.values : filterCopy.values[0]
46
+
47
+ const queryStringFilterValue = getQueryStringFilterValue(filterCopy)
48
+ if (queryStringFilterValue) {
49
+ filterCopy.active = queryStringFilterValue
50
+ } else {
51
+ filterCopy.active = filterCopy.active || defaultValues
52
+ }
53
+ }
54
+ return filterCopy
55
+ }) as Array<T>
56
+ }
@@ -9,6 +9,7 @@ import chroma from 'chroma-js'
9
9
  export const WCAG_CONTRAST_RATIO = 4.5
10
10
 
11
11
  export const getContrastColor = (textColor: string, bgColor: string) => {
12
+ if (!bgColor) return
12
13
  if (chroma.contrast(textColor, bgColor) < WCAG_CONTRAST_RATIO) {
13
14
  switch (textColor) {
14
15
  case '#FFF':
@@ -0,0 +1,2 @@
1
+ // TODO: handle with css
2
+ export const fontSizes = { small: 16, medium: 18, large: 20 }
@@ -1,13 +1,22 @@
1
1
  // If config key names or position in the config have been changed with a version change,
2
2
  // process those config entries and format old values into new
3
+ import update_4_24_4 from './ver/4.23.4'
3
4
  import update_4_24_3 from './ver/4.24.3'
5
+ import update_4_24_5 from './ver/4.24.5'
6
+ import update_4_24_7 from './ver/4.24.7'
4
7
 
5
- // 4.23.6 ------------------------------------------------------
6
8
  export const coveUpdateWorker = config => {
9
+ if (config.multiDashboards) {
10
+ config.multiDashboards.forEach((dashboard, index) => {
11
+ config.multiDashboards[index] = coveUpdateWorker(dashboard)
12
+ })
13
+ }
7
14
  let genConfig = config
8
15
 
9
- // v4.24.3
10
16
  genConfig = update_4_24_3(genConfig)
17
+ genConfig = update_4_24_4(genConfig)
18
+ genConfig = update_4_24_5(genConfig)
19
+ genConfig = update_4_24_7(genConfig)
11
20
 
12
21
  return genConfig
13
22
  }
@@ -0,0 +1,30 @@
1
+ export const filterVizData = (filters, data) => {
2
+ if (!data) {
3
+ console.warn('COVE: No data to filter')
4
+ return []
5
+ }
6
+
7
+ if (!filters) return data
8
+ const filteredData: any[] = []
9
+
10
+ data?.forEach(row => {
11
+ let add = true
12
+ filters
13
+ .filter(filter => filter.type !== 'url')
14
+ .forEach(filter => {
15
+ const value = row[filter.columnName]
16
+ if (filter.active === undefined) return
17
+ if (Array.isArray(filter.active)) {
18
+ if (!filter.active.includes(value)) {
19
+ add = false
20
+ }
21
+ } else if (value != filter.active) {
22
+ add = false
23
+ }
24
+ })
25
+
26
+ if (add) filteredData.push(row)
27
+ })
28
+
29
+ return filteredData
30
+ }
@@ -0,0 +1,11 @@
1
+ import _ from 'lodash'
2
+
3
+ const symbols = [
4
+ ['*', 'Asterisk'],
5
+ ['†', 'Dagger'],
6
+ ['§', 'Section Symbol'],
7
+ ['¶', 'Paragraph Symbol']
8
+ ]
9
+
10
+ export const footnotesSymbols = symbols.concat(symbols.map(([symbol, name]) => [symbol + symbol, 'Double ' + name]))
11
+ export const adjustedSymbols = _.fromPairs(_.map(footnotesSymbols, ([symbol, name]) => [name, symbol]))
@@ -0,0 +1,90 @@
1
+ import Footnotes from '@cdc/core/types/Footnotes'
2
+ import { Visualization } from '@cdc/core/types/Visualization'
3
+ import { DashboardConfig } from '@cdc/dashboard/src/types/DashboardConfig'
4
+ import _ from 'lodash'
5
+
6
+ const cleanDashboardFootnotes = (config: DashboardConfig) => {
7
+ // strip any blank footnote visualizations
8
+ const footnoteIds: string[] = []
9
+
10
+ if (config.rows) {
11
+ config.rows.forEach(row => {
12
+ if (row.footnotesId) {
13
+ const { dataKey, staticFootnotes } = config.visualizations[row.footnotesId] as Footnotes
14
+ if (!dataKey && !staticFootnotes?.length) {
15
+ delete config.visualizations[row.footnotesId]
16
+ } else {
17
+ footnoteIds.push(row.footnotesId)
18
+ }
19
+ }
20
+ })
21
+ }
22
+
23
+ if (config.visualizations) {
24
+ Object.keys(config.visualizations).forEach(vizKey => {
25
+ const viz: Visualization = config.visualizations[vizKey]
26
+ if (viz.type === 'footnotes' && !footnoteIds.includes(vizKey)) {
27
+ // if footnote isn't being used by any rows, remove it
28
+ delete config.visualizations[vizKey]
29
+ }
30
+ })
31
+ }
32
+ }
33
+
34
+ const cleanDashboardData = (config: DashboardConfig) => {
35
+ if (config.datasets) {
36
+ Object.keys(config.datasets).forEach(datasetKey => {
37
+ delete config.datasets[datasetKey].formattedData
38
+ if (config.datasets[datasetKey].dataUrl) {
39
+ delete config.datasets[datasetKey].data
40
+ }
41
+ })
42
+ }
43
+ if (config.visualizations) {
44
+ Object.keys(config.visualizations).forEach(vizKey => {
45
+ config.visualizations[vizKey] = _.omit(config.visualizations[vizKey], ['runtime', 'formattedData', 'data'])
46
+ })
47
+ }
48
+ if (config.rows) {
49
+ config.rows.forEach((row, i) => {
50
+ if (row.dataKey) {
51
+ config.rows[i] = _.omit(row, ['data', 'formattedData'])
52
+ }
53
+ })
54
+ }
55
+ }
56
+
57
+ const cleanSharedFilters = (config: DashboardConfig) => {
58
+ if (config.dashboard?.sharedFilters) {
59
+ config.dashboard.sharedFilters.forEach((filter, index) => {
60
+ delete config.dashboard.sharedFilters[index].active
61
+ if (filter.type === 'urlfilter') {
62
+ delete config.dashboard.sharedFilters[index].values
63
+ }
64
+ })
65
+ }
66
+ }
67
+
68
+ export const formatConfigBeforeSave = configToStrip => {
69
+ let strippedConfig = _.cloneDeep(configToStrip)
70
+ if (strippedConfig.type === 'dashboard') {
71
+ if (strippedConfig.multiDashboards) {
72
+ strippedConfig.multiDashboards.forEach((multiDashboard, i) => {
73
+ cleanDashboardData(strippedConfig.multiDashboards[i])
74
+ cleanSharedFilters(strippedConfig.multiDashboards[i])
75
+ cleanDashboardFootnotes(strippedConfig.multiDashboards[i])
76
+ })
77
+ }
78
+ cleanDashboardData(strippedConfig)
79
+ cleanSharedFilters(strippedConfig)
80
+ cleanDashboardFootnotes(strippedConfig)
81
+ } else {
82
+ delete strippedConfig.runtime
83
+ delete strippedConfig.formattedData
84
+ if (strippedConfig.dataUrl) {
85
+ delete strippedConfig.data
86
+ }
87
+ }
88
+
89
+ return strippedConfig
90
+ }
@@ -1,7 +1,14 @@
1
- export const gatherQueryParams = (params: {key: string, value: string}[]) => {
2
- return params.map(({key, value}, i) => {
3
- const leadingCharacter = i === 0 ? '?' : '&'
4
- return leadingCharacter + key + '=' + value
5
- })
6
- .join('')
7
- }
1
+ import _ from 'lodash'
2
+
3
+ export const gatherQueryParams = (baseEndpoint: string, params: { key: string; value: string }[]) => {
4
+ const baseEndpointHasQueryParams = baseEndpoint.includes('?')
5
+ return params
6
+ .filter(({ value }) => value !== '')
7
+ .map(({ key, value }, i) => {
8
+ const leadingCharacter = i === 0 && !baseEndpointHasQueryParams ? '?' : '&'
9
+ const isStatementParam = key.match(/\$.*/)
10
+ if (!_.isNaN(parseInt(value)) || isStatementParam) return leadingCharacter + key + '=' + value
11
+ return leadingCharacter + key + '=' + `"${value}"`
12
+ })
13
+ .join('')
14
+ }
@@ -3,5 +3,6 @@ export const approvedCurveTypes = {
3
3
  Cardinal: 'curveCardinal',
4
4
  Natural: 'curveNatural',
5
5
  'Monotone X': 'curveMonotoneX',
6
- Step: 'curveStep'
6
+ Step: 'curveStep',
7
+ 'Curve Basis': 'curveBasis'
7
8
  }
@@ -0,0 +1,18 @@
1
+ import _ from 'lodash'
2
+
3
+ /** columnName is the column you'd like to select data values from to show as column headers.
4
+ * Pivot is the value column who's data you'd like to show under those respective columns*/
5
+ export const pivotData = (data: Record<string, any>[], columnName: string, pivot: string) => {
6
+ const grouped = _.groupBy(data, val => val[columnName])
7
+ const newData = []
8
+ for (const key in grouped) {
9
+ const group = grouped[key]
10
+ group.forEach((val, index) => {
11
+ const row = newData[index] || {}
12
+ row[key] = val[pivot]
13
+ const toAdd = _.omit(val, [columnName, pivot])
14
+ newData[index] = { ...toAdd, ...row }
15
+ })
16
+ }
17
+ return newData
18
+ }
@@ -0,0 +1,29 @@
1
+ export function getQueryStringFilterValue(filter) {
2
+ const urlParams = new URLSearchParams(window.location.search)
3
+ if (filter.setByQueryParameter) {
4
+ // Only check the query string if the filter is supposed to be set by QS param
5
+ const filterValue = urlParams.get(filter.setByQueryParameter)
6
+ if (filterValue && filter.values) {
7
+ for (let i = 0; i < filter.values.length; i++) {
8
+ if (filter.values[i] && filter.values[i].toLowerCase() === filterValue.toLowerCase()) {
9
+ return filter.values[i]
10
+ }
11
+ }
12
+ }
13
+ }
14
+ }
15
+
16
+ export function getQueryParams() {
17
+ const queryParams = {}
18
+ for (const [key, value] of Array.from(new URLSearchParams(window.location.search).entries())) {
19
+ queryParams[key] = value
20
+ }
21
+ return queryParams
22
+ }
23
+
24
+ export function updateQueryString(queryParams) {
25
+ const updateUrl = `${window.location.origin}${window.location.pathname}?${Object.keys(queryParams)
26
+ .map(queryParam => `${queryParam}=${encodeURIComponent(queryParams[queryParam])}`)
27
+ .join('&')}`
28
+ window.history.pushState({ path: updateUrl }, '', updateUrl)
29
+ }
@@ -1,4 +1,5 @@
1
1
  import { updateFieldFactory } from '../updateFieldFactory'
2
+ import { expect, describe, it } from 'vitest'
2
3
 
3
4
  describe('updateFieldFactory', () => {
4
5
  it('should update the top level field when section and subsection are null', () => {
@@ -1,7 +1,7 @@
1
1
  import { UpdateFieldFunc } from '../types/UpdateFieldFunc'
2
2
 
3
3
  export const updateFieldFactory =
4
- (config, updateConfig, legacy = false): UpdateFieldFunc<any> =>
4
+ <T>(config, updateConfig, legacy = false): UpdateFieldFunc<T> =>
5
5
  (section, subsection, fieldName, newValue) => {
6
6
  // Top level
7
7
  if (null === section && null === subsection) {
@@ -18,10 +18,6 @@ export default function useDataVizClasses(config, viewport = null) {
18
18
  if (title && showTitle) contentClasses.push('component--has-title')
19
19
  }
20
20
 
21
- if (config.type === 'markup-include') {
22
- contentClasses = contentClasses.filter(item => item !== 'cove-component__content')
23
- }
24
-
25
21
  config.showTitle && contentClasses.push('component--has-title')
26
22
  config.title && config.visualizationType !== 'chart' && config.visualizationType !== 'Spark Line' && contentClasses.push('component--has-title')
27
23
  config.subtext && innerContainerClasses.push('component--has-subtext')
@@ -0,0 +1,27 @@
1
+ import _ from 'lodash'
2
+
3
+ const addFiltersToTables = config => {
4
+ if (config.type === 'dashboard') {
5
+ Object.keys(config.visualizations).forEach(vizKey => {
6
+ const viz = config.visualizations[vizKey]
7
+ if (viz.type === 'table') {
8
+ if (!viz.filters) {
9
+ viz.filters = []
10
+ viz.filterBehavior = 'Filter Change'
11
+ }
12
+ }
13
+ })
14
+ }
15
+ }
16
+
17
+ const update_4_24_4 = config => {
18
+ const ver = '4.24.4'
19
+
20
+ const newConfig = _.cloneDeep(config)
21
+ addFiltersToTables(newConfig)
22
+
23
+ newConfig.version = ver
24
+ return newConfig
25
+ }
26
+
27
+ export default update_4_24_4
@@ -0,0 +1,32 @@
1
+ import _ from 'lodash'
2
+
3
+ const migrateMarkupInclude = newConfig => {
4
+ if (newConfig.type === 'markup-include') {
5
+ if (!newConfig.contentEditor) {
6
+ newConfig.contentEditor = {
7
+ title: newConfig.title,
8
+ showHeader: newConfig.showHeader,
9
+ srcUrl: newConfig.srcUrl
10
+ }
11
+ delete newConfig.title
12
+ delete newConfig.showHeader
13
+ delete newConfig.srcUrl
14
+ }
15
+ if (!newConfig.visual) {
16
+ newConfig.visual = {}
17
+ }
18
+ }
19
+ }
20
+
21
+ const update_4_24_4 = config => {
22
+ const ver = '4.24.4'
23
+
24
+ const newConfig = _.cloneDeep(config)
25
+
26
+ migrateMarkupInclude(newConfig)
27
+
28
+ newConfig.version = ver
29
+ return newConfig
30
+ }
31
+
32
+ export default update_4_24_4
@@ -0,0 +1,92 @@
1
+ import _ from 'lodash'
2
+ import { DashboardFilters } from '@cdc/dashboard/src/types/DashboardFilters'
3
+ import { MultiDashboardConfig } from '@cdc/dashboard/src/types/MultiDashboard'
4
+ import { AnyVisualization } from '../../types/Visualization'
5
+
6
+ export const dashboardFiltersMigrate = config => {
7
+ if (!config.dashboard) return config
8
+ const dashboardConfig = config as MultiDashboardConfig
9
+ const newVisualizations = {}
10
+ // autoload was removed from APIFilter type
11
+ const newSharedFilters = (dashboardConfig.dashboard.sharedFilters || []).map(sf => {
12
+ if (sf.apiFilter?.autoLoad !== undefined) {
13
+ delete sf.apiFilter.autoLoad
14
+ }
15
+ if (sf.apiFilter?.defaultValue !== undefined) {
16
+ delete sf.apiFilter.defaultValue
17
+ }
18
+ return sf
19
+ })
20
+ config.dashboard.sharedFilters = newSharedFilters
21
+
22
+ Object.keys(dashboardConfig.visualizations).forEach(vizKey => {
23
+ const viz = dashboardConfig.visualizations[vizKey] as DashboardFilters
24
+ // hide was removed from visualizations
25
+ if (viz.hide !== undefined) {
26
+ viz.sharedFilterIndexes = newSharedFilters.map((_sf, i) => i).filter(i => !viz.hide.includes(i))
27
+ viz.type = 'dashboardFilters'
28
+ if (viz.autoLoad) {
29
+ viz.filterBehavior = 'Filter Change'
30
+ } else {
31
+ viz.filterBehavior = 'Apply Button'
32
+ }
33
+
34
+ delete viz.hide
35
+ }
36
+ // 'filter-dropdowns' was renamed to 'dashboardFilters' for clarity
37
+ if (viz.type === 'filter-dropdowns') viz.type = 'dashboardFilters'
38
+ if (viz.visualizationType === 'filter-dropdowns') viz.visualizationType = 'dashboardFilters'
39
+ newVisualizations[vizKey] = viz
40
+ })
41
+
42
+ if (config.dashboard.sharedFilters.length && !Object.values(newVisualizations).find((v: AnyVisualization) => v.type === 'dashboardFilters')) {
43
+ const newViz = {
44
+ type: 'dashboardFilters',
45
+ visualizationType: 'dashboardFilters',
46
+ sharedFilterIndexes: config.dashboard.sharedFilters.map((_sf, i) => i),
47
+ filterBehavior: config.filterBehavior || 'Filter Change'
48
+ }
49
+ const key = 'legacySharedFilters'
50
+ newVisualizations[key] = newViz
51
+ const newRow = {
52
+ columns: [
53
+ {
54
+ width: 12,
55
+ widget: key
56
+ }
57
+ ]
58
+ }
59
+ config.rows = [newRow, ...config.rows]
60
+ }
61
+ // if there's no dashboardFilters visualization but there are sharedFilters create a visualization and update rows.
62
+
63
+ config.visualizations = newVisualizations
64
+ }
65
+
66
+ const mapUpdates = newConfig => {
67
+ // When switching between old version of equal number, and the revised equal number opt in, roundToPlace needs to be set.
68
+ // There wasn't an initial value set for this, and legends would return NaN if it wasn't set. ie. 0 - NAN instead of 0 - 1
69
+ const equalNumberRoundingPatch = newConfig => {
70
+ if (newConfig.type === 'map') {
71
+ if (newConfig.columns.primary.roundToPlace === undefined) {
72
+ newConfig.columns.primary.roundToPlace = 0
73
+ }
74
+ }
75
+ }
76
+
77
+ equalNumberRoundingPatch(newConfig)
78
+
79
+ return newConfig
80
+ }
81
+
82
+ const update_4_24_7 = config => {
83
+ const ver = '4.24.7'
84
+
85
+ const newConfig = _.cloneDeep(config)
86
+
87
+ mapUpdates(newConfig)
88
+ dashboardFiltersMigrate(newConfig)
89
+ newConfig.version = ver
90
+ return newConfig
91
+ }
92
+ export default update_4_24_7