@cdc/chart 4.25.3-6 → 4.25.5-1
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-1a1724a1.es.js +4886 -0
- package/dist/cdcchart.js +50347 -75521
- package/index.html +1 -0
- package/package.json +22 -27
- package/src/CdcChart.tsx +1 -22
- package/src/CdcChartComponent.tsx +35 -21
- package/src/_stories/Chart.CI.stories.tsx +43 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +68 -49
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +6 -0
- package/src/_stories/Chart.stories.tsx +7 -16
- package/src/_stories/_mock/bar_chart_ci_labels.json +620 -0
- package/src/_stories/_mock/barchart_labels.mock.json +612 -0
- package/src/_stories/_mock/dynamic_series_bar_config.json +1 -1
- package/src/_stories/_mock/dynamic_series_suppression_mock.json +610 -0
- package/{examples/private/line-issue.json → src/_stories/_mock/legend_groupBy_mock.json} +46 -69
- package/src/components/Annotations/components/AnnotationDropdown.tsx +2 -2
- package/src/components/AreaChart/components/AreaChart.jsx +33 -5
- package/src/components/Axis/Categorical.Axis.tsx +2 -2
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +51 -41
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +19 -9
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +20 -9
- package/src/components/BarChart/components/BarChart.Vertical.tsx +48 -31
- package/src/components/BarChart/components/{BarChart.jsx → BarChart.tsx} +23 -5
- package/src/components/BarChart/components/context.tsx +20 -2
- package/src/components/BarChart/helpers/getBarHeights.ts +47 -0
- package/src/components/BarChart/helpers/index.ts +5 -2
- package/src/components/BarChart/helpers/tests/getBarHeights.test.ts +83 -0
- package/src/{hooks → components/BarChart/helpers}/useBarChart.ts +11 -47
- package/src/components/BoxPlot/BoxPlot.tsx +2 -1
- package/src/components/DeviationBar.jsx +2 -1
- package/src/components/EditorPanel/EditorPanel.tsx +60 -24
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +34 -34
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +51 -25
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +10 -3
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +4 -3
- package/src/components/EditorPanel/useEditorPermissions.ts +1 -4
- package/src/components/ForestPlot/ForestPlot.tsx +2 -2
- package/src/components/Legend/Legend.Component.tsx +69 -58
- package/src/components/Legend/Legend.Suppression.tsx +12 -22
- package/src/components/Legend/Legend.tsx +3 -1
- package/src/components/Legend/LegendGroup/LegendGroup.styles.css +40 -0
- package/src/components/Legend/LegendGroup/LegendGroup.tsx +103 -0
- package/src/components/Legend/LegendGroup/index.tsx +3 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +28 -0
- package/src/components/LineChart/LineChartProps.ts +3 -1
- package/src/components/LineChart/components/LineChart.Circle.tsx +77 -119
- package/src/components/LineChart/helpers.ts +133 -56
- package/src/components/LineChart/index.tsx +125 -60
- package/src/components/LinearChart.tsx +74 -115
- package/src/components/PairedBarChart.jsx +3 -2
- package/src/components/PieChart/PieChart.tsx +71 -91
- package/src/components/ScatterPlot/ScatterPlot.jsx +5 -0
- package/src/components/Sparkline/components/SparkLine.tsx +80 -18
- package/src/components/ZoomBrush.tsx +4 -4
- package/src/data/initial-state.js +4 -2
- package/src/helpers/countNumOfTicks.ts +1 -1
- package/src/helpers/dataHelpers.ts +31 -0
- package/src/helpers/sizeHelpers.ts +23 -0
- package/src/hooks/useMinMax.ts +21 -28
- package/src/hooks/useRightAxis.ts +4 -2
- package/src/hooks/useScales.ts +12 -14
- package/src/hooks/useTooltip.tsx +204 -203
- package/src/index.jsx +2 -2
- package/src/scss/main.scss +13 -6
- package/src/store/chart.actions.ts +1 -1
- package/src/types/ChartConfig.ts +7 -1
- package/LICENSE +0 -201
- package/examples/private/DEV-8850-2.json +0 -493
- package/examples/private/DEV-9822.json +0 -574
- package/examples/private/DEV-9840.json +0 -553
- package/examples/private/DEV-9850-3.json +0 -461
- package/examples/private/chart.json +0 -1084
- package/examples/private/ci_formatted.json +0 -202
- package/examples/private/ci_issue.json +0 -3016
- package/examples/private/completed.json +0 -634
- package/examples/private/dem-data-long.csv +0 -20
- package/examples/private/dem-data-long.json +0 -36
- package/examples/private/demographic_data.csv +0 -157
- package/examples/private/demographic_data.json +0 -2654
- package/examples/private/demographic_dynamic.json +0 -443
- package/examples/private/demographic_standard.json +0 -560
- package/examples/private/ehdi.json +0 -29939
- package/examples/private/not-loading.json +0 -360
- package/examples/private/test.json +0 -493
package/src/hooks/useTooltip.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
|
8
8
|
import { localPoint } from '@visx/event'
|
|
9
9
|
import { bisector } from 'd3-array'
|
|
10
10
|
import _ from 'lodash'
|
|
11
|
+
import { getHorizontalBarHeights } from '../components/BarChart/helpers/getBarHeights'
|
|
11
12
|
|
|
12
13
|
export const useTooltip = props => {
|
|
13
14
|
const {
|
|
@@ -21,31 +22,35 @@ export const useTooltip = props => {
|
|
|
21
22
|
setSharedFilter,
|
|
22
23
|
isDraggingAnnotation
|
|
23
24
|
} = useContext<ChartContext>(ConfigContext)
|
|
24
|
-
const { xScale, yScale, showTooltip, hideTooltip } = props
|
|
25
|
+
const { xScale, yScale, seriesScale, showTooltip, hideTooltip } = props
|
|
25
26
|
const { xAxis, visualizationType, orientation, yAxis, runtime } = config
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
* Provides the tooltip information based on the tooltip data array and svg cursor coordinates
|
|
29
|
-
* @function getTooltipInformation
|
|
30
|
-
* @param {Array} tooltipDataArray - The array containing the tooltip data.
|
|
31
|
-
* @param {Object} eventSvgCoords - The object containing the SVG coordinates of the event.
|
|
32
|
-
* @return {Object} - The tooltip information with tooltip data.
|
|
33
|
-
*/
|
|
28
|
+
const Y_AXIS_SIZE = Number(config.yAxis.size || 0)
|
|
34
29
|
|
|
35
|
-
// function handles only Single series
|
|
30
|
+
// function handles only Single series hovered data tooltips
|
|
36
31
|
const findDataKeyByThreshold = (mouseY, datapoint) => {
|
|
37
|
-
let sum = 0
|
|
38
|
-
let threshold = Number(yScale.invert(mouseY))
|
|
39
32
|
let hoveredKey = null
|
|
40
33
|
let hoveredValue = null
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
34
|
+
const dynamicSeries = config.series.find(s => s.dynamicCategory)
|
|
35
|
+
if (dynamicSeries) {
|
|
36
|
+
hoveredKey = datapoint[dynamicSeries.dynamicCategory]
|
|
37
|
+
hoveredValue = datapoint[dynamicSeries.dataKey]
|
|
38
|
+
} else {
|
|
39
|
+
let sum = 0
|
|
40
|
+
let threshold
|
|
41
|
+
try {
|
|
42
|
+
threshold = Number(yScale.invert(mouseY))
|
|
43
|
+
} catch (e) {
|
|
44
|
+
return []
|
|
45
|
+
}
|
|
46
|
+
for (let key of config.runtime?.seriesKeys) {
|
|
47
|
+
if (datapoint.hasOwnProperty(key)) {
|
|
48
|
+
sum += Number(datapoint[key])
|
|
49
|
+
if (sum >= threshold) {
|
|
50
|
+
hoveredValue = datapoint[key]
|
|
51
|
+
hoveredKey = key
|
|
52
|
+
break
|
|
53
|
+
}
|
|
49
54
|
}
|
|
50
55
|
}
|
|
51
56
|
}
|
|
@@ -62,25 +67,6 @@ export const useTooltip = props => {
|
|
|
62
67
|
return showMissingDataValue ? 'N/A' : formattedValue
|
|
63
68
|
}
|
|
64
69
|
|
|
65
|
-
const getTooltipInformation = (tooltipDataArray, eventSvgCoords) => {
|
|
66
|
-
const { x, y } = eventSvgCoords
|
|
67
|
-
let initialTooltipData = tooltipDataArray || {}
|
|
68
|
-
|
|
69
|
-
const tooltipData = {
|
|
70
|
-
data: initialTooltipData,
|
|
71
|
-
dataXPosition: x + 10,
|
|
72
|
-
dataYPosition: y
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const tooltipInformation = {
|
|
76
|
-
tooltipLeft: tooltipData.dataXPosition,
|
|
77
|
-
tooltipTop: tooltipData.dataYPosition,
|
|
78
|
-
tooltipData: tooltipData
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return tooltipInformation
|
|
82
|
-
}
|
|
83
|
-
|
|
84
70
|
/**
|
|
85
71
|
* Handles the mouse over event for the tooltip.
|
|
86
72
|
* @function handleTooltipMouseOver
|
|
@@ -89,81 +75,75 @@ export const useTooltip = props => {
|
|
|
89
75
|
*/
|
|
90
76
|
const handleTooltipMouseOver = (e, additionalChartData) => {
|
|
91
77
|
if (visualizationType === 'Bump Chart') return
|
|
92
|
-
e.stopPropagation()
|
|
78
|
+
//e.stopPropagation()
|
|
93
79
|
if (isDraggingAnnotation) return
|
|
94
80
|
|
|
95
81
|
const eventSvgCoords = localPoint(e)
|
|
96
82
|
const { x, y } = eventSvgCoords
|
|
97
83
|
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
const
|
|
84
|
+
const resolvedScaleValues = getResolvedScaleValues([x, y])
|
|
85
|
+
const singleSeriesValue = getYValueFromCoordinate(y, resolvedScaleValues)
|
|
86
|
+
const columnsWithTooltips = []
|
|
87
|
+
const tooltipItems = [] as any[][]
|
|
88
|
+
for (const [colKey, column] of Object.entries(config.columns)) {
|
|
89
|
+
const formattingParams = {
|
|
90
|
+
addColPrefix: column.prefix,
|
|
91
|
+
addColSuffix: column.suffix,
|
|
92
|
+
addColRoundTo: column.roundToPlace || '',
|
|
93
|
+
addColCommas: column.commas
|
|
94
|
+
}
|
|
102
95
|
|
|
103
|
-
|
|
96
|
+
const pieColumnData = additionalChartData?.arc?.data[column.name]
|
|
97
|
+
const columnData =
|
|
98
|
+
config.tooltips.singleSeries && visualizationType === 'Line'
|
|
99
|
+
? resolvedScaleValues.filter(
|
|
100
|
+
value => value[config.runtime.series[0].dynamicCategory] === singleSeriesValue
|
|
101
|
+
)[0][colKey]
|
|
102
|
+
: resolvedScaleValues[0]?.[colKey]
|
|
103
|
+
const closestValue = config.visualizationType === 'Pie' ? pieColumnData : columnData
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
const formattedValue = formatColNumber(closestValue, 'left', true, config, formattingParams)
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return position
|
|
107
|
+
if (column.tooltips) {
|
|
108
|
+
columnsWithTooltips.push([column.label, formattedValue])
|
|
109
|
+
}
|
|
111
110
|
}
|
|
111
|
+
const additionalTooltipItems = [] as [string, string | number][]
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const tooltipItems = []
|
|
117
|
-
|
|
118
|
-
for (const [colKeys, colVals] of Object.entries(columns)) {
|
|
119
|
-
const formattingParams = {
|
|
120
|
-
addColPrefix: config.columns[colKeys].prefix,
|
|
121
|
-
addColSuffix: config.columns[colKeys].suffix,
|
|
122
|
-
addColRoundTo: config.columns[colKeys].roundToPlace ? config.columns[colKeys].roundToPlace : '',
|
|
123
|
-
addColCommas: config.columns[colKeys].commas
|
|
124
|
-
}
|
|
125
|
-
let closestValue = null
|
|
126
|
-
|
|
127
|
-
if (config.visualizationType === 'Pie') {
|
|
128
|
-
closestValue = arc?.data[colVals.name]
|
|
129
|
-
} else {
|
|
130
|
-
closestValue = resolvedScaleValues[0]?.[colVals.name]
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const formattedValue = formatColNumber(closestValue, 'left', true, config, formattingParams)
|
|
113
|
+
columnsWithTooltips.forEach(columnData => {
|
|
114
|
+
additionalTooltipItems.push([columnData[0], columnData[1]])
|
|
115
|
+
})
|
|
134
116
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
const additionalTooltipItems = []
|
|
117
|
+
if (visualizationType === 'Pie') {
|
|
118
|
+
const roundTo = Number(config.dataFormat.roundTo) || 0
|
|
140
119
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
120
|
+
const pieData = additionalChartData?.data ?? {}
|
|
121
|
+
const startAngle = additionalChartData?.startAngle ?? 0
|
|
122
|
+
const endAngle = additionalChartData?.endAngle ?? 0
|
|
144
123
|
|
|
145
|
-
|
|
146
|
-
|
|
124
|
+
const degrees = ((endAngle - startAngle) * 180) / Math.PI
|
|
125
|
+
const pctOf360 = (degrees / 360) * 100
|
|
126
|
+
const pctString = pctOf360.toFixed(roundTo) + '%'
|
|
147
127
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
128
|
+
tooltipItems.push(
|
|
129
|
+
[config.xAxis.dataKey, pieData[config.xAxis.dataKey]],
|
|
130
|
+
[config.runtime.yAxis.dataKey, formatNumber(pieData[config.runtime.yAxis.dataKey])],
|
|
131
|
+
['Percent', pctString]
|
|
132
|
+
)
|
|
133
|
+
}
|
|
153
134
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
[config.runtime.yAxis.dataKey, formatNumber(arc?.data[config.runtime.yAxis.dataKey])],
|
|
158
|
-
['Percent', `${roundedPercentage + '%'}`]
|
|
159
|
-
)
|
|
160
|
-
}
|
|
135
|
+
if (visualizationType === 'Forest Plot') {
|
|
136
|
+
tooltipItems.push([config.xAxis.dataKey, getClosestYValue(y)])
|
|
137
|
+
}
|
|
161
138
|
|
|
162
|
-
|
|
163
|
-
|
|
139
|
+
const isLinearChart = !['Pie', 'Forest Plot'].includes(visualizationType)
|
|
140
|
+
if (isLinearChart) {
|
|
141
|
+
const getAxisPosition = seriesKey => {
|
|
142
|
+
const seriesObj = config.runtime.series.filter(s => s.dataKey === seriesKey)[0]
|
|
143
|
+
const position = seriesObj?.axis ? String(seriesObj.axis).toLowerCase() : 'left'
|
|
144
|
+
return position
|
|
164
145
|
}
|
|
165
|
-
|
|
166
|
-
if (visualizationType !== 'Pie' && visualizationType !== 'Forest Plot' && !config.tooltips.singleSeries) {
|
|
146
|
+
if (!config.tooltips.singleSeries || visualizationType === 'Line') {
|
|
167
147
|
tooltipItems.push(
|
|
168
148
|
...getIncludedTooltipSeries()
|
|
169
149
|
?.filter(seriesKey => {
|
|
@@ -191,37 +171,53 @@ export const useTooltip = props => {
|
|
|
191
171
|
})
|
|
192
172
|
)
|
|
193
173
|
|
|
194
|
-
|
|
174
|
+
const runtimeSeries =
|
|
175
|
+
config.tooltips.singleSeries && visualizationType === 'Line'
|
|
176
|
+
? [_.find(config.runtime.series, d => d.dataKey === singleSeriesValue)]
|
|
177
|
+
: config.runtime.series
|
|
178
|
+
|
|
179
|
+
runtimeSeries?.forEach(series => {
|
|
195
180
|
if (series?.dynamicCategory) {
|
|
196
181
|
const seriesKey = series.dataKey
|
|
197
182
|
const resolvedScaleValue = resolvedScaleValues.find(v => v[series.dynamicCategory] === seriesKey)
|
|
198
|
-
if (
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
183
|
+
if (resolvedScaleValue) {
|
|
184
|
+
const value = resolvedScaleValue[series.originalDataKey]
|
|
185
|
+
const formattedValue = getFormattedValue(seriesKey, value, config, getAxisPosition)
|
|
186
|
+
tooltipItems.push([seriesKey, formattedValue, getAxisPosition(seriesKey)])
|
|
187
|
+
}
|
|
202
188
|
}
|
|
203
189
|
})
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const [seriesKey, value] = findDataKeyByThreshold(y, resolvedScaleValues[0])
|
|
190
|
+
} else {
|
|
191
|
+
// Show Only the Hovered Series in Tooltip
|
|
192
|
+
const dataColumn = resolvedScaleValues[0]
|
|
193
|
+
const [seriesKey, value] = findDataKeyByThreshold(y, dataColumn)
|
|
209
194
|
if (seriesKey && value) {
|
|
210
|
-
|
|
195
|
+
const xVal = dataColumn[config.xAxis.dataKey]
|
|
196
|
+
const closestXScaleValue = getXValueFromCoordinate(x - Y_AXIS_SIZE)
|
|
197
|
+
|
|
198
|
+
tooltipItems.push([config.xAxis.dataKey, closestXScaleValue || xVal])
|
|
211
199
|
const formattedValue = getFormattedValue(seriesKey, value, config, getAxisPosition)
|
|
212
200
|
tooltipItems.push([seriesKey, formattedValue])
|
|
201
|
+
} else {
|
|
202
|
+
Object.keys(dataColumn).forEach(key => {
|
|
203
|
+
tooltipItems.push([key, dataColumn[key]])
|
|
204
|
+
})
|
|
213
205
|
}
|
|
214
206
|
}
|
|
215
|
-
|
|
216
|
-
return [...tooltipItems, ...additionalTooltipItems]
|
|
217
207
|
}
|
|
218
208
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const tooltipDataArray = getTooltipDataArray()
|
|
209
|
+
const dataXPosition = eventSvgCoords.x + 10
|
|
210
|
+
const dataYPosition = eventSvgCoords.y
|
|
222
211
|
|
|
223
|
-
|
|
224
|
-
|
|
212
|
+
const tooltipInformation = {
|
|
213
|
+
tooltipLeft: dataXPosition,
|
|
214
|
+
tooltipTop: dataYPosition,
|
|
215
|
+
tooltipData: {
|
|
216
|
+
data: [...tooltipItems, ...additionalTooltipItems],
|
|
217
|
+
dataXPosition,
|
|
218
|
+
dataYPosition
|
|
219
|
+
}
|
|
220
|
+
}
|
|
225
221
|
showTooltip(tooltipInformation)
|
|
226
222
|
}
|
|
227
223
|
|
|
@@ -291,10 +287,7 @@ export const useTooltip = props => {
|
|
|
291
287
|
return closestX
|
|
292
288
|
}
|
|
293
289
|
|
|
294
|
-
if (
|
|
295
|
-
config.xAxis.type === 'categorical' ||
|
|
296
|
-
(visualizationType === 'Combo' && orientation !== 'horizontal' && visualizationType !== 'Forest Plot')
|
|
297
|
-
) {
|
|
290
|
+
if (config.xAxis.type === 'categorical' || visualizationType === 'Combo') {
|
|
298
291
|
let range = xScale.range()[1] - xScale.range()[0]
|
|
299
292
|
let eachBand = range / (xScale.domain().length + 1)
|
|
300
293
|
|
|
@@ -302,17 +295,62 @@ export const useTooltip = props => {
|
|
|
302
295
|
const index = Math.floor((Number(numerator) - eachBand / 2) / eachBand)
|
|
303
296
|
return xScale.domain()[index] // fixes off by 1 error
|
|
304
297
|
}
|
|
298
|
+
}
|
|
305
299
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
300
|
+
const findClosest = (dataArray: [any, number][], mouseXorY) => {
|
|
301
|
+
let dataColumn: Object
|
|
302
|
+
dataArray.find(([d, xOrY]) => {
|
|
303
|
+
if (xOrY > mouseXorY) {
|
|
304
|
+
return true
|
|
305
|
+
}
|
|
306
|
+
dataColumn = d
|
|
307
|
+
})
|
|
308
|
+
return dataColumn
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const getYValueFromCoordinate = (y, xData) => {
|
|
312
|
+
let closestYSeriesValue = null
|
|
313
|
+
let minDistance = Number.MAX_VALUE
|
|
314
|
+
let offset = y
|
|
315
|
+
|
|
316
|
+
xData.forEach(d => {
|
|
317
|
+
const yPosition = yScale(d[config.runtime.series[0].originalDataKey])
|
|
318
|
+
const distance = Math.abs(Number(yPosition - offset))
|
|
319
|
+
|
|
320
|
+
if (distance <= minDistance) {
|
|
321
|
+
minDistance = distance
|
|
322
|
+
closestYSeriesValue = d[config.runtime.series[0].dynamicCategory]
|
|
323
|
+
}
|
|
324
|
+
})
|
|
325
|
+
return closestYSeriesValue
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const getClosestYValueHorizontalChart = mouseY => {
|
|
329
|
+
const barGroups = yScale.domain().map((group, index) => ({ group, index }))
|
|
330
|
+
const barsWithHeights = getHorizontalBarHeights<{ group }>(config, barGroups)
|
|
331
|
+
|
|
332
|
+
const barGroup = findClosest(
|
|
333
|
+
barsWithHeights.map(d => [d, _.round(d.y)]),
|
|
334
|
+
mouseY
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
const subGroupMouseY = mouseY - barGroup.y
|
|
338
|
+
const columns = data.filter(d => d[config.xAxis.dataKey] === barGroup.group)
|
|
339
|
+
|
|
340
|
+
if (config.series.length > 1 && !config.series.find(s => s.dynamicCategory)) {
|
|
341
|
+
const seriesWithY = config.series.map((c, i) => [c, config.barHeight * i]) as [Object, number][]
|
|
342
|
+
const hoveredSeries = findClosest(seriesWithY, subGroupMouseY)
|
|
343
|
+
const exludeColumns = config.series.filter(s => s.dataKey !== hoveredSeries.dataKey).map(s => s.dataKey)
|
|
344
|
+
const dataColumn = _.omit(columns[0], exludeColumns)
|
|
345
|
+
return dataColumn
|
|
346
|
+
} else {
|
|
347
|
+
const columnsWithY = columns.map((c, i) => [c, config.barHeight * i]) as [Object, number][]
|
|
348
|
+
const dataColumn = findClosest(columnsWithY, subGroupMouseY)
|
|
349
|
+
return dataColumn
|
|
312
350
|
}
|
|
313
351
|
}
|
|
314
352
|
|
|
315
|
-
const getClosestYValue = (yPosition, key) => {
|
|
353
|
+
const getClosestYValue = (yPosition, key = '') => {
|
|
316
354
|
if (visualizationType === 'Pie') return
|
|
317
355
|
let minDistance = Number.MAX_VALUE
|
|
318
356
|
let closestYValue = null
|
|
@@ -369,11 +407,15 @@ export const useTooltip = props => {
|
|
|
369
407
|
}
|
|
370
408
|
|
|
371
409
|
/**
|
|
372
|
-
* Provides an array of objects with the closest
|
|
373
|
-
* @param {String} closestXScaleValue
|
|
374
|
-
* @returns an array of objects with the closest y series data items
|
|
410
|
+
* Provides an array of objects with the closest series data items
|
|
375
411
|
*/
|
|
376
|
-
const
|
|
412
|
+
const getResolvedScaleValues = ([x, y]) => {
|
|
413
|
+
if (orientation !== 'vertical') {
|
|
414
|
+
if (config.visualizationType === 'Bar' && config.tooltips.singleSeries) {
|
|
415
|
+
return [getClosestYValueHorizontalChart(y)]
|
|
416
|
+
}
|
|
417
|
+
return data.filter(d => d[xAxis.dataKey] === getClosestYValue(y))
|
|
418
|
+
}
|
|
377
419
|
const runtimeSeries = config.runtime.series.filter(
|
|
378
420
|
series => visualizationType === 'Pie' || (series.tooltip === true && !series.dynamicCategory)
|
|
379
421
|
)
|
|
@@ -403,21 +445,22 @@ export const useTooltip = props => {
|
|
|
403
445
|
const colNames = Object.values(config.columns).map(column => column.name)
|
|
404
446
|
// @ Murad why are we adding them twice?
|
|
405
447
|
includedSeries.push(...colNames, ...colNames)
|
|
448
|
+
const closestXScaleValue = getXValueFromCoordinate(x - Y_AXIS_SIZE)
|
|
406
449
|
|
|
407
|
-
|
|
408
|
-
const dataToSearch = data.filter(d => d[xAxis.dataKey] === closestXScaleValue)
|
|
409
|
-
// Return an empty array if no matching data is found.
|
|
410
|
-
if (!dataToSearch || dataToSearch.length === 0) {
|
|
411
|
-
return []
|
|
412
|
-
}
|
|
450
|
+
let dataToSearch = (data || []).filter(d => d[xAxis.dataKey] === closestXScaleValue)
|
|
413
451
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
452
|
+
if (config.tooltips.singleSeries && config.visualizationType !== 'Line') {
|
|
453
|
+
const dynamicSeries = config.series.find(s => s.dynamicCategory)
|
|
454
|
+
if (dynamicSeries) {
|
|
455
|
+
const dataWithXScale = dataToSearch.map(
|
|
456
|
+
d => [d, seriesScale(d[dynamicSeries.dynamicCategory])] as [Object, number]
|
|
457
|
+
)
|
|
458
|
+
const xOffset = x - Y_AXIS_SIZE - xScale(closestXScaleValue)
|
|
459
|
+
dataToSearch = [findClosest(dataWithXScale, xOffset)]
|
|
460
|
+
}
|
|
420
461
|
}
|
|
462
|
+
|
|
463
|
+
return dataToSearch.map(d => _.pick(d, includedSeries))
|
|
421
464
|
}
|
|
422
465
|
|
|
423
466
|
/**
|
|
@@ -427,67 +470,26 @@ export const useTooltip = props => {
|
|
|
427
470
|
* @returns {Array} Array of items to be included in the tooltip.
|
|
428
471
|
*/
|
|
429
472
|
const getIncludedTooltipSeries = () => {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
if (!config.dashboard) {
|
|
452
|
-
switch (visualizationType) {
|
|
453
|
-
case 'Combo':
|
|
454
|
-
standardLoopItems = [runtime.xAxis.dataKey, ...runtime?.seriesKeys, ...ciItems]
|
|
455
|
-
break
|
|
456
|
-
case 'Forecasting':
|
|
457
|
-
standardLoopItems = [runtime.xAxis.dataKey, ...stageColumns, ...ciItems]
|
|
458
|
-
break
|
|
459
|
-
case 'Line':
|
|
460
|
-
standardLoopItems = [runtime.xAxis.dataKey, ...runtime?.seriesKeys]
|
|
461
|
-
break
|
|
462
|
-
case 'Area Chart':
|
|
463
|
-
standardLoopItems = [runtime.xAxis.dataKey, ...runtime?.seriesKeys]
|
|
464
|
-
break
|
|
465
|
-
case 'Bar':
|
|
466
|
-
standardLoopItems =
|
|
467
|
-
orientation === 'vertical'
|
|
468
|
-
? [runtime.xAxis.dataKey, ...runtime?.seriesKeys]
|
|
469
|
-
: [runtime.yAxis.dataKey, ...runtime?.seriesKeys]
|
|
470
|
-
break
|
|
471
|
-
case 'Pie':
|
|
472
|
-
standardLoopItems = [runtime.xAxis.dataKey, ...runtime?.seriesKeys]
|
|
473
|
-
default:
|
|
474
|
-
throw new Error('No visualization type found in handleTooltipMouseOver')
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
if (config.dashboard) {
|
|
479
|
-
standardLoopItems = [
|
|
480
|
-
runtime.xAxis.dataKey,
|
|
481
|
-
...runtime?.barSeriesKeys,
|
|
482
|
-
...runtime?.lineSeriesKeys,
|
|
483
|
-
...stageColumns,
|
|
484
|
-
...ciItems
|
|
485
|
-
]
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
return standardLoopItems
|
|
489
|
-
} catch (error) {
|
|
490
|
-
console.error('COVE', error)
|
|
473
|
+
const forcastingSeries = config.runtime.series.filter(series => series.type === 'Forecasting')
|
|
474
|
+
const stageColumns = forcastingSeries.map(series => series.stageColumn)
|
|
475
|
+
const ciItems = forcastingSeries.flatMap(series =>
|
|
476
|
+
series.confidenceIntervals?.filter(ci => ci.showInTooltip).map(ci => [ci.low, ci.high])
|
|
477
|
+
)
|
|
478
|
+
const common = [runtime.xAxis.dataKey, ...runtime?.seriesKeys]
|
|
479
|
+
switch (visualizationType) {
|
|
480
|
+
case 'Line':
|
|
481
|
+
case 'Area Chart':
|
|
482
|
+
case 'Pie':
|
|
483
|
+
return common
|
|
484
|
+
case 'Combo':
|
|
485
|
+
return [...common, ...ciItems]
|
|
486
|
+
case 'Forecasting':
|
|
487
|
+
return [runtime.xAxis.dataKey, ...stageColumns, ...ciItems]
|
|
488
|
+
|
|
489
|
+
case 'Bar':
|
|
490
|
+
return orientation === 'vertical' ? common : [runtime.yAxis.dataKey, ...runtime?.seriesKeys]
|
|
491
|
+
default:
|
|
492
|
+
throw new Error('No visualization type found in handleTooltipMouseOver')
|
|
491
493
|
}
|
|
492
494
|
}
|
|
493
495
|
|
|
@@ -586,7 +588,6 @@ export const useTooltip = props => {
|
|
|
586
588
|
getIncludedTooltipSeries,
|
|
587
589
|
getXValueFromCoordinate,
|
|
588
590
|
getXValueFromCoordinateDate,
|
|
589
|
-
getYScaleValues,
|
|
590
591
|
handleTooltipClick,
|
|
591
592
|
handleTooltipMouseOff,
|
|
592
593
|
handleTooltipMouseOver,
|
package/src/index.jsx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import ReactDOM from 'react-dom/client'
|
|
3
3
|
|
|
4
|
-
import CdcChart from './CdcChart'
|
|
5
4
|
import './coreStyles_chart.scss'
|
|
6
|
-
|
|
7
5
|
import '@cdc/core/styles/cove-main.scss'
|
|
8
6
|
import 'react-tooltip/dist/react-tooltip.css'
|
|
9
7
|
|
|
8
|
+
import CdcChart from './CdcChart'
|
|
9
|
+
|
|
10
10
|
let isEditor = window.location.href.includes('editor=true')
|
|
11
11
|
let isDebug = window.location.href.includes('debug=true')
|
|
12
12
|
|
package/src/scss/main.scss
CHANGED
|
@@ -97,11 +97,6 @@
|
|
|
97
97
|
margin-bottom: 24px;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
.visually-hidden {
|
|
101
|
-
position: fixed;
|
|
102
|
-
left: -10000px;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
100
|
.loader {
|
|
106
101
|
width: 100%;
|
|
107
102
|
text-align: center;
|
|
@@ -144,6 +139,17 @@
|
|
|
144
139
|
}
|
|
145
140
|
|
|
146
141
|
.legend-container {
|
|
142
|
+
#supression-tooltip {
|
|
143
|
+
font-family: 'Nunito', sans-serif;
|
|
144
|
+
font-size: 0.8337rem;
|
|
145
|
+
font-weight: 400;
|
|
146
|
+
max-width: 16.7rem;
|
|
147
|
+
padding: 0.5rem 1rem;
|
|
148
|
+
border-radius: 4px;
|
|
149
|
+
box-shadow: 0px 2px 2px rgba(28, 29, 31, 0.45);
|
|
150
|
+
white-space: normal;
|
|
151
|
+
line-height: 1.4;
|
|
152
|
+
}
|
|
147
153
|
background: #fff;
|
|
148
154
|
width: 100%;
|
|
149
155
|
vertical-align: top;
|
|
@@ -232,12 +238,13 @@
|
|
|
232
238
|
}
|
|
233
239
|
|
|
234
240
|
&__outer {
|
|
235
|
-
&.
|
|
241
|
+
&.link-container {
|
|
236
242
|
display: flex;
|
|
237
243
|
flex-direction: row;
|
|
238
244
|
position: absolute;
|
|
239
245
|
left: 0%;
|
|
240
246
|
top: 108%;
|
|
247
|
+
|
|
241
248
|
& > * {
|
|
242
249
|
margin: 0;
|
|
243
250
|
}
|
|
@@ -14,7 +14,7 @@ type SET_COLOR_SCALE = Action<'SET_COLOR_SCALE', Function>
|
|
|
14
14
|
type SET_STATE_DATA = Action<'SET_STATE_DATA', object[]>
|
|
15
15
|
type SET_EXCLUDED_DATA = Action<'SET_EXCLUDED_DATA', object[]>
|
|
16
16
|
type SET_FILTERED_DATA = Action<'SET_FILTERED_DATA', object[]>
|
|
17
|
-
type SET_SERIES_HIGHLIGHT = Action<'SET_SERIES_HIGHLIGHT',
|
|
17
|
+
type SET_SERIES_HIGHLIGHT = Action<'SET_SERIES_HIGHLIGHT', string[]>
|
|
18
18
|
type SET_VIEWPORT = Action<'SET_VIEWPORT', string>
|
|
19
19
|
type SET_DIMENSIONS = Action<'SET_DIMENSIONS', DimensionsType>
|
|
20
20
|
type SET_CONTAINER = Action<'SET_CONTAINER', object>
|
package/src/types/ChartConfig.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { General } from '@cdc/core/types/General'
|
|
|
10
10
|
import { type Link } from './../components/Sankey/types'
|
|
11
11
|
import { type DataDescription } from '@cdc/core/types/DataDescription'
|
|
12
12
|
import { type Legend as CoreLegend } from '@cdc/core/types/Legend'
|
|
13
|
+
import { Label } from './Label'
|
|
13
14
|
import { ConfidenceInterval } from '@cdc/core/types/ConfidenceInterval'
|
|
14
15
|
import { Region } from '@cdc/core/types/Region'
|
|
15
16
|
import { VizFilter } from '@cdc/core/types/VizFilter'
|
|
@@ -80,16 +81,20 @@ type Exclusions = {
|
|
|
80
81
|
|
|
81
82
|
export type Legend = CoreLegend & {
|
|
82
83
|
seriesHighlight: string[]
|
|
83
|
-
|
|
84
|
+
unified: boolean
|
|
84
85
|
hideSuppressionLink: boolean
|
|
85
86
|
style: 'circles' | 'boxes' | 'gradient' | 'lines'
|
|
86
87
|
subStyle: 'linear blocks' | 'smooth'
|
|
87
88
|
hasShape: boolean
|
|
89
|
+
order: 'dataColumn' | 'asc' | 'desc'
|
|
90
|
+
orderedValues: Label[]
|
|
88
91
|
tickRotation: string
|
|
89
92
|
hideBorder: {
|
|
90
93
|
side: boolean
|
|
91
94
|
topBottom: boolean
|
|
92
95
|
}
|
|
96
|
+
groupBy: string
|
|
97
|
+
separators?: string
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
type Visual = {
|
|
@@ -232,6 +237,7 @@ export type ForestPlotConfig = {
|
|
|
232
237
|
export type LineChartConfig = {
|
|
233
238
|
allowLineToBarGraph: boolean
|
|
234
239
|
convertLineToBarGraph: boolean
|
|
240
|
+
isolatedDotsSameSize: boolean
|
|
235
241
|
lineDatapointStyle: 'hidden' | 'always show' | 'hover'
|
|
236
242
|
visualizationType: 'Line'
|
|
237
243
|
} & AllChartsConfig
|