@cdc/chart 4.24.4 → 4.24.7

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 (76) hide show
  1. package/dist/cdcchart.js +39611 -36038
  2. package/examples/feature/annotations/index.json +542 -0
  3. package/examples/xaxis.json +493 -0
  4. package/index.html +9 -8
  5. package/package.json +5 -4
  6. package/src/CdcChart.tsx +115 -71
  7. package/src/_stories/Chart.stories.tsx +26 -171
  8. package/src/_stories/ChartAnnotation.stories.tsx +32 -0
  9. package/src/_stories/_mock/annotation_category_mock.json +473 -0
  10. package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
  11. package/src/_stories/_mock/annotation_date-time_mock.json +530 -0
  12. package/src/_stories/_mock/bar-chart-suppressed.json +474 -0
  13. package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
  14. package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
  15. package/src/_stories/_mock/lollipop.json +171 -0
  16. package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
  17. package/src/components/Annotations/components/AnnotationDraggable.tsx +154 -0
  18. package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
  19. package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
  20. package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
  21. package/src/components/Annotations/components/AnnotationList.tsx +42 -0
  22. package/src/components/Annotations/components/findNearestDatum.ts +138 -0
  23. package/src/components/Annotations/components/helpers/index.tsx +46 -0
  24. package/src/components/Annotations/index.tsx +13 -0
  25. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
  26. package/src/components/AreaChart/components/AreaChart.jsx +2 -2
  27. package/src/components/BarChart/components/BarChart.Horizontal.tsx +78 -71
  28. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -2
  29. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -11
  30. package/src/components/BarChart/components/BarChart.Vertical.tsx +100 -87
  31. package/src/components/BarChart/helpers/index.ts +102 -0
  32. package/src/components/DeviationBar.jsx +4 -2
  33. package/src/components/EditorPanel/EditorPanel.tsx +435 -613
  34. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +306 -0
  35. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +135 -7
  36. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +2 -3
  37. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -5
  38. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +3 -2
  39. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  40. package/src/components/EditorPanel/components/panels.scss +4 -0
  41. package/src/components/EditorPanel/editor-panel.scss +19 -0
  42. package/src/components/EditorPanel/useEditorPermissions.js +23 -3
  43. package/src/components/Legend/Legend.Component.tsx +66 -15
  44. package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
  45. package/src/components/Legend/helpers/index.ts +5 -0
  46. package/src/components/LineChart/LineChartProps.ts +16 -6
  47. package/src/components/LineChart/components/LineChart.Circle.tsx +22 -11
  48. package/src/components/LineChart/helpers.ts +148 -10
  49. package/src/components/LineChart/index.tsx +71 -44
  50. package/src/components/LinearChart.jsx +184 -125
  51. package/src/components/PairedBarChart.jsx +9 -9
  52. package/src/components/PieChart/PieChart.tsx +4 -4
  53. package/src/components/Sankey/index.tsx +73 -20
  54. package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
  55. package/src/components/ZoomBrush.tsx +120 -55
  56. package/src/data/initial-state.js +14 -6
  57. package/src/helpers/handleChartTabbing.ts +8 -0
  58. package/src/helpers/isConvertLineToBarGraph.ts +4 -0
  59. package/src/hooks/{useBarChart.js → useBarChart.ts} +9 -22
  60. package/src/hooks/useColorScale.ts +1 -1
  61. package/src/hooks/useMinMax.ts +29 -5
  62. package/src/hooks/useScales.ts +48 -26
  63. package/src/hooks/useTooltip.tsx +62 -15
  64. package/src/scss/main.scss +69 -12
  65. package/src/types/ChartConfig.ts +53 -16
  66. package/src/types/ChartContext.ts +13 -0
  67. package/tests-examples/helpers/testZeroValue.test.ts +30 -0
  68. package/LICENSE +0 -201
  69. package/src/_stories/ChartLine.preliminary.tsx +0 -19
  70. package/src/_stories/ChartSuppress.stories.tsx +0 -19
  71. package/src/_stories/_mock/suppress_mock.json +0 -911
  72. package/src/helpers/computeMarginBottom.ts +0 -56
  73. package/src/helpers/filterData.ts +0 -18
  74. package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
  75. /package/src/hooks/{useLegendClasses.js → useLegendClasses.ts} +0 -0
  76. /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
@@ -24,35 +24,66 @@ import PairedBarChart from './PairedBarChart'
24
24
  import useIntersectionObserver from './../hooks/useIntersectionObserver'
25
25
  import Regions from './Regions'
26
26
 
27
+ // Helpers
28
+ import { isConvertLineToBarGraph } from '../helpers/isConvertLineToBarGraph'
29
+
27
30
  // Hooks
28
31
  import useMinMax from '../hooks/useMinMax'
29
32
  import useReduceData from '../hooks/useReduceData'
30
33
  import useRightAxis from '../hooks/useRightAxis'
31
- import useScales, { getTickValues } from '../hooks/useScales'
34
+ import useScales, { getTickValues } from '../hooks/useScales'
32
35
  import useTopAxis from '../hooks/useTopAxis'
33
36
  import { useTooltip as useCoveTooltip } from '../hooks/useTooltip'
34
37
  import { useEditorPermissions } from './EditorPanel/useEditorPermissions'
38
+ import Annotation from './Annotations'
35
39
 
36
40
  // styles
37
41
  import ZoomBrush from './ZoomBrush'
38
42
 
39
43
  const LinearChart = props => {
40
- const { transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, formatNumber, handleChartAriaLabels, updateConfig, handleLineType, getTextWidth } = useContext(ConfigContext)
44
+ // prettier-ignore
45
+ const {
46
+ brushConfig,
47
+ config,
48
+ currentViewport,
49
+ dimensions,
50
+ formatDate,
51
+ formatNumber,
52
+ getTextWidth,
53
+ handleChartAriaLabels,
54
+ handleLineType,
55
+ handleDragStateChange,
56
+ parseDate,
57
+ tableData,
58
+ transformedData: data,
59
+ updateConfig,
60
+ isDraggingAnnotation
61
+ } = useContext(ConfigContext)
62
+
41
63
  // todo: start destructuring this file for conciseness
42
64
  const { visualizationType, visualizationSubType, orientation, xAxis, yAxis, runtime, debugSvg } = config
43
65
 
66
+ const checkLineToBarGraph = () => {
67
+ return isConvertLineToBarGraph(config.visualizationType, data, config.allowLineToBarGraph)
68
+ }
69
+
44
70
  // configure width
45
71
  let [width] = dimensions
46
- if (config && config.legend && !config.legend.hide && config.legend.position !== 'bottom' && ['lg', 'md'].includes(currentViewport)) {
72
+ if (config && config.legend && !config.legend.hide && config.legend?.position !== 'bottom' && ['lg', 'md'].includes(currentViewport)) {
47
73
  width = width * 0.73
48
74
  }
49
75
  // configure height , yMax, xMax
50
76
  const { horizontal: heightHorizontal } = config.heights
51
77
  const isHorizontal = orientation === 'horizontal' || config.visualizationType === 'Forest Plot'
52
78
  const shouldAbbreviate = true
79
+ const xLabelOffset = isNaN(parseInt(runtime.xAxis.labelOffset)) ? 0 : parseInt(runtime.xAxis.labelOffset)
80
+ const yLabelOffset = isNaN(parseInt(runtime.yAxis.labelOffset)) ? 0 : parseInt(runtime.yAxis.labelOffset)
81
+ const xAxisSize = isNaN(parseInt(runtime.xAxis.size)) ? 0 : parseInt(runtime.xAxis.size)
53
82
  let height = config.aspectRatio ? width * config.aspectRatio : config.visualizationType === 'Forest Plot' ? config.heights['vertical'] : config.heights[orientation]
83
+ height = Number(height)
54
84
  const xMax = width - runtime.yAxis.size - (visualizationType === 'Combo' ? config.yAxis.rightAxisSize : 0)
55
- let yMax = height - (orientation === 'horizontal' ? 0 : runtime.xAxis.size)
85
+ let yMax = height - (orientation === 'horizontal' ? 0 : xAxisSize)
86
+ height += orientation === 'horizontal' ? xAxisSize : 0
56
87
 
57
88
  if (config.visualizationType === 'Forest Plot') {
58
89
  height = height + config.data.length * config.forestPlot.rowHeight
@@ -69,6 +100,7 @@ const LinearChart = props => {
69
100
  const { hasTopAxis } = useTopAxis(config)
70
101
  const [animatedChart, setAnimatedChart] = useState(false)
71
102
  const [point, setPoint] = useState({ x: 0, y: 0 })
103
+ const annotationRefs = useRef(null)
72
104
 
73
105
  // refs
74
106
  const triggerRef = useRef()
@@ -80,12 +112,12 @@ const LinearChart = props => {
80
112
  // getters & functions
81
113
  const getXAxisData = d => (isDateScale(config.runtime.xAxis) ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
82
114
  const getYAxisData = (d, seriesKey) => d[seriesKey]
83
- const xAxisDataMapped = config.brush.active && config.brush.data?.length ? config.brush.data.map(d => getXAxisData(d)) : data.map(d => getXAxisData(d))
115
+ const xAxisDataMapped = config.brush?.active && brushConfig.data?.length ? brushConfig.data.map(d => getXAxisData(d)) : data.map(d => getXAxisData(d))
84
116
  const section = config.orientation === 'horizontal' || config.visualizationType === 'Forest Plot' ? 'yAxis' : 'xAxis'
85
- const properties = { data, config, minValue, maxValue, isAllLine, existPositiveValue, xAxisDataMapped, xMax, yMax }
117
+ const properties = { data, tableData, config, minValue, maxValue, isAllLine, existPositiveValue, xAxisDataMapped, xMax, yMax }
86
118
  const { min, max, leftMax, rightMax } = useMinMax(properties)
87
119
  const { yScaleRight, hasRightAxis } = useRightAxis({ config, yMax, data, updateConfig })
88
- const { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding, xScaleBrush } = useScales({ ...properties, min, max, leftMax, rightMax, dimensions })
120
+ const { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding, xScaleBrush, xScaleAnnotation } = useScales({ ...properties, min, max, leftMax, rightMax, dimensions })
89
121
 
90
122
  // sets the portal x/y for where tooltips should appear on the page.
91
123
  const [chartPosition, setChartPosition] = useState(null)
@@ -102,6 +134,7 @@ const LinearChart = props => {
102
134
  if (config.data && !config.data[index] && visualizationType === 'Forest Plot') return
103
135
  if (config.visualizationType === 'Forest Plot') return config.data[index][config.xAxis.dataKey]
104
136
  if (isDateScale(runtime.yAxis)) return formatDate(parseDate(tick))
137
+ if (orientation === 'vertical' && max - min < 3) return formatNumber(tick, 'left', shouldAbbreviate, false, false, '1')
105
138
  if (orientation === 'vertical') return formatNumber(tick, 'left', shouldAbbreviate)
106
139
  return tick
107
140
  }
@@ -120,7 +153,10 @@ const LinearChart = props => {
120
153
  }
121
154
 
122
155
  const countNumOfTicks = axis => {
123
- const { numTicks } = runtime[axis]
156
+ let { numTicks } = runtime[axis]
157
+ if (runtime[axis].viewportNumTicks && runtime[axis].viewportNumTicks[currentViewport]) {
158
+ numTicks = runtime[axis].viewportNumTicks[currentViewport]
159
+ }
124
160
  let tickCount = undefined
125
161
 
126
162
  if (axis === 'yAxis') {
@@ -208,6 +244,7 @@ const LinearChart = props => {
208
244
  }
209
245
 
210
246
  const padding = orientation === 'horizontal' ? Number(config.xAxis.size) : Number(config.yAxis.size)
247
+ const fontSize = { small: 16, medium: 18, large: 20 }
211
248
 
212
249
  const handleNumTicks = () => {
213
250
  // On forest plots we need to return every "study" or y axis value.
@@ -215,6 +252,14 @@ const LinearChart = props => {
215
252
  return countNumOfTicks('yAxis')
216
253
  }
217
254
 
255
+ const getManualStep = () => {
256
+ let manualStep = config.xAxis.manualStep
257
+ if (config.xAxis.viewportStepCount && config.xAxis.viewportStepCount[currentViewport]) {
258
+ manualStep = config.xAxis.viewportStepCount[currentViewport]
259
+ }
260
+ return manualStep
261
+ }
262
+
218
263
  const onMouseMove = event => {
219
264
  const svgRect = event.currentTarget.getBoundingClientRect()
220
265
  const x = event.clientX - svgRect.left
@@ -226,27 +271,110 @@ const LinearChart = props => {
226
271
  })
227
272
  }
228
273
 
274
+ const generatePairedBarAxis = () => {
275
+ let axisMaxHeight = 40
276
+
277
+ return (
278
+ <>
279
+ <AxisBottom top={yMax} left={Number(runtime.yAxis.size)} label={runtime.xAxis.label} tickFormat={isDateScale(runtime.xAxis) ? formatDate : formatNumber} scale={g1xScale} stroke='#333' tickStroke='#333' numTicks={runtime.xAxis.numTicks || undefined}>
280
+ {props => {
281
+ return (
282
+ <Group className='bottom-axis'>
283
+ {props.ticks.map((tick, i) => {
284
+ const angle = tick.index !== 0 ? config.yAxis.tickRotation : 0
285
+ const textAnchor = tick.index !== 0 && config.yAxis.tickRotation && config.yAxis.tickRotation > 0 ? 'end' : 'middle'
286
+
287
+ const textWidth = getTextWidth(tick.value, `normal ${fontSize[config.fontSize]}px sans-serif`)
288
+ const axisHeight = textWidth * Math.sin(angle * (Math.PI / 180)) + 25
289
+
290
+ if (axisHeight > axisMaxHeight) axisMaxHeight = axisHeight
291
+
292
+ return (
293
+ <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
294
+ {!runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
295
+ {!runtime.yAxis.hideLabel && (
296
+ <Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
297
+ {formatNumber(tick.value, 'left')}
298
+ </Text>
299
+ )}
300
+ </Group>
301
+ )
302
+ })}
303
+ {!runtime.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
304
+ </Group>
305
+ )
306
+ }}
307
+ </AxisBottom>
308
+ <AxisBottom
309
+ top={yMax}
310
+ left={Number(runtime.yAxis.size)}
311
+ label={runtime.xAxis.label}
312
+ tickFormat={isDateScale(runtime.xAxis) ? formatDate : runtime.xAxis.dataKey !== 'Year' ? formatNumber : tick => tick}
313
+ scale={g2xScale}
314
+ stroke='#333'
315
+ tickStroke='#333'
316
+ numTicks={runtime.xAxis.numTicks || undefined}
317
+ >
318
+ {props => {
319
+ return (
320
+ <>
321
+ <Group className='bottom-axis'>
322
+ {props.ticks.map((tick, i) => {
323
+ const angle = tick.index !== 0 ? config.yAxis.tickRotation : 0
324
+ const textAnchor = tick.index !== 0 && config.yAxis.tickRotation && config.yAxis.tickRotation > 0 ? 'end' : 'middle'
325
+
326
+ const textWidth = getTextWidth(tick.value, `normal ${fontSize[config.fontSize]}px sans-serif`)
327
+ const axisHeight = textWidth * Math.sin(angle * (Math.PI / 180)) + 25
328
+
329
+ if (axisHeight > axisMaxHeight) axisMaxHeight = axisHeight
330
+
331
+ return (
332
+ <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
333
+ {!runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
334
+ {!runtime.yAxis.hideLabel && (
335
+ <Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
336
+ {formatNumber(tick.value, 'left')}
337
+ </Text>
338
+ )}
339
+ </Group>
340
+ )
341
+ })}
342
+ {!runtime.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
343
+ </Group>
344
+ <Group>
345
+ <Text x={xMax / 2} y={axisMaxHeight + 20 + xLabelOffset} stroke='#333' textAnchor={'middle'} verticalAnchor='start'>
346
+ {runtime.xAxis.label}
347
+ </Text>
348
+ </Group>
349
+ {svgRef.current ? svgRef.current.setAttribute('height', Number(height) + Number(axisMaxHeight) + (runtime.xAxis.label ? 50 : 0) + 'px') : ''}
350
+ </>
351
+ )
352
+ }}
353
+ </AxisBottom>
354
+ </>
355
+ )
356
+ }
357
+
229
358
  return isNaN(width) ? (
230
359
  <React.Fragment></React.Fragment>
231
360
  ) : (
232
361
  <ErrorBoundary component='LinearChart'>
233
362
  {/* ! Notice - div needed for tooltip boundaries (flip/flop) */}
234
- <div style={{ width: `${width}px`, height: `${height}px`, overflow: 'visible' }} className='tooltip-boundary'>
363
+ <div style={{ width: `${width}px`, overflow: 'visible' }} className='tooltip-boundary'>
235
364
  <svg
236
- // onMouseLeave={() => setPoint(null)}
237
365
  onMouseMove={onMouseMove}
238
366
  width={'100%'}
239
- height={'100%'}
240
- className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${debugSvg && 'debug'}`}
367
+ height={height}
368
+ className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${debugSvg && 'debug'} ${isDraggingAnnotation && 'dragging-annotation'}`}
241
369
  role='img'
242
370
  aria-label={handleChartAriaLabels(config)}
243
371
  ref={svgRef}
244
372
  style={{ overflow: 'visible' }}
245
373
  >
246
- <Bar width={width} height={height} fill={'transparent'}></Bar> {/* Highlighted regions */}
374
+ {!isDraggingAnnotation && <Bar width={width} height={height} fill={'transparent'}></Bar>} {/* Highlighted regions */}
247
375
  {/* Y axis */}
248
376
  {!['Spark Line', 'Forest Plot'].includes(visualizationType) && (
249
- <AxisLeft scale={yScale} tickLength={config.useLogScale ? 6 : 8} left={Number(runtime.yAxis.size) - config.yAxis.axisPadding} label={runtime.yAxis.yAxis?.label || runtime.yAxis.label} stroke='#333' tickFormat={(tick, i) => handleLeftTickFormatting(tick, i)} numTicks={handleNumTicks()}>
377
+ <AxisLeft scale={yScale} tickLength={config.useLogScale ? 6 : 8} left={Number(runtime.yAxis.size) - config.yAxis.axisPadding} label={runtime.yAxis.label || runtime.yAxis.label} stroke='#333' tickFormat={(tick, i) => handleLeftTickFormatting(tick, i)} numTicks={handleNumTicks()}>
250
378
  {props => {
251
379
  const axisCenter = config.orientation === 'horizontal' ? (props.axisToPoint.y - props.axisFromPoint.y) / 2 : (props.axisFromPoint.y - props.axisToPoint.y) / 2
252
380
  const horizontalTickOffset = yMax / props.ticks.length / 2 - (yMax / props.ticks.length) * (1 - config.barThickness) + 5
@@ -258,12 +386,13 @@ const LinearChart = props => {
258
386
  const showTicks = String(tick.value).startsWith('1') || tick.value === 0.1 ? 'block' : 'none'
259
387
  const tickLength = showTicks === 'block' ? 7 : 0
260
388
  const to = { x: tick.to.x - tickLength, y: tick.to.y }
389
+ const displayFirstGridLine = tick.index !== 0 || config.xAxis.hideAxis
261
390
 
262
391
  return (
263
392
  <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
264
393
  {!runtime.yAxis.hideTicks && <Line key={`${tick.value}--hide-hideTicks`} from={tick.from} to={config.useLogScale ? to : tick.to} stroke={config.yAxis.tickColor} display={orientation === 'horizontal' ? 'none' : 'block'} />}
265
394
 
266
- {runtime.yAxis.gridLines ? <Line key={`${tick.value}--hide-hideGridLines`} display={(config.useLogScale && showTicks).toString()} from={{ x: tick.from.x + xMax, y: tick.from.y }} to={tick.from} stroke='rgba(0,0,0,0.3)' /> : ''}
395
+ {runtime.yAxis.gridLines && displayFirstGridLine ? <Line key={`${tick.value}--hide-hideGridLines`} display={(config.useLogScale && showTicks).toString()} from={{ x: tick.from.x + xMax, y: tick.from.y }} to={tick.from} stroke='rgba(0,0,0,0.3)' /> : ''}
267
396
 
268
397
  {orientation === 'horizontal' && visualizationSubType !== 'stacked' && config.yAxis.labelPlacement === 'On Date/Category Axis' && !config.yAxis.hideLabel && (
269
398
  <Text
@@ -312,7 +441,7 @@ const LinearChart = props => {
312
441
  {!config.yAxis.hideAxis && <Line from={props.axisFromPoint} to={runtime.horizontal ? { x: 0, y: config.visualizationType === 'Forest Plot' ? height : Number(heightHorizontal) } : props.axisToPoint} stroke='#000' />}
313
442
  {yScale.domain()[0] < 0 && <Line from={{ x: props.axisFromPoint.x, y: yScale(0) }} to={{ x: xMax, y: yScale(0) }} stroke='#333' />}
314
443
  {visualizationType === 'Bar' && orientation === 'horizontal' && xScale.domain()[0] < 0 && <Line from={{ x: xScale(0), y: 0 }} to={{ x: xScale(0), y: yMax }} stroke='#333' strokeWidth={2} />}
315
- <Text className='y-label' textAnchor='middle' verticalAnchor='start' transform={`translate(${-1 * runtime.yAxis.size}, ${axisCenter}) rotate(-90)`} fontWeight='bold' fill={config.yAxis.labelColor}>
444
+ <Text className='y-label' textAnchor='middle' verticalAnchor='start' transform={`translate(${-1 * runtime.yAxis.size + yLabelOffset}, ${axisCenter}) rotate(-90)`} fontWeight='bold' fill={config.yAxis.labelColor}>
316
445
  {props.label}
317
446
  </Text>
318
447
  </Group>
@@ -369,13 +498,13 @@ const LinearChart = props => {
369
498
  <AxisBottom
370
499
  top={runtime.horizontal && config.visualizationType !== 'Forest Plot' ? Number(heightHorizontal) + Number(config.xAxis.axisPadding) : config.visualizationType === 'Forest Plot' ? yMax + Number(config.xAxis.axisPadding) : yMax}
371
500
  left={config.visualizationType !== 'Forest Plot' ? Number(runtime.yAxis.size) : 0}
372
- label={runtime.xAxis.label}
501
+ label={config[section].label}
373
502
  tickFormat={handleBottomTickFormatting}
374
503
  scale={xScale}
375
504
  stroke='#333'
376
505
  numTicks={countNumOfTicks('xAxis')}
377
506
  tickStroke='#333'
378
- tickValues={config.xAxis.manual ? getTickValues(xAxisDataMapped, xScale, config.xAxis.type === 'date-time' ? countNumOfTicks('xAxis') : config.xAxis.manualStep) : undefined}
507
+ tickValues={config.xAxis.manual ? getTickValues(xAxisDataMapped, xScale, config.xAxis.type === 'date-time' ? countNumOfTicks('xAxis') : getManualStep()) : undefined}
379
508
  >
380
509
  {props => {
381
510
  const axisCenter = config.visualizationType !== 'Forest Plot' ? (props.axisToPoint.x - props.axisFromPoint.x) / 2 : dimensions[0] / 2
@@ -383,7 +512,6 @@ const LinearChart = props => {
383
512
  const ismultiLabel = props.ticks.some(tick => containsMultipleWords(tick.value))
384
513
 
385
514
  // Calculate sumOfTickWidth here, before map function
386
- const fontSize = { small: 16, medium: 18, large: 20 }
387
515
  const defaultTickLength = 8
388
516
  const tickWidthMax = Math.max(...props.ticks.map(tick => getTextWidth(tick.formattedValue, `normal ${fontSize[config.fontSize]}px sans-serif`)))
389
517
  // const marginTop = 20 // moved to top bc need for yMax calcs
@@ -417,15 +545,17 @@ const LinearChart = props => {
417
545
  config.dynamicMarginTop = dynamicMarginTop
418
546
  config.xAxis.tickWidthMax = tickWidthMax
419
547
 
420
- return (
548
+ let axisMaxHeight = 40
549
+
550
+ const axisContents = (
421
551
  <Group className='bottom-axis' width={dimensions[0]}>
422
552
  {props.ticks.map((tick, i, propsTicks) => {
423
553
  // when using LogScale show major ticks values only
424
554
  const showTick = String(tick.value).startsWith('1') || tick.value === 0.1 ? 'block' : 'none'
425
555
  const tickLength = showTick === 'block' ? 16 : defaultTickLength
426
556
  const to = { x: tick.to.x, y: tickLength }
427
- let textWidth = getTextWidth(tick.formattedValue, `normal ${fontSize[config.fontSize]}px sans-serif`)
428
- let limitedWidth = 100 / propsTicks.length
557
+ const textWidth = getTextWidth(tick.formattedValue, `normal ${fontSize[config.fontSize]}px sans-serif`)
558
+ const limitedWidth = 100 / propsTicks.length
429
559
  //reset rotations by updating config
430
560
  config.yAxis.tickRotation = config.isResponsiveTicks && config.orientation === 'horizontal' ? 0 : config.yAxis.tickRotation
431
561
  config.xAxis.tickRotation = config.isResponsiveTicks && config.orientation === 'vertical' ? 0 : config.xAxis.tickRotation
@@ -433,6 +563,10 @@ const LinearChart = props => {
433
563
 
434
564
  const tickRotation = config.isResponsiveTicks && areTicksTouching ? -Number(config.xAxis.maxTickRotation) || -90 : -Number(config.runtime.xAxis.tickRotation)
435
565
 
566
+ const axisHeight = textWidth * Math.sin(tickRotation * -1 * (Math.PI / 180)) + 25
567
+
568
+ if (axisHeight > axisMaxHeight) axisMaxHeight = axisHeight
569
+
436
570
  return (
437
571
  <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
438
572
  {!config.xAxis.hideTicks && <Line from={tick.from} to={orientation === 'horizontal' && config.useLogScale ? to : tick.to} stroke={config.xAxis.tickColor} strokeWidth={showTick === 'block' && config.useLogScale ? 1.3 : 1} />}
@@ -445,7 +579,7 @@ const LinearChart = props => {
445
579
  angle={tickRotation}
446
580
  verticalAnchor={tickRotation < -50 ? 'middle' : 'start'}
447
581
  textAnchor={tickRotation ? 'end' : 'middle'}
448
- width={areTicksTouching && !config.isResponsiveTicks && !Number(config[section].tickRotation) ? limitedWidth : textWidth}
582
+ width={areTicksTouching && !config.isResponsiveTicks && !Number(config[section].tickRotation) ? limitedWidth : undefined}
449
583
  fill={config.xAxis.tickLabelColor}
450
584
  >
451
585
  {tick.formattedValue}
@@ -455,97 +589,19 @@ const LinearChart = props => {
455
589
  )
456
590
  })}
457
591
  {!config.xAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
458
- <Text
459
- x={axisCenter}
460
- y={
461
- config.visualizationType === 'Forest Plot'
462
- ? config.xAxis.tickWidthMax + 40
463
- : config.orientation === 'horizontal'
464
- ? dynamicMarginTop || config.xAxis.labelOffset
465
- : config.isResponsiveTicks && dynamicMarginTop && !isHorizontal
466
- ? dynamicMarginTop
467
- : Number(rotation) && !config.isResponsiveTicks && !isHorizontal
468
- ? Number(rotation + tickWidthMax / 1.3)
469
- : Number(config.xAxis.labelOffset)
470
- }
471
- textAnchor='middle'
472
- verticalAnchor='start'
473
- fontWeight='bold'
474
- fill={config.xAxis.labelColor}
475
- >
592
+ <Text x={axisCenter} y={axisMaxHeight + 20 + xLabelOffset} textAnchor='middle' verticalAnchor='start' fontWeight='bold' fill={config.xAxis.labelColor}>
476
593
  {props.label}
477
594
  </Text>
478
595
  </Group>
479
596
  )
597
+
598
+ if (svgRef.current) svgRef.current.setAttribute('height', Number(height) + Number(axisMaxHeight) + (runtime.xAxis.label ? 50 : 0) + 'px')
599
+
600
+ return axisContents
480
601
  }}
481
602
  </AxisBottom>
482
603
  )}
483
- {visualizationType === 'Paired Bar' && (
484
- <>
485
- <AxisBottom top={yMax} left={Number(runtime.yAxis.size)} label={runtime.xAxis.label} tickFormat={isDateScale(runtime.xAxis) ? formatDate : formatNumber} scale={g1xScale} stroke='#333' tickStroke='#333' numTicks={runtime.xAxis.numTicks || undefined}>
486
- {props => {
487
- return (
488
- <Group className='bottom-axis'>
489
- {props.ticks.map((tick, i) => {
490
- const angle = tick.index !== 0 ? config.yAxis.tickRotation : 0
491
- const textAnchor = tick.index !== 0 && config.yAxis.tickRotation && config.yAxis.tickRotation > 0 ? 'end' : 'middle'
492
- return (
493
- <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
494
- {!runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
495
- {!runtime.yAxis.hideLabel && (
496
- <Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
497
- {formatNumber(tick.value, 'left')}
498
- </Text>
499
- )}
500
- </Group>
501
- )
502
- })}
503
- {!runtime.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
504
- </Group>
505
- )
506
- }}
507
- </AxisBottom>
508
- <AxisBottom
509
- top={yMax}
510
- left={Number(runtime.yAxis.size)}
511
- label={runtime.xAxis.label}
512
- tickFormat={isDateScale(runtime.xAxis) ? formatDate : runtime.xAxis.dataKey !== 'Year' ? formatNumber : tick => tick}
513
- scale={g2xScale}
514
- stroke='#333'
515
- tickStroke='#333'
516
- numTicks={runtime.xAxis.numTicks || undefined}
517
- >
518
- {props => {
519
- return (
520
- <>
521
- <Group className='bottom-axis'>
522
- {props.ticks.map((tick, i) => {
523
- const angle = tick.index !== 0 ? config.yAxis.tickRotation : 0
524
- const textAnchor = tick.index !== 0 && config.yAxis.tickRotation && config.yAxis.tickRotation > 0 ? 'end' : 'middle'
525
- return (
526
- <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
527
- {!runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
528
- {!runtime.yAxis.hideLabel && (
529
- <Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
530
- {formatNumber(tick.value, 'left')}
531
- </Text>
532
- )}
533
- </Group>
534
- )
535
- })}
536
- {!runtime.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
537
- </Group>
538
- <Group>
539
- <Text x={xMax / 2} y={config.xAxis.labelOffset} stroke='#333' textAnchor={'middle'} verticalAnchor='start'>
540
- {runtime.xAxis.label}
541
- </Text>
542
- </Group>
543
- </>
544
- )
545
- }}
546
- </AxisBottom>
547
- </>
548
- )}
604
+ {visualizationType === 'Paired Bar' && generatePairedBarAxis()}
549
605
  {visualizationType === 'Deviation Bar' && config.series?.length === 1 && <DeviationBar animatedChart={animatedChart} xScale={xScale} yScale={yScale} width={xMax} height={yMax} />}
550
606
  {visualizationType === 'Paired Bar' && <PairedBarChart originalWidth={width} width={xMax} height={yMax} />}
551
607
  {visualizationType === 'Scatter Plot' && (
@@ -570,7 +626,7 @@ const LinearChart = props => {
570
626
  {((visualizationType === 'Area Chart' && config.visualizationSubType === 'stacked') || visualizationType === 'Combo') && (
571
627
  <AreaChartStacked xScale={xScale} yScale={yScale} yMax={yMax} xMax={xMax} chartRef={svgRef} width={xMax} height={yMax} handleTooltipMouseOver={handleTooltipMouseOver} handleTooltipMouseOff={handleTooltipMouseOff} tooltipData={tooltipData} showTooltip={showTooltip} />
572
628
  )}
573
- {(visualizationType === 'Bar' || visualizationType === 'Combo') && (
629
+ {(visualizationType === 'Bar' || visualizationType === 'Combo' || checkLineToBarGraph()) && (
574
630
  <BarChart
575
631
  xScale={xScale}
576
632
  yScale={yScale}
@@ -589,7 +645,7 @@ const LinearChart = props => {
589
645
  chartRef={svgRef}
590
646
  />
591
647
  )}
592
- {(visualizationType === 'Line' || visualizationType === 'Combo') && (
648
+ {((visualizationType === 'Line' && !checkLineToBarGraph()) || visualizationType === 'Combo') && (
593
649
  <LineChart
594
650
  xScale={xScale}
595
651
  yScale={yScale}
@@ -649,10 +705,10 @@ const LinearChart = props => {
649
705
  />
650
706
  )}
651
707
  {/*Zoom Brush */}
652
- {['Line', 'Bar', 'Combo', 'Area Chart'].includes(config.visualizationType) && !isHorizontal && <ZoomBrush xScaleBrush={xScaleBrush} yScale={yScale} xMax={xMax} yMax={yMax} />}
708
+ {['Line', 'Bar', 'Combo', 'Area Chart'].includes(config.visualizationType) && false && !isHorizontal && <ZoomBrush xScaleBrush={xScaleBrush} yScale={yScale} xMax={xMax} yMax={yMax} />}
653
709
  {/* Line chart */}
654
710
  {/* TODO: Make this just line or combo? */}
655
- {visualizationType !== 'Bar' && visualizationType !== 'Paired Bar' && visualizationType !== 'Box Plot' && visualizationType !== 'Area Chart' && visualizationType !== 'Scatter Plot' && visualizationType !== 'Deviation Bar' && visualizationType !== 'Forecasting' && (
711
+ {!['Paired Bar', 'Box Plot', 'Area Chart', 'Scatter Plot', 'Deviation Bar', 'Forecasting', 'Bar'].includes(visualizationType) && !checkLineToBarGraph() && (
656
712
  <>
657
713
  <LineChart xScale={xScale} yScale={yScale} getXAxisData={getXAxisData} getYAxisData={getYAxisData} xMax={xMax} yMax={yMax} seriesStyle={config.series} />
658
714
  </>
@@ -671,13 +727,13 @@ const LinearChart = props => {
671
727
  return (
672
728
  // prettier-ignore
673
729
  <Line
674
- key={`yAxis-${anchor.value}--${index}`}
675
- strokeDasharray={handleLineType(anchor.lineStyle)}
676
- stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
677
- className='anchor-y'
678
- from={{ x: 0 + padding, y: anchorPosition - middleOffset}}
679
- to={{ x: width - config.yAxis.rightAxisSize, y: anchorPosition - middleOffset }}
680
- />
730
+ key={`yAxis-${anchor.value}--${index}`}
731
+ strokeDasharray={handleLineType(anchor.lineStyle)}
732
+ stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
733
+ className='anchor-y'
734
+ from={{ x: 0 + padding, y: anchorPosition - middleOffset}}
735
+ to={{ x: width - config.yAxis.rightAxisSize, y: anchorPosition - middleOffset }}
736
+ />
681
737
  )
682
738
  })}
683
739
  {/* x anchors */}
@@ -724,31 +780,34 @@ const LinearChart = props => {
724
780
  </Group>
725
781
  )}
726
782
  {config.filters && config.filters.values.length === 0 && data.length === 0 && (
727
- <Text x={Number(config.yAxis.size) + Number(xMax / 2)} y={height / 2 - config.xAxis.size / 2} textAnchor='middle'>
783
+ <Text x={Number(config.yAxis.size) + Number(xMax / 2)} y={height / 2 - config.xAxis.padding / 2} textAnchor='middle'>
728
784
  {config.chartMessage.noData}
729
785
  </Text>
730
786
  )}
731
- {config.visualizationType === 'Bar' && config.tooltips.singleSeries && config.visual.horizontalHoverLine && (
787
+ {(config.visualizationType === 'Bar' || checkLineToBarGraph()) && config.tooltips.singleSeries && config.visual.horizontalHoverLine && (
732
788
  <Group key='tooltipLine-horizontal' className='horizontal-tooltip-line' left={config.yAxis.size ? config.yAxis.size : 0}>
733
789
  <Line from={{ x: 0, y: point.y }} to={{ x: xMax, y: point.y }} stroke={'black'} strokeWidth={1} pointerEvents='none' strokeDasharray='5,5' className='horizontal-tooltip-line' />
734
790
  </Group>
735
791
  )}
736
- {config.visualizationType === 'Bar' && config.tooltips.singleSeries && config.visual.verticalHoverLine && (
792
+ {(config.visualizationType === 'Bar' || checkLineToBarGraph()) && config.tooltips.singleSeries && config.visual.verticalHoverLine && (
737
793
  <Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
738
794
  <Line from={{ x: point.x, y: 0 }} to={{ x: point.x, y: yMax }} stroke={'black'} strokeWidth={1} pointerEvents='none' strokeDasharray='5,5' className='vertical-tooltip-line' />
739
795
  </Group>
740
796
  )}
797
+ <Group left={Number(config.runtime.yAxis.size)}>
798
+ <Annotation.Draggable xScale={xScale} yScale={yScale} xScaleAnnotation={xScaleAnnotation} xMax={xMax} svgRef={svgRef} onDragStateChange={handleDragStateChange} />
799
+ </Group>
741
800
  </svg>
742
- {tooltipData && Object.entries(tooltipData.data).length > 0 && tooltipOpen && showTooltip && tooltipData.dataYPosition && tooltipData.dataXPosition && !config.tooltips.singleSeries && (
801
+ {!isDraggingAnnotation && tooltipData && Object.entries(tooltipData.data).length > 0 && tooltipOpen && showTooltip && tooltipData.dataYPosition && tooltipData.dataXPosition && (
743
802
  <>
744
- <style>{`.tooltip {background-color: rgba(255,255,255, ${config.tooltips.opacity / 100}) !important`}</style>
803
+ <style>{`.tooltip {background-color: rgba(255,255,255, ${config.tooltips.opacity / 100}) !important;`}</style>
804
+ <style>{`.tooltip {max-width:300px} !important; word-wrap: break-word; `}</style>
745
805
  <TooltipWithBounds key={Math.random()} className={'tooltip cdc-open-viz-module'} left={tooltipLeft} top={tooltipTop}>
746
806
  <ul>{typeof tooltipData === 'object' && Object.entries(tooltipData.data).map((item, index) => <TooltipListItem item={item} key={index} />)}</ul>
747
807
  </TooltipWithBounds>
748
808
  </>
749
809
  )}
750
-
751
- {visSupportsReactTooltip() && <ReactTooltip id={`cdc-open-viz-tooltip-${runtime.uniqueId}`} variant='light' arrowColor='rgba(0,0,0,0)' className='tooltip' style={{ background: `rgba(255,255,255, ${config.tooltips.opacity / 100})`, color: 'black' }} />}
810
+ {visSupportsReactTooltip() && !isDraggingAnnotation && <ReactTooltip id={`cdc-open-viz-tooltip-${runtime.uniqueId}`} variant='light' arrowColor='rgba(0,0,0,0)' className='tooltip' style={{ background: `rgba(255,255,255, ${config.tooltips.opacity / 100})`, color: 'black' }} />}
752
811
  <div className='animation-trigger' ref={triggerRef} />
753
812
  </div>
754
813
  </ErrorBoundary>
@@ -18,7 +18,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
18
18
  const offset = 1.02 // Offset of the left bar from the Axis
19
19
 
20
20
  const groupOne = {
21
- parentKey: config.dataDescription.seriesKey,
21
+ parentKey: config.dataDescription?.seriesKey,
22
22
  dataKey: config.series[0].dataKey,
23
23
  dataKeyLabel: config.runtime.seriesLabels[config.series[0].dataKey] || config.series[0].dataKey,
24
24
  color: colorScale(config.runtime.seriesLabels[config.series[0].dataKey]),
@@ -30,7 +30,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
30
30
  }
31
31
 
32
32
  const groupTwo = {
33
- parentKey: config.dataDescription.seriesKey,
33
+ parentKey: config.dataDescription?.seriesKey,
34
34
  dataKey: config.series[1].dataKey,
35
35
  dataKeyLabel: config.runtime.seriesLabels[config.series[1].dataKey] || config.series[1].dataKey,
36
36
  color: colorScale(config.runtime.seriesLabels[config.series[1].dataKey]),
@@ -54,7 +54,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
54
54
 
55
55
  const dataTipOne = d => {
56
56
  return `<p>
57
- ${config.dataDescription.seriesKey}: ${groupOne.dataKeyLabel}<br/>
57
+ ${config.dataDescription?.seriesKey}: ${groupOne.dataKeyLabel}<br/>
58
58
  ${config.xAxis.dataKey}: ${d[config.xAxis.dataKey]}<br/>
59
59
  ${label}${formatNumber(d[groupOne.dataKey], 'left')}
60
60
  </p>`
@@ -62,7 +62,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
62
62
 
63
63
  const dataTipTwo = d => {
64
64
  return `<p>
65
- ${config.dataDescription.seriesKey}: ${groupTwo.dataKeyLabel}<br/>
65
+ ${config.dataDescription?.seriesKey}: ${groupTwo.dataKeyLabel}<br/>
66
66
  ${config.xAxis.dataKey}: ${d[config.xAxis.dataKey]}<br/>
67
67
  ${label}${formatNumber(d[groupTwo.dataKey], 'left')}
68
68
  </p>`
@@ -102,9 +102,9 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
102
102
  <>
103
103
  <Group key={`group-${groupOne.dataKey}-${d[config.xAxis.dataKey]}`} className='horizontal'>
104
104
  <Bar
105
- id={`bar-${groupOne.dataKey}-${d[config.dataDescription.xKey]}`}
105
+ id={`bar-${groupOne.dataKey}-${d[config.dataDescription?.xKey]}`}
106
106
  className='bar group-1'
107
- key={`bar-${groupOne.dataKey}-${d[config.dataDescription.xKey]}`}
107
+ key={`bar-${groupOne.dataKey}-${d[config.dataDescription?.xKey]}`}
108
108
  x={halfWidth - barWidth}
109
109
  y={y}
110
110
  width={xScale(d[config.series[0].dataKey])}
@@ -152,11 +152,11 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
152
152
  }
153
153
  `}
154
154
  </style>
155
- <Group key={`group-${groupTwo.dataKey}-${d[config.dataDescription.xKey]}`} className='horizontal'>
155
+ <Group key={`group-${groupTwo.dataKey}-${d[config.dataDescription?.xKey]}`} className='horizontal'>
156
156
  <Bar
157
- id={`bar-${groupTwo.dataKey}-${d[config.dataDescription.xKey]}`}
157
+ id={`bar-${groupTwo.dataKey}-${d[config.dataDescription?.xKey]}`}
158
158
  className='bar group-2'
159
- key={`bar-${groupTwo.dataKey}-${d[config.dataDescription.xKey]}`}
159
+ key={`bar-${groupTwo.dataKey}-${d[config.dataDescription?.xKey]}`}
160
160
  x={halfWidth}
161
161
  y={y}
162
162
  width={xScale(d[config.series[1].dataKey])}
@@ -33,7 +33,7 @@ type TooltipData = {
33
33
  }
34
34
 
35
35
  const PieChart = props => {
36
- const { transformedData: data, config, colorScale, currentViewport, dimensions, highlight, highlightReset, seriesHighlight } = useContext(ConfigContext)
36
+ const { transformedData: data, config, colorScale, currentViewport, dimensions, highlight, highlightReset, seriesHighlight, isDraggingAnnotation } = useContext(ConfigContext)
37
37
  const { tooltipData, showTooltip, hideTooltip, tooltipOpen, tooltipLeft, tooltipTop } = useTooltip<TooltipData>()
38
38
  const { handleTooltipMouseOver, handleTooltipMouseOff, TooltipListItem } = useCoveTooltip({
39
39
  xScale: false,
@@ -208,8 +208,8 @@ const PieChart = props => {
208
208
  return (
209
209
  <>
210
210
  <ErrorBoundary component='PieChart'>
211
- <svg width={width} height={height} className={`animated-pie group ${config.animate === false || animatedPie ? 'animated' : ''}`} role='img' aria-label={handleChartAriaLabels(config)}>
212
- <Group top={centerY} left={centerX}>
211
+ <svg width={radius * 2} height={height} className={`animated-pie group ${config.animate === false || animatedPie ? 'animated' : ''}`} role='img' aria-label={handleChartAriaLabels(config)}>
212
+ <Group top={centerY} left={radius}>
213
213
  {/* prettier-ignore */}
214
214
  <Pie
215
215
  data={filteredData || _data}
@@ -223,7 +223,7 @@ const PieChart = props => {
223
223
  </Group>
224
224
  </svg>
225
225
  <div ref={triggerRef} />
226
- {tooltipData && Object.entries(tooltipData.data).length > 0 && tooltipOpen && showTooltip && tooltipData.dataYPosition && tooltipData.dataXPosition && (
226
+ {!isDraggingAnnotation && tooltipData && Object.entries(tooltipData.data).length > 0 && tooltipOpen && showTooltip && tooltipData.dataYPosition && tooltipData.dataXPosition && (
227
227
  <>
228
228
  <style>{`.tooltip {background-color: rgba(255,255,255, ${config.tooltips.opacity / 100}) !important`}</style>
229
229
  <TooltipWithBounds key={Math.random()} className={'tooltip cdc-open-viz-module'} left={tooltipLeft} top={tooltipTop}>