@cdc/chart 4.24.5 → 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 (68) hide show
  1. package/dist/cdcchart.js +39128 -35959
  2. package/examples/feature/annotations/index.json +542 -0
  3. package/examples/xaxis.json +493 -0
  4. package/index.html +5 -4
  5. package/package.json +5 -4
  6. package/src/CdcChart.tsx +104 -64
  7. package/src/_stories/Chart.stories.tsx +18 -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/line_chart_two_points_new_chart.json +128 -0
  13. package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
  14. package/src/_stories/_mock/lollipop.json +171 -0
  15. package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
  16. package/src/components/Annotations/components/AnnotationDraggable.tsx +154 -0
  17. package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
  18. package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
  19. package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
  20. package/src/components/Annotations/components/AnnotationList.tsx +42 -0
  21. package/src/components/Annotations/components/findNearestDatum.ts +138 -0
  22. package/src/components/Annotations/components/helpers/index.tsx +46 -0
  23. package/src/components/Annotations/index.tsx +13 -0
  24. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
  25. package/src/components/AreaChart/components/AreaChart.jsx +1 -1
  26. package/src/components/BarChart/components/BarChart.Horizontal.tsx +44 -42
  27. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -2
  28. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -11
  29. package/src/components/BarChart/components/BarChart.Vertical.tsx +50 -22
  30. package/src/components/BarChart/helpers/index.ts +102 -0
  31. package/src/components/EditorPanel/EditorPanel.tsx +232 -98
  32. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +306 -0
  33. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +117 -6
  34. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +2 -3
  35. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +3 -2
  36. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  37. package/src/components/EditorPanel/components/panels.scss +4 -0
  38. package/src/components/EditorPanel/editor-panel.scss +19 -0
  39. package/src/components/EditorPanel/useEditorPermissions.js +19 -2
  40. package/src/components/Legend/Legend.Component.tsx +7 -8
  41. package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
  42. package/src/components/Legend/helpers/index.ts +5 -0
  43. package/src/components/LineChart/LineChartProps.ts +3 -0
  44. package/src/components/LineChart/helpers.ts +21 -7
  45. package/src/components/LineChart/index.tsx +7 -7
  46. package/src/components/LinearChart.jsx +179 -136
  47. package/src/components/PairedBarChart.jsx +9 -9
  48. package/src/components/PieChart/PieChart.tsx +4 -4
  49. package/src/components/Sankey/index.tsx +73 -20
  50. package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
  51. package/src/components/ZoomBrush.tsx +90 -44
  52. package/src/data/initial-state.js +14 -6
  53. package/src/helpers/handleChartTabbing.ts +8 -0
  54. package/src/helpers/isConvertLineToBarGraph.ts +4 -0
  55. package/src/hooks/{useBarChart.js → useBarChart.ts} +2 -40
  56. package/src/hooks/useColorScale.ts +1 -1
  57. package/src/hooks/useMinMax.ts +8 -3
  58. package/src/hooks/useScales.ts +25 -3
  59. package/src/hooks/useTooltip.tsx +58 -11
  60. package/src/scss/main.scss +21 -14
  61. package/src/types/ChartConfig.ts +50 -3
  62. package/src/types/ChartContext.ts +9 -0
  63. package/tests-examples/helpers/testZeroValue.test.ts +30 -0
  64. package/LICENSE +0 -201
  65. package/src/helpers/filterData.ts +0 -18
  66. package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
  67. /package/src/hooks/{useLegendClasses.js → useLegendClasses.ts} +0 -0
  68. /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
@@ -24,6 +24,9 @@ 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'
@@ -32,27 +35,55 @@ 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, tableData, dimensions, config, parseDate, formatDate, currentViewport, formatNumber, handleChartAriaLabels, updateConfig, handleLineType, getTextWidth, brushConfig } = 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.padding || 0))
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 && brushConfig.data?.length ? brushConfig.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
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') {
@@ -207,7 +243,7 @@ const LinearChart = props => {
207
243
  return false
208
244
  }
209
245
 
210
- const padding = orientation === 'horizontal' ? Number(config.xAxis.padding) : Number(config.yAxis.size)
246
+ const padding = orientation === 'horizontal' ? Number(config.xAxis.size) : Number(config.yAxis.size)
211
247
  const fontSize = { small: 16, medium: 18, large: 20 }
212
248
 
213
249
  const handleNumTicks = () => {
@@ -216,6 +252,14 @@ const LinearChart = props => {
216
252
  return countNumOfTicks('yAxis')
217
253
  }
218
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
+
219
263
  const onMouseMove = event => {
220
264
  const svgRect = event.currentTarget.getBoundingClientRect()
221
265
  const x = event.clientX - svgRect.left
@@ -228,60 +272,22 @@ const LinearChart = props => {
228
272
  }
229
273
 
230
274
  const generatePairedBarAxis = () => {
231
- let axisMaxHeight = 40;
232
-
233
- return <>
234
- <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}>
235
- {props => {
236
- return (
237
- <Group className='bottom-axis'>
238
- {props.ticks.map((tick, i) => {
239
- const angle = tick.index !== 0 ? config.yAxis.tickRotation : 0
240
- const textAnchor = tick.index !== 0 && config.yAxis.tickRotation && config.yAxis.tickRotation > 0 ? 'end' : 'middle'
241
-
242
- const textWidth = getTextWidth(tick.value, `normal ${fontSize[config.fontSize]}px sans-serif`)
243
- const axisHeight = textWidth * Math.sin(angle * (Math.PI / 180)) + 25;
275
+ let axisMaxHeight = 40
244
276
 
245
- if(axisHeight > axisMaxHeight) axisMaxHeight = axisHeight
246
-
247
- return (
248
- <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
249
- {!runtime.yAxis.hideTicks && <Line from={tick.from} to={tick.to} stroke='#333' />}
250
- {!runtime.yAxis.hideLabel && (
251
- <Text x={tick.to.x} y={tick.to.y} angle={-angle} verticalAnchor='start' textAnchor={textAnchor}>
252
- {formatNumber(tick.value, 'left')}
253
- </Text>
254
- )}
255
- </Group>
256
- )
257
- })}
258
- {!runtime.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
259
- </Group>
260
- )
261
- }}
262
- </AxisBottom>
263
- <AxisBottom
264
- top={yMax}
265
- left={Number(runtime.yAxis.size)}
266
- label={runtime.xAxis.label}
267
- tickFormat={isDateScale(runtime.xAxis) ? formatDate : runtime.xAxis.dataKey !== 'Year' ? formatNumber : tick => tick}
268
- scale={g2xScale}
269
- stroke='#333'
270
- tickStroke='#333'
271
- numTicks={runtime.xAxis.numTicks || undefined}
272
- >
273
- {props => {
274
- return (
275
- <>
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 (
276
282
  <Group className='bottom-axis'>
277
283
  {props.ticks.map((tick, i) => {
278
284
  const angle = tick.index !== 0 ? config.yAxis.tickRotation : 0
279
285
  const textAnchor = tick.index !== 0 && config.yAxis.tickRotation && config.yAxis.tickRotation > 0 ? 'end' : 'middle'
280
286
 
281
287
  const textWidth = getTextWidth(tick.value, `normal ${fontSize[config.fontSize]}px sans-serif`)
282
- const axisHeight = textWidth * Math.sin(angle * (Math.PI / 180)) + 25;
288
+ const axisHeight = textWidth * Math.sin(angle * (Math.PI / 180)) + 25
283
289
 
284
- if(axisHeight > axisMaxHeight) axisMaxHeight = axisHeight
290
+ if (axisHeight > axisMaxHeight) axisMaxHeight = axisHeight
285
291
 
286
292
  return (
287
293
  <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
@@ -296,17 +302,57 @@ const LinearChart = props => {
296
302
  })}
297
303
  {!runtime.yAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
298
304
  </Group>
299
- <Group>
300
- <Text x={xMax / 2} y={axisMaxHeight + 20} stroke='#333' textAnchor={'middle'} verticalAnchor='start'>
301
- {runtime.xAxis.label}
302
- </Text>
303
- </Group>
304
- {svgRef.current ? svgRef.current.setAttribute('height', (Number(height) + Number(axisMaxHeight) + (runtime.xAxis.label ? 50 : 0)) + 'px') : ''}
305
- </>
306
- )
307
- }}
308
- </AxisBottom>
309
- </>
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
+ )
310
356
  }
311
357
 
312
358
  return isNaN(width) ? (
@@ -316,17 +362,16 @@ const LinearChart = props => {
316
362
  {/* ! Notice - div needed for tooltip boundaries (flip/flop) */}
317
363
  <div style={{ width: `${width}px`, overflow: 'visible' }} className='tooltip-boundary'>
318
364
  <svg
319
- // onMouseLeave={() => setPoint(null)}
320
365
  onMouseMove={onMouseMove}
321
366
  width={'100%'}
322
367
  height={height}
323
- className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${debugSvg && 'debug'}`}
368
+ className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${debugSvg && 'debug'} ${isDraggingAnnotation && 'dragging-annotation'}`}
324
369
  role='img'
325
370
  aria-label={handleChartAriaLabels(config)}
326
371
  ref={svgRef}
327
372
  style={{ overflow: 'visible' }}
328
373
  >
329
- <Bar width={width} height={height} fill={'transparent'}></Bar> {/* Highlighted regions */}
374
+ {!isDraggingAnnotation && <Bar width={width} height={height} fill={'transparent'}></Bar>} {/* Highlighted regions */}
330
375
  {/* Y axis */}
331
376
  {!['Spark Line', 'Forest Plot'].includes(visualizationType) && (
332
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()}>
@@ -341,12 +386,13 @@ const LinearChart = props => {
341
386
  const showTicks = String(tick.value).startsWith('1') || tick.value === 0.1 ? 'block' : 'none'
342
387
  const tickLength = showTicks === 'block' ? 7 : 0
343
388
  const to = { x: tick.to.x - tickLength, y: tick.to.y }
389
+ const displayFirstGridLine = tick.index !== 0 || config.xAxis.hideAxis
344
390
 
345
391
  return (
346
392
  <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
347
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'} />}
348
394
 
349
- {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)' /> : ''}
350
396
 
351
397
  {orientation === 'horizontal' && visualizationSubType !== 'stacked' && config.yAxis.labelPlacement === 'On Date/Category Axis' && !config.yAxis.hideLabel && (
352
398
  <Text
@@ -395,7 +441,7 @@ const LinearChart = props => {
395
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' />}
396
442
  {yScale.domain()[0] < 0 && <Line from={{ x: props.axisFromPoint.x, y: yScale(0) }} to={{ x: xMax, y: yScale(0) }} stroke='#333' />}
397
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} />}
398
- <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}>
399
445
  {props.label}
400
446
  </Text>
401
447
  </Group>
@@ -458,7 +504,7 @@ const LinearChart = props => {
458
504
  stroke='#333'
459
505
  numTicks={countNumOfTicks('xAxis')}
460
506
  tickStroke='#333'
461
- 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}
462
508
  >
463
509
  {props => {
464
510
  const axisCenter = config.visualizationType !== 'Forest Plot' ? (props.axisToPoint.x - props.axisFromPoint.x) / 2 : dimensions[0] / 2
@@ -499,62 +545,57 @@ const LinearChart = props => {
499
545
  config.dynamicMarginTop = dynamicMarginTop
500
546
  config.xAxis.tickWidthMax = tickWidthMax
501
547
 
502
- let axisMaxHeight = 40;
548
+ let axisMaxHeight = 40
503
549
 
504
- const axisContents = <Group className='bottom-axis' width={dimensions[0]}>
505
- {props.ticks.map((tick, i, propsTicks) => {
506
- // when using LogScale show major ticks values only
507
- const showTick = String(tick.value).startsWith('1') || tick.value === 0.1 ? 'block' : 'none'
508
- const tickLength = showTick === 'block' ? 16 : defaultTickLength
509
- const to = { x: tick.to.x, y: tickLength }
510
- const textWidth = getTextWidth(tick.formattedValue, `normal ${fontSize[config.fontSize]}px sans-serif`)
511
- const limitedWidth = 100 / propsTicks.length
512
- //reset rotations by updating config
513
- config.yAxis.tickRotation = config.isResponsiveTicks && config.orientation === 'horizontal' ? 0 : config.yAxis.tickRotation
514
- config.xAxis.tickRotation = config.isResponsiveTicks && config.orientation === 'vertical' ? 0 : config.xAxis.tickRotation
515
- //configure rotation
550
+ const axisContents = (
551
+ <Group className='bottom-axis' width={dimensions[0]}>
552
+ {props.ticks.map((tick, i, propsTicks) => {
553
+ // when using LogScale show major ticks values only
554
+ const showTick = String(tick.value).startsWith('1') || tick.value === 0.1 ? 'block' : 'none'
555
+ const tickLength = showTick === 'block' ? 16 : defaultTickLength
556
+ const to = { x: tick.to.x, y: tickLength }
557
+ const textWidth = getTextWidth(tick.formattedValue, `normal ${fontSize[config.fontSize]}px sans-serif`)
558
+ const limitedWidth = 100 / propsTicks.length
559
+ //reset rotations by updating config
560
+ config.yAxis.tickRotation = config.isResponsiveTicks && config.orientation === 'horizontal' ? 0 : config.yAxis.tickRotation
561
+ config.xAxis.tickRotation = config.isResponsiveTicks && config.orientation === 'vertical' ? 0 : config.xAxis.tickRotation
562
+ //configure rotation
516
563
 
517
- const tickRotation = config.isResponsiveTicks && areTicksTouching ? -Number(config.xAxis.maxTickRotation) || -90 : -Number(config.runtime.xAxis.tickRotation)
564
+ const tickRotation = config.isResponsiveTicks && areTicksTouching ? -Number(config.xAxis.maxTickRotation) || -90 : -Number(config.runtime.xAxis.tickRotation)
518
565
 
519
- const axisHeight = textWidth * Math.sin(tickRotation * -1 * (Math.PI / 180)) + 25;
566
+ const axisHeight = textWidth * Math.sin(tickRotation * -1 * (Math.PI / 180)) + 25
520
567
 
521
- if(axisHeight > axisMaxHeight) axisMaxHeight = axisHeight
568
+ if (axisHeight > axisMaxHeight) axisMaxHeight = axisHeight
522
569
 
523
- return (
524
- <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
525
- {!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} />}
526
- {!config.xAxis.hideLabel && (
527
- <Text
528
- dy={config.orientation === 'horizontal' && config.useLogScale ? 8 : 0}
529
- display={config.orientation === 'horizontal' && config.useLogScale ? showTick : 'block'}
530
- x={tick.to.x}
531
- y={tick.to.y}
532
- angle={tickRotation}
533
- verticalAnchor={tickRotation < -50 ? 'middle' : 'start'}
534
- textAnchor={tickRotation ? 'end' : 'middle'}
535
- width={areTicksTouching && !config.isResponsiveTicks && !Number(config[section].tickRotation) ? limitedWidth : undefined}
536
- fill={config.xAxis.tickLabelColor}
537
- >
538
- {tick.formattedValue}
539
- </Text>
540
- )}
541
- </Group>
542
- )
543
- })}
544
- {!config.xAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
545
- <Text
546
- x={axisCenter}
547
- y={axisMaxHeight + 20}
548
- textAnchor='middle'
549
- verticalAnchor='start'
550
- fontWeight='bold'
551
- fill={config.xAxis.labelColor}
552
- >
553
- {props.label}
554
- </Text>
555
- </Group>
570
+ return (
571
+ <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
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} />}
573
+ {!config.xAxis.hideLabel && (
574
+ <Text
575
+ dy={config.orientation === 'horizontal' && config.useLogScale ? 8 : 0}
576
+ display={config.orientation === 'horizontal' && config.useLogScale ? showTick : 'block'}
577
+ x={tick.to.x}
578
+ y={tick.to.y}
579
+ angle={tickRotation}
580
+ verticalAnchor={tickRotation < -50 ? 'middle' : 'start'}
581
+ textAnchor={tickRotation ? 'end' : 'middle'}
582
+ width={areTicksTouching && !config.isResponsiveTicks && !Number(config[section].tickRotation) ? limitedWidth : undefined}
583
+ fill={config.xAxis.tickLabelColor}
584
+ >
585
+ {tick.formattedValue}
586
+ </Text>
587
+ )}
588
+ </Group>
589
+ )
590
+ })}
591
+ {!config.xAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
592
+ <Text x={axisCenter} y={axisMaxHeight + 20 + xLabelOffset} textAnchor='middle' verticalAnchor='start' fontWeight='bold' fill={config.xAxis.labelColor}>
593
+ {props.label}
594
+ </Text>
595
+ </Group>
596
+ )
556
597
 
557
- if(svgRef.current) svgRef.current.setAttribute('height',(Number(height) + Number(axisMaxHeight) + (runtime.xAxis.label ? 50 : 0)) + 'px')
598
+ if (svgRef.current) svgRef.current.setAttribute('height', Number(height) + Number(axisMaxHeight) + (runtime.xAxis.label ? 50 : 0) + 'px')
558
599
 
559
600
  return axisContents
560
601
  }}
@@ -585,7 +626,7 @@ const LinearChart = props => {
585
626
  {((visualizationType === 'Area Chart' && config.visualizationSubType === 'stacked') || visualizationType === 'Combo') && (
586
627
  <AreaChartStacked xScale={xScale} yScale={yScale} yMax={yMax} xMax={xMax} chartRef={svgRef} width={xMax} height={yMax} handleTooltipMouseOver={handleTooltipMouseOver} handleTooltipMouseOff={handleTooltipMouseOff} tooltipData={tooltipData} showTooltip={showTooltip} />
587
628
  )}
588
- {(visualizationType === 'Bar' || visualizationType === 'Combo') && (
629
+ {(visualizationType === 'Bar' || visualizationType === 'Combo' || checkLineToBarGraph()) && (
589
630
  <BarChart
590
631
  xScale={xScale}
591
632
  yScale={yScale}
@@ -604,7 +645,7 @@ const LinearChart = props => {
604
645
  chartRef={svgRef}
605
646
  />
606
647
  )}
607
- {(visualizationType === 'Line' || visualizationType === 'Combo') && (
648
+ {((visualizationType === 'Line' && !checkLineToBarGraph()) || visualizationType === 'Combo') && (
608
649
  <LineChart
609
650
  xScale={xScale}
610
651
  yScale={yScale}
@@ -664,10 +705,10 @@ const LinearChart = props => {
664
705
  />
665
706
  )}
666
707
  {/*Zoom Brush */}
667
- {['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} />}
668
709
  {/* Line chart */}
669
710
  {/* TODO: Make this just line or combo? */}
670
- {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() && (
671
712
  <>
672
713
  <LineChart xScale={xScale} yScale={yScale} getXAxisData={getXAxisData} getYAxisData={getYAxisData} xMax={xMax} yMax={yMax} seriesStyle={config.series} />
673
714
  </>
@@ -686,13 +727,13 @@ const LinearChart = props => {
686
727
  return (
687
728
  // prettier-ignore
688
729
  <Line
689
- key={`yAxis-${anchor.value}--${index}`}
690
- strokeDasharray={handleLineType(anchor.lineStyle)}
691
- stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
692
- className='anchor-y'
693
- from={{ x: 0 + padding, y: anchorPosition - middleOffset}}
694
- to={{ x: width - config.yAxis.rightAxisSize, y: anchorPosition - middleOffset }}
695
- />
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
+ />
696
737
  )
697
738
  })}
698
739
  {/* x anchors */}
@@ -743,18 +784,21 @@ const LinearChart = props => {
743
784
  {config.chartMessage.noData}
744
785
  </Text>
745
786
  )}
746
- {config.visualizationType === 'Bar' && config.tooltips.singleSeries && config.visual.horizontalHoverLine && (
787
+ {(config.visualizationType === 'Bar' || checkLineToBarGraph()) && config.tooltips.singleSeries && config.visual.horizontalHoverLine && (
747
788
  <Group key='tooltipLine-horizontal' className='horizontal-tooltip-line' left={config.yAxis.size ? config.yAxis.size : 0}>
748
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' />
749
790
  </Group>
750
791
  )}
751
- {config.visualizationType === 'Bar' && config.tooltips.singleSeries && config.visual.verticalHoverLine && (
792
+ {(config.visualizationType === 'Bar' || checkLineToBarGraph()) && config.tooltips.singleSeries && config.visual.verticalHoverLine && (
752
793
  <Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
753
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' />
754
795
  </Group>
755
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>
756
800
  </svg>
757
- {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 && (
758
802
  <>
759
803
  <style>{`.tooltip {background-color: rgba(255,255,255, ${config.tooltips.opacity / 100}) !important;`}</style>
760
804
  <style>{`.tooltip {max-width:300px} !important; word-wrap: break-word; `}</style>
@@ -763,8 +807,7 @@ const LinearChart = props => {
763
807
  </TooltipWithBounds>
764
808
  </>
765
809
  )}
766
-
767
- {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' }} />}
768
811
  <div className='animation-trigger' ref={triggerRef} />
769
812
  </div>
770
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}>