@cdc/chart 4.23.11 → 4.24.2

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 (104) hide show
  1. package/dist/cdcchart.js +35740 -35027
  2. package/examples/feature/bar/additional-column-tooltip.json +446 -0
  3. package/examples/feature/bar/tall-data.json +98 -0
  4. package/examples/feature/forest-plot/forest-plot.json +63 -19
  5. package/examples/feature/forest-plot/linear.json +52 -3
  6. package/examples/feature/forest-plot/log.json +26 -0
  7. package/examples/feature/forest-plot/logarithmic.json +0 -35
  8. package/examples/feature/line/line-chart-preliminary.json +393 -0
  9. package/examples/feature/regions/index.json +9 -5
  10. package/examples/feature/scatterplot/scatterplot.json +272 -33
  11. package/index.html +10 -8
  12. package/package.json +2 -2
  13. package/src/CdcChart.tsx +70 -234
  14. package/src/ConfigContext.tsx +6 -0
  15. package/src/_stories/ChartEditor.stories.tsx +22 -0
  16. package/src/_stories/ChartLine.preliminary.tsx +19 -0
  17. package/src/_stories/_mock/pie_config.json +192 -0
  18. package/src/_stories/_mock/pie_data.json +218 -0
  19. package/src/_stories/_mock/preliminary_mock.json +346 -0
  20. package/src/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +2 -2
  21. package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +2 -26
  22. package/src/components/AreaChart/index.tsx +4 -0
  23. package/src/components/{BarChart.Horizontal.tsx → BarChart/components/BarChart.Horizontal.tsx} +8 -8
  24. package/src/components/{BarChart.StackedHorizontal.tsx → BarChart/components/BarChart.StackedHorizontal.tsx} +37 -7
  25. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +108 -0
  26. package/src/components/{BarChart.Vertical.tsx → BarChart/components/BarChart.Vertical.tsx} +53 -70
  27. package/src/components/BarChart/components/BarChart.jsx +39 -0
  28. package/src/components/{BarChartType.jsx → BarChart/components/BarChartType.jsx} +0 -2
  29. package/src/components/BarChart/components/context.tsx +13 -0
  30. package/src/components/BarChart/index.tsx +3 -0
  31. package/src/components/{BoxPlot.jsx → BoxPlot/BoxPlot.jsx} +10 -9
  32. package/src/components/BoxPlot/index.tsx +3 -0
  33. package/src/components/EditorPanel/EditorPanel.tsx +2776 -0
  34. package/src/components/EditorPanel/EditorPanelContext.ts +40 -0
  35. package/src/components/EditorPanel/components/PanelProps.ts +3 -0
  36. package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +148 -0
  37. package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx} +97 -167
  38. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +160 -0
  39. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +168 -0
  40. package/src/components/{Series.jsx → EditorPanel/components/Panels/Panel.Series.tsx} +4 -4
  41. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +297 -0
  42. package/src/components/EditorPanel/components/Panels/index.tsx +17 -0
  43. package/src/components/EditorPanel/components/panels.scss +72 -0
  44. package/src/components/EditorPanel/editor-panel.scss +739 -0
  45. package/src/components/EditorPanel/index.tsx +3 -0
  46. package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +34 -2
  47. package/src/components/{Forecasting.jsx → Forecasting/Forecasting.jsx} +1 -1
  48. package/src/components/Forecasting/index.tsx +3 -0
  49. package/src/components/ForestPlot/ForestPlot.tsx +254 -0
  50. package/src/components/ForestPlot/ForestPlotProps.ts +7 -0
  51. package/src/components/ForestPlot/index.tsx +1 -209
  52. package/src/components/Legend/Legend.Component.tsx +199 -0
  53. package/src/components/Legend/Legend.tsx +28 -0
  54. package/src/components/Legend/helpers/createFormatLabels.tsx +140 -0
  55. package/src/components/Legend/index.tsx +3 -0
  56. package/src/components/LineChart/LineChartProps.ts +29 -0
  57. package/src/components/LineChart/components/LineChart.Circle.tsx +147 -0
  58. package/src/components/LineChart/helpers.ts +45 -0
  59. package/src/components/LineChart/index.tsx +111 -23
  60. package/src/components/LinearChart.jsx +55 -72
  61. package/src/components/PairedBarChart.jsx +4 -2
  62. package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +93 -31
  63. package/src/components/PieChart/index.tsx +3 -0
  64. package/src/components/Regions/components/Regions.tsx +144 -0
  65. package/src/components/Regions/index.tsx +3 -0
  66. package/src/components/{ScatterPlot.jsx → ScatterPlot/ScatterPlot.jsx} +3 -3
  67. package/src/components/ScatterPlot/index.tsx +3 -0
  68. package/src/components/{SparkLine.jsx → Sparkline/SparkLine.jsx} +2 -2
  69. package/src/components/Sparkline/index.tsx +3 -0
  70. package/src/data/initial-state.js +10 -8
  71. package/src/helpers/abbreviateNumber.ts +17 -0
  72. package/src/helpers/computeMarginBottom.ts +55 -0
  73. package/src/helpers/filterData.ts +18 -0
  74. package/src/helpers/generateColorsArray.ts +8 -0
  75. package/src/helpers/getQuartiles.ts +30 -0
  76. package/src/helpers/handleChartAriaLabels.ts +19 -0
  77. package/src/helpers/handleLineType.ts +18 -0
  78. package/src/helpers/lineOptions.ts +18 -0
  79. package/src/helpers/sort.ts +7 -0
  80. package/src/helpers/tests/computeMarginBottom.test.ts +20 -0
  81. package/src/hooks/useBarChart.js +7 -6
  82. package/src/hooks/useHighlightedBars.js +1 -1
  83. package/src/hooks/useMinMax.ts +3 -3
  84. package/src/hooks/useScales.ts +19 -6
  85. package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +31 -25
  86. package/src/scss/main.scss +0 -3
  87. package/src/types/ChartConfig.ts +167 -23
  88. package/src/types/ChartContext.ts +34 -12
  89. package/src/types/ForestPlot.ts +7 -14
  90. package/src/types/Label.ts +7 -0
  91. package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
  92. package/src/ConfigContext.jsx +0 -5
  93. package/src/components/BarChart.StackedVertical.tsx +0 -91
  94. package/src/components/BarChart.jsx +0 -30
  95. package/src/components/EditorPanel.jsx +0 -3356
  96. package/src/components/ForestPlot/Readme.md +0 -0
  97. package/src/components/Legend.jsx +0 -310
  98. package/src/components/LineChart/LineChart.Circle.tsx +0 -105
  99. package/src/scss/LinearChart.scss +0 -0
  100. package/src/scss/editor-panel.scss +0 -745
  101. package/src/scss/legend.scss +0 -206
  102. package/src/scss/mixins.scss +0 -0
  103. package/src/scss/variables.scss +0 -1
  104. package/src/types/ChartProps.ts +0 -7
@@ -1,4 +1,4 @@
1
- import React, { useContext, useState, useEffect, useRef } from 'react'
1
+ import React, { useContext, useState, useEffect, useRef, useMemo } from 'react'
2
2
  import { animated, useTransition, interpolate } from 'react-spring'
3
3
  import chroma from 'chroma-js'
4
4
 
@@ -7,21 +7,34 @@ import { Pie } from '@visx/shape'
7
7
  import { Group } from '@visx/group'
8
8
  import { Text } from '@visx/text'
9
9
  import { useTooltip, TooltipWithBounds } from '@visx/tooltip'
10
+ import { colorPalettesChart as colorPalettes } from '@cdc/core/data/colorPalettes'
10
11
 
11
12
  // cove
12
- import ConfigContext from '../ConfigContext'
13
- import { useTooltip as useCoveTooltip } from '../hooks/useTooltip'
14
- import useIntersectionObserver from '../hooks/useIntersectionObserver'
13
+ import ConfigContext from '../../ConfigContext'
14
+ import { useTooltip as useCoveTooltip } from '../../hooks/useTooltip'
15
+ import useIntersectionObserver from '../../hooks/useIntersectionObserver'
16
+ import { handleChartAriaLabels } from '../../helpers/handleChartAriaLabels'
15
17
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
18
+ import LegendComponent from '../Legend/Legend.Component'
19
+ import { createFormatLabels } from '../Legend/helpers/createFormatLabels'
20
+ import { scaleOrdinal } from '@visx/scale'
16
21
 
17
22
  const enterUpdateTransition = ({ startAngle, endAngle }) => ({
18
23
  startAngle,
19
24
  endAngle
20
25
  })
21
26
 
27
+ type TooltipData = {
28
+ data: {
29
+ [key: string]: string | number
30
+ }
31
+ dataXPosition: number
32
+ dataYPosition: number
33
+ }
34
+
22
35
  const PieChart = props => {
23
- const { transformedData: data, config, dimensions, seriesHighlight, colorScale, formatNumber, currentViewport, handleChartAriaLabels, isEditor } = useContext(ConfigContext)
24
- const { tooltipData, showTooltip, hideTooltip, tooltipOpen, tooltipLeft, tooltipTop } = useTooltip()
36
+ const { transformedData: data, config, colorScale, currentViewport, dimensions, highlight, highlightReset, seriesHighlight } = useContext(ConfigContext)
37
+ const { tooltipData, showTooltip, hideTooltip, tooltipOpen, tooltipLeft, tooltipTop } = useTooltip<TooltipData>()
25
38
  const { handleTooltipMouseOver, handleTooltipMouseOff, TooltipListItem } = useCoveTooltip({
26
39
  xScale: false,
27
40
  yScale: false,
@@ -30,6 +43,50 @@ const PieChart = props => {
30
43
  })
31
44
  const [filteredData, setFilteredData] = useState(undefined)
32
45
  const [animatedPie, setAnimatePie] = useState(false)
46
+ const pivotColumns = Object.values(config.columns).filter(column => column.showInViz)
47
+ const dataNeedsPivot = pivotColumns.length > 0
48
+ const pivotKey = dataNeedsPivot ? 'pivotColumn' : undefined
49
+ const _data = useMemo(() => {
50
+ if (dataNeedsPivot) {
51
+ let newData = []
52
+ const primaryColumn = config.yAxis.dataKey
53
+ const additionalColumns = pivotColumns.map(column => column.name)
54
+ const allColumns = [primaryColumn, ...additionalColumns]
55
+ const columnToUpdate = config.xAxis.dataKey
56
+ data.forEach(d => {
57
+ allColumns.forEach(col => {
58
+ const data = d[col]
59
+ if (data) {
60
+ newData.push({
61
+ [pivotKey]: data,
62
+ [columnToUpdate]: `${d[columnToUpdate]} - ${col}`
63
+ })
64
+ }
65
+ })
66
+ })
67
+ return newData
68
+ }
69
+ return data
70
+ }, [data, dataNeedsPivot])
71
+
72
+ const _colorScale = useMemo(() => {
73
+ if (dataNeedsPivot) {
74
+ const keys = {}
75
+ _data.forEach(d => {
76
+ if (!keys[d[config.xAxis.dataKey]]) keys[d[config.xAxis.dataKey]] = true
77
+ })
78
+ const numberOfKeys = Object.entries(keys).length
79
+ let palette = config.customColors || colorPalettes[config.palette]
80
+ palette = palette.slice(0, numberOfKeys)
81
+
82
+ return scaleOrdinal({
83
+ domain: Object.keys(keys),
84
+ range: palette,
85
+ unknown: null
86
+ })
87
+ }
88
+ return colorScale
89
+ }, [colorScale, dataNeedsPivot])
33
90
 
34
91
  const triggerRef = useRef()
35
92
  const dataRef = useIntersectionObserver(triggerRef, {
@@ -87,24 +144,24 @@ const PieChart = props => {
87
144
  endAngle
88
145
  })
89
146
  )}
90
- fill={colorScale(arc.data[config.runtime.xAxis.dataKey])}
147
+ fill={_colorScale(arc.data[config.runtime.xAxis.dataKey])}
91
148
  onMouseEnter={e => handleTooltipMouseOver(e, { data: arc.data[config.runtime.xAxis.dataKey], arc })}
92
149
  onMouseLeave={e => handleTooltipMouseOff()}
93
150
  />
94
151
  </Group>
95
152
  )
96
153
  })}
97
- {transitions.map(({ item: arc, key }) => {
154
+ {transitions.map(({ item: arc, key }, i) => {
98
155
  const [centroidX, centroidY] = path.centroid(arc)
99
156
  const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.1
100
157
 
101
158
  let textColor = '#FFF'
102
- if (colorScale(arc.data[config.runtime.xAxis.dataKey]) && chroma.contrast(textColor, colorScale(arc.data[config.runtime.xAxis.dataKey])) < 3.5) {
159
+ if (_colorScale(arc.data[config.runtime.xAxis.dataKey]) && chroma.contrast(textColor, _colorScale(arc.data[config.runtime.xAxis.dataKey])) < 3.5) {
103
160
  textColor = '000'
104
161
  }
105
162
 
106
163
  return (
107
- <animated.g key={key}>
164
+ <animated.g key={`${key}${i}`}>
108
165
  {hasSpaceForLabel && (
109
166
  <Text style={{ fill: textColor }} x={centroidX} y={centroidY} dy='.33em' textAnchor='middle' pointerEvents='none'>
110
167
  {Math.round((((arc.endAngle - arc.startAngle) * 180) / Math.PI / 360) * 100) + '%'}
@@ -134,7 +191,7 @@ const PieChart = props => {
134
191
  if (seriesHighlight.length > 0 && config.legend.behavior !== 'highlight') {
135
192
  let newFilteredData = []
136
193
 
137
- data.forEach(d => {
194
+ _data.forEach(d => {
138
195
  if (seriesHighlight.indexOf(d[config.runtime.xAxis.dataKey]) !== -1) {
139
196
  newFilteredData.push(d)
140
197
  }
@@ -146,33 +203,38 @@ const PieChart = props => {
146
203
  }
147
204
  }, [seriesHighlight]) // eslint-disable-line
148
205
 
206
+ const createLegendLabels = createFormatLabels(config, [], _data, _colorScale)
207
+
149
208
  return (
150
- <ErrorBoundary component='PieChart'>
151
- <svg width={width} height={height} className={`animated-pie group ${config.animate === false || animatedPie ? 'animated' : ''}`} role='img' aria-label={handleChartAriaLabels(config)}>
152
- <Group top={centerY} left={centerX}>
153
- {/* prettier-ignore */}
154
- <Pie
155
- data={filteredData || data}
156
- pieValue={d => d[config.runtime.yAxis.dataKey]}
209
+ <>
210
+ <ErrorBoundary component='PieChart'>
211
+ <svg width={width} height={height} className={`animated-pie group ${config.animate === false || animatedPie ? 'animated' : ''}`} role='img' aria-label={handleChartAriaLabels(config)}>
212
+ <Group top={centerY} left={centerX}>
213
+ {/* prettier-ignore */}
214
+ <Pie
215
+ data={filteredData || _data}
216
+ pieValue={d => d[pivotKey || config.runtime.yAxis.dataKey]}
157
217
  pieSortValues={() => -1}
158
218
  innerRadius={radius - donutThickness}
159
219
  outerRadius={radius}
160
220
  >
161
221
  {pie => <AnimatedPie {...pie} getKey={d => d.data[config.runtime.xAxis.dataKey]}/>}
162
222
  </Pie>
163
- </Group>
164
- </svg>
165
- <div ref={triggerRef} />
166
- {tooltipData && Object.entries(tooltipData.data).length > 0 && tooltipOpen && showTooltip && tooltipData.dataYPosition && tooltipData.dataXPosition && (
167
- <>
168
- <style>{`.tooltip {background-color: rgba(255,255,255, ${config.tooltips.opacity / 100}) !important`}</style>
169
- <TooltipWithBounds key={Math.random()} className={'tooltip cdc-open-viz-module'} left={tooltipLeft} top={tooltipTop}>
170
- <ul>{typeof tooltipData === 'object' && Object.entries(tooltipData.data).map((item, index) => <TooltipListItem item={item} key={index} />)}</ul>
171
- </TooltipWithBounds>
172
- </>
173
- )}
174
- {/* <ReactTooltip id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`} variant='light' arrowColor='rgba(0,0,0,0)' className='tooltip' /> */}
175
- </ErrorBoundary>
223
+ </Group>
224
+ </svg>
225
+ <div ref={triggerRef} />
226
+ {tooltipData && Object.entries(tooltipData.data).length > 0 && tooltipOpen && showTooltip && tooltipData.dataYPosition && tooltipData.dataXPosition && (
227
+ <>
228
+ <style>{`.tooltip {background-color: rgba(255,255,255, ${config.tooltips.opacity / 100}) !important`}</style>
229
+ <TooltipWithBounds key={Math.random()} className={'tooltip cdc-open-viz-module'} left={tooltipLeft} top={tooltipTop}>
230
+ <ul>{typeof tooltipData === 'object' && Object.entries(tooltipData.data).map((item, index) => <TooltipListItem item={item} key={index} />)}</ul>
231
+ </TooltipWithBounds>
232
+ </>
233
+ )}
234
+ {/* <ReactTooltip id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`} variant='light' arrowColor='rgba(0,0,0,0)' className='tooltip' /> */}
235
+ </ErrorBoundary>
236
+ <LegendComponent config={config} colorScale={_colorScale} seriesHighlight={seriesHighlight} highlight={highlight} highlightReset={highlightReset} currentViewport={currentViewport} formatLabels={createLegendLabels} />
237
+ </>
176
238
  )
177
239
  }
178
240
 
@@ -0,0 +1,3 @@
1
+ import PieChart from './PieChart'
2
+
3
+ export default PieChart
@@ -0,0 +1,144 @@
1
+ import React, { useContext } from 'react'
2
+ import { ChartConfig } from '../../../types/ChartConfig'
3
+ import ConfigContext from '../../../ConfigContext'
4
+ import { ChartContext } from '../../../types/ChartContext'
5
+ import { Text } from '@visx/text'
6
+ import { Group } from '@visx/group'
7
+ import * as d3 from 'd3'
8
+ import { formatDate } from '@cdc/core/helpers/cove/date.js'
9
+
10
+ type RegionsProps = {
11
+ xScale: Function
12
+ yMax: number
13
+ barWidth?: number
14
+ totalBarsInGroup?: number
15
+ }
16
+
17
+ const Regions = ({ xScale, barWidth = 0, totalBarsInGroup = 1, yMax, handleTooltipMouseOff, handleTooltipMouseOver, handleTooltipClick, tooltipData, showTooltip, hideTooltip }: RegionsProps) => {
18
+ const { parseDate, config } = useContext<ChartContext>(ConfigContext)
19
+
20
+ const { runtime, regions, visualizationType, orientation, xAxis } = config
21
+
22
+ let from
23
+ let to
24
+ let width
25
+
26
+ if (regions && orientation === 'vertical') {
27
+ return regions.map(region => {
28
+ if (xAxis.type === 'date' && region.fromType !== 'Previous Days') {
29
+ from = xScale(parseDate(region.from).getTime())
30
+ to = xScale(parseDate(region.to).getTime())
31
+ width = to - from
32
+ }
33
+
34
+ if (xAxis.type === 'categorical') {
35
+ from = xScale(region.from)
36
+ to = xScale(region.to)
37
+ width = to - from
38
+ }
39
+
40
+ if ((visualizationType === 'Bar' || config.visualizationType === 'Combo') && xAxis.type === 'date') {
41
+ from = region.fromType !== 'Previous Days' ? xScale(parseDate(region.from).getTime()) - (barWidth * totalBarsInGroup) / 2 : null
42
+ to = region.toType !== 'Last Date' ? xScale(parseDate(region.to).getTime()) + (barWidth * totalBarsInGroup) / 2 : null
43
+
44
+ width = to - from
45
+ }
46
+
47
+ if ((visualizationType === 'Bar' || config.visualizationType === 'Combo') && config.xAxis.type === 'categorical') {
48
+ from = xScale(region.from)
49
+ to = xScale(region.to)
50
+ width = to - from
51
+ }
52
+
53
+ if (region.fromType === 'Previous Days') {
54
+ to = region.toType !== 'Last Date' ? xScale(parseDate(region.to).getTime()) + (barWidth * totalBarsInGroup) / 2 : null
55
+
56
+ let domain = xScale.domain()
57
+ let bisectDate = d3.bisector(d => d).left
58
+ let closestValue
59
+
60
+ let previousDays = Number(region.from)
61
+ let lastDate = region.toType === 'Last Date' ? domain[domain.length - 1] : region.to
62
+ let toDate = new Date(lastDate)
63
+
64
+ from = new Date(toDate.setDate(toDate.getDate() - previousDays)).getTime()
65
+ let targetValue = from
66
+
67
+ let index = bisectDate(domain, targetValue)
68
+ if (index === 0) {
69
+ closestValue = domain[0]
70
+ } else if (index === domain.length) {
71
+ closestValue = domain[domain.length - 1]
72
+ } else {
73
+ let d0 = domain[index - 1]
74
+ let d1 = domain[index]
75
+ closestValue = targetValue - d0 > d1 - targetValue ? d1 : d0
76
+ }
77
+ from = Number(xScale(closestValue) - (visualizationType === 'Bar' || visualizationType === 'Combo' ? (barWidth * totalBarsInGroup) / 2 : 0))
78
+
79
+ width = to - from
80
+ }
81
+
82
+ // set the region max to the charts max range.
83
+ if (region.toType === 'Last Date') {
84
+ let domainValues = xScale.domain()
85
+ let lastDate = domainValues[domainValues.length - 1]
86
+ to = Number(xScale(lastDate) + (visualizationType === 'Bar' || visualizationType === 'Combo' ? (barWidth * totalBarsInGroup) / 2 : 0))
87
+ width = to - from
88
+ }
89
+
90
+ if (region.fromType === 'Previous Days' && xAxis.type === 'date' && xAxis.sortDates && config.visualizationType === 'Line') {
91
+ let domain = xScale.domain()
92
+ let previousDays = Number(region.from)
93
+ let to = region.toType === 'Last Date' ? formatDate(config.xAxis.dateParseFormat, domain[domain.length - 1]) : region.to
94
+ let toDate = new Date(to)
95
+ from = new Date(toDate.setDate(toDate.getDate() - previousDays)).getTime()
96
+ from = xScale(from)
97
+ to = xScale(parseDate(to))
98
+ width = to - from
99
+ }
100
+
101
+ if (!from) return null
102
+ if (!to) return null
103
+
104
+ const TopRegionBorderShape = () => {
105
+ return (
106
+ <path
107
+ stroke='#333'
108
+ d={`M${from} -5
109
+ L${from} 5
110
+ M${from} 0
111
+ L${to} 0
112
+ M${to} -5
113
+ L${to} 5`}
114
+ />
115
+ )
116
+ }
117
+
118
+ const HighlightedArea = () => {
119
+ return <rect x={from} y={0} width={width} height={yMax} fill={region.background} opacity={0.3} />
120
+ }
121
+
122
+ return (
123
+ <Group
124
+ className='regions regions-group--line'
125
+ left={config.visualizationType === 'Bar' || config.visualizationType === 'Combo' ? 0 : config?.visualizationType === 'Line' ? Number(runtime.yAxis.size) : 0}
126
+ key={region.label}
127
+ onMouseMove={handleTooltipMouseOver}
128
+ onMouseLeave={handleTooltipMouseOff}
129
+ handleTooltipClick={handleTooltipClick}
130
+ tooltipData={JSON.stringify(tooltipData)}
131
+ showTooltip={showTooltip}
132
+ >
133
+ <TopRegionBorderShape />
134
+ <HighlightedArea />
135
+ <Text x={from + width / 2} y={5} fill={region.color} verticalAnchor='start' textAnchor='middle'>
136
+ {region.label}
137
+ </Text>
138
+ </Group>
139
+ )
140
+ })
141
+ }
142
+ }
143
+
144
+ export default Regions
@@ -0,0 +1,3 @@
1
+ import Regions from './components/Regions'
2
+
3
+ export default Regions
@@ -1,8 +1,8 @@
1
1
  import React, { useContext } from 'react'
2
- import ConfigContext from '../ConfigContext'
2
+ import ConfigContext from '../../ConfigContext'
3
3
  import { Group } from '@visx/group'
4
4
 
5
- const CoveScatterPlot = ({ xScale, yScale, getXAxisData, getYAxisData }) => {
5
+ const ScatterPlot = ({ xScale, yScale, getXAxisData, getYAxisData }) => {
6
6
  const { colorScale, transformedData: data, config, formatNumber, seriesHighlight, colorPalettes } = useContext(ConfigContext)
7
7
 
8
8
  // TODO: copied from line chart should probably be a constant somewhere.
@@ -48,4 +48,4 @@ const CoveScatterPlot = ({ xScale, yScale, getXAxisData, getYAxisData }) => {
48
48
  </Group>
49
49
  )
50
50
  }
51
- export default CoveScatterPlot
51
+ export default ScatterPlot
@@ -0,0 +1,3 @@
1
+ import ScatterPlot from './ScatterPlot.jsx'
2
+
3
+ export default ScatterPlot
@@ -11,9 +11,9 @@ import { MarkerArrow } from '@visx/marker'
11
11
 
12
12
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
13
13
 
14
- import useReduceData from '../hooks/useReduceData'
14
+ import useReduceData from '../../hooks/useReduceData'
15
15
 
16
- import ConfigContext from '../ConfigContext'
16
+ import ConfigContext from '../../ConfigContext'
17
17
 
18
18
  const SparkLine = props => {
19
19
  const { width: parentWidth, height: parentHeight } = props
@@ -0,0 +1,3 @@
1
+ import SparkLine from './SparkLine.jsx'
2
+
3
+ export default SparkLine
@@ -29,6 +29,7 @@ export default {
29
29
  right: 5
30
30
  },
31
31
  suppressedData: [],
32
+ preliminaryData: [],
32
33
 
33
34
  yAxis: {
34
35
  hideAxis: false,
@@ -52,6 +53,7 @@ export default {
52
53
  rightAxisTickColor: '#333',
53
54
  numTicks: '',
54
55
  axisPadding: 0,
56
+ scalePadding: 10,
55
57
  tickRotation: 0,
56
58
  anchors: []
57
59
  },
@@ -128,7 +130,8 @@ export default {
128
130
  showDataTableLink: true,
129
131
  indexLabel: '',
130
132
  download: false,
131
- showVertical: true
133
+ showVertical: true,
134
+ dateDisplayFormat: ''
132
135
  },
133
136
  orientation: 'vertical',
134
137
  color: 'pinkpurple',
@@ -138,7 +141,7 @@ export default {
138
141
  legend: {
139
142
  hide: false,
140
143
  behavior: 'isolate',
141
- singleRow: false,
144
+ singleRow: true,
142
145
  colorCode: '',
143
146
  reverseLabelOrder: false,
144
147
  description: '',
@@ -190,7 +193,8 @@ export default {
190
193
  series: [],
191
194
  tooltips: {
192
195
  opacity: 90,
193
- singleSeries: false
196
+ singleSeries: false,
197
+ dateDisplayFormat: ''
194
198
  },
195
199
  forestPlot: {
196
200
  startAt: 0,
@@ -208,7 +212,7 @@ export default {
208
212
  },
209
213
  estimateField: '',
210
214
  estimateRadius: '',
211
- shape: '',
215
+ shape: 'square',
212
216
  rowHeight: 20,
213
217
  description: {
214
218
  show: true,
@@ -221,8 +225,8 @@ export default {
221
225
  location: 100
222
226
  },
223
227
  radius: {
224
- min: 1,
225
- max: 8,
228
+ min: 2,
229
+ max: 10,
226
230
  scalingColumn: ''
227
231
  },
228
232
  regression: {
@@ -233,11 +237,9 @@ export default {
233
237
  leftWidthOffset: 0,
234
238
  rightWidthOffset: 0,
235
239
  showZeroLine: false,
236
- hideDateCategoryCol: false,
237
240
  leftLabel: '',
238
241
  rightLabel: ''
239
242
  },
240
-
241
243
  area: {
242
244
  isStacked: false
243
245
  }
@@ -0,0 +1,17 @@
1
+ export const abbreviateNumber = num => {
2
+ let unit = ''
3
+ let absNum = Math.abs(num)
4
+
5
+ if (absNum >= 1e9) {
6
+ unit = 'B'
7
+ num = num / 1e9
8
+ } else if (absNum >= 1e6) {
9
+ unit = 'M'
10
+ num = num / 1e6
11
+ } else if (absNum >= 1e3) {
12
+ unit = 'K'
13
+ num = num / 1e3
14
+ }
15
+
16
+ return num + unit
17
+ }
@@ -0,0 +1,55 @@
1
+ import { ChartConfig, Legend } from '../types/ChartConfig'
2
+
3
+ export const computeMarginBottom = (config: ChartConfig, legend: Legend, currentViewport: string): string | number => {
4
+ const isLegendBottom = legend.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
5
+ const isHorizontal = config.orientation === 'horizontal'
6
+ const tickRotation = Number(config.xAxis.tickRotation) > 0 ? Number(config.xAxis.tickRotation) : 0
7
+ const isBrush = config?.brush?.active
8
+ const offset = 20
9
+ const brushHeight = config?.brush?.height
10
+ let bottom = 0
11
+ if (!isLegendBottom && isHorizontal && !config.yAxis.label) {
12
+ bottom = Number(config.xAxis.labelOffset)
13
+ }
14
+ if (!isLegendBottom && isHorizontal && config.yAxis.label && !config.isResponsiveTicks) {
15
+ bottom = Number(config.runtime.xAxis.size) + Number(config.xAxis.labelOffset)
16
+ }
17
+ if (!isLegendBottom && isHorizontal && config.yAxis.label && config.isResponsiveTicks) {
18
+ bottom = config.dynamicMarginTop + offset
19
+ }
20
+ if (!isLegendBottom && isHorizontal && !config.yAxis.label && config.isResponsiveTicks) {
21
+ bottom = config.dynamicMarginTop ? config.dynamicMarginTop - offset : Number(config.xAxis.labelOffset) - offset
22
+ }
23
+ if (!isLegendBottom && isHorizontal && config.yAxis.label && config.isResponsiveTicks) {
24
+ bottom = config.dynamicMarginTop ? config.dynamicMarginTop + offset : Number(config.xAxis.labelOffset)
25
+ }
26
+
27
+ if (!isHorizontal && !isLegendBottom && config.xAxis.label && tickRotation && !config.isResponsiveTicks) {
28
+ bottom = isBrush ? brushHeight + config.xAxis.tickWidthMax + -config.xAxis.size + config.xAxis.labelOffset + offset : config.xAxis.tickWidthMax + offset + -config.xAxis.size + config.xAxis.labelOffset
29
+ }
30
+ if (!isHorizontal && !isLegendBottom && !config.xAxis.label && tickRotation && !config.isResponsiveTicks) {
31
+ }
32
+ if (!isHorizontal && !isLegendBottom && !config.xAxis.label && tickRotation && !config.dynamicMarginTop && !config.isResponsiveTicks) {
33
+ bottom = isBrush ? config.xAxis.tickWidthMax + brushHeight + offset + -config.xAxis.size : 0
34
+ }
35
+
36
+ if (!isHorizontal && !isLegendBottom && config.xAxis.label && !tickRotation && !config.isResponsiveTicks) {
37
+ bottom = isBrush ? brushHeight + -config.xAxis.size + config.xAxis.labelOffset + offset : -config.xAxis.size + config.xAxis.labelOffset + offset
38
+ }
39
+ if (!isHorizontal && !isLegendBottom && config.xAxis.label && config.dynamicMarginTop && config.isResponsiveTicks) {
40
+ bottom = isBrush ? brushHeight + config.xAxis.labelOffset + -config.xAxis.size + config.xAxis.tickWidthMax : config.dynamicMarginTop + -config.xAxis.size + offset
41
+ }
42
+ if (!isHorizontal && !isLegendBottom && !config.xAxis.label && config.dynamicMarginTop && config.isResponsiveTicks) {
43
+ bottom = isBrush ? brushHeight + config.xAxis.labelOffset + -config.xAxis.size + config.xAxis.tickWidthMax : config.dynamicMarginTop + -config.xAxis.size - offset
44
+ }
45
+ if (!isHorizontal && !isLegendBottom && config.xAxis.label && !config.dynamicMarginTop && config.isResponsiveTicks) {
46
+ bottom = isBrush ? brushHeight + config.xAxis.labelOffset + -config.xAxis.size + 25 : config.xAxis.labelOffset + -config.xAxis.size + offset
47
+ }
48
+ if (!isHorizontal && !isLegendBottom && !config.xAxis.label && !config.dynamicMarginTop && config.isResponsiveTicks) {
49
+ bottom = -config.xAxis.size + offset + config.xAxis.labelOffset
50
+ }
51
+ if (!isHorizontal && !isLegendBottom && !config.xAxis.label && !tickRotation && !config.dynamicMarginTop && !config.isResponsiveTicks) {
52
+ bottom = isBrush ? brushHeight + -config.xAxis.size + config.xAxis.labelOffset : 0
53
+ }
54
+ return `${bottom}px`
55
+ }
@@ -0,0 +1,18 @@
1
+ export const filterData = (filters, data) => {
2
+ let filteredData: any[] = []
3
+
4
+ data.forEach(row => {
5
+ let add = true
6
+ filters
7
+ .filter(filter => filter.type !== 'url')
8
+ .forEach(filter => {
9
+ if (row[filter.columnName] != filter.active) {
10
+ add = false
11
+ }
12
+ })
13
+
14
+ if (add) filteredData.push(row)
15
+ })
16
+
17
+ return filteredData
18
+ }
@@ -0,0 +1,8 @@
1
+ import chroma from 'chroma-js'
2
+
3
+ export const generateColorsArray = (color = '#000000', special = false) => {
4
+ let colorObj = chroma(color)
5
+ let hoverColor = special ? colorObj.brighten(0.5).hex() : colorObj.saturate(1.3).hex()
6
+
7
+ return [color, hoverColor, colorObj.darken(0.3).hex()]
8
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Calculates the first quartile (q1) and third quartile (q3) from an array of integers or decimals.
3
+ *
4
+ * @param {Array} arr - The array of integers or decimals.
5
+ * @returns {Object} An object containing the q1 and q3 values.
6
+ */
7
+ export const getQuartiles = arr => {
8
+ arr.sort((a, b) => a - b)
9
+
10
+ // Calculate the index of the median value of the array
11
+ const medianIndex = Math.floor(arr.length / 2)
12
+
13
+ // Check if the length of the array is even or odd
14
+ const isEvenLength = arr.length % 2 === 0
15
+
16
+ // Split the array into two subarrays based on the median index
17
+ const q1Array = isEvenLength ? arr.slice(0, medianIndex) : arr.slice(0, medianIndex + 1)
18
+ const q3Array = isEvenLength ? arr.slice(medianIndex) : arr.slice(medianIndex + 1)
19
+
20
+ // Calculate the median of the first subarray to get the q1 value
21
+ const q1Index = Math.floor(q1Array.length / 2)
22
+ const q1 = isEvenLength ? (q1Array[q1Index - 1] + q1Array[q1Index]) / 2 : q1Array[q1Index]
23
+
24
+ // Calculate the median of the second subarray to get the q3 value
25
+ const q3Index = Math.floor(q3Array.length / 2)
26
+ const q3 = isEvenLength ? (q3Array[q3Index - 1] + q3Array[q3Index]) / 2 : q3Array[q3Index]
27
+
28
+ // Return an object containing the q1 and q3 values
29
+ return { q1, q3 }
30
+ }
@@ -0,0 +1,19 @@
1
+ export const handleChartAriaLabels = (state, testing = false) => {
2
+ if (testing) console.log(`handleChartAriaLabels Testing On:`, state) // eslint-disable-line
3
+ try {
4
+ if (!state.visualizationType) throw Error('handleChartAriaLabels: no visualization type found in state')
5
+ let ariaLabel = ''
6
+
7
+ if (state.visualizationType) {
8
+ ariaLabel += `${state.visualizationType} chart`
9
+ }
10
+
11
+ if (state.title && state.visualizationType) {
12
+ ariaLabel += ` with the title: ${state.title}`
13
+ }
14
+
15
+ return ariaLabel
16
+ } catch (e) {
17
+ console.error('COVE: ', e.message) // eslint-disable-line
18
+ }
19
+ }
@@ -0,0 +1,18 @@
1
+ export const handleLineType = lineType => {
2
+ switch (lineType) {
3
+ case 'dashed-sm':
4
+ return '5 5'
5
+ case 'Dashed Small':
6
+ return '5 5'
7
+ case 'dashed-md':
8
+ return '10 5'
9
+ case 'Dashed Medium':
10
+ return '10 5'
11
+ case 'dashed-lg':
12
+ return '15 5'
13
+ case 'Dashed Large':
14
+ return '15 5'
15
+ default:
16
+ return 0
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ export const lineOptions = [
2
+ {
3
+ value: 'Dashed Small',
4
+ key: 'dashed-sm'
5
+ },
6
+ {
7
+ value: 'Dashed Medium',
8
+ key: 'dashed-md'
9
+ },
10
+ {
11
+ value: 'Dashed Large',
12
+ key: 'dashed-lg'
13
+ },
14
+ {
15
+ value: 'Solid Line',
16
+ key: 'solid-line'
17
+ }
18
+ ]
@@ -0,0 +1,7 @@
1
+ export const sortAsc = (a, b) => {
2
+ return a.toString().localeCompare(b.toString(), 'en', { numeric: true })
3
+ }
4
+
5
+ export const sortDesc = (a, b) => {
6
+ return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
7
+ }