@cdc/chart 4.23.1 → 4.23.2
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 +54532 -696
- package/examples/Barchart_with_negative.json +34 -0
- package/examples/box-plot.json +2 -2
- package/examples/dynamic-legends.json +1 -1
- package/examples/example-bar-chart-nonnumeric.json +36 -0
- package/examples/example-bar-chart.json +33 -0
- package/examples/example-combo-bar-nonnumeric.json +105 -0
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +1 -1
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart.json +2 -2
- package/examples/gallery/paired-bar/paired-bar-chart.json +65 -13
- package/examples/line-chart-nonnumeric.json +32 -0
- package/examples/line-chart.json +21 -63
- package/examples/newdata.json +1 -1
- package/examples/planet-combo-example-config.json +143 -20
- package/examples/planet-example-data-nonnumeric.json +56 -0
- package/examples/planet-example-data.json +2 -2
- package/examples/planet-pie-example-config-nonnumeric.json +30 -0
- package/examples/scatterplot-continuous.csv +17 -0
- package/examples/scatterplot.json +136 -0
- package/examples/sparkline-chart-nonnumeric.json +76 -0
- package/examples/stacked-vertical-bar-example-negative.json +154 -0
- package/examples/stacked-vertical-bar-example-nonnumerics.json +154 -0
- package/index.html +74 -0
- package/package.json +29 -23
- package/src/{CdcChart.tsx → CdcChart.jsx} +74 -56
- package/src/components/{BarChart.tsx → BarChart.jsx} +99 -91
- package/src/components/BoxPlot.jsx +88 -0
- package/src/components/{DataTable.tsx → DataTable.jsx} +102 -25
- package/src/components/{EditorPanel.js → EditorPanel.jsx} +228 -14
- package/src/components/{Filters.js → Filters.jsx} +6 -12
- package/src/components/{Legend.js → Legend.jsx} +120 -108
- package/src/components/{LineChart.tsx → LineChart.jsx} +26 -12
- package/src/components/{LinearChart.tsx → LinearChart.jsx} +67 -47
- package/src/components/{PairedBarChart.tsx → PairedBarChart.jsx} +45 -78
- package/src/components/{PieChart.tsx → PieChart.jsx} +17 -32
- package/src/components/ScatterPlot.jsx +48 -0
- package/src/components/{SparkLine.js → SparkLine.jsx} +49 -18
- package/src/components/{useIntersectionObserver.tsx → useIntersectionObserver.jsx} +1 -1
- package/src/data/initial-state.js +33 -3
- package/src/hooks/{useColorPalette.ts → useColorPalette.js} +10 -28
- package/src/hooks/{useReduceData.ts → useReduceData.js} +25 -14
- package/src/hooks/useRightAxis.js +3 -1
- package/src/index.jsx +16 -0
- package/src/scss/DataTable.scss +22 -0
- package/src/scss/main.scss +30 -10
- package/vite.config.js +4 -0
- package/dist/495.js +0 -3
- package/dist/703.js +0 -1
- package/src/components/BoxPlot.js +0 -92
- package/src/index.html +0 -67
- package/src/index.tsx +0 -18
- /package/src/{context.tsx → ConfigContext.jsx} +0 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Example Stacked Vertical",
|
|
3
|
+
"visualizationType": "Bar",
|
|
4
|
+
"visualizationSubType": "stacked",
|
|
5
|
+
"orientation": "vertical",
|
|
6
|
+
"animate": true,
|
|
7
|
+
"animateReplay": true,
|
|
8
|
+
"yAxis": {
|
|
9
|
+
"hideAxis": false,
|
|
10
|
+
"hideLabel": false,
|
|
11
|
+
"hideTicks": false,
|
|
12
|
+
"size": 50,
|
|
13
|
+
"gridLines": false
|
|
14
|
+
},
|
|
15
|
+
"barThickness": 0.35,
|
|
16
|
+
"height": 300,
|
|
17
|
+
"xAxis": {
|
|
18
|
+
"type": "categorical",
|
|
19
|
+
"hideAxis": false,
|
|
20
|
+
"hideLabel": false,
|
|
21
|
+
"hideTicks": false,
|
|
22
|
+
"size": 75,
|
|
23
|
+
"tickRotation": 0,
|
|
24
|
+
"dataKey": "Date"
|
|
25
|
+
},
|
|
26
|
+
"table": {
|
|
27
|
+
"label": "Data Table",
|
|
28
|
+
"expanded": true,
|
|
29
|
+
"show": true
|
|
30
|
+
},
|
|
31
|
+
"legend": {
|
|
32
|
+
"behavior": "isolate",
|
|
33
|
+
"position": "right",
|
|
34
|
+
"reverseLabelOrder": false
|
|
35
|
+
},
|
|
36
|
+
"series": [
|
|
37
|
+
{
|
|
38
|
+
"dataKey": "Data 1",
|
|
39
|
+
"type": "Bar"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"dataKey": "Data 2",
|
|
43
|
+
"type": "Bar"
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"data": [
|
|
47
|
+
{
|
|
48
|
+
"Date": "1/15/2016",
|
|
49
|
+
"Data 1": "90",
|
|
50
|
+
"Data 2": "NA",
|
|
51
|
+
"Data 3": "300",
|
|
52
|
+
"Data 4": "95",
|
|
53
|
+
"Data 5": "120",
|
|
54
|
+
"Data 6": "310",
|
|
55
|
+
"Data 7": "40",
|
|
56
|
+
"Data 8": "90",
|
|
57
|
+
"Data 9": "240",
|
|
58
|
+
"Data 10": "60",
|
|
59
|
+
"Data 11": "160",
|
|
60
|
+
"Data 12": "200"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"Date": "2/15/2016",
|
|
64
|
+
"Data 1": "40",
|
|
65
|
+
"Data 2": "90",
|
|
66
|
+
"Data 3": "240",
|
|
67
|
+
"Data 4": "60",
|
|
68
|
+
"Data 5": "160",
|
|
69
|
+
"Data 6": "200",
|
|
70
|
+
"Data 7": "40",
|
|
71
|
+
"Data 8": "90",
|
|
72
|
+
"Data 9": "240",
|
|
73
|
+
"Data 10": "60",
|
|
74
|
+
"Data 11": "160",
|
|
75
|
+
"Data 12": "200"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"Date": "3/15/2016",
|
|
79
|
+
"Data 1": "50",
|
|
80
|
+
"Data 2": "300",
|
|
81
|
+
"Data 3": "290",
|
|
82
|
+
"Data 4": "100",
|
|
83
|
+
"Data 5": "200",
|
|
84
|
+
"Data 6": "250",
|
|
85
|
+
"Data 7": "80",
|
|
86
|
+
"Data 8": "350",
|
|
87
|
+
"Data 9": "300",
|
|
88
|
+
"Data 10": "150",
|
|
89
|
+
"Data 11": "130",
|
|
90
|
+
"Data 12": "100"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"Date": "4/15/2016",
|
|
94
|
+
"Data 1": "120",
|
|
95
|
+
"Data 2": "160",
|
|
96
|
+
"Data 3": "230",
|
|
97
|
+
"Data 4": "180",
|
|
98
|
+
"Data 5": "160",
|
|
99
|
+
"Data 6": "220",
|
|
100
|
+
"Data 7": "40",
|
|
101
|
+
"Data 8": "90",
|
|
102
|
+
"Data 9": "240",
|
|
103
|
+
"Data 10": "60",
|
|
104
|
+
"Data 11": "160",
|
|
105
|
+
"Data 12": "200"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"Date": "5/15/2016",
|
|
109
|
+
"Data 1": "80",
|
|
110
|
+
"Data 2": "350",
|
|
111
|
+
"Data 3": "300",
|
|
112
|
+
"Data 4": "150",
|
|
113
|
+
"Data 5": "130",
|
|
114
|
+
"Data 6": "100",
|
|
115
|
+
"Data 7": "120",
|
|
116
|
+
"Data 8": "160",
|
|
117
|
+
"Data 9": "230",
|
|
118
|
+
"Data 10": "180",
|
|
119
|
+
"Data 11": "160",
|
|
120
|
+
"Data 12": "220"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"Date": "6/15/2016",
|
|
124
|
+
"Data 1": "90",
|
|
125
|
+
"Data 2": "220",
|
|
126
|
+
"Data 3": "320",
|
|
127
|
+
"Data 4": "100",
|
|
128
|
+
"Data 5": "220",
|
|
129
|
+
"Data 6": "300",
|
|
130
|
+
"Data 7": "40",
|
|
131
|
+
"Data 8": "90",
|
|
132
|
+
"Data 9": "240",
|
|
133
|
+
"Data 10": "60",
|
|
134
|
+
"Data 11": "160",
|
|
135
|
+
"Data 12": "200"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"Date": "7/15/2016",
|
|
139
|
+
"Data 1": "120",
|
|
140
|
+
"Data 2": "160",
|
|
141
|
+
"Data 3": "230",
|
|
142
|
+
"Data 4": "180",
|
|
143
|
+
"Data 5": "160",
|
|
144
|
+
"Data 6": "220",
|
|
145
|
+
"Data 7": "80",
|
|
146
|
+
"Data 8": "350",
|
|
147
|
+
"Data 9": "300",
|
|
148
|
+
"Data 10": "150",
|
|
149
|
+
"Data 11": "130",
|
|
150
|
+
"Data 12": "100"
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
]
|
|
154
|
+
}
|
package/index.html
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
|
7
|
+
<style>
|
|
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
|
+
}
|
|
19
|
+
</style>
|
|
20
|
+
</head>
|
|
21
|
+
|
|
22
|
+
<body>
|
|
23
|
+
<!-- <div class="react-container" data-config="/examples/temp-example-config.json"></div> -->
|
|
24
|
+
|
|
25
|
+
<!-- <select id="cove_select">
|
|
26
|
+
<option value=""">Choose An Option</option>
|
|
27
|
+
<option value="Non-Hispanic White">Non-Hispanic White</option>
|
|
28
|
+
<option value="Hispanic or Latino">Test 2</option>
|
|
29
|
+
</select> -->
|
|
30
|
+
|
|
31
|
+
<!-- <div class="react-container" data-config="/examples/dynamic-legends.json"></div> -->
|
|
32
|
+
<!-- <div class="react-container" data-config="/examples/cutoff-example-config.json"></div> -->
|
|
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> -->
|
|
35
|
+
<!-- <div class="react-container" data-config="/examples/planet-chart-horizontal-example-config.json"></div> -->
|
|
36
|
+
<!-- <div class="react-container" data-config="/examples/planet-example-config.json"></div> -->
|
|
37
|
+
<!-- <div class="react-container" data-config="/examples/date-exclusions-config.json"></div> -->
|
|
38
|
+
<!-- <div class="react-container" data-config="/examples/case-rate-example-config.json"></div> -->
|
|
39
|
+
<!-- <div class="react-container" data-config="/examples/private/textelements.json"></div> -->
|
|
40
|
+
<!-- <div class="react-container" data-config="/examples/private/example-bar-chart.json"></div> -->
|
|
41
|
+
<!-- <div class="react-container" data-config="/examples/example-sparkline.json"></div> -->
|
|
42
|
+
<!-- DATA PRESENTATION GALLERY: https://www.cdc.gov/wcms/4.0/cdc-wp/data-presentation/bar-chart.html#examples -->
|
|
43
|
+
|
|
44
|
+
<!-- NONNUMERIC TESTS -->
|
|
45
|
+
<!-- <div class="react-container" data-config="/examples/planet-pie-example-config-nonnumeric.json"></div> -->
|
|
46
|
+
<div class="react-container" data-config="/examples/example-combo-bar-nonnumeric.json"></div>
|
|
47
|
+
<!-- <div class="react-container" data-config="/examples/example-bar-chart-nonnumeric.json"></div> -->
|
|
48
|
+
<!-- <div class="react-container" data-config="/examples/line-chart-nonnumeric.json"></div> -->
|
|
49
|
+
<!-- <div class="react-container" data-config="/examples/sparkline-chart-nonnumeric.json"></div> -->
|
|
50
|
+
|
|
51
|
+
<!-- HORIZONTAL BAR CHARTS -->
|
|
52
|
+
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-horizontal/horizontal-bar-chart-with-numbers-on-bar.json"></div> -->
|
|
53
|
+
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-horizontal/horizontal-bar-chart.json"></div> -->
|
|
54
|
+
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-horizontal/horizontal-stacked.json"></div> -->
|
|
55
|
+
|
|
56
|
+
<!-- VERTICAL BAR CHARTS -->
|
|
57
|
+
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/combo-line-chart.json"></div> -->
|
|
58
|
+
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json"></div> -->
|
|
59
|
+
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json"></div> -->
|
|
60
|
+
<!-- <div class="react-container" data-config="/examples/stacked-vertical-bar-example-nonnumerics.json"></div> -->
|
|
61
|
+
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-with-confidence.json"></div> -->
|
|
62
|
+
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart.json"></div> -->
|
|
63
|
+
<!-- <div class="react-container" data-config="/examples/box-plot.json"></div> -->
|
|
64
|
+
|
|
65
|
+
<!-- Paired bar Chart -->
|
|
66
|
+
<div class="react-container" data-config="/examples/gallery/paired-bar/paired-bar-chart.json"></div>
|
|
67
|
+
|
|
68
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
69
|
+
</body>
|
|
70
|
+
|
|
71
|
+
<script type="module" src="./src/index.jsx"></script>
|
|
72
|
+
</body>
|
|
73
|
+
|
|
74
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/chart",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.2",
|
|
4
4
|
"description": "React component for visualizing tabular data in various types of charts",
|
|
5
|
+
"moduleName": "CdcChart",
|
|
5
6
|
"main": "dist/cdcchart",
|
|
7
|
+
"type": "module",
|
|
6
8
|
"scripts": {
|
|
7
|
-
"start": "
|
|
8
|
-
"build": "
|
|
9
|
+
"start": "vite --open",
|
|
10
|
+
"build": "vite build",
|
|
11
|
+
"preview": "vite preview",
|
|
12
|
+
"graph": "nx graph",
|
|
9
13
|
"prepublishOnly": "lerna run --scope @cdc/chart build"
|
|
10
14
|
},
|
|
11
15
|
"repository": {
|
|
@@ -19,37 +23,39 @@
|
|
|
19
23
|
},
|
|
20
24
|
"license": "Apache-2.0",
|
|
21
25
|
"dependencies": {
|
|
22
|
-
"@
|
|
23
|
-
"@visx/axis": "^
|
|
24
|
-
"@visx/curve": "^
|
|
25
|
-
"@visx/gradient": "^
|
|
26
|
-
"@visx/group": "^
|
|
27
|
-
"@visx/legend": "^
|
|
28
|
-
"@visx/marker": "^
|
|
29
|
-
"@visx/mock-data": "^
|
|
30
|
-
"@visx/scale": "^
|
|
31
|
-
"@visx/shape": "^
|
|
32
|
-
"@visx/stats": "^
|
|
33
|
-
"@visx/
|
|
26
|
+
"@hello-pangea/dnd": "^16.2.0",
|
|
27
|
+
"@visx/axis": "^3.0.0",
|
|
28
|
+
"@visx/curve": "^3.0.0",
|
|
29
|
+
"@visx/gradient": "^3.0.0",
|
|
30
|
+
"@visx/group": "^3.0.0",
|
|
31
|
+
"@visx/legend": "^3.0.0",
|
|
32
|
+
"@visx/marker": "^3.0.0",
|
|
33
|
+
"@visx/mock-data": "^3.0.0",
|
|
34
|
+
"@visx/scale": "^3.0.0",
|
|
35
|
+
"@visx/shape": "^3.0.0",
|
|
36
|
+
"@visx/stats": "^3.0.0",
|
|
37
|
+
"@visx/text": "^3.0.0",
|
|
38
|
+
"@visx/tooltip": "^3.0.0",
|
|
34
39
|
"chroma-js": "^2.1.2",
|
|
35
40
|
"d3-array": "^2.8.0",
|
|
41
|
+
"d3-format": "^3.1.0",
|
|
36
42
|
"d3-time-format": "^3.0.0",
|
|
37
|
-
"html-react-parser": "
|
|
43
|
+
"html-react-parser": "^3.0.8",
|
|
38
44
|
"js-base64": "^2.5.2",
|
|
39
45
|
"papaparse": "^5.3.0",
|
|
40
46
|
"react-accessible-accordion": "^3.3.4",
|
|
41
47
|
"react-spring": "^8.0.27",
|
|
42
48
|
"react-table": "^7.5.0",
|
|
43
|
-
"react-tooltip": "
|
|
49
|
+
"react-tooltip": "5.8.2-beta.3",
|
|
44
50
|
"use-debounce": "^6.0.1",
|
|
45
51
|
"whatwg-fetch": "^3.6.2"
|
|
46
52
|
},
|
|
47
53
|
"peerDependencies": {
|
|
48
|
-
"react": "
|
|
49
|
-
"react-dom": "
|
|
54
|
+
"react": "^18.2.0",
|
|
55
|
+
"react-dom": "^18.2.0"
|
|
50
56
|
},
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
"gitHead": "cd4216f47b1c41bfbc1de3b704f70c52cc7293c2",
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"resize-observer-polyfill": "^1.5.1"
|
|
60
|
+
}
|
|
55
61
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback } from 'react'
|
|
2
2
|
|
|
3
3
|
// IE11
|
|
4
|
-
import 'core-js/stable'
|
|
5
4
|
import ResizeObserver from 'resize-observer-polyfill'
|
|
6
5
|
import 'whatwg-fetch'
|
|
7
6
|
import * as d3 from 'd3-array'
|
|
@@ -14,9 +13,10 @@ import { format } from 'd3-format'
|
|
|
14
13
|
import Papa from 'papaparse'
|
|
15
14
|
import parse from 'html-react-parser'
|
|
16
15
|
import { Base64 } from 'js-base64'
|
|
16
|
+
import 'react-tooltip/dist/react-tooltip.css'
|
|
17
17
|
|
|
18
18
|
// Primary Components
|
|
19
|
-
import
|
|
19
|
+
import ConfigContext from './ConfigContext'
|
|
20
20
|
import PieChart from './components/PieChart'
|
|
21
21
|
import LinearChart from './components/LinearChart'
|
|
22
22
|
|
|
@@ -33,36 +33,35 @@ import defaults from './data/initial-state'
|
|
|
33
33
|
import EditorPanel from './components/EditorPanel'
|
|
34
34
|
import Loading from '@cdc/core/components/Loading'
|
|
35
35
|
import Filters from './components/Filters'
|
|
36
|
-
import CoveMediaControls from '@cdc/core/
|
|
36
|
+
import CoveMediaControls from '@cdc/core/components/CoveMediaControls'
|
|
37
37
|
|
|
38
|
-
//
|
|
38
|
+
// Helpers
|
|
39
39
|
import numberFromString from '@cdc/core/helpers/numberFromString'
|
|
40
40
|
import getViewport from '@cdc/core/helpers/getViewport'
|
|
41
41
|
import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
42
42
|
import cacheBustingString from '@cdc/core/helpers/cacheBustingString'
|
|
43
|
+
import isNumber from '@cdc/core/helpers/isNumber'
|
|
44
|
+
import cleanData from '@cdc/core/helpers/cleanData'
|
|
43
45
|
|
|
44
46
|
import './scss/main.scss'
|
|
45
47
|
|
|
46
|
-
export default function CdcChart({ configUrl, config: configObj, isEditor = false, isDashboard = false, setConfig: setParentConfig, setEditing, hostname, link }
|
|
48
|
+
export default function CdcChart({ configUrl, config: configObj, isEditor = false, isDashboard = false, setConfig: setParentConfig, setEditing, hostname, link }) {
|
|
47
49
|
const transform = new DataTransform()
|
|
48
|
-
interface keyable {
|
|
49
|
-
[key: string]: any
|
|
50
|
-
}
|
|
51
50
|
|
|
52
|
-
const [loading, setLoading] = useState
|
|
53
|
-
const [colorScale, setColorScale] = useState
|
|
54
|
-
const [config, setConfig] = useState
|
|
55
|
-
const [stateData, setStateData] = useState
|
|
56
|
-
const [excludedData, setExcludedData] = useState
|
|
57
|
-
const [filteredData, setFilteredData] = useState
|
|
58
|
-
const [seriesHighlight, setSeriesHighlight] = useState
|
|
59
|
-
const [currentViewport, setCurrentViewport] = useState
|
|
60
|
-
const [dimensions, setDimensions] = useState
|
|
51
|
+
const [loading, setLoading] = useState(true)
|
|
52
|
+
const [colorScale, setColorScale] = useState(null)
|
|
53
|
+
const [config, setConfig] = useState({})
|
|
54
|
+
const [stateData, setStateData] = useState(config.data || [])
|
|
55
|
+
const [excludedData, setExcludedData] = useState()
|
|
56
|
+
const [filteredData, setFilteredData] = useState()
|
|
57
|
+
const [seriesHighlight, setSeriesHighlight] = useState([])
|
|
58
|
+
const [currentViewport, setCurrentViewport] = useState('lg')
|
|
59
|
+
const [dimensions, setDimensions] = useState([])
|
|
61
60
|
const [externalFilters, setExternalFilters] = useState(null)
|
|
62
61
|
const [container, setContainer] = useState()
|
|
63
62
|
const [coveLoadedEventRan, setCoveLoadedEventRan] = useState(false)
|
|
64
63
|
const [dynamicLegendItems, setDynamicLegendItems] = useState([])
|
|
65
|
-
const [imageId
|
|
64
|
+
const [imageId] = useState(`cove-${Math.random().toString(16).slice(-4)}`)
|
|
66
65
|
|
|
67
66
|
const legendGlyphSize = 15
|
|
68
67
|
const legendGlyphSizeHalf = legendGlyphSize / 2
|
|
@@ -219,8 +218,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
219
218
|
}
|
|
220
219
|
|
|
221
220
|
if (newConfig.visualizationType === 'Box Plot' && newConfig.series) {
|
|
222
|
-
console.log('hit', newConfig)
|
|
223
|
-
|
|
224
221
|
// stats
|
|
225
222
|
let allKeys = data.map(d => d[newConfig.xAxis.dataKey])
|
|
226
223
|
let allValues = data.map(d => Number(d[newConfig?.series[0]?.dataKey]))
|
|
@@ -232,50 +229,57 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
232
229
|
}
|
|
233
230
|
|
|
234
231
|
const groups = uniqueArray(allKeys)
|
|
232
|
+
let tableData = []
|
|
235
233
|
const plots = []
|
|
236
234
|
|
|
237
|
-
console.log('d', data)
|
|
238
|
-
console.log('newConfig', newConfig)
|
|
239
|
-
console.log('groups', groups)
|
|
240
|
-
console.log('allKeys', allKeys)
|
|
241
|
-
console.log('allValues', allValues)
|
|
242
|
-
|
|
243
235
|
// group specific statistics
|
|
244
236
|
// prevent re-renders
|
|
245
|
-
groups.
|
|
237
|
+
groups.forEach((g, index) => {
|
|
246
238
|
if (!g) return
|
|
247
239
|
// filter data by group
|
|
248
240
|
let filteredData = data.filter(item => item[newConfig.xAxis.dataKey] === g)
|
|
249
241
|
let filteredDataValues = filteredData.map(item => Number(item[newConfig?.series[0]?.dataKey]))
|
|
250
|
-
console.log('g', g)
|
|
251
|
-
console.log('item', filteredData)
|
|
252
|
-
console.log('item', newConfig)
|
|
253
242
|
// let filteredDataValues = filteredData.map(item => Number(item[newConfig.yAxis.dataKey]))
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
const q3 = d3.quantile(filteredDataValues, 0.75)
|
|
243
|
+
const q1 = d3.quantile(filteredDataValues, parseFloat(newConfig.boxplot.firstQuartilePercentage) / 100)
|
|
244
|
+
const q3 = d3.quantile(filteredDataValues, parseFloat(newConfig.boxplot.thirdQuartilePercentage) / 100)
|
|
257
245
|
const iqr = q3 - q1
|
|
258
246
|
const lowerBounds = q1 - (q3 - q1) * 1.5
|
|
259
247
|
const upperBounds = q3 + (q3 - q1) * 1.5
|
|
260
248
|
const outliers = filteredDataValues.filter(v => v < lowerBounds || v > upperBounds)
|
|
249
|
+
let nonOutliers = filteredDataValues
|
|
250
|
+
|
|
251
|
+
nonOutliers = nonOutliers.filter(item => !outliers.includes(item))
|
|
252
|
+
|
|
261
253
|
plots.push({
|
|
262
254
|
columnCategory: g,
|
|
263
|
-
|
|
255
|
+
columnMax: Number(q3 + 1.5 * iqr).toFixed(2),
|
|
256
|
+
columnThirdQuartile: q3.toFixed(2),
|
|
264
257
|
columnMedian: d3.median(filteredDataValues),
|
|
265
|
-
columnFirstQuartile: q1,
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
258
|
+
columnFirstQuartile: q1.toFixed(2),
|
|
259
|
+
columnMin: Number(q1 - 1.5 * iqr).toFixed(2),
|
|
260
|
+
columnCount: filteredDataValues.reduce((partialSum, a) => partialSum + a, 0),
|
|
261
|
+
columnSd: d3.deviation(filteredDataValues).toFixed(2),
|
|
262
|
+
columnMean: d3.mean(filteredDataValues).toFixed(2),
|
|
263
|
+
columnIqr: iqr.toFixed(2),
|
|
270
264
|
columnOutliers: outliers,
|
|
271
|
-
values: filteredDataValues
|
|
265
|
+
values: filteredDataValues,
|
|
266
|
+
nonOutlierValues: nonOutliers
|
|
272
267
|
})
|
|
273
268
|
})
|
|
274
269
|
|
|
270
|
+
// make deep copy so we can remove some fields for data
|
|
271
|
+
// this appears to be the easiest option instead of running logic against the datatable cell...
|
|
272
|
+
tableData = JSON.parse(JSON.stringify(plots))
|
|
273
|
+
tableData.map(table => {
|
|
274
|
+
delete table.columnIqr
|
|
275
|
+
delete table.nonOutlierValues
|
|
276
|
+
})
|
|
277
|
+
|
|
275
278
|
// any other data we can add to boxplots
|
|
276
279
|
newConfig.boxplot['allValues'] = allValues
|
|
277
280
|
newConfig.boxplot['categories'] = groups
|
|
278
|
-
newConfig.boxplot.
|
|
281
|
+
newConfig.boxplot.plots = plots
|
|
282
|
+
newConfig.boxplot.tableData = tableData
|
|
279
283
|
}
|
|
280
284
|
|
|
281
285
|
if (newConfig.visualizationType === 'Combo' && newConfig.series) {
|
|
@@ -328,6 +332,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
328
332
|
|
|
329
333
|
data.forEach(row => {
|
|
330
334
|
const value = row[columnName]
|
|
335
|
+
//@ts-ignore
|
|
331
336
|
if (value && false === values.includes(value)) {
|
|
332
337
|
values.push(value)
|
|
333
338
|
}
|
|
@@ -352,7 +357,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
352
357
|
}
|
|
353
358
|
|
|
354
359
|
// Observes changes to outermost container and changes viewport size in state
|
|
355
|
-
const resizeObserver
|
|
360
|
+
const resizeObserver = new ResizeObserver(entries => {
|
|
356
361
|
for (let entry of entries) {
|
|
357
362
|
let { width, height } = entry.contentRect
|
|
358
363
|
let newViewport = getViewport(width)
|
|
@@ -408,13 +413,13 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
408
413
|
* Another useEffect listens to externalFilterChanges and updates the config.
|
|
409
414
|
*/
|
|
410
415
|
useEffect(() => {
|
|
411
|
-
const handleFilterData =
|
|
416
|
+
const handleFilterData = e => {
|
|
412
417
|
let tmp = []
|
|
413
418
|
tmp.push(e.detail)
|
|
414
419
|
setExternalFilters(tmp)
|
|
415
420
|
}
|
|
416
421
|
|
|
417
|
-
subscribe('cove_filterData',
|
|
422
|
+
subscribe('cove_filterData', e => handleFilterData(e))
|
|
418
423
|
|
|
419
424
|
return () => {
|
|
420
425
|
unsubscribe('cove_filterData', handleFilterData)
|
|
@@ -447,6 +452,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
447
452
|
|
|
448
453
|
// Load data when configObj data changes
|
|
449
454
|
if (configObj) {
|
|
455
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
450
456
|
useEffect(() => {
|
|
451
457
|
loadConfig()
|
|
452
458
|
}, [configObj.data])
|
|
@@ -523,7 +529,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
523
529
|
|
|
524
530
|
const section = config.orientation === 'horizontal' ? 'yAxis' : 'xAxis'
|
|
525
531
|
|
|
526
|
-
const parseDate =
|
|
532
|
+
const parseDate = dateString => {
|
|
527
533
|
let date = timeParse(config.runtime[section].dateParseFormat)(dateString)
|
|
528
534
|
if (!date) {
|
|
529
535
|
config.runtime.editorErrorMessage = `Error parsing date "${dateString}". Try reviewing your data and date parse settings in the X Axis section.`
|
|
@@ -533,20 +539,18 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
533
539
|
}
|
|
534
540
|
}
|
|
535
541
|
|
|
536
|
-
const formatDate =
|
|
542
|
+
const formatDate = date => {
|
|
537
543
|
return timeFormat(config.runtime[section].dateDisplayFormat)(date)
|
|
538
544
|
}
|
|
539
545
|
|
|
540
|
-
const DownloadButton = ({ data }
|
|
546
|
+
const DownloadButton = ({ data }, type = 'link') => {
|
|
541
547
|
const fileName = `${config.title.substring(0, 50)}.csv`
|
|
542
548
|
|
|
543
549
|
const csvData = Papa.unparse(data)
|
|
544
550
|
|
|
545
551
|
const saveBlob = () => {
|
|
546
|
-
//@ts-ignore
|
|
547
552
|
if (typeof window.navigator.msSaveBlob === 'function') {
|
|
548
553
|
const dataBlob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' })
|
|
549
|
-
//@ts-ignore
|
|
550
554
|
window.navigator.msSaveBlob(dataBlob, fileName)
|
|
551
555
|
}
|
|
552
556
|
}
|
|
@@ -566,6 +570,16 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
566
570
|
}
|
|
567
571
|
}
|
|
568
572
|
|
|
573
|
+
// function calculates the width of given text and its font-size
|
|
574
|
+
function getTextWidth(text, font) {
|
|
575
|
+
const canvas = document.createElement('canvas')
|
|
576
|
+
const context = canvas.getContext('2d')
|
|
577
|
+
|
|
578
|
+
context.font = font || getComputedStyle(document.body).font
|
|
579
|
+
|
|
580
|
+
return Math.ceil(context.measureText(text).width)
|
|
581
|
+
}
|
|
582
|
+
|
|
569
583
|
// Format numeric data based on settings in config
|
|
570
584
|
const formatNumber = (num, axis) => {
|
|
571
585
|
// if num is NaN return num
|
|
@@ -617,7 +631,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
617
631
|
// Use commas also updates bars and the data table
|
|
618
632
|
// We can't use commas when we're formatting the dataFormatted number
|
|
619
633
|
// Example: commas -> 12,000; abbreviated -> 12k (correct); abbreviated & commas -> 12 (incorrect)
|
|
620
|
-
if (axis === 'left' && commas && abbreviated) {
|
|
634
|
+
if ((axis === 'left' && commas && abbreviated) || (axis === 'bottom' && commas && abbreviated)) {
|
|
621
635
|
num = num
|
|
622
636
|
} else {
|
|
623
637
|
num = num.toLocaleString('en-US', stringFormattingOptions)
|
|
@@ -660,7 +674,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
660
674
|
Line: <LinearChart />,
|
|
661
675
|
Combo: <LinearChart />,
|
|
662
676
|
Pie: <PieChart />,
|
|
663
|
-
'Box Plot': <LinearChart
|
|
677
|
+
'Box Plot': <LinearChart />,
|
|
678
|
+
'Scatter Plot': <LinearChart />
|
|
664
679
|
}
|
|
665
680
|
|
|
666
681
|
const missingRequiredSections = () => {
|
|
@@ -750,8 +765,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
750
765
|
)
|
|
751
766
|
}
|
|
752
767
|
|
|
753
|
-
const getXAxisData =
|
|
754
|
-
const getYAxisData = (d
|
|
768
|
+
const getXAxisData = d => (config.runtime.xAxis.type === 'date' ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
|
|
769
|
+
const getYAxisData = (d, seriesKey) => d[seriesKey]
|
|
755
770
|
|
|
756
771
|
const contextValues = {
|
|
757
772
|
getXAxisData,
|
|
@@ -785,7 +800,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
785
800
|
dynamicLegendItems,
|
|
786
801
|
setDynamicLegendItems,
|
|
787
802
|
filterData,
|
|
788
|
-
|
|
803
|
+
isNumber,
|
|
804
|
+
cleanData,
|
|
805
|
+
imageId,
|
|
806
|
+
getTextWidth
|
|
789
807
|
}
|
|
790
808
|
|
|
791
809
|
const classes = ['cdc-open-viz-module', 'type-chart', `${currentViewport}`, `font-${config.fontSize}`, `${config.theme}`]
|
|
@@ -795,10 +813,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
795
813
|
isEditor && classes.push('isEditor')
|
|
796
814
|
|
|
797
815
|
return (
|
|
798
|
-
<
|
|
816
|
+
<ConfigContext.Provider value={contextValues}>
|
|
799
817
|
<div className={`${classes.join(' ')}`} ref={outerContainerRef} data-lollipop={config.isLollipopChart} data-download-id={imageId}>
|
|
800
818
|
{body}
|
|
801
819
|
</div>
|
|
802
|
-
</
|
|
820
|
+
</ConfigContext.Provider>
|
|
803
821
|
)
|
|
804
822
|
}
|