@cdc/chart 4.23.5 → 4.23.7
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 +29320 -28775
- package/examples/feature/__data__/planet-example-data.json +15 -16
- package/examples/feature/bar/new.json +561 -0
- package/examples/feature/combo/right-issues.json +190 -0
- package/examples/feature/forecasting/combo-forecasting.json +271 -0
- package/examples/feature/forecasting/effective_reproduction.json +57 -8
- package/examples/feature/forecasting/forecasting.json +5334 -0
- package/examples/feature/forecasting/index.json +203 -0
- package/examples/feature/line/line-chart.json +12 -12
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +167 -20
- package/examples/gallery/line/line.json +173 -1
- package/index.html +14 -8
- package/package.json +2 -2
- package/src/CdcChart.jsx +104 -26
- package/src/components/AreaChart.jsx +23 -149
- package/src/components/BarChart.jsx +87 -15
- package/src/components/DataTable.jsx +35 -14
- package/src/components/EditorPanel.jsx +1829 -1954
- package/src/components/Forecasting.jsx +84 -0
- package/src/components/Legend.jsx +191 -275
- package/src/components/LineChart.jsx +34 -7
- package/src/components/LinearChart.jsx +510 -101
- package/src/components/Series.jsx +554 -0
- package/src/components/SparkLine.jsx +3 -3
- package/src/data/initial-state.js +13 -5
- package/src/hooks/useMinMax.js +37 -0
- package/src/hooks/useRightAxis.js +9 -2
- package/src/hooks/useScales.js +7 -13
- package/src/scss/main.scss +4 -17
- package/LICENSE +0 -201
package/index.html
CHANGED
|
@@ -34,22 +34,27 @@
|
|
|
34
34
|
-->
|
|
35
35
|
|
|
36
36
|
<!-- GENERIC CHART TYPES -->
|
|
37
|
-
<div class="react-container" data-config="/examples/
|
|
37
|
+
<!-- <div class="react-container" data-config="/examples/private/datatable-issue.json"></div> -->
|
|
38
|
+
<!-- <div class="react-container" data-config="/examples/feature/filters/filter-testing.json"></div> -->
|
|
38
39
|
<!-- <div class="react-container" data-config="/examples/feature/pie/planet-pie-example-config.json"></div> -->
|
|
39
40
|
<!-- <div class="react-container" data-config="/examples/feature/line/line-chart.json"></div> -->
|
|
40
|
-
|
|
41
|
-
<!-- <div class="react-container" data-config="/examples/feature/
|
|
42
|
-
<!-- <div class="react-container" data-config="/examples/feature/
|
|
41
|
+
<div class="react-container" data-config="/examples/feature/forecasting/index.json"></div>
|
|
42
|
+
<!-- <div class="react-container" data-config="/examples/feature/forecasting/forecasting.json"></div> -->
|
|
43
|
+
<!-- <div class="react-container" data-config="/examples/feature/forecasting/combo-forecasting.json"></div> -->
|
|
44
|
+
<!-- <div class="react-container" data-config="/examples/feature/forecasting/effective_reproduction.json"></div> -->
|
|
43
45
|
<!-- <div class="react-container" data-config="/examples/feature/area/area-chart-date.json"></div> -->
|
|
46
|
+
<!-- <div class="react-container" data-config="/examples/feature/area/area-chart-category.json"></div> -->
|
|
44
47
|
<!-- <div class="react-container" data-config="/examples/feature/scatterplot/scatterplot.json"></div> -->
|
|
45
48
|
<!-- <div class="react-container" data-config="/examples/feature/deviation/planet-deviation-config.json"></div> -->
|
|
46
49
|
<!-- <div class="react-container" data-config="/examples/feature/boxplot/boxplot.json"></div> -->
|
|
47
50
|
<!-- <div class="react-container" data-config="/examples/feature/combo/planet-combo-example-config.json"></div> -->
|
|
51
|
+
<!-- <div class="react-container" data-config="/examples/feature/combo/right-issues.json"></div> -->
|
|
48
52
|
<!-- <div class="react-container" data-config="/examples/feature/paired-bar/paired-bar-example.json"></div> -->
|
|
49
53
|
|
|
50
54
|
<!-- BAR -->
|
|
51
55
|
<!-- <div class="react-container" data-config="/examples/feature/bar/planet-example-config.json"></div> -->
|
|
52
56
|
<!-- <div class="react-container" data-config="/examples/feature/bar/planet-chart-horizontal-example-config.json"></div> -->
|
|
57
|
+
<!-- <div class="react-container" data-config="/examples/feature/bar/new.json"></div> -->
|
|
53
58
|
<!-- <div class="react-container" data-config="/examples/feature/bar/example-bar-chart.json"></div> -->
|
|
54
59
|
<!-- <div class="react-container" data-config="/examples/feature/bar/horizontal-chart-max-increase.json"></div> -->
|
|
55
60
|
<!-- <div class="react-container" data-config="/examples/feature/bar/horizontal-chart.json"></div> -->
|
|
@@ -60,11 +65,11 @@
|
|
|
60
65
|
<!-- <div class="react-container" data-config="/examples/feature/sparkline/example-sparkline.json"></div> -->
|
|
61
66
|
|
|
62
67
|
<!-- TESTS DATE EXCLUSIONS -->
|
|
63
|
-
|
|
68
|
+
<div class="react-container" data-config="/examples/feature/tests-date-exclusions/date-exclusions-config.json"></div>
|
|
64
69
|
<!-- <div class="react-container" data-config="/examples/feature/tests-case-rate/case-rate-example-config.json"></div> -->
|
|
65
70
|
|
|
66
71
|
<!-- TESTS BIG SMALL-->
|
|
67
|
-
<!-- <div class="react-container" data-config="/examples/feature/tests-big-small/big-small-test-line.json"></div>
|
|
72
|
+
<!-- <div class="react-container" data-config="/examples/feature/tests-big-small/big-small-test-line.json"></div> -->
|
|
68
73
|
<!-- <div class="react-container" data-config="/examples/feature/tests-big-small/big-small-test-bar.json"></div> -->
|
|
69
74
|
<!-- <div class="react-container" data-config="/examples/feature/tests-big-small/big-small-test-negative.json"></div> -->
|
|
70
75
|
<!-- <div class="react-container" data-config="/examples/feature/tests-big-small/line-chart-max-increase.json"></div> -->
|
|
@@ -74,7 +79,7 @@
|
|
|
74
79
|
<!-- <div class="react-container" data-config="/examples/feature/tests-non-numerics/example-combo-bar-nonnumeric.json"></div> -->
|
|
75
80
|
<!-- <div class="react-container" data-config="/examples/feature/tests-non-numerics/example-bar-chart-nonnumeric.json"></div> -->
|
|
76
81
|
<!-- <div class="react-container" data-config="/examples/feature/tests-non-numerics/sparkline-chart-nonnumeric.json"></div> -->
|
|
77
|
-
<div class="react-container" data-config="/examples/feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json"></div>
|
|
82
|
+
<!-- <div class="react-container" data-config="/examples/feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json"></div> -->
|
|
78
83
|
|
|
79
84
|
<!-- TESTS CUTOFF -->
|
|
80
85
|
<!-- <div class="react-container" data-config="/examples/feature/tests-cutoff/cutoff-example-config.json"></div> -->
|
|
@@ -100,7 +105,8 @@
|
|
|
100
105
|
|
|
101
106
|
<!-- VERTICAL BAR CHARTS -->
|
|
102
107
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/combo-line-chart.json"></div> -->
|
|
103
|
-
<!-- <div class="react-container" data-config="/examples/
|
|
108
|
+
<!-- <div class="react-container" data-config="/examples/private/problem.json"></div> -->
|
|
109
|
+
<div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json"></div>
|
|
104
110
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json"></div> -->
|
|
105
111
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-confidence.json"></div> -->
|
|
106
112
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-confidence.json"></div> -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/chart",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.7",
|
|
4
4
|
"description": "React component for visualizing tabular data in various types of charts",
|
|
5
5
|
"moduleName": "CdcChart",
|
|
6
6
|
"main": "dist/cdcchart",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"react": "^18.2.0",
|
|
58
58
|
"react-dom": "^18.2.0"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "6c7ac5215dcf3bc1cc7d199089c8c2e75f53a93e",
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"resize-observer-polyfill": "^1.5.1"
|
|
63
63
|
}
|
package/src/CdcChart.jsx
CHANGED
|
@@ -31,7 +31,7 @@ import defaults from './data/initial-state'
|
|
|
31
31
|
import EditorPanel from './components/EditorPanel'
|
|
32
32
|
import Loading from '@cdc/core/components/Loading'
|
|
33
33
|
import Filters from '@cdc/core/components/Filters'
|
|
34
|
-
import
|
|
34
|
+
import MediaControls from '@cdc/core/components/MediaControls'
|
|
35
35
|
|
|
36
36
|
// Helpers
|
|
37
37
|
import numberFromString from '@cdc/core/helpers/numberFromString'
|
|
@@ -39,6 +39,7 @@ import getViewport from '@cdc/core/helpers/getViewport'
|
|
|
39
39
|
import { DataTransform } from '@cdc/core/helpers/DataTransform'
|
|
40
40
|
import cacheBustingString from '@cdc/core/helpers/cacheBustingString'
|
|
41
41
|
import isNumber from '@cdc/core/helpers/isNumber'
|
|
42
|
+
import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
|
|
42
43
|
|
|
43
44
|
import './scss/main.scss'
|
|
44
45
|
// load both then config below determines which to use
|
|
@@ -72,7 +73,10 @@ const hashObj = row => {
|
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
// * FILE REVIEW
|
|
77
|
+
// TODO: @tturnerswdev33 - remove/fix mentions of runtimeLegend that were added
|
|
78
|
+
|
|
79
|
+
export default function CdcChart({ configUrl, config: configObj, isEditor = false, isDebug = false, isDashboard = false, setConfig: setParentConfig, setEditing, hostname, link, setSharedFilter, setSharedFilterValue, dashboardConfig }) {
|
|
76
80
|
const transform = new DataTransform()
|
|
77
81
|
const [loading, setLoading] = useState(true)
|
|
78
82
|
const [colorScale, setColorScale] = useState(null)
|
|
@@ -261,10 +265,20 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
261
265
|
data = await fetch(response.dataUrl + `?v=${cacheBustingString()}`)
|
|
262
266
|
.then(response => response.text())
|
|
263
267
|
.then(responseText => {
|
|
268
|
+
// for every comma NOT inside quotes, replace with a pipe delimiter
|
|
269
|
+
// - this will let commas inside the quotes not be parsed as a new column
|
|
270
|
+
// - Limitation: if a delimiter other than comma is used in the csv this will break
|
|
271
|
+
// Examples of other delimiters that would break: tab
|
|
272
|
+
responseText = responseText.replace(/(".*?")|,/g, (...m) => m[1] || '|')
|
|
273
|
+
// now strip the double quotes
|
|
274
|
+
responseText = responseText.replace(/["]+/g, '')
|
|
264
275
|
const parsedCsv = Papa.parse(responseText, {
|
|
276
|
+
//quotes: "true", // dont need these
|
|
277
|
+
//quoteChar: "'", // has no effect that I can tell
|
|
265
278
|
header: true,
|
|
266
279
|
dynamicTyping: true,
|
|
267
|
-
skipEmptyLines: true
|
|
280
|
+
skipEmptyLines: true,
|
|
281
|
+
delimiter: '|' // we are using pipe symbol as delimiter so setting this explicitly for Papa.parse
|
|
268
282
|
})
|
|
269
283
|
return parsedCsv.data
|
|
270
284
|
})
|
|
@@ -302,7 +316,13 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
302
316
|
}
|
|
303
317
|
if (undefined === newConfig.table.show) newConfig.table.show = !isDashboard
|
|
304
318
|
|
|
305
|
-
|
|
319
|
+
newConfig.series.map(series => {
|
|
320
|
+
if (!series.tooltip) series.tooltip = true
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
const processedConfig = { ...(await coveUpdateWorker(newConfig)) }
|
|
324
|
+
|
|
325
|
+
updateConfig(processedConfig, data)
|
|
306
326
|
}
|
|
307
327
|
|
|
308
328
|
const updateConfig = (newConfig, dataOverride = undefined) => {
|
|
@@ -315,7 +335,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
315
335
|
}
|
|
316
336
|
})
|
|
317
337
|
|
|
318
|
-
// Loop through and set initial data with exclusions - this should persist through any following data transformations (ie. filters)
|
|
319
338
|
let newExcludedData
|
|
320
339
|
|
|
321
340
|
if (newConfig.exclusions && newConfig.exclusions.active) {
|
|
@@ -378,7 +397,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
378
397
|
newConfig.runtime.seriesKeys = newConfig.series
|
|
379
398
|
? newConfig.series.map(series => {
|
|
380
399
|
newConfig.runtime.seriesLabels[series.dataKey] = series.label || series.dataKey
|
|
381
|
-
newConfig.runtime.seriesLabelsAll.push(series.label || series.dataKey)
|
|
400
|
+
newConfig.runtime.seriesLabelsAll.push(series.name || series.label || series.dataKey)
|
|
382
401
|
return series.dataKey
|
|
383
402
|
})
|
|
384
403
|
: []
|
|
@@ -512,11 +531,15 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
512
531
|
newConfig.runtime.barSeriesKeys = []
|
|
513
532
|
newConfig.runtime.lineSeriesKeys = []
|
|
514
533
|
newConfig.runtime.areaSeriesKeys = []
|
|
534
|
+
newConfig.runtime.forecastingSeriesKeys = []
|
|
515
535
|
|
|
516
536
|
newConfig.series.forEach(series => {
|
|
517
537
|
if (series.type === 'Area Chart') {
|
|
518
538
|
newConfig.runtime.areaSeriesKeys.push(series)
|
|
519
539
|
}
|
|
540
|
+
if (series.type === 'Forecasting') {
|
|
541
|
+
newConfig.runtime.forecastingSeriesKeys.push(series)
|
|
542
|
+
}
|
|
520
543
|
if (series.type === 'Bar') {
|
|
521
544
|
newConfig.runtime.barSeriesKeys.push(series.dataKey)
|
|
522
545
|
}
|
|
@@ -525,6 +548,17 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
525
548
|
}
|
|
526
549
|
})
|
|
527
550
|
}
|
|
551
|
+
|
|
552
|
+
if (newConfig.visualizationType === 'Forecasting' && newConfig.series) {
|
|
553
|
+
newConfig.runtime.forecastingSeriesKeys = []
|
|
554
|
+
|
|
555
|
+
newConfig.series.forEach(series => {
|
|
556
|
+
if (series.type === 'Forecasting') {
|
|
557
|
+
newConfig.runtime.forecastingSeriesKeys.push(series)
|
|
558
|
+
}
|
|
559
|
+
})
|
|
560
|
+
}
|
|
561
|
+
|
|
528
562
|
if (newConfig.visualizationType === 'Area Chart' && newConfig.series) {
|
|
529
563
|
newConfig.runtime.areaSeriesKeys = []
|
|
530
564
|
|
|
@@ -535,10 +569,11 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
535
569
|
})
|
|
536
570
|
}
|
|
537
571
|
|
|
538
|
-
if ((
|
|
572
|
+
if ((newConfig.visualizationType === 'Bar' && newConfig.orientation === 'horizontal') || ['Deviation Bar', 'Paired Bar'].includes(newConfig.visualizationType)) {
|
|
539
573
|
newConfig.runtime.xAxis = newConfig.yAxis
|
|
540
574
|
newConfig.runtime.yAxis = newConfig.xAxis
|
|
541
575
|
newConfig.runtime.horizontal = true
|
|
576
|
+
newConfig.orientation = 'horizontal'
|
|
542
577
|
} else {
|
|
543
578
|
newConfig.runtime.xAxis = newConfig.xAxis
|
|
544
579
|
newConfig.runtime.yAxis = newConfig.yAxis
|
|
@@ -740,7 +775,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
740
775
|
const newSeriesHighlight = []
|
|
741
776
|
|
|
742
777
|
// If we're highlighting all the series, reset them
|
|
743
|
-
if (seriesHighlight.length + 1 === config.runtime.seriesKeys.length && !config.legend.dynamicLegend) {
|
|
778
|
+
if (seriesHighlight.length + 1 === config.runtime.seriesKeys.length && !config.legend.dynamicLegend && config.visualizationType !== 'Forecasting') {
|
|
744
779
|
highlightReset()
|
|
745
780
|
return
|
|
746
781
|
}
|
|
@@ -764,6 +799,21 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
764
799
|
} else {
|
|
765
800
|
newSeriesHighlight.push(newHighlight)
|
|
766
801
|
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* pushDataKeyBySeriesName
|
|
805
|
+
* - pushes series.dataKey into the series highlight based on the found series.name
|
|
806
|
+
* @param {String} value
|
|
807
|
+
*/
|
|
808
|
+
const pushDataKeyBySeriesName = value => {
|
|
809
|
+
let matchingSeries = config.series.filter(series => series.name === value.text)
|
|
810
|
+
if (matchingSeries?.length > 0) {
|
|
811
|
+
newSeriesHighlight.push(matchingSeries[0].dataKey)
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
pushDataKeyBySeriesName(label)
|
|
816
|
+
|
|
767
817
|
setSeriesHighlight(newSeriesHighlight)
|
|
768
818
|
}
|
|
769
819
|
|
|
@@ -822,8 +872,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
822
872
|
return num + unit
|
|
823
873
|
}
|
|
824
874
|
|
|
825
|
-
// Format numeric data based on settings in config
|
|
826
|
-
const formatNumber = (num, axis, shouldAbbreviate = false) => {
|
|
875
|
+
// Format numeric data based on settings in config OR from passed in settings for Additional Columns
|
|
876
|
+
const formatNumber = (num, axis, shouldAbbreviate = false, addColPrefix, addColSuffix, addColRoundTo) => {
|
|
827
877
|
// if num is NaN return num
|
|
828
878
|
if (isNaN(num) || !num) return num
|
|
829
879
|
// Check if the input number is negative
|
|
@@ -845,10 +895,17 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
845
895
|
let original = num
|
|
846
896
|
let stringFormattingOptions
|
|
847
897
|
if (axis === 'left') {
|
|
898
|
+
let roundToPlace
|
|
899
|
+
if (addColRoundTo !== undefined) {
|
|
900
|
+
// if its an Additional Column
|
|
901
|
+
roundToPlace = addColRoundTo ? Number(addColRoundTo) : 0
|
|
902
|
+
} else {
|
|
903
|
+
roundToPlace = roundTo ? Number(roundTo) : 0
|
|
904
|
+
}
|
|
848
905
|
stringFormattingOptions = {
|
|
849
|
-
useGrouping: config.dataFormat.commas ? true : false,
|
|
850
|
-
minimumFractionDigits:
|
|
851
|
-
maximumFractionDigits:
|
|
906
|
+
useGrouping: addColRoundTo ? true : config.dataFormat.commas ? true : false,
|
|
907
|
+
minimumFractionDigits: roundToPlace,
|
|
908
|
+
maximumFractionDigits: roundToPlace
|
|
852
909
|
}
|
|
853
910
|
}
|
|
854
911
|
|
|
@@ -907,8 +964,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
907
964
|
num = abbreviateNumber(parseFloat(num))
|
|
908
965
|
}
|
|
909
966
|
|
|
910
|
-
if (
|
|
911
|
-
result
|
|
967
|
+
if (addColPrefix && axis === 'left') {
|
|
968
|
+
result = addColPrefix + result
|
|
969
|
+
} else {
|
|
970
|
+
if (prefix && axis === 'left') {
|
|
971
|
+
result = prefix + result
|
|
972
|
+
}
|
|
912
973
|
}
|
|
913
974
|
|
|
914
975
|
if (rightPrefix && axis === 'right') {
|
|
@@ -921,8 +982,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
921
982
|
|
|
922
983
|
result += num
|
|
923
984
|
|
|
924
|
-
if (
|
|
925
|
-
result +=
|
|
985
|
+
if (addColSuffix && axis === 'left') {
|
|
986
|
+
result += addColSuffix
|
|
987
|
+
} else {
|
|
988
|
+
if (suffix && axis === 'left') {
|
|
989
|
+
result += suffix
|
|
990
|
+
}
|
|
926
991
|
}
|
|
927
992
|
|
|
928
993
|
if (rightSuffix && axis === 'right') {
|
|
@@ -942,6 +1007,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
942
1007
|
// Select appropriate chart type
|
|
943
1008
|
const chartComponents = {
|
|
944
1009
|
'Paired Bar': <LinearChart />,
|
|
1010
|
+
Forecasting: <LinearChart />,
|
|
945
1011
|
Bar: <LinearChart />,
|
|
946
1012
|
Line: <LinearChart />,
|
|
947
1013
|
Combo: <LinearChart />,
|
|
@@ -953,6 +1019,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
953
1019
|
}
|
|
954
1020
|
|
|
955
1021
|
const missingRequiredSections = () => {
|
|
1022
|
+
if (config.visualizationType === 'Forecasting') return false // skip required checks for now.
|
|
956
1023
|
if (config.visualizationType === 'Pie') {
|
|
957
1024
|
if (undefined === config?.yAxis.dataKey) {
|
|
958
1025
|
return true
|
|
@@ -1069,6 +1136,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1069
1136
|
}
|
|
1070
1137
|
|
|
1071
1138
|
const clean = data => {
|
|
1139
|
+
// cleaning is deleting data we need in forecasting charts.
|
|
1140
|
+
if (config.visualizationType === 'Forecasting') return data
|
|
1072
1141
|
return config?.xAxis?.dataKey ? transform.cleanData(data, config.xAxis.dataKey) : data
|
|
1073
1142
|
}
|
|
1074
1143
|
|
|
@@ -1107,10 +1176,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1107
1176
|
{config?.introText && <section className='introText'>{parse(config.introText)}</section>}
|
|
1108
1177
|
<div
|
|
1109
1178
|
style={{ marginBottom: config.legend.position !== 'bottom' && config.orientation === 'horizontal' ? `${config.runtime.xAxis.size}px` : '0px' }}
|
|
1110
|
-
className={`chart-container ${config.legend.position === 'bottom' ? 'bottom' : ''}${config.legend.hide ? ' legend-hidden' : ''}${lineDatapointClass}${barBorderClass} ${contentClasses.join(' ')}`}
|
|
1179
|
+
className={`chart-container p-relative ${config.legend.position === 'bottom' ? 'bottom' : ''}${config.legend.hide ? ' legend-hidden' : ''}${lineDatapointClass}${barBorderClass} ${contentClasses.join(' ')}`}
|
|
1111
1180
|
>
|
|
1112
1181
|
{/* All charts except sparkline */}
|
|
1113
|
-
{config.visualizationType !== 'Spark Line' && chartComponents[visualizationType]}
|
|
1182
|
+
{config.visualizationType !== 'Spark Line' && chartComponents[config.visualizationType]}
|
|
1114
1183
|
|
|
1115
1184
|
{/* Sparkline */}
|
|
1116
1185
|
{config.visualizationType === 'Spark Line' && (
|
|
@@ -1136,10 +1205,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1136
1205
|
{description && config.visualizationType !== 'Spark Line' && <div className='subtext'>{parse(description)}</div>}
|
|
1137
1206
|
|
|
1138
1207
|
{/* buttons */}
|
|
1139
|
-
<
|
|
1140
|
-
{config.table.showDownloadImgButton && <
|
|
1141
|
-
{config.table.showDownloadPdfButton && <
|
|
1142
|
-
</
|
|
1208
|
+
<MediaControls.Section classes={['download-buttons']}>
|
|
1209
|
+
{config.table.showDownloadImgButton && <MediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={config} elementToCapture={imageId} />}
|
|
1210
|
+
{config.table.showDownloadPdfButton && <MediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={config} elementToCapture={imageId} />}
|
|
1211
|
+
</MediaControls.Section>
|
|
1143
1212
|
|
|
1144
1213
|
{/* Data Table */}
|
|
1145
1214
|
{config.xAxis.dataKey && config.table.show && config.visualizationType !== 'Spark Line' && (
|
|
@@ -1147,9 +1216,9 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1147
1216
|
config={config}
|
|
1148
1217
|
rawData={config.data}
|
|
1149
1218
|
runtimeData={filteredData || excludedData}
|
|
1150
|
-
//navigationHandler={navigationHandler}
|
|
1219
|
+
//navigationHandler={navigationHandler} // do we need this? What does it do?
|
|
1151
1220
|
expandDataTable={config.table.expanded}
|
|
1152
|
-
//headerColor={general.headerColor}
|
|
1221
|
+
//headerColor={general.headerColor} // have this in map but not chart
|
|
1153
1222
|
columns={config.columns}
|
|
1154
1223
|
showDownloadButton={config.general.showDownloadButton}
|
|
1155
1224
|
runtimeLegend={dynamicLegendItems}
|
|
@@ -1170,6 +1239,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1170
1239
|
outerContainerRef={outerContainerRef}
|
|
1171
1240
|
imageRef={imageId}
|
|
1172
1241
|
isDebug={isDebug}
|
|
1242
|
+
isEditor={isEditor}
|
|
1173
1243
|
/>
|
|
1174
1244
|
)}
|
|
1175
1245
|
{config?.footnotes && <section className='footnotes'>{parse(config.footnotes)}</section>}
|
|
@@ -1183,7 +1253,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1183
1253
|
const getXAxisData = d => (config.runtime.xAxis.type === 'date' ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
|
|
1184
1254
|
const getYAxisData = (d, seriesKey) => d[seriesKey]
|
|
1185
1255
|
|
|
1256
|
+
const capitalize = str => {
|
|
1257
|
+
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1186
1260
|
const contextValues = {
|
|
1261
|
+
capitalize,
|
|
1187
1262
|
getXAxisData,
|
|
1188
1263
|
getYAxisData,
|
|
1189
1264
|
config,
|
|
@@ -1222,7 +1297,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1222
1297
|
isNumber,
|
|
1223
1298
|
getTextWidth,
|
|
1224
1299
|
twoColorPalette,
|
|
1225
|
-
isDebug
|
|
1300
|
+
isDebug,
|
|
1301
|
+
setSharedFilter,
|
|
1302
|
+
setSharedFilterValue,
|
|
1303
|
+
dashboardConfig
|
|
1226
1304
|
}
|
|
1227
1305
|
|
|
1228
1306
|
const classes = ['cdc-open-viz-module', 'type-chart', `${currentViewport}`, `font-${config.fontSize}`, `${config.theme}`]
|
|
@@ -1,124 +1,27 @@
|
|
|
1
|
-
import React, { useContext,
|
|
1
|
+
import React, { useContext, memo } from 'react'
|
|
2
2
|
|
|
3
3
|
// cdc
|
|
4
4
|
import ConfigContext from '../ConfigContext'
|
|
5
5
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
6
|
-
import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
|
|
7
6
|
|
|
8
7
|
// visx & d3
|
|
9
8
|
import * as allCurves from '@visx/curve'
|
|
10
9
|
import { AreaClosed, LinePath, Bar } from '@visx/shape'
|
|
11
10
|
import { Group } from '@visx/group'
|
|
12
|
-
import { useTooltip, useTooltipInPortal, defaultStyles, Tooltip } from '@visx/tooltip'
|
|
13
|
-
import { localPoint } from '@visx/event'
|
|
14
|
-
import { bisector } from 'd3-array'
|
|
15
11
|
|
|
16
|
-
const
|
|
12
|
+
const AreaChart = ({ xScale, yScale, yMax, xMax, chartRef, handleTooltipMouseOver, handleTooltipMouseOff, tooltipData }) => {
|
|
17
13
|
// enable various console logs in the file
|
|
18
14
|
const DEBUG = false
|
|
19
|
-
const [chartPosition, setChartPosition] = useState(null)
|
|
20
|
-
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
setChartPosition(chartRef.current.getBoundingClientRect())
|
|
23
|
-
}, [chartRef])
|
|
24
15
|
|
|
25
16
|
// import data from context
|
|
26
|
-
const { transformedData: data, config, handleLineType, parseDate,
|
|
27
|
-
const tooltip_id = `cdc-open-viz-tooltip-${config.runtime.uniqueId}`
|
|
28
|
-
|
|
29
|
-
// import tooltip helpers
|
|
30
|
-
const { tooltipData, showTooltip } = useTooltip()
|
|
31
|
-
|
|
32
|
-
// here we're inside of the svg,
|
|
33
|
-
// it appears we need to use TooltipInPortal.
|
|
34
|
-
const { TooltipInPortal } = useTooltipInPortal({
|
|
35
|
-
detectBounds: true,
|
|
36
|
-
// when tooltip containers are scrolled, this will correctly update the Tooltip position
|
|
37
|
-
scroll: true
|
|
38
|
-
})
|
|
17
|
+
const { transformedData: data, config, handleLineType, parseDate, seriesHighlight, colorScale, rawData } = useContext(ConfigContext)
|
|
39
18
|
|
|
40
19
|
// Draw transparent bars over the chart to get tooltip data
|
|
41
20
|
// Turn DEBUG on for additional context.
|
|
42
21
|
if (!data) return
|
|
43
|
-
let barThickness = xMax / data.length
|
|
44
|
-
let barThicknessAdjusted = barThickness * (config.barThickness || 0.8)
|
|
45
|
-
let offset = (barThickness * (1 - (config.barThickness || 0.8))) / 2
|
|
46
|
-
|
|
47
|
-
// Tooltip helper for getting data to the closest date/category hovered.
|
|
48
|
-
const getXValueFromCoordinate = x => {
|
|
49
|
-
if (config.xAxis.type === 'categorical' || config.visualizationType === 'Combo') {
|
|
50
|
-
let eachBand = xScale.step()
|
|
51
|
-
let numerator = x
|
|
52
|
-
const index = Math.floor(Number(numerator) / eachBand)
|
|
53
|
-
return xScale.domain()[index - 1] // fixes off by 1 error
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (config.xAxis.type === 'date' && config.visualizationType !== 'Combo') {
|
|
57
|
-
const bisectDate = bisector(d => parseDate(d[config.xAxis.dataKey])).left
|
|
58
|
-
const x0 = xScale.invert(x)
|
|
59
|
-
const index = bisectDate(config.data, x0, 1)
|
|
60
|
-
const val = parseDate(config.data[index - 1][config.xAxis.dataKey])
|
|
61
|
-
return val
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const handleMouseOver = (e, data) => {
|
|
66
|
-
// get the svg coordinates of the mouse
|
|
67
|
-
// and get the closest values
|
|
68
|
-
const eventSvgCoords = localPoint(e)
|
|
69
|
-
const { x, y } = eventSvgCoords
|
|
70
|
-
|
|
71
|
-
let closestXScaleValue = getXValueFromCoordinate(x)
|
|
72
|
-
let formattedDate = formatDate(closestXScaleValue)
|
|
73
|
-
|
|
74
|
-
let yScaleValues
|
|
75
|
-
if (config.xAxis.type === 'categorical') {
|
|
76
|
-
yScaleValues = data.filter(d => d[config.xAxis.dataKey] === closestXScaleValue)
|
|
77
|
-
} else {
|
|
78
|
-
yScaleValues = data.filter(d => formatDate(parseDate(d[config.xAxis.dataKey])) === formattedDate)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
let seriesToInclude = []
|
|
82
|
-
let yScaleMaxValues = []
|
|
83
|
-
let itemsToLoop = [config.runtime.xAxis.dataKey, ...config.runtime.seriesKeys]
|
|
84
|
-
|
|
85
|
-
itemsToLoop.map(seriesKey => {
|
|
86
|
-
if (!seriesKey) return
|
|
87
|
-
if (!yScaleValues[0]) return
|
|
88
|
-
for (const item of Object.entries(yScaleValues[0])) {
|
|
89
|
-
if (item[0] === seriesKey) {
|
|
90
|
-
seriesToInclude.push(item)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
// filter out the series that aren't added to the map.
|
|
96
|
-
seriesToInclude.map(series => yScaleMaxValues.push(Number(yScaleValues[0][series])))
|
|
97
|
-
if (!seriesToInclude) return
|
|
98
|
-
let tooltipDataFromSeries = Object.fromEntries(seriesToInclude) ? Object.fromEntries(seriesToInclude) : {}
|
|
99
|
-
|
|
100
|
-
let tooltipData = {}
|
|
101
|
-
tooltipData.data = tooltipDataFromSeries
|
|
102
|
-
tooltipData.dataXPosition = x + 20
|
|
103
|
-
tooltipData.dataYPosition = y - 100
|
|
104
|
-
|
|
105
|
-
let tooltipInformation = {
|
|
106
|
-
tooltipData: tooltipData,
|
|
107
|
-
tooltipTop: 0,
|
|
108
|
-
tooltipValues: yScaleValues,
|
|
109
|
-
tooltipLeft: x
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
showTooltip(tooltipInformation)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const TooltipListItem = ({ item }) => {
|
|
116
|
-
const [label, value] = item
|
|
117
|
-
return label === config.xAxis.dataKey ? `${label}: ${value}` : `${label}: ${formatNumber(value, 'left')}`
|
|
118
|
-
}
|
|
119
22
|
|
|
120
23
|
const handleX = d => {
|
|
121
|
-
return config.xAxis.type === 'date' ? xScale(parseDate(d[config.xAxis.dataKey])) : xScale(d[config.xAxis.dataKey])
|
|
24
|
+
return config.xAxis.type === 'date' ? xScale(parseDate(d[config.xAxis.dataKey], false)) : xScale(d[config.xAxis.dataKey])
|
|
122
25
|
}
|
|
123
26
|
|
|
124
27
|
const handleY = (d, index, s = undefined) => {
|
|
@@ -143,6 +46,8 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
|
|
|
143
46
|
|
|
144
47
|
if (config.xAxis.type === 'date') {
|
|
145
48
|
data.map(d => xScale(parseDate(d[config.xAxis.dataKey])))
|
|
49
|
+
} else {
|
|
50
|
+
data.map(d => xScale(d[config.xAxis.dataKey]))
|
|
146
51
|
}
|
|
147
52
|
|
|
148
53
|
return (
|
|
@@ -171,70 +76,39 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
|
|
|
171
76
|
yScale={yScale}
|
|
172
77
|
curve={curveType}
|
|
173
78
|
strokeDasharray={s.type ? handleLineType(s.type) : 0}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
{/* Transparent bar for tooltips */}
|
|
177
|
-
{/* prettier-ignore */}
|
|
178
|
-
<Bar
|
|
179
|
-
width={ Number(xMax)}
|
|
180
|
-
height={ Number(yMax)}
|
|
181
|
-
fill={DEBUG ? 'red' : 'transparent'}
|
|
182
|
-
fillOpacity={0.05}
|
|
183
|
-
style={DEBUG ? { stroke: 'black', strokeWidth: 2 } : {}}
|
|
184
|
-
onMouseMove={e => handleMouseOver(e, data)}
|
|
185
|
-
/>
|
|
79
|
+
/>
|
|
186
80
|
|
|
187
81
|
{/* circles that appear on hover */}
|
|
188
|
-
{tooltipData && Object.entries(tooltipData.data).length > 0 && (
|
|
82
|
+
{/* {tooltipData && Object.entries(tooltipData.data).length > 0 && (
|
|
189
83
|
<circle
|
|
190
84
|
cx={config.xAxis.type === 'categorical' ? xScale(tooltipData.data[config.xAxis.dataKey]) : xScale(parseDate(tooltipData.data[config.xAxis.dataKey]))}
|
|
191
|
-
cy={yScale(tooltipData.data[
|
|
85
|
+
cy={yScale(tooltipData.data[index][1])}
|
|
192
86
|
r={4.5}
|
|
193
87
|
opacity={1}
|
|
194
88
|
fillOpacity={1}
|
|
195
89
|
fill={displayArea ? (colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[s.dataKey] : s.dataKey) : '#000') : 'transparent'}
|
|
196
90
|
style={{ filter: 'unset', opacity: 1 }}
|
|
197
91
|
/>
|
|
198
|
-
)}
|
|
199
|
-
|
|
200
|
-
{/* another tool for showing bars during debug mode. */}
|
|
201
|
-
{DEBUG &&
|
|
202
|
-
data.map((item, index) => {
|
|
203
|
-
return (
|
|
204
|
-
<Bar
|
|
205
|
-
className='bar-here'
|
|
206
|
-
x={Number(barThickness * index)}
|
|
207
|
-
y={d => Number(yScale(d[config.series[index].dataKey]))}
|
|
208
|
-
yScale={yScale}
|
|
209
|
-
width={Number(barThickness)}
|
|
210
|
-
height={yMax}
|
|
211
|
-
fill={DEBUG ? 'red' : 'transparent'}
|
|
212
|
-
fillOpacity={1}
|
|
213
|
-
style={{ stroke: 'black', strokeWidth: 2 }}
|
|
214
|
-
onMouseMove={e => handleMouseOver(e, data)}
|
|
215
|
-
/>
|
|
216
|
-
)
|
|
217
|
-
})}
|
|
218
|
-
|
|
219
|
-
{tooltipData && Object.entries(tooltipData.data).length > 0 && (
|
|
220
|
-
<TooltipInPortal key={Math.random()} top={tooltipData.dataYPosition + chartPosition?.top} left={tooltipData.dataXPosition + chartPosition?.left} style={defaultStyles}>
|
|
221
|
-
<ul style={{ listStyle: 'none', paddingLeft: 'unset', fontFamily: 'sans-serif', margin: 'auto', lineHeight: '1rem' }} data-tooltip-id={tooltip_id}>
|
|
222
|
-
{typeof tooltipData === 'object' &&
|
|
223
|
-
Object.entries(tooltipData.data).map(item => (
|
|
224
|
-
<li style={{ padding: '2.5px 0' }}>
|
|
225
|
-
<TooltipListItem item={item} />
|
|
226
|
-
</li>
|
|
227
|
-
))}
|
|
228
|
-
</ul>
|
|
229
|
-
</TooltipInPortal>
|
|
230
|
-
)}
|
|
92
|
+
)} */}
|
|
231
93
|
</React.Fragment>
|
|
232
94
|
)
|
|
233
95
|
})}
|
|
96
|
+
|
|
97
|
+
{/* Transparent bar for tooltips */}
|
|
98
|
+
{/* prettier-ignore */}
|
|
99
|
+
<Bar
|
|
100
|
+
width={ Number(xMax)}
|
|
101
|
+
height={ Number(yMax)}
|
|
102
|
+
fill={DEBUG ? 'red' : 'transparent'}
|
|
103
|
+
fillOpacity={0.05}
|
|
104
|
+
style={DEBUG ? { stroke: 'black', strokeWidth: 2 } : {}}
|
|
105
|
+
onMouseMove={e => handleTooltipMouseOver(e, rawData)}
|
|
106
|
+
onMouseLeave={ handleTooltipMouseOff }
|
|
107
|
+
/>
|
|
234
108
|
</Group>
|
|
235
109
|
</ErrorBoundary>
|
|
236
110
|
)
|
|
237
111
|
)
|
|
238
112
|
}
|
|
239
113
|
|
|
240
|
-
export default
|
|
114
|
+
export default memo(AreaChart)
|