@cdc/chart 4.25.3 → 4.25.6
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 +46641 -42561
- package/index.html +130 -129
- package/package.json +22 -27
- package/src/CdcChartComponent.tsx +75 -35
- package/src/_stories/Chart.CI.stories.tsx +10 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +68 -49
- package/src/_stories/Chart.stories.tsx +99 -86
- package/src/_stories/ChartPrefixSuffix.stories.tsx +29 -32
- package/{examples/private/line-issue.json → src/_stories/_mock/barchart_labels.mock.json} +150 -35
- package/src/_stories/_mock/dynamic_series_bar_config.json +1 -1
- package/src/_stories/_mock/dynamic_series_suppression_mock.json +610 -0
- package/{examples/private/not-loading.json → src/_stories/_mock/pie_calculated_area.json} +152 -95
- package/src/components/Annotations/components/AnnotationDropdown.tsx +2 -2
- package/src/components/AreaChart/components/AreaChart.jsx +33 -5
- package/src/components/Axis/Categorical.Axis.tsx +2 -2
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +38 -37
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +18 -8
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +8 -8
- package/src/components/BarChart/components/BarChart.Vertical.tsx +47 -36
- package/src/components/BarChart/components/{BarChart.jsx → BarChart.tsx} +23 -5
- package/src/components/BarChart/components/context.tsx +20 -2
- package/src/components/BarChart/helpers/getBarHeights.ts +47 -0
- package/src/components/BarChart/helpers/index.ts +5 -2
- package/src/components/BarChart/helpers/tests/getBarHeights.test.ts +83 -0
- package/src/{hooks → components/BarChart/helpers}/useBarChart.ts +9 -46
- package/src/components/BoxPlot/BoxPlot.tsx +2 -1
- package/src/components/Brush/BrushChart.tsx +73 -0
- package/src/components/Brush/BrushController..tsx +39 -0
- package/src/components/DeviationBar.jsx +2 -2
- package/src/components/EditorPanel/EditorPanel.tsx +232 -147
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +36 -36
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +52 -25
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +12 -4
- package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +8 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +5 -5
- package/src/components/ForestPlot/ForestPlot.tsx +2 -2
- package/src/components/HoverLine/HoverLine.tsx +74 -0
- package/src/components/Legend/Legend.Component.tsx +1 -1
- package/src/components/Legend/Legend.Suppression.tsx +59 -25
- package/src/components/Legend/helpers/createFormatLabels.tsx +28 -0
- package/src/components/Legend/helpers/index.ts +1 -1
- package/src/components/LineChart/LineChartProps.ts +3 -1
- package/src/components/LineChart/components/LineChart.Circle.tsx +72 -119
- package/src/components/LineChart/helpers.ts +133 -56
- package/src/components/LineChart/index.tsx +106 -55
- package/src/components/LinearChart.tsx +178 -198
- package/src/components/PairedBarChart.jsx +3 -2
- package/src/components/PieChart/PieChart.tsx +127 -102
- package/src/components/ScatterPlot/ScatterPlot.jsx +5 -0
- package/src/components/Sparkline/components/SparkLine.tsx +80 -18
- package/src/data/initial-state.js +11 -6
- package/src/helpers/countNumOfTicks.ts +1 -1
- package/src/helpers/dataHelpers.ts +23 -2
- package/src/helpers/getNewRuntime.ts +35 -0
- package/src/helpers/getPiePercent.ts +22 -0
- package/src/helpers/getTransformedData.ts +22 -0
- package/src/helpers/sizeHelpers.ts +1 -1
- package/src/helpers/tests/getNewRuntime.test.ts +82 -0
- package/src/helpers/tests/getPiePercent.test.ts +38 -0
- package/src/hooks/useMinMax.ts +21 -28
- package/src/hooks/useRightAxis.ts +5 -3
- package/src/hooks/useScales.ts +15 -6
- package/src/hooks/useTooltip.tsx +218 -203
- package/src/index.jsx +2 -2
- package/src/scss/main.scss +13 -6
- package/src/store/chart.actions.ts +2 -6
- package/src/store/chart.reducer.ts +23 -23
- package/src/types/ChartConfig.ts +11 -3
- package/src/types/ChartContext.ts +0 -2
- package/examples/private/DEV-8850-2.json +0 -493
- package/examples/private/DEV-9822.json +0 -574
- package/examples/private/DEV-9840.json +0 -553
- package/examples/private/DEV-9850-3.json +0 -461
- package/examples/private/chart.json +0 -1084
- package/examples/private/ci_formatted.json +0 -202
- package/examples/private/ci_issue.json +0 -3016
- package/examples/private/completed.json +0 -634
- package/examples/private/dem-data-long.csv +0 -20
- package/examples/private/dem-data-long.json +0 -36
- package/examples/private/demographic_data.csv +0 -157
- package/examples/private/demographic_data.json +0 -2654
- package/examples/private/demographic_dynamic.json +0 -443
- package/examples/private/demographic_standard.json +0 -560
- package/examples/private/ehdi.json +0 -29939
- package/examples/private/test.json +0 -493
- package/src/components/ZoomBrush.tsx +0 -251
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React, { useContext } from 'react'
|
|
2
2
|
import ConfigContext from '../../../ConfigContext'
|
|
3
|
-
import { useBarChart } from '../../../hooks/useBarChart'
|
|
4
3
|
import { BarStackHorizontal } from '@visx/shape'
|
|
5
4
|
import { Group } from '@visx/group'
|
|
6
5
|
import { Text } from '@visx/text'
|
|
7
6
|
import { getColorContrast, getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
7
|
+
import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
|
|
8
8
|
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
9
9
|
|
|
10
10
|
// types
|
|
@@ -12,9 +12,10 @@ import BarChartContext, { type BarChartContextValues } from './context'
|
|
|
12
12
|
import { type ChartContext } from '../../../types/ChartContext'
|
|
13
13
|
|
|
14
14
|
import createBarElement from '@cdc/core/components/createBarElement'
|
|
15
|
+
import { getHorizontalBarHeights } from '../helpers/getBarHeights'
|
|
15
16
|
|
|
16
17
|
const BarChartStackedHorizontal = () => {
|
|
17
|
-
const { yMax, yScale, xScale } = useContext<BarChartContextValues>(BarChartContext)
|
|
18
|
+
const { yMax, yScale, xScale, barChart } = useContext<BarChartContextValues>(BarChartContext)
|
|
18
19
|
|
|
19
20
|
// prettier-ignore
|
|
20
21
|
const {
|
|
@@ -29,8 +30,17 @@ const BarChartStackedHorizontal = () => {
|
|
|
29
30
|
transformedData: data
|
|
30
31
|
} = useContext<ChartContext>(ConfigContext)
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
const {
|
|
34
|
+
barBorderWidth,
|
|
35
|
+
displayNumbersOnBar,
|
|
36
|
+
getAdditionalColumn,
|
|
37
|
+
hoveredBar,
|
|
38
|
+
isHorizontal,
|
|
39
|
+
isLabelBelowBar,
|
|
40
|
+
onMouseLeaveBar,
|
|
41
|
+
onMouseOverBar,
|
|
42
|
+
barStackedSeriesKeys
|
|
43
|
+
} = barChart
|
|
34
44
|
|
|
35
45
|
const { orientation, visualizationSubType } = config
|
|
36
46
|
return (
|
|
@@ -49,7 +59,7 @@ const BarChartStackedHorizontal = () => {
|
|
|
49
59
|
>
|
|
50
60
|
{barStacks =>
|
|
51
61
|
barStacks.map(barStack =>
|
|
52
|
-
|
|
62
|
+
getHorizontalBarHeights(config, barStack.bars).map((bar, index) => {
|
|
53
63
|
const transparentBar =
|
|
54
64
|
config.legend.behavior === 'highlight' &&
|
|
55
65
|
seriesHighlight.length > 0 &&
|
|
@@ -60,8 +70,8 @@ const BarChartStackedHorizontal = () => {
|
|
|
60
70
|
seriesHighlight.indexOf(bar.key) !== -1
|
|
61
71
|
config.barHeight = Number(config.barHeight)
|
|
62
72
|
let barColor = colorScale(config.runtime.seriesLabels[bar.key])
|
|
63
|
-
let labelColor = getContrastColor(
|
|
64
|
-
let constrast = getColorContrast(
|
|
73
|
+
let labelColor = getContrastColor(APP_FONT_COLOR, barColor)
|
|
74
|
+
let constrast = getColorContrast(APP_FONT_COLOR, barColor)
|
|
65
75
|
const contrastLevel = 7
|
|
66
76
|
if (constrast < contrastLevel) {
|
|
67
77
|
labelColor = '#fff'
|
|
@@ -100,7 +110,7 @@ const BarChartStackedHorizontal = () => {
|
|
|
100
110
|
height: bar.height,
|
|
101
111
|
x: bar.x,
|
|
102
112
|
y: bar.y,
|
|
103
|
-
onMouseOver:
|
|
113
|
+
onMouseOver: e => onMouseOverBar(yAxisValue, bar.key, e, data),
|
|
104
114
|
onMouseLeave: onMouseLeaveBar,
|
|
105
115
|
tooltipHtml: tooltip,
|
|
106
116
|
tooltipId: `cdc-open-viz-tooltip-${config.runtime.uniqueId}`,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useContext, useState } from 'react'
|
|
2
2
|
import ConfigContext from '../../../ConfigContext'
|
|
3
|
-
import { useBarChart } from '../../../hooks/useBarChart'
|
|
4
3
|
import { BarStack } from '@visx/shape'
|
|
5
4
|
import { Group } from '@visx/group'
|
|
6
5
|
import { Text } from '@visx/text'
|
|
@@ -12,22 +11,23 @@ import createBarElement from '@cdc/core/components/createBarElement'
|
|
|
12
11
|
|
|
13
12
|
const BarChartStackedVertical = () => {
|
|
14
13
|
const [barWidth, setBarWidth] = useState(0)
|
|
15
|
-
const { xScale, yScale, seriesScale, xMax, yMax } = useContext(BarChartContext)
|
|
16
|
-
const { transformedData, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, setSharedFilter } =
|
|
17
|
-
useContext(ConfigContext)
|
|
14
|
+
const { xScale, yScale, seriesScale, xMax, yMax, barChart } = useContext(BarChartContext)
|
|
18
15
|
const {
|
|
19
16
|
isHorizontal,
|
|
20
17
|
barBorderWidth,
|
|
21
|
-
applyRadius,
|
|
22
18
|
hoveredBar,
|
|
23
19
|
getAdditionalColumn,
|
|
24
20
|
onMouseLeaveBar,
|
|
25
21
|
onMouseOverBar,
|
|
26
22
|
barStackedSeriesKeys
|
|
27
|
-
} =
|
|
23
|
+
} = barChart
|
|
24
|
+
const { transformedData, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, setSharedFilter } =
|
|
25
|
+
useContext(ConfigContext)
|
|
26
|
+
|
|
28
27
|
const { orientation } = config
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
let data = transformedData
|
|
30
|
+
|
|
31
31
|
const isDateAxisType = config.runtime.xAxis.type === 'date-time' || config.runtime.xAxis.type === 'date'
|
|
32
32
|
const isDateTimeScaleAxisType = config.runtime.xAxis.type === 'date-time'
|
|
33
33
|
|
|
@@ -98,7 +98,7 @@ const BarChartStackedVertical = () => {
|
|
|
98
98
|
height: bar.height,
|
|
99
99
|
x: barX,
|
|
100
100
|
y: bar.y,
|
|
101
|
-
onMouseOver:
|
|
101
|
+
onMouseOver: e => onMouseOverBar(xAxisValue, bar.key, e, data),
|
|
102
102
|
onMouseLeave: onMouseLeaveBar,
|
|
103
103
|
tooltipHtml: tooltip,
|
|
104
104
|
tooltipId: `cdc-open-viz-tooltip-${config.runtime.uniqueId}`,
|
|
@@ -3,7 +3,6 @@ import React, { useContext, useState } from 'react'
|
|
|
3
3
|
import ConfigContext from '../../../ConfigContext'
|
|
4
4
|
import BarChartContext, { type BarChartContextValues } from './context'
|
|
5
5
|
// Local hooks
|
|
6
|
-
import { useBarChart } from '../../../hooks/useBarChart'
|
|
7
6
|
import { useHighlightedBars } from '../../../hooks/useHighlightedBars'
|
|
8
7
|
import { getBarConfig, testZeroValue } from '../helpers'
|
|
9
8
|
// VisX library imports
|
|
@@ -16,6 +15,7 @@ import Regions from '../../Regions'
|
|
|
16
15
|
import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
17
16
|
import isNumber from '@cdc/core/helpers/isNumber'
|
|
18
17
|
import createBarElement from '@cdc/core/components/createBarElement'
|
|
18
|
+
import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
|
|
19
19
|
// Third party libraries
|
|
20
20
|
import chroma from 'chroma-js'
|
|
21
21
|
// Types
|
|
@@ -24,14 +24,9 @@ import _ from 'lodash'
|
|
|
24
24
|
import { getBarData } from '../helpers/getBarData'
|
|
25
25
|
|
|
26
26
|
export const BarChartVertical = () => {
|
|
27
|
-
const { xScale, yScale, xMax, yMax, seriesScale, convertLineToBarGraph } =
|
|
27
|
+
const { xScale, yScale, xMax, yMax, seriesScale, convertLineToBarGraph, barChart } =
|
|
28
28
|
useContext<BarChartContextValues>(BarChartContext)
|
|
29
|
-
|
|
30
|
-
const [barWidth, setBarWidth] = useState(0)
|
|
31
|
-
const [totalBarsInGroup, setTotalBarsInGroup] = useState(0)
|
|
32
|
-
// prettier-ignore
|
|
33
29
|
const {
|
|
34
|
-
// prettier-ignore
|
|
35
30
|
assignColorsToValues,
|
|
36
31
|
barBorderWidth,
|
|
37
32
|
getAdditionalColumn,
|
|
@@ -42,17 +37,28 @@ export const BarChartVertical = () => {
|
|
|
42
37
|
onMouseLeaveBar,
|
|
43
38
|
onMouseOverBar,
|
|
44
39
|
section
|
|
45
|
-
} =
|
|
40
|
+
} = barChart
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
const
|
|
42
|
+
const [barWidth, setBarWidth] = useState(0)
|
|
43
|
+
const [totalBarsInGroup, setTotalBarsInGroup] = useState(0)
|
|
44
|
+
|
|
45
|
+
const {
|
|
46
|
+
colorScale,
|
|
47
|
+
config,
|
|
48
|
+
dashboardConfig,
|
|
49
|
+
tableData,
|
|
50
|
+
formatDate,
|
|
51
|
+
formatNumber,
|
|
52
|
+
parseDate,
|
|
53
|
+
seriesHighlight,
|
|
54
|
+
setSharedFilter,
|
|
55
|
+
transformedData
|
|
56
|
+
} = useContext<ChartContext>(ConfigContext)
|
|
49
57
|
|
|
50
58
|
const { HighLightedBarUtils } = useHighlightedBars(config)
|
|
51
59
|
|
|
52
60
|
const root = document.documentElement
|
|
53
61
|
|
|
54
|
-
const coolGray90 = getComputedStyle(root).getPropertyValue('--cool-gray-90')
|
|
55
|
-
|
|
56
62
|
let data = transformedData
|
|
57
63
|
// check if user add suppression
|
|
58
64
|
const isSuppressionActive = config.preliminaryData.some(pd => pd.value && pd.type === 'suppression')
|
|
@@ -60,12 +66,12 @@ export const BarChartVertical = () => {
|
|
|
60
66
|
if (isSuppressionActive) {
|
|
61
67
|
data = tableData
|
|
62
68
|
}
|
|
63
|
-
// if brush active use brush data (filtered|excluded) not cleaned
|
|
64
|
-
if (brushConfig.data.length) {
|
|
65
|
-
data = brushConfig.data
|
|
66
|
-
}
|
|
67
69
|
|
|
68
|
-
const hasConfidenceInterval =
|
|
70
|
+
const hasConfidenceInterval =
|
|
71
|
+
config.confidenceKeys.upper &&
|
|
72
|
+
config.confidenceKeys.lower &&
|
|
73
|
+
config.confidenceKeys.upper !== '' &&
|
|
74
|
+
config.confidenceKeys.lower !== ''
|
|
69
75
|
|
|
70
76
|
const _data = getBarData(config, data, hasConfidenceInterval)
|
|
71
77
|
return (
|
|
@@ -144,7 +150,14 @@ export const BarChartVertical = () => {
|
|
|
144
150
|
<li class="tooltip-body ">${tooltipBody}</li>
|
|
145
151
|
${additionalColTooltip ? '<li class="tooltip-body ">' + additionalColTooltip + '</li>' : ''}
|
|
146
152
|
</li></ul>`
|
|
147
|
-
|
|
153
|
+
const { barHeight, isSuppressed, getBarY, absentDataLabel } = getBarConfig({
|
|
154
|
+
bar,
|
|
155
|
+
defaultBarHeight,
|
|
156
|
+
config,
|
|
157
|
+
barWidth,
|
|
158
|
+
isVertical: true,
|
|
159
|
+
yAxisValue
|
|
160
|
+
})
|
|
148
161
|
// configure colors
|
|
149
162
|
let labelColor = '#000000'
|
|
150
163
|
labelColor = HighLightedBarUtils.checkFontColor(yAxisValue, highlightedBarValues, labelColor) // Set if background is transparent'
|
|
@@ -153,6 +166,12 @@ export const BarChartVertical = () => {
|
|
|
153
166
|
const isHighlightedBar = highlightedBarValues?.includes(xAxisValue)
|
|
154
167
|
const highlightedBarColor = getHighlightedBarColorByValue(xAxisValue)
|
|
155
168
|
const highlightedBar = getHighlightedBarByValue(xAxisValue)
|
|
169
|
+
const hideGroup =
|
|
170
|
+
(!isNumber(bar.value) && !config.general.showMissingDataLabel) ||
|
|
171
|
+
(String(bar.value) === '0' && !config.general.showZeroValueData)
|
|
172
|
+
? 'none'
|
|
173
|
+
: 'block' // hide bar group if no value or zero value
|
|
174
|
+
|
|
156
175
|
const borderColor = isHighlightedBar
|
|
157
176
|
? highlightedBarColor
|
|
158
177
|
: config.barHasBorder === 'true'
|
|
@@ -162,20 +181,11 @@ export const BarChartVertical = () => {
|
|
|
162
181
|
? highlightedBar.borderWidth
|
|
163
182
|
: config.isLollipopChart
|
|
164
183
|
? 0
|
|
165
|
-
: config.barHasBorder === 'true'
|
|
184
|
+
: config.barHasBorder === 'true' && !absentDataLabel && !isSuppressed
|
|
166
185
|
? barBorderWidth
|
|
167
186
|
: 0
|
|
168
187
|
|
|
169
|
-
const
|
|
170
|
-
bar,
|
|
171
|
-
defaultBarHeight,
|
|
172
|
-
config,
|
|
173
|
-
barWidth,
|
|
174
|
-
isVertical: true
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
const absentDataLabel = getAbsentDataLabel(yAxisValue)
|
|
178
|
-
const barDefaultLabel = isSuppressed || !config.labels ? '' : yAxisValue
|
|
188
|
+
const barDefaultLabel = isSuppressed || absentDataLabel || !config.labels ? '' : yAxisValue
|
|
179
189
|
const barY = getBarY(defaultBarY, yScale(scaleVal))
|
|
180
190
|
const displaylollipopShape = testZeroValue(bar.value) ? 'none' : 'block'
|
|
181
191
|
const getBarBackgroundColor = (barColor: string, filteredOutColor?: string): string => {
|
|
@@ -242,8 +252,10 @@ export const BarChartVertical = () => {
|
|
|
242
252
|
)
|
|
243
253
|
// End Confidence Interval Variables
|
|
244
254
|
|
|
255
|
+
const BAR_LABEL_PADDING = 10
|
|
256
|
+
|
|
245
257
|
return (
|
|
246
|
-
<Group key={`${barGroup.index}--${index}`}>
|
|
258
|
+
<Group display={hideGroup} key={`${barGroup.index}--${index}`}>
|
|
247
259
|
<Group key={`bar-sub-group-${barGroup.index}-${barGroup.x0}-${barY}--${index}`}>
|
|
248
260
|
{createBarElement({
|
|
249
261
|
config: config,
|
|
@@ -257,7 +269,7 @@ export const BarChartVertical = () => {
|
|
|
257
269
|
height: barHeight,
|
|
258
270
|
x: barX,
|
|
259
271
|
y: barY,
|
|
260
|
-
onMouseOver:
|
|
272
|
+
onMouseOver: e => onMouseOverBar(xAxisValue, bar.key, e, data),
|
|
261
273
|
onMouseLeave: onMouseLeaveBar,
|
|
262
274
|
tooltipHtml: tooltip,
|
|
263
275
|
tooltipId: `cdc-open-viz-tooltip-${config.runtime.uniqueId}`,
|
|
@@ -275,6 +287,7 @@ export const BarChartVertical = () => {
|
|
|
275
287
|
cursor: dashboardConfig ? 'pointer' : 'default'
|
|
276
288
|
}
|
|
277
289
|
})}
|
|
290
|
+
|
|
278
291
|
{config.preliminaryData.map((pd, index) => {
|
|
279
292
|
// check if user selected column
|
|
280
293
|
const selectedSuppressionColumn = !pd.column || pd.column === bar.key
|
|
@@ -318,12 +331,11 @@ export const BarChartVertical = () => {
|
|
|
318
331
|
</Text>
|
|
319
332
|
)
|
|
320
333
|
})}
|
|
321
|
-
|
|
322
334
|
<Text // prettier-ignore
|
|
323
335
|
display={displayBar ? 'block' : 'none'}
|
|
324
336
|
opacity={transparentBar ? 0.5 : 1}
|
|
325
337
|
x={hasConfidenceInterval ? barX + barWidth : barX + barWidth / 2}
|
|
326
|
-
y={barY -
|
|
338
|
+
y={barY - BAR_LABEL_PADDING}
|
|
327
339
|
fill={labelColor}
|
|
328
340
|
textAnchor='middle'
|
|
329
341
|
>
|
|
@@ -333,14 +345,13 @@ export const BarChartVertical = () => {
|
|
|
333
345
|
display={displayBar ? 'block' : 'none'}
|
|
334
346
|
opacity={transparentBar ? 0.5 : 1}
|
|
335
347
|
x={barX + barWidth / 2}
|
|
336
|
-
y={barY -
|
|
348
|
+
y={barY - BAR_LABEL_PADDING}
|
|
337
349
|
fill={labelColor}
|
|
338
350
|
textAnchor='middle'
|
|
339
351
|
fontSize={config.isLollipopChart ? null : barWidth / 2}
|
|
340
352
|
>
|
|
341
353
|
{absentDataLabel}
|
|
342
354
|
</Text>
|
|
343
|
-
|
|
344
355
|
{config.isLollipopChart && config.lollipopShape === 'circle' && (
|
|
345
356
|
<circle
|
|
346
357
|
display={displaylollipopShape}
|
|
@@ -373,7 +384,7 @@ export const BarChartVertical = () => {
|
|
|
373
384
|
{hasConfidenceInterval && bar.value !== undefined && datum && (
|
|
374
385
|
<path
|
|
375
386
|
key={`confidence-interval-v-${datum[config.runtime.originalXAxis.dataKey]}`}
|
|
376
|
-
stroke={
|
|
387
|
+
stroke={APP_FONT_COLOR}
|
|
377
388
|
strokeWidth='px'
|
|
378
389
|
d={`M${xPos - tickWidth} ${upperPos}
|
|
379
390
|
L${xPos + tickWidth} ${upperPos}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useContext } from 'react'
|
|
1
|
+
import React, { MouseEventHandler, useContext, useState } from 'react'
|
|
2
2
|
|
|
3
3
|
// visx
|
|
4
4
|
import { Group } from '@visx/group'
|
|
@@ -9,8 +9,21 @@ import BarChartType from './BarChartType'
|
|
|
9
9
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
10
10
|
import ConfigContext from '../../../ConfigContext'
|
|
11
11
|
import BarChartContext from './context'
|
|
12
|
+
import { useBarChart } from '../helpers/useBarChart'
|
|
13
|
+
import { PositionScale } from '@visx/shape/lib/types'
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
type BarChartProps = {
|
|
16
|
+
xScale: PositionScale
|
|
17
|
+
yScale: PositionScale
|
|
18
|
+
seriesScale: PositionScale
|
|
19
|
+
xMax: number
|
|
20
|
+
yMax: number
|
|
21
|
+
handleTooltipMouseOver: MouseEventHandler<SVGRectElement>
|
|
22
|
+
handleTooltipMouseOff: MouseEventHandler<SVGRectElement>
|
|
23
|
+
handleTooltipClick: MouseEventHandler<SVGRectElement>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const BarChart: React.FC<BarChartProps> = ({
|
|
14
27
|
xScale,
|
|
15
28
|
yScale,
|
|
16
29
|
seriesScale,
|
|
@@ -20,7 +33,9 @@ const BarChart = ({
|
|
|
20
33
|
handleTooltipMouseOff,
|
|
21
34
|
handleTooltipClick
|
|
22
35
|
}) => {
|
|
23
|
-
const
|
|
36
|
+
const configContext = useContext(ConfigContext)
|
|
37
|
+
const { transformedData: data, config, convertLineToBarGraph } = configContext
|
|
38
|
+
const barChart = useBarChart(handleTooltipMouseOver, handleTooltipMouseOff, configContext)
|
|
24
39
|
|
|
25
40
|
const contextValues = {
|
|
26
41
|
xScale,
|
|
@@ -28,7 +43,8 @@ const BarChart = ({
|
|
|
28
43
|
xMax,
|
|
29
44
|
yMax,
|
|
30
45
|
seriesScale,
|
|
31
|
-
convertLineToBarGraph
|
|
46
|
+
convertLineToBarGraph,
|
|
47
|
+
barChart
|
|
32
48
|
}
|
|
33
49
|
|
|
34
50
|
return (
|
|
@@ -46,7 +62,9 @@ const BarChart = ({
|
|
|
46
62
|
height={Number(yMax)}
|
|
47
63
|
fill={'transparent'}
|
|
48
64
|
fillOpacity={0.05}
|
|
49
|
-
onMouseMove={e =>
|
|
65
|
+
onMouseMove={e => {
|
|
66
|
+
handleTooltipMouseOver(e, data)
|
|
67
|
+
}}
|
|
50
68
|
onMouseOut={handleTooltipMouseOff}
|
|
51
69
|
onClick={e => handleTooltipClick(e, data)}
|
|
52
70
|
/>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PositionScale } from '@visx/shape/lib/types'
|
|
1
2
|
import { createContext } from 'react'
|
|
2
3
|
|
|
3
4
|
const BarChartContext = createContext(null)
|
|
@@ -5,10 +6,27 @@ const BarChartContext = createContext(null)
|
|
|
5
6
|
export type BarChartContextValues = {
|
|
6
7
|
xMax: number
|
|
7
8
|
yMax: number
|
|
8
|
-
xScale:
|
|
9
|
-
yScale:
|
|
9
|
+
xScale: PositionScale
|
|
10
|
+
yScale: PositionScale
|
|
10
11
|
seriesScale: Function
|
|
11
12
|
convertLineToBarGraph: boolean
|
|
13
|
+
barChart: {
|
|
14
|
+
assignColorsToValues: Function
|
|
15
|
+
barBorderWidth: number
|
|
16
|
+
getAdditionalColumn: Function
|
|
17
|
+
getHighlightedBarByValue: Function
|
|
18
|
+
getHighlightedBarColorByValue: Function
|
|
19
|
+
lollipopBarWidth: number
|
|
20
|
+
lollipopShapeSize: number
|
|
21
|
+
onMouseLeaveBar: Function
|
|
22
|
+
onMouseOverBar: Function
|
|
23
|
+
section: string
|
|
24
|
+
hoveredBar: string
|
|
25
|
+
isHorizontal: boolean
|
|
26
|
+
isLabelBelowBar: boolean
|
|
27
|
+
displayNumbersOnBar: boolean
|
|
28
|
+
barStackedSeriesKeys: string[]
|
|
29
|
+
}
|
|
12
30
|
}
|
|
13
31
|
|
|
14
32
|
export default BarChartContext
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { APP_FONT_SIZE } from '@cdc/core/helpers/constants'
|
|
2
|
+
import _ from 'lodash'
|
|
3
|
+
import { ChartConfig } from '../../../types/ChartConfig'
|
|
4
|
+
|
|
5
|
+
export const getHorizontalBarHeights = <T>(config: Partial<ChartConfig>, bars: (T & { index })[]) => {
|
|
6
|
+
const isHorizontal = config.orientation === 'horizontal'
|
|
7
|
+
|
|
8
|
+
const isStacked = config.visualizationSubType === 'stacked'
|
|
9
|
+
|
|
10
|
+
let barHeight
|
|
11
|
+
if (!isStacked) {
|
|
12
|
+
const stackCount = config.runtime.seriesKeys.length
|
|
13
|
+
if (config.isLollipopChart) {
|
|
14
|
+
const lollipopSizes = {
|
|
15
|
+
large: 7,
|
|
16
|
+
medium: 6,
|
|
17
|
+
small: 5
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const lollipopBarWidth = lollipopSizes[config.lollipopSize] || 5
|
|
21
|
+
barHeight = lollipopBarWidth * stackCount
|
|
22
|
+
} else {
|
|
23
|
+
barHeight = config.barHeight * stackCount
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
barHeight = config.barHeight
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const labelHeight = APP_FONT_SIZE * 1.2
|
|
30
|
+
const labelSpace = config.yAxis.labelPlacement === 'Below Bar' ? labelHeight : 0
|
|
31
|
+
|
|
32
|
+
const totalBarHeight = barHeight + labelSpace + Number(config.barSpace)
|
|
33
|
+
|
|
34
|
+
if (isHorizontal) {
|
|
35
|
+
// calculate height of container based height, space and fontSize of labels
|
|
36
|
+
const totalHeight = bars.length * totalBarHeight
|
|
37
|
+
if (!config.heights) config.heights = {}
|
|
38
|
+
config.heights.horizontal = totalHeight
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// return new updated bars/groupes
|
|
42
|
+
return bars.map((bar, i) => {
|
|
43
|
+
// set bars Y dynamically to handle space between bars
|
|
44
|
+
const y = bar.index === 0 ? 0 : _.round(totalBarHeight * i)
|
|
45
|
+
return { ...bar, y, height: barHeight }
|
|
46
|
+
})
|
|
47
|
+
}
|
|
@@ -9,6 +9,7 @@ interface BarConfigProps {
|
|
|
9
9
|
config: { [key: string]: any }
|
|
10
10
|
barWidth: number
|
|
11
11
|
isVertical: boolean
|
|
12
|
+
yAxisValue: number
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
// Function to create bar width based on suppression status and missing data label
|
|
@@ -18,7 +19,8 @@ export const getBarConfig = ({
|
|
|
18
19
|
defaultBarWidth,
|
|
19
20
|
config,
|
|
20
21
|
barWidth,
|
|
21
|
-
isVertical
|
|
22
|
+
isVertical,
|
|
23
|
+
yAxisValue
|
|
22
24
|
}: BarConfigProps) => {
|
|
23
25
|
const heightMini = 3 /// height of small bars aka suppressed/NA/Zero valued
|
|
24
26
|
let barHeight = defaultBarHeight
|
|
@@ -97,7 +99,8 @@ export const getBarConfig = ({
|
|
|
97
99
|
return labelFits && isVertical ? label : !isVertical ? label : ''
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
|
-
|
|
102
|
+
const absentDataLabel = getAbsentDataLabel(yAxisValue)
|
|
103
|
+
return { barWidthHorizontal, barHeight, isSuppressed, showMissingDataLabel, getBarY, absentDataLabel }
|
|
101
104
|
}
|
|
102
105
|
|
|
103
106
|
export const testZeroValue = value => {
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { getHorizontalBarHeights } from '../getBarHeights'
|
|
2
|
+
|
|
3
|
+
describe('getHorizontalBarHeights', () => {
|
|
4
|
+
it('should calculate bar heights for non-stacked horizontal bars', () => {
|
|
5
|
+
const config = {
|
|
6
|
+
orientation: 'horizontal',
|
|
7
|
+
visualizationSubType: 'grouped',
|
|
8
|
+
runtime: { seriesKeys: ['A', 'B'] },
|
|
9
|
+
barHeight: 10,
|
|
10
|
+
barSpace: 5,
|
|
11
|
+
yAxis: { labelPlacement: 'Above Bar' }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const bars = [{ index: 0 }, { index: 1 }]
|
|
15
|
+
|
|
16
|
+
const result = getHorizontalBarHeights(config, bars)
|
|
17
|
+
|
|
18
|
+
expect(result).toEqual([
|
|
19
|
+
{ index: 0, y: 0, height: 20 },
|
|
20
|
+
{ index: 1, y: 25, height: 20 }
|
|
21
|
+
])
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('should calculate bar heights for stacked horizontal bars', () => {
|
|
25
|
+
const config = {
|
|
26
|
+
orientation: 'horizontal',
|
|
27
|
+
visualizationSubType: 'stacked',
|
|
28
|
+
barHeight: 15,
|
|
29
|
+
barSpace: 5,
|
|
30
|
+
yAxis: { labelPlacement: 'Above Bar' }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const bars = [{ index: 0 }, { index: 1 }]
|
|
34
|
+
|
|
35
|
+
const result = getHorizontalBarHeights(config, bars)
|
|
36
|
+
|
|
37
|
+
expect(result).toEqual([
|
|
38
|
+
{ index: 0, y: 0, height: 15 },
|
|
39
|
+
{ index: 1, y: 20, height: 15 }
|
|
40
|
+
])
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('should include label space when labelPlacement is Below Bar', () => {
|
|
44
|
+
const config = {
|
|
45
|
+
orientation: 'horizontal',
|
|
46
|
+
visualizationSubType: 'grouped',
|
|
47
|
+
runtime: { seriesKeys: ['A'] },
|
|
48
|
+
barHeight: 10,
|
|
49
|
+
barSpace: 5,
|
|
50
|
+
yAxis: { labelPlacement: 'Below Bar' }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const bars = [{ index: 0 }, { index: 1 }]
|
|
54
|
+
|
|
55
|
+
const result = getHorizontalBarHeights(config, bars)
|
|
56
|
+
|
|
57
|
+
expect(result).toEqual([
|
|
58
|
+
{ index: 0, y: 0, height: 10 },
|
|
59
|
+
{ index: 1, y: 37, height: 10 }
|
|
60
|
+
])
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('should handle lollipop chart bar heights', () => {
|
|
64
|
+
const config = {
|
|
65
|
+
orientation: 'horizontal',
|
|
66
|
+
visualizationSubType: 'grouped',
|
|
67
|
+
runtime: { seriesKeys: ['A', 'B', 'C'] },
|
|
68
|
+
isLollipopChart: true,
|
|
69
|
+
lollipopSize: 'medium',
|
|
70
|
+
barSpace: 5,
|
|
71
|
+
yAxis: { labelPlacement: 'Above Bar' }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const bars = [{ index: 0 }, { index: 1 }]
|
|
75
|
+
|
|
76
|
+
const result = getHorizontalBarHeights(config, bars)
|
|
77
|
+
|
|
78
|
+
expect(result).toEqual([
|
|
79
|
+
{ index: 0, y: 0, height: 18 },
|
|
80
|
+
{ index: 1, y: 23, height: 18 }
|
|
81
|
+
])
|
|
82
|
+
})
|
|
83
|
+
})
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React, { useContext, useEffect, useState } from 'react'
|
|
2
|
-
import
|
|
2
|
+
import { ChartDispatchContext } from '../../../ConfigContext'
|
|
3
3
|
import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const dispatch = useContext(ChartDispatchContext)
|
|
4
|
+
import { APP_FONT_SIZE } from '@cdc/core/helpers/constants'
|
|
5
|
+
|
|
6
|
+
export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, configContext) => {
|
|
7
|
+
const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate, seriesHighlight } = configContext
|
|
9
8
|
const { orientation } = config
|
|
9
|
+
const dispatch = useContext(ChartDispatchContext)
|
|
10
10
|
const [hoveredBar, setHoveredBar] = useState(null)
|
|
11
11
|
|
|
12
12
|
const isHorizontal = orientation === 'horizontal'
|
|
@@ -123,43 +123,6 @@ export const useBarChart = () => {
|
|
|
123
123
|
const barColor = palette[barIndex]
|
|
124
124
|
return barColor
|
|
125
125
|
}
|
|
126
|
-
const updateBars = defaultBars => {
|
|
127
|
-
// function updates stacked && regular && lollipop horizontal bars
|
|
128
|
-
if (config.visualizationType !== 'Bar' && !isHorizontal) return defaultBars
|
|
129
|
-
|
|
130
|
-
const barsArr = [...defaultBars]
|
|
131
|
-
let barHeight
|
|
132
|
-
|
|
133
|
-
const heights = {
|
|
134
|
-
stacked: config.barHeight,
|
|
135
|
-
lollipop: lollipopBarWidth
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (!isStacked) {
|
|
139
|
-
barHeight = heights[config.isLollipopChart ? 'lollipop' : 'stacked'] * stackCount
|
|
140
|
-
} else {
|
|
141
|
-
barHeight = heights.stacked
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const labelHeight = isLabelBelowBar ? appFontSize * 1.2 : 0
|
|
145
|
-
let barSpace = Number(config.barSpace)
|
|
146
|
-
|
|
147
|
-
// calculate height of container based height, space and fontSize of labels
|
|
148
|
-
let totalHeight = barsArr.length * (barHeight + labelHeight + barSpace)
|
|
149
|
-
|
|
150
|
-
if (isHorizontal) {
|
|
151
|
-
config.heights.horizontal = totalHeight
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// return new updated bars/groupes
|
|
155
|
-
return barsArr.map((bar, i) => {
|
|
156
|
-
// set bars Y dynamically to handle space between bars
|
|
157
|
-
let y = 0
|
|
158
|
-
bar.index !== 0 && (y = (barHeight + barSpace + labelHeight) * i)
|
|
159
|
-
|
|
160
|
-
return { ...bar, y: y, height: barHeight }
|
|
161
|
-
})
|
|
162
|
-
}
|
|
163
126
|
|
|
164
127
|
const getHighlightedBarColorByValue = value => {
|
|
165
128
|
const match = config?.highlightedBarValues.find(item => {
|
|
@@ -226,17 +189,18 @@ export const useBarChart = () => {
|
|
|
226
189
|
return additionalTooltipItems
|
|
227
190
|
}
|
|
228
191
|
|
|
229
|
-
const onMouseOverBar = (categoryValue, barKey) => {
|
|
192
|
+
const onMouseOverBar = (categoryValue, barKey, event, data) => {
|
|
230
193
|
if (config.legend.highlightOnHover && config.legend.behavior === 'highlight' && barKey) {
|
|
231
194
|
dispatch({ type: 'SET_SERIES_HIGHLIGHT', payload: [barKey] })
|
|
232
195
|
}
|
|
233
|
-
|
|
196
|
+
handleTooltipMouseOver(event, data)
|
|
234
197
|
setHoveredBar(categoryValue)
|
|
235
198
|
}
|
|
236
199
|
const onMouseLeaveBar = () => {
|
|
237
200
|
if (config.legend.highlightOnHover && config.legend.behavior === 'highlight') {
|
|
238
201
|
dispatch({ type: 'SET_SERIES_HIGHLIGHT', payload: [] })
|
|
239
202
|
}
|
|
203
|
+
handleTooltipMouseOff()
|
|
240
204
|
}
|
|
241
205
|
|
|
242
206
|
return {
|
|
@@ -256,7 +220,6 @@ export const useBarChart = () => {
|
|
|
256
220
|
barStackedSeriesKeys,
|
|
257
221
|
hasMultipleSeries,
|
|
258
222
|
applyRadius,
|
|
259
|
-
updateBars,
|
|
260
223
|
assignColorsToValues,
|
|
261
224
|
getHighlightedBarColorByValue,
|
|
262
225
|
getHighlightedBarByValue,
|