@cdc/chart 1.3.4 → 4.22.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/LICENSE +201 -0
- package/dist/cdcchart.js +6 -6
- package/examples/cutoff-example-config.json +2 -0
- package/examples/cutoff-example-data.json +1 -1
- package/examples/dynamic-legends.json +125 -0
- package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart-with-numbers-on-bar.json +198 -0
- package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart.json +241 -0
- package/examples/gallery/bar-chart-horizontal/horizontal-stacked.json +248 -0
- package/examples/gallery/bar-chart-vertical/combo-line-chart.json +137 -0
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +80 -0
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +81 -0
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-with-confidence.json +68 -0
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart.json +111 -0
- package/examples/gallery/lollipop/lollipop-style-horizontal.json +220 -0
- package/examples/gallery/paired-bar/paired-bar-chart.json +196 -0
- package/examples/horizontal-chart.json +3 -0
- package/examples/paired-bar-data.json +1 -1
- package/examples/paired-bar-example.json +2 -0
- package/examples/planet-combo-example-config.json +2 -0
- package/examples/planet-example-config.json +2 -2
- package/examples/planet-example-data.json +1 -1
- package/examples/planet-pie-example-config.json +2 -0
- package/examples/private/line-test-data.json +22 -0
- package/examples/private/line-test-two.json +216 -0
- package/examples/private/line-test.json +102 -0
- package/examples/private/shawn.json +1296 -0
- package/examples/private/yaxis-test.json +132 -0
- package/examples/private/yaxis-testing.csv +27 -0
- package/examples/private/yaxis.json +28 -0
- package/examples/stacked-vertical-bar-example.json +228 -0
- package/package.json +2 -2
- package/src/CdcChart.tsx +121 -168
- package/src/components/BarChart.tsx +92 -40
- package/src/components/DataTable.tsx +28 -13
- package/src/components/EditorPanel.js +286 -182
- package/src/components/Legend.js +334 -0
- package/src/components/LineChart.tsx +57 -17
- package/src/components/LinearChart.tsx +171 -77
- package/src/components/PairedBarChart.tsx +139 -42
- package/src/components/PieChart.tsx +33 -6
- package/src/components/SparkLine.js +28 -27
- package/src/components/useIntersectionObserver.tsx +30 -0
- package/src/data/initial-state.js +23 -7
- package/src/hooks/useChartClasses.js +35 -0
- package/src/hooks/useLegendClasses.js +20 -0
- package/src/hooks/useReduceData.ts +72 -24
- package/src/index.html +29 -30
- package/src/scss/editor-panel.scss +34 -4
- package/src/scss/main.scss +201 -5
- package/src/components/BarStackVertical.js +0 -0
|
@@ -6,6 +6,7 @@ import Pie, { ProvidedProps, PieArcDatum } from '@visx/shape/lib/shapes/Pie';
|
|
|
6
6
|
import chroma from "chroma-js";
|
|
7
7
|
import { Group } from '@visx/group';
|
|
8
8
|
import { Text } from '@visx/text';
|
|
9
|
+
import useIntersectionObserver from "./useIntersectionObserver";
|
|
9
10
|
|
|
10
11
|
import Context from '../context';
|
|
11
12
|
|
|
@@ -20,11 +21,23 @@ const enterUpdateTransition = ({ startAngle, endAngle }: PieArcDatum<any>) => ({
|
|
|
20
21
|
});
|
|
21
22
|
|
|
22
23
|
export default function PieChart() {
|
|
23
|
-
const { transformedData: data, config, dimensions, seriesHighlight, colorScale, formatNumber, currentViewport } = useContext<any>(Context);
|
|
24
|
+
const { transformedData: data, config, dimensions, seriesHighlight, colorScale, formatNumber, currentViewport, handleChartAriaLabels } = useContext<any>(Context);
|
|
24
25
|
|
|
25
26
|
const [filteredData, setFilteredData] = useState<any>(undefined);
|
|
27
|
+
const [animatedPie, setAnimatePie] = useState<boolean>(false);
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
const triggerRef = useRef();
|
|
30
|
+
const dataRef = useIntersectionObserver(triggerRef, {
|
|
31
|
+
freezeOnceVisible: false
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
useEffect( () => {
|
|
35
|
+
if (dataRef?.isIntersecting && config.animate && !animatedPie) {
|
|
36
|
+
setTimeout(() => {
|
|
37
|
+
setAnimatePie(true);
|
|
38
|
+
}, 500);
|
|
39
|
+
}
|
|
40
|
+
}, [dataRef?.isIntersecting, config.animate])
|
|
28
41
|
|
|
29
42
|
type AnimatedPieProps<Datum> = ProvidedProps<Datum> & {
|
|
30
43
|
animate?: boolean;
|
|
@@ -66,7 +79,14 @@ export default function PieChart() {
|
|
|
66
79
|
|
|
67
80
|
const tooltip = `<div>
|
|
68
81
|
${yAxisTooltip}<br />
|
|
69
|
-
${xAxisTooltip}<br
|
|
82
|
+
${xAxisTooltip}<br />
|
|
83
|
+
Percent: ${Math.round(
|
|
84
|
+
(((arc.endAngle - arc.startAngle) * 180) /
|
|
85
|
+
Math.PI /
|
|
86
|
+
360) *
|
|
87
|
+
100
|
|
88
|
+
) + "%"}
|
|
89
|
+
`
|
|
70
90
|
|
|
71
91
|
return (
|
|
72
92
|
<Group key={key} style={{ opacity: (config.legend.behavior === "highlight" && seriesHighlight.length > 0 && seriesHighlight.indexOf(arc.data[config.runtime.xAxis.dataKey]) === -1) ? 0.5 : 1 }}>
|
|
@@ -94,12 +114,12 @@ export default function PieChart() {
|
|
|
94
114
|
props: PieStyles;
|
|
95
115
|
key: string;
|
|
96
116
|
}) => {
|
|
97
|
-
|
|
117
|
+
|
|
98
118
|
const [centroidX, centroidY] = path.centroid(arc);
|
|
99
119
|
const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.1;
|
|
100
120
|
|
|
101
121
|
let textColor = "#FFF";
|
|
102
|
-
if (chroma.contrast(textColor, colorScale(arc.data[config.runtime.xAxis.dataKey])) < 3.5) {
|
|
122
|
+
if (colorScale(arc.data[config.runtime.xAxis.dataKey]) && chroma.contrast(textColor, colorScale(arc.data[config.runtime.xAxis.dataKey])) < 3.5) {
|
|
103
123
|
textColor = "000";
|
|
104
124
|
}
|
|
105
125
|
|
|
@@ -165,7 +185,13 @@ export default function PieChart() {
|
|
|
165
185
|
|
|
166
186
|
return (
|
|
167
187
|
<ErrorBoundary component="PieChart">
|
|
168
|
-
<svg
|
|
188
|
+
<svg
|
|
189
|
+
width={width}
|
|
190
|
+
height={height}
|
|
191
|
+
className={`group ${animatedPie ? 'animated' : ''}`}
|
|
192
|
+
role="img"
|
|
193
|
+
aria-label={handleChartAriaLabels(config)}
|
|
194
|
+
>
|
|
169
195
|
<Group top={centerY} left={centerX}>
|
|
170
196
|
<Pie
|
|
171
197
|
data={filteredData || data}
|
|
@@ -183,6 +209,7 @@ export default function PieChart() {
|
|
|
183
209
|
</Pie>
|
|
184
210
|
</Group>
|
|
185
211
|
</svg>
|
|
212
|
+
<div ref={triggerRef} />
|
|
186
213
|
<ReactTooltip id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`} html={true} type="light" arrowColor="rgba(0,0,0,0)" className="tooltip"/>
|
|
187
214
|
</ErrorBoundary>
|
|
188
215
|
)
|
|
@@ -15,17 +15,14 @@ import ReactTooltip from 'react-tooltip';
|
|
|
15
15
|
|
|
16
16
|
import useReduceData from '../hooks/useReduceData';
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
import Context from '../context';
|
|
20
19
|
|
|
21
20
|
export default function SparkLine({width: parentWidth, height: parentHeight}) {
|
|
22
21
|
|
|
23
|
-
const { transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, seriesHighlight, formatNumber, colorScale } = useContext(Context);
|
|
22
|
+
const { transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, seriesHighlight, formatNumber, colorScale, handleChartAriaLabels } = useContext(Context);
|
|
24
23
|
let width = parentWidth
|
|
25
24
|
const { minValue, maxValue } = useReduceData(config, data)
|
|
26
|
-
|
|
27
|
-
// width = width * 0.73;
|
|
28
|
-
// }
|
|
25
|
+
|
|
29
26
|
const margin = ({ top: 5, right: 10, bottom: 10, left: 10 })
|
|
30
27
|
const height = parentHeight;
|
|
31
28
|
|
|
@@ -38,16 +35,14 @@ export default function SparkLine({width: parentWidth, height: parentHeight}) {
|
|
|
38
35
|
let xScale;
|
|
39
36
|
let yScale;
|
|
40
37
|
let seriesScale;
|
|
41
|
-
|
|
38
|
+
const {max:enteredMaxValue,min:enteredMinValue} = config.runtime.yAxis;
|
|
39
|
+
const isMaxValid = Number(enteredMaxValue) >= Number(maxValue);
|
|
40
|
+
const isMinValid = Number(enteredMinValue) <= Number(minValue);
|
|
42
41
|
|
|
43
42
|
if (data) {
|
|
44
|
-
let min =
|
|
45
|
-
let max =
|
|
46
|
-
|
|
47
|
-
if ((config.visualizationType === 'Bar' || config.visualizationType === 'Combo') && min > 0) {
|
|
48
|
-
min = 0;
|
|
49
|
-
}
|
|
50
|
-
//If data value max wasn't provided, calculate it
|
|
43
|
+
let min = (enteredMinValue && isMinValid) ? enteredMinValue : minValue;
|
|
44
|
+
let max = (enteredMaxValue && isMaxValid )? enteredMaxValue : Number.MIN_VALUE;
|
|
45
|
+
|
|
51
46
|
if (max === Number.MIN_VALUE) {
|
|
52
47
|
max = maxValue
|
|
53
48
|
}
|
|
@@ -107,15 +102,19 @@ export default function SparkLine({width: parentWidth, height: parentHeight}) {
|
|
|
107
102
|
return (
|
|
108
103
|
<ErrorBoundary component="SparkLine">
|
|
109
104
|
<svg
|
|
105
|
+
role="img"
|
|
106
|
+
aria-label={handleChartAriaLabels(config)}
|
|
110
107
|
width={width}
|
|
111
108
|
height={height}
|
|
112
109
|
className={'sparkline'}
|
|
110
|
+
tabIndex={0}
|
|
113
111
|
>
|
|
114
112
|
{(config.runtime.lineSeriesKeys || config.runtime.seriesKeys).map((seriesKey, index) => (
|
|
115
113
|
<>
|
|
116
114
|
<Group
|
|
117
115
|
className='sparkline-group'
|
|
118
116
|
height={parentHeight}
|
|
117
|
+
style={{ height: parentHeight}}
|
|
119
118
|
top={margin.top}
|
|
120
119
|
key={`series-${seriesKey}`}
|
|
121
120
|
opacity={config.legend.behavior === "highlight" && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
|
|
@@ -126,13 +125,12 @@ export default function SparkLine({width: parentWidth, height: parentHeight}) {
|
|
|
126
125
|
let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${d[config.runtime.xAxis.dataKey]}` : d[config.runtime.xAxis.dataKey]
|
|
127
126
|
|
|
128
127
|
const tooltip = `<div>
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
${yAxisTooltip}<br />
|
|
129
|
+
${xAxisTooltip}<br />
|
|
130
|
+
${config.seriesLabel ? `${config.seriesLabel}: ${seriesKey}` : ''}
|
|
131
|
+
</div>`
|
|
133
132
|
|
|
134
133
|
let circleRadii = 4.5
|
|
135
|
-
|
|
136
134
|
return (
|
|
137
135
|
<Group key={`series-${seriesKey}-point-${dataIndex}`}>
|
|
138
136
|
<Text
|
|
@@ -144,7 +142,7 @@ export default function SparkLine({width: parentWidth, height: parentHeight}) {
|
|
|
144
142
|
{formatNumber(d[seriesKey])}
|
|
145
143
|
</Text>
|
|
146
144
|
|
|
147
|
-
{dataIndex + 1 !== data.length &&
|
|
145
|
+
{ dataIndex + 1 !== data.length && ( (config.lineDatapointStyle === 'always show') || (config.lineDatapointStyle === 'hover') ) &&
|
|
148
146
|
<circle
|
|
149
147
|
key={`${seriesKey}-${dataIndex}`}
|
|
150
148
|
r={circleRadii}
|
|
@@ -168,16 +166,19 @@ export default function SparkLine({width: parentWidth, height: parentHeight}) {
|
|
|
168
166
|
strokeWidth={2}
|
|
169
167
|
strokeOpacity={1}
|
|
170
168
|
shapeRendering="geometricPrecision"
|
|
171
|
-
marker-end=
|
|
169
|
+
marker-end={`url(#${'arrow'}--${index})`}
|
|
172
170
|
|
|
173
171
|
/>
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
172
|
+
<MarkerArrow
|
|
173
|
+
id={`arrow--${index}`}
|
|
174
|
+
refX={2}
|
|
175
|
+
size={6}
|
|
176
|
+
marker-end={`url(#${'arrow'}--${index})`}
|
|
177
|
+
strokeOpacity={1}
|
|
178
|
+
fillOpacity={1}
|
|
179
|
+
// stroke={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
|
|
180
|
+
fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
|
|
181
|
+
/>
|
|
181
182
|
|
|
182
183
|
</Group>
|
|
183
184
|
<AxisBottom
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {useEffect, useState} from "react";
|
|
2
|
+
|
|
3
|
+
export default function useIntersectionObserver(
|
|
4
|
+
elementRef,
|
|
5
|
+
{ threshold = 0, root = null, rootMargin = "0%", freezeOnceVisible = false }
|
|
6
|
+
) {
|
|
7
|
+
const [entry, setEntry] = useState<any>();
|
|
8
|
+
|
|
9
|
+
const frozen = entry?.isIntersecting && freezeOnceVisible;
|
|
10
|
+
|
|
11
|
+
const updateEntry = ([entry]) => {
|
|
12
|
+
setEntry(entry);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const node = elementRef?.current;
|
|
17
|
+
const hasIOSupport = !!window.IntersectionObserver;
|
|
18
|
+
|
|
19
|
+
if (!hasIOSupport || frozen || !node) return;
|
|
20
|
+
|
|
21
|
+
const observerParams = { threshold, root, rootMargin };
|
|
22
|
+
const observer = new IntersectionObserver(updateEntry, observerParams);
|
|
23
|
+
|
|
24
|
+
observer.observe(node);
|
|
25
|
+
|
|
26
|
+
return () => observer.disconnect();
|
|
27
|
+
}, [elementRef, threshold, root, rootMargin, frozen]);
|
|
28
|
+
|
|
29
|
+
return entry;
|
|
30
|
+
}
|
|
@@ -2,6 +2,7 @@ export default {
|
|
|
2
2
|
type: 'chart',
|
|
3
3
|
title: '',
|
|
4
4
|
theme: 'theme-blue',
|
|
5
|
+
animate: false,
|
|
5
6
|
fontSize: 'medium',
|
|
6
7
|
lineDatapointStyle: 'hover',
|
|
7
8
|
barHasBorder: 'false',
|
|
@@ -9,20 +10,25 @@ export default {
|
|
|
9
10
|
lollipopShape: 'circle',
|
|
10
11
|
lollipopColorStyle: 'two-tone',
|
|
11
12
|
visualizationSubType: 'regular',
|
|
13
|
+
barStyle:'',
|
|
14
|
+
roundingStyle:'standard',
|
|
15
|
+
tipRounding:'top',
|
|
12
16
|
padding: {
|
|
13
17
|
left: 5,
|
|
14
18
|
right: 5
|
|
15
19
|
},
|
|
16
20
|
yAxis: {
|
|
17
21
|
hideAxis: false,
|
|
22
|
+
displayNumbersOnBar:false,
|
|
18
23
|
hideLabel: false,
|
|
19
24
|
hideTicks: false,
|
|
20
25
|
size: 50,
|
|
21
26
|
gridLines: false,
|
|
22
|
-
min:
|
|
23
|
-
max:
|
|
27
|
+
min:'',
|
|
28
|
+
max:'',
|
|
24
29
|
},
|
|
25
30
|
barThickness: 0.35,
|
|
31
|
+
barHeight: 25,
|
|
26
32
|
height: 300,
|
|
27
33
|
xAxis: {
|
|
28
34
|
type: 'categorical',
|
|
@@ -31,18 +37,28 @@ export default {
|
|
|
31
37
|
hideTicks: false,
|
|
32
38
|
size: 75,
|
|
33
39
|
tickRotation: 0,
|
|
34
|
-
min:
|
|
35
|
-
max:
|
|
40
|
+
min: '',
|
|
41
|
+
max:'',
|
|
36
42
|
},
|
|
37
43
|
table: {
|
|
38
44
|
label: 'Data Table',
|
|
39
|
-
expanded: true
|
|
45
|
+
expanded: true,
|
|
46
|
+
limitHeight:false,
|
|
47
|
+
height:"",
|
|
48
|
+
caption:""
|
|
40
49
|
},
|
|
41
50
|
orientation: 'vertical',
|
|
42
51
|
legend: {
|
|
43
52
|
behavior: 'isolate',
|
|
44
53
|
position: 'right',
|
|
45
|
-
|
|
54
|
+
colorCode:'',
|
|
55
|
+
reverseLabelOrder:false,
|
|
56
|
+
description:'',
|
|
57
|
+
dynamicLegend: false,
|
|
58
|
+
dynamicLegendDefaultText: "Show All",
|
|
59
|
+
dynamicLegendItemLimit: 5,
|
|
60
|
+
dynamicLegendItemLimitMessage: 'Dynamic Legend Item Limit Hit.',
|
|
61
|
+
dynamicLegendChartMessage: 'Select Options from the Legend',
|
|
46
62
|
},
|
|
47
63
|
exclusions: {
|
|
48
64
|
active: false,
|
|
@@ -51,7 +67,7 @@ export default {
|
|
|
51
67
|
palette: 'qualitative-bold',
|
|
52
68
|
isPaletteReversed: false,
|
|
53
69
|
labels: false,
|
|
54
|
-
dataFormat: {},
|
|
70
|
+
dataFormat: {commas:false,prefix:'',suffix:""},
|
|
55
71
|
confidenceKeys: {},
|
|
56
72
|
visual: {
|
|
57
73
|
border: true,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export default function useChartClasses(config) {
|
|
2
|
+
let lineDatapointClass = ''
|
|
3
|
+
let barBorderClass = ''
|
|
4
|
+
|
|
5
|
+
if (config.lineDatapointStyle === "hover") { lineDatapointClass = ' chart-line--hover' }
|
|
6
|
+
if (config.lineDatapointStyle === "always show") { lineDatapointClass = ' chart-line--always' }
|
|
7
|
+
if (config.barHasBorder === "false") { barBorderClass = ' chart-bar--no-border' }
|
|
8
|
+
|
|
9
|
+
let innerContainerClasses = ['cove-component__inner']
|
|
10
|
+
config.title && innerContainerClasses.push('component--has-title')
|
|
11
|
+
config.subtext && innerContainerClasses.push('component--has-subtext')
|
|
12
|
+
config.biteStyle && innerContainerClasses.push(`bite__style--${config.biteStyle}`)
|
|
13
|
+
config.general?.isCompactStyle && innerContainerClasses.push(`component--isCompactStyle`)
|
|
14
|
+
|
|
15
|
+
let contentClasses = ['cove-component__content'];
|
|
16
|
+
config.visualizationType === 'Spark Line' && contentClasses.push('sparkline')
|
|
17
|
+
!config.visual?.border && contentClasses.push('no-borders');
|
|
18
|
+
config.visual?.borderColorTheme && contentClasses.push('component--has-borderColorTheme');
|
|
19
|
+
config.visual?.accent && contentClasses.push('component--has-accent');
|
|
20
|
+
config.visual?.background && contentClasses.push('component--has-background');
|
|
21
|
+
config.visual?.hideBackgroundColor && contentClasses.push('component--hideBackgroundColor');
|
|
22
|
+
|
|
23
|
+
let sparkLineStyles = {
|
|
24
|
+
width: '100%',
|
|
25
|
+
height: '100px',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
barBorderClass,
|
|
30
|
+
lineDatapointClass,
|
|
31
|
+
contentClasses,
|
|
32
|
+
innerContainerClasses,
|
|
33
|
+
sparkLineStyles
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export default function useLegendClasses(config) {
|
|
2
|
+
let containerClasses = ['legend-container']
|
|
3
|
+
let innerClasses = ['legend-container__inner'];
|
|
4
|
+
|
|
5
|
+
// Legend Positioning
|
|
6
|
+
if (config.legend.position === "left") {
|
|
7
|
+
containerClasses.push('left')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Legend > Item Ordering
|
|
11
|
+
if (config.legend.reverseLabelOrder) {
|
|
12
|
+
innerClasses.push('d-flex')
|
|
13
|
+
innerClasses.push('flex-column-reverse')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
containerClasses,
|
|
18
|
+
innerClasses
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -1,30 +1,64 @@
|
|
|
1
1
|
|
|
2
2
|
function useReduceData(config,data) {
|
|
3
|
+
// for combo charts check all Data Series set to Bar;
|
|
4
|
+
const isBar = config?.series?.every(element=>element?.type === 'Bar');
|
|
3
5
|
|
|
4
|
-
const getMaxValueFromData = ()=>{
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
max = Math.max(...yTotals);
|
|
21
|
-
} else if(config.visualizationType === 'Bar' && config.confidenceKeys && config.confidenceKeys.upper) {
|
|
22
|
-
max = Math.max(...data.map((d) => Number(d[config.confidenceKeys.upper])));
|
|
23
|
-
} else {
|
|
24
|
-
max = Math.max(...data.map((d) => Math.max(...config.runtime.seriesKeys.map((key) => Number(d[key])))));
|
|
6
|
+
const getMaxValueFromData = () => {
|
|
7
|
+
let max; // will hold max number from data.
|
|
8
|
+
if (
|
|
9
|
+
(config.visualizationType === "Bar" || (config.visualizationType === "Combo" && isBar )) &&
|
|
10
|
+
config.visualizationSubType === "stacked"
|
|
11
|
+
) {
|
|
12
|
+
const yTotals = data.reduce((allTotals, xValue) => {
|
|
13
|
+
const totalYValues = config.runtime.seriesKeys.reduce((yTotal, k) => {
|
|
14
|
+
yTotal += Number(xValue[k]);
|
|
15
|
+
return yTotal;
|
|
16
|
+
}, 0);
|
|
17
|
+
|
|
18
|
+
allTotals.push(totalYValues);
|
|
19
|
+
if (totalYValues > max) {
|
|
20
|
+
max = totalYValues;
|
|
25
21
|
}
|
|
22
|
+
return allTotals;
|
|
23
|
+
}, [] as number[]);
|
|
24
|
+
|
|
25
|
+
max = Math.max(...yTotals);
|
|
26
|
+
} else if (
|
|
27
|
+
config.visualizationType === "Bar" &&
|
|
28
|
+
config.series &&
|
|
29
|
+
config.series.dataKey
|
|
30
|
+
) {
|
|
31
|
+
max = Math.max(...data.map((d) => Number(d[config.series.dataKey])));
|
|
32
|
+
|
|
33
|
+
}else if(config.visualizationType === "Combo" && config.visualizationSubType === "stacked" && !isBar){
|
|
34
|
+
let total = []
|
|
35
|
+
|
|
36
|
+
if(config.runtime.barSeriesKeys && config.runtime.lineSeriesKeys ){
|
|
37
|
+
// get barSeries max Values added to each other
|
|
38
|
+
data.map(function (d,index) {
|
|
39
|
+
const totalYValues =config.runtime.barSeriesKeys.reduce((yTotal, k) => {
|
|
40
|
+
yTotal += Number(d[k]);
|
|
41
|
+
return yTotal;
|
|
42
|
+
}, 0);
|
|
43
|
+
total.push(totalYValues)
|
|
44
|
+
|
|
45
|
+
});
|
|
46
|
+
// get lineSeries largest values
|
|
47
|
+
const lineMax = Math.max(...data.map((d) =>Math.max(...config.runtime.lineSeriesKeys.map((key) => Number(d[key])))));
|
|
26
48
|
|
|
27
|
-
|
|
49
|
+
const barMax = Math.max(...total)
|
|
50
|
+
|
|
51
|
+
max = (Number(barMax) > Number(lineMax)) ? barMax : lineMax;
|
|
52
|
+
}
|
|
53
|
+
}else {
|
|
54
|
+
max = Math.max(
|
|
55
|
+
...data.map((d) =>
|
|
56
|
+
Math.max(...config.runtime.seriesKeys.map((key) => Number(d[key])))
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return max;
|
|
28
62
|
};
|
|
29
63
|
|
|
30
64
|
const getMinValueFromData = ()=> {
|
|
@@ -34,10 +68,24 @@ const getMinValueFromData = ()=> {
|
|
|
34
68
|
|
|
35
69
|
return min;
|
|
36
70
|
};
|
|
71
|
+
|
|
72
|
+
const findPositiveNum = ():boolean=>{
|
|
73
|
+
// loop throught provided data to find positve number in arr based on series keys.
|
|
74
|
+
let existPositiveValue = false;
|
|
75
|
+
if (config.runtime.seriesKeys) {
|
|
76
|
+
for(let i = 0; i < config.runtime.seriesKeys.length; i++) {
|
|
77
|
+
existPositiveValue = data.some(d => d[config.runtime.seriesKeys[i]] >= 0);
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
return existPositiveValue;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
|
|
37
84
|
|
|
38
85
|
const maxValue = getMaxValueFromData();
|
|
39
|
-
const minValue = getMinValueFromData()
|
|
40
|
-
|
|
86
|
+
const minValue = getMinValueFromData();
|
|
87
|
+
const existPositiveValue = findPositiveNum();
|
|
88
|
+
return {minValue,maxValue,existPositiveValue};
|
|
41
89
|
}
|
|
42
90
|
|
|
43
91
|
export default useReduceData
|
package/src/index.html
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
<head>
|
|
4
5
|
<meta charset="utf-8" />
|
|
5
|
-
<meta
|
|
6
|
-
name="viewport"
|
|
7
|
-
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
|
8
|
-
/>
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
|
9
7
|
<style>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
body {
|
|
9
|
+
/* max-width: 1000px; */
|
|
10
|
+
margin: 0 auto !important;
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.react-container+.react-container {
|
|
17
|
+
margin-top: 3rem;
|
|
18
|
+
}
|
|
20
19
|
</style>
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
</head>
|
|
21
|
+
|
|
22
|
+
<body>
|
|
23
23
|
<!-- <div class="react-container" data-config="/examples/temp-example-config.json"></div> -->
|
|
24
24
|
|
|
25
25
|
<!-- <select id="cove_select">
|
|
@@ -28,22 +28,20 @@
|
|
|
28
28
|
<option value="Hispanic or Latino">Test 2</option>
|
|
29
29
|
</select> -->
|
|
30
30
|
|
|
31
|
-
<!-- <div class="react-container" data-config="/examples/
|
|
31
|
+
<!-- <div class="react-container" data-config="/examples/dynamic-legends.json"></div> -->
|
|
32
32
|
<!-- <div class="react-container" data-config="/examples/cutoff-example-config.json"></div> -->
|
|
33
33
|
<!-- <div class="react-container" data-config="/examples/covid-confidence-example-config.json"></div> -->
|
|
34
|
-
<div class="react-container" data-config="/examples/planet-example-config.json"></div>
|
|
34
|
+
<!-- <div class="react-container" data-config="/examples/planet-example-config.json"></div> -->
|
|
35
35
|
<!-- <div class="react-container" data-config="/examples/planet-chart-horizontal-example-config.json"></div> -->
|
|
36
|
-
<!--
|
|
37
|
-
<!--
|
|
36
|
+
<!-- <div class="react-container" data-config="/examples/planet-combo-example-config.json"></div>-->
|
|
37
|
+
<!-- <div class="react-container" data-config="/examples/planet-pie-example-config.json"></div>-->
|
|
38
38
|
<!-- <div class="react-container" data-config="/examples/date-exclusions-config.json"></div> -->
|
|
39
39
|
<!--<div class="react-container" data-config="/examples/case-rate-example-config.json"></div>-->
|
|
40
|
+
<!-- <div class="react-container" data-config="/examples/private/textelements.json"></div> -->
|
|
40
41
|
<!-- <div class="react-container" data-config="/examples/private/example-bar-chart.json"></div> -->
|
|
41
|
-
|
|
42
|
-
<!-- LINE -->
|
|
43
|
-
<!-- <div class="react-container" data-config="/examples/line-chart.json"></div> -->
|
|
44
|
-
|
|
42
|
+
<!-- <div class="react-container" data-config="/examples/example-sparkline.json"></div> -->
|
|
45
43
|
<!-- DATA PRESENTATION GALLERY: https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/bar-chart.html#examples -->
|
|
46
|
-
|
|
44
|
+
|
|
47
45
|
<!-- HORIZONTAL BAR CHARTS -->
|
|
48
46
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-horizontal/horizontal-bar-chart-with-numbers-on-bar.json"></div> -->
|
|
49
47
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-horizontal/horizontal-bar-chart.json"></div> -->
|
|
@@ -51,18 +49,19 @@
|
|
|
51
49
|
|
|
52
50
|
|
|
53
51
|
<!-- VERTICAL BAR CHARTS -->
|
|
54
|
-
|
|
52
|
+
<div class="react-container" data-config="/examples/gallery/bar-chart-vertical/combo-line-chart.json"></div>
|
|
55
53
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json"></div> -->
|
|
56
54
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json"></div> -->
|
|
57
55
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-with-confidence.json"></div> -->
|
|
58
56
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart.json"></div> -->
|
|
59
|
-
|
|
57
|
+
|
|
60
58
|
<!-- LOLLIPOP CHARTS -->
|
|
61
59
|
<!-- <div class="react-container" data-config="/examples/gallery/lollipop/lollipop-style-horizontal.json"></div> -->
|
|
62
|
-
|
|
60
|
+
|
|
63
61
|
<!-- PAIRED BAR CHARTS -->
|
|
64
62
|
<!-- <div class="react-container" data-config="/examples/gallery/paired-bar/paired-bar-chart.json"></div> -->
|
|
65
63
|
|
|
66
64
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
67
|
-
|
|
65
|
+
</body>
|
|
66
|
+
|
|
68
67
|
</html>
|
|
@@ -265,7 +265,6 @@
|
|
|
265
265
|
display: block;
|
|
266
266
|
text-align: left;
|
|
267
267
|
cursor: pointer;
|
|
268
|
-
color: rgba(0, 0, 0, .5);
|
|
269
268
|
text-decoration: underline;
|
|
270
269
|
|
|
271
270
|
span {
|
|
@@ -310,6 +309,8 @@
|
|
|
310
309
|
z-index: 3;
|
|
311
310
|
}
|
|
312
311
|
|
|
312
|
+
|
|
313
|
+
legend,
|
|
313
314
|
label {
|
|
314
315
|
text-transform: uppercase;
|
|
315
316
|
display: block;
|
|
@@ -341,6 +342,29 @@
|
|
|
341
342
|
}
|
|
342
343
|
}
|
|
343
344
|
|
|
345
|
+
legend {
|
|
346
|
+
font-weight: normal;
|
|
347
|
+
margin-bottom: 0.3em;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
legend + span {
|
|
351
|
+
float: left;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.radio-label {
|
|
355
|
+
font-weight: normal;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.accordion__panel fieldset:not(:first-child) {
|
|
359
|
+
margin-top: 1em;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.accordion__panel fieldset {
|
|
363
|
+
label:not(:first-child) {
|
|
364
|
+
margin-top: 0rem;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
344
368
|
input[type="text"], input[role="combobox"], input[type="number"], textarea {
|
|
345
369
|
min-width: 100%;
|
|
346
370
|
max-width: 100%; // Doing this prevents width of textarea from being changed
|
|
@@ -350,7 +374,7 @@
|
|
|
350
374
|
min-height: 140px;
|
|
351
375
|
}
|
|
352
376
|
|
|
353
|
-
.header .color-palette
|
|
377
|
+
.header .color-palette button {
|
|
354
378
|
width: 21px;
|
|
355
379
|
height: 21px;
|
|
356
380
|
border-radius: 40px;
|
|
@@ -365,7 +389,7 @@
|
|
|
365
389
|
list-style: none;
|
|
366
390
|
flex-wrap: wrap;
|
|
367
391
|
|
|
368
|
-
|
|
392
|
+
button {
|
|
369
393
|
width: 45px;
|
|
370
394
|
height: 23px;
|
|
371
395
|
margin-right: 4px;
|
|
@@ -375,6 +399,7 @@
|
|
|
375
399
|
overflow: hidden;
|
|
376
400
|
cursor: pointer;
|
|
377
401
|
position: relative;
|
|
402
|
+
padding: 0;
|
|
378
403
|
|
|
379
404
|
.click-target {
|
|
380
405
|
position: absolute;
|
|
@@ -390,12 +415,17 @@
|
|
|
390
415
|
|
|
391
416
|
span {
|
|
392
417
|
width: 33.3%;
|
|
418
|
+
height: 100%;
|
|
393
419
|
}
|
|
394
420
|
}
|
|
395
421
|
}
|
|
396
422
|
|
|
397
423
|
fieldset {
|
|
398
424
|
display: block;
|
|
425
|
+
|
|
426
|
+
.float-left {
|
|
427
|
+
float: left;
|
|
428
|
+
}
|
|
399
429
|
}
|
|
400
430
|
|
|
401
431
|
.primary-fieldset {
|
|
@@ -641,7 +671,7 @@
|
|
|
641
671
|
cursor: auto;
|
|
642
672
|
}
|
|
643
673
|
|
|
644
|
-
[type="radio"] {
|
|
674
|
+
input[type="radio"] {
|
|
645
675
|
border: 1px solid #8c8f94;
|
|
646
676
|
border-radius: 50%;
|
|
647
677
|
margin-right: 5px;
|