@cdc/chart 4.26.2 → 4.26.4

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 (165) hide show
  1. package/CONFIG.md +169 -0
  2. package/README.md +42 -17
  3. package/dist/cdcchart.js +39419 -35180
  4. package/examples/__data__/data-with-metadata.json +10 -0
  5. package/examples/feature/bar/additional-column-tooltip.json +3 -3
  6. package/examples/feature/deviation/planet-deviation-config.json +2 -2
  7. package/examples/feature/paired-bar/paired-bar-example.json +2 -2
  8. package/examples/feature/pie/planet-pie-example-config.json +2 -1
  9. package/examples/feature/tests-case-rate/case-rate-example-config.json +2 -2
  10. package/examples/feature/tests-covid/covid-confidence-example-config.json +2 -2
  11. package/examples/feature/tests-covid/covid-example-config.json +2 -2
  12. package/examples/feature/tests-cutoff/cutoff-example-config.json +2 -2
  13. package/examples/feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json +2 -2
  14. package/examples/metadata-variables.json +58 -0
  15. package/examples/minimal-example.json +24 -0
  16. package/examples/private/test2.json +35012 -0
  17. package/package.json +4 -4
  18. package/src/CdcChart.tsx +8 -4
  19. package/src/CdcChartComponent.tsx +326 -290
  20. package/src/_stories/Chart.CustomColors.smoke.stories.tsx +97 -0
  21. package/src/_stories/Chart.Defaults.smoke.stories.tsx +95 -0
  22. package/src/_stories/{Chart.HTMLInDataTable.stories.tsx → Chart.HTMLInDataTable.smoke.stories.tsx} +10 -0
  23. package/src/_stories/Chart.Minimal.smoke.stories.tsx +25 -0
  24. package/src/_stories/{Chart.SmallMultiples.stories.tsx → Chart.SmallMultiples.smoke.stories.tsx} +17 -1
  25. package/src/_stories/Chart.SmallestLeftAxisMax.smoke.stories.tsx +64 -0
  26. package/src/_stories/{Chart.stories.tsx → Chart.smoke.stories.tsx} +48 -2
  27. package/src/_stories/{Chart.tooltip.stories.tsx → Chart.tooltip.smoke.stories.tsx} +68 -0
  28. package/src/_stories/ChartBar.Editor.stories.tsx +177 -115
  29. package/src/_stories/ChartBrush.Editor.stories.tsx +11 -25
  30. package/src/_stories/ChartEditor.Editor.stories.tsx +24 -1
  31. package/src/_stories/{Chart.CustomColors.stories.tsx → ChartLine.DashedDates.smoke.stories.tsx} +4 -4
  32. package/src/_stories/_mock/line_chart_dashed_dates.json +276 -0
  33. package/src/_stories/_mock/paired-bar-abbr.json +421 -0
  34. package/src/_stories/_mock/paired-bar.json +1 -1
  35. package/src/_stories/_mock/pie_custom_colors.json +268 -0
  36. package/src/_stories/_mock/smallest_left_axis_max.json +104 -0
  37. package/src/components/Annotations/components/AnnotationDraggable.styles.css +10 -10
  38. package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -1
  39. package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -1
  40. package/src/components/Annotations/components/AnnotationList.styles.css +11 -11
  41. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -2
  42. package/src/components/Axis/BottomAxis.tsx +10 -3
  43. package/src/components/Axis/LeftAxis.tsx +2 -4
  44. package/src/components/Axis/LeftAxisGridlines.tsx +1 -3
  45. package/src/components/Axis/PairedBarAxis.tsx +10 -4
  46. package/src/components/BarChart/components/BarChart.Horizontal.tsx +21 -33
  47. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +21 -35
  48. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +21 -36
  49. package/src/components/BarChart/components/BarChart.Vertical.tsx +21 -34
  50. package/src/components/BarChart/components/BarChart.tsx +2 -6
  51. package/src/components/BarChart/helpers/getPatternUrl.ts +94 -0
  52. package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +134 -0
  53. package/src/components/BarChart/helpers/useBarChart.ts +4 -1
  54. package/src/components/BoxPlot/BoxPlot.Vertical.tsx +3 -3
  55. package/src/components/Brush/BrushSelector.tsx +2 -1
  56. package/src/components/Brush/MiniChartPreview.tsx +21 -26
  57. package/src/components/EditorPanel/EditorPanel.tsx +692 -954
  58. package/src/components/EditorPanel/EditorPanelContext.ts +1 -1
  59. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +218 -167
  60. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +0 -78
  61. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +39 -1
  62. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +169 -135
  63. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +154 -109
  64. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
  65. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +95 -28
  66. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +45 -42
  67. package/src/components/EditorPanel/editor-panel.scss +1 -1
  68. package/src/components/EditorPanel/useEditorPermissions.ts +1 -10
  69. package/src/components/Forecasting/Forecasting.tsx +2 -2
  70. package/src/components/ForestPlot/ForestPlot.tsx +26 -22
  71. package/src/components/HorizonChart/HorizonChart.tsx +10 -2
  72. package/src/components/HoverLine/HoverLine.tsx +6 -8
  73. package/src/components/Legend/Legend.Component.tsx +4 -4
  74. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +9 -4
  75. package/src/components/Legend/LegendGroup/LegendGroup.tsx +3 -1
  76. package/src/components/Legend/helpers/createFormatLabels.tsx +3 -2
  77. package/src/components/Legend/helpers/index.ts +0 -2
  78. package/src/components/LineChart/LineChartProps.ts +1 -0
  79. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +3 -2
  80. package/src/components/LineChart/index.tsx +11 -4
  81. package/src/components/LinearChart/VisualizationRenderer.tsx +11 -1
  82. package/src/components/LinearChart/tests/LinearChart.test.tsx +224 -0
  83. package/src/components/LinearChart/tests/mockConfigContext.ts +2 -0
  84. package/src/components/LinearChart.tsx +124 -47
  85. package/src/components/PairedBarChart.jsx +2 -2
  86. package/src/components/PieChart/PieChart.tsx +30 -13
  87. package/src/components/RadarChart/RadarChart.tsx +1 -1
  88. package/src/components/Regions/components/Regions.tsx +36 -26
  89. package/src/components/Sankey/components/Sankey.tsx +3 -3
  90. package/src/components/Sankey/sankey.scss +1 -1
  91. package/src/components/ScatterPlot/ScatterPlot.jsx +19 -17
  92. package/src/components/SmallMultiples/SmallMultipleTile.tsx +1 -0
  93. package/src/components/SmallMultiples/SmallMultiples.css +5 -5
  94. package/src/components/Sparkline/components/SparkLine.tsx +1 -1
  95. package/src/components/Sparkline/index.scss +4 -2
  96. package/src/components/WarmingStripes/WarmingStripes.tsx +5 -3
  97. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +8 -8
  98. package/src/data/initial-state.js +24 -14
  99. package/src/data/legacy-defaults.ts +18 -0
  100. package/src/helpers/abbreviateNumber.ts +24 -17
  101. package/src/helpers/calculateLeftYAxisWidth.ts +193 -0
  102. package/src/helpers/getChartPatternId.ts +17 -0
  103. package/src/helpers/getMinMax.ts +16 -2
  104. package/src/helpers/seriesColumnSettings.ts +114 -0
  105. package/src/helpers/tests/countNumOfTicks.test.ts +77 -0
  106. package/src/helpers/tests/seriesColumnSettings.test.ts +84 -0
  107. package/src/helpers/tests/tooltipHelpers.test.ts +151 -0
  108. package/src/helpers/tooltipHelpers.ts +176 -0
  109. package/src/hooks/useHighlightedBars.ts +9 -16
  110. package/src/hooks/useProgrammaticTooltip.ts +16 -2
  111. package/src/hooks/useRightAxis.ts +14 -0
  112. package/src/hooks/useScales.ts +92 -56
  113. package/src/hooks/useSmallMultipleSynchronization.ts +3 -4
  114. package/src/hooks/useTooltip.tsx +186 -40
  115. package/src/scss/main.scss +152 -79
  116. package/src/test/CdcChart.test.jsx +26 -2
  117. package/src/types/ChartConfig.ts +5 -0
  118. package/tests/fixtures/chart-config-with-metadata.json +29 -0
  119. package/tests/fixtures/data-with-metadata.json +10 -0
  120. package/examples/chart-regression-2.json +0 -2360
  121. package/examples/feature/__data__/age-adjusted-rates.json +0 -1486
  122. package/examples/feature/__data__/city-temperature.json +0 -2198
  123. package/examples/feature/bar/Barchart_with_negative.json +0 -34
  124. package/examples/feature/boxplot/box-plot-data.json +0 -71
  125. package/examples/feature/boxplot/boxplot-data.json +0 -156
  126. package/examples/feature/combo/combochart-categories_are_numbers .json +0 -18
  127. package/examples/feature/forest-plot/log.json +0 -26
  128. package/examples/feature/tests-date-exclusions/date-exclusions-data.json +0 -162
  129. package/examples/region-issue.json +0 -2065
  130. package/examples/xaxis.json +0 -493
  131. /package/examples/feature/bar/{tall-data.json → __data__/tall-data.json} +0 -0
  132. /package/examples/feature/deviation/{planet-deviation-data.json → __data__/planet-deviation-data.json} +0 -0
  133. /package/examples/feature/paired-bar/{paired-bar-data.json → __data__/paired-bar-data.json} +0 -0
  134. /package/examples/feature/paired-bar/{paired-bar-formatted.json → __data__/paired-bar-formatted.json} +0 -0
  135. /package/examples/feature/tests-case-rate/{case-rate-example-data.json → __data__/case-rate-example-data.json} +0 -0
  136. /package/examples/feature/tests-covid/{covid-example-data-confidence.json → __data__/covid-example-data-confidence.json} +0 -0
  137. /package/examples/feature/tests-covid/{covid-example-data.json → __data__/covid-example-data.json} +0 -0
  138. /package/examples/feature/tests-cutoff/{cutoff-example-data.json → __data__/cutoff-example-data.json} +0 -0
  139. /package/examples/feature/tests-non-numerics/{planet-example-data-nonnumeric.json → __data__/planet-example-data-nonnumeric.json} +0 -0
  140. /package/src/_stories/{Chart.Anchors.stories.tsx → Chart.Anchors.smoke.stories.tsx} +0 -0
  141. /package/src/_stories/{Chart.BoxPlot.stories.tsx → Chart.BoxPlot.smoke.stories.tsx} +0 -0
  142. /package/src/_stories/{Chart.CI.stories.tsx → Chart.CI.smoke.stories.tsx} +0 -0
  143. /package/src/_stories/{Chart.Combo.stories.tsx → Chart.Combo.smoke.stories.tsx} +0 -0
  144. /package/src/_stories/{Chart.DynamicSeries.stories.tsx → Chart.DynamicSeries.smoke.stories.tsx} +0 -0
  145. /package/src/_stories/{Chart.Filters.stories.tsx → Chart.Filters.smoke.stories.tsx} +0 -0
  146. /package/src/_stories/{Chart.Forecast.stories.tsx → Chart.Forecast.smoke.stories.tsx} +0 -0
  147. /package/src/_stories/{Chart.Legend.Gradient.stories.tsx → Chart.Legend.Gradient.smoke.stories.tsx} +0 -0
  148. /package/src/_stories/{Chart.Patterns.stories.tsx → Chart.Patterns.smoke.stories.tsx} +0 -0
  149. /package/src/_stories/{Chart.PreserveDecimals.stories.tsx → Chart.PreserveDecimals.smoke.stories.tsx} +0 -0
  150. /package/src/_stories/{Chart.Regions.Categorical.stories.tsx → Chart.Regions.Categorical.smoke.stories.tsx} +0 -0
  151. /package/src/_stories/{Chart.Regions.DateScale.stories.tsx → Chart.Regions.DateScale.smoke.stories.tsx} +0 -0
  152. /package/src/_stories/{Chart.Regions.DateTimeScale.stories.tsx → Chart.Regions.DateTimeScale.smoke.stories.tsx} +0 -0
  153. /package/src/_stories/{Chart.ScatterPlot.stories.tsx → Chart.ScatterPlot.smoke.stories.tsx} +0 -0
  154. /package/src/_stories/{ChartAnnotation.stories.tsx → ChartAnnotation.smoke.stories.tsx} +0 -0
  155. /package/src/_stories/{ChartAxisLabels.stories.tsx → ChartAxisLabels.smoke.stories.tsx} +0 -0
  156. /package/src/_stories/{ChartAxisTitles.stories.tsx → ChartAxisTitles.smoke.stories.tsx} +0 -0
  157. /package/src/_stories/{ChartBrush.Matrix.Continuous.stories.tsx → ChartBrush.Matrix.Continuous.smoke.stories.tsx} +0 -0
  158. /package/src/_stories/{ChartBrush.Matrix.Date.stories.tsx → ChartBrush.Matrix.Date.smoke.stories.tsx} +0 -0
  159. /package/src/_stories/{ChartBrush.Matrix.DateTime.stories.tsx → ChartBrush.Matrix.DateTime.smoke.stories.tsx} +0 -0
  160. /package/src/_stories/{ChartBrush.stories.tsx → ChartBrush.smoke.stories.tsx} +0 -0
  161. /package/src/_stories/{ChartLine.QuadrantAngles.stories.tsx → ChartLine.QuadrantAngles.smoke.stories.tsx} +0 -0
  162. /package/src/_stories/{ChartLine.Suppression.stories.tsx → ChartLine.Suppression.smoke.stories.tsx} +0 -0
  163. /package/src/_stories/{ChartLine.Symbols.stories.tsx → ChartLine.Symbols.smoke.stories.tsx} +0 -0
  164. /package/src/_stories/{ChartPrefixSuffix.stories.tsx → ChartPrefixSuffix.smoke.stories.tsx} +0 -0
  165. /package/src/_stories/{TechAdoptionWithLinks.stories.tsx → TechAdoptionWithLinks.smoke.stories.tsx} +0 -0
@@ -24,14 +24,9 @@ import { handleChartAriaLabels } from '../../helpers/handleChartAriaLabels'
24
24
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
25
25
  import { scaleOrdinal } from '@visx/scale'
26
26
  import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
27
+ import { type TooltipDisplayData } from '../../helpers/tooltipHelpers'
27
28
 
28
- type TooltipData = {
29
- data: {
30
- [key: string]: string | number
31
- }
32
- dataXPosition: number
33
- dataYPosition: number
34
- }
29
+ type TooltipData = TooltipDisplayData
35
30
 
36
31
  type PieChartProps = {
37
32
  parentWidth?: number
@@ -172,8 +167,22 @@ const PieChart = React.forwardRef<SVGSVGElement, PieChartProps>((props, ref) =>
172
167
  const domainKeys = isPercentageMode ? dataKeys.filter(k => k !== labelForCalcArea) : dataKeys
173
168
  const numberOfKeys = domainKeys.length
174
169
 
175
- let palette = getPaletteColors(config, colorPalettes)
176
- palette = applyEnhancedColorDistribution(config, palette, numberOfKeys)
170
+ const orderedCustomColors = config.general?.palette?.customColorsOrdered
171
+ const customColors = config.general?.palette?.customColors
172
+ const shouldUseOrderedCustomColors = Array.isArray(orderedCustomColors) && orderedCustomColors.length > 0
173
+
174
+ let palette = shouldUseOrderedCustomColors ? orderedCustomColors : getPaletteColors(config, colorPalettes)
175
+ if (!shouldUseOrderedCustomColors && customColors?.length) {
176
+ palette = customColors
177
+ }
178
+
179
+ while (palette.length > 0 && palette.length < numberOfKeys) {
180
+ palette = palette.concat(palette)
181
+ }
182
+
183
+ palette = shouldUseOrderedCustomColors
184
+ ? palette.slice(0, numberOfKeys)
185
+ : applyEnhancedColorDistribution(config, palette, numberOfKeys)
177
186
 
178
187
  const unknownColor = isPercentageMode
179
188
  ? getComputedStyle(document.documentElement).getPropertyValue('--cool-gray-10').trim()
@@ -206,6 +215,7 @@ const PieChart = React.forwardRef<SVGSVGElement, PieChartProps>((props, ref) =>
206
215
  config.general?.palette?.name,
207
216
  config.general?.palette?.isReversed,
208
217
  config.general?.palette?.customColors,
218
+ config.general?.palette?.customColorsOrdered,
209
219
  config.palette
210
220
  ])
211
221
 
@@ -216,7 +226,7 @@ const PieChart = React.forwardRef<SVGSVGElement, PieChartProps>((props, ref) =>
216
226
 
217
227
  // Make sure the chart is visible if in the editor
218
228
  useEffect(() => {
219
- const element = document.querySelector('.isEditor')
229
+ const element = document.querySelector('.is-editor')
220
230
  if (element) {
221
231
  // parent element is visible
222
232
  setAnimatePie(true)
@@ -411,7 +421,7 @@ const PieChart = React.forwardRef<SVGSVGElement, PieChartProps>((props, ref) =>
411
421
  <div ref={triggerRef} />
412
422
  {!isDraggingAnnotation &&
413
423
  tooltipData &&
414
- Object.entries(tooltipData.data).length > 0 &&
424
+ tooltipData.data?.length > 0 &&
415
425
  tooltipOpen &&
416
426
  showTooltip &&
417
427
  tooltipData.dataYPosition &&
@@ -421,13 +431,20 @@ const PieChart = React.forwardRef<SVGSVGElement, PieChartProps>((props, ref) =>
421
431
  config.tooltips.opacity / 100
422
432
  }) !important`}</style>
423
433
  <TooltipWithBounds
424
- className={'tooltip cdc-open-viz-module'}
434
+ className={'tooltip cove-visualization'}
425
435
  left={tooltipLeft + centerX - radius}
426
436
  top={tooltipTop}
427
437
  >
428
438
  <ul>
429
439
  {typeof tooltipData === 'object' &&
430
- Object.entries(tooltipData.data).map((item, index) => <TooltipListItem item={item} key={index} />)}
440
+ tooltipData.data.map((row, index) => (
441
+ <TooltipListItem
442
+ row={row}
443
+ index={index}
444
+ key={index}
445
+ useMarkerColumn={tooltipData.useMarkerColumn}
446
+ />
447
+ ))}
431
448
  </ul>
432
449
  </TooltipWithBounds>
433
450
  </>
@@ -278,7 +278,7 @@ const RadarChart = React.forwardRef<SVGSVGElement, RadarChartProps>((props, ref)
278
278
  <style>{`.tooltip {background-color: rgba(255,255,255, ${
279
279
  (config.tooltips?.opacity || 90) / 100
280
280
  }) !important`}</style>
281
- <TooltipWithBounds className='tooltip cdc-open-viz-module' left={tooltipLeft} top={tooltipTop}>
281
+ <TooltipWithBounds className='tooltip cove-visualization' left={tooltipLeft} top={tooltipTop}>
282
282
  <div style={{ fontWeight: 'bold', marginBottom: '8px' }}>{tooltipData.entityName}</div>
283
283
  <ul style={{ margin: 0, padding: 0, listStyle: 'none' }}>
284
284
  {tooltipData.values.map((item, index) => (
@@ -10,7 +10,8 @@ const VIZ_TYPES = {
10
10
  BAR: 'Bar',
11
11
  LINE: 'Line',
12
12
  AREA: 'Area Chart',
13
- COMBO: 'Combo'
13
+ COMBO: 'Combo',
14
+ HORIZON: 'Horizon Chart'
14
15
  } as const
15
16
 
16
17
  type Region = {
@@ -35,6 +36,7 @@ type RegionsProps = {
35
36
  barWidth?: number
36
37
  totalBarsInGroup?: number
37
38
  xMax?: number
39
+ yAxisWidth?: number
38
40
  }
39
41
 
40
42
  type HighlightedAreaProps = {
@@ -63,14 +65,22 @@ const findClosestDate = <T,>(targetTime: number, domain: T[], getTime: (d: T) =>
63
65
  return closest
64
66
  }
65
67
 
66
- /** Check if visualization type is line-like (Line or Area Chart) */
67
- const isLineLike = (type: string): boolean => type === VIZ_TYPES.LINE || type === VIZ_TYPES.AREA
68
+ /** Check if visualization type is line-like (Line, Area Chart, or Horizon Chart) */
69
+ const isLineLike = (type: string): boolean =>
70
+ type === VIZ_TYPES.LINE || type === VIZ_TYPES.AREA || type === VIZ_TYPES.HORIZON
68
71
 
69
72
  /** Check if visualization type is bar-like (Bar or Combo) */
70
73
  const isBarLike = (type: string): boolean => type === VIZ_TYPES.BAR || type === VIZ_TYPES.COMBO
71
74
 
72
75
  // TODO: should regions be removed on categorical axis?
73
- const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGroup = 1, yMax, xMax }) => {
76
+ const Regions: React.FC<RegionsProps> = ({
77
+ xScale,
78
+ barWidth = 0,
79
+ totalBarsInGroup = 1,
80
+ yMax,
81
+ xMax,
82
+ yAxisWidth = 0
83
+ }) => {
74
84
  const { parseDate, config } = useContext<ChartContext>(ConfigContext)
75
85
 
76
86
  const { regions, visualizationType, orientation, xAxis } = config
@@ -91,7 +101,7 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
91
101
  ? new Date(domain[domain.length - 1] as string | number).getTime()
92
102
  : new Date(region.to)
93
103
 
94
- const toFormatted = formatDate(config.xAxis.dateParseFormat, toRefDate)
104
+ const toFormatted = formatDate(config.xAxis.dateParseFormat, toRefDate, config.locale)
95
105
  const toDate = new Date(toFormatted)
96
106
  const fromDate = new Date(toDate)
97
107
  fromDate.setDate(fromDate.getDate() - previousDays)
@@ -99,7 +109,7 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
99
109
  let closestValue: unknown
100
110
 
101
111
  if (axisType === 'date') {
102
- const fromTime = new Date(formatDate(xAxis.dateParseFormat, fromDate)).getTime()
112
+ const fromTime = new Date(formatDate(xAxis.dateParseFormat, fromDate, config.locale)).getTime()
103
113
  closestValue = findClosestDate(fromTime, domain as number[], d => d)
104
114
  } else if (axisType === 'categorical') {
105
115
  const fromTime = fromDate.getTime()
@@ -122,8 +132,8 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
122
132
  } else {
123
133
  from = xScale(region.from)
124
134
  }
125
- // Add left padding (yAxis.size) + half bandwidth to center on the category
126
- let scalePadding = Number(config.yAxis.size)
135
+ // Add left padding (yAxisWidth) + half bandwidth to center on the category
136
+ let scalePadding = yAxisWidth
127
137
  if (xScale.bandwidth) {
128
138
  scalePadding += xScale.bandwidth() / 2
129
139
  }
@@ -135,8 +145,8 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
135
145
  return calculateLineLastDatePosition_Categorical()
136
146
  }
137
147
  let to = xScale(region.to)
138
- // Add left padding (yAxis.size) + half bandwidth
139
- let scalePadding = Number(config.yAxis.size)
148
+ // Add left padding (yAxisWidth) + half bandwidth
149
+ let scalePadding = yAxisWidth
140
150
  if (xScale.bandwidth) {
141
151
  scalePadding += xScale.bandwidth() / 2
142
152
  }
@@ -151,15 +161,15 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
151
161
  // For date scale (band), we need to find the value in the domain
152
162
  // Parse the region date to match the format in the domain
153
163
  const date = new Date(region.from)
154
- const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date)).getTime()
164
+ const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date, config.locale)).getTime()
155
165
 
156
166
  // For band scales, find the closest date in the domain
157
167
  const domain = xScale.domain() as number[]
158
168
  const closestDate = findClosestDate(parsedDate, domain, d => d)
159
169
  from = xScale(closestDate)
160
170
  }
161
- // Add left padding (yAxis.size) + half bandwidth
162
- let scalePadding = Number(config.yAxis.size)
171
+ // Add left padding (yAxisWidth) + half bandwidth
172
+ let scalePadding = yAxisWidth
163
173
  if (xScale.bandwidth) {
164
174
  scalePadding += xScale.bandwidth() / 2
165
175
  }
@@ -179,8 +189,8 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
179
189
  const closestDate = findClosestDate(parsedDate, domain, d => d)
180
190
  let to = xScale(closestDate)
181
191
 
182
- // Add left padding (yAxis.size) + half bandwidth
183
- let scalePadding = Number(config.yAxis.size)
192
+ // Add left padding (yAxisWidth) + half bandwidth
193
+ let scalePadding = yAxisWidth
184
194
  if (xScale.bandwidth) {
185
195
  scalePadding += xScale.bandwidth() / 2
186
196
  }
@@ -190,13 +200,13 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
190
200
  const getLineFromValue_DateTime = (region: Region): number => {
191
201
  if (region.fromType === 'Previous Days') {
192
202
  const from = calculatePreviousDaysFrom(region, 'date-time')
193
- return from + Number(config.yAxis.size)
203
+ return from + yAxisWidth
194
204
  }
195
205
  const date = new Date(region.from)
196
- const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date)).getTime()
206
+ const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date, config.locale)).getTime()
197
207
  let from = xScale(parsedDate)
198
208
  // For date-time, xScale returns correct position (no bandwidth), just add left padding
199
- return from + Number(config.yAxis.size)
209
+ return from + yAxisWidth
200
210
  }
201
211
 
202
212
  const getLineToValue_DateTime = (region: Region): number => {
@@ -204,17 +214,17 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
204
214
  return calculateLineLastDatePosition_DateTime()
205
215
  }
206
216
  let to = xScale(parseDate(region.to).getTime())
207
- return to + Number(config.yAxis.size)
217
+ return to + yAxisWidth
208
218
  }
209
219
 
210
220
  const calculateLineLastDatePosition_Categorical = (): number => {
211
- const chartStart = Number(config.yAxis.size || 0)
221
+ const chartStart = yAxisWidth
212
222
  // Extend to the right edge of the chart
213
223
  return chartStart + (xMax || 0)
214
224
  }
215
225
 
216
226
  const calculateLineLastDatePosition_Date = (): number => {
217
- const chartStart = Number(config.yAxis.size || 0)
227
+ const chartStart = yAxisWidth
218
228
  // For date scale line charts with Last Date, extend to the right edge of the chart
219
229
  return chartStart + (xMax || 0)
220
230
  }
@@ -223,8 +233,8 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
223
233
  const domain = xScale.domain()
224
234
  const lastDate = domain[domain.length - 1]
225
235
  const lastDatePosition = xScale(lastDate)
226
- // Match the non-Last Date logic: just add yAxis.size
227
- return Number(lastDatePosition + Number(config.yAxis.size))
236
+ // Match the non-Last Date logic: just add yAxisWidth
237
+ return Number(lastDatePosition + yAxisWidth)
228
238
  }
229
239
 
230
240
  // ============================================
@@ -252,7 +262,7 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
252
262
  }
253
263
  // For date scale (band), we need to find the value in the domain
254
264
  const date = new Date(region.from)
255
- const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date)).getTime()
265
+ const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date, config.locale)).getTime()
256
266
 
257
267
  // For band scales, find the closest date in the domain
258
268
  const domain = xScale.domain() as number[]
@@ -281,7 +291,7 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
281
291
  from = calculatePreviousDaysFrom(region, 'date-time')
282
292
  } else {
283
293
  const date = new Date(region.from)
284
- const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date)).getTime()
294
+ const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date, config.locale)).getTime()
285
295
  from = xScale(parsedDate)
286
296
  }
287
297
  return from - getBarOffset()
@@ -389,7 +399,7 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
389
399
 
390
400
  if (!regions || orientation !== 'vertical') return null
391
401
 
392
- const chartStart = Number(config.yAxis.size || 0)
402
+ const chartStart = yAxisWidth
393
403
  const chartEnd = xMax !== undefined ? chartStart + xMax : chartStart + 1000
394
404
 
395
405
  return regions.map((region: Region) => {
@@ -226,7 +226,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
226
226
  data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
227
227
  data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
228
228
  >
229
- {typeof node.value === 'number' ? node.value.toLocaleString() : node.value}
229
+ {typeof node.value === 'number' ? node.value.toLocaleString(config.locale) : node.value}
230
230
  </Text>
231
231
  <Text
232
232
  width={linkLength()}
@@ -281,7 +281,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
281
281
  >
282
282
  <tspan className={classStyle}>
283
283
  {sankeyConfig.nodeValueStyle.textBefore +
284
- (typeof node.value === 'number' ? node.value.toLocaleString() : node.value) +
284
+ (typeof node.value === 'number' ? node.value.toLocaleString(config.locale) : node.value) +
285
285
  sankeyConfig.nodeValueStyle.textAfter}
286
286
  </tspan>
287
287
  </text>
@@ -394,7 +394,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
394
394
  textAnchor='start'
395
395
  style={{ pointerEvents: 'none' }}
396
396
  >
397
- {typeof node.value === 'number' ? node.value.toLocaleString() : node.value}
397
+ {typeof node.value === 'number' ? node.value.toLocaleString(config.locale) : node.value}
398
398
  </Text>
399
399
  <Text
400
400
  x={node.x0! + textPositionHorizontal}
@@ -5,7 +5,7 @@
5
5
  column-gap: 30px;
6
6
  }
7
7
 
8
- .cdc-open-viz-module .sankey-chart {
8
+ .cove-visualization .sankey-chart {
9
9
  --font-size-small: 12px;
10
10
  --font-size-medium: 16px;
11
11
  --font-size-large: 18px;
@@ -4,22 +4,21 @@ import { Group } from '@visx/group'
4
4
  import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
5
5
  import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
6
6
  import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
7
+ import { buildSeriesTooltipListHtml } from '../../helpers/tooltipHelpers'
7
8
 
8
- const ScatterPlot = ({ xScale, yScale }) => {
9
+ const ScatterPlot = ({ xScale, yScale, yAxisWidth }) => {
9
10
  const {
10
11
  transformedData: data,
11
12
  config,
12
13
  tableData,
13
14
  formatNumber,
14
15
  seriesHighlight,
15
- colorPalettes,
16
16
  colorScale,
17
17
  interactionLabel
18
18
  } = useContext(ConfigContext)
19
19
 
20
20
  // TODO: copied from line chart should probably be a constant somewhere.
21
21
  const circleRadii = 4.5
22
- const hasMultipleSeries = Object.keys(config.runtime.seriesLabels).length > 1
23
22
 
24
23
  // Track current hover for analytics
25
24
  const [currentHover, setCurrentHover] = useState({ dataIndex: null, seriesKey: null })
@@ -37,23 +36,26 @@ const ScatterPlot = ({ xScale, yScale }) => {
37
36
  }
38
37
  ])
39
38
  const handleTooltip = (item, s, dataIndex) => `<div>
40
- ${
41
- config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && hasMultipleSeries
42
- ? `${config.runtime.seriesLabels[s] || ''}<br/>`
43
- : ''
44
- }
45
- ${config.runtime?.xAxis?.label || config.xAxis.label}: ${formatNumber(item[config.xAxis.dataKey], 'bottom')} <br/>
46
- ${config.runtime?.yAxis?.label || config.yAxis.label}: ${formatNumber(item[s], 'left')}<br/>
47
- ${additionalColumns
48
- .map(
49
- ([label, name, options]) =>
50
- `${label} : ${formatColNumber(tableData[dataIndex][name], 'left', false, config, options)}<br/>`
51
- )
52
- .join('')}
39
+ ${buildSeriesTooltipListHtml({
40
+ config,
41
+ colorScale,
42
+ heading: `${config.runtime?.xAxis?.label || config.xAxis.label}: ${formatNumber(
43
+ item[config.xAxis.dataKey],
44
+ 'bottom'
45
+ )}`,
46
+ seriesKey: s,
47
+ seriesText: `${config.runtime.seriesLabels[s] || s}: ${formatNumber(item[s], 'left')}`,
48
+ extraRows: additionalColumns
49
+ .map(
50
+ ([label, name, options]) =>
51
+ `${label} : ${formatColNumber(tableData[dataIndex][name], 'left', false, config, options)}`
52
+ )
53
+ .filter(Boolean)
54
+ })}
53
55
  </div>`
54
56
 
55
57
  return (
56
- <Group className='scatter-plot' left={config.yAxis.size}>
58
+ <Group className='scatter-plot' left={yAxisWidth}>
57
59
  {data.map((item, dataIndex) => {
58
60
  // prettier-ignore
59
61
  return config.runtime.seriesKeys.map((s, index) => {
@@ -114,6 +114,7 @@ const SmallMultipleTile: React.FC<SmallMultipleTileProps> = ({
114
114
  hideYAxisLabel: !isFirstInRow,
115
115
  legend: {
116
116
  ...tileConfig.legend,
117
+ tooltipLegendVisible: !config.legend?.hide,
117
118
  hide: true // Hide legends for all small multiple tiles
118
119
  },
119
120
  xAxis: {
@@ -1,13 +1,13 @@
1
1
  .small-multiples-container {
2
- width: 100%;
3
2
  display: flex;
4
3
  flex-direction: column;
4
+ width: 100%;
5
5
  }
6
6
 
7
7
  .small-multiples-grid {
8
8
  display: grid;
9
- width: 100%;
10
9
  flex: 1;
10
+ width: 100%;
11
11
  }
12
12
 
13
13
  .small-multiple-tile {
@@ -20,13 +20,13 @@
20
20
  }
21
21
 
22
22
  .tile-title {
23
- margin: 0;
24
23
  font-weight: 700;
25
- text-align: left;
26
24
  line-height: 1.3;
25
+ margin: 0;
26
+ text-align: left;
27
27
  }
28
28
 
29
29
  .tile-chart {
30
- width: 100%;
31
30
  flex-shrink: 0;
31
+ width: 100%;
32
32
  }
@@ -34,7 +34,7 @@ const SparkLine: React.FC<SparkLineProps> = props => {
34
34
  const margin = { top: 5, right: 20, bottom: 10, left: 10 }
35
35
  const height = Number(parentHeight)
36
36
 
37
- const xMax = width - config.runtime.yAxis.size
37
+ const xMax = width - 50
38
38
  const yMax = height - margin.top - 20
39
39
 
40
40
  const getXAxisData = d =>
@@ -1,3 +1,5 @@
1
- .cdc-open-viz-module.type-chart.lg.type-sparkline .filters-section {
2
- margin: 0;
1
+ .cove-visualization.type-chart.type-sparkline {
2
+ &.lg .filters-section {
3
+ margin: 0;
4
+ }
3
5
  }
@@ -15,6 +15,7 @@ type WarmingStripesProps = {
15
15
  yScale: any
16
16
  xMax: number
17
17
  yMax: number
18
+ yAxisWidth: number
18
19
  synchronizedXValue?: any
19
20
  showTooltip: (args: any) => void
20
21
  handleTooltipMouseOff: () => void
@@ -23,6 +24,7 @@ type WarmingStripesProps = {
23
24
  const WarmingStripes = ({
24
25
  xMax,
25
26
  yMax,
27
+ yAxisWidth,
26
28
  synchronizedXValue,
27
29
  showTooltip,
28
30
  handleTooltipMouseOff
@@ -119,7 +121,7 @@ const WarmingStripes = ({
119
121
  [valueKey, formattedValue, 'left']
120
122
  ]
121
123
 
122
- const dataXPosition = index * stripeWidth + stripeWidth / 2 + Number(config.yAxis.size)
124
+ const dataXPosition = index * stripeWidth + stripeWidth / 2 + yAxisWidth
123
125
  const dataYPosition = mouseY ?? yMax / 2
124
126
 
125
127
  showTooltip({
@@ -132,7 +134,7 @@ const WarmingStripes = ({
132
134
  }
133
135
  })
134
136
  },
135
- [valueKey, xAxisDataKey, stripeWidth, config.yAxis.size, yMax, formatNumber, showTooltip]
137
+ [valueKey, xAxisDataKey, stripeWidth, yAxisWidth, yMax, formatNumber, showTooltip]
136
138
  )
137
139
 
138
140
  // Handle incoming synchronized tooltip from sibling small multiple tiles
@@ -156,7 +158,7 @@ const WarmingStripes = ({
156
158
  }
157
159
 
158
160
  return (
159
- <Group className='warming-stripes' left={config.yAxis.size}>
161
+ <Group className='warming-stripes' left={yAxisWidth}>
160
162
  {displayData.map((item, index) => {
161
163
  const value = Number(item[valueKey])
162
164
  if (isNaN(value)) return null
@@ -4,16 +4,16 @@
4
4
  }
5
5
 
6
6
  .warming-stripes-gradient-legend__title {
7
+ font-size: 16px;
7
8
  font-weight: 600;
8
9
  margin-bottom: 0.5rem;
9
- font-size: 16px;
10
10
  }
11
11
 
12
12
  .warming-stripes-gradient-legend__description {
13
- margin-top: 0.5rem;
14
- margin-bottom: 1rem;
15
- font-size: 14px;
16
13
  color: #555;
14
+ font-size: 14px;
15
+ margin-bottom: 1rem;
16
+ margin-top: 0.5rem;
17
17
  }
18
18
 
19
19
  .warming-stripes-gradient-legend__container {
@@ -22,14 +22,14 @@
22
22
 
23
23
  .warming-stripes-gradient-legend__svg {
24
24
  display: block;
25
- width: 100%;
26
25
  overflow: visible;
26
+ width: 100%;
27
27
  }
28
28
 
29
29
  .warming-stripes-gradient-legend__series-label {
30
- text-align: center;
31
- margin-top: 0.5rem;
30
+ color: #333;
32
31
  font-size: 14px;
33
32
  font-weight: 500;
34
- color: #333;
33
+ margin-top: 0.5rem;
34
+ text-align: center;
35
35
  }
@@ -54,12 +54,12 @@ const createInitialState = () => {
54
54
  },
55
55
  preliminaryData: [],
56
56
  yAxis: {
57
- hideAxis: false,
57
+ hideAxis: true,
58
58
  displayNumbersOnBar: false,
59
59
  hideLabel: false,
60
- hideTicks: false,
60
+ hideTicks: true,
61
61
  size: 50,
62
- gridLines: false,
62
+ gridLines: true,
63
63
  enablePadding: false,
64
64
  min: '',
65
65
  max: '',
@@ -73,7 +73,7 @@ const createInitialState = () => {
73
73
  rightAxisLabelColor: '#1c1d1f',
74
74
  rightAxisTickLabelColor: '#1c1d1f',
75
75
  rightAxisTickColor: '#1c1d1f',
76
- numTicks: '',
76
+ numTicks: 4,
77
77
  axisPadding: 0,
78
78
  scalePadding: 10,
79
79
  tickRotation: 0,
@@ -133,7 +133,8 @@ const createInitialState = () => {
133
133
  labelColor: '#1c1d1f',
134
134
  tickLabelColor: '#1c1d1f',
135
135
  tickColor: '#1c1d1f',
136
- numTicks: '',
136
+ numTicks: 6,
137
+ dateDisplayFormat: '%b. %-d %Y',
137
138
  labelOffset: 0,
138
139
  axisPadding: 200,
139
140
  target: 0,
@@ -142,21 +143,26 @@ const createInitialState = () => {
142
143
  showYearsOnce: false,
143
144
  sortByRecentDate: false,
144
145
  brushActive: false,
145
- brushDefaultRecentDateCount: undefined
146
+ brushDefaultRecentDateCount: undefined,
147
+ viewportNumTicks: {
148
+ xs: 4,
149
+ xxs: 4
150
+ }
146
151
  },
147
152
  table: {
148
153
  label: 'Data Table',
149
- expanded: true,
154
+ expanded: false,
150
155
  limitHeight: false,
151
156
  height: '',
152
157
  caption: '',
153
158
  showDownloadUrl: false,
159
+ downloadUrlLabel: '',
154
160
  showDataTableLink: true,
155
161
  showDownloadLinkBelow: true,
156
162
  indexLabel: '',
157
163
  download: false,
158
164
  showVertical: true,
159
- dateDisplayFormat: '',
165
+ dateDisplayFormat: '%B %-d, %Y',
160
166
  showMissingDataLabel: true,
161
167
  showSuppressedSymbol: true,
162
168
  collapsible: true
@@ -196,7 +202,7 @@ const createInitialState = () => {
196
202
  side: false,
197
203
  topBottom: true
198
204
  },
199
- position: 'right',
205
+ position: 'top',
200
206
  orderedValues: [],
201
207
  patterns: {},
202
208
  patternField: ''
@@ -224,7 +230,7 @@ const createInitialState = () => {
224
230
  },
225
231
  labels: false,
226
232
  dataFormat: {
227
- commas: false,
233
+ commas: true,
228
234
  prefix: '',
229
235
  suffix: '',
230
236
  abbreviated: false,
@@ -235,9 +241,13 @@ const createInitialState = () => {
235
241
  filters: [],
236
242
  confidenceKeys: {},
237
243
  visual: {
238
- border: true,
239
- accent: true,
240
- background: true,
244
+ border: false,
245
+ borderColorTheme: false,
246
+ accent: false,
247
+ background: false,
248
+ hideBackgroundColor: false,
249
+ tp5Treatment: false,
250
+ tp5Background: false,
241
251
  verticalHoverLine: false,
242
252
  horizontalHoverLine: false,
243
253
  lineDatapointSymbol: 'none',
@@ -250,7 +260,7 @@ const createInitialState = () => {
250
260
  tooltips: {
251
261
  opacity: 90,
252
262
  singleSeries: false,
253
- dateDisplayFormat: ''
263
+ dateDisplayFormat: '%B %-d, %Y'
254
264
  },
255
265
  forestPlot: {
256
266
  startAt: 0,
@@ -0,0 +1,18 @@
1
+ // Preserves the OLD default values for properties changed in initial-state.js.
2
+ // When the backfill loop fills a missing property, it uses these values instead
3
+ // of the current defaults so that existing configs aren't visually affected.
4
+ //
5
+ // - Changed defaults: record the ORIGINAL value before any changes.
6
+ // - New properties: set to `undefined` so they are not backfilled at all.
7
+ //
8
+ // See backfillDefaults() in @cdc/core for the shared fill logic.
9
+ export const LEGACY_CHART_DEFAULTS: Record<string, Record<string, unknown>> = {
10
+ general: { useIntelligentLineChartLabels: undefined },
11
+ yAxis: { hideAxis: false, hideTicks: false, gridLines: false, numTicks: '' },
12
+ xAxis: { numTicks: '', dateDisplayFormat: undefined, viewportNumTicks: undefined },
13
+ table: { expanded: true, dateDisplayFormat: '' },
14
+ legend: { position: 'right' },
15
+ dataFormat: { commas: false },
16
+ tooltips: { dateDisplayFormat: '' },
17
+ visual: { border: false, accent: false, background: false }
18
+ }