@cdc/chart 4.23.3 → 4.23.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/cdcchart.js +24397 -24193
  2. package/examples/feature/__data__/area-chart.json +56 -0
  3. package/examples/{planet-example-data.json → feature/__data__/planet-example-data.json} +16 -4
  4. package/examples/{area-chart.json → feature/area/area-chart.json} +70 -13
  5. package/examples/{horizontal-chart-max-increase.json → feature/bar/horizontal-chart-max-increase.json} +10 -4
  6. package/examples/{horizontal-chart.json → feature/bar/horizontal-chart.json} +10 -4
  7. package/examples/{horizontal-stacked-bar-chart.json → feature/bar/horizontal-stacked-bar-chart.json} +7 -3
  8. package/examples/{planet-chart-horizontal-example-config.json → feature/bar/planet-chart-horizontal-example-config.json} +8 -3
  9. package/examples/{planet-example-config.json → feature/bar/planet-example-config.json} +2 -2
  10. package/examples/{box-plot.json → feature/boxplot/boxplot.json} +7 -7
  11. package/examples/feature/boxplot/testing.csv +38 -0
  12. package/examples/feature/combo/combochart-categories_are_numbers .json +18 -0
  13. package/examples/{planet-combo-example-config.json → feature/combo/planet-combo-example-config.json} +1 -1
  14. package/examples/{planet-deviation-config.json → feature/deviation/planet-deviation-config.json} +2 -2
  15. package/examples/{planet-deviation-data.json → feature/deviation/planet-deviation-data.json} +9 -9
  16. package/examples/feature/filters/filter-testing.json +178 -0
  17. package/examples/feature/forecasting/case_date_example.csv +130 -0
  18. package/examples/feature/forecasting/effective_reproduction.json +202 -0
  19. package/examples/feature/forecasting/r_data.csv +130 -0
  20. package/examples/feature/line/line-chart.json +124 -0
  21. package/examples/{paired-bar-example.json → feature/paired-bar/paired-bar-example.json} +10 -4
  22. package/examples/{planet-pie-example-config.json → feature/pie/planet-pie-example-config.json} +2 -2
  23. package/examples/{scatterplot.json → feature/scatterplot/scatterplot.json} +1 -1
  24. package/examples/{case-rate-example-config.json → feature/tests-case-rate/case-rate-example-config.json} +2 -2
  25. package/examples/{covid-confidence-example-config.json → feature/tests-covid/covid-confidence-example-config.json} +8 -3
  26. package/examples/{covid-example-config.json → feature/tests-covid/covid-example-config.json} +7 -3
  27. package/examples/{cutoff-example-config.json → feature/tests-cutoff/cutoff-example-config.json} +7 -3
  28. package/examples/{date-exclusions-config.json → feature/tests-date-exclusions/date-exclusions-config.json} +2 -2
  29. package/examples/{example-bar-chart-nonnumeric.json → feature/tests-non-numerics/example-bar-chart-nonnumeric.json} +1 -1
  30. package/examples/{planet-pie-example-config-nonnumeric.json → feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json} +2 -2
  31. package/examples/{sparkline-chart-nonnumeric.json → feature/tests-non-numerics/sparkline-chart-nonnumeric.json} +1 -1
  32. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +145 -7
  33. package/examples/gallery/paired-bar/paired-bar-chart.json +1 -0
  34. package/index.html +73 -49
  35. package/package.json +2 -2
  36. package/src/CdcChart.jsx +111 -26
  37. package/src/components/AreaChart.jsx +105 -70
  38. package/src/components/BarChart.jsx +45 -28
  39. package/src/components/BoxPlot.jsx +28 -20
  40. package/src/components/DataTable.jsx +7 -6
  41. package/src/components/DeviationBar.jsx +2 -2
  42. package/src/components/EditorPanel.jsx +252 -193
  43. package/src/components/Legend.jsx +1 -1
  44. package/src/components/LineChart.jsx +10 -16
  45. package/src/components/LinearChart.jsx +30 -34
  46. package/src/components/PairedBarChart.jsx +6 -6
  47. package/src/components/PieChart.jsx +2 -4
  48. package/src/components/SparkLine.jsx +6 -42
  49. package/src/data/initial-state.js +7 -3
  50. package/src/index.jsx +2 -1
  51. package/src/scss/editor-panel.scss +15 -0
  52. package/src/scss/main.scss +8 -6
  53. package/examples/box-plot.csv +0 -5
  54. package/examples/dynamic-legends.json +0 -125
  55. package/examples/line-chart.json +0 -34
  56. package/examples/temp-example-config.json +0 -64
  57. package/examples/temp-example-data.json +0 -130
  58. package/src/components/Filters.jsx +0 -126
  59. /package/examples/{age-adjusted-rates.json → feature/__data__/age-adjusted-rates.json} +0 -0
  60. /package/examples/{new-data.csv → feature/__data__/new-data.csv} +0 -0
  61. /package/examples/{planet-example-data-max-increase.json → feature/__data__/planet-example-data-max-increase.json} +0 -0
  62. /package/examples/{Barchart_with_negative.json → feature/bar/Barchart_with_negative.json} +0 -0
  63. /package/examples/{example-bar-chart.json → feature/bar/example-bar-chart.json} +0 -0
  64. /package/examples/{stacked-vertical-bar-example-negative.json → feature/bar/stacked-vertical-bar-example-negative.json} +0 -0
  65. /package/examples/{stacked-vertical-bar-example.json → feature/bar/stacked-vertical-bar-example.json} +0 -0
  66. /package/examples/{box-plot-data.json → feature/boxplot/box-plot-data.json} +0 -0
  67. /package/examples/{newdata.json → feature/boxplot/boxplot-data.json} +0 -0
  68. /package/examples/{line-chart-max-increase.json → feature/line/line-chart-max-increase.json} +0 -0
  69. /package/examples/{paired-bar-data.json → feature/paired-bar/paired-bar-data.json} +0 -0
  70. /package/examples/{paired-bar-formatted.json → feature/paired-bar/paired-bar-formatted.json} +0 -0
  71. /package/examples/{scatterplot-continuous.csv → feature/scatterplot/scatterplot-continuous.csv} +0 -0
  72. /package/examples/{example-sparkline.json → feature/sparkline/example-sparkline.json} +0 -0
  73. /package/examples/{big-small-test-bar.json → feature/tests-big-small/big-small-test-bar.json} +0 -0
  74. /package/examples/{big-small-test-line.json → feature/tests-big-small/big-small-test-line.json} +0 -0
  75. /package/examples/{big-small-test-negative.json → feature/tests-big-small/big-small-test-negative.json} +0 -0
  76. /package/examples/{case-rate-example-data.json → feature/tests-case-rate/case-rate-example-data.json} +0 -0
  77. /package/examples/{covid-example-data-confidence.json → feature/tests-covid/covid-example-data-confidence.json} +0 -0
  78. /package/examples/{covid-example-data.json → feature/tests-covid/covid-example-data.json} +0 -0
  79. /package/examples/{cutoff-example-data.json → feature/tests-cutoff/cutoff-example-data.json} +0 -0
  80. /package/examples/{date-exclusions-data.json → feature/tests-date-exclusions/date-exclusions-data.json} +0 -0
  81. /package/examples/{example-combo-bar-nonnumeric.json → feature/tests-non-numerics/example-combo-bar-nonnumeric.json} +0 -0
  82. /package/examples/{line-chart-nonnumeric.json → feature/tests-non-numerics/line-chart-nonnumeric.json} +0 -0
  83. /package/examples/{planet-example-data-nonnumeric.json → feature/tests-non-numerics/planet-example-data-nonnumeric.json} +0 -0
  84. /package/examples/{stacked-vertical-bar-example-nonnumerics.json → feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json} +0 -0
@@ -128,7 +128,7 @@ const Legend = () => {
128
128
  return defaultLabels
129
129
  }
130
130
 
131
- const isBottomOrSmallViewport = config.legend.position === 'bottom' || currentViewport === 'sm' || currentViewport === 'xs'
131
+ const isBottomOrSmallViewport = config.legend.position === 'bottom' || currentViewport === 'sm' || currentViewport === 'xs' || currentViewport === 'xxs'
132
132
  const isHorizontal = config.orientation === 'horizontal'
133
133
  const marginTop = isBottomOrSmallViewport && isHorizontal ? `${config.runtime.xAxis.size}px` : '0px'
134
134
  const marginBottom = isBottomOrSmallViewport ? '15px' : '0px'
@@ -6,16 +6,13 @@ import { LinePath } from '@visx/shape'
6
6
  import { Text } from '@visx/text'
7
7
 
8
8
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
9
-
10
9
  import ConfigContext from '../ConfigContext'
11
-
12
10
  import useRightAxis from '../hooks/useRightAxis'
11
+ import isNumber from '@cdc/core/helpers/isNumber'
13
12
 
14
13
  export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData, xMax, yMax, seriesStyle = 'Line' }) {
15
- const { colorPalettes, transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, isNumber, cleanData, updateConfig, handleLineType } = useContext(ConfigContext)
16
- // Just do this once up front otherwise we end up
17
- // calling clean several times on same set of data (TT)
18
- const cleanedData = cleanData(data, config.xAxis.dataKey)
14
+ const { colorPalettes, transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, isNumber, updateConfig, handleLineType } = useContext(ConfigContext)
15
+
19
16
  const { yScaleRight } = useRightAxis({ config, yMax, data, updateConfig })
20
17
 
21
18
  const handleAxisFormating = (axis = 'left', label, value) => {
@@ -45,14 +42,13 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
45
42
  opacity={config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
46
43
  display={config.legend.behavior === 'highlight' || (seriesHighlight.length === 0 && !config.legend.dynamicLegend) || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
47
44
  >
48
- {cleanedData.map((d, dataIndex) => {
45
+ {data.map((d, dataIndex) => {
49
46
  // Find the series object from the config.series array that has a dataKey matching the seriesKey variable.
50
47
  const series = config.series.find(({ dataKey }) => dataKey === seriesKey)
51
48
  const { axis } = series
52
49
 
53
50
  const xAxisValue = config.runtime.xAxis.type === 'date' ? formatDate(parseDate(d[config.runtime.xAxis.dataKey])) : d[config.runtime.xAxis.dataKey]
54
51
  const yAxisValue = getYAxisData(d, seriesKey)
55
-
56
52
  const hasMultipleSeries = Object.keys(config.runtime.seriesLabels).length > 1
57
53
  const labeltype = axis === 'Right' ? 'rightLabel' : 'label'
58
54
  let label = config.runtime.yAxis[labeltype]
@@ -72,7 +68,8 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
72
68
  return (
73
69
  d[seriesKey] !== undefined &&
74
70
  d[seriesKey] !== '' &&
75
- d[seriesKey] !== null && (
71
+ d[seriesKey] !== null &&
72
+ isNumber(d[seriesKey]) && (
76
73
  <Group key={`series-${seriesKey}-point-${dataIndex}`}>
77
74
  {/* Render legend */}
78
75
  <Text
@@ -99,10 +96,9 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
99
96
  )
100
97
  )
101
98
  })}
102
-
103
99
  <LinePath
104
- curve={allCurves.curveLinear}
105
- data={cleanedData}
100
+ curve={allCurves[seriesData[0].lineType]}
101
+ data={data}
106
102
  x={d => xScale(getXAxisData(d))}
107
103
  y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey)))}
108
104
  stroke={
@@ -116,7 +112,6 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
116
112
  }
117
113
  strokeWidth={2}
118
114
  strokeOpacity={1}
119
- shapeRendering='geometricPrecision'
120
115
  strokeDasharray={lineType ? handleLineType(lineType) : 0}
121
116
  defined={(item, i) => {
122
117
  return item[config.runtime.seriesLabels[seriesKey]] !== '' && item[config.runtime.seriesLabels[seriesKey]] !== null && item[config.runtime.seriesLabels[seriesKey]] !== undefined
@@ -125,8 +120,8 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
125
120
  {config.animate && (
126
121
  <LinePath
127
122
  className='animation'
128
- curve={allCurves.curveLinear}
129
- data={cleanedData}
123
+ curve={seriesData.lineType}
124
+ data={data}
130
125
  x={d => xScale(getXAxisData(d))}
131
126
  y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey)))}
132
127
  stroke='#fff'
@@ -139,7 +134,6 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
139
134
  }}
140
135
  />
141
136
  )}
142
-
143
137
  {/* Render series labels at end if each line if selected in the editor */}
144
138
  {config.showLineSeriesLabels &&
145
139
  (config.runtime.lineSeriesKeys || config.runtime.seriesKeys).map(seriesKey => {
@@ -25,7 +25,7 @@ import { DeviationBar } from './DeviationBar'
25
25
 
26
26
  // TODO: Move scaling functions into hooks to manage complexity
27
27
  export default function LinearChart() {
28
- const { transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, formatNumber, handleChartAriaLabels, updateConfig, stringFormattingOptions } = useContext(ConfigContext)
28
+ const { transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, formatNumber, handleChartAriaLabels, updateConfig } = useContext(ConfigContext)
29
29
 
30
30
  let [width] = dimensions
31
31
  const { minValue, maxValue, existPositiveValue, isAllLine } = useReduceData(config, data)
@@ -83,7 +83,7 @@ export default function LinearChart() {
83
83
  min = enteredMinValue && isMinValid ? enteredMinValue : minValue
84
84
  max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
85
85
 
86
- // DEV-3263 - If Confidence Intervals in data, then need to account for increased height in max for YScale
86
+ // If Confidence Intervals in data, then need to account for increased height in max for YScale
87
87
  if (config.visualizationType === 'Bar' || config.visualizationType === 'Combo' || config.visualizationType === 'Deviation Bar') {
88
88
  let ciYMax = 0
89
89
  if (config.hasOwnProperty('confidenceKeys')) {
@@ -95,19 +95,7 @@ export default function LinearChart() {
95
95
  }
96
96
  }
97
97
 
98
- // DEV-3263 - If Confidence Intervals in data, then need to account for increased height in max for YScale
99
- if (config.visualizationType === 'Bar' || config.visualizationType === 'Combo') {
100
- let ciYMax = 0
101
- if (config.hasOwnProperty('confidenceKeys')) {
102
- let upperCIValues = data.map(function (d) {
103
- return d[config.confidenceKeys.upper]
104
- })
105
- ciYMax = Math.max.apply(Math, upperCIValues)
106
- if (ciYMax > max) max = ciYMax // bump up the max
107
- }
108
- }
109
-
110
- if ((config.visualizationType === 'Bar' || config.visualizationType === 'Deviation Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && min > 0) {
98
+ if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && min > 0) {
111
99
  min = 0
112
100
  }
113
101
  if (config.visualizationType === 'Combo' && isAllLine) {
@@ -120,6 +108,11 @@ export default function LinearChart() {
120
108
  }
121
109
  }
122
110
 
111
+ if (config.visualizationType === 'Deviation Bar' && min > 0) {
112
+ const isMinValid = Number(enteredMinValue) < Math.min(minValue, Number(config.xAxis.target))
113
+ min = enteredMinValue && isMinValid ? enteredMinValue : 0
114
+ }
115
+
123
116
  if (config.visualizationType === 'Line') {
124
117
  const isMinValid = enteredMinValue < minValue
125
118
  min = enteredMinValue && isMinValid ? enteredMinValue : minValue
@@ -161,6 +154,7 @@ export default function LinearChart() {
161
154
  if (min < 0) {
162
155
  // sets with negative data need more padding on the max
163
156
  max *= 1.2
157
+ min *= 1.2
164
158
  } else {
165
159
  max *= 1.1
166
160
  }
@@ -168,7 +162,7 @@ export default function LinearChart() {
168
162
 
169
163
  if (config.runtime.horizontal) {
170
164
  xScale = scaleLinear({
171
- domain: [min, max],
165
+ domain: [min * 1.03, max],
172
166
  range: [0, xMax]
173
167
  })
174
168
 
@@ -255,7 +249,8 @@ export default function LinearChart() {
255
249
  xScale = scaleLinear({
256
250
  domain: [min * leftOffset, Math.max(Number(config.xAxis.target), max)],
257
251
  range: [0, xMax],
258
- round: true
252
+ round: true,
253
+ nice: true
259
254
  })
260
255
  }
261
256
  // Handle Box Plots
@@ -274,8 +269,8 @@ export default function LinearChart() {
274
269
  }
275
270
 
276
271
  // check fences for max/min
277
- let lowestFence = Math.min(...config.boxplot.plots.map(item => item.columnMin))
278
- let highestFence = Math.max(...config.boxplot.plots.map(item => item.columnMax))
272
+ let lowestFence = Math.min(...config.boxplot.plots.map(item => item.columnLowerBounds))
273
+ let highestFence = Math.max(...config.boxplot.plots.map(item => item.columnUpperBounds))
279
274
 
280
275
  if (lowestFence < min) min = lowestFence
281
276
  if (highestFence > max) max = highestFence
@@ -296,16 +291,18 @@ export default function LinearChart() {
296
291
  }
297
292
  }
298
293
 
294
+ const shouldAbbreviate = true
295
+
299
296
  const handleLeftTickFormatting = tick => {
300
297
  if (config.runtime.yAxis.type === 'date') return formatDate(parseDate(tick))
301
- if (config.orientation === 'vertical') return formatNumber(tick, 'left')
298
+ if (config.orientation === 'vertical') return formatNumber(tick, 'left', shouldAbbreviate)
302
299
  return tick
303
300
  }
304
301
 
305
302
  const handleBottomTickFormatting = tick => {
306
303
  if (config.runtime.xAxis.type === 'date') return formatDate(tick)
307
- if (config.orientation === 'horizontal') return formatNumber(tick, 'left')
308
- if (config.xAxis.type === 'continuous') return formatNumber(tick, 'bottom')
304
+ if (config.orientation === 'horizontal') return formatNumber(tick, 'left', shouldAbbreviate)
305
+ if (config.xAxis.type === 'continuous') return formatNumber(tick, 'bottom', shouldAbbreviate)
309
306
  return tick
310
307
  }
311
308
 
@@ -347,11 +344,13 @@ export default function LinearChart() {
347
344
  return tickCount
348
345
  }
349
346
 
347
+ const svgRef = useRef()
348
+
350
349
  return isNaN(width) ? (
351
350
  <></>
352
351
  ) : (
353
352
  <ErrorBoundary component='LinearChart'>
354
- <svg width={width} height={height} className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''}`} role='img' aria-label={handleChartAriaLabels(config)} tabIndex={0}>
353
+ <svg width={width} height={height} className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''}`} role='img' aria-label={handleChartAriaLabels(config)} tabIndex={0} ref={svgRef}>
355
354
  {/* Higlighted regions */}
356
355
  {config.regions
357
356
  ? config.regions.map(region => {
@@ -457,6 +456,7 @@ export default function LinearChart() {
457
456
  })}
458
457
  {!config.yAxis.hideAxis && <Line from={props.axisFromPoint} to={config.runtime.horizontal ? { x: 0, y: Number(heightHorizontal) } : props.axisToPoint} stroke='#000' />}
459
458
  {yScale.domain()[0] < 0 && <Line from={{ x: props.axisFromPoint.x, y: yScale(0) }} to={{ x: xMax, y: yScale(0) }} stroke='#333' />}
459
+ {config.visualizationType === 'Bar' && config.orientation === 'horizontal' && xScale.domain()[0] < 0 && <Line from={{ x: xScale(0), y: 0 }} to={{ x: xScale(0), y: yMax }} stroke='#333' strokeWidth={2} />}
460
460
  <Text className='y-label' textAnchor='middle' verticalAnchor='start' transform={`translate(${-1 * config.runtime.yAxis.size}, ${axisCenter}) rotate(-90)`} fontWeight='bold' fill={config.yAxis.labelColor}>
461
461
  {props.label}
462
462
  </Text>
@@ -571,7 +571,7 @@ export default function LinearChart() {
571
571
  {!config.runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
572
572
  {!config.runtime.yAxis.hideLabel && (
573
573
  <Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
574
- {formatNumber(tick.formattedValue)}
574
+ {formatNumber(tick.value, 'left')}
575
575
  </Text>
576
576
  )}
577
577
  </Group>
@@ -604,7 +604,7 @@ export default function LinearChart() {
604
604
  {!config.runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
605
605
  {!config.runtime.yAxis.hideLabel && (
606
606
  <Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
607
- {tick.formattedValue}
607
+ {formatNumber(tick.value, 'left')}
608
608
  </Text>
609
609
  )}
610
610
  </Group>
@@ -625,11 +625,13 @@ export default function LinearChart() {
625
625
  )}
626
626
 
627
627
  {config.visualizationType === 'Deviation Bar' && <DeviationBar xScale={xScale} yScale={yScale} width={xMax} height={yMax} />}
628
-
629
- {/* Paired Bar chart */}
630
628
  {config.visualizationType === 'Paired Bar' && <PairedBarChart originalWidth={width} width={xMax} height={yMax} />}
629
+ {config.visualizationType === 'Scatter Plot' && <CoveScatterPlot xScale={xScale} yScale={yScale} getXAxisData={getXAxisData} getYAxisData={getYAxisData} />}
630
+ {config.visualizationType === 'Box Plot' && <CoveBoxPlot xScale={xScale} yScale={yScale} />}
631
+ {(config.visualizationType === 'Area Chart' || config.visualizationType === 'Combo') && <CoveAreaChart xScale={xScale} yScale={yScale} yMax={yMax} xMax={xMax} chartRef={svgRef} />}
631
632
 
632
633
  {/* Bar chart */}
634
+ {/* TODO: Make this just bar or combo? */}
633
635
  {config.visualizationType !== 'Line' && config.visualizationType !== 'Paired Bar' && config.visualizationType !== 'Box Plot' && config.visualizationType !== 'Area Chart' && config.visualizationType !== 'Scatter Plot' && config.visualizationType !== 'Deviation Bar' && (
634
636
  <>
635
637
  <BarChart xScale={xScale} yScale={yScale} seriesScale={seriesScale} xMax={xMax} yMax={yMax} getXAxisData={getXAxisData} getYAxisData={getYAxisData} animatedChart={animatedChart} visible={animatedChart} />
@@ -637,18 +639,12 @@ export default function LinearChart() {
637
639
  )}
638
640
 
639
641
  {/* Line chart */}
642
+ {/* TODO: Make this just line or combo? */}
640
643
  {config.visualizationType !== 'Bar' && config.visualizationType !== 'Paired Bar' && config.visualizationType !== 'Box Plot' && config.visualizationType !== 'Area Chart' && config.visualizationType !== 'Scatter Plot' && config.visualizationType !== 'Deviation Bar' && (
641
644
  <>
642
645
  <LineChart xScale={xScale} yScale={yScale} getXAxisData={getXAxisData} getYAxisData={getYAxisData} xMax={xMax} yMax={yMax} seriesStyle={config.series} />
643
646
  </>
644
647
  )}
645
-
646
- {/* Scatter Plot chart */}
647
- {config.visualizationType === 'Scatter Plot' && <CoveScatterPlot xScale={xScale} yScale={yScale} getXAxisData={getXAxisData} getYAxisData={getYAxisData} />}
648
-
649
- {/* Box Plot chart */}
650
- {config.visualizationType === 'Box Plot' && <CoveBoxPlot xScale={xScale} yScale={yScale} />}
651
- {config.visualizationType === 'Area Chart' && <CoveAreaChart xScale={xScale} yScale={yScale} yMax={yMax} xMax={xMax} />}
652
648
  </svg>
653
649
  <ReactTooltip id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`} variant='light' arrowColor='rgba(0,0,0,0)' className='tooltip' />
654
650
  <div className='animation-trigger' ref={triggerRef} />
@@ -61,7 +61,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
61
61
  return `<p>
62
62
  ${config.dataDescription.seriesKey}: ${groupOne.dataKey}<br/>
63
63
  ${config.xAxis.dataKey}: ${d[config.xAxis.dataKey]}<br/>
64
- ${label}${formatNumber(d[groupOne.dataKey])}
64
+ ${label}${formatNumber(d[groupOne.dataKey], 'left')}
65
65
  </p>`
66
66
  }
67
67
 
@@ -69,7 +69,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
69
69
  return `<p>
70
70
  ${config.dataDescription.seriesKey}: ${groupTwo.dataKey}<br/>
71
71
  ${config.xAxis.dataKey}: ${d[config.xAxis.dataKey]}<br/>
72
- ${label}${formatNumber(d[groupTwo.dataKey])}
72
+ ${label}${formatNumber(d[groupTwo.dataKey], 'left')}
73
73
  </p>`
74
74
  }
75
75
 
@@ -99,7 +99,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
99
99
  const totalheight = (Number(config.barSpace) + barHeight + borderWidth) * data.length
100
100
  config.heights.horizontal = totalheight
101
101
  // check if text fits inside of the bar including suffix/prefix,comma,fontSize ..etc
102
- const textWidth = getTextWidth(formatNumber(d[groupOne.dataKey]), `normal ${fontSize[config.fontSize]}px sans-serif`)
102
+ const textWidth = getTextWidth(formatNumber(d[groupOne.dataKey], 'left'), `normal ${fontSize[config.fontSize]}px sans-serif`)
103
103
  const textFits = textWidth < barWidth - 5 // minus padding dx(5)
104
104
 
105
105
  return (
@@ -123,7 +123,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
123
123
  />
124
124
  {config.yAxis.displayNumbersOnBar && displayBar && (
125
125
  <Text textAnchor={textFits ? 'start' : 'end'} dx={textFits ? 5 : -5} verticalAnchor='middle' x={halfWidth - barWidth} y={y + config.barHeight / 2} fill={textFits ? groupOne.labelColor : '#000'}>
126
- {formatNumber(d[groupOne.dataKey])}
126
+ {formatNumber(d[groupOne.dataKey], 'left')}
127
127
  </Text>
128
128
  )}
129
129
  </Group>
@@ -143,7 +143,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
143
143
  const totalheight = (Number(config.barSpace) + barHeight + borderWidth) * data.length
144
144
  config.heights.horizontal = totalheight
145
145
  // check if text fits inside of the bar including suffix/prefix,comma,fontSize ..etc
146
- const textWidth = getTextWidth(formatNumber(d[groupTwo.dataKey]), `normal ${fontSize[config.fontSize]}px sans-serif`)
146
+ const textWidth = getTextWidth(formatNumber(d[groupTwo.dataKey], 'left'), `normal ${fontSize[config.fontSize]}px sans-serif`)
147
147
  const isTextFits = textWidth < barWidth - 5 // minus padding dx(5)
148
148
 
149
149
  return (
@@ -174,7 +174,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
174
174
  />
175
175
  {config.yAxis.displayNumbersOnBar && displayBar && (
176
176
  <Text textAnchor={isTextFits ? 'end' : 'start'} dx={isTextFits ? -5 : 5} verticalAnchor='middle' x={halfWidth + barWidth} y={y + config.barHeight / 2} fill={isTextFits ? groupTwo.labelColor : '#000'}>
177
- {formatNumber(d[groupTwo.dataKey])}
177
+ {formatNumber(d[groupTwo.dataKey], 'left')}
178
178
  </Text>
179
179
  )}
180
180
  </Group>
@@ -18,9 +18,7 @@ const enterUpdateTransition = ({ startAngle, endAngle }) => ({
18
18
  })
19
19
 
20
20
  export default function PieChart() {
21
- const { transformedData: data, config, dimensions, seriesHighlight, colorScale, formatNumber, currentViewport, handleChartAriaLabels, cleanData } = useContext(ConfigContext)
22
-
23
- const cleanedData = cleanData(data, config.xAxis.dataKey)
21
+ const { transformedData: data, config, dimensions, seriesHighlight, colorScale, formatNumber, currentViewport, handleChartAriaLabels } = useContext(ConfigContext)
24
22
 
25
23
  const [filteredData, setFilteredData] = useState(undefined)
26
24
  const [animatedPie, setAnimatePie] = useState(false)
@@ -140,7 +138,7 @@ export default function PieChart() {
140
138
  <ErrorBoundary component='PieChart'>
141
139
  <svg width={width} height={height} className={`animated-pie group ${config.animate === false || animatedPie ? 'animated' : ''}`} role='img' aria-label={handleChartAriaLabels(config)}>
142
140
  <Group top={centerY} left={centerX}>
143
- <Pie data={filteredData || cleanedData} pieValue={d => d[config.runtime.yAxis.dataKey]} pieSortValues={() => -1} innerRadius={radius - donutThickness} outerRadius={radius}>
141
+ <Pie data={filteredData || data} pieValue={d => d[config.runtime.yAxis.dataKey]} pieSortValues={() => -1} innerRadius={radius - donutThickness} outerRadius={radius}>
144
142
  {pie => <AnimatedPie {...pie} getKey={d => d.data[config.runtime.xAxis.dataKey]} />}
145
143
  </Pie>
146
144
  </Group>
@@ -35,43 +35,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
35
35
  const isMaxValid = Number(enteredMaxValue) >= Number(maxValue)
36
36
  const isMinValid = Number(enteredMinValue) <= Number(minValue)
37
37
 
38
- // REMOVE bad data points from the data set
39
- // Examples: NA, N/A, "1,234", "anystring"
40
- // - if you dont call this on data into LineGroup below, for example
41
- // then entire data series are removed because of the defined statement
42
- // i.e. if a series has any bad data points the entire series wont plot
43
- const cleanData = (data, testing = false) => {
44
- let cleanedup = []
45
- if (testing) console.log('## Data to clean=', data)
46
- data.forEach(function (d, i) {
47
- let cleanedSeries = {}
48
- Object.keys(d).forEach(function (key) {
49
- if (key === 'Date') {
50
- // pass thru the dates
51
- cleanedSeries[key] = d[key]
52
- } else {
53
- // remove comma and dollar signs
54
- let tmp = d[key] !== null && d[key] !== '' ? d[key].replace(/[,$]/g, '') : ''
55
- if (testing) console.log('tmp no comma or $', tmp)
56
- if ((tmp !== '' && tmp !== null && !isNaN(tmp)) || (tmp !== '' && tmp !== null && /\d+\.?\d*/.test(tmp))) {
57
- cleanedSeries[key] = tmp
58
- } else {
59
- // return nothing to skip bad data point
60
- cleanedSeries[key] = '' // returning blank fixes broken chart draw
61
- }
62
- }
63
- })
64
- cleanedup.push(cleanedSeries)
65
- })
66
- if (testing) console.log('## cleanedData =', cleanedup)
67
- return cleanedup
68
- }
69
-
70
- // Just do this once up front otherwise we end up
71
- // calling clean several times on same set of data (TT)
72
- const cleanedData = cleanData(data, config.xAxis.dataKey)
73
-
74
- if (cleanedData) {
38
+ if (data) {
75
39
  let min = enteredMinValue && isMinValid ? enteredMinValue : minValue
76
40
  let max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
77
41
 
@@ -86,7 +50,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
86
50
  max += paddingValue
87
51
  }
88
52
 
89
- let xAxisDataMapped = cleanedData.map(d => getXAxisData(d))
53
+ let xAxisDataMapped = data.map(d => getXAxisData(d))
90
54
 
91
55
  if (config.runtime.horizontal) {
92
56
  xScale = scaleLinear({
@@ -128,7 +92,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
128
92
  return (
129
93
  <ErrorBoundary component='SparkLine'>
130
94
  <svg role='img' aria-label={handleChartAriaLabels(config)} width={width} height={height} className={'sparkline'} tabIndex={0}>
131
- {config.runtime.lineSeriesKeys.length > 0
95
+ {config.runtime.lineSeriesKeys?.length > 0
132
96
  ? config.runtime.lineSeriesKeys
133
97
  : config.runtime.seriesKeys.map((seriesKey, index) => (
134
98
  <>
@@ -141,7 +105,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
141
105
  opacity={config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
142
106
  display={config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
143
107
  >
144
- {cleanedData.map((d, dataIndex) => {
108
+ {data.map((d, dataIndex) => {
145
109
  let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${formatNumber(getYAxisData(d, seriesKey))}` : formatNumber(getYAxisData(d, seriesKey))
146
110
  let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${d[config.runtime.xAxis.dataKey]}` : d[config.runtime.xAxis.dataKey]
147
111
 
@@ -175,7 +139,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
175
139
  })}
176
140
  <LinePath
177
141
  curve={allCurves.curveLinear}
178
- data={cleanedData}
142
+ data={data}
179
143
  x={d => xScale(getXAxisData(d))}
180
144
  y={d => yScale(getYAxisData(d, seriesKey))}
181
145
  stroke={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
@@ -201,7 +165,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
201
165
  hideTicks
202
166
  scale={xScale}
203
167
  tickValues={handleSparkLineTicks}
204
- tickFormat={formatDate}
168
+ tickFormat={tick => (config.xAxis.type === 'date' ? formatDate(tick) : null)}
205
169
  stroke={'black'}
206
170
  tickStroke={'black'}
207
171
  tickLabelProps={() => ({
@@ -67,7 +67,9 @@ export default {
67
67
  iqr: 'Interquartile Range',
68
68
  total: 'Total',
69
69
  outliers: 'Outliers',
70
- values: 'Values'
70
+ values: 'Values',
71
+ lowerBounds: 'Lower Bounds',
72
+ upperBounds: 'Upper Bounds'
71
73
  }
72
74
  },
73
75
  topAxis: {
@@ -106,7 +108,8 @@ export default {
106
108
  limitHeight: false,
107
109
  height: '',
108
110
  caption: '',
109
- showDownloadUrl: false
111
+ showDownloadUrl: false,
112
+ showDataTableLink: true
110
113
  },
111
114
  orientation: 'vertical',
112
115
  legend: {
@@ -146,5 +149,6 @@ export default {
146
149
  border: true,
147
150
  accent: true,
148
151
  background: true
149
- }
152
+ },
153
+ filterBehavior: 'Filter Change'
150
154
  }
package/src/index.jsx CHANGED
@@ -6,11 +6,12 @@ import CdcChart from './CdcChart'
6
6
  import 'react-tooltip/dist/react-tooltip.css'
7
7
 
8
8
  let isEditor = window.location.href.includes('editor=true')
9
+ let isDebug = window.location.href.includes('debug=true')
9
10
 
10
11
  let domContainer = document.getElementsByClassName('react-container')[0]
11
12
 
12
13
  ReactDOM.createRoot(domContainer).render(
13
14
  <React.StrictMode>
14
- <CdcChart configUrl={domContainer.attributes['data-config'].value} isEditor={isEditor} />
15
+ <CdcChart configUrl={domContainer.attributes['data-config'].value} isEditor={isEditor} isDebug={isDebug} />
15
16
  </React.StrictMode>
16
17
  )
@@ -75,11 +75,16 @@
75
75
  align-items: center;
76
76
  justify-content: space-between;
77
77
  font-size: 0.9em;
78
+ position: relative;
78
79
 
79
80
  &:hover {
80
81
  background-color: $lightestGray;
81
82
  }
82
83
 
84
+ div {
85
+ width: 100%;
86
+ }
87
+
83
88
  .series-list__name {
84
89
  position: relative;
85
90
  user-select: none;
@@ -132,7 +137,13 @@
132
137
  }
133
138
 
134
139
  .series-list__dropdown {
140
+ width: 100%;
141
+ display: block;
135
142
  font-size: 0.8em;
143
+ margin-bottom: 10px;
144
+ select {
145
+ width: 100%;
146
+ }
136
147
  }
137
148
 
138
149
  .series-list__remove {
@@ -140,6 +151,9 @@
140
151
  font-size: 1.125rem;
141
152
  color: #f00;
142
153
  cursor: pointer;
154
+ position: absolute;
155
+ top: 5px;
156
+ right: 5px;
143
157
  }
144
158
 
145
159
  + li {
@@ -150,6 +164,7 @@
150
164
 
151
165
  .series-list__name-text {
152
166
  max-width: 150px;
167
+ margin-bottom: 10px;
153
168
  white-space: nowrap;
154
169
  text-overflow: ellipsis;
155
170
  overflow: hidden;
@@ -231,9 +231,13 @@
231
231
  }
232
232
  }
233
233
 
234
+ .visx-tooltip {
235
+ z-index: 100000;
236
+ }
237
+
234
238
  .tooltip {
235
- border: rgba(0,0,0,.3) 1px solid;
236
- box-shadow: rgba(0,0,0,.1) 3px 3px 7px;
239
+ border: rgba(0, 0, 0, 0.3) 1px solid;
240
+ box-shadow: rgba(0, 0, 0, 0.1) 3px 3px 7px;
237
241
  opacity: 1;
238
242
  line-height: 1.4em;
239
243
  font-size: 1em;
@@ -242,8 +246,8 @@
242
246
  z-index: 1;
243
247
 
244
248
  .react-tooltip-arrow {
245
- border-bottom: rgba(0,0,0,.3) 1px solid;
246
- border-right: rgba(0,0,0,.3) 1px solid;
249
+ border-bottom: rgba(0, 0, 0, 0.3) 1px solid;
250
+ border-right: rgba(0, 0, 0, 0.3) 1px solid;
247
251
  backface-visibility: hidden;
248
252
  }
249
253
  }
@@ -337,8 +341,6 @@
337
341
  }
338
342
 
339
343
  &__wrapper {
340
- margin-bottom: 40px;
341
-
342
344
  hr {
343
345
  margin-bottom: 20px;
344
346
  }
@@ -1,5 +0,0 @@
1
- x,min,firstQuartile,median,thirdQuartile,max,outliers
2
- Group 1,-3.529439249291613,2.498913996933074,6,6.517816161082865,12.546169407307552,-4.222649440089676,-3.7995254226812145,12.669680018702707,,,,,,,,,,,,
3
- Group 2,-2.961278855962981,3.1553763355078277,5.31642875968012,7.233146463155033,13.349801654625843,-4.5962772985737645,14.42798091207488,14.832072412460995,,,,,,,,,,,,
4
- Group 3,-0.784362943544294,5.600233888319485,7.606034555385235,9.85663177622867,16.24122860809245,-2.0568265981730285,-2.036299998010181,-1.634595257757523,-0.9751707921193091,-0.9256799494292718,-0.813852054679872,16.255225428689318,19.221712546396496,,,,,,,
5
- Group 4,-3.4976011611041598,2.362493132101971,4.364242960871863,6.269222660906058,12.129316954112188,-5.912277243480174,-4.535668956980487,-4.255719319016919,-4.175200716132927,-4.1021204775116455,-3.7913796362224352,-3.6909919981778567,-3.6261129831962697,12.169135739844744,12.1724073804239,12.191268834215071,12.236896118210165,12.34513716605812,12.826785108558722,13.048968511771164