@cdc/chart 4.24.9-1 → 4.24.10
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 +37673 -36530
- package/index.html +1 -1
- package/package.json +2 -2
- package/src/CdcChart.tsx +128 -106
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +33 -0
- package/src/_stories/Chart.stories.tsx +28 -0
- package/src/_stories/ChartAxisLabels.stories.tsx +20 -0
- package/src/_stories/ChartAxisTitles.stories.tsx +53 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +151 -0
- package/src/_stories/_mock/horizontal_bar.json +257 -0
- package/src/_stories/_mock/large_x_axis_labels.json +261 -0
- package/src/_stories/_mock/paired-bar.json +262 -0
- package/src/_stories/_mock/pie_with_data.json +255 -0
- package/src/_stories/_mock/simplified_line.json +1510 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +0 -3
- package/src/components/Annotations/components/AnnotationDropdown.tsx +1 -1
- package/src/components/Axis/Categorical.Axis.tsx +22 -4
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +95 -16
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +41 -17
- package/src/components/BarChart/components/BarChart.Vertical.tsx +78 -20
- package/src/components/BarChart/helpers/index.ts +23 -4
- package/src/components/BrushChart.tsx +3 -2
- package/src/components/DeviationBar.jsx +58 -8
- package/src/components/EditorPanel/EditorPanel.tsx +62 -39
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +6 -23
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +21 -4
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +297 -35
- package/src/components/EditorPanel/components/panels.scss +4 -6
- package/src/components/EditorPanel/editor-panel.scss +0 -8
- package/src/components/EditorPanel/helpers/tests/updateFieldRankByValue.test.ts +38 -0
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +42 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +1 -0
- package/src/components/ForestPlot/ForestPlot.tsx +2 -3
- package/src/components/ForestPlot/ForestPlotProps.ts +2 -0
- package/src/components/Legend/Legend.Component.tsx +16 -16
- package/src/components/Legend/Legend.Suppression.tsx +25 -20
- package/src/components/Legend/Legend.tsx +0 -2
- package/src/components/Legend/helpers/index.ts +16 -19
- package/src/components/LegendWrapper.tsx +3 -1
- package/src/components/LinearChart.tsx +740 -562
- package/src/components/PairedBarChart.jsx +50 -10
- package/src/components/PieChart/PieChart.tsx +1 -6
- package/src/components/Regions/components/Regions.tsx +33 -19
- package/src/components/ZoomBrush.tsx +25 -6
- package/src/coreStyles_chart.scss +3 -0
- package/src/data/initial-state.js +6 -2
- package/src/helpers/configHelpers.ts +28 -0
- package/src/helpers/handleRankByValue.ts +15 -0
- package/src/helpers/sizeHelpers.ts +25 -0
- package/src/helpers/tests/handleRankByValue.test.ts +37 -0
- package/src/helpers/tests/sizeHelpers.test.ts +80 -0
- package/src/hooks/useColorPalette.js +10 -2
- package/src/hooks/useLegendClasses.ts +4 -0
- package/src/hooks/useScales.ts +31 -3
- package/src/hooks/useTooltip.tsx +9 -5
- package/src/index.jsx +1 -0
- package/src/scss/DataTable.scss +5 -4
- package/src/scss/main.scss +57 -52
- package/src/types/ChartConfig.ts +38 -16
- package/src/types/ChartContext.ts +18 -14
- package/src/_stories/Chart.Legend.Gradient.tsx +0 -19
- package/src/_stories/ChartBrush.stories.tsx +0 -19
|
@@ -32,11 +32,8 @@ const Annotations = ({ xScale, yScale, xScaleAnnotation, xMax, svgRef, onDragSta
|
|
|
32
32
|
// prettier-ignore
|
|
33
33
|
const {
|
|
34
34
|
config,
|
|
35
|
-
currentViewport,
|
|
36
35
|
dimensions,
|
|
37
|
-
isDraggingAnnotation,
|
|
38
36
|
isEditor,
|
|
39
|
-
isLegendBottom,
|
|
40
37
|
updateConfig
|
|
41
38
|
} = useContext(ConfigContext)
|
|
42
39
|
|
|
@@ -30,7 +30,7 @@ const AnnotationDropdown = () => {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const handleSectionClasses = () => {
|
|
33
|
-
const classes = [`data-table-container`, viewport, `d-block`, `d-lg-none`]
|
|
33
|
+
const classes = [`data-table-container`, viewport, `d-block`, `d-lg-none`, `w-100`]
|
|
34
34
|
|
|
35
35
|
if (config.general.showAnnotationDropdown) {
|
|
36
36
|
classes.push('d-lg-block')
|
|
@@ -7,9 +7,10 @@ import ConfigContext from '../../ConfigContext'
|
|
|
7
7
|
import chroma from 'chroma-js'
|
|
8
8
|
import createBarElement from '@cdc/core/components/createBarElement'
|
|
9
9
|
import { useBarChart } from '../../hooks/useBarChart'
|
|
10
|
+
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
10
11
|
|
|
11
12
|
const CategoricalYAxis = ({ yMax, leftSize, max, xMax }) => {
|
|
12
|
-
const { config
|
|
13
|
+
const { config } = useContext(ConfigContext)
|
|
13
14
|
const { fontSize } = useBarChart()
|
|
14
15
|
|
|
15
16
|
const { orientation } = config
|
|
@@ -82,7 +83,14 @@ const CategoricalYAxis = ({ yMax, leftSize, max, xMax }) => {
|
|
|
82
83
|
const keys = Object.keys(transformedData[0])
|
|
83
84
|
return (
|
|
84
85
|
<Group left={leftSize - xScale.bandwidth()} top={0}>
|
|
85
|
-
<BarStack
|
|
86
|
+
<BarStack
|
|
87
|
+
data={transformedData}
|
|
88
|
+
keys={keys}
|
|
89
|
+
x={() => xScale(xScaleValue)}
|
|
90
|
+
xScale={xScale}
|
|
91
|
+
yScale={yScale}
|
|
92
|
+
color={colorScale}
|
|
93
|
+
>
|
|
86
94
|
{barStacks =>
|
|
87
95
|
barStacks.map(barStack =>
|
|
88
96
|
barStack.bars.map(bar => {
|
|
@@ -96,7 +104,11 @@ const CategoricalYAxis = ({ yMax, leftSize, max, xMax }) => {
|
|
|
96
104
|
</li></ul>`
|
|
97
105
|
return (
|
|
98
106
|
<Group key={`${barStack.index}--${bar.index}--${orientation}`}>
|
|
99
|
-
<Group
|
|
107
|
+
<Group
|
|
108
|
+
key={`bar-stack-${barStack.index}-${bar.index}`}
|
|
109
|
+
id={`barStack${barStack.index}-${bar.index}`}
|
|
110
|
+
className='stack vertical'
|
|
111
|
+
>
|
|
100
112
|
{createBarElement({
|
|
101
113
|
type: 'axisBar',
|
|
102
114
|
config: config,
|
|
@@ -126,7 +138,13 @@ const CategoricalYAxis = ({ yMax, leftSize, max, xMax }) => {
|
|
|
126
138
|
{bar.key}
|
|
127
139
|
</Text>
|
|
128
140
|
{/* gridLines */}
|
|
129
|
-
{config.runtime.yAxis.gridLines &&
|
|
141
|
+
{config.runtime.yAxis.gridLines && (
|
|
142
|
+
<Line
|
|
143
|
+
from={{ x: bar.x + xScale.bandwidth(), y: bar.y }}
|
|
144
|
+
to={{ x: xMax + xScale.bandwidth(), y: bar.y }}
|
|
145
|
+
stroke='#d6d6d6'
|
|
146
|
+
/>
|
|
147
|
+
)}
|
|
130
148
|
{/* White background spacing between stackes */}
|
|
131
149
|
{!isLastIndex && <rect x={bar.x} y={bar.y} width={bar.width} height={1} fill={'#fff'}></rect>}
|
|
132
150
|
{/* Right side Axis line */}
|
|
@@ -14,6 +14,7 @@ import { BarGroup } from '@visx/shape'
|
|
|
14
14
|
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
15
15
|
import createBarElement from '@cdc/core/components/createBarElement'
|
|
16
16
|
import { getBarConfig, testZeroValue } from '../helpers'
|
|
17
|
+
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
17
18
|
|
|
18
19
|
// Third party libraries
|
|
19
20
|
import chroma from 'chroma-js'
|
|
@@ -24,9 +25,38 @@ import { ChartContext } from '../../../types/ChartContext'
|
|
|
24
25
|
|
|
25
26
|
export const BarChartHorizontal = () => {
|
|
26
27
|
const { xScale, yScale, yMax, seriesScale } = useContext<BarChartContextValues>(BarChartContext)
|
|
27
|
-
const {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const {
|
|
29
|
+
transformedData: data,
|
|
30
|
+
tableData,
|
|
31
|
+
colorScale,
|
|
32
|
+
seriesHighlight,
|
|
33
|
+
config,
|
|
34
|
+
formatNumber,
|
|
35
|
+
formatDate,
|
|
36
|
+
parseDate,
|
|
37
|
+
setSharedFilter,
|
|
38
|
+
isNumber,
|
|
39
|
+
getYAxisData,
|
|
40
|
+
getXAxisData
|
|
41
|
+
} = useContext<ChartContext>(ConfigContext)
|
|
42
|
+
const {
|
|
43
|
+
isHorizontal,
|
|
44
|
+
barBorderWidth,
|
|
45
|
+
updateBars,
|
|
46
|
+
assignColorsToValues,
|
|
47
|
+
section,
|
|
48
|
+
fontSize,
|
|
49
|
+
isLabelBelowBar,
|
|
50
|
+
displayNumbersOnBar,
|
|
51
|
+
lollipopBarWidth,
|
|
52
|
+
lollipopShapeSize,
|
|
53
|
+
getHighlightedBarColorByValue,
|
|
54
|
+
getHighlightedBarByValue,
|
|
55
|
+
getAdditionalColumn,
|
|
56
|
+
hoveredBar,
|
|
57
|
+
onMouseLeaveBar,
|
|
58
|
+
onMouseOverBar
|
|
59
|
+
} = useBarChart()
|
|
30
60
|
|
|
31
61
|
const { HighLightedBarUtils } = useHighlightedBars(config)
|
|
32
62
|
|
|
@@ -49,13 +79,29 @@ export const BarChartHorizontal = () => {
|
|
|
49
79
|
>
|
|
50
80
|
{barGroups => {
|
|
51
81
|
return updateBars(barGroups).map((barGroup, index) => (
|
|
52
|
-
<Group
|
|
82
|
+
<Group
|
|
83
|
+
className={`bar-group-${barGroup.index}-${barGroup.x0}--${index} ${config.orientation}`}
|
|
84
|
+
key={`bar-group-${barGroup.index}-${barGroup.x0}--${index}`}
|
|
85
|
+
id={`bar-group-${barGroup.index}-${barGroup.x0}--${index}`}
|
|
86
|
+
top={barGroup.y}
|
|
87
|
+
>
|
|
53
88
|
{barGroup.bars.map((bar, index) => {
|
|
54
89
|
const scaleVal = config.yAxis.type === 'logarithmic' ? 0.1 : 0
|
|
55
|
-
let highlightedBarValues = config.highlightedBarValues
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
90
|
+
let highlightedBarValues = config.highlightedBarValues
|
|
91
|
+
.map(item => item.value)
|
|
92
|
+
.filter(item => item !== ('' || undefined))
|
|
93
|
+
highlightedBarValues =
|
|
94
|
+
config.xAxis.type === 'date'
|
|
95
|
+
? HighLightedBarUtils.formatDates(highlightedBarValues)
|
|
96
|
+
: highlightedBarValues
|
|
97
|
+
let transparentBar =
|
|
98
|
+
config.legend.behavior === 'highlight' &&
|
|
99
|
+
seriesHighlight.length > 0 &&
|
|
100
|
+
seriesHighlight.indexOf(bar.key) === -1
|
|
101
|
+
let displayBar =
|
|
102
|
+
config.legend.behavior === 'highlight' ||
|
|
103
|
+
seriesHighlight.length === 0 ||
|
|
104
|
+
seriesHighlight.indexOf(bar.key) !== -1
|
|
59
105
|
let barHeight = config.barHeight
|
|
60
106
|
let numbericBarHeight = parseInt(!config.isLollipopChart ? barHeight : lollipopBarWidth)
|
|
61
107
|
if (isNaN(numbericBarHeight)) {
|
|
@@ -65,10 +111,17 @@ export const BarChartHorizontal = () => {
|
|
|
65
111
|
const defaultBarWidth = Math.abs(xScale(bar.value) - xScale(scaleVal))
|
|
66
112
|
const isPositiveBar = bar.value >= 0 && isNumber(bar.value)
|
|
67
113
|
|
|
68
|
-
const {
|
|
114
|
+
const {
|
|
115
|
+
barWidthHorizontal: barWidth,
|
|
116
|
+
isSuppressed,
|
|
117
|
+
getAbsentDataLabel
|
|
118
|
+
} = getBarConfig({ bar, defaultBarWidth, config, isNumber, isVertical: false })
|
|
69
119
|
const barX = bar.value < 0 ? Math.abs(xScale(bar.value)) : xScale(scaleVal)
|
|
70
120
|
const yAxisValue = formatNumber(bar.value, 'left')
|
|
71
|
-
const xAxisValue =
|
|
121
|
+
const xAxisValue =
|
|
122
|
+
config.runtime[section].type === 'date'
|
|
123
|
+
? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey]))
|
|
124
|
+
: data[barGroup.index][config.runtime.originalXAxis.dataKey]
|
|
72
125
|
|
|
73
126
|
const barPosition = !isPositiveBar ? 'below' : 'above'
|
|
74
127
|
const absentDataLabel = getAbsentDataLabel(yAxisValue)
|
|
@@ -96,7 +149,9 @@ export const BarChartHorizontal = () => {
|
|
|
96
149
|
// create new Index for bars with negative values
|
|
97
150
|
const newIndex = bar.value < 0 ? -1 : index
|
|
98
151
|
|
|
99
|
-
let yAxisTooltip = config.runtime.yAxis.label
|
|
152
|
+
let yAxisTooltip = config.runtime.yAxis.label
|
|
153
|
+
? `${config.runtime.yAxis.label}: ${xAxisValue}`
|
|
154
|
+
: xAxisValue
|
|
100
155
|
const additionalColTooltip = getAdditionalColumn(hoveredBar)
|
|
101
156
|
const tooltipBody = `${config.runtime.seriesLabels[bar.key]}: ${yAxisValue}`
|
|
102
157
|
const tooltip = `<ul>
|
|
@@ -108,15 +163,28 @@ export const BarChartHorizontal = () => {
|
|
|
108
163
|
// configure colors
|
|
109
164
|
let labelColor = '#000000'
|
|
110
165
|
labelColor = HighLightedBarUtils.checkFontColor(yAxisValue, highlightedBarValues, labelColor) // Set if background is transparent'
|
|
111
|
-
let barColor =
|
|
166
|
+
let barColor =
|
|
167
|
+
config.runtime.seriesLabels && config.runtime.seriesLabels[bar.key]
|
|
168
|
+
? colorScale(config.runtime.seriesLabels[bar.key])
|
|
169
|
+
: colorScale(bar.key)
|
|
112
170
|
barColor = assignColorsToValues(barGroups.length, barGroup.index, barColor) // Color code by category
|
|
113
171
|
const isRegularLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'regular'
|
|
114
172
|
const isTwoToneLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'two-tone'
|
|
115
173
|
const isHighlightedBar = highlightedBarValues?.includes(xAxisValue)
|
|
116
174
|
const highlightedBarColor = getHighlightedBarColorByValue(xAxisValue)
|
|
117
175
|
const highlightedBar = getHighlightedBarByValue(xAxisValue)
|
|
118
|
-
const borderColor = isHighlightedBar
|
|
119
|
-
|
|
176
|
+
const borderColor = isHighlightedBar
|
|
177
|
+
? highlightedBarColor
|
|
178
|
+
: config.barHasBorder === 'true'
|
|
179
|
+
? '#000'
|
|
180
|
+
: 'transparent'
|
|
181
|
+
const borderWidth = isHighlightedBar
|
|
182
|
+
? highlightedBar.borderWidth
|
|
183
|
+
: config.isLollipopChart
|
|
184
|
+
? 0
|
|
185
|
+
: config.barHasBorder === 'true'
|
|
186
|
+
? barBorderWidth
|
|
187
|
+
: 0
|
|
120
188
|
const displaylollipopShape = testZeroValue(bar.value) ? 'none' : 'block'
|
|
121
189
|
|
|
122
190
|
// update label color
|
|
@@ -176,7 +244,12 @@ export const BarChartHorizontal = () => {
|
|
|
176
244
|
|
|
177
245
|
const hasAsterisk = String(pd.symbol).includes('Asterisk')
|
|
178
246
|
const verticalAnchor = hasAsterisk ? 'middle' : 'end'
|
|
179
|
-
const iconSize =
|
|
247
|
+
const iconSize =
|
|
248
|
+
pd.symbol === 'Asterisk'
|
|
249
|
+
? barHeight * 1.2
|
|
250
|
+
: pd.symbol === 'Double Asterisk'
|
|
251
|
+
? barHeight
|
|
252
|
+
: barHeight / 1.5
|
|
180
253
|
const fillColor = pd.displayGray ? '#8b8b8a' : '#000'
|
|
181
254
|
return (
|
|
182
255
|
<Text // prettier-ignore
|
|
@@ -240,7 +313,13 @@ export const BarChartHorizontal = () => {
|
|
|
240
313
|
</Text>
|
|
241
314
|
)}
|
|
242
315
|
{isLabelBelowBar && !config.yAxis.hideLabel && (
|
|
243
|
-
<Text
|
|
316
|
+
<Text
|
|
317
|
+
x={config.yAxis.hideAxis ? 0 : 5}
|
|
318
|
+
y={barGroup.height}
|
|
319
|
+
dy={4}
|
|
320
|
+
verticalAnchor={'start'}
|
|
321
|
+
textAnchor={'start'}
|
|
322
|
+
>
|
|
244
323
|
{config.runtime.yAxis.type === 'date'
|
|
245
324
|
? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey]))
|
|
246
325
|
: isHorizontal
|
|
@@ -5,6 +5,7 @@ import { BarStackHorizontal } from '@visx/shape'
|
|
|
5
5
|
import { Group } from '@visx/group'
|
|
6
6
|
import { Text } from '@visx/text'
|
|
7
7
|
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
8
|
+
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
8
9
|
|
|
9
10
|
// types
|
|
10
11
|
import BarChartContext, { type BarChartContextValues } from './context'
|
|
@@ -22,7 +23,6 @@ const BarChartStackedHorizontal = () => {
|
|
|
22
23
|
config,
|
|
23
24
|
formatDate,
|
|
24
25
|
formatNumber,
|
|
25
|
-
getTextWidth,
|
|
26
26
|
parseDate,
|
|
27
27
|
seriesHighlight,
|
|
28
28
|
setSharedFilter,
|
|
@@ -37,18 +37,38 @@ const BarChartStackedHorizontal = () => {
|
|
|
37
37
|
config.visualizationSubType === 'stacked' &&
|
|
38
38
|
isHorizontal && (
|
|
39
39
|
<>
|
|
40
|
-
<BarStackHorizontal
|
|
40
|
+
<BarStackHorizontal
|
|
41
|
+
data={data}
|
|
42
|
+
keys={barStackedSeriesKeys}
|
|
43
|
+
height={yMax}
|
|
44
|
+
y={d => d[config.runtime.yAxis.dataKey]}
|
|
45
|
+
xScale={xScale}
|
|
46
|
+
yScale={yScale}
|
|
47
|
+
color={colorScale}
|
|
48
|
+
offset='none'
|
|
49
|
+
>
|
|
41
50
|
{barStacks =>
|
|
42
51
|
barStacks.map(barStack =>
|
|
43
52
|
updateBars(barStack.bars).map((bar, index) => {
|
|
44
|
-
const transparentBar =
|
|
45
|
-
|
|
53
|
+
const transparentBar =
|
|
54
|
+
config.legend.behavior === 'highlight' &&
|
|
55
|
+
seriesHighlight.length > 0 &&
|
|
56
|
+
seriesHighlight.indexOf(bar.key) === -1
|
|
57
|
+
const displayBar =
|
|
58
|
+
config.legend.behavior === 'highlight' ||
|
|
59
|
+
seriesHighlight.length === 0 ||
|
|
60
|
+
seriesHighlight.indexOf(bar.key) !== -1
|
|
46
61
|
config.barHeight = Number(config.barHeight)
|
|
47
62
|
const labelColor = getContrastColor('#000', colorScale(config.runtime.seriesLabels[bar.key]))
|
|
48
63
|
// tooltips
|
|
49
64
|
const xAxisValue = formatNumber(data[bar.index][bar.key], 'left')
|
|
50
|
-
const yAxisValue =
|
|
51
|
-
|
|
65
|
+
const yAxisValue =
|
|
66
|
+
config.runtime.yAxis.type === 'date'
|
|
67
|
+
? formatDate(parseDate(data[bar.index][config.runtime.originalXAxis.dataKey]))
|
|
68
|
+
: data[bar.index][config.runtime.originalXAxis.dataKey]
|
|
69
|
+
const yAxisTooltip = config.runtime.yAxis.label
|
|
70
|
+
? `${config.runtime.yAxis.label}: ${yAxisValue}`
|
|
71
|
+
: yAxisValue
|
|
52
72
|
const textWidth = getTextWidth(xAxisValue, `normal ${fontSize[config.fontSize]}px sans-serif`)
|
|
53
73
|
const additionalColTooltip = getAdditionalColumn(hoveredBar)
|
|
54
74
|
const tooltipBody = `${config.runtime.seriesLabels[bar.key]}: ${xAxisValue}`
|
|
@@ -93,17 +113,21 @@ const BarChartStackedHorizontal = () => {
|
|
|
93
113
|
}
|
|
94
114
|
})}
|
|
95
115
|
|
|
96
|
-
{orientation === 'horizontal' &&
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
116
|
+
{orientation === 'horizontal' &&
|
|
117
|
+
visualizationSubType === 'stacked' &&
|
|
118
|
+
isLabelBelowBar &&
|
|
119
|
+
barStack.index === 0 &&
|
|
120
|
+
!config.yAxis.hideLabel && (
|
|
121
|
+
<Text
|
|
122
|
+
x={`${bar.x + (config.isLollipopChart ? 15 : 5)}`} // padding
|
|
123
|
+
y={bar.y + bar.height * 1.2}
|
|
124
|
+
fill={'#000000'}
|
|
125
|
+
textAnchor='start'
|
|
126
|
+
verticalAnchor='start'
|
|
127
|
+
>
|
|
128
|
+
{yAxisValue}
|
|
129
|
+
</Text>
|
|
130
|
+
)}
|
|
107
131
|
|
|
108
132
|
{displayNumbersOnBar && textWidth < bar.width && (
|
|
109
133
|
<Text
|
|
@@ -42,7 +42,7 @@ export const BarChartVertical = () => {
|
|
|
42
42
|
} = useBarChart()
|
|
43
43
|
|
|
44
44
|
// prettier-ignore
|
|
45
|
-
const { colorScale, config, dashboardConfig, tableData, formatDate, formatNumber, getXAxisData, getYAxisData, isNumber, parseDate, seriesHighlight, setSharedFilter, transformedData, brushConfig
|
|
45
|
+
const { colorScale, config, dashboardConfig, tableData, formatDate, formatNumber, getXAxisData, getYAxisData, isNumber, parseDate, seriesHighlight, setSharedFilter, transformedData, brushConfig } = useContext<ChartContext>(ConfigContext)
|
|
46
46
|
const { HighLightedBarUtils } = useHighlightedBars(config)
|
|
47
47
|
let data = transformedData
|
|
48
48
|
// check if user add suppression
|
|
@@ -58,7 +58,9 @@ export const BarChartVertical = () => {
|
|
|
58
58
|
|
|
59
59
|
return (
|
|
60
60
|
config.visualizationSubType !== 'stacked' &&
|
|
61
|
-
(config.visualizationType === 'Bar' ||
|
|
61
|
+
(config.visualizationType === 'Bar' ||
|
|
62
|
+
config.visualizationType === 'Combo' ||
|
|
63
|
+
isConvertLineToBarGraph(config.visualizationType, data, config.allowLineToBarGraph)) &&
|
|
62
64
|
config.orientation === 'vertical' && (
|
|
63
65
|
<Group>
|
|
64
66
|
<BarGroup
|
|
@@ -86,27 +88,48 @@ export const BarChartVertical = () => {
|
|
|
86
88
|
>
|
|
87
89
|
{barGroup.bars.map((bar, index) => {
|
|
88
90
|
const scaleVal = config.yAxis.type === 'logarithmic' ? 0.1 : 0
|
|
89
|
-
let highlightedBarValues = config.highlightedBarValues
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
let highlightedBarValues = config.highlightedBarValues
|
|
92
|
+
.map(item => item.value)
|
|
93
|
+
.filter(item => item !== ('' || undefined))
|
|
94
|
+
highlightedBarValues =
|
|
95
|
+
config.xAxis.type === 'date'
|
|
96
|
+
? HighLightedBarUtils.formatDates(highlightedBarValues)
|
|
97
|
+
: highlightedBarValues
|
|
98
|
+
const transparentBar =
|
|
99
|
+
config.legend.behavior === 'highlight' &&
|
|
100
|
+
seriesHighlight.length > 0 &&
|
|
101
|
+
seriesHighlight.indexOf(bar.key) === -1
|
|
102
|
+
const displayBar =
|
|
103
|
+
config.legend.behavior === 'highlight' ||
|
|
104
|
+
seriesHighlight.length === 0 ||
|
|
105
|
+
seriesHighlight.indexOf(bar.key) !== -1
|
|
93
106
|
|
|
94
107
|
let barGroupWidth = seriesScale.range()[1] - seriesScale.range()[0]
|
|
95
108
|
const defaultBarHeight = Math.abs(yScale(bar.value) - yScale(scaleVal))
|
|
96
109
|
const defaultBarY = bar.value >= 0 && isNumber(bar.value) ? bar.y : yScale(0)
|
|
97
110
|
let barWidth = config.isLollipopChart ? lollipopBarWidth : seriesScale.bandwidth()
|
|
98
|
-
let barX =
|
|
111
|
+
let barX =
|
|
112
|
+
bar.x +
|
|
113
|
+
(config.isLollipopChart ? (barGroupWidth / barGroup.bars.length - lollipopBarWidth) / 2 : 0) -
|
|
114
|
+
(config.xAxis.type === 'date-time' ? barGroupWidth / 2 : 0)
|
|
99
115
|
setBarWidth(barWidth)
|
|
100
116
|
setTotalBarsInGroup(barGroup.bars.length)
|
|
101
117
|
const yAxisValue = formatNumber(/[a-zA-Z]/.test(String(bar.value)) ? '' : bar.value, 'left')
|
|
102
118
|
const xAxisValue =
|
|
103
|
-
config.runtime[section].type === 'date'
|
|
119
|
+
config.runtime[section].type === 'date'
|
|
120
|
+
? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey]))
|
|
121
|
+
: data[barGroup.index][config.runtime.originalXAxis.dataKey]
|
|
104
122
|
|
|
105
123
|
// create new Index for bars with negative values
|
|
106
124
|
const newIndex = bar.value < 0 ? -1 : index
|
|
107
125
|
// tooltips
|
|
108
|
-
const additionalColTooltip = getAdditionalColumn(
|
|
109
|
-
|
|
126
|
+
const additionalColTooltip = getAdditionalColumn(
|
|
127
|
+
bar.key,
|
|
128
|
+
data[barGroup.index][config.runtime.originalXAxis.dataKey]
|
|
129
|
+
)
|
|
130
|
+
let xAxisTooltip = config.runtime.xAxis.label
|
|
131
|
+
? `${config.runtime.xAxis.label}: ${xAxisValue}`
|
|
132
|
+
: xAxisValue
|
|
110
133
|
const tooltipBody = `${config.runtime.seriesLabels[bar.key]}: ${yAxisValue}`
|
|
111
134
|
|
|
112
135
|
const tooltip = `<ul>
|
|
@@ -118,16 +141,37 @@ export const BarChartVertical = () => {
|
|
|
118
141
|
// configure colors
|
|
119
142
|
let labelColor = '#000000'
|
|
120
143
|
labelColor = HighLightedBarUtils.checkFontColor(yAxisValue, highlightedBarValues, labelColor) // Set if background is transparent'
|
|
121
|
-
let barColor =
|
|
144
|
+
let barColor =
|
|
145
|
+
config.runtime.seriesLabels && config.runtime.seriesLabels[bar.key]
|
|
146
|
+
? colorScale(config.runtime.seriesLabels[bar.key])
|
|
147
|
+
: colorScale(bar.key)
|
|
122
148
|
const isRegularLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'regular'
|
|
123
149
|
const isTwoToneLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'two-tone'
|
|
124
150
|
const isHighlightedBar = highlightedBarValues?.includes(xAxisValue)
|
|
125
151
|
const highlightedBarColor = getHighlightedBarColorByValue(xAxisValue)
|
|
126
152
|
const highlightedBar = getHighlightedBarByValue(xAxisValue)
|
|
127
|
-
const borderColor = isHighlightedBar
|
|
128
|
-
|
|
153
|
+
const borderColor = isHighlightedBar
|
|
154
|
+
? highlightedBarColor
|
|
155
|
+
: config.barHasBorder === 'true'
|
|
156
|
+
? '#000'
|
|
157
|
+
: 'transparent'
|
|
158
|
+
const borderWidth = isHighlightedBar
|
|
159
|
+
? highlightedBar.borderWidth
|
|
160
|
+
: config.isLollipopChart
|
|
161
|
+
? 0
|
|
162
|
+
: config.barHasBorder === 'true'
|
|
163
|
+
? barBorderWidth
|
|
164
|
+
: 0
|
|
129
165
|
|
|
130
|
-
const { barHeight, isSuppressed, getBarY, getAbsentDataLabel } = getBarConfig({
|
|
166
|
+
const { barHeight, isSuppressed, getBarY, getAbsentDataLabel } = getBarConfig({
|
|
167
|
+
bar,
|
|
168
|
+
defaultBarHeight,
|
|
169
|
+
config,
|
|
170
|
+
isNumber,
|
|
171
|
+
barWidth,
|
|
172
|
+
isVertical: true,
|
|
173
|
+
yAxisValue
|
|
174
|
+
})
|
|
131
175
|
|
|
132
176
|
const absentDataLabel = getAbsentDataLabel(yAxisValue)
|
|
133
177
|
const barDefaultLabel = isSuppressed || !config.labels ? '' : yAxisValue
|
|
@@ -149,9 +193,11 @@ export const BarChartVertical = () => {
|
|
|
149
193
|
? sharedFilters.map(_sharedFilter => {
|
|
150
194
|
if (_sharedFilter.setBy === config.uid) {
|
|
151
195
|
// If the current filter is the reset filter item.
|
|
152
|
-
if (_sharedFilter.resetLabel === _sharedFilter.active)
|
|
196
|
+
if (_sharedFilter.resetLabel === _sharedFilter.active)
|
|
197
|
+
return colorScale(config.runtime.seriesLabels[bar.key])
|
|
153
198
|
// If the current filter is the bars
|
|
154
|
-
if (_sharedFilter.active === transformedData[barGroup.index][config.xAxis.dataKey])
|
|
199
|
+
if (_sharedFilter.active === transformedData[barGroup.index][config.xAxis.dataKey])
|
|
200
|
+
return colorScale(config.runtime.seriesLabels[bar.key])
|
|
155
201
|
return _filteredOutColor
|
|
156
202
|
} else {
|
|
157
203
|
// If the setBy isn't the config.uid return the original barColor
|
|
@@ -163,14 +209,16 @@ export const BarChartVertical = () => {
|
|
|
163
209
|
if (isRegularLollipopColor) _barColor = barColor
|
|
164
210
|
|
|
165
211
|
if (isHighlightedBar) _barColor = 'transparent'
|
|
166
|
-
if (config.legend.colorCode)
|
|
212
|
+
if (config.legend.colorCode)
|
|
213
|
+
_barColor = assignColorsToValues(barGroups.length, barGroup.index, barColor)
|
|
167
214
|
if (isTwoToneLollipopColor) _barColor = chroma(barColor).brighten(1)
|
|
168
215
|
return _barColor
|
|
169
216
|
}
|
|
170
217
|
|
|
171
218
|
// if this is a two tone lollipop slightly lighten the bar.
|
|
172
219
|
if (isTwoToneLollipopColor) _barColor = chroma(barColor).brighten(1)
|
|
173
|
-
if (config.legend.colorCode)
|
|
220
|
+
if (config.legend.colorCode)
|
|
221
|
+
_barColor = assignColorsToValues(barGroups.length, barGroup.index, barColor)
|
|
174
222
|
|
|
175
223
|
// if we're highlighting a bar make it invisible since it gets a border
|
|
176
224
|
if (isHighlightedBar) _barColor = 'transparent'
|
|
@@ -217,13 +265,23 @@ export const BarChartVertical = () => {
|
|
|
217
265
|
const isValueMatch = String(pd.value) === String(bar.value) && pd.value !== ''
|
|
218
266
|
const isSuppressed = isValueMatch && selectedSuppressionColumn
|
|
219
267
|
|
|
220
|
-
if (
|
|
268
|
+
if (
|
|
269
|
+
!isSuppressed ||
|
|
270
|
+
barWidth < 10 ||
|
|
271
|
+
!config.general.showSuppressedSymbol ||
|
|
272
|
+
pd.hideBarSymbol
|
|
273
|
+
) {
|
|
221
274
|
return
|
|
222
275
|
}
|
|
223
276
|
const hasAsterisk = String(pd.symbol).includes('Asterisk')
|
|
224
277
|
const yPadding = hasAsterisk ? -5 : -8
|
|
225
278
|
const verticalAnchor = hasAsterisk ? 'middle' : 'end'
|
|
226
|
-
const iconSize =
|
|
279
|
+
const iconSize =
|
|
280
|
+
pd.symbol === 'Asterisk'
|
|
281
|
+
? barWidth * 1.2
|
|
282
|
+
: pd.symbol === 'Double Asterisk'
|
|
283
|
+
? barWidth
|
|
284
|
+
: barWidth / 1.5
|
|
227
285
|
const fillColor = pd.displayGray ? '#8b8b8a' : '#000'
|
|
228
286
|
|
|
229
287
|
return (
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
2
|
+
|
|
1
3
|
// Define an interface for the function's parameter
|
|
2
4
|
interface BarConfigProps {
|
|
3
5
|
defaultBarWidth?: number
|
|
@@ -5,13 +7,20 @@ interface BarConfigProps {
|
|
|
5
7
|
bar?: { [key: string]: any }
|
|
6
8
|
isNumber?: Function
|
|
7
9
|
config: { [key: string]: any }
|
|
8
|
-
getTextWidth: (a: string, b: string) => string
|
|
9
10
|
barWidth: number
|
|
10
11
|
isVertical: boolean
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
// Function to create bar width based on suppression status and missing data label
|
|
14
|
-
export const getBarConfig = ({
|
|
15
|
+
export const getBarConfig = ({
|
|
16
|
+
bar,
|
|
17
|
+
defaultBarHeight,
|
|
18
|
+
defaultBarWidth,
|
|
19
|
+
config,
|
|
20
|
+
isNumber,
|
|
21
|
+
barWidth,
|
|
22
|
+
isVertical
|
|
23
|
+
}: BarConfigProps) => {
|
|
15
24
|
const heightMini = 3 /// height of small bars aka suppressed/NA/Zero valued
|
|
16
25
|
let barHeight = defaultBarHeight
|
|
17
26
|
|
|
@@ -20,6 +29,7 @@ export const getBarConfig = ({ bar, defaultBarHeight, defaultBarWidth, config, i
|
|
|
20
29
|
let barLabel = ''
|
|
21
30
|
let isSuppressed = false
|
|
22
31
|
let showMissingDataLabel = false
|
|
32
|
+
let showZeroValueData = false
|
|
23
33
|
const showSuppressedSymbol = config.general.showSuppressedSymbol
|
|
24
34
|
|
|
25
35
|
config.preliminaryData.forEach(pd => {
|
|
@@ -45,10 +55,19 @@ export const getBarConfig = ({ bar, defaultBarHeight, defaultBarWidth, config, i
|
|
|
45
55
|
barHeight = labelFits ? heightMini : 0
|
|
46
56
|
barWidthHorizontal = heightMini
|
|
47
57
|
}
|
|
58
|
+
// Handle undefined, null, or non-calculable bar.value
|
|
59
|
+
|
|
60
|
+
if (!isSuppressed && bar.value === '0' && config.general.showZeroValueData) {
|
|
61
|
+
const labelWidth = getTextWidth('0', `normal ${barWidth / 2}px sans-serif`)
|
|
62
|
+
const labelFits = Number(labelWidth) < barWidth && barWidth > 10
|
|
63
|
+
showZeroValueData = true
|
|
64
|
+
barHeight = labelFits ? heightMini : 0
|
|
65
|
+
barWidthHorizontal = heightMini
|
|
66
|
+
}
|
|
48
67
|
|
|
49
68
|
const getBarY = (defaultBarY, yScale) => {
|
|
50
69
|
// calculate Y position of small bars (suppressed,N/A,Zero valued) bars
|
|
51
|
-
if (isSuppressed || showMissingDataLabel) {
|
|
70
|
+
if (isSuppressed || showMissingDataLabel || showZeroValueData) {
|
|
52
71
|
if (config.isLollipopChart) {
|
|
53
72
|
return yScale - heightMini * 2
|
|
54
73
|
} else {
|
|
@@ -68,6 +87,7 @@ export const getBarConfig = ({ bar, defaultBarHeight, defaultBarWidth, config, i
|
|
|
68
87
|
if (isSuppressed) label = ''
|
|
69
88
|
// If the config is set to show a label for missing data, display 'N/A'
|
|
70
89
|
if (showMissingDataLabel) label = 'N/A'
|
|
90
|
+
if (showZeroValueData) label = '0'
|
|
71
91
|
|
|
72
92
|
// determine label width in pixels & check if it fits to the bar width
|
|
73
93
|
const labelWidth = getTextWidth(barLabel, `normal ${barWidth / 2}px sans-serif`)
|
|
@@ -78,7 +98,6 @@ export const getBarConfig = ({ bar, defaultBarHeight, defaultBarWidth, config, i
|
|
|
78
98
|
return labelFits && isVertical ? label : !isVertical ? label : ''
|
|
79
99
|
}
|
|
80
100
|
}
|
|
81
|
-
|
|
82
101
|
return { barWidthHorizontal, barHeight, isSuppressed, showMissingDataLabel, getBarY, getAbsentDataLabel }
|
|
83
102
|
}
|
|
84
103
|
|
|
@@ -2,15 +2,16 @@ import { Group } from '@visx/group'
|
|
|
2
2
|
import { useContext, useEffect, useRef, useState } from 'react'
|
|
3
3
|
import ConfigContext from '../ConfigContext'
|
|
4
4
|
import * as d3 from 'd3'
|
|
5
|
-
import { invertValue } from '@cdc/core/helpers/scaling'
|
|
6
5
|
import { Text } from '@visx/text'
|
|
6
|
+
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
7
|
+
|
|
7
8
|
interface BrushChartProps {
|
|
8
9
|
xMax: number
|
|
9
10
|
yMax: number
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
const BrushChart = ({ xMax, yMax }: BrushChartProps) => {
|
|
13
|
-
const { tableData, config, setBrushConfig,
|
|
14
|
+
const { tableData, config, setBrushConfig, dashboardConfig, formatDate } = useContext(ConfigContext)
|
|
14
15
|
const [brushState, setBrushState] = useState({ isBrushing: false, selection: [] })
|
|
15
16
|
const [brushKey, setBrushKey] = useState(0)
|
|
16
17
|
const sharedFilters = dashboardConfig?.dashboard?.sharedFilters ?? []
|