@cdc/chart 4.22.11 → 4.23.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 (65) hide show
  1. package/dist/cdcchart.js +54569 -16
  2. package/examples/Barchart_with_negative.json +34 -0
  3. package/examples/box-plot-data.json +71 -0
  4. package/examples/box-plot.csv +5 -0
  5. package/examples/box-plot.json +124 -0
  6. package/examples/dynamic-legends.json +1 -1
  7. package/examples/example-bar-chart-nonnumeric.json +36 -0
  8. package/examples/example-bar-chart.json +33 -0
  9. package/examples/example-combo-bar-nonnumeric.json +105 -0
  10. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +3 -1
  11. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +1 -1
  12. package/examples/gallery/bar-chart-vertical/vertical-bar-chart.json +86 -17
  13. package/examples/gallery/paired-bar/paired-bar-chart.json +65 -13
  14. package/examples/line-chart-nonnumeric.json +32 -0
  15. package/examples/line-chart.json +21 -63
  16. package/examples/new-data.csv +17 -0
  17. package/examples/newdata.json +90 -0
  18. package/examples/planet-combo-example-config.json +143 -20
  19. package/examples/planet-example-data-nonnumeric.json +56 -0
  20. package/examples/planet-example-data.json +2 -2
  21. package/examples/planet-pie-example-config-nonnumeric.json +30 -0
  22. package/examples/scatterplot-continuous.csv +17 -0
  23. package/examples/{private/yaxis-test.json → scatterplot.json} +53 -50
  24. package/examples/sparkline-chart-nonnumeric.json +76 -0
  25. package/examples/stacked-vertical-bar-example-negative.json +154 -0
  26. package/examples/stacked-vertical-bar-example-nonnumerics.json +154 -0
  27. package/{src/index.html → index.html} +18 -11
  28. package/package.json +29 -22
  29. package/src/{CdcChart.tsx → CdcChart.jsx} +193 -119
  30. package/src/components/BarChart.jsx +517 -0
  31. package/src/components/BoxPlot.jsx +88 -0
  32. package/src/components/{DataTable.tsx → DataTable.jsx} +125 -32
  33. package/src/components/{EditorPanel.js → EditorPanel.jsx} +376 -115
  34. package/src/components/Filters.jsx +125 -0
  35. package/src/components/Legend.jsx +303 -0
  36. package/src/components/{LineChart.tsx → LineChart.jsx} +87 -22
  37. package/src/components/{LinearChart.tsx → LinearChart.jsx} +172 -113
  38. package/src/components/{PairedBarChart.tsx → PairedBarChart.jsx} +46 -79
  39. package/src/components/{PieChart.tsx → PieChart.jsx} +29 -34
  40. package/src/components/ScatterPlot.jsx +48 -0
  41. package/src/components/{SparkLine.js → SparkLine.jsx} +49 -18
  42. package/src/components/useIntersectionObserver.jsx +29 -0
  43. package/src/data/initial-state.js +44 -8
  44. package/src/hooks/{useColorPalette.ts → useColorPalette.js} +10 -28
  45. package/src/hooks/{useReduceData.ts → useReduceData.js} +27 -13
  46. package/src/hooks/useRightAxis.js +3 -1
  47. package/src/index.jsx +16 -0
  48. package/src/scss/DataTable.scss +23 -1
  49. package/src/scss/main.scss +83 -32
  50. package/vite.config.js +4 -0
  51. package/examples/private/filters.json +0 -170
  52. package/examples/private/line-test-data.json +0 -22
  53. package/examples/private/line-test-two.json +0 -210
  54. package/examples/private/line-test.json +0 -102
  55. package/examples/private/new.json +0 -48800
  56. package/examples/private/newtest.csv +0 -101
  57. package/examples/private/shawn.json +0 -1106
  58. package/examples/private/test.json +0 -10124
  59. package/examples/private/yaxis-testing.csv +0 -27
  60. package/examples/private/yaxis.json +0 -28
  61. package/src/components/BarChart.tsx +0 -579
  62. package/src/components/Legend.js +0 -284
  63. package/src/components/useIntersectionObserver.tsx +0 -27
  64. package/src/index.tsx +0 -18
  65. /package/src/{context.tsx → ConfigContext.jsx} +0 -0
@@ -1,43 +1,47 @@
1
- import React, { Fragment, useContext, useEffect, useRef, useState } from 'react'
2
- import ReactTooltip from 'react-tooltip'
1
+ import React, { useContext, useEffect, useRef, useState } from 'react'
2
+ import { Tooltip as ReactTooltip } from 'react-tooltip'
3
3
 
4
4
  import { Group } from '@visx/group'
5
5
  import { Line } from '@visx/shape'
6
6
  import { Text } from '@visx/text'
7
- import { scaleLinear, scalePoint } from '@visx/scale'
7
+ import { scaleLinear, scalePoint, scaleBand } from '@visx/scale'
8
8
  import { AxisLeft, AxisBottom, AxisRight, AxisTop } from '@visx/axis'
9
9
 
10
+ import CoveScatterPlot from './ScatterPlot'
10
11
  import BarChart from './BarChart'
11
12
  import LineChart from './LineChart'
12
- import Context from '../context'
13
+ import ConfigContext from '../ConfigContext'
13
14
  import PairedBarChart from './PairedBarChart'
14
15
  import useIntersectionObserver from './useIntersectionObserver'
15
- import SparkLine from './SparkLine'
16
+ import CoveBoxPlot from './BoxPlot'
16
17
 
17
18
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
18
- import numberFromString from '@cdc/core/helpers/numberFromString'
19
19
  import '../scss/LinearChart.scss'
20
20
  import useReduceData from '../hooks/useReduceData'
21
21
  import useRightAxis from '../hooks/useRightAxis'
22
22
  import useTopAxis from '../hooks/useTopAxis'
23
23
 
24
24
  // TODO: Move scaling functions into hooks to manage complexity
25
-
26
- // TODO: remove unused imports/variables
27
- // TODO: consider moving logic into hooks
28
- // TODO: formatting
29
25
  export default function LinearChart() {
30
- const { transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, formatNumber, handleChartAriaLabels, updateConfig } = useContext<any>(Context)
26
+ const { transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, formatNumber, handleChartAriaLabels, updateConfig } = useContext(ConfigContext)
31
27
  let [width] = dimensions
32
- const { minValue, maxValue, existPositiveValue } = useReduceData(config, data)
33
- const [animatedChart, setAnimatedChart] = useState<boolean>(false)
34
- const [animatedChartPlayed, setAnimatedChartPlayed] = useState<boolean>(false)
28
+ const { minValue, maxValue, existPositiveValue, isAllLine } = useReduceData(config, data)
29
+ const [animatedChart, setAnimatedChart] = useState(false)
35
30
 
36
31
  const triggerRef = useRef()
37
32
  const dataRef = useIntersectionObserver(triggerRef, {
38
33
  freezeOnceVisible: false
39
34
  })
40
- // If the chart is in view and set to animate and it has not already played
35
+ // Make sure the chart is visible if in the editor
36
+ useEffect(() => {
37
+ const element = document.querySelector('.isEditor')
38
+ if (element) {
39
+ // parent element is visible
40
+ setAnimatedChart(prevState => true)
41
+ }
42
+ })
43
+
44
+ // If the chart is in view, set to animate if it has not already played
41
45
  useEffect(() => {
42
46
  if (dataRef?.isIntersecting === true && config.animate) {
43
47
  setTimeout(() => {
@@ -49,35 +53,45 @@ export default function LinearChart() {
49
53
  if (config && config.legend && !config.legend.hide && config.legend.position !== 'bottom' && (currentViewport === 'lg' || currentViewport === 'md')) {
50
54
  width = width * 0.73
51
55
  }
52
-
53
- const height = config.aspectRatio ? width * config.aspectRatio : config.height
54
- const xMax = width - config.runtime.yAxis.size - config.yAxis.rightAxisSize
55
- const yMax = height - config.runtime.xAxis.size
56
+ const { horizontal: heightHorizontal } = config.heights
57
+ const height = config.aspectRatio ? width * config.aspectRatio : config.heights[config.orientation]
58
+ const xMax = width - config.runtime.yAxis.size - (config.visualizationType === 'Combo' ? config.yAxis.rightAxisSize : 0)
59
+ const yMax = height - (config.orientation === 'horizontal' ? 0 : config.runtime.xAxis.size)
56
60
 
57
61
  const { yScaleRight, hasRightAxis } = useRightAxis({ config, yMax, data, updateConfig })
58
62
  const { hasTopAxis } = useTopAxis(config)
59
63
 
60
- const getXAxisData = (d: any) => (config.runtime.xAxis.type === 'date' ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
61
- const getYAxisData = (d: any, seriesKey: string) => d[seriesKey]
64
+ const getXAxisData = d => (config.runtime.xAxis.type === 'date' ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
65
+ const getYAxisData = (d, seriesKey) => d[seriesKey]
62
66
 
63
67
  let xScale
64
68
  let yScale
65
69
  let seriesScale
66
70
 
67
71
  const { max: enteredMaxValue, min: enteredMinValue } = config.runtime.yAxis
68
- const isMaxValid = existPositiveValue ? numberFromString(enteredMaxValue) >= numberFromString(maxValue) : numberFromString(enteredMaxValue) >= 0
69
- const isMinValid = (numberFromString(enteredMinValue) <= 0 && numberFromString(minValue) >= 0) || (numberFromString(enteredMinValue) <= minValue && minValue < 0)
72
+ const isMaxValid = existPositiveValue ? enteredMaxValue >= maxValue : enteredMaxValue >= 0
73
+ const isMinValid = (enteredMinValue <= 0 && minValue >= 0) || (enteredMinValue <= minValue && minValue < 0)
70
74
 
71
75
  if (data) {
72
76
  let min = enteredMinValue && isMinValid ? enteredMinValue : minValue
73
77
  let max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
74
78
 
75
- if ((config.visualizationType === 'Bar' || config.visualizationType === 'Combo') && min > 0) {
79
+ if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && min > 0) {
76
80
  min = 0
77
81
  }
82
+ if (config.visualizationType === 'Combo' && isAllLine) {
83
+ if ((enteredMinValue === undefined || enteredMinValue === null || enteredMinValue === '') && min > 0) {
84
+ min = 0
85
+ }
86
+ if (enteredMinValue) {
87
+ const isMinValid = +enteredMinValue < minValue
88
+ min = +enteredMinValue && isMinValid ? enteredMinValue : minValue
89
+ }
90
+ }
91
+
78
92
  if (config.visualizationType === 'Line') {
79
- const isMinValid = Number(enteredMinValue) < Number(minValue)
80
- min = enteredMinValue && isMinValid ? Number(enteredMinValue) : minValue
93
+ const isMinValid = enteredMinValue < minValue
94
+ min = enteredMinValue && isMinValid ? enteredMinValue : minValue
81
95
  }
82
96
  //If data value max wasn't provided, calculate it
83
97
  if (max === Number.MIN_VALUE) {
@@ -109,19 +123,19 @@ export default function LinearChart() {
109
123
  }
110
124
 
111
125
  if (config.runtime.horizontal) {
112
- xScale = scaleLinear<number>({
126
+ xScale = scaleLinear({
113
127
  domain: [min, max],
114
128
  range: [0, xMax]
115
129
  })
116
130
 
117
131
  yScale =
118
132
  config.runtime.xAxis.type === 'date'
119
- ? scaleLinear<number>({
133
+ ? scaleLinear({
120
134
  domain: [Math.min(...xAxisDataMapped), Math.max(...xAxisDataMapped)]
121
135
  })
122
- : scalePoint<string>({ domain: xAxisDataMapped, padding: 0.5 })
136
+ : scalePoint({ domain: xAxisDataMapped, padding: 0.5 })
123
137
 
124
- seriesScale = scalePoint<string>({
138
+ seriesScale = scalePoint({
125
139
  domain: config.runtime.barSeriesKeys || config.runtime.seriesKeys,
126
140
  range: [0, yMax]
127
141
  })
@@ -130,24 +144,25 @@ export default function LinearChart() {
130
144
  } else {
131
145
  min = min < 0 ? min * 1.11 : min
132
146
 
133
- yScale = scaleLinear<number>({
147
+ yScale = scaleLinear({
134
148
  domain: [min, max],
135
149
  range: [yMax, 0]
136
150
  })
137
151
 
138
- xScale = scalePoint<string>({
152
+ xScale = scalePoint({
139
153
  domain: xAxisDataMapped,
140
154
  range: [0, xMax],
141
155
  padding: 0.5
142
156
  })
143
157
 
144
- seriesScale = scalePoint<string>({
158
+ seriesScale = scalePoint({
145
159
  domain: config.runtime.barSeriesKeys || config.runtime.seriesKeys,
146
160
  range: [0, xMax]
147
161
  })
148
162
  }
149
163
 
150
164
  if (config.visualizationType === 'Paired Bar') {
165
+ const offset = 1.02 // Offset of the ticks/values from the Axis
151
166
  let groupOneMax = Math.max.apply(
152
167
  Math,
153
168
  data.map(d => d[config.series[0].dataKey])
@@ -158,49 +173,99 @@ export default function LinearChart() {
158
173
  )
159
174
 
160
175
  // group one
161
- var g1xScale = scaleLinear<number>({
162
- domain: [0, Math.max(groupOneMax, groupTwoMax)],
176
+ var g1xScale = scaleLinear({
177
+ domain: [0, Math.max(groupOneMax, groupTwoMax) * offset],
163
178
  range: [xMax / 2, 0]
164
179
  })
165
180
 
166
181
  // group 2
167
- var g2xScale = scaleLinear<number>({
182
+ var g2xScale = scaleLinear({
168
183
  domain: g1xScale.domain(),
169
- range: [xMax / 2, xMax]
184
+ range: [xMax / 2, xMax],
185
+ nice: true
170
186
  })
171
187
  }
188
+
189
+ if (config.visualizationType === 'Scatter Plot') {
190
+ if (config.xAxis.type === 'continuous') {
191
+ xScale = scaleLinear({
192
+ domain: [0, Math.max.apply(null, xScale.domain())],
193
+ range: [0, xMax]
194
+ })
195
+ }
196
+ }
197
+ }
198
+
199
+ const handleLeftTickFormatting = tick => {
200
+ if (config.runtime.yAxis.type === 'date') return formatDate(parseDate(tick))
201
+ if (config.orientation === 'vertical') return formatNumber(tick, 'left')
202
+ return tick
172
203
  }
173
204
 
174
-
175
- const countNumOfTicks = (axis)=>{
205
+ const handleBottomTickFormatting = tick => {
206
+ if (config.runtime.xAxis.type === 'date') return formatDate(tick)
207
+ if (config.orientation === 'horizontal') return formatNumber(tick, 'bottom')
208
+ return tick
209
+ }
210
+
211
+ const countNumOfTicks = axis => {
176
212
  // function get number of ticks based on bar type & users value
177
- const isHorizontal = config.orientation ==='horizontal';
178
- const {numTicks} = config.runtime[axis];
179
- let tickCount = undefined;
180
-
181
- if(axis === 'yAxis'){
182
- tickCount = (
183
- (isHorizontal && !numTicks) ? data.length
184
- : (isHorizontal && numTicks) ? numTicks
185
- :(!isHorizontal && !numTicks) ? undefined
186
- :(!isHorizontal && numTicks) && numTicks
187
- );
188
- };
189
-
190
- if(axis === 'xAxis'){
191
- tickCount = (
192
- (isHorizontal && !numTicks) ? undefined
193
- : (isHorizontal && numTicks) ? numTicks
194
- :(!isHorizontal && !numTicks) ? undefined
195
- :(!isHorizontal && numTicks) && numTicks
196
- );
197
- };
198
- return tickCount;
199
- };
213
+ const isHorizontal = config.orientation === 'horizontal'
214
+ const { numTicks } = config.runtime[axis]
215
+ let tickCount = undefined
200
216
 
201
- useEffect(() => {
202
- ReactTooltip.rebuild()
203
- })
217
+ if (axis === 'yAxis') {
218
+ tickCount = isHorizontal && !numTicks ? data.length : isHorizontal && numTicks ? numTicks : !isHorizontal && !numTicks ? undefined : !isHorizontal && numTicks && numTicks
219
+ }
220
+
221
+ if (axis === 'xAxis') {
222
+ tickCount = isHorizontal && !numTicks ? undefined : isHorizontal && numTicks ? numTicks : !isHorizontal && !numTicks ? undefined : !isHorizontal && numTicks && numTicks
223
+ }
224
+ return tickCount
225
+ }
226
+
227
+ // Handle Box Plots
228
+ if (config.visualizationType === 'Box Plot') {
229
+ let minYValue
230
+ let maxYValue
231
+ let allOutliers = []
232
+ let allLowerBounds = config.boxplot.plots.map(plot => plot.columnMin)
233
+ let allUpperBounds = config.boxplot.plots.map(plot => plot.columnMax)
234
+
235
+ minYValue = Math.min(...allLowerBounds)
236
+ maxYValue = Math.max(...allUpperBounds)
237
+
238
+ const hasOutliers = config.boxplot.plots.map(b => b.columnOutliers.map(outlier => allOutliers.push(outlier))) && !config.boxplot.hideOutliers
239
+
240
+ if (hasOutliers) {
241
+ let outlierMin = Math.min(...allOutliers)
242
+ let outlierMax = Math.max(...allOutliers)
243
+
244
+ // check if outliers exceed standard bounds
245
+ if (outlierMin < minYValue) minYValue = outlierMin
246
+ if (outlierMax > maxYValue) maxYValue = outlierMax
247
+ }
248
+
249
+ const seriesNames = data.map(d => d[config.xAxis.dataKey])
250
+
251
+ // Set Scales
252
+ yScale = scaleLinear({
253
+ range: [yMax, 0],
254
+ round: true,
255
+ domain: [minYValue, maxYValue]
256
+ })
257
+
258
+ xScale = scaleBand({
259
+ range: [0, xMax],
260
+ round: true,
261
+ domain: config.boxplot.categories,
262
+ padding: 0.4
263
+ })
264
+ }
265
+
266
+ const handleTick = tick => {
267
+ return config.runtime.xAxis.type === 'date' ? formatDate(tick) : config.orientation === 'horizontal' ? formatNumber(tick) : tick
268
+ }
204
269
 
205
270
  return isNaN(width) ? (
206
271
  <></>
@@ -217,7 +282,7 @@ export default function LinearChart() {
217
282
  const width = to - from
218
283
 
219
284
  return (
220
- <Group className='regions' left={config.runtime.yAxis.size} key={region.label}>
285
+ <Group className='regions' left={Number(config.runtime.yAxis.size)} key={region.label}>
221
286
  <path
222
287
  stroke='#333'
223
288
  d={`M${from} -5
@@ -238,22 +303,16 @@ export default function LinearChart() {
238
303
 
239
304
  {/* Y axis */}
240
305
  {config.visualizationType !== 'Spark Line' && (
241
- <AxisLeft
242
- scale={yScale}
243
- left={config.runtime.yAxis.size}
244
- label={config.runtime.yAxis.label}
245
- stroke='#333'
246
- tickFormat={tick => (config.runtime.yAxis.type === 'date' ? formatDate(parseDate(tick)) : config.orientation === 'vertical' ? formatNumber(tick) : tick)}
247
- numTicks={countNumOfTicks('yAxis')}
248
- >
306
+ <AxisLeft scale={yScale} left={Number(config.runtime.yAxis.size) - config.yAxis.axisPadding} label={config.runtime.yAxis.label} stroke='#333' tickFormat={tick => handleLeftTickFormatting(tick)} numTicks={countNumOfTicks('yAxis')}>
249
307
  {props => {
250
- const lollipopShapeSize = config.lollipopSize === 'large' ? 14 : config.lollipopSize === 'medium' ? 12 : 10
251
308
  const axisCenter = config.runtime.horizontal ? (props.axisToPoint.y - props.axisFromPoint.y) / 2 : (props.axisFromPoint.y - props.axisToPoint.y) / 2
252
309
  const horizontalTickOffset = yMax / props.ticks.length / 2 - (yMax / props.ticks.length) * (1 - config.barThickness) + 5
253
- const belowBarPaddingFromTop = 9
254
310
  return (
255
311
  <Group className='left-axis'>
256
312
  {props.ticks.map((tick, i) => {
313
+ const minY = props.ticks[0].to.y
314
+ const barMinHeight = 15 // 15 is the min height for bars by default
315
+
257
316
  return (
258
317
  <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
259
318
  {!config.runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke={config.yAxis.tickColor} display={config.runtime.horizontal ? 'none' : 'block'} />}
@@ -261,32 +320,24 @@ export default function LinearChart() {
261
320
  {config.runtime.yAxis.gridLines ? <Line from={{ x: tick.from.x + xMax, y: tick.from.y }} to={tick.from} stroke='rgba(0,0,0,0.3)' /> : ''}
262
321
 
263
322
  {config.orientation === 'horizontal' && config.visualizationSubType !== 'stacked' && config.yAxis.labelPlacement === 'On Date/Category Axis' && !config.yAxis.hideLabel && (
264
- // 17 is a magic number from the offset in barchart.
265
- <Fragment>
266
- <Text transform={`translate(${tick.to.x - 5}, ${config.isLollipopChart ? tick.from.y : tick.from.y - 17}) rotate(-${config.runtime.horizontal ? config.runtime.yAxis.tickRotation : 0})`} verticalAnchor={config.isLollipopChart ? 'middle' : 'middle'} textAnchor={'end'}>
267
- {tick.formattedValue}
268
- </Text>
269
- </Fragment>
270
- )}
271
-
272
- {config.orientation === 'horizontal' && config.visualizationSubType === 'stacked' && config.yAxis.labelPlacement === 'On Date/Category Axis' && !config.yAxis.hideLabel && (
273
- // 17 is a magic number from the offset in barchart.
274
- <Text transform={`translate(${tick.to.x - 5}, ${tick.from.y - config.barHeight / 2 - 3}) rotate(-${config.runtime.horizontal ? config.runtime.yAxis.tickRotation : 0})`} verticalAnchor={config.isLollipopChart ? 'middle' : 'middle'} textAnchor={'end'}>
323
+ <Text
324
+ transform={`translate(${tick.to.x - 5}, ${config.isLollipopChart ? tick.to.y - minY : tick.to.y - minY + (Number(config.barHeight * config.series.length) - barMinHeight) / 2}) rotate(-${config.runtime.horizontal ? config.runtime.yAxis.tickRotation : 0})`}
325
+ verticalAnchor={'start'}
326
+ textAnchor={'end'}
327
+ >
275
328
  {tick.formattedValue}
276
329
  </Text>
277
330
  )}
278
331
 
279
- {config.orientation === 'horizontal' && config.visualizationType === 'Paired Bar' && !config.yAxis.hideLabel && (
280
- // 17 is a magic number from the offset in barchart.
281
- <Text transform={`translate(${-15}, ${tick.from.y}) rotate(-${config.runtime.horizontal ? config.runtime.yAxis.tickRotation : 0})`} verticalAnchor={config.isLollipopChart ? 'middle' : 'middle'} textAnchor={'end'}>
332
+ {config.orientation === 'horizontal' && config.visualizationSubType === 'stacked' && config.yAxis.labelPlacement === 'On Date/Category Axis' && !config.yAxis.hideLabel && (
333
+ <Text transform={`translate(${tick.to.x - 5}, ${tick.to.y - minY + (Number(config.barHeight) - barMinHeight) / 2}) rotate(-${config.runtime.horizontal ? config.runtime.yAxis.tickRotation : 0})`} verticalAnchor={'start'} textAnchor={'end'}>
282
334
  {tick.formattedValue}
283
335
  </Text>
284
336
  )}
285
337
 
286
338
  {config.orientation === 'horizontal' && config.visualizationType === 'Paired Bar' && !config.yAxis.hideLabel && (
287
- // 17 is a magic number from the offset in barchart.
288
- <Text transform={`translate(${-15}, ${tick.from.y}) rotate(-${config.runtime.horizontal ? config.runtime.yAxis.tickRotation : 0})`} verticalAnchor={config.isLollipopChart ? 'middle' : 'middle'} textAnchor={'end'}>
289
- {formatNumber(tick.formattedValue)}
339
+ <Text transform={`translate(${tick.to.x - 5}, ${tick.to.y - minY + Number(config.barHeight) / 2}) rotate(-${config.runtime.horizontal ? config.runtime.yAxis.tickRotation : 0})`} textAnchor={'end'} verticalAnchor='middle'>
340
+ {tick.formattedValue}
290
341
  </Text>
291
342
  )}
292
343
 
@@ -304,7 +355,7 @@ export default function LinearChart() {
304
355
  </Group>
305
356
  )
306
357
  })}
307
- {!config.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
358
+ {!config.yAxis.hideAxis && <Line from={props.axisFromPoint} to={config.runtime.horizontal ? { x: 0, y: Number(heightHorizontal) } : props.axisToPoint} stroke='#000' />}
308
359
  {yScale.domain()[0] < 0 && <Line from={{ x: props.axisFromPoint.x, y: yScale(0) }} to={{ x: xMax, y: yScale(0) }} stroke='#333' />}
309
360
  <Text className='y-label' textAnchor='middle' verticalAnchor='start' transform={`translate(${-1 * config.runtime.yAxis.size}, ${axisCenter}) rotate(-90)`} fontWeight='bold' fill={config.yAxis.labelColor}>
310
361
  {props.label}
@@ -317,7 +368,7 @@ export default function LinearChart() {
317
368
 
318
369
  {/* Right Axis */}
319
370
  {hasRightAxis && (
320
- <AxisRight scale={yScaleRight} left={width - config.yAxis.rightAxisSize} label={config.yAxis.rightLabel} tickFormat={tick => formatNumber(tick, 'right')} numTicks={config.runtime.yAxis.rightNumTicks || undefined} labelOffset={45}>
371
+ <AxisRight scale={yScaleRight} left={Number(width - config.yAxis.rightAxisSize)} label={config.yAxis.rightLabel} tickFormat={tick => formatNumber(tick, 'right')} numTicks={config.runtime.yAxis.rightNumTicks || undefined} labelOffset={45}>
321
372
  {props => {
322
373
  const axisCenter = config.runtime.horizontal ? (props.axisToPoint.y - props.axisFromPoint.y) / 2 : (props.axisFromPoint.y - props.axisToPoint.y) / 2
323
374
  const horizontalTickOffset = yMax / props.ticks.length / 2 - (yMax / props.ticks.length) * (1 - config.barThickness) + 5
@@ -351,7 +402,7 @@ export default function LinearChart() {
351
402
  {hasTopAxis && config.topAxis.hasLine && (
352
403
  <AxisTop
353
404
  stroke='#333'
354
- left={config.runtime.yAxis.size}
405
+ left={Number(config.runtime.yAxis.size)}
355
406
  scale={xScale}
356
407
  hideTicks
357
408
  hideZero
@@ -364,10 +415,10 @@ export default function LinearChart() {
364
415
  {/* X axis */}
365
416
  {config.visualizationType !== 'Paired Bar' && config.visualizationType !== 'Spark Line' && (
366
417
  <AxisBottom
367
- top={yMax}
368
- left={config.runtime.yAxis.size}
418
+ top={config.runtime.horizontal ? Number(heightHorizontal) + Number(config.xAxis.axisPadding) : yMax + Number(config.xAxis.axisPadding)}
419
+ left={Number(config.runtime.yAxis.size)}
369
420
  label={config.runtime.xAxis.label}
370
- tickFormat={tick => (config.runtime.xAxis.type === 'date' ? formatDate(tick) : config.orientation === 'horizontal' ? formatNumber(tick) : tick)}
421
+ tickFormat={handleBottomTickFormatting}
371
422
  scale={xScale}
372
423
  stroke='#333'
373
424
  tickStroke='#333'
@@ -397,7 +448,7 @@ export default function LinearChart() {
397
448
  )
398
449
  })}
399
450
  {!config.xAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
400
- <Text x={axisCenter} y={config.runtime.xAxis.size} textAnchor='middle' verticalAnchor='end' fontWeight='bold' fill={config.xAxis.labelColor}>
451
+ <Text x={axisCenter} y={config.orientation === 'horizontal' ? config.xAxis.labelOffset : config.xAxis.size} textAnchor='middle' fontWeight='bold' fill={config.xAxis.labelColor}>
401
452
  {props.label}
402
453
  </Text>
403
454
  </Group>
@@ -408,18 +459,18 @@ export default function LinearChart() {
408
459
 
409
460
  {config.visualizationType === 'Paired Bar' && (
410
461
  <>
411
- <AxisBottom top={yMax} left={config.runtime.yAxis.size} label={config.runtime.xAxis.label} tickFormat={config.runtime.xAxis.type === 'date' ? formatDate : formatNumber} scale={g1xScale} stroke='#333' tickStroke='#333' numTicks={config.runtime.xAxis.numTicks || undefined}>
462
+ <AxisBottom top={yMax} left={Number(config.runtime.yAxis.size)} label={config.runtime.xAxis.label} tickFormat={config.runtime.xAxis.type === 'date' ? formatDate : formatNumber} scale={g1xScale} stroke='#333' tickStroke='#333' numTicks={config.runtime.xAxis.numTicks || undefined}>
412
463
  {props => {
413
- const axisCenter = (props.axisToPoint.x - props.axisFromPoint.x) / 2
414
464
  return (
415
465
  <Group className='bottom-axis'>
416
466
  {props.ticks.map((tick, i) => {
417
- const tickWidth = xMax / props.ticks.length
467
+ const angle = tick.index !== 0 ? config.yAxis.tickRotation : 0
468
+ const textAnchor = tick.index !== 0 && config.yAxis.tickRotation && config.yAxis.tickRotation > 0 ? 'end' : 'middle'
418
469
  return (
419
470
  <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
420
471
  {!config.runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
421
472
  {!config.runtime.yAxis.hideLabel && (
422
- <Text transform={`translate(${tick.to.x}, ${tick.to.y}) rotate(-${60})`} verticalAnchor='start' textAnchor={'end'} width={config.runtime.xAxis.tickRotation && config.runtime.xAxis.tickRotation !== '0' ? undefined : tickWidth}>
473
+ <Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
423
474
  {formatNumber(tick.formattedValue)}
424
475
  </Text>
425
476
  )}
@@ -433,7 +484,7 @@ export default function LinearChart() {
433
484
  </AxisBottom>
434
485
  <AxisBottom
435
486
  top={yMax}
436
- left={config.runtime.yAxis.size}
487
+ left={Number(config.runtime.yAxis.size)}
437
488
  label={config.runtime.xAxis.label}
438
489
  tickFormat={config.runtime.xAxis.type === 'date' ? formatDate : config.runtime.xAxis.dataKey !== 'Year' ? formatNumber : tick => tick}
439
490
  scale={g2xScale}
@@ -442,17 +493,17 @@ export default function LinearChart() {
442
493
  numTicks={config.runtime.xAxis.numTicks || undefined}
443
494
  >
444
495
  {props => {
445
- const axisCenter = (props.axisToPoint.x - props.axisFromPoint.x) / 2
446
496
  return (
447
497
  <>
448
498
  <Group className='bottom-axis'>
449
499
  {props.ticks.map((tick, i) => {
450
- const tickWidth = xMax / props.ticks.length
500
+ const angle = tick.index !== 0 ? config.yAxis.tickRotation : 0
501
+ const textAnchor = tick.index !== 0 && config.yAxis.tickRotation && config.yAxis.tickRotation > 0 ? 'end' : 'middle'
451
502
  return (
452
503
  <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
453
504
  {!config.runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
454
505
  {!config.runtime.yAxis.hideLabel && (
455
- <Text transform={`translate(${tick.to.x}, ${tick.to.y}) rotate(-${60})`} verticalAnchor='start' textAnchor={'end'} width={config.runtime.xAxis.tickRotation && config.runtime.xAxis.tickRotation !== '0' ? undefined : tickWidth}>
506
+ <Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
456
507
  {tick.formattedValue}
457
508
  </Text>
458
509
  )}
@@ -462,7 +513,7 @@ export default function LinearChart() {
462
513
  {!config.runtime.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
463
514
  </Group>
464
515
  <Group>
465
- <Text transform={`translate(${xMax / 2}, ${config.height - yMax + 20}) rotate(-${0})`} verticalAnchor='start' textAnchor={'middle'} stroke='#333'>
516
+ <Text x={xMax / 2} y={config.xAxis.labelOffset} stroke='#333' textAnchor={'middle'} verticalAnchor='start'>
466
517
  {config.runtime.xAxis.label}
467
518
  </Text>
468
519
  </Group>
@@ -472,23 +523,31 @@ export default function LinearChart() {
472
523
  </AxisBottom>
473
524
  </>
474
525
  )}
475
- {config.visualizationType === 'Paired Bar' && <PairedBarChart width={xMax} height={yMax} />}
526
+
527
+ {/* Paired Bar chart */}
528
+ {config.visualizationType === 'Paired Bar' && <PairedBarChart originalWidth={width} width={xMax} height={yMax} />}
476
529
 
477
530
  {/* Bar chart */}
478
- {config.visualizationType !== 'Line' && config.visualizationType !== 'Paired Bar' && (
531
+ {config.visualizationType !== 'Line' && config.visualizationType !== 'Paired Bar' && config.visualizationType !== 'Box Plot' && config.visualizationType !== 'Scatter Plot' && (
479
532
  <>
480
533
  <BarChart xScale={xScale} yScale={yScale} seriesScale={seriesScale} xMax={xMax} yMax={yMax} getXAxisData={getXAxisData} getYAxisData={getYAxisData} animatedChart={animatedChart} visible={animatedChart} />
481
534
  </>
482
535
  )}
483
536
 
484
537
  {/* Line chart */}
485
- {config.visualizationType !== 'Bar' && config.visualizationType !== 'Paired Bar' && (
538
+ {config.visualizationType !== 'Bar' && config.visualizationType !== 'Paired Bar' && config.visualizationType !== 'Box Plot' && config.visualizationType !== 'Scatter Plot' && (
486
539
  <>
487
540
  <LineChart xScale={xScale} yScale={yScale} getXAxisData={getXAxisData} getYAxisData={getYAxisData} xMax={xMax} yMax={yMax} seriesStyle={config.series} />
488
541
  </>
489
542
  )}
543
+
544
+ {/* Scatter Plot chart */}
545
+ {config.visualizationType === 'Scatter Plot' && <CoveScatterPlot xScale={xScale} yScale={yScale} getXAxisData={getXAxisData} getYAxisData={getYAxisData} />}
546
+
547
+ {/* Box Plot chart */}
548
+ {config.visualizationType === 'Box Plot' && <CoveBoxPlot xScale={xScale} yScale={yScale} />}
490
549
  </svg>
491
- <ReactTooltip id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`} html={true} type='light' arrowColor='rgba(0,0,0,0)' className='tooltip' />
550
+ <ReactTooltip id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`} variant='light' arrowColor='rgba(0,0,0,0)' className='tooltip' />
492
551
  <div className='animation-trigger' ref={triggerRef} />
493
552
  </ErrorBoundary>
494
553
  )