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