@cdc/chart 4.26.2 → 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 (70) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcchart.js +35674 -32430
  3. package/examples/data/data-with-metadata.json +10 -0
  4. package/examples/feature/pie/planet-pie-example-config.json +2 -1
  5. package/examples/metadata-variables.json +58 -0
  6. package/package.json +3 -3
  7. package/src/CdcChart.tsx +8 -4
  8. package/src/CdcChartComponent.tsx +321 -288
  9. package/src/_stories/Chart.CustomColors.stories.tsx +74 -0
  10. package/src/_stories/Chart.Defaults.stories.tsx +95 -0
  11. package/src/_stories/Chart.SmallestLeftAxisMax.stories.tsx +64 -0
  12. package/src/_stories/Chart.stories.tsx +36 -2
  13. package/src/_stories/ChartBar.Editor.stories.tsx +97 -38
  14. package/src/_stories/ChartBrush.Editor.stories.tsx +11 -25
  15. package/src/_stories/ChartEditor.Editor.stories.tsx +1 -1
  16. package/src/_stories/_mock/paired-bar-abbr.json +421 -0
  17. package/src/_stories/_mock/pie_custom_colors.json +268 -0
  18. package/src/_stories/_mock/smallest_left_axis_max.json +104 -0
  19. package/src/components/Annotations/components/AnnotationDraggable.styles.css +10 -10
  20. package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -1
  21. package/src/components/Annotations/components/AnnotationList.styles.css +11 -11
  22. package/src/components/Axis/BottomAxis.tsx +10 -3
  23. package/src/components/Axis/PairedBarAxis.tsx +10 -4
  24. package/src/components/BarChart/components/BarChart.Horizontal.tsx +12 -28
  25. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +12 -30
  26. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +12 -31
  27. package/src/components/BarChart/components/BarChart.Vertical.tsx +12 -28
  28. package/src/components/BarChart/helpers/getPatternUrl.ts +94 -0
  29. package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +134 -0
  30. package/src/components/BarChart/helpers/useBarChart.ts +3 -0
  31. package/src/components/Brush/BrushSelector.tsx +2 -1
  32. package/src/components/Brush/MiniChartPreview.tsx +21 -26
  33. package/src/components/EditorPanel/EditorPanel.tsx +56 -43
  34. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +9 -9
  35. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +0 -78
  36. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +39 -1
  37. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +24 -42
  38. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +83 -2
  39. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +45 -42
  40. package/src/components/EditorPanel/editor-panel.scss +1 -1
  41. package/src/components/ForestPlot/ForestPlot.tsx +26 -22
  42. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +4 -4
  43. package/src/components/Legend/helpers/createFormatLabels.tsx +3 -2
  44. package/src/components/LinearChart/tests/LinearChart.test.tsx +77 -0
  45. package/src/components/LinearChart/tests/mockConfigContext.ts +2 -0
  46. package/src/components/LinearChart.tsx +26 -6
  47. package/src/components/PieChart/PieChart.tsx +19 -4
  48. package/src/components/RadarChart/RadarChart.tsx +1 -1
  49. package/src/components/Regions/components/Regions.tsx +6 -6
  50. package/src/components/Sankey/components/Sankey.tsx +3 -3
  51. package/src/components/Sankey/sankey.scss +1 -1
  52. package/src/components/SmallMultiples/SmallMultiples.css +5 -5
  53. package/src/components/Sparkline/index.scss +4 -2
  54. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +8 -8
  55. package/src/data/initial-state.js +23 -14
  56. package/src/data/legacy-defaults.ts +18 -0
  57. package/src/helpers/abbreviateNumber.ts +24 -17
  58. package/src/helpers/getChartPatternId.ts +17 -0
  59. package/src/helpers/getMinMax.ts +16 -2
  60. package/src/helpers/seriesColumnSettings.ts +114 -0
  61. package/src/helpers/tests/countNumOfTicks.test.ts +77 -0
  62. package/src/helpers/tests/seriesColumnSettings.test.ts +84 -0
  63. package/src/hooks/useRightAxis.ts +14 -0
  64. package/src/hooks/useScales.ts +92 -56
  65. package/src/hooks/useTooltip.tsx +20 -3
  66. package/src/scss/main.scss +152 -79
  67. package/src/test/CdcChart.test.jsx +2 -2
  68. package/src/types/ChartConfig.ts +4 -0
  69. package/tests/fixtures/chart-config-with-metadata.json +29 -0
  70. package/tests/fixtures/data-with-metadata.json +10 -0
@@ -26,7 +26,7 @@ const ForestPlot = ({
26
26
  handleTooltipMouseOver,
27
27
  forestPlotRightLabelRef
28
28
  }: ForestPlotProps) => {
29
- const { rawData: data, updateConfig } = useContext<ChartContext>(ConfigContext)
29
+ const { transformedData: data, updateConfig } = useContext<ChartContext>(ConfigContext)
30
30
  const { forestPlot } = config as ChartConfig
31
31
  const labelPosition = config.xAxis.tickWidthMax + 10
32
32
  const [initialLogTicksSet, setInitialLogTicks] = useState(false)
@@ -90,7 +90,9 @@ const ForestPlot = ({
90
90
  }
91
91
  }, [config.forestPlot.type])
92
92
 
93
- const pooledData = config.data.find(d => d[config.xAxis.dataKey] === config.forestPlot.pooledResult.column)
93
+ const pooledData = data.find(d => d[config.xAxis.dataKey] === config.forestPlot.pooledResult.column)
94
+ const [plotStart, plotEnd] = [...xScale.range()].sort((a, b) => a - b)
95
+ const plotWidth = plotEnd - plotStart
94
96
 
95
97
  const regressionPoints = pooledData
96
98
  ? [
@@ -112,12 +114,12 @@ const ForestPlot = ({
112
114
 
113
115
  const topLine = [
114
116
  { x: 0, y: topMarginOffset },
115
- { x: width, y: topMarginOffset }
117
+ { x: plotEnd, y: topMarginOffset }
116
118
  ]
117
119
 
118
120
  const bottomLine = [
119
121
  { x: 0, y: height },
120
- { x: width, y: height }
122
+ { x: plotEnd, y: height }
121
123
  ]
122
124
 
123
125
  type Columns = {
@@ -289,30 +291,21 @@ const ForestPlot = ({
289
291
  {forestPlot.regression.description}
290
292
  </Text>
291
293
  )}
292
-
293
- <Bar
294
- key='forest-plot-tooltip-area'
295
- className='forest-plot-tooltip-area'
296
- width={width}
297
- height={height}
298
- fill={false ? 'red' : 'transparent'}
299
- fillOpacity={0.5}
300
- onMouseMove={e => handleTooltipMouseOver(e, data)}
301
- onMouseOut={handleTooltipMouseOff}
302
- />
303
294
  </Group>
304
295
  <Line
305
296
  from={topLine[0]}
306
297
  to={topLine[1]}
307
- style={{ stroke: 'black', strokeWidth: 2 }}
298
+ style={{ stroke: '#333', strokeWidth: 1 }}
308
299
  className='forestplot__top-line'
309
300
  />
310
- <Line
311
- from={bottomLine[0]}
312
- to={bottomLine[1]}
313
- style={{ stroke: 'black', strokeWidth: 2 }}
314
- className='forestplot__bottom-line'
315
- />
301
+ {config.xAxis.hideAxis && (
302
+ <Line
303
+ from={bottomLine[0]}
304
+ to={bottomLine[1]}
305
+ style={{ stroke: '#333', strokeWidth: 1 }}
306
+ className='forestplot__bottom-line'
307
+ />
308
+ )}
316
309
 
317
310
  {/* column data */}
318
311
  {columnsOnChart.map((column, colIndex) => {
@@ -400,6 +393,17 @@ const ForestPlot = ({
400
393
  {forestPlot.rightLabel}
401
394
  </Text>
402
395
  )}
396
+ <Bar
397
+ key='forest-plot-tooltip-area'
398
+ className='forest-plot-tooltip-area'
399
+ x={0}
400
+ width={width}
401
+ height={height}
402
+ fill={false ? 'red' : 'transparent'}
403
+ fillOpacity={0.5}
404
+ onMouseMove={e => handleTooltipMouseOver(e, data)}
405
+ onMouseOut={handleTooltipMouseOff}
406
+ />
403
407
  </>
404
408
  )
405
409
  }
@@ -6,14 +6,14 @@
6
6
  margin-bottom: 0.5rem;
7
7
 
8
8
  .group-item .visx-legend-label {
9
- font-weight: 400;
10
9
  font-size: 0.889rem;
10
+ font-weight: 400;
11
11
  margin-bottom: 0.5rem;
12
12
  }
13
13
  .group-label {
14
- font-weight: 500;
15
14
  font-family: Nunito, sans-serif;
16
15
  font-size: 1rem;
16
+ font-weight: 500;
17
17
  }
18
18
  }
19
19
 
@@ -22,9 +22,9 @@
22
22
  grid-gap: 10px;
23
23
 
24
24
  &.group-item {
25
+ align-items: flex-start;
25
26
  display: flex;
26
27
  flex-direction: column;
27
- align-items: flex-start;
28
28
  }
29
29
 
30
30
  & .inactive {
@@ -32,9 +32,9 @@
32
32
  transition: 0.2s all;
33
33
  }
34
34
  & .highlighted {
35
+ border-radius: 1px;
35
36
  outline: 1px solid #005ea2;
36
37
  outline-offset: 5px;
37
- border-radius: 1px;
38
38
  }
39
39
  }
40
40
  }
@@ -17,7 +17,8 @@ import { FaStar } from 'react-icons/fa'
17
17
  import { Label } from '../../../types/Label'
18
18
  import { ColorScale, TransformedData } from '../../../types/ChartContext'
19
19
  import { ChartConfig } from '../../../types/ChartConfig'
20
- import _ from 'lodash'
20
+ import cloneDeep from 'lodash/cloneDeep'
21
+ import sortBy from 'lodash/sortBy'
21
22
  import { scaleSequential } from 'd3-scale'
22
23
  import { interpolateRgbBasis } from 'd3-interpolate'
23
24
  import { filterChartColorPalettes } from '@cdc/core/helpers/filterColorPalettes'
@@ -173,7 +174,7 @@ export const createFormatLabels =
173
174
 
174
175
  const sortVertical = labels =>
175
176
  legend.verticalSorted
176
- ? _.sortBy(_.cloneDeep(labels), label => {
177
+ ? sortBy(cloneDeep(labels), label => {
177
178
  const match = label.datum?.match(/-?\d+(\.\d+)?/)
178
179
  return match ? parseFloat(match[0]) : Number.MAX_SAFE_INTEGER
179
180
  })
@@ -4,6 +4,7 @@ import { describe, expect, it, vi, beforeAll } from 'vitest'
4
4
  import LinearChart from '../../LinearChart'
5
5
  import ConfigContext from '../../../ConfigContext'
6
6
  import { createMockChartContext } from './mockConfigContext'
7
+ import forestPlotConfig from '../../../../examples/feature/forest-plot/forest-plot.json'
7
8
 
8
9
  // Mock ResizeObserver
9
10
  vi.stubGlobal(
@@ -147,6 +148,82 @@ describe('LinearChart', () => {
147
148
  })
148
149
  expect(container).toBeTruthy()
149
150
  })
151
+
152
+ it('keeps forest plot lines inside the computed plot bounds at narrow and wide widths', () => {
153
+ const forestContextOverrides = {
154
+ transformedData: forestPlotConfig.data,
155
+ rawData: forestPlotConfig.data
156
+ }
157
+
158
+ const narrowRender = renderLinearChart(forestPlotConfig as any, forestContextOverrides, {
159
+ parentWidth: 320,
160
+ parentHeight: 500
161
+ })
162
+ const wideRender = renderLinearChart(forestPlotConfig as any, forestContextOverrides, {
163
+ parentWidth: 960,
164
+ parentHeight: 500
165
+ })
166
+
167
+ const narrowTopLine = narrowRender.container.querySelector('.forestplot__top-line')
168
+ const wideTopLine = wideRender.container.querySelector('.forestplot__top-line')
169
+ const narrowCiLine = narrowRender.container.querySelector('line[class^="line-"]')
170
+ const wideCiLine = wideRender.container.querySelector('line[class^="line-"]')
171
+
172
+ expect(narrowTopLine).toBeTruthy()
173
+ expect(wideTopLine).toBeTruthy()
174
+ expect(narrowCiLine).toBeTruthy()
175
+ expect(wideCiLine).toBeTruthy()
176
+
177
+ const narrowStart = Number(narrowTopLine?.getAttribute('x1'))
178
+ const narrowEnd = Number(narrowTopLine?.getAttribute('x2'))
179
+ const wideStart = Number(wideTopLine?.getAttribute('x1'))
180
+ const wideEnd = Number(wideTopLine?.getAttribute('x2'))
181
+
182
+ expect(narrowStart).toBe(0)
183
+ expect(narrowEnd).toBeLessThanOrEqual(320)
184
+ expect(wideStart).toBe(0)
185
+ expect(wideEnd).toBeLessThanOrEqual(960)
186
+ expect(wideEnd - wideStart).toBeGreaterThan(narrowEnd - narrowStart)
187
+
188
+ expect(Number(narrowCiLine?.getAttribute('x1'))).toBeGreaterThan(narrowStart)
189
+ expect(Number(narrowCiLine?.getAttribute('x2'))).toBeLessThanOrEqual(narrowEnd)
190
+ expect(Number(wideCiLine?.getAttribute('x1'))).toBeGreaterThan(wideStart)
191
+ expect(Number(wideCiLine?.getAttribute('x2'))).toBeLessThanOrEqual(wideEnd)
192
+ })
193
+
194
+ it('avoids rendering a duplicate manual bottom border when the forest plot x-axis is visible', () => {
195
+ const { container } = renderLinearChart(
196
+ forestPlotConfig as any,
197
+ {
198
+ transformedData: forestPlotConfig.data,
199
+ rawData: forestPlotConfig.data
200
+ },
201
+ { parentWidth: 800, parentHeight: 500 }
202
+ )
203
+
204
+ expect(container.querySelector('.forestplot__top-line')).toBeTruthy()
205
+ expect(container.querySelector('.forestplot__bottom-line')).toBeFalsy()
206
+ const bottomAxisLine = container.querySelector('.bottom-axis > line[stroke="#333"]')
207
+ expect(bottomAxisLine?.getAttribute('x1')).toBe('0')
208
+ })
209
+
210
+ it('renders forest plot rows from transformedData instead of rawData', () => {
211
+ const filteredData = forestPlotConfig.data.slice(0, 2)
212
+ const { container } = renderLinearChart(
213
+ forestPlotConfig as any,
214
+ {
215
+ transformedData: filteredData,
216
+ rawData: forestPlotConfig.data
217
+ },
218
+ { parentWidth: 800, parentHeight: 500 }
219
+ )
220
+
221
+ expect(container.querySelectorAll('.lower-ci')).toHaveLength(filteredData.length)
222
+ expect(container.querySelectorAll('line[class^="line-"]')).toHaveLength(filteredData.length)
223
+ expect(container.textContent).not.toContain(
224
+ String(forestPlotConfig.data[forestPlotConfig.data.length - 1]['Author(s) and Year'])
225
+ )
226
+ })
150
227
  })
151
228
 
152
229
  describe('axis rendering', () => {
@@ -124,6 +124,8 @@ export const createMockChartContext = (
124
124
  clean: (s: any) => s,
125
125
  formatTooltipsDate: (date: any) => String(date),
126
126
  legendId: 'test-legend',
127
+ rawData: config.data,
128
+ updateConfig: () => {},
127
129
  ...contextOverrides
128
130
  } as ChartContext
129
131
  }
@@ -18,8 +18,7 @@ import { Tooltip as ReactTooltip } from 'react-tooltip'
18
18
  import 'react-tooltip/dist/react-tooltip.css'
19
19
  import { Text } from '@visx/text'
20
20
  import { useTooltip, TooltipWithBounds } from '@visx/tooltip'
21
- import _ from 'lodash'
22
-
21
+ import ResizeObserver from 'resize-observer-polyfill'
23
22
  // CDC Components
24
23
  import { isDateScale } from '@cdc/core/helpers/cove/date'
25
24
  import ConfigContext from '../ConfigContext'
@@ -152,6 +151,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
152
151
  const [suffixWidth, setSuffixWidth] = useState(0)
153
152
  const [calculatedSvgHeight, setCalculatedSvgHeight] = useState<number | null>(null)
154
153
  const [axisUpdateKey, setAxisUpdateKey] = useState(0)
154
+ const [axisBottomSizeKey, setAxisBottomSizeKey] = useState(0)
155
155
  const [synchronizedXValue, setSynchronizedXValue] = useState<any>(null)
156
156
 
157
157
  // REFS
@@ -245,6 +245,17 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
245
245
  setAxisUpdateKey(prev => prev + 1)
246
246
  }, [data.length, xAxisDataMapped?.[0], xAxisDataMapped?.[xAxisDataMapped.length - 1]])
247
247
 
248
+ // Recompute heights when bottom axis size changes (e.g., font load or wrap).
249
+ useEffect(() => {
250
+ const axisBottomEl = axisBottomRef.current
251
+ if (!axisBottomEl) return
252
+ const observer = new ResizeObserver(() => {
253
+ setAxisBottomSizeKey(prev => prev + 1)
254
+ })
255
+ observer.observe(axisBottomEl)
256
+ return () => observer.disconnect()
257
+ }, [axisBottomRef.current])
258
+
248
259
  const { yScaleRight, hasRightAxis } = useRightAxis({ config, yMax, data })
249
260
 
250
261
  // State for computed y-axis width - allows re-render when horizontal bar label space is calculated
@@ -415,7 +426,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
415
426
 
416
427
  // Make sure the chart is visible if in the editor
417
428
  useEffect(() => {
418
- const element = document.querySelector('.isEditor')
429
+ const element = document.querySelector('.is-editor')
419
430
  if (element) {
420
431
  setAnimatedChart(true)
421
432
  }
@@ -498,7 +509,16 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
498
509
  const legendIsLeftOrRight =
499
510
  legend?.position !== 'top' && legend?.position !== 'bottom' && !isLegendWrapViewport(currentViewport)
500
511
  legendRef.current.style.transform = legendIsLeftOrRight ? `translateY(${topLabelOnGridlineHeight}px)` : 'none'
501
- }, [axisBottomRef.current, config, config.xAxis.brushActive, currentViewport, topYLabelRef.current, initialHeight])
512
+ }, [
513
+ axisBottomRef.current,
514
+ config,
515
+ config.xAxis.brushActive,
516
+ currentViewport,
517
+ topYLabelRef.current,
518
+ initialHeight,
519
+ parentWidth,
520
+ axisBottomSizeKey
521
+ ])
502
522
 
503
523
  useEffect(() => {
504
524
  if (!tooltipOpen) return
@@ -542,7 +562,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
542
562
  <svg
543
563
  ref={internalSvgRef}
544
564
  onMouseMove={onMouseMove}
545
- width={parentWidth + config.yAxis.rightAxisSize}
565
+ width={parentWidth}
546
566
  height={isNoDataAvailable ? 1 : calculatedSvgHeight ?? parentHeight}
547
567
  className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${
548
568
  debugSvg && 'debug'
@@ -834,7 +854,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
834
854
  <TooltipWithBounds
835
855
  ref={tooltipRef}
836
856
  key={Math.random()}
837
- className={'tooltip cdc-open-viz-module'}
857
+ className={'tooltip cove-visualization'}
838
858
  left={tooltipLeft}
839
859
  top={tooltipTop}
840
860
  >
@@ -172,8 +172,22 @@ const PieChart = React.forwardRef<SVGSVGElement, PieChartProps>((props, ref) =>
172
172
  const domainKeys = isPercentageMode ? dataKeys.filter(k => k !== labelForCalcArea) : dataKeys
173
173
  const numberOfKeys = domainKeys.length
174
174
 
175
- let palette = getPaletteColors(config, colorPalettes)
176
- palette = applyEnhancedColorDistribution(config, palette, numberOfKeys)
175
+ const orderedCustomColors = config.general?.palette?.customColorsOrdered
176
+ const customColors = config.general?.palette?.customColors
177
+ const shouldUseOrderedCustomColors = Array.isArray(orderedCustomColors) && orderedCustomColors.length > 0
178
+
179
+ let palette = shouldUseOrderedCustomColors ? orderedCustomColors : getPaletteColors(config, colorPalettes)
180
+ if (!shouldUseOrderedCustomColors && customColors?.length) {
181
+ palette = customColors
182
+ }
183
+
184
+ while (palette.length > 0 && palette.length < numberOfKeys) {
185
+ palette = palette.concat(palette)
186
+ }
187
+
188
+ palette = shouldUseOrderedCustomColors
189
+ ? palette.slice(0, numberOfKeys)
190
+ : applyEnhancedColorDistribution(config, palette, numberOfKeys)
177
191
 
178
192
  const unknownColor = isPercentageMode
179
193
  ? getComputedStyle(document.documentElement).getPropertyValue('--cool-gray-10').trim()
@@ -206,6 +220,7 @@ const PieChart = React.forwardRef<SVGSVGElement, PieChartProps>((props, ref) =>
206
220
  config.general?.palette?.name,
207
221
  config.general?.palette?.isReversed,
208
222
  config.general?.palette?.customColors,
223
+ config.general?.palette?.customColorsOrdered,
209
224
  config.palette
210
225
  ])
211
226
 
@@ -216,7 +231,7 @@ const PieChart = React.forwardRef<SVGSVGElement, PieChartProps>((props, ref) =>
216
231
 
217
232
  // Make sure the chart is visible if in the editor
218
233
  useEffect(() => {
219
- const element = document.querySelector('.isEditor')
234
+ const element = document.querySelector('.is-editor')
220
235
  if (element) {
221
236
  // parent element is visible
222
237
  setAnimatePie(true)
@@ -421,7 +436,7 @@ const PieChart = React.forwardRef<SVGSVGElement, PieChartProps>((props, ref) =>
421
436
  config.tooltips.opacity / 100
422
437
  }) !important`}</style>
423
438
  <TooltipWithBounds
424
- className={'tooltip cdc-open-viz-module'}
439
+ className={'tooltip cove-visualization'}
425
440
  left={tooltipLeft + centerX - radius}
426
441
  top={tooltipTop}
427
442
  >
@@ -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) => (
@@ -91,7 +91,7 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
91
91
  ? new Date(domain[domain.length - 1] as string | number).getTime()
92
92
  : new Date(region.to)
93
93
 
94
- const toFormatted = formatDate(config.xAxis.dateParseFormat, toRefDate)
94
+ const toFormatted = formatDate(config.xAxis.dateParseFormat, toRefDate, config.locale)
95
95
  const toDate = new Date(toFormatted)
96
96
  const fromDate = new Date(toDate)
97
97
  fromDate.setDate(fromDate.getDate() - previousDays)
@@ -99,7 +99,7 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
99
99
  let closestValue: unknown
100
100
 
101
101
  if (axisType === 'date') {
102
- const fromTime = new Date(formatDate(xAxis.dateParseFormat, fromDate)).getTime()
102
+ const fromTime = new Date(formatDate(xAxis.dateParseFormat, fromDate, config.locale)).getTime()
103
103
  closestValue = findClosestDate(fromTime, domain as number[], d => d)
104
104
  } else if (axisType === 'categorical') {
105
105
  const fromTime = fromDate.getTime()
@@ -151,7 +151,7 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
151
151
  // For date scale (band), we need to find the value in the domain
152
152
  // Parse the region date to match the format in the domain
153
153
  const date = new Date(region.from)
154
- const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date)).getTime()
154
+ const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date, config.locale)).getTime()
155
155
 
156
156
  // For band scales, find the closest date in the domain
157
157
  const domain = xScale.domain() as number[]
@@ -193,7 +193,7 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
193
193
  return from + Number(config.yAxis.size)
194
194
  }
195
195
  const date = new Date(region.from)
196
- const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date)).getTime()
196
+ const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date, config.locale)).getTime()
197
197
  let from = xScale(parsedDate)
198
198
  // For date-time, xScale returns correct position (no bandwidth), just add left padding
199
199
  return from + Number(config.yAxis.size)
@@ -252,7 +252,7 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
252
252
  }
253
253
  // For date scale (band), we need to find the value in the domain
254
254
  const date = new Date(region.from)
255
- const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date)).getTime()
255
+ const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date, config.locale)).getTime()
256
256
 
257
257
  // For band scales, find the closest date in the domain
258
258
  const domain = xScale.domain() as number[]
@@ -281,7 +281,7 @@ const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGrou
281
281
  from = calculatePreviousDaysFrom(region, 'date-time')
282
282
  } else {
283
283
  const date = new Date(region.from)
284
- const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date)).getTime()
284
+ const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date, config.locale)).getTime()
285
285
  from = xScale(parsedDate)
286
286
  }
287
287
  return from - getBarOffset()
@@ -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;
@@ -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
  }
@@ -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
  }
@@ -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,11 +143,15 @@ 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: '',
@@ -156,7 +161,7 @@ const createInitialState = () => {
156
161
  indexLabel: '',
157
162
  download: false,
158
163
  showVertical: true,
159
- dateDisplayFormat: '',
164
+ dateDisplayFormat: '%B %-d, %Y',
160
165
  showMissingDataLabel: true,
161
166
  showSuppressedSymbol: true,
162
167
  collapsible: true
@@ -196,7 +201,7 @@ const createInitialState = () => {
196
201
  side: false,
197
202
  topBottom: true
198
203
  },
199
- position: 'right',
204
+ position: 'top',
200
205
  orderedValues: [],
201
206
  patterns: {},
202
207
  patternField: ''
@@ -224,7 +229,7 @@ const createInitialState = () => {
224
229
  },
225
230
  labels: false,
226
231
  dataFormat: {
227
- commas: false,
232
+ commas: true,
228
233
  prefix: '',
229
234
  suffix: '',
230
235
  abbreviated: false,
@@ -235,9 +240,13 @@ const createInitialState = () => {
235
240
  filters: [],
236
241
  confidenceKeys: {},
237
242
  visual: {
238
- border: true,
239
- accent: true,
240
- background: true,
243
+ border: false,
244
+ borderColorTheme: false,
245
+ accent: false,
246
+ background: false,
247
+ hideBackgroundColor: false,
248
+ tp5Treatment: false,
249
+ tp5Background: false,
241
250
  verticalHoverLine: false,
242
251
  horizontalHoverLine: false,
243
252
  lineDatapointSymbol: 'none',
@@ -250,7 +259,7 @@ const createInitialState = () => {
250
259
  tooltips: {
251
260
  opacity: 90,
252
261
  singleSeries: false,
253
- dateDisplayFormat: ''
262
+ dateDisplayFormat: '%B %-d, %Y'
254
263
  },
255
264
  forestPlot: {
256
265
  startAt: 0,