@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
@@ -1,33 +1,36 @@
1
1
  import React, { useContext, useState } from 'react'
2
+ // Local contexts
2
3
  import ConfigContext from '../../../ConfigContext'
3
- import { type ChartContext } from '../../../types/ChartContext'
4
+ import BarChartContext, { type BarChartContextValues } from './context'
5
+ // Local hooks
4
6
  import { useBarChart } from '../../../hooks/useBarChart'
7
+ import { useHighlightedBars } from '../../../hooks/useHighlightedBars'
8
+ import { getBarConfig, testZeroValue } from '../helpers'
9
+ import { isConvertLineToBarGraph } from '../../../helpers/isConvertLineToBarGraph'
10
+ // VisX library imports
5
11
  import { Group } from '@visx/group'
6
12
  import { Text } from '@visx/text'
7
13
  import { BarGroup } from '@visx/shape'
8
- import { useHighlightedBars } from '../../../hooks/useHighlightedBars'
9
- import { FaStar } from 'react-icons/fa'
10
- import Regions from './../../Regions'
14
+ // Local components
15
+ import Regions from '../../Regions'
16
+ // CDC core components and helpers
11
17
  import { isDateScale } from '@cdc/core/helpers/cove/date'
12
-
13
18
  import createBarElement from '@cdc/core/components/createBarElement'
14
-
15
- // third party
19
+ // Third party libraries
16
20
  import chroma from 'chroma-js'
17
- import BarChartContext, { type BarChartContextValues } from './context'
21
+ // Types
22
+ import { type ChartContext } from '../../../types/ChartContext'
18
23
 
19
24
  export const BarChartVertical = () => {
20
25
  const { xScale, yScale, xMax, yMax, seriesScale } = useContext<BarChartContextValues>(BarChartContext)
21
26
 
22
27
  const [barWidth, setBarWidth] = useState(0)
23
28
  const [totalBarsInGroup, setTotalBarsInGroup] = useState(0)
24
-
25
29
  // prettier-ignore
26
30
  const {
27
- applyRadius,
31
+ // prettier-ignore
28
32
  assignColorsToValues,
29
33
  barBorderWidth,
30
- generateIconSize,
31
34
  getAdditionalColumn,
32
35
  getHighlightedBarByValue,
33
36
  getHighlightedBarColorByValue,
@@ -39,37 +42,23 @@ export const BarChartVertical = () => {
39
42
  } = useBarChart()
40
43
 
41
44
  // prettier-ignore
42
- const {
43
- colorScale,
44
- config,
45
- dashboardConfig,
46
- formatDate,
47
- formatNumber,
48
- getXAxisData,
49
- getYAxisData,
50
- isNumber,
51
- parseDate,
52
- seriesHighlight,
53
- setSharedFilter,
54
- transformedData,
55
- } = useContext<ChartContext>(ConfigContext)
56
-
45
+ const { colorScale, config, dashboardConfig, tableData, formatDate, formatNumber, getXAxisData, getYAxisData, isNumber, parseDate, seriesHighlight, setSharedFilter, transformedData, brushConfig, getTextWidth } = useContext<ChartContext>(ConfigContext)
57
46
  const { HighLightedBarUtils } = useHighlightedBars(config)
58
- const data = config.brush.active && config.brush.data?.length ? config.brush.data : transformedData
59
-
60
- const getIcon = (bar, barWidth) => {
61
- let icon = null
62
- const iconSize = generateIconSize(barWidth)
63
- config.suppressedData?.forEach(d => {
64
- if (bar.key === d.column && String(bar.value) === String(d.value) && d.icon) {
65
- icon = <FaStar color='#000' size={iconSize} />
66
- }
67
- })
68
- return icon
47
+ let data = transformedData
48
+ // check if user add suppression
49
+ const isSuppressionActive = config.preliminaryData.some(pd => pd.value && pd.type === 'suppression')
50
+ // if suppression active use table data (filtere | excluded) but non cleaned
51
+ if (isSuppressionActive) {
52
+ data = tableData
53
+ }
54
+ // if brush active use brush data (filtered|excluded) not cleaned
55
+ if (brushConfig.data.length) {
56
+ data = brushConfig.data
69
57
  }
58
+
70
59
  return (
71
60
  config.visualizationSubType !== 'stacked' &&
72
- (config.visualizationType === 'Bar' || config.visualizationType === 'Combo') &&
61
+ (config.visualizationType === 'Bar' || config.visualizationType === 'Combo' || isConvertLineToBarGraph(config.visualizationType, data, config.allowLineToBarGraph)) &&
73
62
  config.orientation === 'vertical' && (
74
63
  <Group>
75
64
  <BarGroup
@@ -92,30 +81,24 @@ export const BarChartVertical = () => {
92
81
  <Group className={`bar-group-${barGroup.index}-${barGroup.x0}--${index} ${config.orientation}`} key={`bar-group-${barGroup.index}-${barGroup.x0}--${index}`} id={`bar-group-${barGroup.index}-${barGroup.x0}--${index}`} left={barGroup.x0}>
93
82
  {barGroup.bars.map((bar, index) => {
94
83
  const scaleVal = config.useLogScale ? 0.1 : 0
95
- const suppresedBarHeight = 20
96
84
  let highlightedBarValues = config.highlightedBarValues.map(item => item.value).filter(item => item !== ('' || undefined))
97
85
  highlightedBarValues = config.xAxis.type === 'date' ? HighLightedBarUtils.formatDates(highlightedBarValues) : highlightedBarValues
98
- let transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
99
- let displayBar = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(bar.key) !== -1
100
- let barHeightBase = Math.abs(yScale(bar.value) - yScale(scaleVal))
101
- let barYBase = bar.value >= 0 && isNumber(bar.value) ? bar.y : yScale(0)
102
- const supprssedBarY = bar.value >= 0 && isNumber(bar.value) ? yScale(scaleVal) - suppresedBarHeight : yScale(0)
103
- const barY = config.suppressedData.some(d => bar.key === d.column && String(bar.value) === String(d.value)) ? supprssedBarY : barYBase
104
-
105
- let barGroupWidth = seriesScale.range()[1]
86
+ const transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
87
+ const displayBar = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(bar.key) !== -1
106
88
 
107
- let barWidth = config.isLollipopChart ? lollipopBarWidth : barGroupWidth / barGroup.bars.length
89
+ let barGroupWidth = seriesScale.range()[1] - seriesScale.range()[0]
90
+ const defaultBarHeight = Math.abs(yScale(bar.value) - yScale(scaleVal))
91
+ const defaultBarY = bar.value >= 0 && isNumber(bar.value) ? bar.y : yScale(0)
92
+ let barWidth = config.isLollipopChart ? lollipopBarWidth : seriesScale.bandwidth()
108
93
  let barX = bar.x + (config.isLollipopChart ? (barGroupWidth / barGroup.bars.length - lollipopBarWidth) / 2 : 0) - (config.xAxis.type === 'date-time' ? barGroupWidth / 2 : 0)
109
94
  setBarWidth(barWidth)
110
95
  setTotalBarsInGroup(barGroup.bars.length)
111
-
112
- let yAxisValue = formatNumber(bar.value, 'left')
113
- let xAxisValue = config.runtime[section].type === 'date' ? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey])) : data[barGroup.index][config.runtime.originalXAxis.dataKey]
96
+ const yAxisValue = formatNumber(/[a-zA-Z]/.test(String(bar.value)) ? '' : bar.value, 'left')
97
+ const xAxisValue = config.runtime[section].type === 'date' ? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey])) : data[barGroup.index][config.runtime.originalXAxis.dataKey]
114
98
 
115
99
  // create new Index for bars with negative values
116
100
  const newIndex = bar.value < 0 ? -1 : index
117
101
  // tooltips
118
-
119
102
  const additionalColTooltip = getAdditionalColumn(bar.key, data[barGroup.index][config.runtime.originalXAxis.dataKey])
120
103
  let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
121
104
  const tooltipBody = `${config.runtime.seriesLabels[bar.key]}: ${yAxisValue}`
@@ -138,10 +121,13 @@ export const BarChartVertical = () => {
138
121
  const highlightedBar = getHighlightedBarByValue(xAxisValue)
139
122
  const borderColor = isHighlightedBar ? highlightedBarColor : config.barHasBorder === 'true' ? '#000' : 'transparent'
140
123
  const borderWidth = isHighlightedBar ? highlightedBar.borderWidth : config.isLollipopChart ? 0 : config.barHasBorder === 'true' ? barBorderWidth : 0
141
- const barValueLabel = config.suppressedData.some(d => bar.key === d.column && bar.value === d.value) ? '' : yAxisValue
142
- let barHeight = config.suppressedData.some(d => bar.key === d.column && String(bar.value) === String(d.value)) ? suppresedBarHeight : barHeightBase
143
- const displaylollipopShape = config.suppressedData.some(d => bar.key === d.column && bar.value === d.value) ? 'none' : 'block'
144
124
 
125
+ const { barHeight, isSuppressed, getBarY, getAbsentDataLabel } = getBarConfig({ bar, defaultBarHeight, config, isNumber, getTextWidth, barWidth, isVertical: true, yAxisValue })
126
+
127
+ const absentDataLabel = getAbsentDataLabel(yAxisValue)
128
+ const barDefaultLabel = isSuppressed || !config.labels ? '' : yAxisValue
129
+ const barY = getBarY(defaultBarY, yScale(scaleVal))
130
+ const displaylollipopShape = testZeroValue(bar.value) ? 'none' : 'block'
145
131
  const getBarBackgroundColor = (barColor: string, filteredOutColor?: string): string => {
146
132
  let _barColor = barColor
147
133
  let _filteredOutColor = filteredOutColor || '#f2f2f2'
@@ -154,18 +140,20 @@ export const BarChartVertical = () => {
154
140
  if (dashboardConfig && dashboardConfig.dashboard.sharedFilters?.length !== 0) {
155
141
  const { sharedFilters } = dashboardConfig.dashboard
156
142
 
157
- _barColor = sharedFilters.map(_sharedFilter => {
158
- if (_sharedFilter.setBy === config.uid) {
159
- // If the current filter is the reset filter item.
160
- if (_sharedFilter.resetLabel === _sharedFilter.active) return colorScale(config.runtime.seriesLabels[bar.key])
161
- // If the current filter is the bars
162
- if (_sharedFilter.active === transformedData[barGroup.index][config.xAxis.dataKey]) return colorScale(config.runtime.seriesLabels[bar.key])
163
- return _filteredOutColor
164
- } else {
165
- // If the setBy isn't the config.uid return the original barColor
166
- return colorScale(config.runtime.seriesLabels[bar.key])
167
- }
168
- })[0]
143
+ _barColor = sharedFilters
144
+ ? sharedFilters.map(_sharedFilter => {
145
+ if (_sharedFilter.setBy === config.uid) {
146
+ // If the current filter is the reset filter item.
147
+ if (_sharedFilter.resetLabel === _sharedFilter.active) return colorScale(config.runtime.seriesLabels[bar.key])
148
+ // If the current filter is the bars
149
+ if (_sharedFilter.active === transformedData[barGroup.index][config.xAxis.dataKey]) return colorScale(config.runtime.seriesLabels[bar.key])
150
+ return _filteredOutColor
151
+ } else {
152
+ // If the setBy isn't the config.uid return the original barColor
153
+ return colorScale(config.runtime.seriesLabels[bar.key])
154
+ }
155
+ })[0]
156
+ : colorScale(config.runtime.seriesLabels[bar.key])
169
157
 
170
158
  if (isRegularLollipopColor) _barColor = barColor
171
159
  if (isTwoToneLollipopColor) _barColor = chroma(barColor).brighten(1)
@@ -214,34 +202,59 @@ export const BarChartVertical = () => {
214
202
  cursor: dashboardConfig ? 'pointer' : 'default'
215
203
  }
216
204
  })}
217
- <g
218
- transform={`translate(${barX},${yMax - suppresedBarHeight})`}
219
- onMouseOver={() => onMouseOverBar(xAxisValue, bar.key)}
220
- onMouseLeave={onMouseLeaveBar}
221
- opacity={transparentBar ? 0.2 : 1}
205
+ {config.preliminaryData.map((pd, index) => {
206
+ // check if user selected column
207
+ const selectedSuppressionColumn = !pd.column || pd.column === bar.key
208
+ // compare entered suppressed value with data value
209
+ const isValueMatch = String(pd.value) === String(bar.value) && pd.value !== ''
210
+ const isSuppressed = isValueMatch && selectedSuppressionColumn
211
+
212
+ if (!isSuppressed || barWidth < 10 || !config.general.showSuppressedSymbol || pd.hideBarSymbol) {
213
+ return
214
+ }
215
+ const hasAsterisk = String(pd.symbol).includes('Asterisk')
216
+ const yPadding = hasAsterisk ? -5 : -8
217
+ const verticalAnchor = hasAsterisk ? 'middle' : 'end'
218
+ const iconSize = pd.symbol === 'Asterisk' ? barWidth * 1.2 : pd.symbol === 'Double Asterisk' ? barWidth : barWidth / 1.5
219
+
220
+ return (
221
+ <Text // prettier-ignore
222
+ key={index}
223
+ dy={yPadding}
224
+ display={displayBar ? 'block' : 'none'}
225
+ opacity={transparentBar ? 0.5 : 1}
226
+ x={barX + barWidth / 2}
227
+ y={barY}
228
+ verticalAnchor={verticalAnchor}
229
+ fill={labelColor}
230
+ textAnchor='middle'
231
+ fontSize={`${iconSize}px`}
232
+ >
233
+ {pd.iconCode}
234
+ </Text>
235
+ )
236
+ })}
237
+
238
+ <Text // prettier-ignore
222
239
  display={displayBar ? 'block' : 'none'}
223
- data-tooltip-html={tooltip}
224
- data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
225
- onClick={e => {
226
- e.preventDefault()
227
- if (setSharedFilter) {
228
- bar[config.xAxis.dataKey] = xAxisValue
229
- setSharedFilter(config.uid, bar)
230
- }
231
- }}
240
+ opacity={transparentBar ? 0.5 : 1}
241
+ x={barX + barWidth / 2}
242
+ y={barY - 5}
243
+ fill={labelColor}
244
+ textAnchor='middle'
232
245
  >
233
- {getIcon(bar, barWidth)}
234
- </g>
235
-
246
+ {testZeroValue(bar.value) ? '' : barDefaultLabel}
247
+ </Text>
236
248
  <Text // prettier-ignore
237
- display={config.labels && displayBar ? 'block' : 'none'}
249
+ display={displayBar ? 'block' : 'none'}
238
250
  opacity={transparentBar ? 0.5 : 1}
239
251
  x={barX + barWidth / 2}
240
252
  y={barY - 5}
241
253
  fill={labelColor}
242
254
  textAnchor='middle'
255
+ fontSize={config.isLollipopChart ? null : barWidth / 2}
243
256
  >
244
- {barValueLabel}
257
+ {absentDataLabel}
245
258
  </Text>
246
259
 
247
260
  {config.isLollipopChart && config.lollipopShape === 'circle' && (
@@ -288,7 +301,7 @@ export const BarChartVertical = () => {
288
301
  let upperPos
289
302
  let lowerPos
290
303
  let tickWidth = 5
291
- xPos = xScale(getXAxisData(d)) + (config.xAxis.type !== 'date' || config.xAxis.type !== 'date-time' ? seriesScale.range()[1] / 2 : 0)
304
+ xPos = xScale(getXAxisData(d)) + (config.xAxis.type !== 'date-time' ? seriesScale.range()[1] / 2 : 0)
292
305
  upperPos = yScale(getYAxisData(d, config.confidenceKeys.lower))
293
306
  lowerPos = yScale(getYAxisData(d, config.confidenceKeys.upper))
294
307
  return (
@@ -0,0 +1,102 @@
1
+ // Define an interface for the function's parameter
2
+ interface BarConfigProps {
3
+ defaultBarWidth?: number
4
+ defaultBarHeight?: number
5
+ bar?: { [key: string]: any }
6
+ isNumber?: Function
7
+ config: { [key: string]: any }
8
+ getTextWidth: Function
9
+ barWidth: number
10
+ isVertical: boolean
11
+ }
12
+
13
+ // Function to create bar width based on suppression status and missing data label
14
+ export const getBarConfig = ({ bar, defaultBarHeight, defaultBarWidth, config, isNumber, getTextWidth, barWidth, isVertical }: BarConfigProps) => {
15
+ const heightMini = 3 /// height of small bars aka suppressed/NA/Zero valued
16
+ let barHeight = defaultBarHeight
17
+
18
+ let barWidthHorizontal = defaultBarWidth
19
+
20
+ let barLabel = ''
21
+ let isSuppressed = false
22
+ let showMissingDataLabel = false
23
+ let showZeroValueDataLabel = false
24
+ const showSuppressedSymbol = config.general.showSuppressedSymbol
25
+
26
+ config.preliminaryData.forEach(pd => {
27
+ const hasColumn = !pd.column || pd.column === bar.key
28
+ if (hasColumn && pd.type === 'suppression' && pd.value && String(pd.value) === String(bar.value)) {
29
+ if (!pd.hideBarSymbol && showSuppressedSymbol) {
30
+ barHeight = barWidth > 10 ? heightMini : 0
31
+ barWidthHorizontal = heightMini
32
+ isSuppressed = true
33
+ } else {
34
+ barHeight = 0
35
+ barWidthHorizontal = 0
36
+ isSuppressed = true
37
+ }
38
+ }
39
+ })
40
+
41
+ // Handle undefined, null, or non-calculable bar.value
42
+ if (!isSuppressed && !isNumber(bar.value) && config.general.showMissingDataLabel) {
43
+ const labelWidth = getTextWidth(barLabel, `normal ${barWidth / 2}px sans-serif`)
44
+ const labelFits = labelWidth < barWidth && barWidth > 10
45
+ showMissingDataLabel = true
46
+ barHeight = labelFits ? heightMini : 0
47
+ barWidthHorizontal = heightMini
48
+ }
49
+ // handle zero values
50
+ if (!isSuppressed && String(bar.value) === '0' && config.general.showZeroValueDataLabel) {
51
+ const labelWidth = getTextWidth(barLabel, `normal ${barWidth / 2}px sans-serif`)
52
+ const labelFits = labelWidth < barWidth && barWidth > 10
53
+ barHeight = config.isLollipopChart ? heightMini * 2 : !config.isLollipopChart && labelFits ? heightMini : 0
54
+ barWidthHorizontal = heightMini
55
+ showZeroValueDataLabel = true
56
+ }
57
+
58
+ const getBarY = (defaultBarY, yScale) => {
59
+ // calculate Y position of small bars (suppressed,N/A,Zero valued) bars
60
+ if (isSuppressed || showMissingDataLabel || showZeroValueDataLabel) {
61
+ if (config.isLollipopChart) {
62
+ return yScale - heightMini * 2
63
+ } else {
64
+ return yScale - heightMini
65
+ }
66
+ }
67
+ return defaultBarY
68
+ }
69
+
70
+ // Function to determine the label for a bar in a bar chart vertical/Horizontal
71
+ const getAbsentDataLabel = yAxisValue => {
72
+ // Initialize label with the yAxisValue
73
+ let label = ''
74
+ // Check if the label is exactly '0' and if so, hide it
75
+ if (String(yAxisValue) === '0') label = ''
76
+ // Check if the bar is marked as suppressed. If so, do not show any label.
77
+ if (isSuppressed) label = ''
78
+ // If the config is set to show a label for missing data, display 'N/A'
79
+ if (showMissingDataLabel) label = 'N/A'
80
+ // If the config is set to specifically show zero values, set the label to '0'
81
+ if (showZeroValueDataLabel) label = '0'
82
+
83
+ // determine label width in pixels & check if it fits to the bar width
84
+ const labelWidth = getTextWidth(barLabel, `normal ${barWidth / 2}px sans-serif`)
85
+ const labelFits = labelWidth < barWidth && barWidth > 10
86
+ if (config.isLollipopChart) {
87
+ return label
88
+ } else {
89
+ return labelFits && isVertical ? label : !isVertical ? label : ''
90
+ }
91
+ }
92
+
93
+ return { barWidthHorizontal, barHeight, isSuppressed, showMissingDataLabel, showZeroValueDataLabel, getBarY, getAbsentDataLabel }
94
+ }
95
+
96
+ export const testZeroValue = value => {
97
+ if (value === undefined || value === null) {
98
+ return
99
+ }
100
+ const regex = /^0(\.0)?$/
101
+ return regex.test(value.toString())
102
+ }
@@ -82,7 +82,8 @@ export default function DeviationBar({ height, xScale }) {
82
82
  useEffect(() => {
83
83
  const handleResize = () => {
84
84
  setWindowWidth(window.innerWidth)
85
- barRefs.current.forEach(bar => {
85
+ barRefs.current?.forEach(bar => {
86
+ if (!bar || !bar.style) return
86
87
  bar.style.transition = 'none'
87
88
  bar.style.transform = 'translate(0) scale(1)'
88
89
  })
@@ -104,7 +105,8 @@ export default function DeviationBar({ height, xScale }) {
104
105
  }, [entry?.isIntersecting, config.animate]) // eslint-disable-line
105
106
 
106
107
  useEffect(() => {
107
- barRefs.current.forEach((bar, i) => {
108
+ barRefs.current?.forEach((bar, i) => {
109
+ if (!bar || !bar.style) return
108
110
  if (config.animate) {
109
111
  const normalizedTarget = (target / maxVal) * 100
110
112
  bar.style.opacity = '0'