@cdc/chart 4.23.3 → 4.23.5
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 +52543 -50830
- package/examples/feature/__data__/area-chart.json +56 -0
- package/examples/{planet-example-data.json → feature/__data__/planet-example-data.json} +3 -8
- package/examples/feature/__data__/planet-logaritmic-data.json +56 -0
- package/examples/feature/area/area-chart-category.json +240 -0
- package/examples/{area-chart.json → feature/area/area-chart-date.json} +70 -13
- package/examples/feature/bar/example-bar-chart.json +558 -0
- package/examples/{horizontal-chart-max-increase.json → feature/bar/horizontal-chart-max-increase.json} +10 -4
- package/examples/{horizontal-chart.json → feature/bar/horizontal-chart.json} +10 -4
- package/examples/{horizontal-stacked-bar-chart.json → feature/bar/horizontal-stacked-bar-chart.json} +7 -3
- package/examples/{planet-chart-horizontal-example-config.json → feature/bar/planet-chart-horizontal-example-config.json} +8 -3
- package/examples/feature/bar/planet-chart-logaritmic-config.json +170 -0
- package/examples/{planet-example-config.json → feature/bar/planet-example-config.json} +2 -2
- package/examples/{box-plot.json → feature/boxplot/boxplot.json} +7 -7
- package/examples/feature/boxplot/testing.csv +38 -0
- package/examples/feature/boxplot/valid-boxplot.csv +17 -0
- package/examples/feature/combo/combochart-categories_are_numbers .json +18 -0
- package/examples/{planet-combo-example-config.json → feature/combo/planet-combo-example-config.json} +1 -1
- package/examples/{planet-deviation-config.json → feature/deviation/planet-deviation-config.json} +2 -2
- package/examples/{planet-deviation-data.json → feature/deviation/planet-deviation-data.json} +9 -9
- package/examples/feature/filters/filter-testing.json +212 -0
- package/examples/feature/forecasting/case_date_example.csv +130 -0
- package/examples/feature/forecasting/effective_reproduction.json +202 -0
- package/examples/feature/forecasting/r_data.csv +130 -0
- package/examples/feature/forecasting/random_data.csv +366 -0
- package/examples/feature/line/line-chart.json +124 -0
- package/examples/{paired-bar-example.json → feature/paired-bar/paired-bar-example.json} +10 -4
- package/examples/{planet-pie-example-config.json → feature/pie/planet-pie-example-config.json} +2 -2
- package/examples/{scatterplot.json → feature/scatterplot/scatterplot.json} +1 -1
- package/examples/feature/test-highlight/test-highlight-2.json +789 -0
- package/examples/feature/test-highlight/test-highlight-vertical.json +561 -0
- package/examples/feature/test-highlight/test-highlight.json +100 -0
- package/examples/{case-rate-example-config.json → feature/tests-case-rate/case-rate-example-config.json} +2 -2
- package/examples/{covid-confidence-example-config.json → feature/tests-covid/covid-confidence-example-config.json} +8 -3
- package/examples/{covid-example-config.json → feature/tests-covid/covid-example-config.json} +7 -3
- package/examples/{cutoff-example-config.json → feature/tests-cutoff/cutoff-example-config.json} +7 -3
- package/examples/{date-exclusions-config.json → feature/tests-date-exclusions/date-exclusions-config.json} +2 -2
- package/examples/{example-bar-chart-nonnumeric.json → feature/tests-non-numerics/example-bar-chart-nonnumeric.json} +1 -1
- package/examples/{planet-pie-example-config-nonnumeric.json → feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json} +2 -2
- package/examples/{sparkline-chart-nonnumeric.json → feature/tests-non-numerics/sparkline-chart-nonnumeric.json} +1 -1
- package/examples/{stacked-vertical-bar-example-nonnumerics.json → feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json} +1 -2
- package/examples/gallery/bar-chart-horizontal/horizontal-highlight.json +345 -0
- package/examples/gallery/bar-chart-vertical/combo-line-chart.json +145 -7
- package/examples/gallery/paired-bar/paired-bar-chart.json +1 -0
- package/index.html +73 -49
- package/package.json +2 -2
- package/src/CdcChart.jsx +405 -40
- package/src/components/AreaChart.jsx +122 -80
- package/src/components/BarChart.jsx +126 -49
- package/src/components/BoxPlot.jsx +28 -20
- package/src/components/DataTable.jsx +7 -6
- package/src/components/DeviationBar.jsx +34 -34
- package/src/components/EditorPanel.jsx +1332 -352
- package/src/components/Legend.jsx +40 -4
- package/src/components/LineChart.jsx +10 -23
- package/src/components/LinearChart.jsx +133 -286
- package/src/components/PairedBarChart.jsx +6 -6
- package/src/components/PieChart.jsx +2 -4
- package/src/components/SparkLine.jsx +6 -42
- package/src/data/initial-state.js +23 -4
- package/src/hooks/useHighlightedBars.js +154 -0
- package/src/hooks/useMinMax.js +92 -0
- package/src/hooks/useReduceData.js +31 -57
- package/src/hooks/useScales.js +202 -0
- package/src/index.jsx +2 -1
- package/src/scss/editor-panel.scss +15 -0
- package/src/scss/main.scss +8 -6
- package/examples/box-plot.csv +0 -5
- package/examples/dynamic-legends.json +0 -125
- package/examples/example-bar-chart.json +0 -36
- package/examples/line-chart.json +0 -34
- package/examples/temp-example-config.json +0 -64
- package/examples/temp-example-data.json +0 -130
- package/src/components/Filters.jsx +0 -126
- /package/examples/{age-adjusted-rates.json → feature/__data__/age-adjusted-rates.json} +0 -0
- /package/examples/{new-data.csv → feature/__data__/new-data.csv} +0 -0
- /package/examples/{planet-example-data-max-increase.json → feature/__data__/planet-example-data-max-increase.json} +0 -0
- /package/examples/{Barchart_with_negative.json → feature/bar/Barchart_with_negative.json} +0 -0
- /package/examples/{stacked-vertical-bar-example-negative.json → feature/bar/stacked-vertical-bar-example-negative.json} +0 -0
- /package/examples/{stacked-vertical-bar-example.json → feature/bar/stacked-vertical-bar-example.json} +0 -0
- /package/examples/{box-plot-data.json → feature/boxplot/box-plot-data.json} +0 -0
- /package/examples/{newdata.json → feature/boxplot/boxplot-data.json} +0 -0
- /package/examples/{line-chart-max-increase.json → feature/line/line-chart-max-increase.json} +0 -0
- /package/examples/{paired-bar-data.json → feature/paired-bar/paired-bar-data.json} +0 -0
- /package/examples/{paired-bar-formatted.json → feature/paired-bar/paired-bar-formatted.json} +0 -0
- /package/examples/{scatterplot-continuous.csv → feature/scatterplot/scatterplot-continuous.csv} +0 -0
- /package/examples/{example-sparkline.json → feature/sparkline/example-sparkline.json} +0 -0
- /package/examples/{big-small-test-bar.json → feature/tests-big-small/big-small-test-bar.json} +0 -0
- /package/examples/{big-small-test-line.json → feature/tests-big-small/big-small-test-line.json} +0 -0
- /package/examples/{big-small-test-negative.json → feature/tests-big-small/big-small-test-negative.json} +0 -0
- /package/examples/{case-rate-example-data.json → feature/tests-case-rate/case-rate-example-data.json} +0 -0
- /package/examples/{covid-example-data-confidence.json → feature/tests-covid/covid-example-data-confidence.json} +0 -0
- /package/examples/{covid-example-data.json → feature/tests-covid/covid-example-data.json} +0 -0
- /package/examples/{cutoff-example-data.json → feature/tests-cutoff/cutoff-example-data.json} +0 -0
- /package/examples/{date-exclusions-data.json → feature/tests-date-exclusions/date-exclusions-data.json} +0 -0
- /package/examples/{example-combo-bar-nonnumeric.json → feature/tests-non-numerics/example-combo-bar-nonnumeric.json} +0 -0
- /package/examples/{line-chart-nonnumeric.json → feature/tests-non-numerics/line-chart-nonnumeric.json} +0 -0
- /package/examples/{planet-example-data-nonnumeric.json → feature/tests-non-numerics/planet-example-data-nonnumeric.json} +0 -0
|
@@ -35,43 +35,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
35
35
|
const isMaxValid = Number(enteredMaxValue) >= Number(maxValue)
|
|
36
36
|
const isMinValid = Number(enteredMinValue) <= Number(minValue)
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
// Examples: NA, N/A, "1,234", "anystring"
|
|
40
|
-
// - if you dont call this on data into LineGroup below, for example
|
|
41
|
-
// then entire data series are removed because of the defined statement
|
|
42
|
-
// i.e. if a series has any bad data points the entire series wont plot
|
|
43
|
-
const cleanData = (data, testing = false) => {
|
|
44
|
-
let cleanedup = []
|
|
45
|
-
if (testing) console.log('## Data to clean=', data)
|
|
46
|
-
data.forEach(function (d, i) {
|
|
47
|
-
let cleanedSeries = {}
|
|
48
|
-
Object.keys(d).forEach(function (key) {
|
|
49
|
-
if (key === 'Date') {
|
|
50
|
-
// pass thru the dates
|
|
51
|
-
cleanedSeries[key] = d[key]
|
|
52
|
-
} else {
|
|
53
|
-
// remove comma and dollar signs
|
|
54
|
-
let tmp = d[key] !== null && d[key] !== '' ? d[key].replace(/[,$]/g, '') : ''
|
|
55
|
-
if (testing) console.log('tmp no comma or $', tmp)
|
|
56
|
-
if ((tmp !== '' && tmp !== null && !isNaN(tmp)) || (tmp !== '' && tmp !== null && /\d+\.?\d*/.test(tmp))) {
|
|
57
|
-
cleanedSeries[key] = tmp
|
|
58
|
-
} else {
|
|
59
|
-
// return nothing to skip bad data point
|
|
60
|
-
cleanedSeries[key] = '' // returning blank fixes broken chart draw
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
})
|
|
64
|
-
cleanedup.push(cleanedSeries)
|
|
65
|
-
})
|
|
66
|
-
if (testing) console.log('## cleanedData =', cleanedup)
|
|
67
|
-
return cleanedup
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Just do this once up front otherwise we end up
|
|
71
|
-
// calling clean several times on same set of data (TT)
|
|
72
|
-
const cleanedData = cleanData(data, config.xAxis.dataKey)
|
|
73
|
-
|
|
74
|
-
if (cleanedData) {
|
|
38
|
+
if (data) {
|
|
75
39
|
let min = enteredMinValue && isMinValid ? enteredMinValue : minValue
|
|
76
40
|
let max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
|
|
77
41
|
|
|
@@ -86,7 +50,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
86
50
|
max += paddingValue
|
|
87
51
|
}
|
|
88
52
|
|
|
89
|
-
let xAxisDataMapped =
|
|
53
|
+
let xAxisDataMapped = data.map(d => getXAxisData(d))
|
|
90
54
|
|
|
91
55
|
if (config.runtime.horizontal) {
|
|
92
56
|
xScale = scaleLinear({
|
|
@@ -128,7 +92,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
128
92
|
return (
|
|
129
93
|
<ErrorBoundary component='SparkLine'>
|
|
130
94
|
<svg role='img' aria-label={handleChartAriaLabels(config)} width={width} height={height} className={'sparkline'} tabIndex={0}>
|
|
131
|
-
{config.runtime.lineSeriesKeys
|
|
95
|
+
{config.runtime.lineSeriesKeys?.length > 0
|
|
132
96
|
? config.runtime.lineSeriesKeys
|
|
133
97
|
: config.runtime.seriesKeys.map((seriesKey, index) => (
|
|
134
98
|
<>
|
|
@@ -141,7 +105,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
141
105
|
opacity={config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
|
|
142
106
|
display={config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
|
|
143
107
|
>
|
|
144
|
-
{
|
|
108
|
+
{data.map((d, dataIndex) => {
|
|
145
109
|
let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${formatNumber(getYAxisData(d, seriesKey))}` : formatNumber(getYAxisData(d, seriesKey))
|
|
146
110
|
let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${d[config.runtime.xAxis.dataKey]}` : d[config.runtime.xAxis.dataKey]
|
|
147
111
|
|
|
@@ -175,7 +139,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
175
139
|
})}
|
|
176
140
|
<LinePath
|
|
177
141
|
curve={allCurves.curveLinear}
|
|
178
|
-
data={
|
|
142
|
+
data={data}
|
|
179
143
|
x={d => xScale(getXAxisData(d))}
|
|
180
144
|
y={d => yScale(getYAxisData(d, seriesKey))}
|
|
181
145
|
stroke={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
|
|
@@ -201,7 +165,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
|
|
|
201
165
|
hideTicks
|
|
202
166
|
scale={xScale}
|
|
203
167
|
tickValues={handleSparkLineTicks}
|
|
204
|
-
tickFormat={formatDate}
|
|
168
|
+
tickFormat={tick => (config.xAxis.type === 'date' ? formatDate(tick) : null)}
|
|
205
169
|
stroke={'black'}
|
|
206
170
|
tickStroke={'black'}
|
|
207
171
|
tickLabelProps={() => ({
|
|
@@ -2,6 +2,7 @@ export default {
|
|
|
2
2
|
type: 'chart',
|
|
3
3
|
title: '',
|
|
4
4
|
showTitle: true,
|
|
5
|
+
showDownloadMediaButton: false,
|
|
5
6
|
theme: 'theme-blue',
|
|
6
7
|
animate: false,
|
|
7
8
|
fontSize: 'medium',
|
|
@@ -14,6 +15,9 @@ export default {
|
|
|
14
15
|
barStyle: '',
|
|
15
16
|
roundingStyle: 'standard',
|
|
16
17
|
tipRounding: 'top',
|
|
18
|
+
general: {
|
|
19
|
+
showDownloadButton: false
|
|
20
|
+
},
|
|
17
21
|
padding: {
|
|
18
22
|
left: 5,
|
|
19
23
|
right: 5
|
|
@@ -40,7 +44,8 @@ export default {
|
|
|
40
44
|
rightAxisTickColor: '#333',
|
|
41
45
|
numTicks: '',
|
|
42
46
|
axisPadding: 0,
|
|
43
|
-
tickRotation: 0
|
|
47
|
+
tickRotation: 0,
|
|
48
|
+
anchors: []
|
|
44
49
|
},
|
|
45
50
|
boxplot: {
|
|
46
51
|
plots: [],
|
|
@@ -67,7 +72,9 @@ export default {
|
|
|
67
72
|
iqr: 'Interquartile Range',
|
|
68
73
|
total: 'Total',
|
|
69
74
|
outliers: 'Outliers',
|
|
70
|
-
values: 'Values'
|
|
75
|
+
values: 'Values',
|
|
76
|
+
lowerBounds: 'Lower Bounds',
|
|
77
|
+
upperBounds: 'Upper Bounds'
|
|
71
78
|
}
|
|
72
79
|
},
|
|
73
80
|
topAxis: {
|
|
@@ -82,6 +89,7 @@ export default {
|
|
|
82
89
|
horizontal: 750
|
|
83
90
|
},
|
|
84
91
|
xAxis: {
|
|
92
|
+
anchors: [],
|
|
85
93
|
type: 'categorical',
|
|
86
94
|
showTargetLabel: true,
|
|
87
95
|
targetLabel: 'Target',
|
|
@@ -106,9 +114,16 @@ export default {
|
|
|
106
114
|
limitHeight: false,
|
|
107
115
|
height: '',
|
|
108
116
|
caption: '',
|
|
109
|
-
showDownloadUrl: false
|
|
117
|
+
showDownloadUrl: false,
|
|
118
|
+
showDataTableLink: true,
|
|
119
|
+
indexLabel: '',
|
|
120
|
+
download: false,
|
|
121
|
+
showVertical: true
|
|
110
122
|
},
|
|
111
123
|
orientation: 'vertical',
|
|
124
|
+
color: 'pinkpurple',
|
|
125
|
+
columns: { // start with a blank list
|
|
126
|
+
},
|
|
112
127
|
legend: {
|
|
113
128
|
behavior: 'isolate',
|
|
114
129
|
singleRow: false,
|
|
@@ -146,5 +161,9 @@ export default {
|
|
|
146
161
|
border: true,
|
|
147
162
|
accent: true,
|
|
148
163
|
background: true
|
|
149
|
-
}
|
|
164
|
+
},
|
|
165
|
+
useLogScale: false,
|
|
166
|
+
filterBehavior: 'Filter Change',
|
|
167
|
+
highlightedBarValues: [],
|
|
168
|
+
series: []
|
|
150
169
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import ConfigContext from '../ConfigContext'
|
|
3
|
+
|
|
4
|
+
export const useHighlightedBars = (config, updateConfig) => {
|
|
5
|
+
const { formatDate, parseDate } = useContext(ConfigContext)
|
|
6
|
+
|
|
7
|
+
let highlightedSeries = [] // only allow single series for highlights
|
|
8
|
+
let highlightedSeriesKey = ''
|
|
9
|
+
let highlightedBarValues = []
|
|
10
|
+
let highlightedSeriesValues = []
|
|
11
|
+
|
|
12
|
+
if (config.series?.length > 0 && config.data) {
|
|
13
|
+
highlightedSeries = config.series[0] // only allow single series for highlights
|
|
14
|
+
highlightedSeriesKey = config.series[0].dataKey
|
|
15
|
+
highlightedBarValues = config.highlightedBarValues
|
|
16
|
+
highlightedSeriesValues = config.data.map(item => item[config.xAxis.dataKey])
|
|
17
|
+
} else {
|
|
18
|
+
highlightedSeries = [] // only allow single series for highlights
|
|
19
|
+
highlightedSeriesKey = ''
|
|
20
|
+
highlightedBarValues = []
|
|
21
|
+
highlightedSeriesValues = []
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const handleUpdateHighlightedBorderWidth = (e, index) => {
|
|
25
|
+
const copyOfHighlightedBarValues = [...config.highlightedBarValues]
|
|
26
|
+
copyOfHighlightedBarValues[index].borderWidth = e.target.value
|
|
27
|
+
|
|
28
|
+
updateConfig({
|
|
29
|
+
...config,
|
|
30
|
+
highlightedBarValues: copyOfHighlightedBarValues
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const handleUpdateHighlightedBar = (e, index) => {
|
|
35
|
+
e.preventDefault()
|
|
36
|
+
|
|
37
|
+
const copyOfHighlightedBarValues = [...config.highlightedBarValues]
|
|
38
|
+
|
|
39
|
+
copyOfHighlightedBarValues[index].value = e.target.value
|
|
40
|
+
copyOfHighlightedBarValues[index].dataKey = highlightedSeriesKey
|
|
41
|
+
|
|
42
|
+
updateConfig({
|
|
43
|
+
...config,
|
|
44
|
+
highlightedBarValues: copyOfHighlightedBarValues
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const handleAddNewHighlightedBar = (e, index) => {
|
|
49
|
+
e.preventDefault()
|
|
50
|
+
const copyOfHighlightedBarValues = [...config.highlightedBarValues]
|
|
51
|
+
copyOfHighlightedBarValues.push({ dataKey: highlightedSeriesKey })
|
|
52
|
+
|
|
53
|
+
updateConfig({
|
|
54
|
+
...config,
|
|
55
|
+
highlightedBarValues: copyOfHighlightedBarValues
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const handleRemoveHighlightedBar = (e, index) => {
|
|
60
|
+
e.preventDefault()
|
|
61
|
+
const copyOfHighlightedBarValues = [...config.highlightedBarValues]
|
|
62
|
+
copyOfHighlightedBarValues.splice(index, 1)
|
|
63
|
+
updateConfig({
|
|
64
|
+
...config,
|
|
65
|
+
highlightedBarValues: copyOfHighlightedBarValues
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const handleUpdateHighlightedBarColor = (e, index) => {
|
|
70
|
+
const copyOfHighlightedBarValues = [...config.highlightedBarValues]
|
|
71
|
+
copyOfHighlightedBarValues[index].color = e.target.value
|
|
72
|
+
|
|
73
|
+
updateConfig({
|
|
74
|
+
...config
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const handleHighlightedBarLegendLabel = (e, index) => {
|
|
79
|
+
const copyOfHighlightedBarValues = [...config.highlightedBarValues]
|
|
80
|
+
copyOfHighlightedBarValues[index].legendLabel = e.target.value
|
|
81
|
+
|
|
82
|
+
updateConfig({
|
|
83
|
+
...config,
|
|
84
|
+
copyOfHighlightedBarValues
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const HighLightedBarUtils = () => {}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {string} formattedValue - The value to check for.
|
|
92
|
+
* @param {Array<string>} highlightedBarValuesIn - An array of highlighted bar values.
|
|
93
|
+
* @param {string} labelColor - The default label color.
|
|
94
|
+
* @returns {string} - Returns the font color for the given value.
|
|
95
|
+
*/
|
|
96
|
+
HighLightedBarUtils.checkFontColor = (formattedValue, highlightedBarValuesIn, labelColor) => {
|
|
97
|
+
if (config.xAxis.type === 'date') {
|
|
98
|
+
if (HighLightedBarUtils.formatDates(highlightedBarValuesIn).includes(formattedValue)) {
|
|
99
|
+
return '#000'
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
if (highlightedBarValuesIn.includes(formattedValue)) {
|
|
103
|
+
return '#000'
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return labelColor
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Formats an array of date values using the formatDate and parseDate functions.
|
|
111
|
+
*
|
|
112
|
+
* @param {string[]} highlightedBarValues - An array of date values to format.
|
|
113
|
+
* @returns {?Date[]} - An array of formatted date objects, or null if invalid input is provided.
|
|
114
|
+
*/
|
|
115
|
+
HighLightedBarUtils.formatDates = highlightedBarValues => {
|
|
116
|
+
return highlightedBarValues.map(dateItem => {
|
|
117
|
+
if (!dateItem) return false
|
|
118
|
+
return formatDate(parseDate(dateItem))
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Finds duplicate objects in an array based on the legendLabel property.
|
|
124
|
+
*
|
|
125
|
+
* @param {Array} arr - The array of objects to be checked.
|
|
126
|
+
* @return {Array} - An array of arrays, each containing objects that have the same legendLabel property.
|
|
127
|
+
*/
|
|
128
|
+
HighLightedBarUtils.findDuplicates = arr => {
|
|
129
|
+
const duplicates = {}
|
|
130
|
+
const result = arr.filter(obj => {
|
|
131
|
+
const { legendLabel } = obj
|
|
132
|
+
if (!duplicates[legendLabel]) {
|
|
133
|
+
duplicates[legendLabel] = true
|
|
134
|
+
return true
|
|
135
|
+
}
|
|
136
|
+
return false
|
|
137
|
+
})
|
|
138
|
+
return result
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
HighLightedBarUtils,
|
|
143
|
+
highlightedSeries,
|
|
144
|
+
highlightedSeriesKey,
|
|
145
|
+
highlightedBarValues,
|
|
146
|
+
highlightedSeriesValues,
|
|
147
|
+
handleUpdateHighlightedBar,
|
|
148
|
+
handleAddNewHighlightedBar,
|
|
149
|
+
handleRemoveHighlightedBar,
|
|
150
|
+
handleUpdateHighlightedBarColor,
|
|
151
|
+
handleHighlightedBarLegendLabel,
|
|
152
|
+
handleUpdateHighlightedBorderWidth
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAllLine }) => {
|
|
2
|
+
let min = 0
|
|
3
|
+
let max = 0
|
|
4
|
+
|
|
5
|
+
if (!data) {
|
|
6
|
+
return { min, max }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { max: enteredMaxValue, min: enteredMinValue } = config.runtime.yAxis
|
|
10
|
+
|
|
11
|
+
// do validation bafore applying t0 charts
|
|
12
|
+
const isMaxValid = existPositiveValue ? enteredMaxValue >= maxValue : enteredMaxValue >= 0
|
|
13
|
+
const isMinValid = config.useLogScale ? enteredMinValue >= 0 : (enteredMinValue <= 0 && minValue >= 0) || (enteredMinValue <= minValue && minValue < 0)
|
|
14
|
+
|
|
15
|
+
min = enteredMinValue && isMinValid ? enteredMinValue : minValue
|
|
16
|
+
max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
|
|
17
|
+
|
|
18
|
+
if (config.visualizationType === 'Bar' || config.visualizationType === 'Combo' || config.visualizationType === 'Deviation Bar') {
|
|
19
|
+
let ciYMax = 0
|
|
20
|
+
if (config.hasOwnProperty('confidenceKeys')) {
|
|
21
|
+
let upperCIValues = data.map(function (d) {
|
|
22
|
+
return d[config.confidenceKeys.upper]
|
|
23
|
+
})
|
|
24
|
+
ciYMax = Math.max.apply(Math, upperCIValues)
|
|
25
|
+
if (ciYMax > max) max = ciYMax // bump up the max
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && min > 0) {
|
|
30
|
+
min = 0
|
|
31
|
+
}
|
|
32
|
+
if (config.visualizationType === 'Combo' && isAllLine) {
|
|
33
|
+
if ((enteredMinValue === undefined || enteredMinValue === null || enteredMinValue === '') && min > 0) {
|
|
34
|
+
min = 0
|
|
35
|
+
}
|
|
36
|
+
if (enteredMinValue) {
|
|
37
|
+
const isMinValid = config.useLogScale ? enteredMinValue >= 0 && enteredMinValue < minValue : enteredMinValue < minValue
|
|
38
|
+
min = enteredMinValue && isMinValid ? enteredMinValue : minValue
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (config.visualizationType === 'Deviation Bar' && min > 0) {
|
|
43
|
+
const isMinValid = Number(enteredMinValue) < Math.min(minValue, Number(config.xAxis.target))
|
|
44
|
+
min = enteredMinValue && isMinValid ? enteredMinValue : 0
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (config.visualizationType === 'Line') {
|
|
48
|
+
const isMinValid = config.useLogScale ? enteredMinValue >= 0 && enteredMinValue < minValue : enteredMinValue < minValue
|
|
49
|
+
min = enteredMinValue && isMinValid ? enteredMinValue : minValue
|
|
50
|
+
}
|
|
51
|
+
//If data value max wasn't provided, calculate it
|
|
52
|
+
if (max === Number.MIN_VALUE) {
|
|
53
|
+
// if all values in data are negative set max = 0
|
|
54
|
+
max = existPositiveValue ? maxValue : 0
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//Adds Y Axis data padding if applicable
|
|
58
|
+
if (config.runtime.yAxis.paddingPercent) {
|
|
59
|
+
let paddingValue = (max - min) * config.runtime.yAxis.paddingPercent
|
|
60
|
+
min -= paddingValue
|
|
61
|
+
max += paddingValue
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (config.isLollipopChart && config.yAxis.displayNumbersOnBar) {
|
|
65
|
+
const dataKey = data.map(item => item[config.series[0].dataKey])
|
|
66
|
+
const maxDataVal = Math.max(...dataKey).toString().length
|
|
67
|
+
|
|
68
|
+
switch (true) {
|
|
69
|
+
case maxDataVal > 8 && maxDataVal <= 12:
|
|
70
|
+
max = max * 1.3
|
|
71
|
+
break
|
|
72
|
+
case maxDataVal > 4 && maxDataVal <= 7:
|
|
73
|
+
max = max * 1.1
|
|
74
|
+
break
|
|
75
|
+
default:
|
|
76
|
+
break
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (config.yAxis.enablePadding) {
|
|
81
|
+
if (min < 0) {
|
|
82
|
+
// sets with negative data need more padding on the max
|
|
83
|
+
max *= 1.2
|
|
84
|
+
min *= 1.2
|
|
85
|
+
} else {
|
|
86
|
+
max *= 1.1
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { min, max }
|
|
91
|
+
}
|
|
92
|
+
export default useMinMax
|
|
@@ -1,81 +1,55 @@
|
|
|
1
1
|
import isNumber from '@cdc/core/helpers/isNumber'
|
|
2
2
|
|
|
3
3
|
function useReduceData(config, data) {
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const cleanChars = value => {
|
|
9
|
-
// remove comma and $ signs
|
|
10
|
-
let tmp
|
|
11
|
-
if (typeof value === 'string') {
|
|
12
|
-
tmp = value !== null && value !== '' ? value.replace(/[,$]/g, '') : ''
|
|
13
|
-
} else {
|
|
14
|
-
tmp = value !== null && value !== '' ? value : ''
|
|
15
|
-
}
|
|
16
|
-
return tmp
|
|
17
|
-
}
|
|
4
|
+
const isBar = config.series.every(({ type }) => type === 'Bar')
|
|
5
|
+
const isAllLine = config.series.every(({ type }) => ['Line', 'dashed-sm', 'dashed-md', 'dashed-lg'].includes(type))
|
|
6
|
+
const sumYValues = seriesKeys => xValue => seriesKeys.reduce((yTotal, k) => yTotal + Number(xValue[k]), 0)
|
|
7
|
+
|
|
18
8
|
const getMaxValueFromData = () => {
|
|
19
|
-
let max
|
|
20
|
-
if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && isBar)) && config.visualizationSubType === 'stacked') {
|
|
21
|
-
const yTotals = data.reduce((allTotals, xValue) => {
|
|
22
|
-
const totalYValues = config.runtime.seriesKeys.reduce((yTotal, k) => {
|
|
23
|
-
yTotal += Number(xValue[k])
|
|
24
|
-
return yTotal
|
|
25
|
-
}, 0)
|
|
26
|
-
|
|
27
|
-
allTotals.push(totalYValues)
|
|
28
|
-
if (totalYValues > max) {
|
|
29
|
-
max = totalYValues
|
|
30
|
-
}
|
|
31
|
-
return allTotals
|
|
32
|
-
}, [])
|
|
9
|
+
let max = Math.max(...data.map(d => Math.max(...config.runtime.seriesKeys.map(key => (isNumber(d[key]) ? Number(cleanChars(d[key])) : 0)))))
|
|
33
10
|
|
|
11
|
+
if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && isBar)) && config.visualizationSubType === 'stacked') {
|
|
12
|
+
const yTotals = data.map(sumYValues(config.runtime.seriesKeys))
|
|
34
13
|
max = Math.max(...yTotals)
|
|
35
|
-
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if ((config.visualizationType === 'Bar' || config.visualizationType === 'Deviation Bar') && config.series && config.series.dataKey) {
|
|
36
17
|
max = Math.max(...data.map(d => (isNumber(d[config.series.dataKey]) ? Number(cleanChars(d[config.series.dataKey])) : 0)))
|
|
37
|
-
|
|
38
|
-
} else if (config.visualizationType === 'Combo' && config.visualizationSubType === 'stacked' && !isBar) {
|
|
39
|
-
let total = []
|
|
18
|
+
}
|
|
40
19
|
|
|
20
|
+
if (config.visualizationType === 'Combo' && config.visualizationSubType === 'stacked' && !isBar) {
|
|
41
21
|
if (config.runtime.barSeriesKeys && config.runtime.lineSeriesKeys) {
|
|
42
|
-
|
|
43
|
-
data.map(function (d, index) {
|
|
44
|
-
const totalYValues = config.runtime.barSeriesKeys.reduce((yTotal, k) => {
|
|
45
|
-
yTotal += Number(d[k])
|
|
46
|
-
return yTotal
|
|
47
|
-
}, 0)
|
|
48
|
-
return total.push(totalYValues)
|
|
49
|
-
})
|
|
50
|
-
// get lineSeries largest values
|
|
51
|
-
const lineMax = Math.max(...data.map(d => Math.max(...config.runtime.lineSeriesKeys.map(key => Number(cleanChars(d[key]))))))
|
|
22
|
+
const yTotals = data.map(sumYValues(config.runtime.barSeriesKeys))
|
|
52
23
|
|
|
53
|
-
const
|
|
24
|
+
const lineMax = Math.max(...data.map(d => Math.max(...config.runtime.lineSeriesKeys.map(key => Number(cleanChars(d[key]))))))
|
|
25
|
+
const barMax = Math.max(...yTotals)
|
|
54
26
|
|
|
55
|
-
max =
|
|
27
|
+
max = Math.max(barMax, lineMax)
|
|
56
28
|
}
|
|
57
|
-
} else {
|
|
58
|
-
max = Math.max(...data.map(d => Math.max(...config.runtime.seriesKeys.map(key => (isNumber(d[key]) ? Number(cleanChars(d[key])) : 0)))))
|
|
59
29
|
}
|
|
30
|
+
|
|
60
31
|
return max
|
|
61
32
|
}
|
|
62
33
|
|
|
63
34
|
const getMinValueFromData = () => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return min
|
|
35
|
+
const minNumberFromData = Math.min(...data.map(d => Math.min(...config.runtime.seriesKeys.map(key => (isNumber(d[key]) ? Number(cleanChars(d[key])) : Infinity)))))
|
|
36
|
+
|
|
37
|
+
return String(minNumberFromData)
|
|
68
38
|
}
|
|
69
39
|
|
|
70
40
|
const findPositiveNum = () => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (config.runtime.seriesKeys) {
|
|
74
|
-
for (let i = 0; i < config.runtime.seriesKeys.length; i++) {
|
|
75
|
-
existPositiveValue = data.some(d => d[config.runtime.seriesKeys[i]] >= 0)
|
|
76
|
-
}
|
|
41
|
+
if (!config.runtime.seriesKeys) {
|
|
42
|
+
return false
|
|
77
43
|
}
|
|
78
|
-
return
|
|
44
|
+
return config.runtime.seriesKeys.some(key => data.some(d => d[key] >= 0))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const cleanChars = value => {
|
|
48
|
+
if (value === null || value === '') {
|
|
49
|
+
return ''
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return typeof value === 'string' ? value.replace(/[,$]/g, '') : value
|
|
79
53
|
}
|
|
80
54
|
|
|
81
55
|
const maxValue = Number(getMaxValueFromData())
|