@cdc/chart 4.25.10 → 4.25.11

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 (85) hide show
  1. package/dist/{cdcchart-1a1724a1.es.js → cdcchart-dgT_1dIT.es.js} +136 -151
  2. package/dist/cdcchart.js +36258 -34658
  3. package/examples/feature/__data__/planet-example-data.json +1 -1
  4. package/examples/feature/boxplot/valid-boxplot.csv +38 -17
  5. package/examples/private/DEV-11825.json +573 -0
  6. package/examples/private/na.json +913 -0
  7. package/examples/private/test-data.csv +28 -0
  8. package/index.html +2 -121
  9. package/package.json +4 -4
  10. package/src/CdcChart.tsx +8 -11
  11. package/src/CdcChartComponent.tsx +256 -87
  12. package/src/_stories/Chart.Combo.stories.tsx +18 -0
  13. package/src/_stories/Chart.Forecast.stories.tsx +36 -0
  14. package/src/_stories/Chart.HTMLInDataTable.stories.tsx +520 -0
  15. package/src/_stories/Chart.Patterns.stories.tsx +2 -1
  16. package/src/_stories/Chart.PreserveDecimals.stories.tsx +220 -0
  17. package/src/_stories/Chart.SmallMultiples.stories.tsx +47 -0
  18. package/src/_stories/ChartAnnotation.stories.tsx +6 -3
  19. package/src/_stories/ChartBar.Editor.stories.tsx +3580 -0
  20. package/src/_stories/ChartEditor.Editor.stories.tsx +658 -0
  21. package/src/_stories/ChartEditor.stories.tsx +1 -2
  22. package/src/_stories/_mock/combo.json +451 -0
  23. package/src/_stories/_mock/editor-test-configs.json +376 -0
  24. package/src/_stories/_mock/editor-test-datasets.json +477 -0
  25. package/src/_stories/_mock/editor-tests/bar-chart-editor-test.json +255 -0
  26. package/src/_stories/_mock/editor-tests/bar-chart-general-test.json +267 -0
  27. package/src/_stories/_mock/editor-tests/bar-chart-test.json +237 -0
  28. package/src/_stories/_mock/forecast_combo_with_gaps.json +913 -0
  29. package/src/_stories/_mock/pie_config.json +257 -62
  30. package/src/_stories/_mock/small_multiples/small_multiples_bars.json +1944 -0
  31. package/src/_stories/_mock/small_multiples/small_multiples_big_data_bars.json +1114 -0
  32. package/src/_stories/_mock/small_multiples/small_multiples_lines.json +2646 -0
  33. package/src/_stories/_mock/small_multiples/small_multiples_lines_colors.json +1305 -0
  34. package/src/_stories/_mock/small_multiples/small_multiples_stacked_bars.json +1936 -0
  35. package/src/components/Annotations/components/findNearestDatum.ts +6 -41
  36. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -6
  37. package/src/components/AreaChart/index.tsx +1 -2
  38. package/src/components/BarChart/components/BarChart.Horizontal.tsx +4 -4
  39. package/src/components/BarChart/components/BarChart.Vertical.tsx +3 -2
  40. package/src/components/BoxPlot/helpers/index.ts +3 -3
  41. package/src/components/Brush/BrushChart.tsx +1 -1
  42. package/src/components/EditorPanel/EditorPanel.tsx +199 -190
  43. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +96 -111
  44. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +19 -1
  45. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +102 -55
  46. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +54 -49
  47. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +422 -0
  48. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +75 -21
  49. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  50. package/src/components/EditorPanel/editor-panel.scss +0 -20
  51. package/src/components/EditorPanel/useEditorPermissions.ts +7 -15
  52. package/src/components/Forecasting/Forecasting.tsx +139 -21
  53. package/src/components/Legend/Legend.Component.tsx +16 -9
  54. package/src/components/Legend/helpers/createFormatLabels.tsx +181 -181
  55. package/src/components/Legend/helpers/getLegendClasses.ts +0 -1
  56. package/src/components/LineChart/LineChartProps.ts +0 -3
  57. package/src/components/LineChart/helpers.ts +1 -1
  58. package/src/components/LineChart/index.tsx +36 -13
  59. package/src/components/LinearChart.tsx +75 -80
  60. package/src/components/Regions/components/Regions.tsx +3 -24
  61. package/src/components/Sankey/types/index.ts +1 -1
  62. package/src/components/SmallMultiples/SmallMultipleTile.tsx +198 -0
  63. package/src/components/SmallMultiples/SmallMultiples.css +32 -0
  64. package/src/components/SmallMultiples/SmallMultiples.tsx +271 -0
  65. package/src/components/SmallMultiples/index.ts +2 -0
  66. package/src/data/initial-state.js +13 -1
  67. package/src/helpers/buildForecastPaletteOptions.ts +0 -38
  68. package/src/helpers/getColorScale.ts +10 -0
  69. package/src/{hooks/useMinMax.ts → helpers/getMinMax.ts} +14 -7
  70. package/src/helpers/getYAxisAutoPadding.ts +53 -0
  71. package/src/helpers/smallMultiplesHelpers.ts +529 -0
  72. package/src/hooks/useProgrammaticTooltip.ts +96 -0
  73. package/src/hooks/useScales.ts +88 -34
  74. package/src/hooks/useSmallMultipleSynchronization.ts +59 -0
  75. package/src/hooks/useTooltip.tsx +60 -15
  76. package/src/scss/main.scss +1 -80
  77. package/src/store/chart.actions.ts +2 -0
  78. package/src/store/chart.reducer.ts +4 -0
  79. package/src/types/ChartConfig.ts +24 -6
  80. package/src/types/ChartContext.ts +3 -0
  81. package/src/_stories/_mock/pie_data.json +0 -218
  82. package/src/components/AreaChart/components/AreaChart.jsx +0 -109
  83. package/src/helpers/sort.ts +0 -7
  84. package/src/hooks/useActiveElement.js +0 -19
  85. package/src/hooks/useChartClasses.js +0 -41
@@ -13,6 +13,9 @@ import ConfigContext from '../ConfigContext'
13
13
  import { ChartConfig } from '../types/ChartConfig'
14
14
  import { ChartContext } from '../types/ChartContext'
15
15
  import _ from 'lodash'
16
+ import { getYAxisAutoPadding } from '../helpers/getYAxisAutoPadding'
17
+ import getMinMax from '../helpers/getMinMax'
18
+ import { countNumOfTicks } from '../helpers/countNumOfTicks'
16
19
 
17
20
  const scaleTypes = {
18
21
  TIME: 'time',
@@ -27,23 +30,84 @@ export const TOP_PADDING = 10
27
30
  type useScaleProps = {
28
31
  config: ChartConfig // standard chart config
29
32
  data: Object[] // standard data array
30
- max: number // maximum value from useMinMax hook
31
- min: number // minimum value from useMinMax hook
33
+ tableData: Object[] // table data for getMinMax
34
+ minValue: number // raw minimum value from data
35
+ maxValue: number // raw maximum value from data
36
+ existPositiveValue: boolean // whether data contains positive values
37
+ isAllLine: boolean // whether all series are line type
32
38
  xAxisDataMapped: Object[] // array of x axis date/category items
33
39
  xMax: number // chart svg width
34
40
  yMax: number // chart svg height
41
+ needsYAxisAutoPadding?: boolean // whether Y-axis needs auto padding for label overflow
42
+ currentViewport?: string // current viewport for tick calculation
35
43
  }
36
44
 
37
45
  const useScales = (properties: useScaleProps) => {
38
- let { xAxisDataMapped, xMax, yMax, min, max, config, data } = properties
39
-
40
- const { rawData, dimensions } = useContext<ChartContext>(ConfigContext)
46
+ let {
47
+ xAxisDataMapped,
48
+ xMax,
49
+ yMax,
50
+ config,
51
+ data,
52
+ tableData,
53
+ minValue,
54
+ maxValue,
55
+ existPositiveValue,
56
+ isAllLine,
57
+ needsYAxisAutoPadding,
58
+ currentViewport
59
+ } = properties
60
+
61
+ const context = useContext<ChartContext>(ConfigContext)
62
+ const { rawData, dimensions, convertLineToBarGraph = false } = context
41
63
 
42
64
  const [screenWidth] = dimensions
65
+ const isHorizontal = config.orientation === 'horizontal'
66
+ const { visualizationType, xAxis, forestPlot, runtime } = config
67
+ const isForestPlot = visualizationType === 'Forest Plot'
68
+
69
+ const minMaxProps = {
70
+ config,
71
+ minValue,
72
+ maxValue,
73
+ existPositiveValue,
74
+ data,
75
+ isAllLine,
76
+ tableData,
77
+ convertLineToBarGraph
78
+ }
79
+ let { min, max, leftMax, rightMax } = getMinMax(minMaxProps)
80
+
81
+ const yTickCount = countNumOfTicks({
82
+ axis: 'yAxis',
83
+ max,
84
+ runtime,
85
+ currentViewport,
86
+ isHorizontal,
87
+ data,
88
+ config,
89
+ min
90
+ })
91
+ const handleNumTicks = isForestPlot ? config.data.length : yTickCount
92
+
93
+ // Apply auto-padding if needed
94
+ if (needsYAxisAutoPadding && !isHorizontal) {
95
+ for (let i = 0; i < 3; i++) {
96
+ const scale = composeYScale({ min, max, yMax, config, leftMax })
97
+ const padding = getYAxisAutoPadding(scale, handleNumTicks, maxValue, minValue, config)
98
+ if (i === 0 || padding > 0) {
99
+ const adjustedConfig = { ...config, yAxis: { ...config.yAxis, scalePadding: padding, enablePadding: true } }
100
+ const result = getMinMax({ ...minMaxProps, config: adjustedConfig })
101
+ min = result.min
102
+ max = result.max
103
+ leftMax = result.leftMax
104
+ rightMax = result.rightMax
105
+ }
106
+ }
107
+ }
108
+
43
109
  const seriesDomain = config.runtime.barSeriesKeys || config.runtime.seriesKeys
44
110
  const xAxisType = config.runtime.xAxis.type
45
- const isHorizontal = config.orientation === 'horizontal'
46
- const { visualizationType, xAxis, forestPlot } = config
47
111
  const paddingRange = ['Area Chart', 'Forecasting'].includes(config.visualizationType) ? 1 : 1 - config.barThickness
48
112
  // define scales
49
113
  let xScale = null
@@ -59,7 +123,7 @@ const useScales = (properties: useScaleProps) => {
59
123
 
60
124
  // handle Horizontal bars
61
125
  if (isHorizontal) {
62
- xScale = composeXScale({ min: min * 1.03, ...properties })
126
+ xScale = composeXScale({ min: min * 1.03, max, xMax, config })
63
127
  xScale.type = config.yAxis.type === 'logarithmic' ? scaleTypes.LOG : scaleTypes.LINEAR
64
128
  yScale = getYScaleFunction(xAxisType, xAxisDataMapped)
65
129
  yScale.rangeRound([0, yMax])
@@ -69,7 +133,7 @@ const useScales = (properties: useScaleProps) => {
69
133
  // handle Vertical bars
70
134
  if (!isHorizontal) {
71
135
  xScale = composeScaleBand(xAxisDataMapped, [0, xMax], paddingRange)
72
- yScale = composeYScale(properties)
136
+ yScale = composeYScale({ min, max, yMax, config, leftMax })
73
137
  seriesScale = composeScaleBand(seriesDomain, [0, xScale.bandwidth()], 0)
74
138
  }
75
139
 
@@ -290,17 +354,29 @@ const useScales = (properties: useScaleProps) => {
290
354
  }
291
355
  }
292
356
  }
293
- return { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding, xScaleAnnotation }
357
+ return {
358
+ xScale,
359
+ yScale,
360
+ seriesScale,
361
+ g1xScale,
362
+ g2xScale,
363
+ xScaleNoPadding,
364
+ xScaleAnnotation,
365
+ min,
366
+ max,
367
+ leftMax,
368
+ rightMax
369
+ }
294
370
  }
295
371
 
296
372
  export default useScales
297
373
 
298
- export const getFirstDayOfMonth = ms => {
374
+ const getFirstDayOfMonth = ms => {
299
375
  const date = new Date(ms)
300
376
  return new Date(date.getFullYear(), date.getMonth(), 1).getTime()
301
377
  }
302
378
 
303
- export const dateFormatHasMonthButNoDays = dateFormat => {
379
+ const dateFormatHasMonthButNoDays = dateFormat => {
304
380
  return (
305
381
  (dateFormat.includes('%b') ||
306
382
  dateFormat.includes('%B') ||
@@ -352,28 +428,6 @@ export const getTickValues = (xAxisDataMapped, xScale, num, config) => {
352
428
  }
353
429
  }
354
430
 
355
- // Ensure that the last tick is shown for charts with a "Date (Linear Scale)" scale
356
- export const filterAndShiftLinearDateTicks = (config, axisProps, xAxisDataMapped, formatDate) => {
357
- let ticks = axisProps.ticks
358
- const filteredTickValues = getTicks(axisProps.scale, axisProps.numTicks)
359
- if (filteredTickValues.length < xAxisDataMapped.length) {
360
- let shift = 0
361
- const lastIdx = xAxisDataMapped.indexOf(filteredTickValues[filteredTickValues.length - 1])
362
- if (lastIdx < xAxisDataMapped.length - 1) {
363
- shift = !config.xAxis.sortByRecentDate
364
- ? xAxisDataMapped.length - 1 - lastIdx
365
- : xAxisDataMapped.indexOf(filteredTickValues[0]) * -1
366
- }
367
- ticks = filteredTickValues.map(value => {
368
- return axisProps.ticks[axisProps.ticks.findIndex(tick => tick.value === value) + shift]
369
- })
370
- }
371
- ticks.forEach((tick, i) => {
372
- tick.formattedValue = formatDate(tick.value, i, ticks)
373
- })
374
- return ticks
375
- }
376
-
377
431
  /// helper functions
378
432
  const composeXScale = ({ min, max, xMax, config }) => {
379
433
  // Adjust min value if using logarithmic scale
@@ -0,0 +1,59 @@
1
+ import { useContext } from 'react'
2
+ import ConfigContext from '../ConfigContext'
3
+
4
+ /**
5
+ * Custom hook to handle synchronized tooltips in small multiples.
6
+ * This hook provides mouse event handlers that coordinate tooltip display across multiple chart tiles.
7
+ *
8
+ * @param xMax - The maximum x coordinate of the chart area
9
+ * @param yMax - The maximum y coordinate of the chart area
10
+ * @param getXValueFromCoordinate - Function to convert pixel x-coordinate to data value
11
+ * @returns Object with onMouseMove and onMouseLeave handlers, or null if not in small multiples
12
+ */
13
+ export const useSmallMultipleSynchronization = (
14
+ xMax: number,
15
+ yMax: number,
16
+ getXValueFromCoordinate: (x: number) => any
17
+ ) => {
18
+ const { config, handleSmallMultipleHover } = useContext(ConfigContext)
19
+
20
+ // If not in small multiples mode, return null handlers
21
+ if (!handleSmallMultipleHover) {
22
+ return {
23
+ onMouseMove: null,
24
+ onMouseLeave: null
25
+ }
26
+ }
27
+
28
+ const yAxisSize = Number(config.yAxis.size || 0)
29
+
30
+ const onMouseMove = (event: any) => {
31
+ const svgRect = event.currentTarget.getBoundingClientRect()
32
+ const x = event.clientX - svgRect.left
33
+ const y = event.clientY - svgRect.top
34
+
35
+ // Only trigger synchronized tooltips when mouse is over the valid chart area
36
+ // (to the right of the Y-axis and within chart bounds)
37
+ const isOverChartArea = x >= yAxisSize && x <= yAxisSize + xMax && y >= 0 && y <= yMax
38
+
39
+ if (isOverChartArea) {
40
+ const xAxisValue = getXValueFromCoordinate(x - yAxisSize)
41
+ if (xAxisValue !== null && xAxisValue !== undefined) {
42
+ handleSmallMultipleHover(xAxisValue, y)
43
+ return
44
+ }
45
+ }
46
+
47
+ // If we're not over a valid area or couldn't get a value, hide synchronized tooltips
48
+ handleSmallMultipleHover(null, null)
49
+ }
50
+
51
+ const onMouseLeave = () => {
52
+ handleSmallMultipleHover(null, null)
53
+ }
54
+
55
+ return {
56
+ onMouseMove,
57
+ onMouseLeave
58
+ }
59
+ }
@@ -1,4 +1,4 @@
1
- import { useContext, useRef } from 'react'
1
+ import { useContext, useRef, useLayoutEffect } from 'react'
2
2
  // Local imports
3
3
  import parse from 'html-react-parser'
4
4
  import ConfigContext from '../ConfigContext'
@@ -28,6 +28,14 @@ export const useTooltip = props => {
28
28
  const { xScale, yScale, seriesScale, showTooltip, hideTooltip, interactionLabel = '' } = props
29
29
  const { xAxis, visualizationType, orientation, yAxis, runtime } = config
30
30
 
31
+ // Track the latest xScale in a ref to prevent stale closures
32
+ const xScaleRef = useRef(xScale)
33
+
34
+ // Update ref whenever xScale prop changes
35
+ useLayoutEffect(() => {
36
+ xScaleRef.current = xScale
37
+ }, [xScale])
38
+
31
39
  const Y_AXIS_SIZE = Number(config.yAxis.size || 0)
32
40
 
33
41
  // function handles only Single series hovered data tooltips
@@ -289,15 +297,15 @@ export const useTooltip = props => {
289
297
  */
290
298
  const getXValueFromCoordinateDate = x => {
291
299
  if (config.xAxis.type === 'categorical' || config.visualizationType === 'Combo') {
292
- let eachBand = xScale.step()
300
+ let eachBand = xScaleRef.current.step()
293
301
  let numerator = x
294
302
  const index = Math.floor(Number(numerator) / eachBand)
295
- return xScale.domain()[index - 1] // fixes off by 1 error
303
+ return xScaleRef.current.domain()[index - 1] // fixes off by 1 error
296
304
  }
297
305
 
298
306
  if (isDateScale(config.xAxis) && config.visualizationType !== 'Combo') {
299
307
  const bisectDate = bisector(d => parseDate(d[config.xAxis.dataKey])).left
300
- const x0 = xScale.invert(xScale(x))
308
+ const x0 = xScaleRef.current.invert(xScaleRef.current(x))
301
309
  const index = bisectDate(config.data, x0, 1)
302
310
  const val = parseDate(config.data[index - 1][config.xAxis.dataKey])
303
311
  return val
@@ -309,12 +317,12 @@ export const useTooltip = props => {
309
317
  * @function getXValueFromCoordinate
310
318
  * @returns {String} - the closest x value to the cursor position
311
319
  */
312
- const getXValueFromCoordinate = (x, isClick = false) => {
320
+ const getXValueFromCoordinate = x => {
313
321
  if (visualizationType === 'Pie') return
314
322
  if (orientation === 'horizontal') return
315
323
 
316
324
  // Check the type of x equal to point or if the type of xAxis is equal to continuous or date
317
- if (xScale.type === 'point' || xAxis.type === 'continuous' || isDateScale(xAxis)) {
325
+ if (xScaleRef.current.type === 'point' || xAxis.type === 'continuous' || isDateScale(xAxis)) {
318
326
  // Find the closest x value by calculating the minimum distance
319
327
  let closestX = null
320
328
  let minDistance = Number.MAX_VALUE
@@ -322,9 +330,11 @@ export const useTooltip = props => {
322
330
 
323
331
  const barThicknessOffset = config.xAxis.type === 'date' ? xScale.bandwidth() / 2 : 0
324
332
  data.forEach(d => {
325
- const xPosition = isDateScale(xAxis) ? xScale(parseDate(d[xAxis.dataKey])) : xScale(d[xAxis.dataKey])
333
+ const xPosition = isDateScale(xAxis)
334
+ ? xScaleRef.current(parseDate(d[xAxis.dataKey]))
335
+ : xScaleRef.current(d[xAxis.dataKey])
326
336
  let bwOffset = config.barHeight
327
- const distance = Math.abs(Number(xPosition + barThicknessOffset - offset + (isClick ? bwOffset * 2 : 0)))
337
+ const distance = Math.abs(Number(xPosition + barThicknessOffset - offset))
328
338
 
329
339
  if (distance <= minDistance) {
330
340
  minDistance = distance
@@ -334,16 +344,50 @@ export const useTooltip = props => {
334
344
  return closestX
335
345
  }
336
346
 
347
+ // For band scales, find which band the mouse x-coordinate falls within
337
348
  if (config.xAxis.type === 'categorical' || visualizationType === 'Combo') {
338
- let range = xScale.range()[1] - xScale.range()[0]
339
- let eachBand = range / (xScale.domain().length + 1)
349
+ const domain = xScaleRef.current.domain()
350
+ const bandwidth = xScaleRef.current.bandwidth()
340
351
 
341
- let numerator = x
342
- const index = Math.floor((Number(numerator) - eachBand / 2) / eachBand)
343
- return xScale.domain()[index] // fixes off by 1 error
352
+ let closestValue = null
353
+ let minDistance = Number.MAX_VALUE
354
+
355
+ domain.forEach(value => {
356
+ const bandStart = xScaleRef.current(value)
357
+ const bandCenter = bandStart + bandwidth / 2
358
+ const distance = Math.abs(x - bandCenter)
359
+
360
+ if (distance < minDistance) {
361
+ minDistance = distance
362
+ closestValue = value
363
+ }
364
+ })
365
+
366
+ return closestValue
344
367
  }
345
368
  }
346
369
 
370
+ /**
371
+ * Helper for converting data value to pixel coordinate (inverse of getXValueFromCoordinate)
372
+ * @function getCoordinateFromXValue
373
+ * @param {any} xAxisValue - X-axis data value (date, number, or category)
374
+ * @returns {number} - pixel coordinate for the data value
375
+ */
376
+ const getCoordinateFromXValue = xAxisValue => {
377
+ if (visualizationType === 'Pie') return 0
378
+ if (orientation === 'horizontal') return 0
379
+
380
+ // Convert data value to pixel coordinate using current xScale
381
+ let pixelX = isDateScale(xAxis) ? xScaleRef.current(parseDate(xAxisValue)) : xScaleRef.current(xAxisValue)
382
+
383
+ // For band scales (bar charts, categorical axes), add bandwidth offset to point to center of bar
384
+ if (xScaleRef.current.bandwidth) {
385
+ pixelX += xScaleRef.current.bandwidth() / 2
386
+ }
387
+
388
+ return pixelX
389
+ }
390
+
347
391
  const findClosest = (dataArray: [any, number][], mouseXorY) => {
348
392
  let dataColumn: Object
349
393
  dataArray.find(([d, xOrY]) => {
@@ -431,7 +475,7 @@ export const useTooltip = props => {
431
475
  const eventSvgCoords = localPoint(e)
432
476
  const { x } = eventSvgCoords
433
477
  if (!x) throw new Error('COVE: no x value in handleTooltipClick.')
434
- let closestXScaleValue = getXValueFromCoordinate(x, true)
478
+ let closestXScaleValue = getXValueFromCoordinate(x)
435
479
  let datum = config.data?.filter(item => item[config.xAxis.dataKey] === closestXScaleValue)
436
480
  if (!closestXScaleValue) throw new Error('COVE: no closest x scale value in handleTooltipClick')
437
481
  if (isDateScale(xAxis) && closestXScaleValue) {
@@ -502,7 +546,7 @@ export const useTooltip = props => {
502
546
  const dataWithXScale = dataToSearch.map(
503
547
  d => [d, seriesScale(d[dynamicSeries.dynamicCategory])] as [Object, number]
504
548
  )
505
- const xOffset = x - Y_AXIS_SIZE - xScale(closestXScaleValue)
549
+ const xOffset = x - Y_AXIS_SIZE - xScaleRef.current(closestXScaleValue)
506
550
  dataToSearch = [findClosest(dataWithXScale, xOffset)]
507
551
  }
508
552
  }
@@ -645,6 +689,7 @@ export const useTooltip = props => {
645
689
  getIncludedTooltipSeries,
646
690
  getXValueFromCoordinate,
647
691
  getXValueFromCoordinateDate,
692
+ getCoordinateFromXValue,
648
693
  handleTooltipClick,
649
694
  handleTooltipMouseOff,
650
695
  handleTooltipMouseOver,
@@ -1,52 +1,4 @@
1
- @import '@cdc/core/styles/accessibility';
2
-
3
- @mixin breakpoint($class) {
4
- @if $class == xs {
5
- @media (max-width: 767px) {
6
- @content;
7
- }
8
- } @else if $class == sm {
9
- @media (min-width: 768px) {
10
- @content;
11
- }
12
- } @else if $class == md {
13
- @media (min-width: 960px) {
14
- @content;
15
- }
16
- } @else if $class == lg {
17
- @media (min-width: 1300px) {
18
- @content;
19
- }
20
- } @else {
21
- @warn "Breakpoint mixin supports: xs, sm, md, lg";
22
- }
23
- }
24
-
25
- @mixin breakpointClass($class) {
26
- @if $class == xs {
27
- &.xs,
28
- &.xxs {
29
- @content;
30
- }
31
- } @else if $class == sm {
32
- &.sm,
33
- &.md,
34
- &.lg {
35
- @content;
36
- }
37
- } @else if $class == md {
38
- &.md,
39
- &.lg {
40
- @content;
41
- }
42
- } @else if $class == lg {
43
- &.lg {
44
- @content;
45
- }
46
- } @else {
47
- @warn "Breakpoint Class mixin supports: xs, sm, md, lg";
48
- }
49
- }
1
+ @import '@cdc/core/styles/v2/utils/breakpoints';
50
2
 
51
3
  .form-container {
52
4
  overflow-y: auto;
@@ -91,37 +43,6 @@
91
43
 
92
44
  border-radius: 3px;
93
45
 
94
- .checkbox-group {
95
- padding: 16px;
96
- border: 1px solid #c4c4c4;
97
- border-radius: 8px;
98
- margin-top: 8px;
99
- margin-bottom: 24px;
100
- }
101
-
102
- .loader {
103
- width: 100%;
104
- text-align: center;
105
- display: inline-block;
106
- animation: spin 1s linear infinite;
107
-
108
- &::before {
109
- content: '\21BB';
110
- }
111
- }
112
-
113
- .warning-icon {
114
- position: relative;
115
- top: 2px;
116
- width: 15px;
117
- height: 15px;
118
- margin-left: 5px;
119
-
120
- path {
121
- fill: #d8000c;
122
- }
123
- }
124
-
125
46
  .chart-description {
126
47
  margin-bottom: 20px;
127
48
  }
@@ -12,6 +12,7 @@ type SET_EXCLUDED_DATA = Action<'SET_EXCLUDED_DATA', object[]>
12
12
  type SET_FILTERED_DATA = Action<'SET_FILTERED_DATA', object[]>
13
13
  type SET_SERIES_HIGHLIGHT = Action<'SET_SERIES_HIGHLIGHT', string[]>
14
14
  type SET_VIEWPORT = Action<'SET_VIEWPORT', string>
15
+ type SET_VIZ_VIEWPORT = Action<'SET_VIZ_VIEWPORT', string>
15
16
  type SET_DIMENSIONS = Action<'SET_DIMENSIONS', DimensionsType>
16
17
  type SET_CONTAINER = Action<'SET_CONTAINER', object>
17
18
  type SET_LOADED_EVENT = Action<'SET_LOADED_EVENT', boolean>
@@ -26,6 +27,7 @@ type ChartActions =
26
27
  | SET_FILTERED_DATA
27
28
  | SET_SERIES_HIGHLIGHT
28
29
  | SET_VIEWPORT
30
+ | SET_VIZ_VIEWPORT
29
31
  | SET_DIMENSIONS
30
32
  | SET_CONTAINER
31
33
  | SET_LOADED_EVENT
@@ -13,6 +13,7 @@ type ChartState = {
13
13
  filteredData: object[]
14
14
  seriesHighlight: string[]
15
15
  currentViewport: ViewportSize
16
+ vizViewport: ViewportSize
16
17
  dimensions: DimensionsType
17
18
  container: HTMLElement | null
18
19
  coveLoadedEventRan: boolean
@@ -32,6 +33,7 @@ export const getInitialState = (configObj: ChartConfig): ChartState => {
32
33
  seriesHighlight:
33
34
  configObj && configObj?.legend?.seriesHighlight?.length ? [...configObj?.legend?.seriesHighlight] : [],
34
35
  currentViewport: 'lg',
36
+ vizViewport: 'lg',
35
37
  dimensions: [0, 0],
36
38
  container: null,
37
39
  coveLoadedEventRan: false,
@@ -61,6 +63,8 @@ export const reducer = (state: ChartState, action: ChartActions): ChartState =>
61
63
  return { ...state, seriesHighlight: action.payload }
62
64
  case 'SET_VIEWPORT':
63
65
  return { ...state, currentViewport: action.payload }
66
+ case 'SET_VIZ_VIEWPORT':
67
+ return { ...state, vizViewport: action.payload }
64
68
  case 'SET_DIMENSIONS':
65
69
  return { ...state, dimensions: action.payload }
66
70
  case 'SET_CONTAINER':
@@ -10,12 +10,13 @@ import { BoxPlot } from '@cdc/core/types/BoxPlot'
10
10
  import { General as CoreGeneral } from '@cdc/core/types/General'
11
11
 
12
12
  // Extend the core General type to include palette information for charts
13
- export type General = CoreGeneral & {
13
+ type General = CoreGeneral & {
14
14
  palette?: {
15
15
  name?: string
16
16
  version?: string
17
17
  isReversed?: boolean
18
18
  customColors?: string[]
19
+ customColorsOrdered?: string[]
19
20
  }
20
21
  }
21
22
  import { type Link } from './../components/Sankey/types'
@@ -30,7 +31,7 @@ import { Version } from '@cdc/core/types/Version'
30
31
  import Footnotes from '@cdc/core/types/Footnotes'
31
32
 
32
33
  export type ViewportSize = 'xxs' | 'xs' | 'sm' | 'md' | 'lg'
33
- export type ChartColumns = Record<string, Column>
34
+ type ChartColumns = Record<string, Column>
34
35
  export type ChartOrientation = 'vertical' | 'horizontal'
35
36
  export type VisualizationType =
36
37
  | 'Area Chart'
@@ -75,6 +76,7 @@ type DataFormat = {
75
76
  bottomSuffix: string
76
77
  commas: boolean
77
78
  prefix: string
79
+ preserveOriginalDecimals?: boolean
78
80
  rightCommas: boolean
79
81
  rightPrefix: string
80
82
  rightRoundTo: number
@@ -91,7 +93,7 @@ type Exclusions = {
91
93
  dateEnd: string
92
94
  }
93
95
 
94
- export type Legend = CoreLegend & {
96
+ type Legend = CoreLegend & {
95
97
  seriesHighlight: string[]
96
98
  unified: boolean
97
99
  hideSuppressionLink: boolean
@@ -192,8 +194,22 @@ export type AllChartsConfig = {
192
194
  runtimeDataUrl: string
193
195
  series: Series
194
196
  showLineSeriesLabels: boolean
197
+ showAreaUnderLine?: boolean
195
198
  showSidebar: boolean
196
199
  showTitle: boolean
200
+ smallMultiples?: {
201
+ mode?: 'by-column' | 'by-series'
202
+ tileColumn?: string
203
+ tilesPerRowDesktop?: number
204
+ tilesPerRowMobile?: number
205
+ tileOrderType?: 'asc' | 'desc' | 'custom'
206
+ tileOrder?: string[]
207
+ tileTitles?: { [key: string]: string }
208
+ independentYAxis?: boolean
209
+ colorMode?: 'same' | 'different'
210
+ synchronizedTooltips?: boolean
211
+ showAreaUnderLine?: boolean
212
+ }
197
213
  sortData: 'ascending' | 'descending'
198
214
  stackedAreaChartLineType: string
199
215
  suppressedData?: { label: string; icon: string; value: string }[]
@@ -217,6 +233,8 @@ export type AllChartsConfig = {
217
233
  visualizationSubType: string
218
234
  xAxis: Axis
219
235
  yAxis: Axis
236
+ hideXAxisLabel?: boolean
237
+ hideYAxisLabel?: boolean
220
238
  xScale: Function
221
239
  yScale: Function
222
240
  regions: Region[]
@@ -248,7 +266,7 @@ export type AllChartsConfig = {
248
266
  }
249
267
  } & MarkupConfig
250
268
 
251
- export type ForestPlotConfig = {
269
+ type ForestPlotConfig = {
252
270
  visualizationType: 'Forest Plot'
253
271
  forestPlot: ForestPlotConfigSettings
254
272
  } & AllChartsConfig &
@@ -263,7 +281,7 @@ export type LineChartConfig = {
263
281
  } & AllChartsConfig &
264
282
  MarkupConfig
265
283
 
266
- export type SankeyLink = {
284
+ type SankeyLink = {
267
285
  depth: number
268
286
  height: number
269
287
  id: string
@@ -284,7 +302,7 @@ type StoryNode = {
284
302
  segmentTextBefore: string
285
303
  }
286
304
 
287
- export type SankeyChartConfig = {
305
+ type SankeyChartConfig = {
288
306
  enableTooltips: boolean
289
307
  data: [
290
308
  {
@@ -16,7 +16,9 @@ type SharedChartContext = {
16
16
  clean: Function
17
17
  colorScale?: ColorScale
18
18
  config: ChartConfig
19
+ convertLineToBarGraph?: boolean
19
20
  currentViewport?: 'lg' | 'md' | 'sm' | 'xs' | 'xxs'
21
+ vizViewport?: 'lg' | 'md' | 'sm' | 'xs' | 'xxs'
20
22
  dashboardConfig?: DashboardConfig
21
23
  // process top level chart aria label for each chart type
22
24
  handleChartAriaLabels: (config: any) => string
@@ -33,6 +35,7 @@ type SharedChartContext = {
33
35
  parentRef?: React.RefObject<HTMLDivElement>
34
36
  setLegendIsolateValues?: Function
35
37
  svgRef?: React.RefObject<SVGSVGElement>
38
+ handleSmallMultipleHover?: (xAxisValue: any, yCoordinate: number) => void
36
39
  }
37
40
 
38
41
  // Line Chart Specific Context