@cdc/chart 4.23.10 → 4.23.11
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/dist/cdcchart.js +30989 -29057
- package/examples/feature/bar/example-bar-chart.json +1 -46
- package/examples/feature/bar/lollipop.json +156 -0
- package/examples/feature/combo/planet-combo-example-config.json +99 -9
- package/examples/feature/dev-4261.json +399 -0
- package/examples/feature/forest-plot/{broken.json → linear.json} +55 -50
- package/examples/feature/forest-plot/logarithmic.json +306 -0
- package/examples/feature/line/line-points.json +340 -0
- package/examples/feature/regions/index.json +462 -0
- package/examples/gallery/bar-chart-vertical/combo-line-chart.json +181 -48
- package/examples/sparkline-multilple.json +846 -0
- package/index.html +10 -6
- package/package.json +3 -3
- package/src/CdcChart.tsx +75 -63
- package/src/_stories/Chart.stories.tsx +188 -0
- package/src/_stories/Chart.tooltip.stories.tsx +305 -0
- package/src/_stories/ChartBrush.stories.tsx +19 -0
- package/src/_stories/ChartSuppress.stories.tsx +19 -0
- package/src/_stories/_mock/brush_mock.json +393 -0
- package/src/_stories/_mock/suppress_mock.json +911 -0
- package/src/components/AreaChart.Stacked.jsx +4 -5
- package/src/components/AreaChart.jsx +6 -35
- package/src/components/{BarChart.Horizontal.jsx → BarChart.Horizontal.tsx} +106 -29
- package/src/components/{BarChart.StackedHorizontal.jsx → BarChart.StackedHorizontal.tsx} +22 -17
- package/src/components/{BarChart.StackedVertical.jsx → BarChart.StackedVertical.tsx} +22 -26
- package/src/components/{BarChart.Vertical.jsx → BarChart.Vertical.tsx} +175 -31
- package/src/components/BarChart.jsx +1 -1
- package/src/components/DeviationBar.jsx +4 -3
- package/src/components/EditorPanel.jsx +176 -50
- package/src/components/ForestPlot/ForestPlotProps.ts +11 -0
- package/src/components/ForestPlot/Readme.md +0 -0
- package/src/components/ForestPlot/index.scss +1 -0
- package/src/components/{ForestPlot.jsx → ForestPlot/index.tsx} +51 -31
- package/src/components/ForestPlotSettings.jsx +162 -113
- package/src/components/Legend.jsx +36 -3
- package/src/components/{LineChart.Circle.tsx → LineChart/LineChart.Circle.tsx} +26 -29
- package/src/components/LineChart/LineChartProps.ts +17 -0
- package/src/components/LineChart/index.scss +1 -0
- package/src/components/{LineChart.tsx → LineChart/index.tsx} +64 -35
- package/src/components/LinearChart.jsx +120 -60
- package/src/components/PairedBarChart.jsx +2 -2
- package/src/components/Series.jsx +22 -3
- package/src/components/ZoomBrush.tsx +168 -0
- package/src/data/initial-state.js +27 -12
- package/src/hooks/useBarChart.js +71 -7
- package/src/hooks/useColorScale.ts +50 -0
- package/src/hooks/useEditorPermissions.js +22 -4
- package/src/hooks/{useMinMax.js → useMinMax.ts} +75 -23
- package/src/hooks/{useRightAxis.js → useRightAxis.ts} +10 -2
- package/src/hooks/{useScales.js → useScales.ts} +64 -17
- package/src/hooks/useTooltip.jsx +68 -41
- package/src/scss/main.scss +3 -35
- package/src/types/ChartConfig.ts +43 -0
- package/src/types/ChartContext.ts +38 -0
- package/src/types/ChartProps.ts +7 -0
- package/src/types/ForestPlot.ts +60 -0
|
@@ -10,10 +10,10 @@ import { Bar, AreaStack } from '@visx/shape'
|
|
|
10
10
|
import { Group } from '@visx/group'
|
|
11
11
|
import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
|
|
12
12
|
|
|
13
|
-
const AreaChartStacked = ({ xScale, yScale, yMax, xMax, handleTooltipMouseOver, handleTooltipMouseOff, isDebug
|
|
13
|
+
const AreaChartStacked = ({ xScale, yScale, yMax, xMax, handleTooltipMouseOver, handleTooltipMouseOff, isDebug }) => {
|
|
14
14
|
// import data from context
|
|
15
|
-
let { transformedData
|
|
16
|
-
|
|
15
|
+
let { transformedData, config, seriesHighlight, colorScale, rawData } = useContext(ConfigContext)
|
|
16
|
+
const data = config.brush.active && config.brush.data?.length ? config.brush.data : transformedData
|
|
17
17
|
// Draw transparent bars over the chart to get tooltip data
|
|
18
18
|
// Turn DEBUG on for additional context.
|
|
19
19
|
if (!data) return
|
|
@@ -60,9 +60,8 @@ const AreaChartStacked = ({ xScale, yScale, yMax, xMax, handleTooltipMouseOver,
|
|
|
60
60
|
})
|
|
61
61
|
}}
|
|
62
62
|
</AreaStack>
|
|
63
|
-
|
|
64
63
|
{/* prettier-ignore */}
|
|
65
|
-
|
|
64
|
+
<Bar width={Number(xMax)} height={Number(yMax)} fill={'transparent'} onMouseMove={e => handleTooltipMouseOver(e, rawData)} onMouseLeave={handleTooltipMouseOff} />
|
|
66
65
|
</Group>
|
|
67
66
|
</ErrorBoundary>
|
|
68
67
|
</svg>
|
|
@@ -11,17 +11,11 @@ import { Group } from '@visx/group'
|
|
|
11
11
|
import { bisector } from 'd3-array'
|
|
12
12
|
|
|
13
13
|
const AreaChart = props => {
|
|
14
|
-
const { xScale, yScale, yMax, xMax, handleTooltipMouseOver, handleTooltipMouseOff, isDebug,
|
|
14
|
+
const { xScale, yScale, yMax, xMax, handleTooltipMouseOver, handleTooltipMouseOff, isDebug, children } = props
|
|
15
15
|
// import data from context
|
|
16
|
-
let { transformedData
|
|
16
|
+
let { transformedData, config, handleLineType, parseDate, formatDate, formatNumber, seriesHighlight, colorScale, rawData } = useContext(ConfigContext)
|
|
17
|
+
const data = config.brush.active && config.brush.data?.length ? config.brush.data : transformedData
|
|
17
18
|
|
|
18
|
-
// use brush data if it is passed in AND if this is NOT a brush chart
|
|
19
|
-
data = !isBrush && undefined !== brushData && brushData.length ? brushData : data
|
|
20
|
-
|
|
21
|
-
if (isBrush && isDebug) console.log('###AREAchart BRUSH data, xScale, yScale, yMax, xMax', data, xScale, yScale, yMax, xMax)
|
|
22
|
-
|
|
23
|
-
// Draw transparent bars over the chart to get tooltip data
|
|
24
|
-
// Turn isDebug on for additional context.
|
|
25
19
|
if (!data) return
|
|
26
20
|
|
|
27
21
|
// Tooltip helper for getting data to the closest date/category hovered.
|
|
@@ -42,38 +36,19 @@ const AreaChart = props => {
|
|
|
42
36
|
}
|
|
43
37
|
}
|
|
44
38
|
|
|
45
|
-
const getXAxisDates = brushDataSet => {
|
|
46
|
-
if (undefined === brushDataSet || !brushDataSet) return
|
|
47
|
-
let XAxisBrushDates = []
|
|
48
|
-
brushDataSet.forEach(function convertDateTimeNumber(key, value, brushDataSet) {
|
|
49
|
-
let tmp = getXValueFromCoordinate(xScale(value))
|
|
50
|
-
let date = formatDate(tmp)
|
|
51
|
-
XAxisBrushDates.push(date)
|
|
52
|
-
})
|
|
53
|
-
return XAxisBrushDates
|
|
54
|
-
}
|
|
55
|
-
|
|
56
39
|
const handleX = d => {
|
|
57
40
|
return config.xAxis.type === 'date' ? xScale(parseDate(d[config.xAxis.dataKey], false)) : xScale(d[config.xAxis.dataKey])
|
|
58
41
|
}
|
|
59
42
|
|
|
60
43
|
const handleY = (d, index, s = undefined) => {
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// prevents duplicate brush handles being rendered
|
|
65
|
-
const getFirstBrushHandleOnly = (children, index) => {
|
|
66
|
-
if (index === 0) {
|
|
67
|
-
return children
|
|
68
|
-
}
|
|
69
|
-
// else dont return the other brush handles
|
|
44
|
+
return yScale(d[s.dataKey])
|
|
70
45
|
}
|
|
71
46
|
|
|
72
47
|
return (
|
|
73
48
|
data && (
|
|
74
49
|
<svg>
|
|
75
50
|
<ErrorBoundary component='AreaChart'>
|
|
76
|
-
<Group className='area-chart' key='area-wrapper' left={Number(config.yAxis.size)}
|
|
51
|
+
<Group className='area-chart' key='area-wrapper' left={Number(config.yAxis.size)}>
|
|
77
52
|
{(config.runtime.areaSeriesKeys || config.series).map((s, index) => {
|
|
78
53
|
let seriesData = data.map(d => {
|
|
79
54
|
return {
|
|
@@ -119,14 +94,10 @@ const AreaChart = props => {
|
|
|
119
94
|
curve={curveType}
|
|
120
95
|
strokeDasharray={s.type ? handleLineType(s.type) : 0}
|
|
121
96
|
/>
|
|
122
|
-
{getFirstBrushHandleOnly(children, index)}
|
|
123
97
|
</React.Fragment>
|
|
124
98
|
)
|
|
125
99
|
})}
|
|
126
|
-
|
|
127
|
-
{/* Transparent bar for tooltips - disable if AreaChart is a brush */}
|
|
128
|
-
{/* prettier-ignore */}
|
|
129
|
-
{!isBrush && <Bar width={Number(xMax)} height={Number(yMax)} fill={isDebug ? 'red' : 'transparent'} fillOpacity={0.05} style={isDebug ? { stroke: 'black', strokeWidth: 2 } : {}} onMouseMove={e => handleTooltipMouseOver(e, rawData)} onMouseLeave={handleTooltipMouseOff} />}
|
|
100
|
+
<Bar width={Number(xMax)} height={Number(yMax)} fill={'transparent'} fillOpacity={0.05} onMouseMove={e => handleTooltipMouseOver(e, rawData)} onMouseLeave={handleTooltipMouseOff} />
|
|
130
101
|
</Group>
|
|
131
102
|
</ErrorBoundary>
|
|
132
103
|
</svg>
|
|
@@ -5,17 +5,49 @@ import { Group } from '@visx/group'
|
|
|
5
5
|
import { Text } from '@visx/text'
|
|
6
6
|
import { BarGroup } from '@visx/shape'
|
|
7
7
|
import { useHighlightedBars } from '../hooks/useHighlightedBars'
|
|
8
|
+
import { FaStar } from 'react-icons/fa'
|
|
8
9
|
|
|
9
10
|
// third party
|
|
10
11
|
import chroma from 'chroma-js'
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
import { type BarChartProps } from '../types/ChartProps'
|
|
14
|
+
|
|
15
|
+
export const BarChartHorizontal = (props: BarChartProps) => {
|
|
13
16
|
const { xScale, yScale, yMax, seriesScale } = props
|
|
14
17
|
const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, setSharedFilter, isNumber, getTextWidth, getYAxisData, getXAxisData } = useContext(ConfigContext)
|
|
15
|
-
const {
|
|
18
|
+
const {
|
|
19
|
+
isHorizontal,
|
|
20
|
+
barBorderWidth,
|
|
21
|
+
applyRadius,
|
|
22
|
+
updateBars,
|
|
23
|
+
assignColorsToValues,
|
|
24
|
+
section,
|
|
25
|
+
fontSize,
|
|
26
|
+
isLabelBelowBar,
|
|
27
|
+
displayNumbersOnBar,
|
|
28
|
+
lollipopBarWidth,
|
|
29
|
+
lollipopShapeSize,
|
|
30
|
+
getHighlightedBarColorByValue,
|
|
31
|
+
getHighlightedBarByValue,
|
|
32
|
+
generateIconSize,
|
|
33
|
+
getAdditionalColumn,
|
|
34
|
+
hoveredBar,
|
|
35
|
+
onMouseLeaveBar,
|
|
36
|
+
onMouseOverBar
|
|
37
|
+
} = useBarChart()
|
|
16
38
|
|
|
17
39
|
const { HighLightedBarUtils } = useHighlightedBars(config)
|
|
18
|
-
|
|
40
|
+
const getIcon = (bar, barWidth) => {
|
|
41
|
+
let icon = null
|
|
42
|
+
const iconSize = generateIconSize(barWidth)
|
|
43
|
+
config.suppressedData?.forEach(d => {
|
|
44
|
+
if (bar.key === d.column && String(bar.value) === String(d.value) && d.icon) {
|
|
45
|
+
icon = <FaStar color='#000' size={iconSize} />
|
|
46
|
+
// icon = <BarIcon color='#000' size={fontSize[config.fontSize] / 1.7} />
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
return icon
|
|
50
|
+
}
|
|
19
51
|
return (
|
|
20
52
|
config.visualizationSubType !== 'stacked' &&
|
|
21
53
|
config.visualizationType === 'Bar' &&
|
|
@@ -40,21 +72,24 @@ export const BarChartHorizontal = props => {
|
|
|
40
72
|
const scaleVal = config.useLogScale ? 0.1 : 0
|
|
41
73
|
|
|
42
74
|
let highlightedBarValues = config.highlightedBarValues.map(item => item.value).filter(item => item !== ('' || undefined))
|
|
43
|
-
|
|
44
75
|
highlightedBarValues = config.xAxis.type === 'date' ? HighLightedBarUtils.formatDates(highlightedBarValues) : highlightedBarValues
|
|
45
|
-
|
|
46
76
|
let transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
|
|
47
77
|
let displayBar = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(bar.key) !== -1
|
|
48
78
|
let barHeight = config.barHeight
|
|
49
|
-
let barY = bar.value >= 0 && isNumber(bar.value) ? bar.y : yScale(
|
|
50
|
-
const
|
|
79
|
+
let barY = bar.value >= 0 && isNumber(bar.value) ? bar.y : yScale(scaleVal)
|
|
80
|
+
const barXBase = bar.value < 0 ? Math.abs(xScale(bar.value)) : xScale(scaleVal)
|
|
51
81
|
const barWidthHorizontal = Math.abs(xScale(bar.value) - xScale(scaleVal))
|
|
52
|
-
const
|
|
82
|
+
const suppresedBarWidth = 25
|
|
83
|
+
const isPositiveBar = bar.value >= 0 && isNumber(bar.value)
|
|
84
|
+
let barWidth = bar.value && config.suppressedData.some(({ column, value }) => bar.key === column && bar.value === value) ? suppresedBarWidth : barWidthHorizontal
|
|
53
85
|
|
|
86
|
+
const supprssedBarX = isPositiveBar ? xScale(0) : xScale(scaleVal) - suppresedBarWidth
|
|
87
|
+
const barX = config.suppressedData.some(d => bar.key === d.column && String(bar.value) === String(d.value)) ? supprssedBarX : barXBase
|
|
54
88
|
const yAxisValue = formatNumber(bar.value, 'left')
|
|
55
89
|
const xAxisValue = config.runtime[section].type === 'date' ? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey])) : data[barGroup.index][config.runtime.originalXAxis.dataKey]
|
|
56
90
|
|
|
57
|
-
const barPosition =
|
|
91
|
+
const barPosition = !isPositiveBar ? 'below' : 'above'
|
|
92
|
+
const barValueLabel = config.suppressedData.some(d => bar.key === d.column && bar.value === d.value) ? '' : yAxisValue
|
|
58
93
|
|
|
59
94
|
// check if bar text/value string fits into each bars.
|
|
60
95
|
let textWidth = getTextWidth(xAxisValue, `normal ${fontSize[config.fontSize]}px sans-serif`)
|
|
@@ -79,16 +114,13 @@ export const BarChartHorizontal = props => {
|
|
|
79
114
|
const newIndex = bar.value < 0 ? -1 : index
|
|
80
115
|
const borderRadius = applyRadius(newIndex)
|
|
81
116
|
|
|
82
|
-
let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
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
|
|
86
|
-
}
|
|
87
|
-
|
|
117
|
+
let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${xAxisValue}` : xAxisValue
|
|
118
|
+
const additionalColTooltip = getAdditionalColumn(hoveredBar)
|
|
119
|
+
const tooltipBody = `${config.runtime.seriesLabels[bar.key]}: ${yAxisValue}`
|
|
88
120
|
const tooltip = `<ul>
|
|
89
|
-
|
|
90
|
-
<li class="tooltip-
|
|
91
|
-
<li class="tooltip-body">${
|
|
121
|
+
<li class="tooltip-heading"">${yAxisTooltip}</li>
|
|
122
|
+
<li class="tooltip-body ">${tooltipBody}</li>
|
|
123
|
+
<li class="tooltip-body ">${additionalColTooltip}</li>
|
|
92
124
|
</li></ul>`
|
|
93
125
|
|
|
94
126
|
// configure colors
|
|
@@ -103,23 +135,49 @@ export const BarChartHorizontal = props => {
|
|
|
103
135
|
const highlightedBar = getHighlightedBarByValue(yAxisValue)
|
|
104
136
|
const borderColor = isHighlightedBar ? highlightedBarColor : config.barHasBorder === 'true' ? '#000' : 'transparent'
|
|
105
137
|
const borderWidth = isHighlightedBar ? highlightedBar.borderWidth : config.isLollipopChart ? 0 : config.barHasBorder === 'true' ? barBorderWidth : 0
|
|
138
|
+
const displaylollipopShape = config.suppressedData.some(d => bar.key === d.column && bar.value === d.value) ? 'none' : 'block'
|
|
106
139
|
// update label color
|
|
107
140
|
if (barColor && labelColor) {
|
|
108
141
|
if (chroma.contrast(labelColor, barColor) < 4.9) {
|
|
109
142
|
labelColor = textFits ? '#FFFFFF' : '#000000'
|
|
110
143
|
}
|
|
111
144
|
}
|
|
145
|
+
const getTop = () => {
|
|
146
|
+
if (Number(barHeight) < 20) return -4
|
|
147
|
+
if (Number(barHeight) < 25) return -1
|
|
148
|
+
if (Number(barHeight) < 30) return 2
|
|
149
|
+
if (Number(barHeight) < 35) return 4
|
|
150
|
+
if (Number(barHeight) < 40) return 5
|
|
151
|
+
if (Number(barHeight) < 50) return 9
|
|
152
|
+
if (Number(barHeight) < 60) return 10
|
|
153
|
+
else {
|
|
154
|
+
return 12
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const iconStyle: { [key: string]: any } = {
|
|
158
|
+
position: 'absolute',
|
|
159
|
+
top: getTop(),
|
|
160
|
+
left: suppresedBarWidth * 1.2
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (config.isLollipopChart) {
|
|
164
|
+
iconStyle.top = -9
|
|
165
|
+
}
|
|
112
166
|
const background = () => {
|
|
113
167
|
if (isRegularLollipopColor) return barColor
|
|
114
168
|
if (isTwoToneLollipopColor) return chroma(barColor).brighten(1)
|
|
115
169
|
if (isHighlightedBar) return 'transparent'
|
|
116
170
|
return barColor
|
|
117
171
|
}
|
|
172
|
+
|
|
118
173
|
const finalStyle = {
|
|
119
174
|
background: background(),
|
|
120
175
|
borderColor,
|
|
121
176
|
borderStyle: 'solid',
|
|
122
177
|
borderWidth,
|
|
178
|
+
width: barWidth,
|
|
179
|
+
transition: 'all 0.2s linear',
|
|
180
|
+
height: !config.isLollipopChart ? barHeight : lollipopBarWidth,
|
|
123
181
|
...borderRadius
|
|
124
182
|
}
|
|
125
183
|
|
|
@@ -128,22 +186,24 @@ export const BarChartHorizontal = props => {
|
|
|
128
186
|
{/* This feels gross but inline transition was not working well*/}
|
|
129
187
|
<style>
|
|
130
188
|
{`
|
|
131
|
-
.linear #barGroup${barGroup.index},
|
|
132
|
-
.Combo #barGroup${barGroup.index} {
|
|
189
|
+
.linear #barGroup${barGroup.index} div,
|
|
190
|
+
.Combo #barGroup${barGroup.index} div {
|
|
133
191
|
transform-origin: 0 ${barY + barHeight}px;
|
|
134
192
|
}
|
|
135
193
|
`}
|
|
136
194
|
</style>
|
|
137
195
|
<Group key={`bar-sub-group-${barGroup.index}-${barGroup.x0}-${barY}--${index}`}>
|
|
138
196
|
<foreignObject
|
|
197
|
+
onMouseOver={() => onMouseOverBar(xAxisValue, bar.key)}
|
|
198
|
+
onMouseLeave={onMouseLeaveBar}
|
|
139
199
|
id={`barGroup${barGroup.index}`}
|
|
140
200
|
key={`bar-group-bar-${barGroup.index}-${bar.index}-${bar.value}-${bar.key}`}
|
|
141
201
|
x={barX}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
height={!config.isLollipopChart ?
|
|
145
|
-
|
|
146
|
-
opacity={transparentBar ? 0.
|
|
202
|
+
style={{ overflow: 'visible', ...finalStyle }}
|
|
203
|
+
y={barHeight * bar.index}
|
|
204
|
+
height={!config.isLollipopChart ? barHeight : lollipopBarWidth}
|
|
205
|
+
width={barWidth}
|
|
206
|
+
opacity={transparentBar ? 0.2 : 1}
|
|
147
207
|
display={displayBar ? 'block' : 'none'}
|
|
148
208
|
data-tooltip-html={tooltip}
|
|
149
209
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
@@ -154,7 +214,13 @@ export const BarChartHorizontal = props => {
|
|
|
154
214
|
setSharedFilter(config.uid, bar)
|
|
155
215
|
}
|
|
156
216
|
}}
|
|
157
|
-
|
|
217
|
+
>
|
|
218
|
+
<div style={{ position: 'relative' }}>
|
|
219
|
+
<div style={iconStyle}>{getIcon(bar, barWidth)}</div>
|
|
220
|
+
<div style={{ ...finalStyle }}></div>
|
|
221
|
+
</div>
|
|
222
|
+
</foreignObject>
|
|
223
|
+
|
|
158
224
|
{!config.isLollipopChart && displayNumbersOnBar && (
|
|
159
225
|
<Text // prettier-ignore
|
|
160
226
|
display={displayBar ? 'block' : 'none'}
|
|
@@ -165,7 +231,7 @@ export const BarChartHorizontal = props => {
|
|
|
165
231
|
verticalAnchor='middle'
|
|
166
232
|
textAnchor={textAnchor}
|
|
167
233
|
>
|
|
168
|
-
{
|
|
234
|
+
{barValueLabel}
|
|
169
235
|
</Text>
|
|
170
236
|
)}
|
|
171
237
|
{config.isLollipopChart && displayNumbersOnBar && (
|
|
@@ -179,7 +245,7 @@ export const BarChartHorizontal = props => {
|
|
|
179
245
|
verticalAnchor='middle'
|
|
180
246
|
fontWeight={'normal'}
|
|
181
247
|
>
|
|
182
|
-
{
|
|
248
|
+
{barValueLabel}
|
|
183
249
|
</Text>
|
|
184
250
|
)}
|
|
185
251
|
{isLabelBelowBar && !config.yAxis.hideLabel && (
|
|
@@ -193,10 +259,21 @@ export const BarChartHorizontal = props => {
|
|
|
193
259
|
)}
|
|
194
260
|
|
|
195
261
|
{config.isLollipopChart && config.lollipopShape === 'circle' && (
|
|
196
|
-
<circle
|
|
262
|
+
<circle
|
|
263
|
+
display={displaylollipopShape}
|
|
264
|
+
cx={bar.y}
|
|
265
|
+
cy={0 + lollipopBarWidth / 2}
|
|
266
|
+
r={lollipopShapeSize / 2}
|
|
267
|
+
fill={barColor}
|
|
268
|
+
key={`circle--${bar.index}`}
|
|
269
|
+
data-tooltip-html={tooltip}
|
|
270
|
+
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
271
|
+
style={{ filter: 'unset', opacity: 1 }}
|
|
272
|
+
/>
|
|
197
273
|
)}
|
|
198
274
|
{config.isLollipopChart && config.lollipopShape === 'square' && (
|
|
199
275
|
<rect
|
|
276
|
+
display={displaylollipopShape}
|
|
200
277
|
x={bar.y > 10 ? bar.y - lollipopShapeSize / 2 : 0}
|
|
201
278
|
y={0 - lollipopBarWidth / 2}
|
|
202
279
|
width={lollipopShapeSize}
|
|
@@ -8,10 +8,12 @@ import { Text } from '@visx/text'
|
|
|
8
8
|
// third party
|
|
9
9
|
import chroma from 'chroma-js'
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
import { type BarChartProps } from '../types/ChartProps'
|
|
12
|
+
|
|
13
|
+
const BarChartStackedHorizontal = (props: BarChartProps) => {
|
|
12
14
|
const { xScale, yScale, xMax, yMax } = props
|
|
13
|
-
const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, setSharedFilter, animatedChart, getTextWidth } = useContext(ConfigContext)
|
|
14
|
-
const { isHorizontal, barBorderWidth, hasMultipleSeries, applyRadius, updateBars, isLabelBelowBar, displayNumbersOnBar, fontSize } = useBarChart()
|
|
15
|
+
const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, setSharedFilter, animatedChart, getTextWidth, setSeriesHighlight } = useContext(ConfigContext)
|
|
16
|
+
const { isHorizontal, barBorderWidth, hasMultipleSeries, applyRadius, updateBars, isLabelBelowBar, displayNumbersOnBar, fontSize, getAdditionalColumn, hoveredBar, onMouseLeaveBar, onMouseOverBar } = useBarChart()
|
|
15
17
|
const { orientation, visualizationSubType } = config
|
|
16
18
|
|
|
17
19
|
return (
|
|
@@ -31,18 +33,17 @@ const BarChartStackedHorizontal = props => {
|
|
|
31
33
|
const yAxisValue = config.runtime.yAxis.type === 'date' ? formatDate(parseDate(data[bar.index][config.runtime.originalXAxis.dataKey])) : data[bar.index][config.runtime.originalXAxis.dataKey]
|
|
32
34
|
const style = applyRadius(barStack.index)
|
|
33
35
|
let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
|
|
34
|
-
let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
|
|
35
36
|
let textWidth = getTextWidth(xAxisValue, `normal ${fontSize[config.fontSize]}px sans-serif`)
|
|
36
|
-
if (!hasMultipleSeries) {
|
|
37
|
-
xAxisTooltip = config.isLegendValue ? `${bar.key}: ${xAxisValue}` : config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisTooltip
|
|
38
|
-
}
|
|
39
|
-
const tooltip = `<div>
|
|
40
|
-
${config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && hasMultipleSeries ? `${config.runtime.seriesLabels[bar.key] || ''}<br/>` : ''}
|
|
41
|
-
${yAxisTooltip}<br />
|
|
42
|
-
${xAxisTooltip}
|
|
43
|
-
</div>`
|
|
44
37
|
|
|
45
|
-
|
|
38
|
+
const additionalColTooltip = getAdditionalColumn(hoveredBar)
|
|
39
|
+
const tooltipBody = `${config.runtime.seriesLabels[bar.key]}: ${xAxisValue}`
|
|
40
|
+
const tooltip = `<ul>
|
|
41
|
+
<li class="tooltip-heading"">${yAxisTooltip}</li>
|
|
42
|
+
<li class="tooltip-body ">${tooltipBody}</li>
|
|
43
|
+
<li class="tooltip-body ">${additionalColTooltip}</li>
|
|
44
|
+
</li></ul>`
|
|
45
|
+
|
|
46
|
+
if (chroma.contrast(labelColor, colorScale(config.runtime.seriesLabels[bar.key])) < 4.9) {
|
|
46
47
|
labelColor = '#FFFFFF'
|
|
47
48
|
}
|
|
48
49
|
|
|
@@ -51,7 +52,7 @@ const BarChartStackedHorizontal = props => {
|
|
|
51
52
|
<style>
|
|
52
53
|
{`
|
|
53
54
|
#barStack${barStack.index}-${bar.index} rect,
|
|
54
|
-
#barStack${barStack.index}-${bar.index} foreignObject{
|
|
55
|
+
#barStack${barStack.index}-${bar.index} foreignObject div{
|
|
55
56
|
animation-delay: ${barStack.index * 0.5}s;
|
|
56
57
|
transform-origin: ${bar.x}px
|
|
57
58
|
}
|
|
@@ -59,14 +60,16 @@ const BarChartStackedHorizontal = props => {
|
|
|
59
60
|
</style>
|
|
60
61
|
<Group key={index} id={`barStack${barStack.index}-${bar.index}`} className='stack horizontal'>
|
|
61
62
|
<foreignObject
|
|
63
|
+
onMouseOver={() => onMouseOverBar(yAxisValue, bar.key)}
|
|
64
|
+
onMouseLeave={onMouseLeaveBar}
|
|
62
65
|
key={`barstack-horizontal-${barStack.index}-${bar.index}-${index}`}
|
|
63
66
|
className={`animated-chart group ${animatedChart ? 'animated' : ''}`}
|
|
64
67
|
x={bar.x}
|
|
65
68
|
y={bar.y}
|
|
69
|
+
style={{ transition: 'all 0.2s linear' }}
|
|
66
70
|
width={bar.width}
|
|
67
71
|
height={bar.height}
|
|
68
|
-
|
|
69
|
-
opacity={transparentBar ? 0.5 : 1}
|
|
72
|
+
opacity={transparentBar ? 0.2 : 1}
|
|
70
73
|
display={displayBar ? 'block' : 'none'}
|
|
71
74
|
data-tooltip-html={tooltip}
|
|
72
75
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
@@ -77,7 +80,9 @@ const BarChartStackedHorizontal = props => {
|
|
|
77
80
|
setSharedFilter(config.uid, bar)
|
|
78
81
|
}
|
|
79
82
|
}}
|
|
80
|
-
|
|
83
|
+
>
|
|
84
|
+
<div style={{ width: bar.width, height: bar.height, background: colorScale(config.runtime.seriesLabels[bar.key]), border: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`, ...style }}></div>
|
|
85
|
+
</foreignObject>
|
|
81
86
|
|
|
82
87
|
{orientation === 'horizontal' && visualizationSubType === 'stacked' && isLabelBelowBar && barStack.index === 0 && !config.yAxis.hideLabel && (
|
|
83
88
|
<Text
|
|
@@ -4,12 +4,14 @@ import { useBarChart } from '../hooks/useBarChart'
|
|
|
4
4
|
import { BarStack } from '@visx/shape'
|
|
5
5
|
import { Group } from '@visx/group'
|
|
6
6
|
import { Text } from '@visx/text'
|
|
7
|
+
import { type BarChartProps } from '../types/ChartProps'
|
|
7
8
|
|
|
8
|
-
const BarChartStackedVertical = props => {
|
|
9
|
+
const BarChartStackedVertical = (props: BarChartProps) => {
|
|
9
10
|
const { xScale, yScale, xMax, yMax } = props
|
|
10
|
-
const { transformedData
|
|
11
|
-
const { isHorizontal, barBorderWidth,
|
|
11
|
+
const { transformedData, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, setSharedFilter } = useContext(ConfigContext)
|
|
12
|
+
const { isHorizontal, barBorderWidth, applyRadius, hoveredBar, getAdditionalColumn, onMouseLeaveBar, onMouseOverBar } = useBarChart()
|
|
12
13
|
const { orientation } = config
|
|
14
|
+
const data = config.brush.active && config.brush.data?.length ? config.brush.data : transformedData
|
|
13
15
|
|
|
14
16
|
return (
|
|
15
17
|
config.visualizationSubType === 'stacked' &&
|
|
@@ -26,33 +28,23 @@ const BarChartStackedVertical = props => {
|
|
|
26
28
|
// tooltips
|
|
27
29
|
const xAxisValue = config.runtime.xAxis.type === 'date' ? formatDate(parseDate(data[bar.index][config.runtime.xAxis.dataKey])) : data[bar.index][config.runtime.xAxis.dataKey]
|
|
28
30
|
const yAxisValue = formatNumber(bar.bar ? bar.bar.data[bar.key] : 0, 'left')
|
|
29
|
-
|
|
30
|
-
if(!yAxisValue) return <></>
|
|
31
|
-
|
|
31
|
+
if (!yAxisValue) return
|
|
32
32
|
const style = applyRadius(barStack.index)
|
|
33
|
-
let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
|
|
34
33
|
const xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
} = config
|
|
43
|
-
|
|
44
|
-
const barStackTooltip = `<div>
|
|
45
|
-
<p class="tooltip-heading"><strong>${xAxisTooltip}</strong></p>
|
|
46
|
-
${showLegendValuesTooltip && seriesLabels && hasMultipleSeries ? `${seriesLabels[bar.key] || ''}<br/>` : ''}
|
|
47
|
-
${yAxisTooltip}<br />
|
|
48
|
-
</div>`
|
|
34
|
+
const additionalColTooltip = getAdditionalColumn(hoveredBar)
|
|
35
|
+
const tooltipBody = `${config.runtime.seriesLabels[bar.key]}: ${yAxisValue}`
|
|
36
|
+
const tooltip = `<ul>
|
|
37
|
+
<li class="tooltip-heading"">${xAxisTooltip}</li>
|
|
38
|
+
<li class="tooltip-body ">${tooltipBody}</li>
|
|
39
|
+
<li class="tooltip-body ">${additionalColTooltip}</li>
|
|
40
|
+
</li></ul>`
|
|
49
41
|
|
|
50
42
|
return (
|
|
51
43
|
<Group key={`${barStack.index}--${bar.index}--${orientation}`}>
|
|
52
44
|
<style>
|
|
53
45
|
{`
|
|
54
46
|
#barStack${barStack.index}-${bar.index} rect,
|
|
55
|
-
#barStack${barStack.index}-${bar.index} foreignObject{
|
|
47
|
+
#barStack${barStack.index}-${bar.index} foreignObject div{
|
|
56
48
|
animation-delay: ${barStack.index * 0.5}s;
|
|
57
49
|
transform-origin: ${barThicknessAdjusted / 2}px ${bar.y + bar.height}px
|
|
58
50
|
}
|
|
@@ -63,15 +55,15 @@ const BarChartStackedVertical = props => {
|
|
|
63
55
|
{yAxisValue}
|
|
64
56
|
</Text>
|
|
65
57
|
<foreignObject
|
|
58
|
+
onMouseOver={() => onMouseOverBar(xAxisValue, bar.key)}
|
|
59
|
+
onMouseLeave={onMouseLeaveBar}
|
|
66
60
|
key={`bar-stack-${barStack.index}-${bar.index}`}
|
|
67
61
|
x={barThickness * bar.index + offset}
|
|
68
62
|
y={bar.y}
|
|
69
63
|
width={barThicknessAdjusted}
|
|
70
64
|
height={bar.height}
|
|
71
|
-
style={{ background: bar.color, border: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`, ...style }}
|
|
72
|
-
opacity={transparentBar ? 0.5 : 1}
|
|
73
65
|
display={displayBar ? 'block' : 'none'}
|
|
74
|
-
data-tooltip-html={
|
|
66
|
+
data-tooltip-html={tooltip}
|
|
75
67
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
76
68
|
onClick={e => {
|
|
77
69
|
e.preventDefault()
|
|
@@ -80,7 +72,11 @@ const BarChartStackedVertical = props => {
|
|
|
80
72
|
setSharedFilter(config.uid, bar)
|
|
81
73
|
}
|
|
82
74
|
}}
|
|
83
|
-
|
|
75
|
+
>
|
|
76
|
+
<div
|
|
77
|
+
style={{ transition: 'all 0.2s linear', opacity: transparentBar ? 0.2 : 1, width: barThicknessAdjusted, height: bar.height, background: colorScale(config.runtime.seriesLabels[bar.key]), border: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`, ...style }}
|
|
78
|
+
></div>
|
|
79
|
+
</foreignObject>
|
|
84
80
|
</Group>
|
|
85
81
|
</Group>
|
|
86
82
|
)
|