@cdc/chart 4.22.10 → 4.23.1

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