@cdc/chart 4.25.5-1 → 4.25.6

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 (40) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcchart.js +32848 -27824
  3. package/index.html +130 -130
  4. package/package.json +2 -2
  5. package/src/CdcChartComponent.tsx +66 -26
  6. package/src/_stories/Chart.stories.tsx +99 -93
  7. package/src/_stories/ChartPrefixSuffix.stories.tsx +29 -32
  8. package/src/_stories/_mock/pie_calculated_area.json +417 -0
  9. package/src/components/BarChart/components/BarChart.Horizontal.tsx +4 -13
  10. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +3 -14
  11. package/src/components/BarChart/components/BarChart.Vertical.tsx +2 -8
  12. package/src/components/Brush/BrushChart.tsx +73 -0
  13. package/src/components/Brush/BrushController..tsx +39 -0
  14. package/src/components/DeviationBar.jsx +0 -1
  15. package/src/components/EditorPanel/EditorPanel.tsx +232 -147
  16. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +2 -2
  17. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +3 -2
  18. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +2 -1
  19. package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +8 -0
  20. package/src/components/EditorPanel/useEditorPermissions.ts +7 -4
  21. package/src/components/HoverLine/HoverLine.tsx +74 -0
  22. package/src/components/Legend/Legend.Suppression.tsx +47 -3
  23. package/src/components/Legend/helpers/index.ts +1 -1
  24. package/src/components/LineChart/index.tsx +3 -6
  25. package/src/components/LinearChart.tsx +161 -132
  26. package/src/components/PieChart/PieChart.tsx +58 -13
  27. package/src/data/initial-state.js +8 -5
  28. package/src/helpers/getNewRuntime.ts +35 -0
  29. package/src/helpers/getPiePercent.ts +22 -0
  30. package/src/helpers/getTransformedData.ts +22 -0
  31. package/src/helpers/tests/getNewRuntime.test.ts +82 -0
  32. package/src/helpers/tests/getPiePercent.test.ts +38 -0
  33. package/src/hooks/useRightAxis.ts +1 -1
  34. package/src/hooks/useScales.ts +8 -3
  35. package/src/hooks/useTooltip.tsx +24 -10
  36. package/src/store/chart.actions.ts +2 -6
  37. package/src/store/chart.reducer.ts +23 -23
  38. package/src/types/ChartConfig.ts +6 -3
  39. package/src/types/ChartContext.ts +0 -2
  40. package/src/components/ZoomBrush.tsx +0 -251
@@ -46,28 +46,51 @@ const PieChart = props => {
46
46
  const pivotColumns = Object.values(config.columns).filter(column => column.showInViz)
47
47
  const dataNeedsPivot = pivotColumns.length > 0
48
48
  const pivotKey = dataNeedsPivot ? 'pivotColumn' : undefined
49
+ const showPercentage = config.dataFormat.showPiePercent
50
+ const labelForCalcArea = 'Calculated Area'
51
+
49
52
  const _data = useMemo(() => {
53
+ let baseData = []
54
+
50
55
  if (dataNeedsPivot) {
51
- let newData = []
52
56
  const primaryColumn = config.yAxis.dataKey
53
57
  const additionalColumns = pivotColumns.map(column => column.name)
54
58
  const allColumns = [primaryColumn, ...additionalColumns]
55
59
  const columnToUpdate = config.xAxis.dataKey
60
+
56
61
  data.forEach(d => {
57
62
  allColumns.forEach(col => {
58
- const data = d[col]
59
- if (data) {
60
- newData.push({
61
- [pivotKey]: data,
63
+ const val = d[col]
64
+ if (val) {
65
+ baseData.push({
66
+ [pivotKey]: val,
62
67
  [columnToUpdate]: `${d[columnToUpdate]} - ${col}`
63
68
  })
64
69
  }
65
70
  })
66
71
  })
67
- return newData
72
+ } else {
73
+ baseData = [...data]
68
74
  }
69
- return data
70
- }, [data, dataNeedsPivot])
75
+
76
+ // === ADD "OTHER" IF PERCENT MODE IS ENABLED ===
77
+ if (showPercentage) {
78
+ const total = baseData.reduce((sum, d) => {
79
+ const val = parseFloat(d[config.runtime.yAxis.dataKey])
80
+ return sum + (isNaN(val) ? 0 : val)
81
+ }, 0)
82
+
83
+ if (total < 100) {
84
+ const remaining = 100 - total
85
+ baseData.push({
86
+ [config.runtime.xAxis.dataKey]: labelForCalcArea,
87
+ [config.runtime.yAxis.dataKey]: remaining
88
+ })
89
+ }
90
+ }
91
+
92
+ return baseData
93
+ }, [data, dataNeedsPivot, showPercentage, config])
71
94
 
72
95
  const _colorScale = useMemo(() => {
73
96
  if (dataNeedsPivot) {
@@ -78,15 +101,31 @@ const PieChart = props => {
78
101
  const numberOfKeys = Object.entries(keys).length
79
102
  let palette = config.customColors || colorPalettes[config.palette]
80
103
  palette = palette.slice(0, numberOfKeys)
81
-
82
104
  return scaleOrdinal({
83
105
  domain: Object.keys(keys),
84
106
  range: palette,
85
107
  unknown: null
86
108
  })
87
109
  }
110
+
111
+ if (showPercentage) {
112
+ const keys = {}
113
+ _data.forEach(d => {
114
+ keys[d[config.xAxis.dataKey]] = true
115
+ })
116
+ // take out Calculated Area so it falls back to `unknown`
117
+ const domainKeys = Object.keys(keys).filter(k => k !== labelForCalcArea)
118
+
119
+ const basePalette = (config.customColors || colorPalettes[config.palette]).slice(0, domainKeys.length)
120
+ const COOL_GRAY_90 = getComputedStyle(document.documentElement).getPropertyValue('--cool-gray-10').trim()
121
+ return scaleOrdinal({
122
+ domain: domainKeys,
123
+ range: basePalette,
124
+ unknown: COOL_GRAY_90
125
+ })
126
+ }
88
127
  return colorScale
89
- }, [colorScale, dataNeedsPivot])
128
+ }, [_data, dataNeedsPivot, colorScale])
90
129
 
91
130
  const triggerRef = useRef()
92
131
  const dataRef = useIntersectionObserver(triggerRef, {
@@ -130,9 +169,15 @@ const PieChart = props => {
130
169
  const roundTo = Number(config.dataFormat.roundTo) || 0
131
170
  // Calculate the percentage of the full circle (360 degrees)
132
171
  const degrees = ((arc.endAngle - arc.startAngle) * 180) / Math.PI
172
+ const valueFromData = parseFloat(arc.data[config.runtime.yAxis.dataKey])
173
+ const percentageToDisplay = showPercentage ? valueFromData : (degrees / 360) * 100
174
+
175
+ let roundedPercentage = percentageToDisplay.toFixed(roundTo) + '%'
176
+ // add missing pie part
177
+ if (arc.data[config.xAxis.dataKey] === labelForCalcArea && config.dataFormat.showPiePercent) {
178
+ roundedPercentage = '**'
179
+ }
133
180
 
134
- const percentageOfCircle = (degrees / 360) * 100
135
- const roundedPercentage = percentageOfCircle.toFixed(roundTo) + '%'
136
181
  return (
137
182
  <Group key={key} className={`slice-${key}`}>
138
183
  {/* ── the slice */}
@@ -238,7 +283,7 @@ const PieChart = props => {
238
283
  <AnimatedPie
239
284
  {...pie}
240
285
  getKey={d => d.data[config.runtime.xAxis.dataKey]}
241
- colorScale={_colorScale}
286
+ colorScale={_colorScale}
242
287
  onHover={handleTooltipMouseOver}
243
288
  onLeave={handleTooltipMouseOff}
244
289
  />
@@ -89,6 +89,11 @@ export default {
89
89
  topAxis: {
90
90
  hasLine: false
91
91
  },
92
+ brush: {
93
+ isActive: false,
94
+ isBrushing: false,
95
+ data: []
96
+ },
92
97
  isLegendValue: false,
93
98
  barThickness: 0.35,
94
99
  barHeight: 25,
@@ -120,7 +125,8 @@ export default {
120
125
  maxTickRotation: 0,
121
126
  padding: 5,
122
127
  showYearsOnce: false,
123
- sortByRecentDate: false
128
+ sortByRecentDate: false,
129
+ brushActive: false
124
130
  },
125
131
  table: {
126
132
  label: 'Data Table',
@@ -176,10 +182,7 @@ export default {
176
182
  position: 'right',
177
183
  orderedValues: []
178
184
  },
179
- brush: {
180
- height: 45,
181
- active: false
182
- },
185
+
183
186
  exclusions: {
184
187
  active: false,
185
188
  keys: []
@@ -0,0 +1,35 @@
1
+ import _ from 'lodash'
2
+
3
+ export const getNewRuntime = (visualizationConfig, newFilteredData) => {
4
+ const runtime = _.cloneDeep(visualizationConfig.runtime) || {}
5
+ runtime.series = []
6
+ runtime.seriesLabels = {}
7
+ runtime.seriesLabelsAll = []
8
+ const { filters, columns, dynamicSeriesType, dynamicSeriesLineType, xAxis } = visualizationConfig
9
+ if (newFilteredData?.length) {
10
+ const columnNames = Object.keys(newFilteredData[0])
11
+ columnNames.forEach(colName => {
12
+ const isNotXAxis = xAxis.dataKey !== colName
13
+ const isNotFiltered = !filters || !filters?.find(filter => filter.columnName === colName)
14
+ const noColConfiguration = !columns || Object.keys(columns).indexOf(colName) === -1
15
+ if (isNotXAxis && isNotFiltered && noColConfiguration) {
16
+ runtime.series.push({
17
+ dataKey: colName,
18
+ type: dynamicSeriesType,
19
+ lineType: dynamicSeriesLineType,
20
+ tooltip: true
21
+ })
22
+ }
23
+ })
24
+ }
25
+
26
+ runtime.seriesKeys = runtime.series
27
+ ? runtime.series.map(series => {
28
+ runtime.seriesLabels[series.dataKey] = series.name || series.label || series.dataKey
29
+ runtime.seriesLabelsAll.push(series.name || series.dataKey)
30
+ return series.dataKey
31
+ })
32
+ : []
33
+
34
+ return runtime
35
+ }
@@ -0,0 +1,22 @@
1
+ import _ from 'lodash'
2
+
3
+ export const getPiePercent = (data: Record<string, any>[], seriesKey: string): Record<string, any>[] => {
4
+ // getonly the numeric values for each seriesKey
5
+ const numericValues = data.map(row => _.toNumber(row[seriesKey])).filter(v => !Number.isNaN(v))
6
+
7
+ const total = numericValues.reduce((sum, v) => sum + v, 0)
8
+
9
+ return data.map(row => {
10
+ const raw = _.toNumber(row[seriesKey])
11
+ if (Number.isNaN(raw)) {
12
+ // skip non-numeric / undefined
13
+ return row
14
+ }
15
+
16
+ const pct = total === 0 ? 0 : (raw / total) * 100
17
+ return {
18
+ ...row,
19
+ [seriesKey]: pct
20
+ }
21
+ })
22
+ }
@@ -0,0 +1,22 @@
1
+ type DataRow = Record<string, any>
2
+
3
+ export const getTransformedData = ({
4
+ brushData,
5
+ filteredData,
6
+ excludedData,
7
+ clean
8
+ }: {
9
+ brushData: DataRow[]
10
+ filteredData: DataRow[]
11
+ excludedData: DataRow[]
12
+ clean: (data: DataRow[]) => DataRow[]
13
+ }): DataRow[] => {
14
+ const data =
15
+ Array.isArray(brushData) && brushData.length > 0
16
+ ? brushData
17
+ : Array.isArray(filteredData) && filteredData.length > 0
18
+ ? filteredData
19
+ : excludedData
20
+
21
+ return clean(data)
22
+ }
@@ -0,0 +1,82 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { getNewRuntime } from '../getNewRuntime'
3
+
4
+ describe('getNewRuntime', () => {
5
+ it('should return a runtime object with default values when no data is provided', () => {
6
+ const visualizationConfig = { runtime: {} }
7
+ const newFilteredData = null
8
+
9
+ const result = getNewRuntime(visualizationConfig, newFilteredData)
10
+
11
+ expect(result.series).toEqual([])
12
+ expect(result.seriesLabels).toEqual({})
13
+ expect(result.seriesLabelsAll).toEqual([])
14
+ expect(result.seriesKeys).toEqual([])
15
+ })
16
+
17
+ it('should populate runtime.series with valid series from newFilteredData', () => {
18
+ const visualizationConfig = {
19
+ runtime: {},
20
+ filters: [],
21
+ columns: {},
22
+ dynamicSeriesType: 'bar',
23
+ dynamicSeriesLineType: 'solid',
24
+ xAxis: { dataKey: 'x' }
25
+ }
26
+ const newFilteredData = [
27
+ { x: 1, y: 10, z: 20 },
28
+ { x: 2, y: 15, z: 25 }
29
+ ]
30
+
31
+ const result = getNewRuntime(visualizationConfig, newFilteredData)
32
+
33
+ expect(result.series).toEqual([
34
+ { dataKey: 'y', type: 'bar', lineType: 'solid', tooltip: true },
35
+ { dataKey: 'z', type: 'bar', lineType: 'solid', tooltip: true }
36
+ ])
37
+ expect(result.seriesKeys).toEqual(['y', 'z'])
38
+ expect(result.seriesLabels).toEqual({ y: 'y', z: 'z' })
39
+ expect(result.seriesLabelsAll).toEqual(['y', 'z'])
40
+ })
41
+
42
+ it('should exclude series keys that match filters or columns', () => {
43
+ const visualizationConfig = {
44
+ runtime: {},
45
+ filters: [{ columnName: 'y' }],
46
+ columns: { z: {} },
47
+ dynamicSeriesType: 'bar',
48
+ dynamicSeriesLineType: 'solid',
49
+ xAxis: { dataKey: 'x' }
50
+ }
51
+ const newFilteredData = [
52
+ { x: 1, y: 10, z: 20, w: 30 },
53
+ { x: 2, y: 15, z: 25, w: 35 }
54
+ ]
55
+
56
+ const result = getNewRuntime(visualizationConfig, newFilteredData)
57
+
58
+ expect(result.series).toEqual([{ dataKey: 'w', type: 'bar', lineType: 'solid', tooltip: true }])
59
+ expect(result.seriesKeys).toEqual(['w'])
60
+ expect(result.seriesLabels).toEqual({ w: 'w' })
61
+ expect(result.seriesLabelsAll).toEqual(['w'])
62
+ })
63
+
64
+ it('should handle empty newFilteredData gracefully', () => {
65
+ const visualizationConfig = {
66
+ runtime: {},
67
+ filters: [],
68
+ columns: {},
69
+ dynamicSeriesType: 'bar',
70
+ dynamicSeriesLineType: 'solid',
71
+ xAxis: { dataKey: 'x' }
72
+ }
73
+ const newFilteredData = []
74
+
75
+ const result = getNewRuntime(visualizationConfig, newFilteredData)
76
+
77
+ expect(result.series).toEqual([])
78
+ expect(result.seriesKeys).toEqual([])
79
+ expect(result.seriesLabels).toEqual({})
80
+ expect(result.seriesLabelsAll).toEqual([])
81
+ })
82
+ })
@@ -0,0 +1,38 @@
1
+ // getPiePercent.test.ts
2
+ import { getPiePercent } from '../getPiePercent'
3
+
4
+ describe('getPiePercent', () => {
5
+ it('cgets percentages for purely numeric strings', () => {
6
+ const data = [{ A: '1' }, { A: '3' }, { A: '6' }]
7
+ const result = getPiePercent(data, 'A')
8
+
9
+ // sum = 1 + 3 + 6 = 10
10
+ expect(result[0].A).toBeCloseTo((1 / 10) * 100) // 10%
11
+ expect(result[1].A).toBeCloseTo((3 / 10) * 100) // 30%
12
+ expect(result[2].A).toBeCloseTo((6 / 10) * 100) // 60%
13
+ })
14
+
15
+ it('shandle non numbers like "ABC', () => {
16
+ const data = [{ A: '1' }, { A: 'ABC' }, { A: '2' }]
17
+ const result = getPiePercent(data, 'A')
18
+
19
+ expect(result[0].A).toBeCloseTo((1 / 3) * 100)
20
+ expect(result[1].A).toBe('ABC')
21
+ expect(result[2].A).toBeCloseTo((2 / 3) * 100)
22
+ })
23
+
24
+ it('handles all-zero total by producing 0%', () => {
25
+ const data = [{ A: '0' }, { A: '0' }]
26
+ const result = getPiePercent(data, 'A')
27
+ expect(result[0].A).toBe(0)
28
+ expect(result[1].A).toBe(0)
29
+ })
30
+
31
+ it('leaves rows missing the key entirely unchanged', () => {
32
+ const data = [{ A: '2' }, { B: 'foo' }]
33
+ const result = getPiePercent(data, 'A')
34
+
35
+ expect(result[0].A).toBeCloseTo(100)
36
+ expect(result[1]).toEqual({ B: 'foo' })
37
+ })
38
+ })
@@ -2,7 +2,7 @@ import { scaleLinear } from '@visx/scale'
2
2
  import useReduceData from './useReduceData'
3
3
  import { TOP_PADDING } from './useScales'
4
4
 
5
- export default function useRightAxis({ config, yMax = 0, data = [], updateConfig }) {
5
+ export default function useRightAxis({ config, yMax = 0, data = [] }) {
6
6
  const hasRightAxis = config.visualizationType === 'Combo' && config.orientation === 'vertical'
7
7
  const rightSeriesKeys =
8
8
  config.series && config.series.filter(series => series.axis === 'Right').map(key => key.dataKey)
@@ -79,13 +79,18 @@ const useScales = (properties: useScaleProps) => {
79
79
  xScale = composeScaleBand(xAxisDataMappedSorted, [0, xMax], 1 - config.barThickness)
80
80
  }
81
81
 
82
+ // handle Linear scaled viz
83
+ if (config.xAxis.type === 'date' && !isHorizontal) {
84
+ const sorted = sortXAxisData(xAxisDataMapped, config.xAxis.sortByRecentDate)
85
+
86
+ xScale = composeScaleBand(sorted, [0, xMax], 1 - config.barThickness)
87
+ xScale.type = scaleTypes.BAND
88
+ }
89
+
82
90
  if (xAxis.type === 'date-time' || xAxis.type === 'continuous') {
83
91
  let xAxisMin = Math.min(...xAxisDataMapped.map(Number))
84
92
  let xAxisMax = Math.max(...xAxisDataMapped.map(Number))
85
93
  let paddingRatio = config.xAxis.padding ? config.xAxis.padding * 0.01 : 0
86
- if (config.brush.active) {
87
- paddingRatio = config.barThickness * 0.2
88
- }
89
94
 
90
95
  xAxisMin -= paddingRatio * (xAxisMax - xAxisMin)
91
96
  xAxisMax += visualizationType === 'Line' ? 0 : paddingRatio * (xAxisMax - xAxisMin)
@@ -1,5 +1,6 @@
1
1
  import { useContext } from 'react'
2
2
  // Local imports
3
+ import parse from 'html-react-parser'
3
4
  import ConfigContext from '../ConfigContext'
4
5
  import { type ChartContext } from '../types/ChartContext'
5
6
  import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
@@ -120,16 +121,25 @@ export const useTooltip = props => {
120
121
  const pieData = additionalChartData?.data ?? {}
121
122
  const startAngle = additionalChartData?.startAngle ?? 0
122
123
  const endAngle = additionalChartData?.endAngle ?? 0
124
+ const actualPieValue = Number(additionalChartData.data[config?.yAxis?.dataKey])
123
125
 
124
126
  const degrees = ((endAngle - startAngle) * 180) / Math.PI
125
127
  const pctOf360 = (degrees / 360) * 100
126
- const pctString = pctOf360.toFixed(roundTo) + '%'
128
+ const pctString = value => value.toFixed(roundTo) + '%'
129
+ const showPiePercent = config.dataFormat.showPiePercent || false
127
130
 
128
- tooltipItems.push(
129
- [config.xAxis.dataKey, pieData[config.xAxis.dataKey]],
130
- [config.runtime.yAxis.dataKey, formatNumber(pieData[config.runtime.yAxis.dataKey])],
131
- ['Percent', pctString]
132
- )
131
+ if (showPiePercent && pieData[config.xAxis.dataKey] === 'Calculated Area') {
132
+ tooltipItems.push(['', 'Calculated Area'])
133
+ } else {
134
+ tooltipItems.push(
135
+ [config.xAxis.dataKey, pieData[config.xAxis.dataKey]],
136
+ [
137
+ config.runtime.yAxis.dataKey,
138
+ showPiePercent ? pctString(actualPieValue) : formatNumber(pieData[config.runtime.yAxis.dataKey])
139
+ ],
140
+ showPiePercent ? [] : ['Percent', pctString(pctOf360)]
141
+ )
142
+ }
133
143
  }
134
144
 
135
145
  if (visualizationType === 'Forest Plot') {
@@ -188,6 +198,8 @@ export const useTooltip = props => {
188
198
  }
189
199
  })
190
200
  } else {
201
+ const dynamicSeries = config.series.find(s => s.dynamicCategory)
202
+
191
203
  // Show Only the Hovered Series in Tooltip
192
204
  const dataColumn = resolvedScaleValues[0]
193
205
  const [seriesKey, value] = findDataKeyByThreshold(y, dataColumn)
@@ -198,7 +210,7 @@ export const useTooltip = props => {
198
210
  tooltipItems.push([config.xAxis.dataKey, closestXScaleValue || xVal])
199
211
  const formattedValue = getFormattedValue(seriesKey, value, config, getAxisPosition)
200
212
  tooltipItems.push([seriesKey, formattedValue])
201
- } else {
213
+ } else if (dynamicSeries) {
202
214
  Object.keys(dataColumn).forEach(key => {
203
215
  tooltipItems.push([key, dataColumn[key]])
204
216
  })
@@ -546,7 +558,9 @@ export const useTooltip = props => {
546
558
  config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ` : ''
547
559
  )} ${config.xAxis.type === 'date' ? formattedDate : value}`}</li>
548
560
  )
549
-
561
+ if (visualizationType === 'Pie' && config.dataFormat.showPiePercent && value === 'Calculated Area') {
562
+ return <li className='tooltip-heading'>{`${capitalize('Calculated Area')} `}</li>
563
+ }
550
564
  if (key === config.xAxis.dataKey)
551
565
  return (
552
566
  <li className='tooltip-heading'>{`${capitalize(
@@ -571,14 +585,14 @@ export const useTooltip = props => {
571
585
  let newValue = label || value
572
586
  const style = displayGray ? { color: '#8b8b8a' } : {}
573
587
 
574
- if (index == 1 && config.dataFormat.onlyShowTopPrefixSuffix) {
588
+ if (index == 1 && config.yAxis?.inlineLabel) {
575
589
  newValue = `${config.dataFormat.prefix}${newValue}${config.dataFormat.suffix}`
576
590
  }
577
591
  const activeLabel = getSeriesNameFromLabel(key)
578
592
  const displayText = activeLabel ? `${activeLabel}: ${newValue}` : newValue
579
593
 
580
594
  return (
581
- <li style={style} className='tooltip-body'>
595
+ <li style={style} className='tooltip-body mb-1'>
582
596
  {displayText}
583
597
  </li>
584
598
  )
@@ -1,10 +1,6 @@
1
1
  import { DimensionsType } from '@cdc/core/types/Dimensions'
2
2
  import { ChartConfig } from '../types/ChartConfig'
3
-
4
- type Action<T, P = undefined, R = undefined> = {
5
- type: T
6
- payload?: P
7
- }
3
+ import { Action } from '@cdc/core/types/Action'
8
4
 
9
5
  // Action Types
10
6
  type SET_CONFIG = Action<'SET_CONFIG', ChartConfig>
@@ -34,7 +30,7 @@ type ChartActions =
34
30
  | SET_CONTAINER
35
31
  | SET_LOADED_EVENT
36
32
  | SET_DRAG_ANNOTATIONS
37
- | SET_BRUSH_CONFIG
38
33
  | SET_LOADING
34
+ | SET_BRUSH_CONFIG
39
35
 
40
36
  export default ChartActions
@@ -4,7 +4,28 @@ import { ChartConfig, type ViewportSize } from '../types/ChartConfig'
4
4
  import { DimensionsType } from '@cdc/core/types/Dimensions'
5
5
  import _ from 'lodash'
6
6
 
7
- export const getInitialState = (configObj: ChartConfig) => {
7
+ type ChartState = {
8
+ isLoading: boolean
9
+ config: ChartConfig
10
+ stateData: object[]
11
+ colorScale: Function
12
+ excludedData: object[]
13
+ filteredData: object[]
14
+ seriesHighlight: string[]
15
+ currentViewport: ViewportSize
16
+ dimensions: DimensionsType
17
+ container: HTMLElement | null
18
+ coveLoadedEventRan: boolean
19
+ isDraggingAnnotation: boolean
20
+ imageId: string
21
+ brushConfig: {
22
+ data: object[]
23
+ isActive: boolean
24
+ isBrushing: boolean
25
+ }
26
+ }
27
+
28
+ export const getInitialState = (configObj: ChartConfig): ChartState => {
8
29
  return {
9
30
  isLoading: true,
10
31
  config: defaults,
@@ -28,28 +49,7 @@ export const getInitialState = (configObj: ChartConfig) => {
28
49
  }
29
50
  }
30
51
 
31
- type State = {
32
- isLoading: boolean
33
- config: ChartConfig
34
- stateData: object[]
35
- colorScale: Function
36
- excludedData: object[]
37
- filteredData: object[]
38
- seriesHighlight: string[]
39
- currentViewport: ViewportSize
40
- dimensions: DimensionsType
41
- container: HTMLElement | null
42
- coveLoadedEventRan: boolean
43
- isDraggingAnnotation: boolean
44
- imageId: string
45
- brushConfig: {
46
- data: object[]
47
- isActive: boolean
48
- isBrushing: boolean
49
- }
50
- }
51
-
52
- export const reducer = (state: State, action: ChartActions) => {
52
+ export const reducer = (state: ChartState, action: ChartActions): ChartState => {
53
53
  switch (action.type) {
54
54
  case 'SET_LOADING':
55
55
  return { ...state, isLoading: action.payload }
@@ -16,6 +16,7 @@ import { Region } from '@cdc/core/types/Region'
16
16
  import { VizFilter } from '@cdc/core/types/VizFilter'
17
17
  import { type Annotation } from '@cdc/core/types/Annotation'
18
18
  import { Version } from '@cdc/core/types/Version'
19
+ import Footnotes from '@cdc/core/types/Footnotes'
19
20
 
20
21
  export type ViewportSize = 'xxs' | 'xs' | 'sm' | 'md' | 'lg'
21
22
  export type ChartColumns = Record<string, Column>
@@ -69,7 +70,7 @@ type DataFormat = {
69
70
  rightSuffix: string
70
71
  roundTo: number
71
72
  suffix: string
72
- onlyShowTopPrefixSuffix?: boolean
73
+ showPiePercent: boolean
73
74
  }
74
75
 
75
76
  type Exclusions = {
@@ -121,7 +122,8 @@ export type AllChartsConfig = {
121
122
  boxplot: BoxPlot
122
123
  brush: {
123
124
  active: boolean
124
- height: number
125
+ data: object[]
126
+ isBrushing: boolean
125
127
  }
126
128
  chartMessage: { noData?: string }
127
129
  color: string
@@ -140,7 +142,8 @@ export type AllChartsConfig = {
140
142
  exclusions: Exclusions
141
143
  filters: VizFilter[]
142
144
  filterBehavior: FilterBehavior
143
- footnotes: string
145
+ legacyFootnotes: string // this footnote functionality should be moved to the Footnotes component
146
+ footnotes: Footnotes
144
147
  forestPlot: ForestPlotConfigSettings
145
148
  formattedData: Object[] & { urlFiltered: boolean }
146
149
  heights: {
@@ -12,7 +12,6 @@ export type TransformedData = {
12
12
 
13
13
  type SharedChartContext = {
14
14
  animatedChart?: boolean
15
- brushConfig: { data: []; isBrushing: boolean; isActive: boolean }
16
15
  capitalize: (value: string) => string
17
16
  clean: Function
18
17
  colorScale?: ColorScale
@@ -31,7 +30,6 @@ type SharedChartContext = {
31
30
  legendIsolateValues?: string[]
32
31
  legendRef?: React.RefObject<HTMLDivElement>
33
32
  parentRef?: React.RefObject<HTMLDivElement>
34
- setBrushConfig: Function
35
33
  setLegendIsolateValues?: Function
36
34
  svgRef?: React.RefObject<SVGSVGElement>
37
35
  }