@cdc/core 4.26.1 → 4.26.3

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 (249) hide show
  1. package/.claude/agents/qa-test-developer.md +126 -0
  2. package/CLAUDE.local.md +67 -0
  3. package/LICENSE +201 -0
  4. package/_stories/Gallery.Charts.stories.tsx +35 -42
  5. package/_stories/Gallery.DataBite.stories.tsx +15 -8
  6. package/_stories/Gallery.Maps.stories.tsx +37 -28
  7. package/_stories/Gallery.WaffleChart.stories.tsx +1 -1
  8. package/_stories/PageART.stories.tsx +5 -4
  9. package/_stories/PageBRFSS.stories.tsx +21 -16
  10. package/_stories/PageCancerRegistries.stories.tsx +15 -15
  11. package/_stories/PageEasternEquineEncephalitis.stories.tsx +33 -19
  12. package/_stories/PageExcessiveAlcoholUse.stories.tsx +148 -143
  13. package/_stories/PageMaternalMortality.stories.tsx +5 -4
  14. package/_stories/PageOralHealth.stories.tsx +15 -10
  15. package/_stories/PageRespiratory.stories.tsx +4 -4
  16. package/_stories/PageSmokingTobacco.stories.tsx +15 -10
  17. package/_stories/PageStateDiabetesProfiles.stories.tsx +15 -10
  18. package/_stories/PageWastewater.stories.tsx +44 -30
  19. package/_stories/VegaImport.stories.tsx +401 -0
  20. package/_stories/vega-fixtures/bars-with-line.json +444 -0
  21. package/_stories/vega-fixtures/bars.json +58 -0
  22. package/_stories/vega-fixtures/combo-bar-rolling-mean.json +88 -0
  23. package/_stories/vega-fixtures/combo.json +68 -0
  24. package/_stories/vega-fixtures/grouped-horizontal-bars.json +83 -0
  25. package/_stories/vega-fixtures/grouped-horizontal-bars2.json +231 -0
  26. package/_stories/vega-fixtures/horizontal-bar.json +427 -0
  27. package/_stories/vega-fixtures/horizontal-bars-with-bad-colors.json +197 -0
  28. package/_stories/vega-fixtures/horizontal-bars2.json +58 -0
  29. package/_stories/vega-fixtures/lines.json +227 -0
  30. package/_stories/vega-fixtures/measles-bars.json +348 -0
  31. package/_stories/vega-fixtures/measles-map.json +11101 -0
  32. package/_stories/vega-fixtures/measles-stacked-bars.json +2147 -0
  33. package/_stories/vega-fixtures/multi-dataset.json +255 -0
  34. package/_stories/vega-fixtures/no-data.json +14 -0
  35. package/_stories/vega-fixtures/pie-chart.json +94 -0
  36. package/_stories/vega-fixtures/repeat-spec.json +47 -0
  37. package/_stories/vega-fixtures/stacked-area.json +222 -0
  38. package/_stories/vega-fixtures/stacked-bar-with-rect.json +3412 -0
  39. package/_stories/vega-fixtures/stacked-bars-with-line.json +364 -0
  40. package/_stories/vega-fixtures/stacked-bars.json +212 -0
  41. package/_stories/vega-fixtures/stacked-horizontal-bars.json +140 -0
  42. package/_stories/vega-fixtures/warning-combo.json +59 -0
  43. package/_stories/vega-fixtures/warning-scatter-and-line.json +1182 -0
  44. package/assets/callout-flag.svg +7 -0
  45. package/assets/icon-chart-area.svg +1 -0
  46. package/assets/icon-chart-radar.svg +23 -0
  47. package/assets/logo2.svg +31 -0
  48. package/components/AdvancedEditor/EmbedEditor.tsx +270 -38
  49. package/components/Alert/components/Alert.styles.css +2 -2
  50. package/components/ComboBox/combobox.styles.css +48 -48
  51. package/components/CustomColorsEditor/CustomColorsEditor.css +53 -53
  52. package/components/CustomColorsEditor/CustomColorsEditor.tsx +3 -10
  53. package/components/DataTable/DataTable.tsx +46 -18
  54. package/components/DataTable/DataTableStandAlone.tsx +1 -0
  55. package/components/DataTable/components/ChartHeader.tsx +21 -12
  56. package/components/DataTable/components/MapHeader.tsx +34 -28
  57. package/components/DataTable/components/SortIcon/sort-icon.css +5 -5
  58. package/components/DataTable/data-table.css +50 -52
  59. package/components/DataTable/helpers/applyCustomOrder.ts +17 -0
  60. package/components/DataTable/helpers/getChartCellValue.ts +10 -7
  61. package/components/DataTable/helpers/getMapDataTableColumnKeys.ts +22 -0
  62. package/components/DataTable/helpers/getSeriesName.ts +6 -0
  63. package/components/DataTable/helpers/mapCellMatrix.tsx +33 -23
  64. package/components/DataTable/helpers/tests/mapCellMatrix.test.ts +33 -0
  65. package/components/DownloadButton.tsx +14 -6
  66. package/components/EditorPanel/ColumnsEditor.tsx +38 -31
  67. package/components/EditorPanel/CustomSortOrder.tsx +94 -0
  68. package/components/EditorPanel/DataTableEditor.tsx +139 -23
  69. package/components/EditorPanel/EditorPanel.styles.css +71 -71
  70. package/components/EditorPanel/EditorPanel.tsx +3 -8
  71. package/components/EditorPanel/EditorPanelDispatch.tsx +4 -4
  72. package/components/EditorPanel/FootnotesEditor.tsx +2 -2
  73. package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +21 -12
  74. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +16 -10
  75. package/components/EditorPanel/VizFilterEditor/components/FilterOrder.tsx +33 -29
  76. package/components/EditorPanel/components/MarkupVariablesEditor.tsx +160 -106
  77. package/components/EditorPanel/components/PanelMarkup.tsx +5 -1
  78. package/{styles/v2/components → components/EditorPanel}/editor.scss +76 -22
  79. package/components/EditorPanel/sections/StyleTreatmentSection.tsx +99 -0
  80. package/components/EditorPanel/sections/VisualSection.tsx +11 -0
  81. package/components/EditorWrapper/editor-wrapper.style.css +1 -1
  82. package/components/Filters/Filters.tsx +3 -5
  83. package/components/Filters/components/Tabs.tsx +19 -7
  84. package/{styles → components/Filters}/filters.scss +3 -3
  85. package/components/Footnotes/FootnotesStandAlone.tsx +4 -2
  86. package/components/HeaderThemeSelector/HeaderThemeSelector.css +61 -5
  87. package/components/Layout/components/Responsive.tsx +14 -6
  88. package/components/Layout/components/Sidebar/components/Sidebar.tsx +1 -1
  89. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +14 -20
  90. package/components/Layout/components/Visualization/index.tsx +50 -38
  91. package/components/Layout/components/Visualization/visualizations.scss +232 -15
  92. package/components/Layout/components/VisualizationContainer.test.tsx +67 -0
  93. package/components/Layout/components/VisualizationContainer.tsx +37 -0
  94. package/components/Layout/components/VisualizationContent.test.tsx +182 -0
  95. package/components/Layout/components/VisualizationContent.tsx +75 -0
  96. package/components/Layout/index.tsx +5 -5
  97. package/components/Layout/styles/editor-utils.scss +3 -3
  98. package/components/Layout/styles/editor.scss +4 -4
  99. package/components/Legend/Legend.Gradient.tsx +7 -1
  100. package/components/Loader/loader.styles.css +2 -2
  101. package/components/Loading.jsx +1 -1
  102. package/components/MediaControls.tsx +10 -3
  103. package/components/MultiSelect/multiselect.styles.css +19 -19
  104. package/components/NestedDropdown/nesteddropdown.styles.css +15 -15
  105. package/components/PaletteSelector/PaletteSelector.css +15 -15
  106. package/components/RichTooltip/richTooltip.css +6 -6
  107. package/components/Table/table.styles.css +2 -2
  108. package/components/Waiting.tsx +1 -1
  109. package/components/_stories/CustomColorsEditor.stories.tsx +37 -0
  110. package/components/_stories/DataTable.stories.tsx +1 -0
  111. package/components/_stories/Filters.stories.tsx +1 -1
  112. package/components/_stories/styles.scss +0 -1
  113. package/components/elements/Button.jsx +1 -1
  114. package/components/elements/Card.jsx +1 -1
  115. package/{styles/v2/components → components/elements}/button.scss +9 -8
  116. package/components/inputs/InputCheckbox.jsx +1 -1
  117. package/components/inputs/InputSelect.tsx +1 -1
  118. package/components/inputs/InputText.jsx +1 -1
  119. package/components/inputs/InputToggle.tsx +1 -1
  120. package/{styles/v2/components/input → components/inputs}/_input-check-radio.scss +2 -2
  121. package/{styles/v2/components/input → components/inputs}/_input-group.scss +3 -3
  122. package/{styles/v2/components/input → components/inputs}/_input-slider.scss +2 -2
  123. package/{styles/v2/components/input → components/inputs}/_input.scss +5 -5
  124. package/{styles/v2/components/input → components/inputs}/index.scss +2 -2
  125. package/{styles → components}/loading.scss +1 -1
  126. package/components/managers/DataDesigner.tsx +1 -1
  127. package/{styles/v2/components → components/managers}/data-designer.scss +6 -7
  128. package/components/ui/Accordion.jsx +1 -1
  129. package/components/ui/Icon.tsx +1 -1
  130. package/components/ui/LoadSpin.jsx +1 -1
  131. package/components/ui/Modal.jsx +1 -1
  132. package/components/ui/Overlay.jsx +1 -1
  133. package/components/ui/Title/index.test.tsx +34 -0
  134. package/components/ui/Title/index.tsx +24 -7
  135. package/components/ui/Title/title.styles.css +119 -25
  136. package/components/ui/Tooltip.tsx +1 -1
  137. package/components/ui/_stories/Title.stories.tsx +1 -1
  138. package/{styles/v2/components → components/ui}/accordion.scss +3 -3
  139. package/components/ui/accordion.styles.css +11 -11
  140. package/{styles/v2/components → components/ui}/modal.scss +2 -2
  141. package/{styles/v2/components → components/ui}/overlay.scss +6 -6
  142. package/{styles/v2/components → components}/ui/tooltip.scss +1 -1
  143. package/{styles → components}/waiting.scss +9 -3
  144. package/data/colorPalettes.ts +18 -5
  145. package/data/mapColorPalettes.ts +10 -0
  146. package/devTemplate/dev.js +285 -0
  147. package/devTemplate/index.html +30 -0
  148. package/devTemplate/preview.html +1503 -0
  149. package/devTemplate/sidebar.css +151 -0
  150. package/dist/cove-main.css +2530 -3901
  151. package/dist/cove-main.css.map +1 -1
  152. package/generateViteConfig.js +111 -2
  153. package/helpers/DataTransform.ts +1 -5
  154. package/helpers/backfillDefaults.ts +35 -0
  155. package/helpers/constants.ts +12 -0
  156. package/helpers/cove/date.ts +64 -3
  157. package/helpers/cove/number.ts +29 -15
  158. package/helpers/cove/string.ts +29 -0
  159. package/helpers/coveUpdateWorker.ts +14 -8
  160. package/helpers/displayDataAsText.ts +1 -1
  161. package/helpers/embed/embedCodeGenerator.ts +80 -0
  162. package/helpers/embed/embedHelper.js +169 -0
  163. package/helpers/embed/filterUtils.ts +121 -0
  164. package/helpers/embed/index.ts +17 -0
  165. package/helpers/embed/urlValidation.ts +119 -0
  166. package/helpers/extractDataAndMetadata.ts +20 -0
  167. package/helpers/fetchRemoteData.ts +14 -8
  168. package/helpers/filterVizData.ts +6 -1
  169. package/helpers/getFileExtension.ts +0 -6
  170. package/helpers/labelHash.ts +9 -0
  171. package/helpers/markupProcessor.ts +56 -38
  172. package/helpers/metrics/types.ts +3 -0
  173. package/helpers/palettes/colorDistributions.ts +1 -1
  174. package/helpers/palettes/utils.ts +12 -12
  175. package/helpers/parseCsvWithQuotes.ts +15 -14
  176. package/helpers/prepareScreenshot.ts +33 -10
  177. package/helpers/testing.ts +44 -0
  178. package/helpers/tests/DataTransform.test.ts +125 -0
  179. package/helpers/tests/abbreviateNumber.test.ts +59 -0
  180. package/helpers/tests/backfillDefaults.test.ts +253 -0
  181. package/helpers/tests/date.test.ts +110 -0
  182. package/helpers/tests/extractDataAndMetadata.test.ts +93 -0
  183. package/helpers/tests/markupProcessor.test.ts +315 -124
  184. package/helpers/tests/number.test.ts +42 -0
  185. package/helpers/tests/prepareScreenshot.test.ts +28 -28
  186. package/helpers/tests/testStandaloneBuild.ts +36 -26
  187. package/helpers/tests/useDataVizClasses.test.ts +66 -0
  188. package/helpers/tests/visualizationWrapperUsage.test.ts +57 -0
  189. package/helpers/useDataVizClasses.ts +13 -7
  190. package/helpers/vegaConfig.ts +1 -1
  191. package/helpers/vegaConfigImport.ts +160 -0
  192. package/helpers/ver/4.24.4.ts +24 -0
  193. package/helpers/ver/4.26.1.ts +1 -1
  194. package/helpers/ver/4.26.2.ts +84 -0
  195. package/helpers/ver/4.26.3.ts +44 -0
  196. package/helpers/ver/4.26.4.ts +31 -0
  197. package/helpers/ver/tests/4.26.1.test.ts +105 -0
  198. package/helpers/ver/tests/4.26.2.test.ts +298 -0
  199. package/helpers/ver/tests/4.26.3.test.ts +168 -0
  200. package/helpers/ver/tests/4.26.4.test.ts +88 -0
  201. package/helpers/ver/tests/coveUpdateWorker.test.ts +57 -0
  202. package/helpers/viewports.ts +2 -0
  203. package/package.json +27 -32
  204. package/styles/_global.scss +7 -7
  205. package/styles/_reset.scss +2 -2
  206. package/styles/{v2/base → base}/_file-selector.scss +4 -4
  207. package/styles/{v2/base → base}/_general.scss +2 -4
  208. package/styles/{v2/base → base}/index.scss +1 -1
  209. package/styles/base.scss +107 -165
  210. package/styles/cove-main.scss +3 -6
  211. package/styles/layout/_component.scss +110 -0
  212. package/styles/{v2/layout → layout}/_data-table.scss +7 -7
  213. package/styles/layout/_wrapper-padding.scss +27 -0
  214. package/styles/{v2/main.scss → main.scss} +3 -1
  215. package/styles/{v2/themes → themes}/_color-definitions.scss +46 -41
  216. package/styles/{_accessibility.scss → utils/_accessibility.scss} +1 -1
  217. package/styles/{v2/utils → utils}/_grid.scss +8 -3
  218. package/styles/{_global-variables.scss → utils/_properties.scss} +133 -112
  219. package/styles/{v2/utils → utils}/index.scss +2 -1
  220. package/types/Annotation.ts +10 -11
  221. package/types/Axis.ts +2 -0
  222. package/types/ComponentStyles.ts +1 -0
  223. package/types/ConfigureData.ts +1 -0
  224. package/types/General.ts +2 -0
  225. package/types/MarkupInclude.ts +1 -0
  226. package/types/MarkupVariable.ts +2 -1
  227. package/types/Palette.ts +22 -0
  228. package/types/Table.ts +9 -0
  229. package/types/Visualization.ts +7 -0
  230. package/_stories/StoryRenderingTests.stories.tsx +0 -164
  231. package/helpers/embedCodeGenerator.ts +0 -109
  232. package/styles/_common-components.css +0 -73
  233. package/styles/_variables.scss +0 -63
  234. package/styles/v2/layout/_component.scss +0 -21
  235. package/styles/v2/utils/_variables.scss +0 -9
  236. package/{styles/v2/components/card.scss → components/elements/card.css} +2 -2
  237. /package/{styles/v2/components → components/ui}/icon.scss +0 -0
  238. /package/{styles/v2/components → components/ui}/loadspin.scss +0 -0
  239. /package/styles/{v2/base → base}/_heading.scss +0 -0
  240. /package/styles/{v2/base → base}/_reset.scss +0 -0
  241. /package/styles/{v2/layout → layout}/_alert.scss +0 -0
  242. /package/styles/{v2/layout → layout}/_progression.scss +0 -0
  243. /package/styles/{v2/layout → layout}/_tooltip.scss +0 -0
  244. /package/styles/{v2/layout → layout}/index.scss +0 -0
  245. /package/styles/{v2/themes → themes}/index.scss +0 -0
  246. /package/styles/{v2/utils → utils}/_align.scss +0 -0
  247. /package/styles/{v2/utils → utils}/_animations.scss +0 -0
  248. /package/styles/{v2/utils → utils}/_breakpoints.scss +0 -0
  249. /package/styles/{v2/utils → utils}/_mixins.scss +0 -0
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Shared Vega-to-COVE conversion helper for stories.
3
+ */
4
+ import {
5
+ convertVegaConfig,
6
+ getVegaConfigType,
7
+ getVegaErrors,
8
+ getVegaWarnings,
9
+ isVegaConfig,
10
+ parseVegaConfig
11
+ } from './vegaConfig'
12
+
13
+ /** Chart-type "button" definitions used to seed new COVE configs. */
14
+ const buttons = [
15
+ {
16
+ id: 1,
17
+ category: 'Charts',
18
+ label: 'Bar',
19
+ type: 'chart',
20
+ subType: 'Bar',
21
+ orientation: 'vertical',
22
+ barThickness: '0.37',
23
+ visualizationSubType: 'regular',
24
+ xAxis: { type: 'categorical', size: 75, maxTickRotation: 45, labelOffset: 0 },
25
+ content: 'Use bars to show comparisons between data categories.'
26
+ },
27
+ {
28
+ id: 3,
29
+ category: 'Charts',
30
+ label: 'Combo Chart',
31
+ type: 'chart',
32
+ subType: 'Combo',
33
+ orientation: 'vertical',
34
+ content: 'Use bars to show comparisons between data categories.'
35
+ },
36
+ {
37
+ id: 4,
38
+ category: 'Charts',
39
+ label: 'Line',
40
+ type: 'chart',
41
+ subType: 'Line',
42
+ orientation: 'vertical',
43
+ content: 'Present one or more data trends over time.'
44
+ },
45
+ {
46
+ id: 6,
47
+ category: 'Charts',
48
+ label: 'Area Chart',
49
+ type: 'chart',
50
+ subType: 'Area Chart',
51
+ orientation: 'vertical',
52
+ content: 'Display an area chart to visualize quantities over time.'
53
+ },
54
+ {
55
+ id: 8,
56
+ category: 'Charts',
57
+ label: 'Scatter Plot',
58
+ type: 'chart',
59
+ subType: 'Scatter Plot',
60
+ orientation: 'vertical',
61
+ content: 'Display a scatter plot to explore relationships between numeric variables.'
62
+ },
63
+ {
64
+ id: 12,
65
+ category: 'Charts',
66
+ label: 'Horizontal Bar (Stacked)',
67
+ type: 'chart',
68
+ subType: 'Bar',
69
+ visualizationSubType: 'stacked',
70
+ orientation: 'horizontal',
71
+ content: 'Use bars to show comparisons between data categories.'
72
+ },
73
+ {
74
+ id: 19,
75
+ category: 'Maps',
76
+ label: 'United States (State- or County-Level)',
77
+ type: 'map',
78
+ subType: 'us',
79
+ content: 'Present a U.S. choropleth map at state or county level.',
80
+ position: 'right'
81
+ }
82
+ ]
83
+
84
+ /** Build a seed COVE config from a button definition. */
85
+ const generateNewConfig = (props: any) => {
86
+ let newConfig: any = {}
87
+ switch (props.category) {
88
+ case 'Charts': {
89
+ const visualizationType = props.subType
90
+ const visualizationSubType = !props.visualizationSubType ? 'regular' : props.visualizationSubType
91
+ newConfig = {
92
+ ...props,
93
+ visualizationType,
94
+ visualizationSubType,
95
+ newViz: true,
96
+ datasets: {}
97
+ }
98
+ break
99
+ }
100
+ case 'Maps': {
101
+ newConfig = { ...props, newViz: true, datasets: {}, type: 'map' }
102
+ newConfig['general'] = { geoType: props.subType, type: props?.generalType }
103
+ break
104
+ }
105
+ }
106
+ return newConfig
107
+ }
108
+
109
+ /**
110
+ * Convert a raw Vega/Vega-Lite config into a COVE config.
111
+ * Returns `null` if the config has errors.
112
+ */
113
+ export const importVegaConfig = async (rawConfig: any): Promise<any | null> => {
114
+ const vegaConfig = await parseVegaConfig(rawConfig)
115
+ const vegaErrors = getVegaErrors(rawConfig, vegaConfig)
116
+ if (vegaErrors.length > 0) {
117
+ console.warn('Vega import errors:', vegaErrors)
118
+ return null
119
+ }
120
+
121
+ const configType = getVegaConfigType(vegaConfig)
122
+ const configSubType = configType === 'Map' ? 'United States (State- or County-Level)' : configType
123
+ const button = buttons.find(b => b.label === configSubType)
124
+ if (!button) {
125
+ console.warn(`No button found for config type "${configSubType}"`)
126
+ return null
127
+ }
128
+
129
+ const coveConfig = generateNewConfig(JSON.parse(JSON.stringify(button)))
130
+ try {
131
+ const warnings = getVegaWarnings(rawConfig, vegaConfig)
132
+ if (warnings.length) {
133
+ console.warn('Vega import warnings:', warnings)
134
+ }
135
+ const result = convertVegaConfig(configType, vegaConfig, coveConfig)
136
+
137
+ // Ensure imported Vega configs use the v1 qualitative palette so CdcChart
138
+ // doesn't apply v2 sequential-blue defaults (which would change the colors).
139
+ if (result && result.type !== 'map') {
140
+ result.general = result.general || {}
141
+ result.general.palette = {
142
+ name: 'qualitative-bold',
143
+ version: '1.0'
144
+ }
145
+ }
146
+
147
+ return result
148
+ } catch (err) {
149
+ console.error('Vega conversion error:', err)
150
+ return null
151
+ }
152
+ }
153
+
154
+ /** Check if a config is a Vega config and convert it; otherwise pass through. */
155
+ export const maybeConvertVega = async (config: any): Promise<any | null> => {
156
+ if (isVegaConfig(config)) {
157
+ return importVegaConfig(config)
158
+ }
159
+ return config
160
+ }
@@ -1,5 +1,28 @@
1
1
  import _ from 'lodash'
2
2
  import cloneConfig from '../cloneConfig'
3
+ import { ConfigRow } from '@cdc/dashboard/src/types/ConfigRow'
4
+
5
+ // Migrates rows from old array-of-arrays format to new { columns: [] } format.
6
+ // Configs saved at exactly 4.24.3 have this format but the 4.24.3 migration
7
+ // never runs for them (versionNeedsUpdate returns false when versions are equal).
8
+ const remapDashboardRows = config => {
9
+ if (config.type === 'dashboard' && config.rows) {
10
+ config.rows = config.rows.map(row => {
11
+ if (row.columns === undefined) {
12
+ const newRow = {} as ConfigRow
13
+ const newColumns = row.map(column => {
14
+ newRow.uuid = column.uuid
15
+ newRow.toggle = column.toggle
16
+ newRow.equalHeight = column.equalHeight
17
+ return _.pick(column, 'equalHeight', 'width', 'hide', 'widget', 'uuid')
18
+ })
19
+ newRow.columns = newColumns
20
+ return newRow
21
+ }
22
+ return row
23
+ })
24
+ }
25
+ }
3
26
 
4
27
  const addFiltersToTables = config => {
5
28
  if (config.type === 'dashboard') {
@@ -19,6 +42,7 @@ const update_4_24_4 = config => {
19
42
  const ver = '4.24.4'
20
43
 
21
44
  const newConfig = cloneConfig(config)
45
+ remapDashboardRows(newConfig)
22
46
  addFiltersToTables(newConfig)
23
47
 
24
48
  newConfig.version = ver
@@ -20,7 +20,7 @@ const removeOldBrushKeys = config => {
20
20
  delete config.brush
21
21
  }
22
22
 
23
- if (config.type === 'dashboard') {
23
+ if (config.type === 'dashboard' && config.visualizations) {
24
24
  Object.values((config as DashboardConfig).visualizations).forEach(visualization => {
25
25
  removeOldBrushKeys(visualization)
26
26
  })
@@ -0,0 +1,84 @@
1
+ import cloneConfig from '../cloneConfig'
2
+ import { DashboardConfig } from '@cdc/dashboard/src/types/DashboardConfig'
3
+
4
+ const migrateAnnotationDimensions = config => {
5
+ if (config.annotations && Array.isArray(config.annotations)) {
6
+ // Calculate chart area height for Y conversion (matches calcInitialHeight)
7
+ const isHorizontal = config.orientation === 'horizontal'
8
+ const chartAreaHeight = isHorizontal
9
+ ? Number(config.heights?.horizontal) || 750 // default horizontal height
10
+ : Number(config.heights?.vertical) || 300 // default vertical height
11
+
12
+ config.annotations = config.annotations.map(annotation => {
13
+ if (!annotation) return annotation
14
+ if (annotation.y !== undefined && chartAreaHeight > 0) {
15
+ // Convert Y from pixels to percentage using the chart area height
16
+ annotation.y = (annotation.y / chartAreaHeight) * 100
17
+ } else {
18
+ annotation.y = 50
19
+ }
20
+
21
+ // Delete savedDimensions to preserve old dx/dy behavior (fixed pixel offsets).
22
+ // The scaling function falls back to raw dx/dy when savedDimensions is missing.
23
+ // Once user drags the annotation, savedDimensions will be set with current chart
24
+ // dimensions, enabling responsive scaling from that point forward.
25
+ delete annotation.savedDimensions
26
+
27
+ return annotation
28
+ })
29
+ }
30
+
31
+ if (config.type === 'dashboard' && config.visualizations) {
32
+ Object.values((config as DashboardConfig).visualizations).forEach(visualization => {
33
+ migrateAnnotationDimensions(visualization)
34
+ })
35
+ }
36
+ }
37
+
38
+ const migrateAnnotationDataModel = config => {
39
+ if (config.annotations && Array.isArray(config.annotations)) {
40
+ config.annotations = config.annotations.map(annotation => {
41
+ if (!annotation) return annotation
42
+ // Set all existing annotations to fixed mode
43
+ annotation.anchorMode = 'fixed'
44
+
45
+ // Delete xKey entirely - old format stored timestamps for dates,
46
+ // but new dataX expects raw data values. Format is incompatible.
47
+ // User can re-enable data mode which will set fresh dataX value.
48
+ delete annotation.xKey
49
+
50
+ // Delete yKey entirely (Y will be calculated dynamically in data mode)
51
+ delete annotation.yKey
52
+
53
+ // Delete empty seriesKey - it would cause yScale(undefined) errors in data mode
54
+ if (!annotation.seriesKey) {
55
+ delete annotation.seriesKey
56
+ }
57
+
58
+ // Delete deprecated properties
59
+ delete annotation.snapToNearestPoint
60
+ delete annotation.originalX
61
+ delete annotation.originalDX
62
+ delete annotation.originalY
63
+
64
+ return annotation
65
+ })
66
+ }
67
+
68
+ if (config.type === 'dashboard' && config.visualizations) {
69
+ Object.values((config as DashboardConfig).visualizations).forEach(visualization => {
70
+ migrateAnnotationDataModel(visualization)
71
+ })
72
+ }
73
+ }
74
+
75
+ const update_4_26_2 = config => {
76
+ const ver = '4.26.2'
77
+ const newConfig = cloneConfig(config)
78
+ migrateAnnotationDimensions(newConfig)
79
+ migrateAnnotationDataModel(newConfig)
80
+ newConfig.version = ver
81
+ return newConfig
82
+ }
83
+
84
+ export default update_4_26_2
@@ -0,0 +1,44 @@
1
+ import cloneConfig from '../cloneConfig'
2
+ import { DashboardConfig } from '@cdc/dashboard/src/types/DashboardConfig'
3
+
4
+ const addLocale = config => {
5
+ if (!config.locale) {
6
+ config.locale = 'en-US'
7
+ }
8
+
9
+ if (config.type === 'dashboard' && config.visualizations) {
10
+ Object.values((config as DashboardConfig).visualizations).forEach(visualization => {
11
+ addLocale(visualization)
12
+ })
13
+ }
14
+ }
15
+
16
+ const disableExtraChartVisualSettings = config => {
17
+ if (config.type === 'chart') {
18
+ config.visual = {
19
+ ...config.visual,
20
+ border: false,
21
+ borderColorTheme: false,
22
+ accent: false,
23
+ background: false,
24
+ hideBackgroundColor: false
25
+ }
26
+ }
27
+
28
+ if (config.type === 'dashboard' && config.visualizations) {
29
+ Object.values((config as DashboardConfig).visualizations).forEach(visualization => {
30
+ disableExtraChartVisualSettings(visualization)
31
+ })
32
+ }
33
+ }
34
+
35
+ const update_4_26_3 = config => {
36
+ const ver = '4.26.3'
37
+ const newConfig = cloneConfig(config)
38
+ addLocale(newConfig)
39
+ disableExtraChartVisualSettings(newConfig)
40
+ newConfig.version = ver
41
+ return newConfig
42
+ }
43
+
44
+ export default update_4_26_3
@@ -0,0 +1,31 @@
1
+ import cloneConfig from '../cloneConfig'
2
+ import { DashboardConfig } from '@cdc/dashboard/src/types/DashboardConfig'
3
+
4
+ const disableExtraChartVisualSettings = config => {
5
+ if (config.type === 'chart') {
6
+ config.visual = {
7
+ ...config.visual,
8
+ border: false,
9
+ borderColorTheme: false,
10
+ accent: false,
11
+ background: false,
12
+ hideBackgroundColor: false
13
+ }
14
+ }
15
+
16
+ if (config.type === 'dashboard' && config.visualizations) {
17
+ Object.values((config as DashboardConfig).visualizations).forEach(visualization => {
18
+ disableExtraChartVisualSettings(visualization)
19
+ })
20
+ }
21
+ }
22
+
23
+ const update_4_26_4 = config => {
24
+ const ver = '4.26.4'
25
+ const newConfig = cloneConfig(config)
26
+ disableExtraChartVisualSettings(newConfig)
27
+ newConfig.version = ver
28
+ return newConfig
29
+ }
30
+
31
+ export default update_4_26_4
@@ -0,0 +1,105 @@
1
+ import update_4_26_1 from '../4.26.1'
2
+ import { expect, describe, it } from 'vitest'
3
+
4
+ describe('update_4_26_1', () => {
5
+ describe('normalizeFilterParents', () => {
6
+ it('should convert string parents to array in shared filters', () => {
7
+ const config: any = {
8
+ type: 'dashboard',
9
+ version: '4.26.0',
10
+ dashboard: {
11
+ sharedFilters: [
12
+ {
13
+ type: 'datafilter',
14
+ parents: 'parent-filter-id'
15
+ }
16
+ ]
17
+ }
18
+ }
19
+
20
+ const result = update_4_26_1(config)
21
+
22
+ expect(result.dashboard.sharedFilters[0].parents).toEqual(['parent-filter-id'])
23
+ expect(result.version).toBe('4.26.1')
24
+ })
25
+
26
+ it('should leave array parents unchanged', () => {
27
+ const config: any = {
28
+ type: 'dashboard',
29
+ version: '4.26.0',
30
+ dashboard: {
31
+ sharedFilters: [
32
+ {
33
+ type: 'datafilter',
34
+ parents: ['parent1', 'parent2']
35
+ }
36
+ ]
37
+ }
38
+ }
39
+
40
+ const result = update_4_26_1(config)
41
+
42
+ expect(result.dashboard.sharedFilters[0].parents).toEqual(['parent1', 'parent2'])
43
+ })
44
+ })
45
+
46
+ describe('removeOldBrushKeys', () => {
47
+ it('should remove brush config from chart', () => {
48
+ const config: any = {
49
+ type: 'chart',
50
+ version: '4.26.0',
51
+ brush: { enabled: true }
52
+ }
53
+
54
+ const result = update_4_26_1(config)
55
+
56
+ expect(result.brush).toBeUndefined()
57
+ })
58
+
59
+ it('should remove brush config from dashboard visualizations', () => {
60
+ const config: any = {
61
+ type: 'dashboard',
62
+ version: '4.26.0',
63
+ visualizations: {
64
+ chart1: {
65
+ type: 'chart',
66
+ brush: { enabled: true }
67
+ }
68
+ }
69
+ }
70
+
71
+ const result = update_4_26_1(config)
72
+
73
+ expect(result.visualizations.chart1.brush).toBeUndefined()
74
+ })
75
+ })
76
+
77
+ describe('combined migrations', () => {
78
+ it('should run all migrations together', () => {
79
+ const config: any = {
80
+ type: 'dashboard',
81
+ version: '4.26.0',
82
+ dashboard: {
83
+ sharedFilters: [
84
+ {
85
+ type: 'datafilter',
86
+ parents: 'parent-id'
87
+ }
88
+ ]
89
+ },
90
+ visualizations: {
91
+ chart1: {
92
+ type: 'chart',
93
+ brush: { enabled: true }
94
+ }
95
+ }
96
+ }
97
+
98
+ const result = update_4_26_1(config)
99
+
100
+ expect(result.dashboard.sharedFilters[0].parents).toEqual(['parent-id'])
101
+ expect(result.visualizations.chart1.brush).toBeUndefined()
102
+ expect(result.version).toBe('4.26.1')
103
+ })
104
+ })
105
+ })