@cdc/chart 4.23.10 → 4.24.1
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 +34606 -32218
- package/examples/feature/bar/additional-column-tooltip.json +446 -0
- package/examples/feature/bar/example-bar-chart.json +1 -46
- package/examples/feature/bar/lollipop.json +156 -0
- package/examples/feature/bar/tall-data.json +98 -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/forest-plot.json +63 -19
- package/examples/feature/forest-plot/{broken.json → linear.json} +77 -23
- package/examples/feature/forest-plot/log.json +26 -0
- package/examples/feature/forest-plot/logarithmic.json +271 -0
- package/examples/feature/line/line-chart-preliminary.json +346 -0
- package/examples/feature/line/line-points.json +340 -0
- package/examples/feature/regions/index.json +462 -0
- package/examples/feature/scatterplot/scatterplot.json +272 -33
- package/examples/gallery/bar-chart-vertical/combo-line-chart.json +181 -48
- package/examples/private/chart-t.json +3740 -0
- package/examples/private/combo.json +369 -0
- package/examples/private/epi-data.csv +13 -0
- package/examples/private/epi-data.json +62 -0
- package/examples/private/epi.json +403 -0
- package/examples/private/occupancy.json +109283 -0
- package/examples/private/prod-line-config.json +401 -0
- package/examples/private/region-data.json +822 -0
- package/examples/private/region-testing.json +312 -0
- package/examples/private/scaling.json +45325 -0
- package/examples/private/testing-data.json +1739 -0
- package/examples/private/testing.json +816 -0
- package/examples/sparkline-multilple.json +846 -0
- package/index.html +12 -8
- package/package.json +3 -3
- package/src/CdcChart.tsx +42 -211
- package/src/ConfigContext.tsx +6 -0
- 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/ChartEditor.stories.tsx +22 -0
- package/src/_stories/ChartLine.preliminary.tsx +19 -0
- package/src/_stories/ChartSuppress.stories.tsx +19 -0
- package/src/_stories/_mock/brush_mock.json +393 -0
- package/src/_stories/_mock/pie_config.json +191 -0
- package/src/_stories/_mock/pie_data.json +218 -0
- package/src/_stories/_mock/preliminary_mock.json +346 -0
- package/src/_stories/_mock/suppress_mock.json +911 -0
- package/src/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +6 -7
- package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +7 -36
- package/src/components/AreaChart/index.tsx +4 -0
- package/src/components/{BarChart.Horizontal.jsx → BarChart/components/BarChart.Horizontal.tsx} +111 -34
- package/src/components/{BarChart.StackedHorizontal.jsx → BarChart/components/BarChart.StackedHorizontal.tsx} +55 -20
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +106 -0
- package/src/components/{BarChart.Vertical.jsx → BarChart/components/BarChart.Vertical.tsx} +162 -34
- package/src/components/BarChart/components/BarChart.jsx +39 -0
- package/src/components/{BarChartType.jsx → BarChart/components/BarChartType.jsx} +0 -2
- package/src/components/BarChart/components/context.tsx +13 -0
- package/src/components/BarChart/index.tsx +3 -0
- package/src/components/{BoxPlot.jsx → BoxPlot/BoxPlot.jsx} +1 -1
- package/src/components/BoxPlot/index.tsx +3 -0
- package/src/components/DeviationBar.jsx +4 -3
- package/src/components/{EditorPanel.jsx → EditorPanel/EditorPanel.tsx} +807 -865
- package/src/components/EditorPanel/components/Panel.DateHighlighting.tsx +109 -0
- package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panel.ForestPlotSettings.tsx} +190 -220
- package/src/components/EditorPanel/components/Panel.Regions.tsx +168 -0
- package/src/components/{Series.jsx → EditorPanel/components/Panel.Series.tsx} +23 -4
- package/src/components/EditorPanel/components/PanelProps.ts +3 -0
- package/src/components/EditorPanel/components/Panels.tsx +13 -0
- package/src/components/EditorPanel/components/panels.scss +72 -0
- package/src/components/EditorPanel/editor-panel.scss +751 -0
- package/src/components/EditorPanel/index.tsx +3 -0
- package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +50 -5
- package/src/components/{Forecasting.jsx → Forecasting/Forecasting.jsx} +1 -1
- package/src/components/Forecasting/index.tsx +3 -0
- package/src/components/ForestPlot/ForestPlot.tsx +254 -0
- package/src/components/ForestPlot/ForestPlotProps.ts +18 -0
- package/src/components/ForestPlot/index.scss +1 -0
- package/src/components/ForestPlot/index.tsx +3 -0
- package/src/components/Legend/Legend.tsx +347 -0
- package/src/components/Legend/index.tsx +3 -0
- package/src/components/LineChart/LineChartProps.ts +46 -0
- package/src/components/{LineChart.Circle.tsx → LineChart/components/LineChart.Circle.tsx} +36 -30
- package/src/components/LineChart/helpers.ts +45 -0
- package/src/components/LineChart/index.scss +1 -0
- package/src/components/{LineChart.tsx → LineChart/index.tsx} +83 -42
- package/src/components/LinearChart.jsx +125 -82
- package/src/components/PairedBarChart.jsx +2 -2
- package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +16 -7
- package/src/components/PieChart/index.tsx +3 -0
- package/src/components/Regions/components/Regions.tsx +135 -0
- package/src/components/Regions/index.tsx +3 -0
- package/src/components/{ScatterPlot.jsx → ScatterPlot/ScatterPlot.jsx} +3 -3
- package/src/components/ScatterPlot/index.tsx +3 -0
- package/src/components/{SparkLine.jsx → Sparkline/SparkLine.jsx} +2 -2
- package/src/components/Sparkline/index.tsx +3 -0
- package/src/components/ZoomBrush.tsx +168 -0
- package/src/data/initial-state.js +30 -16
- package/src/helpers/abbreviateNumber.ts +17 -0
- package/src/helpers/computeMarginBottom.ts +55 -0
- package/src/helpers/filterData.ts +18 -0
- package/src/helpers/generateColorsArray.ts +8 -0
- package/src/helpers/getQuartiles.ts +30 -0
- package/src/helpers/handleChartAriaLabels.ts +19 -0
- package/src/helpers/handleLineType.ts +18 -0
- package/src/helpers/lineOptions.ts +18 -0
- package/src/helpers/sort.ts +7 -0
- package/src/helpers/tests/computeMarginBottom.test.ts +20 -0
- package/src/hooks/useBarChart.js +72 -7
- package/src/hooks/useColorScale.ts +50 -0
- 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 → useTooltip.tsx} +84 -55
- package/src/scss/main.scss +70 -38
- package/src/types/ChartConfig.ts +178 -0
- package/src/types/ChartContext.ts +54 -0
- package/src/types/ForestPlot.ts +53 -0
- package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
- package/src/ConfigContext.jsx +0 -5
- package/src/components/BarChart.StackedVertical.jsx +0 -95
- package/src/components/BarChart.jsx +0 -30
- package/src/components/ForestPlot.jsx +0 -191
- package/src/components/Legend.jsx +0 -277
- package/src/scss/LinearChart.scss +0 -0
- package/src/scss/editor-panel.scss +0 -745
- package/src/scss/legend.scss +0 -206
- package/src/scss/mixins.scss +0 -0
- package/src/scss/variables.scss +0 -1
|
@@ -2,32 +2,53 @@ import React, { useContext } from 'react'
|
|
|
2
2
|
|
|
3
3
|
import * as allCurves from '@visx/curve'
|
|
4
4
|
import { Group } from '@visx/group'
|
|
5
|
-
import { LinePath, Bar } from '@visx/shape'
|
|
5
|
+
import { LinePath, Bar, SplitLinePath } from '@visx/shape'
|
|
6
6
|
import { Text } from '@visx/text'
|
|
7
7
|
|
|
8
8
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
9
|
-
import ConfigContext from '
|
|
10
|
-
import useRightAxis from '
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
import ConfigContext from '../../ConfigContext'
|
|
10
|
+
import useRightAxis from '../../hooks/useRightAxis'
|
|
11
|
+
import { filterCircles, createStyles } from './helpers'
|
|
12
|
+
import LineChartCircle from './components/LineChart.Circle'
|
|
13
|
+
|
|
14
|
+
// types
|
|
15
|
+
import { type ChartContext } from '../../types/ChartContext'
|
|
16
|
+
import { type LineChartProps } from './LineChartProps'
|
|
17
|
+
|
|
18
|
+
const LineChart = (props: LineChartProps) => {
|
|
19
|
+
// prettier-ignore
|
|
20
|
+
const {
|
|
21
|
+
getXAxisData,
|
|
22
|
+
getYAxisData,
|
|
23
|
+
handleTooltipClick,
|
|
24
|
+
handleTooltipMouseOff,
|
|
25
|
+
handleTooltipMouseOver,
|
|
26
|
+
tooltipData,
|
|
27
|
+
xMax,
|
|
28
|
+
xScale,
|
|
29
|
+
yMax,
|
|
30
|
+
yScale,
|
|
31
|
+
} = props
|
|
32
|
+
|
|
33
|
+
// prettier-ignore
|
|
34
|
+
const {
|
|
35
|
+
colorScale,
|
|
36
|
+
config,
|
|
37
|
+
formatNumber,
|
|
38
|
+
handleLineType,
|
|
39
|
+
isNumber,
|
|
40
|
+
parseDate,
|
|
41
|
+
seriesHighlight,
|
|
42
|
+
tableData,
|
|
43
|
+
transformedData: data,
|
|
44
|
+
updateConfig,
|
|
45
|
+
rawData
|
|
46
|
+
} = useContext<ChartContext>(ConfigContext)
|
|
17
47
|
const { yScaleRight } = useRightAxis({ config, yMax, data, updateConfig })
|
|
18
|
-
|
|
19
48
|
if (!handleTooltipMouseOver) return
|
|
20
|
-
const handleAxisFormating = (axis = 'left', label, value) => {
|
|
21
|
-
// if this is an x axis category/date value return without doing any formatting.
|
|
22
|
-
// if (label === config.runtime.xAxis.label) return value
|
|
23
|
-
axis = String(axis).toLocaleLowerCase()
|
|
24
|
-
if (label) {
|
|
25
|
-
return `${label}: ${formatNumber(value, axis)}`
|
|
26
|
-
}
|
|
27
|
-
return `${formatNumber(value, axis)}`
|
|
28
|
-
}
|
|
29
49
|
|
|
30
50
|
const DEBUG = false
|
|
51
|
+
const { lineDatapointStyle, showLineSeriesLabels, legend } = config
|
|
31
52
|
|
|
32
53
|
return (
|
|
33
54
|
<ErrorBoundary component='LineChart'>
|
|
@@ -38,22 +59,22 @@ const LineChart = ({ xScale, yScale, getXAxisData, getYAxisData, xMax, yMax, han
|
|
|
38
59
|
let lineType = config.series.filter(item => item.dataKey === seriesKey)[0].type
|
|
39
60
|
const seriesData = config.series.filter(item => item.dataKey === seriesKey)
|
|
40
61
|
const seriesAxis = seriesData[0].axis ? seriesData[0].axis : 'left'
|
|
41
|
-
|
|
42
|
-
|
|
62
|
+
let displayArea = legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1
|
|
63
|
+
const circleData = filterCircles(config.preliminaryData, rawData, seriesKey)
|
|
64
|
+
// styles for preliminary Data items
|
|
65
|
+
let styles = createStyles({ preliminaryData: config.preliminaryData, rawData, stroke: colorScale(config.runtime.seriesLabels[seriesKey]), handleLineType, lineType, seriesKey })
|
|
43
66
|
|
|
44
67
|
return (
|
|
45
68
|
<Group
|
|
46
69
|
key={`series-${seriesKey}`}
|
|
47
|
-
opacity={
|
|
48
|
-
display={
|
|
70
|
+
opacity={legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
|
|
71
|
+
display={legend.behavior === 'highlight' || (seriesHighlight.length === 0 && !legend.dynamicLegend) || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
|
|
49
72
|
>
|
|
50
73
|
{data.map((d, dataIndex) => {
|
|
51
74
|
// Find the series object from the config.series array that has a dataKey matching the seriesKey variable.
|
|
52
75
|
const series = config.series.find(({ dataKey }) => dataKey === seriesKey)
|
|
53
76
|
const { axis } = series
|
|
54
77
|
|
|
55
|
-
const xAxisValue = config.runtime.xAxis.type === 'date' ? formatDate(parseDate(d[config.runtime.xAxis.dataKey])) : d[config.runtime.xAxis.dataKey]
|
|
56
|
-
const yAxisValue = getYAxisData(d, seriesKey)
|
|
57
78
|
const hasMultipleSeries = Object.keys(config.runtime.seriesLabels).length > 1
|
|
58
79
|
const labeltype = axis === 'Right' ? 'rightLabel' : 'label'
|
|
59
80
|
let label = config.runtime.yAxis[labeltype]
|
|
@@ -74,54 +95,74 @@ const LineChart = ({ xScale, yScale, getXAxisData, getYAxisData, xMax, yMax, han
|
|
|
74
95
|
{formatNumber(d[seriesKey], 'left')}
|
|
75
96
|
</Text>
|
|
76
97
|
|
|
77
|
-
{
|
|
78
|
-
|
|
98
|
+
{(lineDatapointStyle === 'hidden' || lineDatapointStyle === 'always show') && (
|
|
99
|
+
<LineChartCircle
|
|
100
|
+
circleData={circleData}
|
|
101
|
+
data={data}
|
|
102
|
+
d={d}
|
|
103
|
+
config={config}
|
|
104
|
+
seriesKey={seriesKey}
|
|
105
|
+
displayArea={displayArea}
|
|
106
|
+
tooltipData={tooltipData}
|
|
107
|
+
xScale={xScale}
|
|
108
|
+
yScale={yScale}
|
|
109
|
+
colorScale={colorScale}
|
|
110
|
+
parseDate={parseDate}
|
|
111
|
+
yScaleRight={yScaleRight}
|
|
112
|
+
seriesAxis={seriesAxis}
|
|
113
|
+
key={`line-circle--${dataIndex}`}
|
|
114
|
+
/>
|
|
115
|
+
)}
|
|
79
116
|
</Group>
|
|
80
117
|
)
|
|
81
118
|
)
|
|
82
119
|
})}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
120
|
+
<>
|
|
121
|
+
{lineDatapointStyle === 'hover' && (
|
|
122
|
+
<LineChartCircle circleData={circleData} data={data} config={config} seriesKey={seriesKey} displayArea={displayArea} tooltipData={tooltipData} xScale={xScale} yScale={yScale} colorScale={colorScale} parseDate={parseDate} yScaleRight={yScaleRight} seriesAxis={seriesAxis} />
|
|
123
|
+
)}
|
|
124
|
+
</>
|
|
125
|
+
{/* STANDARD LINE */}
|
|
86
126
|
<LinePath
|
|
87
127
|
curve={allCurves[seriesData[0].lineType]}
|
|
88
128
|
data={data}
|
|
89
129
|
x={d => xScale(getXAxisData(d))}
|
|
90
|
-
y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey)))}
|
|
91
|
-
stroke={
|
|
92
|
-
config.customColors
|
|
93
|
-
? config.customColors[index]
|
|
94
|
-
: colorScale
|
|
95
|
-
? colorPalettes[config.palette][index]
|
|
96
|
-
: // fallback
|
|
97
|
-
'#000'
|
|
98
|
-
}
|
|
130
|
+
y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(Number(getYAxisData(d, seriesKey))))}
|
|
131
|
+
stroke={colorScale(config.runtime.seriesLabels[seriesKey])}
|
|
99
132
|
strokeWidth={2}
|
|
100
133
|
strokeOpacity={1}
|
|
134
|
+
shapeRendering='geometricPrecision'
|
|
101
135
|
strokeDasharray={lineType ? handleLineType(lineType) : 0}
|
|
102
136
|
defined={(item, i) => {
|
|
103
137
|
return item[seriesKey] !== '' && item[seriesKey] !== null && item[seriesKey] !== undefined
|
|
104
138
|
}}
|
|
105
139
|
/>
|
|
140
|
+
|
|
141
|
+
{/* circles for preliminaryData data */}
|
|
142
|
+
{circleData.map((d, i) => {
|
|
143
|
+
return <circle key={i} cx={xScale(getXAxisData(d))} cy={yScale(Number(getYAxisData(d, seriesKey)))} r={6} strokeWidth={2} stroke={colorScale ? colorScale(config.runtime.seriesLabels[seriesKey]) : '#000'} fill='#fff' />
|
|
144
|
+
})}
|
|
145
|
+
|
|
146
|
+
{/* ANIMATED LINE */}
|
|
106
147
|
{config.animate && (
|
|
107
148
|
<LinePath
|
|
108
149
|
className='animation'
|
|
109
150
|
curve={seriesData.lineType}
|
|
110
151
|
data={data}
|
|
111
152
|
x={d => xScale(getXAxisData(d))}
|
|
112
|
-
y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey)))}
|
|
153
|
+
y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(Number(getYAxisData(d, seriesKey))))}
|
|
113
154
|
stroke='#fff'
|
|
114
155
|
strokeWidth={3}
|
|
115
156
|
strokeOpacity={1}
|
|
116
157
|
shapeRendering='geometricPrecision'
|
|
117
158
|
strokeDasharray={lineType ? handleLineType(lineType) : 0}
|
|
118
159
|
defined={(item, i) => {
|
|
119
|
-
return
|
|
160
|
+
return item[seriesKey] !== '' && item[seriesKey] !== null && item[seriesKey] !== undefined
|
|
120
161
|
}}
|
|
121
162
|
/>
|
|
122
163
|
)}
|
|
123
164
|
{/* Render series labels at end if each line if selected in the editor */}
|
|
124
|
-
{
|
|
165
|
+
{showLineSeriesLabels &&
|
|
125
166
|
(config.runtime.lineSeriesKeys || config.runtime.seriesKeys).map(seriesKey => {
|
|
126
167
|
let lastDatum
|
|
127
168
|
for (let i = data.length - 1; i >= 0; i--) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useContext, useEffect, useRef, useState
|
|
1
|
+
import React, { useContext, useEffect, useRef, useState } from 'react'
|
|
2
2
|
|
|
3
3
|
// Libraries
|
|
4
4
|
import { AxisLeft, AxisBottom, AxisRight, AxisTop } from '@visx/axis'
|
|
@@ -6,14 +6,13 @@ import { Group } from '@visx/group'
|
|
|
6
6
|
import { Line, Bar } from '@visx/shape'
|
|
7
7
|
import { Text } from '@visx/text'
|
|
8
8
|
import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
9
|
-
import { useTooltip, TooltipWithBounds
|
|
9
|
+
import { useTooltip, TooltipWithBounds } from '@visx/tooltip'
|
|
10
10
|
|
|
11
11
|
// CDC Components
|
|
12
|
-
import AreaChart from './AreaChart'
|
|
13
|
-
import AreaChartStacked from './AreaChart.Stacked'
|
|
12
|
+
import { AreaChart, AreaChartStacked } from './AreaChart'
|
|
14
13
|
import BarChart from './BarChart'
|
|
15
14
|
import ConfigContext from '../ConfigContext'
|
|
16
|
-
import
|
|
15
|
+
import BoxPlot from './BoxPlot'
|
|
17
16
|
import ScatterPlot from './ScatterPlot'
|
|
18
17
|
import DeviationBar from './DeviationBar'
|
|
19
18
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
@@ -22,6 +21,7 @@ import LineChart from './LineChart'
|
|
|
22
21
|
import ForestPlot from './ForestPlot'
|
|
23
22
|
import PairedBarChart from './PairedBarChart'
|
|
24
23
|
import useIntersectionObserver from './../hooks/useIntersectionObserver'
|
|
24
|
+
import Regions from './Regions'
|
|
25
25
|
|
|
26
26
|
// Hooks
|
|
27
27
|
import useMinMax from '../hooks/useMinMax'
|
|
@@ -30,13 +30,33 @@ import useRightAxis from '../hooks/useRightAxis'
|
|
|
30
30
|
import useScales from '../hooks/useScales'
|
|
31
31
|
import useTopAxis from '../hooks/useTopAxis'
|
|
32
32
|
import { useTooltip as useCoveTooltip } from '../hooks/useTooltip'
|
|
33
|
+
import { useEditorPermissions } from './EditorPanel/useEditorPermissions'
|
|
33
34
|
|
|
34
35
|
// styles
|
|
35
|
-
import '
|
|
36
|
+
import ZoomBrush from './ZoomBrush'
|
|
36
37
|
|
|
37
38
|
const LinearChart = props => {
|
|
38
|
-
const {
|
|
39
|
-
|
|
39
|
+
const {
|
|
40
|
+
isEditor,
|
|
41
|
+
isDashboard,
|
|
42
|
+
computeMarginBottom,
|
|
43
|
+
transformedData: data,
|
|
44
|
+
dimensions,
|
|
45
|
+
config,
|
|
46
|
+
parseDate,
|
|
47
|
+
formatDate,
|
|
48
|
+
currentViewport,
|
|
49
|
+
formatNumber,
|
|
50
|
+
handleChartAriaLabels,
|
|
51
|
+
updateConfig,
|
|
52
|
+
handleLineType,
|
|
53
|
+
rawData,
|
|
54
|
+
capitalize,
|
|
55
|
+
setSharedFilter,
|
|
56
|
+
setSharedFilterValue,
|
|
57
|
+
getTextWidth,
|
|
58
|
+
isDebug
|
|
59
|
+
} = useContext(ConfigContext)
|
|
40
60
|
// todo: start destructuring this file for conciseness
|
|
41
61
|
const { visualizationType, visualizationSubType, orientation, xAxis, yAxis, runtime, debugSvg } = config
|
|
42
62
|
|
|
@@ -49,7 +69,7 @@ const LinearChart = props => {
|
|
|
49
69
|
}
|
|
50
70
|
// configure height , yMax, xMax
|
|
51
71
|
const { horizontal: heightHorizontal } = config.heights
|
|
52
|
-
const isHorizontal = orientation === 'horizontal'
|
|
72
|
+
const isHorizontal = orientation === 'horizontal' || config.visualizationType === 'Forest Plot'
|
|
53
73
|
const shouldAbbreviate = true
|
|
54
74
|
let height = config.aspectRatio ? width * config.aspectRatio : config.visualizationType === 'Forest Plot' ? config.heights['vertical'] : config.heights[orientation]
|
|
55
75
|
const xMax = width - runtime.yAxis.size - (visualizationType === 'Combo' ? config.yAxis.rightAxisSize : 0)
|
|
@@ -58,16 +78,18 @@ const LinearChart = props => {
|
|
|
58
78
|
if (config.visualizationType === 'Forest Plot') {
|
|
59
79
|
height = height + config.data.length * config.forestPlot.rowHeight
|
|
60
80
|
yMax = yMax + config.data.length * config.forestPlot.rowHeight
|
|
81
|
+
width = dimensions[0]
|
|
82
|
+
}
|
|
83
|
+
if (config.brush.active) {
|
|
84
|
+
height = height + config.brush.height
|
|
61
85
|
}
|
|
62
|
-
|
|
63
|
-
let dynamicMarginTop = 0 || config.dynamicMarginTop
|
|
64
|
-
const marginTop = 20
|
|
65
86
|
|
|
66
87
|
// hooks % states
|
|
67
88
|
const { minValue, maxValue, existPositiveValue, isAllLine } = useReduceData(config, data)
|
|
68
|
-
const {
|
|
89
|
+
const { visSupportsReactTooltip } = useEditorPermissions()
|
|
69
90
|
const { hasTopAxis } = useTopAxis(config)
|
|
70
91
|
const [animatedChart, setAnimatedChart] = useState(false)
|
|
92
|
+
const [point, setPoint] = useState({ x: 0, y: 0 })
|
|
71
93
|
|
|
72
94
|
// refs
|
|
73
95
|
const triggerRef = useRef()
|
|
@@ -79,11 +101,12 @@ const LinearChart = props => {
|
|
|
79
101
|
// getters & functions
|
|
80
102
|
const getXAxisData = d => (config.runtime.xAxis.type === 'date' ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
|
|
81
103
|
const getYAxisData = (d, seriesKey) => d[seriesKey]
|
|
82
|
-
const xAxisDataMapped = data.map(d => getXAxisData(d))
|
|
83
|
-
const section = config.orientation === 'horizontal' ? 'yAxis' : 'xAxis'
|
|
104
|
+
const xAxisDataMapped = config.brush.active && config.brush.data?.length ? config.brush.data.map(d => getXAxisData(d)) : data.map(d => getXAxisData(d))
|
|
105
|
+
const section = config.orientation === 'horizontal' || config.visualizationType === 'Forest Plot' ? 'yAxis' : 'xAxis'
|
|
84
106
|
const properties = { data, config, minValue, maxValue, isAllLine, existPositiveValue, xAxisDataMapped, xMax, yMax }
|
|
85
|
-
const { min, max } = useMinMax(properties)
|
|
86
|
-
const {
|
|
107
|
+
const { min, max, leftMax, rightMax } = useMinMax(properties)
|
|
108
|
+
const { yScaleRight, hasRightAxis } = useRightAxis({ config, yMax, data, updateConfig })
|
|
109
|
+
const { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding, xScaleBrush } = useScales({ ...properties, min, max, leftMax, rightMax, dimensions })
|
|
87
110
|
|
|
88
111
|
// sets the portal x/y for where tooltips should appear on the page.
|
|
89
112
|
const [chartPosition, setChartPosition] = useState(null)
|
|
@@ -109,9 +132,11 @@ const LinearChart = props => {
|
|
|
109
132
|
// when logarithmic scale applied change value FIRST of tick
|
|
110
133
|
tick = 0
|
|
111
134
|
}
|
|
112
|
-
|
|
113
|
-
if (
|
|
114
|
-
if (config.
|
|
135
|
+
|
|
136
|
+
if (runtime.xAxis.type === 'date' && config.visualizationType !== 'Forest Plot') return formatDate(tick)
|
|
137
|
+
if (orientation === 'horizontal' && config.visualizationType !== 'Forest Plot') return formatNumber(tick, 'left', shouldAbbreviate)
|
|
138
|
+
if (config.xAxis.type === 'continuous' && config.visualizationType !== 'Forest Plot') return formatNumber(tick, 'bottom', shouldAbbreviate)
|
|
139
|
+
if (config.visualizationType === 'Forest Plot') return formatNumber(tick, 'left', config.dataFormat.abbreviated, config.runtime.xAxis.prefix, config.runtime.xAxis.suffix, Number(config.dataFormat.roundTo))
|
|
115
140
|
return tick
|
|
116
141
|
}
|
|
117
142
|
|
|
@@ -147,7 +172,12 @@ const LinearChart = props => {
|
|
|
147
172
|
tickCount = 4 // same default as standalone components
|
|
148
173
|
}
|
|
149
174
|
}
|
|
175
|
+
|
|
176
|
+
if (config.visualizationType === 'Forest Plot') {
|
|
177
|
+
tickCount = config.yAxis.numTicks !== '' ? config.yAxis.numTicks : 4
|
|
178
|
+
}
|
|
150
179
|
}
|
|
180
|
+
|
|
151
181
|
return tickCount
|
|
152
182
|
}
|
|
153
183
|
|
|
@@ -206,61 +236,40 @@ const LinearChart = props => {
|
|
|
206
236
|
return countNumOfTicks('yAxis')
|
|
207
237
|
}
|
|
208
238
|
|
|
239
|
+
const onMouseMove = event => {
|
|
240
|
+
const svgRect = event.currentTarget.getBoundingClientRect()
|
|
241
|
+
const x = event.clientX - svgRect.left
|
|
242
|
+
const y = event.clientY - svgRect.top
|
|
243
|
+
|
|
244
|
+
setPoint({
|
|
245
|
+
x,
|
|
246
|
+
y
|
|
247
|
+
})
|
|
248
|
+
}
|
|
249
|
+
|
|
209
250
|
return isNaN(width) ? (
|
|
210
251
|
<React.Fragment></React.Fragment>
|
|
211
252
|
) : (
|
|
212
253
|
<ErrorBoundary component='LinearChart'>
|
|
213
254
|
{/* ! Notice - div needed for tooltip boundaries (flip/flop) */}
|
|
214
255
|
<div style={{ width: `${width}px`, height: `${height}px`, overflow: 'visible' }} className='tooltip-boundary'>
|
|
215
|
-
<svg
|
|
256
|
+
<svg
|
|
257
|
+
// onMouseLeave={() => setPoint(null)}
|
|
258
|
+
onMouseMove={onMouseMove}
|
|
259
|
+
width={'100%'}
|
|
260
|
+
height={'100%'}
|
|
261
|
+
className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${debugSvg && 'debug'}`}
|
|
262
|
+
role='img'
|
|
263
|
+
aria-label={handleChartAriaLabels(config)}
|
|
264
|
+
ref={svgRef}
|
|
265
|
+
style={{ overflow: 'visible' }}
|
|
266
|
+
>
|
|
216
267
|
<Bar width={width} height={height} fill={'transparent'}></Bar> {/* Highlighted regions */}
|
|
217
|
-
{config.regions
|
|
218
|
-
? config.regions.map(region => {
|
|
219
|
-
if (!Object.keys(region).includes('from') || !Object.keys(region).includes('to')) return null
|
|
220
|
-
|
|
221
|
-
let from
|
|
222
|
-
let to
|
|
223
|
-
let width
|
|
224
|
-
|
|
225
|
-
if (config.xAxis.type === 'date') {
|
|
226
|
-
from = xScale(parseDate(region.from).getTime())
|
|
227
|
-
to = xScale(parseDate(region.to).getTime())
|
|
228
|
-
width = to - from
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (config.xAxis.type === 'categorical') {
|
|
232
|
-
from = xScale(region.from)
|
|
233
|
-
to = xScale(region.to)
|
|
234
|
-
width = to - from
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (!from) return null
|
|
238
|
-
if (!to) return null
|
|
239
|
-
|
|
240
|
-
return (
|
|
241
|
-
<Group className='regions' left={Number(runtime.yAxis.size)} key={region.label}>
|
|
242
|
-
<path
|
|
243
|
-
stroke='#333'
|
|
244
|
-
d={`M${from} -5
|
|
245
|
-
L${from} 5
|
|
246
|
-
M${from} 0
|
|
247
|
-
L${to} 0
|
|
248
|
-
M${to} -5
|
|
249
|
-
L${to} 5`}
|
|
250
|
-
/>
|
|
251
|
-
<rect x={from} y={0} width={width} height={yMax} fill={region.background} opacity={0.3} />
|
|
252
|
-
<Text x={from + width / 2} y={5} fill={region.color} verticalAnchor='start' textAnchor='middle'>
|
|
253
|
-
{region.label}
|
|
254
|
-
</Text>
|
|
255
|
-
</Group>
|
|
256
|
-
)
|
|
257
|
-
})
|
|
258
|
-
: ''}
|
|
259
268
|
{/* Y axis */}
|
|
260
269
|
{!['Spark Line', 'Forest Plot'].includes(visualizationType) && (
|
|
261
270
|
<AxisLeft scale={yScale} tickLength={config.useLogScale ? 6 : 8} left={Number(runtime.yAxis.size) - config.yAxis.axisPadding} label={runtime.yAxis.label} stroke='#333' tickFormat={(tick, i) => handleLeftTickFormatting(tick, i)} numTicks={handleNumTicks()}>
|
|
262
271
|
{props => {
|
|
263
|
-
const axisCenter =
|
|
272
|
+
const axisCenter = config.orientation === 'horizontal' ? (props.axisToPoint.y - props.axisFromPoint.y) / 2 : (props.axisFromPoint.y - props.axisToPoint.y) / 2
|
|
264
273
|
const horizontalTickOffset = yMax / props.ticks.length / 2 - (yMax / props.ticks.length) * (1 - config.barThickness) + 5
|
|
265
274
|
return (
|
|
266
275
|
<Group className='left-axis'>
|
|
@@ -273,7 +282,7 @@ const LinearChart = props => {
|
|
|
273
282
|
|
|
274
283
|
return (
|
|
275
284
|
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
276
|
-
{!runtime.yAxis.hideTicks && <Line key={`${tick.value}--hide-hideTicks`} from={tick.from} to={config.useLogScale ? to : tick.to} stroke={config.yAxis.tickColor} display={
|
|
285
|
+
{!runtime.yAxis.hideTicks && <Line key={`${tick.value}--hide-hideTicks`} from={tick.from} to={config.useLogScale ? to : tick.to} stroke={config.yAxis.tickColor} display={orientation === 'horizontal' ? 'none' : 'block'} />}
|
|
277
286
|
|
|
278
287
|
{runtime.yAxis.gridLines ? <Line key={`${tick.value}--hide-hideGridLines`} display={(config.useLogScale && showTicks).toString()} from={{ x: tick.from.x + xMax, y: tick.from.y }} to={tick.from} stroke='rgba(0,0,0,0.3)' /> : ''}
|
|
279
288
|
|
|
@@ -336,7 +345,7 @@ const LinearChart = props => {
|
|
|
336
345
|
{hasRightAxis && (
|
|
337
346
|
<AxisRight scale={yScaleRight} left={Number(width - config.yAxis.rightAxisSize)} label={config.yAxis.rightLabel} tickFormat={tick => formatNumber(tick, 'right')} numTicks={runtime.yAxis.rightNumTicks || undefined} labelOffset={45}>
|
|
338
347
|
{props => {
|
|
339
|
-
const axisCenter =
|
|
348
|
+
const axisCenter = config.orientation === 'horizontal' ? (props.axisToPoint.y - props.axisFromPoint.y) / 2 : (props.axisFromPoint.y - props.axisToPoint.y) / 2
|
|
340
349
|
const horizontalTickOffset = yMax / props.ticks.length / 2 - (yMax / props.ticks.length) * (1 - config.barThickness) + 5
|
|
341
350
|
return (
|
|
342
351
|
<Group className='right-axis'>
|
|
@@ -380,7 +389,7 @@ const LinearChart = props => {
|
|
|
380
389
|
{visualizationType !== 'Paired Bar' && visualizationType !== 'Spark Line' && (
|
|
381
390
|
<AxisBottom
|
|
382
391
|
top={runtime.horizontal && config.visualizationType !== 'Forest Plot' ? Number(heightHorizontal) + Number(config.xAxis.axisPadding) : config.visualizationType === 'Forest Plot' ? yMax + Number(config.xAxis.axisPadding) : yMax + Number(config.xAxis.axisPadding)}
|
|
383
|
-
left={Number(runtime.yAxis.size)}
|
|
392
|
+
left={config.visualizationType !== 'Forest Plot' ? Number(runtime.yAxis.size) : 0}
|
|
384
393
|
label={runtime.xAxis.label}
|
|
385
394
|
tickFormat={handleBottomTickFormatting}
|
|
386
395
|
scale={xScale}
|
|
@@ -389,7 +398,7 @@ const LinearChart = props => {
|
|
|
389
398
|
tickStroke='#333'
|
|
390
399
|
>
|
|
391
400
|
{props => {
|
|
392
|
-
const axisCenter = config.visualizationType !== 'Forest Plot' ? (props.axisToPoint.x - props.axisFromPoint.x) / 2 :
|
|
401
|
+
const axisCenter = config.visualizationType !== 'Forest Plot' ? (props.axisToPoint.x - props.axisFromPoint.x) / 2 : dimensions[0] / 2
|
|
393
402
|
const containsMultipleWords = inputString => /\s/.test(inputString)
|
|
394
403
|
const ismultiLabel = props.ticks.some(tick => containsMultipleWords(tick.value))
|
|
395
404
|
|
|
@@ -422,11 +431,14 @@ const LinearChart = props => {
|
|
|
422
431
|
}
|
|
423
432
|
})
|
|
424
433
|
|
|
425
|
-
dynamicMarginTop = areTicksTouching && config.isResponsiveTicks ? tickWidthMax + defaultTickLength +
|
|
434
|
+
const dynamicMarginTop = areTicksTouching && config.isResponsiveTicks ? tickWidthMax + defaultTickLength + 20 : 0
|
|
435
|
+
const rotation = Number(config.xAxis.tickRotation) > 0 ? Number(config.xAxis.tickRotation) : 0
|
|
436
|
+
|
|
426
437
|
config.dynamicMarginTop = dynamicMarginTop
|
|
438
|
+
config.xAxis.tickWidthMax = tickWidthMax
|
|
427
439
|
|
|
428
440
|
return (
|
|
429
|
-
<Group className='bottom-axis'>
|
|
441
|
+
<Group className='bottom-axis' width={dimensions[0]}>
|
|
430
442
|
{props.ticks.map((tick, i, propsTicks) => {
|
|
431
443
|
// when using LogScale show major ticks values only
|
|
432
444
|
const showTick = String(tick.value).startsWith('1') || tick.value === 0.1 ? 'block' : 'none'
|
|
@@ -443,7 +455,7 @@ const LinearChart = props => {
|
|
|
443
455
|
|
|
444
456
|
return (
|
|
445
457
|
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
446
|
-
{!config.xAxis.hideTicks && <Line from={tick.from} to={orientation === 'horizontal' && config.useLogScale ? to : tick.to} stroke={config.xAxis.tickColor} strokeWidth={showTick === 'block' ? 1.3 : 1} />}
|
|
458
|
+
{!config.xAxis.hideTicks && <Line from={tick.from} to={orientation === 'horizontal' && config.useLogScale ? to : tick.to} stroke={config.xAxis.tickColor} strokeWidth={showTick === 'block' && config.useLogScale ? 1.3 : 1} />}
|
|
447
459
|
{!config.xAxis.hideLabel && (
|
|
448
460
|
<Text
|
|
449
461
|
dy={config.orientation === 'horizontal' && config.useLogScale ? 8 : 0}
|
|
@@ -463,7 +475,24 @@ const LinearChart = props => {
|
|
|
463
475
|
)
|
|
464
476
|
})}
|
|
465
477
|
{!config.xAxis.hideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
|
|
466
|
-
<Text
|
|
478
|
+
<Text
|
|
479
|
+
x={axisCenter}
|
|
480
|
+
y={
|
|
481
|
+
config.visualizationType === 'Forest Plot'
|
|
482
|
+
? config.xAxis.tickWidthMax + 40
|
|
483
|
+
: config.orientation === 'horizontal'
|
|
484
|
+
? dynamicMarginTop || config.xAxis.labelOffset
|
|
485
|
+
: config.isResponsiveTicks && dynamicMarginTop && !isHorizontal
|
|
486
|
+
? dynamicMarginTop
|
|
487
|
+
: Number(rotation) && !config.isResponsiveTicks && !isHorizontal
|
|
488
|
+
? Number(rotation + tickWidthMax / 1.3)
|
|
489
|
+
: Number(config.xAxis.labelOffset)
|
|
490
|
+
}
|
|
491
|
+
textAnchor='middle'
|
|
492
|
+
verticalAnchor='start'
|
|
493
|
+
fontWeight='bold'
|
|
494
|
+
fill={config.xAxis.labelColor}
|
|
495
|
+
>
|
|
467
496
|
{props.label}
|
|
468
497
|
</Text>
|
|
469
498
|
</Group>
|
|
@@ -537,7 +566,7 @@ const LinearChart = props => {
|
|
|
537
566
|
</AxisBottom>
|
|
538
567
|
</>
|
|
539
568
|
)}
|
|
540
|
-
{visualizationType === 'Deviation Bar' && <DeviationBar animatedChart={animatedChart} xScale={xScale} yScale={yScale} width={xMax} height={yMax} />}
|
|
569
|
+
{visualizationType === 'Deviation Bar' && config.series?.length === 1 && <DeviationBar animatedChart={animatedChart} xScale={xScale} yScale={yScale} width={xMax} height={yMax} />}
|
|
541
570
|
{visualizationType === 'Paired Bar' && <PairedBarChart originalWidth={width} width={xMax} height={yMax} />}
|
|
542
571
|
{visualizationType === 'Scatter Plot' && (
|
|
543
572
|
<ScatterPlot
|
|
@@ -554,7 +583,7 @@ const LinearChart = props => {
|
|
|
554
583
|
showTooltip={showTooltip}
|
|
555
584
|
/>
|
|
556
585
|
)}
|
|
557
|
-
{visualizationType === 'Box Plot' && <
|
|
586
|
+
{visualizationType === 'Box Plot' && <BoxPlot xScale={xScale} yScale={yScale} />}
|
|
558
587
|
{((visualizationType === 'Area Chart' && config.visualizationSubType === 'regular') || visualizationType === 'Combo') && (
|
|
559
588
|
<AreaChart xScale={xScale} yScale={yScale} yMax={yMax} xMax={xMax} chartRef={svgRef} width={xMax} height={yMax} handleTooltipMouseOver={handleTooltipMouseOver} handleTooltipMouseOff={handleTooltipMouseOff} tooltipData={tooltipData} showTooltip={showTooltip} />
|
|
560
589
|
)}
|
|
@@ -624,10 +653,8 @@ const LinearChart = props => {
|
|
|
624
653
|
xScale={xScale}
|
|
625
654
|
yScale={yScale}
|
|
626
655
|
seriesScale={seriesScale}
|
|
627
|
-
width={
|
|
628
|
-
height={
|
|
629
|
-
maxWidth={width}
|
|
630
|
-
maxHeight={height}
|
|
656
|
+
width={width}
|
|
657
|
+
height={height}
|
|
631
658
|
getXAxisData={getXAxisData}
|
|
632
659
|
getYAxisData={getYAxisData}
|
|
633
660
|
animatedChart={animatedChart}
|
|
@@ -641,6 +668,8 @@ const LinearChart = props => {
|
|
|
641
668
|
config={config}
|
|
642
669
|
/>
|
|
643
670
|
)}
|
|
671
|
+
{/*Zoom Brush */}
|
|
672
|
+
{['Line', 'Bar', 'Combo', 'Area Chart'].includes(config.visualizationType) && !isHorizontal && <ZoomBrush xScaleBrush={xScaleBrush} yScale={yScale} xMax={xMax} yMax={yMax} />}
|
|
644
673
|
{/* Line chart */}
|
|
645
674
|
{/* TODO: Make this just line or combo? */}
|
|
646
675
|
{visualizationType !== 'Bar' && visualizationType !== 'Paired Bar' && visualizationType !== 'Box Plot' && visualizationType !== 'Area Chart' && visualizationType !== 'Scatter Plot' && visualizationType !== 'Deviation Bar' && visualizationType !== 'Forecasting' && (
|
|
@@ -699,6 +728,11 @@ const LinearChart = props => {
|
|
|
699
728
|
/>
|
|
700
729
|
)
|
|
701
730
|
})}
|
|
731
|
+
{/* we are handling regions in bar charts differently, so that we can calculate the bar group into the region space. */}
|
|
732
|
+
{/* prettier-ignore */}
|
|
733
|
+
{config.visualizationType !== 'Bar' && config.visualizationType !== 'Combo' && (
|
|
734
|
+
<Regions xScale={xScale} handleTooltipClick={handleTooltipClick} handleTooltipMouseOff={handleTooltipMouseOff} handleTooltipMouseOver={handleTooltipMouseOver} showTooltip={showTooltip} hideTooltip={hideTooltip} tooltipData={tooltipData} yMax={yMax} width={width} />
|
|
735
|
+
)}
|
|
702
736
|
{chartHasTooltipGuides && showTooltip && tooltipData && config.visual.verticalHoverLine && (
|
|
703
737
|
<Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
|
|
704
738
|
<Line from={{ x: tooltipData.dataXPosition - 10, y: 0 }} to={{ x: tooltipData.dataXPosition - 10, y: yMax }} stroke={'black'} strokeWidth={1} pointerEvents='none' strokeDasharray='5,5' className='vertical-tooltip-line' />
|
|
@@ -714,8 +748,18 @@ const LinearChart = props => {
|
|
|
714
748
|
{config.chartMessage.noData}
|
|
715
749
|
</Text>
|
|
716
750
|
)}
|
|
751
|
+
{config.visualizationType === 'Bar' && config.tooltips.singleSeries && config.visual.horizontalHoverLine && (
|
|
752
|
+
<Group key='tooltipLine-horizontal' className='horizontal-tooltip-line' left={config.yAxis.size ? config.yAxis.size : 0}>
|
|
753
|
+
<Line from={{ x: 0, y: point.y }} to={{ x: xMax, y: point.y }} stroke={'black'} strokeWidth={1} pointerEvents='none' strokeDasharray='5,5' className='horizontal-tooltip-line' />
|
|
754
|
+
</Group>
|
|
755
|
+
)}
|
|
756
|
+
{config.visualizationType === 'Bar' && config.tooltips.singleSeries && config.visual.verticalHoverLine && (
|
|
757
|
+
<Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
|
|
758
|
+
<Line from={{ x: point.x, y: 0 }} to={{ x: point.x, y: yMax }} stroke={'black'} strokeWidth={1} pointerEvents='none' strokeDasharray='5,5' className='vertical-tooltip-line' />
|
|
759
|
+
</Group>
|
|
760
|
+
)}
|
|
717
761
|
</svg>
|
|
718
|
-
{tooltipData && Object.entries(tooltipData.data).length > 0 && tooltipOpen && showTooltip && tooltipData.dataYPosition && tooltipData.dataXPosition && (
|
|
762
|
+
{tooltipData && Object.entries(tooltipData.data).length > 0 && tooltipOpen && showTooltip && tooltipData.dataYPosition && tooltipData.dataXPosition && !config.tooltips.singleSeries && (
|
|
719
763
|
<>
|
|
720
764
|
<style>{`.tooltip {background-color: rgba(255,255,255, ${config.tooltips.opacity / 100}) !important`}</style>
|
|
721
765
|
<TooltipWithBounds key={Math.random()} className={'tooltip cdc-open-viz-module'} left={tooltipLeft} top={tooltipTop}>
|
|
@@ -723,9 +767,8 @@ const LinearChart = props => {
|
|
|
723
767
|
</TooltipWithBounds>
|
|
724
768
|
</>
|
|
725
769
|
)}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
)}
|
|
770
|
+
|
|
771
|
+
{visSupportsReactTooltip() && <ReactTooltip id={`cdc-open-viz-tooltip-${runtime.uniqueId}`} variant='light' arrowColor='rgba(0,0,0,0)' className='tooltip' style={{ background: `rgba(255,255,255, ${config.tooltips.opacity / 100})`, color: 'black' }} />}
|
|
729
772
|
<div className='animation-trigger' ref={triggerRef} />
|
|
730
773
|
</div>
|
|
731
774
|
</ErrorBoundary>
|
|
@@ -47,11 +47,11 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
47
47
|
// Set label color
|
|
48
48
|
let labelColor = '#000000'
|
|
49
49
|
|
|
50
|
-
if (chroma.contrast(labelColor, groupOne.color) < 4.9) {
|
|
50
|
+
if (groupOne.color && chroma.contrast(labelColor, groupOne.color) < 4.9) {
|
|
51
51
|
groupOne.labelColor = '#FFFFFF'
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
if (chroma.contrast(labelColor, groupTwo.color) < 4.9) {
|
|
54
|
+
if (groupTwo.color && chroma.contrast(labelColor, groupTwo.color) < 4.9) {
|
|
55
55
|
groupTwo.labelColor = '#FFFFFF'
|
|
56
56
|
}
|
|
57
57
|
|