@cdc/chart 4.23.6 → 4.23.8

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 (48) hide show
  1. package/dist/cdcchart.js +29981 -29995
  2. package/examples/feature/__data__/area-chart-date-apple.json +5122 -0
  3. package/examples/feature/__data__/city-temperature.json +2198 -0
  4. package/examples/feature/__data__/planet-example-data.json +1 -1
  5. package/examples/feature/area/area-chart-category.json +45 -45
  6. package/examples/feature/area/area-chart-date-apple.json +10376 -0
  7. package/examples/feature/area/area-chart-date-city-temperature.json +4528 -0
  8. package/examples/feature/area/area-chart-date.json +111 -3
  9. package/examples/feature/combo/right-issues.json +1 -1
  10. package/examples/feature/forecasting/combo-forecasting.json +72 -46
  11. package/examples/feature/forecasting/effective_reproduction.json +57 -8
  12. package/examples/feature/forecasting/forecasting.json +12 -3
  13. package/examples/feature/forest-plot/broken.json +700 -0
  14. package/examples/feature/forest-plot/data.csv +24 -0
  15. package/examples/feature/forest-plot/forest-plot.json +717 -0
  16. package/examples/feature/line/line-chart.json +11 -11
  17. package/examples/feature/pie/planet-pie-example-config.json +1 -1
  18. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +167 -20
  19. package/examples/private/confidence_interval_test.json +248 -0
  20. package/examples/private/tooltip-issue.json +45275 -0
  21. package/index.html +13 -11
  22. package/package.json +4 -3
  23. package/src/CdcChart.jsx +78 -27
  24. package/src/components/AreaChart.jsx +65 -151
  25. package/src/components/BarChart.Horizontal.jsx +251 -0
  26. package/src/components/BarChart.StackedHorizontal.jsx +118 -0
  27. package/src/components/BarChart.StackedVertical.jsx +93 -0
  28. package/src/components/BarChart.Vertical.jsx +204 -0
  29. package/src/components/BarChart.jsx +17 -667
  30. package/src/components/BarChartType.jsx +15 -0
  31. package/src/components/BrushHandle.jsx +17 -0
  32. package/src/components/DataTable.jsx +67 -22
  33. package/src/components/EditorPanel.jsx +426 -358
  34. package/src/components/Forecasting.jsx +23 -86
  35. package/src/components/ForestPlot.jsx +191 -0
  36. package/src/components/ForestPlotSettings.jsx +508 -0
  37. package/src/components/Legend.jsx +10 -8
  38. package/src/components/LineChart.jsx +31 -6
  39. package/src/components/LinearChart.jsx +317 -230
  40. package/src/components/Series.jsx +40 -4
  41. package/src/data/initial-state.js +50 -3
  42. package/src/hooks/useBarChart.js +186 -0
  43. package/src/hooks/useEditorPermissions.js +218 -0
  44. package/src/hooks/useMinMax.js +18 -5
  45. package/src/hooks/useRightAxis.js +2 -1
  46. package/src/hooks/useScales.js +45 -2
  47. package/src/hooks/useTooltip.jsx +407 -0
  48. package/src/scss/main.scss +11 -17
@@ -1,680 +1,30 @@
1
- import React, { useContext, useState, useEffect } from 'react'
1
+ import React, { useContext } from 'react'
2
+
3
+ // visx
2
4
  import { Group } from '@visx/group'
3
- import { BarGroup, BarStack } from '@visx/shape'
4
- import { Text } from '@visx/text'
5
- import chroma from 'chroma-js'
5
+ import { Bar } from '@visx/shape'
6
+
7
+ // cdc
8
+ import BarChartType from './BarChartType'
6
9
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
7
10
  import ConfigContext from '../ConfigContext'
8
- import { BarStackHorizontal } from '@visx/shape'
9
- import { useHighlightedBars } from '../hooks/useHighlightedBars'
10
- import { act } from 'react-dom/test-utils'
11
-
12
- export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getXAxisData, getYAxisData, animatedChart, visible }) {
13
- const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, updateConfig, colorPalettes, tableData, formatDate, isNumber, getTextWidth, parseDate, setSharedFilter, setSharedFilterValue, dashboardConfig } = useContext(ConfigContext)
14
- const { HighLightedBarUtils } = useHighlightedBars(config)
15
- const { orientation, visualizationSubType } = config
16
- const isHorizontal = orientation === 'horizontal'
17
- const barBorderWidth = 1
18
- const lollipopBarWidth = config.lollipopSize === 'large' ? 7 : config.lollipopSize === 'medium' ? 6 : 5
19
- const lollipopShapeSize = config.lollipopSize === 'large' ? 14 : config.lollipopSize === 'medium' ? 12 : 10
20
-
21
- const isLabelBelowBar = config.yAxis.labelPlacement === 'Below Bar'
22
- const displayNumbersOnBar = config.yAxis.displayNumbersOnBar
23
- const section = config.orientation === 'horizontal' ? 'yAxis' : 'xAxis'
24
-
25
- const isRounded = config.barStyle === 'rounded'
26
- const isStacked = config.visualizationSubType === 'stacked'
27
- const tipRounding = config.tipRounding
28
- const radius = config.roundingStyle === 'standard' ? '8px' : config.roundingStyle === 'shallow' ? '5px' : config.roundingStyle === 'finger' ? '15px' : '0px'
29
- const stackCount = config.runtime.seriesKeys.length
30
- const fontSize = { small: 16, medium: 18, large: 20 }
31
- const hasMultipleSeries = Object.keys(config.runtime.seriesLabels).length > 1
32
-
33
- const applyRadius = index => {
34
- if (index === undefined || index === null || !isRounded) return {}
35
- let style = {}
36
-
37
- if ((isStacked && index + 1 === stackCount) || !isStacked) {
38
- style = isHorizontal ? { borderRadius: `0 ${radius} ${radius} 0` } : { borderRadius: `${radius} ${radius} 0 0` }
39
- }
40
- if (!isStacked && index === -1) {
41
- style = isHorizontal ? { borderRadius: `${radius} 0 0 ${radius} ` } : { borderRadius: ` 0 0 ${radius} ${radius}` }
42
- }
43
- if (tipRounding === 'full' && isStacked && index === 0 && stackCount > 1) {
44
- style = isHorizontal ? { borderRadius: `${radius} 0 0 ${radius}` } : { borderRadius: `0 0 ${radius} ${radius}` }
45
- }
46
- if (tipRounding === 'full' && ((isStacked && index === 0 && stackCount === 1) || !isStacked)) {
47
- style = { borderRadius: radius }
48
- }
49
- return style
50
- }
51
-
52
- const assignColorsToValues = () => {
53
- const palettesArr = colorPalettes[config.palette]
54
- const values = tableData.map(d => {
55
- return d[config.legend.colorCode]
56
- })
57
- // Map to hold unique values and their colors
58
- let colorMap = new Map()
59
- // Resultant array to hold colors to the values
60
- let result = []
61
-
62
- for (let i = 0; i < values.length; i++) {
63
- // If value not in map, add it and assign a color
64
- if (!colorMap.has(values[i])) {
65
- colorMap.set(values[i], palettesArr[colorMap.size % palettesArr.length])
66
- }
67
- // push the colosr to the result array
68
- result.push(colorMap.get(values[i]))
69
- }
70
- return result
71
- }
72
-
73
- const updateBars = defaultBars => {
74
- // function updates stacked && regular && lollipop horizontal bars
75
- if (config.visualizationType !== 'Bar' && !isHorizontal) return defaultBars
76
-
77
- const barsArr = [...defaultBars]
78
- let barHeight
79
-
80
- const heights = {
81
- stacked: config.barHeight,
82
- lollipop: lollipopBarWidth
83
- }
84
-
85
- if (!isStacked) {
86
- barHeight = heights[config.isLollipopChart ? 'lollipop' : 'stacked'] * stackCount
87
- } else {
88
- barHeight = heights.stacked
89
- }
90
-
91
- const labelHeight = isLabelBelowBar ? fontSize[config.fontSize] * 1.2 : 0
92
- let barSpace = Number(config.barSpace)
93
-
94
- // calculate height of container based height, space and fontSize of labels
95
- let totalHeight = barsArr.length * (barHeight + labelHeight + barSpace)
96
11
 
97
- if (isHorizontal) {
98
- config.heights.horizontal = totalHeight
99
- }
100
-
101
- // return new updated bars/groupes
102
- return barsArr.map((bar, i) => {
103
- // set bars Y dynamically to handle space between bars
104
- let y = 0
105
- bar.index !== 0 && (y = (barHeight + barSpace + labelHeight) * i)
106
-
107
- return { ...bar, y: y, height: barHeight }
108
- })
109
- }
110
-
111
- // Using State
112
- const [textWidth, setTextWidth] = useState(null)
113
-
114
- useEffect(() => {
115
- if (orientation === 'horizontal' && !config.yAxis.labelPlacement) {
116
- updateConfig({
117
- ...config,
118
- yAxis: {
119
- ...config,
120
- labelPlacement: 'Below Bar'
121
- }
122
- })
123
- }
124
- }, [config, updateConfig]) // eslint-disable-line
125
-
126
- useEffect(() => {
127
- if (config.isLollipopChart === false && config.barHeight < 25) {
128
- updateConfig({ ...config, barHeight: 25 })
129
- }
130
- }, [config.isLollipopChart]) // eslint-disable-line
131
-
132
- useEffect(() => {
133
- if (config.visualizationSubType === 'horizontal') {
134
- updateConfig({
135
- ...config,
136
- orientation: 'horizontal'
137
- })
138
- }
139
- }, []) // eslint-disable-line
140
-
141
- useEffect(() => {
142
- if (config.barStyle === 'lollipop' && !config.isLollipopChart) {
143
- updateConfig({ ...config, isLollipopChart: true })
144
- }
145
- if (isRounded || config.barStyle === 'flat') {
146
- updateConfig({ ...config, isLollipopChart: false })
147
- }
148
- }, [config.barStyle]) // eslint-disable-line
12
+ const BarChart = ({ xScale, yScale, seriesScale, xMax, yMax, handleTooltipMouseOver, handleTooltipMouseOff, handleTooltipClick }) => {
13
+ const { transformedData: data, config } = useContext(ConfigContext)
149
14
 
150
15
  return (
151
16
  <ErrorBoundary component='BarChart'>
152
17
  <Group left={parseFloat(config.runtime.yAxis.size)}>
153
- {/* Stacked Vertical */}
154
- {config.visualizationSubType === 'stacked' && !isHorizontal && (
155
- <BarStack data={data} keys={config.runtime.barSeriesKeys || config.runtime.seriesKeys} x={d => d[config.runtime.xAxis.dataKey]} xScale={xScale} yScale={yScale} color={colorScale}>
156
- {barStacks =>
157
- barStacks.reverse().map(barStack =>
158
- barStack.bars.map(bar => {
159
- let transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
160
- let displayBar = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(bar.key) !== -1
161
- let barThickness = xMax / barStack.bars.length
162
- let barThicknessAdjusted = barThickness * (config.barThickness || 0.8)
163
- let offset = (barThickness * (1 - (config.barThickness || 0.8))) / 2
164
- // tooltips
165
- const xAxisValue = config.runtime.xAxis.type === 'date' ? formatDate(parseDate(data[bar.index][config.runtime.xAxis.dataKey])) : data[bar.index][config.runtime.xAxis.dataKey]
166
- const yAxisValue = formatNumber(bar.bar ? bar.bar.data[bar.key] : 0, 'left')
167
-
168
- const style = applyRadius(barStack.index)
169
- let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
170
- const xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
171
- if (!hasMultipleSeries) {
172
- yAxisTooltip = config.isLegendValue ? `${bar.key}: ${yAxisValue}` : config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
173
- }
174
-
175
- const tooltip = `<div>
176
- ${config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && hasMultipleSeries ? `${config.runtime.seriesLabels[bar.key] || ''}<br/>` : ''}
177
- ${yAxisTooltip}<br />
178
- ${xAxisTooltip}
179
- </div>`
180
- return (
181
- <Group key={`${barStack.index}--${bar.index}--${orientation}`}>
182
- <style>
183
- {`
184
- #barStack${barStack.index}-${bar.index} rect,
185
- #barStack${barStack.index}-${bar.index} foreignObject{
186
- animation-delay: ${barStack.index * 0.5}s;
187
- transform-origin: ${barThicknessAdjusted / 2}px ${bar.y + bar.height}px
188
- }
189
- `}
190
- </style>
191
- <Group key={`bar-stack-${barStack.index}-${bar.index}`} id={`barStack${barStack.index}-${bar.index}`} className='stack vertical'>
192
- <Text display={config.labels && displayBar ? 'block' : 'none'} opacity={transparentBar ? 0.5 : 1} x={barThickness * bar.index + offset} y={bar.y - 5} fill={'#000'} textAnchor='middle'>
193
- {yAxisValue}
194
- </Text>
195
- <foreignObject
196
- key={`bar-stack-${barStack.index}-${bar.index}`}
197
- x={barThickness * bar.index + offset}
198
- y={bar.y}
199
- width={barThicknessAdjusted}
200
- height={bar.height}
201
- style={{ background: bar.color, border: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`, ...style }}
202
- opacity={transparentBar ? 0.5 : 1}
203
- display={displayBar ? 'block' : 'none'}
204
- data-tooltip-html={tooltip}
205
- data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
206
- onClick={e => {
207
- e.preventDefault()
208
- if (setSharedFilter) {
209
- bar[config.xAxis.dataKey] = xAxisValue
210
- setSharedFilter(config.uid, bar)
211
- }
212
- }}
213
- ></foreignObject>
214
- </Group>
215
- </Group>
216
- )
217
- })
218
- )
219
- }
220
- </BarStack>
221
- )}
222
-
223
- {/* Stacked Horizontal */}
224
- {config.visualizationSubType === 'stacked' && isHorizontal && (
225
- <>
226
- <BarStackHorizontal data={data} keys={config.runtime.barSeriesKeys || config.runtime.seriesKeys} height={yMax} y={d => d[config.runtime.yAxis.dataKey]} xScale={xScale} yScale={yScale} color={colorScale} offset='none'>
227
- {barStacks =>
228
- barStacks.map(barStack =>
229
- updateBars(barStack.bars).map((bar, index) => {
230
- let transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
231
- let displayBar = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(bar.key) !== -1
232
- config.barHeight = Number(config.barHeight)
233
- let labelColor = '#000000'
234
- // tooltips
235
- const xAxisValue = formatNumber(data[bar.index][bar.key], 'left')
236
- const yAxisValue = config.runtime.yAxis.type === 'date' ? formatDate(parseDate(data[bar.index][config.runtime.originalXAxis.dataKey])) : data[bar.index][config.runtime.originalXAxis.dataKey]
237
- const style = applyRadius(barStack.index)
238
- let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
239
- let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
240
- if (!hasMultipleSeries) {
241
- xAxisTooltip = config.isLegendValue ? `${bar.key}: ${xAxisValue}` : config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisTooltip
242
- }
243
- const tooltip = `<div>
244
- ${config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && hasMultipleSeries ? `${config.runtime.seriesLabels[bar.key] || ''}<br/>` : ''}
245
- ${yAxisTooltip}<br />
246
- ${xAxisTooltip}
247
- </div>`
248
-
249
- if (chroma.contrast(labelColor, bar.color) < 4.9) {
250
- labelColor = '#FFFFFF'
251
- }
252
-
253
- return (
254
- <>
255
- <style>
256
- {`
257
- #barStack${barStack.index}-${bar.index} rect,
258
- #barStack${barStack.index}-${bar.index} foreignObject{
259
- animation-delay: ${barStack.index * 0.5}s;
260
- transform-origin: ${bar.x}px
261
- }
262
- `}
263
- </style>
264
- <Group key={index} id={`barStack${barStack.index}-${bar.index}`} className='stack horizontal'>
265
- <foreignObject
266
- key={`barstack-horizontal-${barStack.index}-${bar.index}-${index}`}
267
- className={`animated-chart group ${animatedChart ? 'animated' : ''}`}
268
- x={bar.x}
269
- y={bar.y}
270
- width={bar.width}
271
- height={bar.height}
272
- style={{ background: bar.color, border: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`, ...style }}
273
- opacity={transparentBar ? 0.5 : 1}
274
- display={displayBar ? 'block' : 'none'}
275
- data-tooltip-html={tooltip}
276
- data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
277
- onClick={e => {
278
- e.preventDefault()
279
- if (setSharedFilter) {
280
- bar[config.xAxis.dataKey] = xAxisValue
281
- setSharedFilter(config.uid, bar)
282
- }
283
- }}
284
- ></foreignObject>
285
-
286
- {orientation === 'horizontal' && visualizationSubType === 'stacked' && isLabelBelowBar && barStack.index === 0 && !config.yAxis.hideLabel && (
287
- <Text
288
- x={`${bar.x + (config.isLollipopChart ? 15 : 5)}`} // padding
289
- y={bar.y + bar.height * 1.2}
290
- fill={'#000000'}
291
- textAnchor='start'
292
- verticalAnchor='start'
293
- >
294
- {yAxisValue}
295
- </Text>
296
- )}
297
-
298
- {displayNumbersOnBar && textWidth < bar.width && (
299
- <Text
300
- display={displayBar ? 'block' : 'none'}
301
- x={bar.x + barStack.bars[bar.index].width / 2} // padding
302
- y={bar.y + bar.height / 2}
303
- fill={labelColor}
304
- textAnchor='middle'
305
- verticalAnchor='middle'
306
- innerRef={e => {
307
- if (e) {
308
- // use font sizes and padding to set the bar height
309
- let elem = e.getBBox()
310
- setTextWidth(elem.width)
311
- }
312
- }}
313
- >
314
- {xAxisValue}
315
- </Text>
316
- )}
317
- </Group>
318
- </>
319
- )
320
- })
321
- )
322
- }
323
- </BarStackHorizontal>
324
- </>
325
- )}
326
-
327
- {/* Bar Groups: Not Stacked */}
328
- {config.visualizationSubType !== 'stacked' && (
329
- <Group>
330
- <BarGroup
331
- data={data}
332
- keys={config.runtime.barSeriesKeys || config.runtime.seriesKeys}
333
- height={yMax}
334
- x0={d => d[config.runtime.originalXAxis.dataKey]}
335
- x0Scale={config.runtime.horizontal ? yScale : xScale}
336
- x1Scale={seriesScale}
337
- yScale={config.runtime.horizontal ? xScale : yScale}
338
- color={() => {
339
- return ''
340
- }}
341
- >
342
- {barGroups => {
343
- return updateBars(barGroups).map((barGroup, index) => (
344
- <Group
345
- className={`bar-group-${barGroup.index}-${barGroup.x0}--${index} ${config.orientation}`}
346
- key={`bar-group-${barGroup.index}-${barGroup.x0}--${index}`}
347
- id={`bar-group-${barGroup.index}-${barGroup.x0}--${index}`}
348
- top={config.runtime.horizontal ? barGroup.y : 0}
349
- left={config.runtime.horizontal ? 0 : (xMax / barGroups.length) * barGroup.index}
350
- >
351
- {barGroup.bars.map((bar, index) => {
352
- const scaleVal = config.useLogScale ? 0.1 : 0
353
- const getHighlightedBarColorByValue = value => {
354
- const match = config?.highlightedBarValues.filter(item => {
355
- if (!item.value) return
356
- return config.xAxis.type === 'date' ? formatDate(parseDate(item.value)) === value : item.value === value
357
- })[0]
18
+ <BarChartType.StackedVertical xScale={xScale} yScale={yScale} xMax={xMax} yMax={yMax} />
19
+ <BarChartType.StackedHorizontal xScale={xScale} yScale={yScale} xMax={xMax} yMax={yMax} />
20
+ <BarChartType.Vertical xScale={xScale} yScale={yScale} xMax={xMax} yMax={yMax} seriesScale={seriesScale} />
21
+ <BarChartType.Horizontal xScale={xScale} yScale={yScale} xMax={xMax} yMax={yMax} seriesScale={seriesScale} />
358
22
 
359
- if (!match?.color) return `rgba(255, 102, 1)`
360
- return match.color
361
- }
362
-
363
- const getHighlightedBarByValue = value => {
364
- const match = config?.highlightedBarValues.filter(item => {
365
- if (!item.value) return
366
- return config.xAxis.type === 'date' ? formatDate(parseDate(item.value)) === value : item.value === value
367
- })[0]
368
-
369
- if (!match?.color) return false
370
- return match
371
- }
372
-
373
- let highlightedBarValues = config.highlightedBarValues.map(item => item.value).filter(item => item !== ('' || undefined))
374
-
375
- highlightedBarValues = config.xAxis.type === 'date' ? HighLightedBarUtils.formatDates(highlightedBarValues) : highlightedBarValues
376
-
377
- let transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
378
- let displayBar = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(bar.key) !== -1
379
- let barHeight = orientation === 'horizontal' ? config.barHeight : isNumber(Math.abs(yScale(bar.value) - yScale(scaleVal))) ? Math.abs(yScale(bar.value) - yScale(scaleVal)) : 0
380
- let barY = bar.value >= 0 && isNumber(bar.value) ? bar.y : yScale(0)
381
- let barGroupWidth = ((config.runtime.horizontal ? yMax : xMax) / barGroups.length) * (config.barThickness || 0.8)
382
- let offset = (((config.runtime.horizontal ? yMax : xMax) / barGroups.length) * (1 - (config.barThickness || 0.8))) / 2
383
- const barX = bar.value < 0 ? Math.abs(xScale(bar.value)) : xScale(0)
384
- const barWidthHorizontal = Math.abs(xScale(bar.value) - xScale(scaleVal))
385
- // ! Unsure if this should go back.
386
- if (config.isLollipopChart) {
387
- offset = (config.runtime.horizontal ? yMax : xMax) / barGroups.length / 2 - lollipopBarWidth / 2
388
- }
389
- let palette = assignColorsToValues()
390
-
391
- let barWidth = config.isLollipopChart ? lollipopBarWidth : barGroupWidth / barGroup.bars.length
392
- let barColor = config.runtime.seriesLabels && config.runtime.seriesLabels[bar.key] ? colorScale(config.runtime.seriesLabels[bar.key]) : colorScale(bar.key)
393
- while (palette.length < barGroups.length) {
394
- palette = palette.concat(palette)
395
- }
396
- if (config.legend.colorCode && config.series.length === 1) barColor = palette[barGroup.index]
397
-
398
- let yAxisValue = formatNumber(bar.value, 'left')
399
- let xAxisValue = config.runtime[section].type === 'date' ? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey])) : data[barGroup.index][config.runtime.originalXAxis.dataKey]
400
-
401
- if (config.runtime.horizontal) {
402
- let tempValue = yAxisValue
403
- yAxisValue = xAxisValue
404
- xAxisValue = tempValue
405
- barWidth = config.barHeight
406
- }
407
-
408
- const barPosition = bar.value < 0 ? 'below' : 'above'
409
-
410
- // check if bar text/value string fits into each bars.
411
- let textWidth = getTextWidth(xAxisValue, `normal ${fontSize[config.fontSize]}px sans-serif`)
412
- let textFits = textWidth < barWidthHorizontal - 5 // minus padding 5
413
- let labelColor = '#000000'
414
-
415
- // Set label color
416
- if (barColor && labelColor) {
417
- if (chroma.contrast(labelColor, barColor) < 4.9) {
418
- labelColor = textFits ? '#FFFFFF' : '#000000'
419
- }
420
- }
421
-
422
- // Set if background is transparent'
423
- labelColor = HighLightedBarUtils.checkFontColor(yAxisValue, highlightedBarValues, labelColor)
424
-
425
- // control text position
426
- let textAnchor = textFits ? 'end' : 'start'
427
- let textAnchorLollipop = 'start'
428
- let textPadding = textFits ? -5 : 5
429
- let textPaddingLollipop = 10
430
- // if bars are negative we change positions of text
431
- if (barPosition === 'below') {
432
- textAnchor = textFits ? 'start' : 'end'
433
- textPadding = textFits ? 5 : -5
434
- if (config.isLollipopChart) {
435
- textAnchorLollipop = 'end'
436
- textPaddingLollipop = -10
437
- }
438
- }
439
-
440
- // create new Index based on bar value for border Radius
441
- const newIndex = bar.value < 0 ? -1 : index
442
- const style = applyRadius(newIndex)
443
-
444
- let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
445
- let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
446
- if (!hasMultipleSeries && config.runtime.horizontal) {
447
- xAxisTooltip = config.isLegendValue ? `${bar.key}: ${xAxisValue}` : config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
448
- }
449
- if (!hasMultipleSeries && !config.runtime.horizontal) {
450
- yAxisTooltip = config.isLegendValue ? `${bar.key}: ${yAxisValue}` : config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
451
- }
452
-
453
- const tooltip = `<div>
454
- ${config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && hasMultipleSeries ? `${config.runtime.seriesLabels[bar.key] || ''}<br/>` : ''}
455
- ${yAxisTooltip}<br />
456
- ${xAxisTooltip}
457
- </div>`
458
-
459
- const isRegularLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'regular'
460
- const isTwoToneLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'two-tone'
461
- const isHighlightedBar = config.orientation === 'vertical' ? highlightedBarValues?.includes(xAxisValue) : highlightedBarValues?.includes(yAxisValue)
462
- const highlightedBarColor = config.orientation === 'vertical' ? getHighlightedBarColorByValue(xAxisValue) : getHighlightedBarColorByValue(yAxisValue)
463
- const highlightedBar = config.orientation === 'vertical' ? getHighlightedBarByValue(xAxisValue) : getHighlightedBarByValue(yAxisValue)
464
-
465
- const background = () => {
466
- if (isRegularLollipopColor) return barColor
467
- if (isTwoToneLollipopColor) return chroma(barColor).brighten(1)
468
- if (isHighlightedBar) return 'transparent'
469
- // loop through shared filters and get active values
470
- if (dashboardConfig && dashboardConfig?.dashboard.sharedFilters?.length > 0) {
471
- let activeFilters = []
472
- let backgroundColor = barColor
473
-
474
- const checkForResetValue = () => {
475
- return dashboardConfig.dashboard.sharedFilters?.map((filter, index) => {
476
- if (filter.resetLabel === filter.active) {
477
- backgroundColor = barColor
478
- } else {
479
- return backgroundColor
480
- }
481
- })
482
- }
483
-
484
- dashboardConfig.dashboard.sharedFilters?.forEach((filter, index) => {
485
- activeFilters.push(filter.active)
486
- })
487
-
488
- // if reset value is found use that.
489
-
490
- if (config.orientation === 'horizontal') {
491
- if (!activeFilters.includes(yAxisValue)) {
492
- backgroundColor = '#ccc'
493
- }
494
- }
495
-
496
- if (config.orientation !== 'horizontal') {
497
- if (!activeFilters.includes(xAxisValue)) {
498
- backgroundColor = '#ccc'
499
- }
500
- }
501
- checkForResetValue()
502
- return backgroundColor
503
- }
504
- return barColor
505
- }
506
-
507
- const borderColor = isHighlightedBar ? highlightedBarColor : config.barHasBorder === 'true' ? '#000' : 'transparent'
508
-
509
- const borderWidth = isHighlightedBar ? highlightedBar.borderWidth : config.isLollipopChart ? 0 : config.barHasBorder === 'true' ? barBorderWidth : 0
510
-
511
- const finalStyle = {
512
- background: background(),
513
- borderColor,
514
- borderStyle: 'solid',
515
- borderWidth,
516
- ...style
517
- }
518
-
519
- return (
520
- <Group key={`${barGroup.index}--${index}--${orientation}`}>
521
- {/* This feels gross but inline transition was not working well*/}
522
- <style>
523
- {`
524
- .linear #barGroup${barGroup.index},
525
- .Combo #barGroup${barGroup.index} {
526
- transform-origin: 0 ${barY + barHeight}px;
527
- }
528
- `}
529
- </style>
530
- <Group key={`bar-sub-group-${barGroup.index}-${barGroup.x0}-${barY}--${index}`}>
531
- <foreignObject
532
- id={`barGroup${barGroup.index}`}
533
- key={`bar-group-bar-${barGroup.index}-${bar.index}-${bar.value}-${bar.key}`}
534
- x={config.runtime.horizontal ? barX : barWidth * bar.index + offset}
535
- y={config.runtime.horizontal ? barWidth * bar.index : barY}
536
- width={config.runtime.horizontal ? barWidthHorizontal : barWidth}
537
- height={isHorizontal && !config.isLollipopChart ? barWidth : isHorizontal && config.isLollipopChart ? lollipopBarWidth : barHeight}
538
- style={finalStyle}
539
- opacity={transparentBar ? 0.5 : 1}
540
- display={displayBar ? 'block' : 'none'}
541
- data-tooltip-html={tooltip}
542
- data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
543
- onClick={e => {
544
- e.preventDefault()
545
- if (setSharedFilter) {
546
- bar[config.xAxis.dataKey] = config.orientation === 'horizontal' ? yAxisValue : xAxisValue
547
- setSharedFilter(config.uid, bar)
548
- }
549
- }}
550
- ></foreignObject>
551
- {orientation === 'horizontal' && !config.isLollipopChart && displayNumbersOnBar && (
552
- <Text // prettier-ignore
553
- display={displayBar ? 'block' : 'none'}
554
- x={bar.y}
555
- y={config.barHeight / 2 + config.barHeight * bar.index}
556
- fill={labelColor}
557
- dx={textPadding}
558
- verticalAnchor='middle'
559
- textAnchor={textAnchor}
560
- >
561
- {xAxisValue}
562
- </Text>
563
- )}
564
- {orientation === 'horizontal' && config.isLollipopChart && displayNumbersOnBar && (
565
- <Text
566
- display={displayBar ? 'block' : 'none'}
567
- x={bar.y} // padding
568
- y={0}
569
- fill={'#000000'}
570
- dx={textPaddingLollipop}
571
- textAnchor={textAnchorLollipop}
572
- verticalAnchor='middle'
573
- fontWeight={'normal'}
574
- >
575
- {xAxisValue}
576
- </Text>
577
- )}
578
- {orientation === 'horizontal' && isLabelBelowBar && !config.yAxis.hideLabel && (
579
- <Text x={config.yAxis.hideAxis ? 0 : 5} y={barGroup.height} dy={4} verticalAnchor={'start'} textAnchor={'start'}>
580
- {config.runtime.yAxis.type === 'date'
581
- ? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey]))
582
- : isHorizontal
583
- ? data[barGroup.index][config.runtime.originalXAxis.dataKey]
584
- : formatNumber(data[barGroup.index][config.runtime.originalXAxis.dataKey])}
585
- </Text>
586
- )}
587
-
588
- {orientation === 'vertical' && (
589
- <Text display={config.labels && displayBar ? 'block' : 'none'} opacity={transparentBar ? 0.5 : 1} x={barWidth * (bar.index + 0.5) + offset} y={barY - 5} fill={labelColor} textAnchor='middle'>
590
- {yAxisValue}
591
- </Text>
592
- )}
593
- {config.isLollipopChart && config.lollipopShape === 'circle' && (
594
- <circle
595
- cx={orientation === 'horizontal' ? bar.y : barWidth * (barGroup.bars.length - bar.index - 1) + (isLabelBelowBar && orientation === 'horizontal' ? 0 : offset) + lollipopShapeSize / 3.5}
596
- cy={orientation === 'horizontal' ? 0 + lollipopBarWidth / 2 : bar.y}
597
- r={lollipopShapeSize / 2}
598
- fill={barColor}
599
- key={`circle--${bar.index}`}
600
- data-tooltip-html={tooltip}
601
- data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
602
- style={{ filter: 'unset', opacity: 1 }}
603
- />
604
- )}
605
- {config.isLollipopChart && config.lollipopShape === 'square' && (
606
- <rect
607
- x={orientation === 'horizontal' && bar.y > 10 ? bar.y - lollipopShapeSize / 2 : orientation === 'horizontal' && bar.y < 10 ? 0 : orientation !== 'horizontal' ? offset - lollipopBarWidth / 2 : barWidth * (barGroup.bars.length - bar.index - 1) + offset - 5.25}
608
- y={orientation === 'horizontal' ? 0 - lollipopBarWidth / 2 : barY}
609
- width={lollipopShapeSize}
610
- height={lollipopShapeSize}
611
- fill={barColor}
612
- key={`circle--${bar.index}`}
613
- data-tooltip-html={tooltip}
614
- data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
615
- style={{ opacity: 1, filter: 'unset' }}
616
- >
617
- <animate attributeName='height' values={`0, ${lollipopShapeSize}`} dur='2.5s' />
618
- </rect>
619
- )}
620
- </Group>
621
- </Group>
622
- )
623
- })}
624
- </Group>
625
- ))
626
- }}
627
- </BarGroup>
628
-
629
- {Object.keys(config.confidenceKeys).length > 0
630
- ? data.map(d => {
631
- let xPos, yPos
632
- let upperPos
633
- let lowerPos
634
- let tickWidth = 5
635
- // DEV-3264 Make Confidence Intervals work on horizontal bar charts
636
- if (orientation === 'horizontal') {
637
- yPos = yScale(getXAxisData(d)) - 0.75 * config.barHeight
638
- upperPos = xScale(getYAxisData(d, config.confidenceKeys.upper))
639
- lowerPos = xScale(getYAxisData(d, config.confidenceKeys.lower))
640
- return (
641
- <path
642
- key={`confidence-interval-h-${yPos}-${d[config.runtime.originalXAxis.dataKey]}`}
643
- stroke='#333'
644
- strokeWidth='px'
645
- d={`
646
- M${lowerPos} ${yPos - tickWidth}
647
- L${lowerPos} ${yPos + tickWidth}
648
- M${lowerPos} ${yPos}
649
- L${upperPos} ${yPos}
650
- M${upperPos} ${yPos - tickWidth}
651
- L${upperPos} ${yPos + tickWidth} `}
652
- />
653
- )
654
- } else {
655
- xPos = xScale(getXAxisData(d))
656
- upperPos = yScale(getYAxisData(d, config.confidenceKeys.lower))
657
- lowerPos = yScale(getYAxisData(d, config.confidenceKeys.upper))
658
- return (
659
- <path
660
- key={`confidence-interval-v-${yPos}-${d[config.runtime.originalXAxis.dataKey]}`}
661
- stroke='#333'
662
- strokeWidth='px'
663
- d={`
664
- M${xPos - tickWidth} ${upperPos}
665
- L${xPos + tickWidth} ${upperPos}
666
- M${xPos} ${upperPos}
667
- L${xPos} ${lowerPos}
668
- M${xPos - tickWidth} ${lowerPos}
669
- L${xPos + tickWidth} ${lowerPos}`}
670
- />
671
- )
672
- }
673
- })
674
- : ''}
675
- </Group>
676
- )}
23
+ {/* tooltips */}
24
+ <Bar key={'bars'} width={Number(xMax)} height={Number(yMax)} fill={false ? 'red' : 'transparent'} fillOpacity={0.05} onMouseMove={e => handleTooltipMouseOver(e, data)} onMouseOut={handleTooltipMouseOff} onClick={e => handleTooltipClick(e, data)} />
677
25
  </Group>
678
26
  </ErrorBoundary>
679
27
  )
680
28
  }
29
+
30
+ export default BarChart