@cdc/chart 4.23.6 → 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 +25700 -26736
- package/examples/feature/__data__/planet-example-data.json +1 -1
- package/examples/feature/combo/right-issues.json +1 -1
- package/examples/feature/forecasting/combo-forecasting.json +72 -46
- package/examples/feature/forecasting/effective_reproduction.json +57 -8
- package/examples/feature/forecasting/forecasting.json +12 -3
- package/examples/feature/line/line-chart.json +11 -11
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +167 -20
- package/index.html +6 -6
- package/package.json +2 -2
- package/src/CdcChart.jsx +56 -15
- package/src/components/AreaChart.jsx +22 -133
- package/src/components/BarChart.jsx +25 -15
- package/src/components/DataTable.jsx +5 -2
- package/src/components/EditorPanel.jsx +97 -77
- package/src/components/Forecasting.jsx +23 -86
- package/src/components/Legend.jsx +10 -8
- package/src/components/LineChart.jsx +31 -6
- package/src/components/LinearChart.jsx +408 -126
- package/src/components/Series.jsx +40 -4
- package/src/data/initial-state.js +7 -3
- package/src/hooks/useMinMax.js +3 -2
- package/src/hooks/useRightAxis.js +2 -1
- package/src/scss/main.scss +4 -17
- package/LICENSE +0 -201
package/index.html
CHANGED
|
@@ -38,10 +38,9 @@
|
|
|
38
38
|
<!-- <div class="react-container" data-config="/examples/feature/filters/filter-testing.json"></div> -->
|
|
39
39
|
<!-- <div class="react-container" data-config="/examples/feature/pie/planet-pie-example-config.json"></div> -->
|
|
40
40
|
<!-- <div class="react-container" data-config="/examples/feature/line/line-chart.json"></div> -->
|
|
41
|
-
|
|
41
|
+
<div class="react-container" data-config="/examples/feature/forecasting/index.json"></div>
|
|
42
42
|
<!-- <div class="react-container" data-config="/examples/feature/forecasting/forecasting.json"></div> -->
|
|
43
43
|
<!-- <div class="react-container" data-config="/examples/feature/forecasting/combo-forecasting.json"></div> -->
|
|
44
|
-
<!-- <div class="react-container" data-config="/examples/feature/forecasting/non-combo-forecasting.json"></div> -->
|
|
45
44
|
<!-- <div class="react-container" data-config="/examples/feature/forecasting/effective_reproduction.json"></div> -->
|
|
46
45
|
<!-- <div class="react-container" data-config="/examples/feature/area/area-chart-date.json"></div> -->
|
|
47
46
|
<!-- <div class="react-container" data-config="/examples/feature/area/area-chart-category.json"></div> -->
|
|
@@ -66,18 +65,18 @@
|
|
|
66
65
|
<!-- <div class="react-container" data-config="/examples/feature/sparkline/example-sparkline.json"></div> -->
|
|
67
66
|
|
|
68
67
|
<!-- TESTS DATE EXCLUSIONS -->
|
|
69
|
-
|
|
68
|
+
<div class="react-container" data-config="/examples/feature/tests-date-exclusions/date-exclusions-config.json"></div>
|
|
70
69
|
<!-- <div class="react-container" data-config="/examples/feature/tests-case-rate/case-rate-example-config.json"></div> -->
|
|
71
70
|
|
|
72
71
|
<!-- TESTS BIG SMALL-->
|
|
73
|
-
<!-- <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> -->
|
|
74
73
|
<!-- <div class="react-container" data-config="/examples/feature/tests-big-small/big-small-test-bar.json"></div> -->
|
|
75
74
|
<!-- <div class="react-container" data-config="/examples/feature/tests-big-small/big-small-test-negative.json"></div> -->
|
|
76
75
|
<!-- <div class="react-container" data-config="/examples/feature/tests-big-small/line-chart-max-increase.json"></div> -->
|
|
77
76
|
|
|
78
77
|
<!-- TESTS NONNUMERICS -->
|
|
79
78
|
<!-- <div class="react-container" data-config="/examples/feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json"></div> -->
|
|
80
|
-
<div class="react-container" data-config="/examples/feature/tests-non-numerics/example-combo-bar-nonnumeric.json"></div>
|
|
79
|
+
<!-- <div class="react-container" data-config="/examples/feature/tests-non-numerics/example-combo-bar-nonnumeric.json"></div> -->
|
|
81
80
|
<!-- <div class="react-container" data-config="/examples/feature/tests-non-numerics/example-bar-chart-nonnumeric.json"></div> -->
|
|
82
81
|
<!-- <div class="react-container" data-config="/examples/feature/tests-non-numerics/sparkline-chart-nonnumeric.json"></div> -->
|
|
83
82
|
<!-- <div class="react-container" data-config="/examples/feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json"></div> -->
|
|
@@ -106,7 +105,8 @@
|
|
|
106
105
|
|
|
107
106
|
<!-- VERTICAL BAR CHARTS -->
|
|
108
107
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/combo-line-chart.json"></div> -->
|
|
109
|
-
<!-- <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>
|
|
110
110
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json"></div> -->
|
|
111
111
|
<!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-confidence.json"></div> -->
|
|
112
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
|
@@ -316,6 +316,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
316
316
|
}
|
|
317
317
|
if (undefined === newConfig.table.show) newConfig.table.show = !isDashboard
|
|
318
318
|
|
|
319
|
+
newConfig.series.map(series => {
|
|
320
|
+
if (!series.tooltip) series.tooltip = true
|
|
321
|
+
})
|
|
322
|
+
|
|
319
323
|
const processedConfig = { ...(await coveUpdateWorker(newConfig)) }
|
|
320
324
|
|
|
321
325
|
updateConfig(processedConfig, data)
|
|
@@ -393,7 +397,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
393
397
|
newConfig.runtime.seriesKeys = newConfig.series
|
|
394
398
|
? newConfig.series.map(series => {
|
|
395
399
|
newConfig.runtime.seriesLabels[series.dataKey] = series.label || series.dataKey
|
|
396
|
-
newConfig.runtime.seriesLabelsAll.push(series.label || series.dataKey)
|
|
400
|
+
newConfig.runtime.seriesLabelsAll.push(series.name || series.label || series.dataKey)
|
|
397
401
|
return series.dataKey
|
|
398
402
|
})
|
|
399
403
|
: []
|
|
@@ -565,10 +569,11 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
565
569
|
})
|
|
566
570
|
}
|
|
567
571
|
|
|
568
|
-
if ((
|
|
572
|
+
if ((newConfig.visualizationType === 'Bar' && newConfig.orientation === 'horizontal') || ['Deviation Bar', 'Paired Bar'].includes(newConfig.visualizationType)) {
|
|
569
573
|
newConfig.runtime.xAxis = newConfig.yAxis
|
|
570
574
|
newConfig.runtime.yAxis = newConfig.xAxis
|
|
571
575
|
newConfig.runtime.horizontal = true
|
|
576
|
+
newConfig.orientation = 'horizontal'
|
|
572
577
|
} else {
|
|
573
578
|
newConfig.runtime.xAxis = newConfig.xAxis
|
|
574
579
|
newConfig.runtime.yAxis = newConfig.yAxis
|
|
@@ -794,6 +799,21 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
794
799
|
} else {
|
|
795
800
|
newSeriesHighlight.push(newHighlight)
|
|
796
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
|
+
|
|
797
817
|
setSeriesHighlight(newSeriesHighlight)
|
|
798
818
|
}
|
|
799
819
|
|
|
@@ -852,8 +872,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
852
872
|
return num + unit
|
|
853
873
|
}
|
|
854
874
|
|
|
855
|
-
// Format numeric data based on settings in config
|
|
856
|
-
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) => {
|
|
857
877
|
// if num is NaN return num
|
|
858
878
|
if (isNaN(num) || !num) return num
|
|
859
879
|
// Check if the input number is negative
|
|
@@ -875,10 +895,17 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
875
895
|
let original = num
|
|
876
896
|
let stringFormattingOptions
|
|
877
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
|
+
}
|
|
878
905
|
stringFormattingOptions = {
|
|
879
|
-
useGrouping: config.dataFormat.commas ? true : false,
|
|
880
|
-
minimumFractionDigits:
|
|
881
|
-
maximumFractionDigits:
|
|
906
|
+
useGrouping: addColRoundTo ? true : config.dataFormat.commas ? true : false,
|
|
907
|
+
minimumFractionDigits: roundToPlace,
|
|
908
|
+
maximumFractionDigits: roundToPlace
|
|
882
909
|
}
|
|
883
910
|
}
|
|
884
911
|
|
|
@@ -937,8 +964,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
937
964
|
num = abbreviateNumber(parseFloat(num))
|
|
938
965
|
}
|
|
939
966
|
|
|
940
|
-
if (
|
|
941
|
-
result
|
|
967
|
+
if (addColPrefix && axis === 'left') {
|
|
968
|
+
result = addColPrefix + result
|
|
969
|
+
} else {
|
|
970
|
+
if (prefix && axis === 'left') {
|
|
971
|
+
result = prefix + result
|
|
972
|
+
}
|
|
942
973
|
}
|
|
943
974
|
|
|
944
975
|
if (rightPrefix && axis === 'right') {
|
|
@@ -951,8 +982,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
951
982
|
|
|
952
983
|
result += num
|
|
953
984
|
|
|
954
|
-
if (
|
|
955
|
-
result +=
|
|
985
|
+
if (addColSuffix && axis === 'left') {
|
|
986
|
+
result += addColSuffix
|
|
987
|
+
} else {
|
|
988
|
+
if (suffix && axis === 'left') {
|
|
989
|
+
result += suffix
|
|
990
|
+
}
|
|
956
991
|
}
|
|
957
992
|
|
|
958
993
|
if (rightSuffix && axis === 'right') {
|
|
@@ -1141,10 +1176,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1141
1176
|
{config?.introText && <section className='introText'>{parse(config.introText)}</section>}
|
|
1142
1177
|
<div
|
|
1143
1178
|
style={{ marginBottom: config.legend.position !== 'bottom' && config.orientation === 'horizontal' ? `${config.runtime.xAxis.size}px` : '0px' }}
|
|
1144
|
-
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(' ')}`}
|
|
1145
1180
|
>
|
|
1146
1181
|
{/* All charts except sparkline */}
|
|
1147
|
-
{config.visualizationType !== 'Spark Line' && chartComponents[visualizationType]}
|
|
1182
|
+
{config.visualizationType !== 'Spark Line' && chartComponents[config.visualizationType]}
|
|
1148
1183
|
|
|
1149
1184
|
{/* Sparkline */}
|
|
1150
1185
|
{config.visualizationType === 'Spark Line' && (
|
|
@@ -1181,9 +1216,9 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1181
1216
|
config={config}
|
|
1182
1217
|
rawData={config.data}
|
|
1183
1218
|
runtimeData={filteredData || excludedData}
|
|
1184
|
-
//navigationHandler={navigationHandler}
|
|
1219
|
+
//navigationHandler={navigationHandler} // do we need this? What does it do?
|
|
1185
1220
|
expandDataTable={config.table.expanded}
|
|
1186
|
-
//headerColor={general.headerColor}
|
|
1221
|
+
//headerColor={general.headerColor} // have this in map but not chart
|
|
1187
1222
|
columns={config.columns}
|
|
1188
1223
|
showDownloadButton={config.general.showDownloadButton}
|
|
1189
1224
|
runtimeLegend={dynamicLegendItems}
|
|
@@ -1204,6 +1239,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1204
1239
|
outerContainerRef={outerContainerRef}
|
|
1205
1240
|
imageRef={imageId}
|
|
1206
1241
|
isDebug={isDebug}
|
|
1242
|
+
isEditor={isEditor}
|
|
1207
1243
|
/>
|
|
1208
1244
|
)}
|
|
1209
1245
|
{config?.footnotes && <section className='footnotes'>{parse(config.footnotes)}</section>}
|
|
@@ -1217,7 +1253,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1217
1253
|
const getXAxisData = d => (config.runtime.xAxis.type === 'date' ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
|
|
1218
1254
|
const getYAxisData = (d, seriesKey) => d[seriesKey]
|
|
1219
1255
|
|
|
1256
|
+
const capitalize = str => {
|
|
1257
|
+
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1220
1260
|
const contextValues = {
|
|
1261
|
+
capitalize,
|
|
1221
1262
|
getXAxisData,
|
|
1222
1263
|
getYAxisData,
|
|
1223
1264
|
config,
|
|
@@ -1,126 +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, hideTooltip } = 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
|
-
|
|
45
|
-
// Tooltip helper for getting data to the closest date/category hovered.
|
|
46
|
-
const getXValueFromCoordinate = x => {
|
|
47
|
-
if (config.xAxis.type === 'categorical' || config.visualizationType === 'Combo') {
|
|
48
|
-
let eachBand = xScale.step()
|
|
49
|
-
let numerator = x
|
|
50
|
-
const index = Math.floor(Number(numerator) / eachBand)
|
|
51
|
-
return xScale.domain()[index - 1] // fixes off by 1 error
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (config.xAxis.type === 'date' && config.visualizationType !== 'Combo') {
|
|
55
|
-
const bisectDate = bisector(d => parseDate(d[config.xAxis.dataKey])).left
|
|
56
|
-
const x0 = xScale.invert(x)
|
|
57
|
-
const index = bisectDate(config.data, x0, 1)
|
|
58
|
-
const val = parseDate(config.data[index - 1][config.xAxis.dataKey])
|
|
59
|
-
return val
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const handleMouseOver = (e, data) => {
|
|
64
|
-
// get the svg coordinates of the mouse
|
|
65
|
-
// and get the closest values
|
|
66
|
-
const eventSvgCoords = localPoint(e)
|
|
67
|
-
const { x, y } = eventSvgCoords
|
|
68
|
-
|
|
69
|
-
let closestXScaleValue = getXValueFromCoordinate(x)
|
|
70
|
-
let formattedDate = formatDate(closestXScaleValue)
|
|
71
|
-
|
|
72
|
-
let yScaleValues
|
|
73
|
-
if (config.xAxis.type === 'categorical') {
|
|
74
|
-
yScaleValues = data.filter(d => d[config.xAxis.dataKey] === closestXScaleValue)
|
|
75
|
-
} else {
|
|
76
|
-
yScaleValues = data.filter(d => formatDate(parseDate(d[config.xAxis.dataKey])) === formattedDate)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
let seriesToInclude = []
|
|
80
|
-
let yScaleMaxValues = []
|
|
81
|
-
let itemsToLoop = [config.runtime.xAxis.dataKey, ...config.runtime.seriesKeys]
|
|
82
|
-
|
|
83
|
-
itemsToLoop.map(seriesKey => {
|
|
84
|
-
if (!seriesKey) return
|
|
85
|
-
if (!yScaleValues[0]) return
|
|
86
|
-
for (const item of Object.entries(yScaleValues[0])) {
|
|
87
|
-
if (item[0] === seriesKey) {
|
|
88
|
-
// let userUpdatedSeriesName = config.series.filter(series => series.dataKey === item[0])?.[0]?.name
|
|
89
|
-
// if (userUpdatedSeriesName) item[0] = userUpdatedSeriesName
|
|
90
|
-
|
|
91
|
-
seriesToInclude.push(item)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
// filter out the series that aren't added to the map.
|
|
97
|
-
seriesToInclude.map(series => yScaleMaxValues.push(Number(yScaleValues[0][series])))
|
|
98
|
-
if (!seriesToInclude) return
|
|
99
|
-
|
|
100
|
-
let tooltipDataFromSeries = Object.fromEntries(seriesToInclude) ? Object.fromEntries(seriesToInclude) : {}
|
|
101
|
-
|
|
102
|
-
let tooltipData = {}
|
|
103
|
-
tooltipData.data = tooltipDataFromSeries
|
|
104
|
-
tooltipData.dataXPosition = x + 20
|
|
105
|
-
tooltipData.dataYPosition = y - 100
|
|
106
|
-
|
|
107
|
-
let tooltipInformation = {
|
|
108
|
-
tooltipData: tooltipData,
|
|
109
|
-
tooltipTop: 0,
|
|
110
|
-
tooltipValues: yScaleValues,
|
|
111
|
-
tooltipLeft: x
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
showTooltip(tooltipInformation)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const TooltipListItem = ({ item }) => {
|
|
118
|
-
const [label, value] = item
|
|
119
|
-
return label === config.xAxis.dataKey ? `${label}: ${value}` : `${label}: ${formatNumber(value, 'left')}`
|
|
120
|
-
}
|
|
121
22
|
|
|
122
23
|
const handleX = d => {
|
|
123
|
-
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])
|
|
124
25
|
}
|
|
125
26
|
|
|
126
27
|
const handleY = (d, index, s = undefined) => {
|
|
@@ -148,6 +49,7 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
|
|
|
148
49
|
} else {
|
|
149
50
|
data.map(d => xScale(d[config.xAxis.dataKey]))
|
|
150
51
|
}
|
|
52
|
+
|
|
151
53
|
return (
|
|
152
54
|
<React.Fragment key={index}>
|
|
153
55
|
{/* prettier-ignore */}
|
|
@@ -174,52 +76,39 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
|
|
|
174
76
|
yScale={yScale}
|
|
175
77
|
curve={curveType}
|
|
176
78
|
strokeDasharray={s.type ? handleLineType(s.type) : 0}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
{/* Transparent bar for tooltips */}
|
|
180
|
-
{/* prettier-ignore */}
|
|
181
|
-
<Bar
|
|
182
|
-
width={ Number(xMax)}
|
|
183
|
-
height={ Number(yMax)}
|
|
184
|
-
fill={DEBUG ? 'red' : 'transparent'}
|
|
185
|
-
fillOpacity={0.05}
|
|
186
|
-
style={DEBUG ? { stroke: 'black', strokeWidth: 2 } : {}}
|
|
187
|
-
onMouseMove={e => handleMouseOver(e, data)}
|
|
188
|
-
onMouseOut={hideTooltip}
|
|
189
|
-
/>
|
|
79
|
+
/>
|
|
190
80
|
|
|
191
81
|
{/* circles that appear on hover */}
|
|
192
|
-
{tooltipData && Object.entries(tooltipData.data).length > 0 && (
|
|
82
|
+
{/* {tooltipData && Object.entries(tooltipData.data).length > 0 && (
|
|
193
83
|
<circle
|
|
194
84
|
cx={config.xAxis.type === 'categorical' ? xScale(tooltipData.data[config.xAxis.dataKey]) : xScale(parseDate(tooltipData.data[config.xAxis.dataKey]))}
|
|
195
|
-
cy={yScale(tooltipData.data[
|
|
85
|
+
cy={yScale(tooltipData.data[index][1])}
|
|
196
86
|
r={4.5}
|
|
197
87
|
opacity={1}
|
|
198
88
|
fillOpacity={1}
|
|
199
89
|
fill={displayArea ? (colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[s.dataKey] : s.dataKey) : '#000') : 'transparent'}
|
|
200
90
|
style={{ filter: 'unset', opacity: 1 }}
|
|
201
91
|
/>
|
|
202
|
-
)}
|
|
203
|
-
|
|
204
|
-
{tooltipData && Object.entries(tooltipData.data).length > 0 && (
|
|
205
|
-
<TooltipInPortal key={Math.random()} top={tooltipData.dataYPosition + chartPosition?.top} left={tooltipData.dataXPosition + chartPosition?.left} style={defaultStyles}>
|
|
206
|
-
<ul style={{ listStyle: 'none', paddingLeft: 'unset', fontFamily: 'sans-serif', margin: 'auto', lineHeight: '1rem' }} data-tooltip-id={tooltip_id}>
|
|
207
|
-
{typeof tooltipData === 'object' &&
|
|
208
|
-
Object.entries(tooltipData.data).map(item => (
|
|
209
|
-
<li style={{ padding: '2.5px 0' }}>
|
|
210
|
-
<TooltipListItem item={item} />
|
|
211
|
-
</li>
|
|
212
|
-
))}
|
|
213
|
-
</ul>
|
|
214
|
-
</TooltipInPortal>
|
|
215
|
-
)}
|
|
92
|
+
)} */}
|
|
216
93
|
</React.Fragment>
|
|
217
94
|
)
|
|
218
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
|
+
/>
|
|
219
108
|
</Group>
|
|
220
109
|
</ErrorBoundary>
|
|
221
110
|
)
|
|
222
111
|
)
|
|
223
112
|
}
|
|
224
113
|
|
|
225
|
-
export default
|
|
114
|
+
export default memo(AreaChart)
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import React, { useContext, useState, useEffect } from 'react'
|
|
2
2
|
import { Group } from '@visx/group'
|
|
3
|
-
import { BarGroup, BarStack } from '@visx/shape'
|
|
3
|
+
import { BarGroup, BarStack, Bar } from '@visx/shape'
|
|
4
4
|
import { Text } from '@visx/text'
|
|
5
5
|
import chroma from 'chroma-js'
|
|
6
6
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
7
7
|
import ConfigContext from '../ConfigContext'
|
|
8
8
|
import { BarStackHorizontal } from '@visx/shape'
|
|
9
9
|
import { useHighlightedBars } from '../hooks/useHighlightedBars'
|
|
10
|
-
import { act } from 'react-dom/test-utils'
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
const BarChart = ({ xScale, yScale, seriesScale, xMax, yMax, getXAxisData, getYAxisData, animatedChart, visible, handleTooltipMouseOver, handleTooltipMouseOff, handleTooltipClick }) => {
|
|
13
12
|
const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, updateConfig, colorPalettes, tableData, formatDate, isNumber, getTextWidth, parseDate, setSharedFilter, setSharedFilterValue, dashboardConfig } = useContext(ConfigContext)
|
|
14
13
|
const { HighLightedBarUtils } = useHighlightedBars(config)
|
|
15
14
|
const { orientation, visualizationSubType } = config
|
|
@@ -64,7 +63,7 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
64
63
|
if (!colorMap.has(values[i])) {
|
|
65
64
|
colorMap.set(values[i], palettesArr[colorMap.size % palettesArr.length])
|
|
66
65
|
}
|
|
67
|
-
// push the
|
|
66
|
+
// push the color to the result array
|
|
68
67
|
result.push(colorMap.get(values[i]))
|
|
69
68
|
}
|
|
70
69
|
return result
|
|
@@ -172,11 +171,17 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
172
171
|
yAxisTooltip = config.isLegendValue ? `${bar.key}: ${yAxisValue}` : config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
|
|
173
172
|
}
|
|
174
173
|
|
|
175
|
-
const
|
|
176
|
-
|
|
174
|
+
const {
|
|
175
|
+
legend: { showLegendValuesTooltip },
|
|
176
|
+
runtime: { seriesLabels }
|
|
177
|
+
} = config
|
|
178
|
+
|
|
179
|
+
const barStackTooltip = `<div>
|
|
180
|
+
<p class="tooltip-heading"><strong>${xAxisTooltip}</strong></p>
|
|
181
|
+
${showLegendValuesTooltip && seriesLabels && hasMultipleSeries ? `${seriesLabels[bar.key] || ''}<br/>` : ''}
|
|
177
182
|
${yAxisTooltip}<br />
|
|
178
|
-
${xAxisTooltip}
|
|
179
183
|
</div>`
|
|
184
|
+
|
|
180
185
|
return (
|
|
181
186
|
<Group key={`${barStack.index}--${bar.index}--${orientation}`}>
|
|
182
187
|
<style>
|
|
@@ -201,7 +206,7 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
201
206
|
style={{ background: bar.color, border: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`, ...style }}
|
|
202
207
|
opacity={transparentBar ? 0.5 : 1}
|
|
203
208
|
display={displayBar ? 'block' : 'none'}
|
|
204
|
-
data-tooltip-html={
|
|
209
|
+
data-tooltip-html={barStackTooltip}
|
|
205
210
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
206
211
|
onClick={e => {
|
|
207
212
|
e.preventDefault()
|
|
@@ -444,17 +449,17 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
444
449
|
let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
|
|
445
450
|
let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
|
|
446
451
|
if (!hasMultipleSeries && config.runtime.horizontal) {
|
|
447
|
-
xAxisTooltip = config.isLegendValue ?
|
|
452
|
+
xAxisTooltip = config.isLegendValue ? `<p className="tooltip-heading">${bar.key}: ${xAxisValue}</p>` : config.runtime.xAxis.label ? `<p className="tooltip-heading">${config.runtime.xAxis.label}: ${xAxisValue}</p>` : xAxisValue
|
|
448
453
|
}
|
|
449
454
|
if (!hasMultipleSeries && !config.runtime.horizontal) {
|
|
450
455
|
yAxisTooltip = config.isLegendValue ? `${bar.key}: ${yAxisValue}` : config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
|
|
451
456
|
}
|
|
452
457
|
|
|
453
|
-
const tooltip = `<
|
|
458
|
+
const tooltip = `<ul>
|
|
454
459
|
${config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && hasMultipleSeries ? `${config.runtime.seriesLabels[bar.key] || ''}<br/>` : ''}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
</
|
|
460
|
+
<li class="tooltip-heading">${yAxisTooltip}</li>
|
|
461
|
+
<li class="tooltip-body">${xAxisTooltip}</li>
|
|
462
|
+
</li></ul>`
|
|
458
463
|
|
|
459
464
|
const isRegularLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'regular'
|
|
460
465
|
const isTwoToneLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'two-tone'
|
|
@@ -467,7 +472,7 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
467
472
|
if (isTwoToneLollipopColor) return chroma(barColor).brighten(1)
|
|
468
473
|
if (isHighlightedBar) return 'transparent'
|
|
469
474
|
// loop through shared filters and get active values
|
|
470
|
-
if (dashboardConfig && dashboardConfig?.dashboard.sharedFilters?.length > 0) {
|
|
475
|
+
/* if (dashboardConfig && dashboardConfig?.dashboard.sharedFilters?.length > 0) {
|
|
471
476
|
let activeFilters = []
|
|
472
477
|
let backgroundColor = barColor
|
|
473
478
|
|
|
@@ -500,7 +505,7 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
500
505
|
}
|
|
501
506
|
checkForResetValue()
|
|
502
507
|
return backgroundColor
|
|
503
|
-
}
|
|
508
|
+
} */
|
|
504
509
|
return barColor
|
|
505
510
|
}
|
|
506
511
|
|
|
@@ -674,7 +679,12 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
674
679
|
: ''}
|
|
675
680
|
</Group>
|
|
676
681
|
)}
|
|
682
|
+
|
|
683
|
+
{/* tooltips */}
|
|
684
|
+
{orientation !== 'horizontal' && <Bar key={'bars'} width={Number(xMax)} height={Number(yMax)} fill={false ? 'red' : 'transparent'} fillOpacity={0.05} onMouseMove={e => handleTooltipMouseOver(e, data)} onMouseOut={handleTooltipMouseOff} onClick={e => handleTooltipClick(e, data)} />}
|
|
677
685
|
</Group>
|
|
678
686
|
</ErrorBoundary>
|
|
679
687
|
)
|
|
680
688
|
}
|
|
689
|
+
|
|
690
|
+
export default BarChart
|
|
@@ -13,11 +13,12 @@ import ConfigContext from '../ConfigContext'
|
|
|
13
13
|
import MediaControls from '@cdc/core/components/MediaControls'
|
|
14
14
|
|
|
15
15
|
export default function DataTable() {
|
|
16
|
-
const { rawData, tableData: data, config, colorScale, parseDate, formatDate, formatNumber: numberFormatter, colorPalettes } = useContext(ConfigContext)
|
|
16
|
+
const { rawData, tableData: data, config, colorScale, parseDate, formatDate, formatNumber: numberFormatter, colorPalettes, currentViewport } = useContext(ConfigContext)
|
|
17
17
|
|
|
18
18
|
const section = config.orientation === 'horizontal' ? 'yAxis' : 'xAxis'
|
|
19
19
|
const [tableExpanded, setTableExpanded] = useState(config.table.expanded)
|
|
20
20
|
const [accessibilityLabel, setAccessibilityLabel] = useState('')
|
|
21
|
+
const isLegendBottom = ['sm', 'xs', 'xxs'].includes(currentViewport)
|
|
21
22
|
|
|
22
23
|
const DownloadButton = ({ data }, type) => {
|
|
23
24
|
const fileName = `${config.title.substring(0, 50)}.csv`
|
|
@@ -145,6 +146,8 @@ export default function DataTable() {
|
|
|
145
146
|
if (rightSeriesItem.dataKey === row.original) resolvedAxis = 'right'
|
|
146
147
|
})
|
|
147
148
|
|
|
149
|
+
if (config.visualizationType !== 'Combo') resolvedAxis = 'left'
|
|
150
|
+
|
|
148
151
|
return <>{numberFormatter(d[row.original], resolvedAxis)}</>
|
|
149
152
|
},
|
|
150
153
|
id: `${d[config.runtime.originalXAxis.dataKey]}--${index}`,
|
|
@@ -233,7 +236,7 @@ export default function DataTable() {
|
|
|
233
236
|
{config.table.download && <DownloadButton data={rawData} type='link' />}
|
|
234
237
|
</MediaControls.Section>
|
|
235
238
|
|
|
236
|
-
<section id={config?.title ? `dataTableSection__${config?.title.replace(/\s/g, '')}` : `dataTableSection`} className={`data-table-container`} aria-label={accessibilityLabel}>
|
|
239
|
+
<section style={{ marginTop: !isLegendBottom ? config.dynamicMarginTop + 'px' : '0px' }} id={config?.title ? `dataTableSection__${config?.title.replace(/\s/g, '')}` : `dataTableSection`} className={`data-table-container`} aria-label={accessibilityLabel}>
|
|
237
240
|
<div
|
|
238
241
|
role='button'
|
|
239
242
|
className={tableExpanded ? 'data-table-heading' : 'collapsed data-table-heading'}
|