@cdc/chart 4.24.9 → 4.24.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/cdcchart.js +45911 -41739
- package/examples/feature/boxplot/boxplot-data.json +88 -22
- package/examples/feature/boxplot/boxplot.json +540 -16
- package/examples/feature/boxplot/testing.csv +7 -7
- package/examples/feature/sankey/sankey-example-data.json +0 -1
- package/examples/private/test.json +20092 -0
- package/index.html +4 -4
- package/package.json +2 -2
- package/src/CdcChart.tsx +209 -188
- package/src/_stories/Chart.CustomColors.stories.tsx +19 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +27 -0
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +74 -0
- package/src/_stories/Chart.stories.tsx +30 -3
- package/src/_stories/ChartAxisLabels.stories.tsx +20 -0
- package/src/_stories/ChartAxisTitles.stories.tsx +53 -0
- package/src/_stories/ChartEditor.stories.tsx +27 -0
- package/src/_stories/ChartLine.Suppression.stories.tsx +25 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +159 -0
- package/src/_stories/_mock/boxplot_multiseries.json +647 -0
- package/src/_stories/_mock/dynamic_series_bar_config.json +723 -0
- package/src/_stories/_mock/dynamic_series_config.json +979 -0
- package/src/_stories/_mock/horizontal_bar.json +257 -0
- package/src/_stories/_mock/large_x_axis_labels.json +261 -0
- package/src/_stories/_mock/paired-bar.json +262 -0
- package/src/_stories/_mock/pie_with_data.json +255 -0
- package/{examples/feature/scatterplot/scatterplot.json → src/_stories/_mock/scatterplot_mock.json} +62 -92
- package/src/_stories/_mock/simplified_line.json +1510 -0
- package/src/_stories/_mock/suppression_mock.json +1549 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +0 -3
- package/src/components/Annotations/components/AnnotationDropdown.tsx +1 -1
- package/src/components/Axis/Categorical.Axis.tsx +22 -4
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +95 -16
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +41 -17
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +43 -9
- package/src/components/BarChart/components/BarChart.Vertical.tsx +123 -47
- package/src/components/BarChart/helpers/index.ts +23 -5
- package/src/components/BoxPlot/BoxPlot.tsx +189 -0
- package/src/components/BrushChart.tsx +3 -2
- package/src/components/DeviationBar.jsx +58 -8
- package/src/components/EditorPanel/EditorPanel.tsx +127 -102
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +11 -28
- package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +51 -6
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +21 -4
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +40 -9
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +121 -56
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +296 -35
- package/src/components/EditorPanel/components/panels.scss +4 -6
- package/src/components/EditorPanel/editor-panel.scss +0 -8
- package/src/components/EditorPanel/helpers/tests/updateFieldRankByValue.test.ts +38 -0
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +42 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +16 -1
- package/src/components/ForestPlot/ForestPlot.tsx +2 -3
- package/src/components/ForestPlot/ForestPlotProps.ts +2 -0
- package/src/components/Legend/Legend.Component.tsx +23 -24
- package/src/components/Legend/Legend.Suppression.tsx +25 -20
- package/src/components/Legend/Legend.tsx +16 -18
- package/src/components/Legend/helpers/index.ts +16 -19
- package/src/components/LegendWrapper.tsx +3 -1
- package/src/components/LineChart/components/LineChart.Circle.tsx +10 -0
- package/src/components/LineChart/helpers.ts +48 -43
- package/src/components/LineChart/index.tsx +88 -82
- package/src/components/LinearChart.tsx +747 -562
- package/src/components/PairedBarChart.jsx +50 -10
- package/src/components/PieChart/PieChart.tsx +1 -6
- package/src/components/Regions/components/Regions.tsx +33 -19
- package/src/components/Sankey/index.tsx +50 -32
- package/src/components/Sankey/sankey.scss +6 -5
- package/src/components/Sankey/useSankeyAlert.tsx +60 -0
- package/src/components/ScatterPlot/ScatterPlot.jsx +20 -4
- package/src/components/ZoomBrush.tsx +25 -6
- package/src/coreStyles_chart.scss +3 -0
- package/src/data/initial-state.js +8 -10
- package/src/helpers/configHelpers.ts +28 -0
- package/src/helpers/handleRankByValue.ts +15 -0
- package/src/helpers/sizeHelpers.ts +25 -0
- package/src/helpers/tests/handleRankByValue.test.ts +37 -0
- package/src/helpers/tests/sizeHelpers.test.ts +80 -0
- package/src/hooks/useColorPalette.js +10 -2
- package/src/hooks/useLegendClasses.ts +13 -22
- package/src/hooks/useMinMax.ts +27 -13
- package/src/hooks/useReduceData.ts +43 -10
- package/src/hooks/useScales.ts +87 -38
- package/src/hooks/useTooltip.tsx +62 -53
- package/src/index.jsx +1 -0
- package/src/scss/DataTable.scss +5 -4
- package/src/scss/main.scss +57 -70
- package/src/types/ChartConfig.ts +43 -34
- package/src/types/ChartContext.ts +22 -15
- package/src/types/ForestPlot.ts +8 -0
- package/src/_stories/Chart.Legend.Gradient.tsx +0 -19
- package/src/_stories/ChartBrush.stories.tsx +0 -19
- package/src/components/BoxPlot/BoxPlot.jsx +0 -111
- package/src/components/LinearChart.jsx +0 -817
package/src/hooks/useScales.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { LogScaleConfig, scaleBand, scaleLinear, scaleLog, scalePoint, scaleTime } from '@visx/scale'
|
|
1
|
+
import { LinearScaleConfig, LogScaleConfig, scaleBand, scaleLinear, scaleLog, scalePoint, scaleTime } from '@visx/scale'
|
|
2
2
|
import { useContext } from 'react'
|
|
3
3
|
import ConfigContext from '../ConfigContext'
|
|
4
4
|
import { ChartConfig } from '../types/ChartConfig'
|
|
5
5
|
import { ChartContext } from '../types/ChartContext'
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
const scaleTypes = {
|
|
8
8
|
TIME: 'time',
|
|
9
9
|
LOG: 'log',
|
|
@@ -31,8 +31,7 @@ const useScales = (properties: useScaleProps) => {
|
|
|
31
31
|
const seriesDomain = config.runtime.barSeriesKeys || config.runtime.seriesKeys
|
|
32
32
|
const xAxisType = config.runtime.xAxis.type
|
|
33
33
|
const isHorizontal = config.orientation === 'horizontal'
|
|
34
|
-
|
|
35
|
-
const { visualizationType } = config
|
|
34
|
+
const { visualizationType, xAxis, forestPlot } = config
|
|
36
35
|
|
|
37
36
|
// define scales
|
|
38
37
|
let xScale = null
|
|
@@ -64,24 +63,29 @@ const useScales = (properties: useScaleProps) => {
|
|
|
64
63
|
|
|
65
64
|
// handle Linear scaled viz
|
|
66
65
|
if (config.xAxis.type === 'date' && !isHorizontal) {
|
|
67
|
-
const xAxisDataMappedSorted = xAxisDataMapped
|
|
66
|
+
const xAxisDataMappedSorted = sortXAxisData(xAxisDataMapped, config.xAxis.sortByRecentDate)
|
|
68
67
|
xScale = composeScaleBand(xAxisDataMappedSorted, [0, xMax], 1 - config.barThickness)
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
if (
|
|
70
|
+
if (xAxis.type === 'date-time' || xAxis.type === 'continuous') {
|
|
72
71
|
let xAxisMin = Math.min(...xAxisDataMapped.map(Number))
|
|
73
72
|
let xAxisMax = Math.max(...xAxisDataMapped.map(Number))
|
|
74
73
|
xAxisMin -= (config.xAxis.padding ? config.xAxis.padding * 0.01 : 0) * (xAxisMax - xAxisMin)
|
|
75
|
-
xAxisMax +=
|
|
74
|
+
xAxisMax +=
|
|
75
|
+
visualizationType === 'Line'
|
|
76
|
+
? 0
|
|
77
|
+
: (config.xAxis.padding ? config.xAxis.padding * 0.01 : 0) * (xAxisMax - xAxisMin)
|
|
78
|
+
const range = config.xAxis.sortByRecentDate ? [xMax, 0] : [0, xMax]
|
|
76
79
|
xScale = scaleTime({
|
|
77
80
|
domain: [xAxisMin, xAxisMax],
|
|
78
|
-
range:
|
|
81
|
+
range: range
|
|
79
82
|
})
|
|
80
83
|
|
|
81
84
|
xScale.type = scaleTypes.TIME
|
|
82
85
|
|
|
83
86
|
let minDistance = Number.MAX_VALUE
|
|
84
|
-
let xAxisDataMappedSorted = xAxisDataMapped
|
|
87
|
+
let xAxisDataMappedSorted = sortXAxisData(xAxisDataMapped, config.xAxis.sortByRecentDate)
|
|
88
|
+
|
|
85
89
|
for (let i = 0; i < xAxisDataMappedSorted.length - 1; i++) {
|
|
86
90
|
let distance = xScale(xAxisDataMappedSorted[i + 1]) - xScale(xAxisDataMappedSorted[i])
|
|
87
91
|
|
|
@@ -103,7 +107,7 @@ const useScales = (properties: useScaleProps) => {
|
|
|
103
107
|
range: [0, yMax]
|
|
104
108
|
})
|
|
105
109
|
xScale = scaleLinear({
|
|
106
|
-
domain: [min * leftOffset, Math.max(Number(
|
|
110
|
+
domain: [min * leftOffset, Math.max(Number(xAxis.target), max)],
|
|
107
111
|
range: [0, xMax],
|
|
108
112
|
round: true,
|
|
109
113
|
nice: true
|
|
@@ -113,9 +117,11 @@ const useScales = (properties: useScaleProps) => {
|
|
|
113
117
|
|
|
114
118
|
// handle Scatter plot
|
|
115
119
|
if (config.visualizationType === 'Scatter Plot') {
|
|
116
|
-
if (
|
|
120
|
+
if (xAxis.type === 'continuous') {
|
|
121
|
+
let min = xAxis.min ? xAxis.min : Math.min.apply(null, xScale.domain())
|
|
122
|
+
let max = xAxis.max ? xAxis.max : Math.max.apply(null, xScale.domain())
|
|
117
123
|
xScale = scaleLinear({
|
|
118
|
-
domain: [
|
|
124
|
+
domain: [min, max],
|
|
119
125
|
range: [0, xMax]
|
|
120
126
|
})
|
|
121
127
|
xScale.type = scaleTypes.LINEAR
|
|
@@ -147,19 +153,19 @@ const useScales = (properties: useScaleProps) => {
|
|
|
147
153
|
if (highestFence > max) max = highestFence
|
|
148
154
|
|
|
149
155
|
// Set Scales
|
|
156
|
+
|
|
150
157
|
yScale = scaleLinear({
|
|
151
158
|
range: [yMax, 0],
|
|
152
159
|
round: true,
|
|
153
160
|
domain: [min, max]
|
|
154
161
|
})
|
|
155
|
-
|
|
156
162
|
xScale = scaleBand({
|
|
157
163
|
range: [0, xMax],
|
|
158
|
-
|
|
159
|
-
domain: config.boxplot.categories,
|
|
160
|
-
padding: 0.4
|
|
164
|
+
domain: config.boxplot.categories
|
|
161
165
|
})
|
|
162
166
|
xScale.type = scaleTypes.BAND
|
|
167
|
+
|
|
168
|
+
seriesScale = composeScalePoint(seriesDomain, [0, yMax])
|
|
163
169
|
}
|
|
164
170
|
|
|
165
171
|
// handle Paired bar
|
|
@@ -190,10 +196,10 @@ const useScales = (properties: useScaleProps) => {
|
|
|
190
196
|
|
|
191
197
|
if (visualizationType === 'Forest Plot') {
|
|
192
198
|
const resolvedYRange = () => {
|
|
193
|
-
if (
|
|
194
|
-
return [0 +
|
|
199
|
+
if (forestPlot.regression.showDiamond || forestPlot.regression.description) {
|
|
200
|
+
return [0 + forestPlot.rowHeight * 2, yMax - forestPlot.rowHeight]
|
|
195
201
|
} else {
|
|
196
|
-
return [0 +
|
|
202
|
+
return [0 + forestPlot.rowHeight * 2, yMax]
|
|
197
203
|
}
|
|
198
204
|
}
|
|
199
205
|
|
|
@@ -204,26 +210,26 @@ const useScales = (properties: useScaleProps) => {
|
|
|
204
210
|
|
|
205
211
|
const xAxisPadding = 5
|
|
206
212
|
|
|
207
|
-
const leftWidthOffset = (Number(
|
|
208
|
-
const rightWidthOffset = (Number(
|
|
213
|
+
const leftWidthOffset = (Number(forestPlot.leftWidthOffset) / 100) * xMax
|
|
214
|
+
const rightWidthOffset = (Number(forestPlot.rightWidthOffset) / 100) * xMax
|
|
209
215
|
|
|
210
|
-
const rightWidthOffsetMobile = (Number(
|
|
211
|
-
const leftWidthOffsetMobile = (Number(
|
|
216
|
+
const rightWidthOffsetMobile = (Number(forestPlot.rightWidthOffsetMobile) / 100) * xMax
|
|
217
|
+
const leftWidthOffsetMobile = (Number(forestPlot.leftWidthOffsetMobile) / 100) * xMax
|
|
212
218
|
|
|
213
219
|
if (screenWidth > 480) {
|
|
214
|
-
if (
|
|
220
|
+
if (forestPlot.type === 'Linear') {
|
|
215
221
|
xScale = scaleLinear({
|
|
216
222
|
domain: [
|
|
217
|
-
Math.min(...data.map(d => parseFloat(d[
|
|
218
|
-
Math.max(...data.map(d => parseFloat(d[
|
|
223
|
+
Math.min(...data.map(d => parseFloat(d[forestPlot.lower]))) - xAxisPadding,
|
|
224
|
+
Math.max(...data.map(d => parseFloat(d[forestPlot.upper]))) + xAxisPadding
|
|
219
225
|
],
|
|
220
226
|
range: [leftWidthOffset, Number(screenWidth) - rightWidthOffset]
|
|
221
227
|
})
|
|
222
228
|
xScale.type = scaleTypes.LINEAR
|
|
223
229
|
}
|
|
224
|
-
if (
|
|
225
|
-
let max = Math.max(...data.map(d => parseFloat(d[
|
|
226
|
-
let fp_min = Math.min(...data.map(d => parseFloat(d[
|
|
230
|
+
if (forestPlot.type === 'Logarithmic') {
|
|
231
|
+
let max = Math.max(...data.map(d => parseFloat(d[forestPlot.upper])))
|
|
232
|
+
let fp_min = Math.min(...data.map(d => parseFloat(d[forestPlot.lower])))
|
|
227
233
|
|
|
228
234
|
xScale = scaleLog<LogScaleConfig>({
|
|
229
235
|
domain: [fp_min, max],
|
|
@@ -233,20 +239,20 @@ const useScales = (properties: useScaleProps) => {
|
|
|
233
239
|
xScale.type = scaleTypes.LOG
|
|
234
240
|
}
|
|
235
241
|
} else {
|
|
236
|
-
if (
|
|
237
|
-
xScale = scaleLinear({
|
|
242
|
+
if (forestPlot.type === 'Linear') {
|
|
243
|
+
xScale = scaleLinear<LinearScaleConfig>({
|
|
238
244
|
domain: [
|
|
239
|
-
Math.min(...data.map(d => parseFloat(d[
|
|
240
|
-
Math.max(...data.map(d => parseFloat(d[
|
|
245
|
+
Math.min(...data.map(d => parseFloat(d[forestPlot.lower]))) - xAxisPadding,
|
|
246
|
+
Math.max(...data.map(d => parseFloat(d[forestPlot.upper]))) + xAxisPadding
|
|
241
247
|
],
|
|
242
248
|
range: [leftWidthOffsetMobile, xMax - rightWidthOffsetMobile],
|
|
243
249
|
type: scaleTypes.LINEAR
|
|
244
250
|
})
|
|
245
251
|
}
|
|
246
252
|
|
|
247
|
-
if (
|
|
248
|
-
let max = Math.max(...data.map(d => parseFloat(d[
|
|
249
|
-
let fp_min = Math.min(...data.map(d => parseFloat(d[
|
|
253
|
+
if (forestPlot.type === 'Logarithmic') {
|
|
254
|
+
let max = Math.max(...data.map(d => parseFloat(d[forestPlot.upper])))
|
|
255
|
+
let fp_min = Math.min(...data.map(d => parseFloat(d[forestPlot.lower])))
|
|
250
256
|
|
|
251
257
|
xScale = scaleLog<LogScaleConfig>({
|
|
252
258
|
domain: [fp_min, max],
|
|
@@ -264,14 +270,34 @@ const useScales = (properties: useScaleProps) => {
|
|
|
264
270
|
|
|
265
271
|
export default useScales
|
|
266
272
|
|
|
267
|
-
export const
|
|
273
|
+
export const getFirstDayOfMonth = ms => {
|
|
274
|
+
const date = new Date(ms)
|
|
275
|
+
return new Date(date.getFullYear(), date.getMonth(), 1).getTime()
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export const dateFormatHasMonthButNoDays = dateFormat => {
|
|
279
|
+
return (
|
|
280
|
+
(dateFormat.includes('%b') ||
|
|
281
|
+
dateFormat.includes('%B') ||
|
|
282
|
+
dateFormat.includes('%m') ||
|
|
283
|
+
dateFormat.includes('%-m') ||
|
|
284
|
+
dateFormat.includes('%_m')) &&
|
|
285
|
+
!dateFormat.includes('%d') &&
|
|
286
|
+
!dateFormat.includes('%-d') &&
|
|
287
|
+
!dateFormat.includes('%_d') &&
|
|
288
|
+
!dateFormat.includes('%e')
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export const getTickValues = (xAxisDataMapped, xScale, num, config) => {
|
|
268
293
|
const xDomain = xScale.domain()
|
|
269
294
|
|
|
270
295
|
if (xScale.type === 'time') {
|
|
271
296
|
const xDomainMax = xAxisDataMapped[xAxisDataMapped.length - 1]
|
|
272
297
|
const xDomainMin = xAxisDataMapped[0]
|
|
298
|
+
|
|
273
299
|
const step = (xDomainMax - xDomainMin) / (num - 1)
|
|
274
|
-
|
|
300
|
+
let tickValues = []
|
|
275
301
|
for (let i = xDomainMax; i >= xDomainMin; i -= step) {
|
|
276
302
|
tickValues.push(i)
|
|
277
303
|
}
|
|
@@ -280,6 +306,11 @@ export const getTickValues = (xAxisDataMapped, xScale, num) => {
|
|
|
280
306
|
}
|
|
281
307
|
tickValues.reverse()
|
|
282
308
|
|
|
309
|
+
// Use first days of months when showing months without days
|
|
310
|
+
if (dateFormatHasMonthButNoDays(config.xAxis.dateDisplayFormat)) {
|
|
311
|
+
tickValues = tickValues.map(tv => getFirstDayOfMonth(tv))
|
|
312
|
+
}
|
|
313
|
+
|
|
283
314
|
return tickValues
|
|
284
315
|
}
|
|
285
316
|
|
|
@@ -358,3 +389,21 @@ const composeScaleBand = (domain, range, padding = 0) => {
|
|
|
358
389
|
padding: padding
|
|
359
390
|
})
|
|
360
391
|
}
|
|
392
|
+
|
|
393
|
+
const sortXAxisData = (xAxisData, sortByRecentDate) => {
|
|
394
|
+
if (!xAxisData || xAxisData.length === 0) {
|
|
395
|
+
return []
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Check if the array has only one item
|
|
399
|
+
if (xAxisData.length === 1) {
|
|
400
|
+
return xAxisData
|
|
401
|
+
}
|
|
402
|
+
if (sortByRecentDate) {
|
|
403
|
+
// Sort from newest to oldes (recent dates first)
|
|
404
|
+
return xAxisData.sort((a, b) => Number(b) - Number(a))
|
|
405
|
+
} else {
|
|
406
|
+
// Sort from oldest to newest
|
|
407
|
+
return xAxisData.sort((a, b) => Number(a) - Number(b))
|
|
408
|
+
}
|
|
409
|
+
}
|
package/src/hooks/useTooltip.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
|
7
7
|
// Third-party library imports
|
|
8
8
|
import { localPoint } from '@visx/event'
|
|
9
9
|
import { bisector } from 'd3-array'
|
|
10
|
+
import _ from 'lodash'
|
|
10
11
|
|
|
11
12
|
export const useTooltip = props => {
|
|
12
13
|
const {
|
|
@@ -56,14 +57,9 @@ export const useTooltip = props => {
|
|
|
56
57
|
const getFormattedValue = (seriesKey, value, config, getAxisPosition) => {
|
|
57
58
|
// handle case where data is missing
|
|
58
59
|
const showMissingDataValue = config.general.showMissingDataLabel && (!value || value === 'null')
|
|
59
|
-
|
|
60
|
+
const formattedValue = seriesKey === config.xAxis.dataKey ? value : formatNumber(value, getAxisPosition(seriesKey))
|
|
60
61
|
|
|
61
|
-
formattedValue
|
|
62
|
-
showMissingDataValue && (config.visualizationSubType === 'stacked' ? !config.general.hideNullValue : true)
|
|
63
|
-
? 'N/A'
|
|
64
|
-
: formattedValue
|
|
65
|
-
|
|
66
|
-
return formattedValue
|
|
62
|
+
return showMissingDataValue ? 'N/A' : formattedValue
|
|
67
63
|
}
|
|
68
64
|
|
|
69
65
|
const getTooltipInformation = (tooltipDataArray, eventSvgCoords) => {
|
|
@@ -104,37 +100,9 @@ export const useTooltip = props => {
|
|
|
104
100
|
|
|
105
101
|
const closestXScaleValue = getXValueFromCoordinate(x - Number(config.yAxis.size || 0))
|
|
106
102
|
|
|
107
|
-
const includedSeries =
|
|
108
|
-
visualizationType !== 'Pie'
|
|
109
|
-
? config.runtime.series.filter(series => series.tooltip === true).map(item => item.dataKey)
|
|
110
|
-
: config.runtime.series.map(item => item.dataKey)
|
|
111
|
-
includedSeries.push(config.xAxis.dataKey)
|
|
112
|
-
if (config.visualizationType === 'Forecasting') {
|
|
113
|
-
config.runtime.series.map(s => {
|
|
114
|
-
s.confidenceIntervals.map(c => {
|
|
115
|
-
if (c.showInTooltip) {
|
|
116
|
-
includedSeries.push(c.high)
|
|
117
|
-
includedSeries.push(c.low)
|
|
118
|
-
}
|
|
119
|
-
})
|
|
120
|
-
})
|
|
121
|
-
}
|
|
122
|
-
function getColumnNames(columns) {
|
|
123
|
-
let names = []
|
|
124
|
-
for (let key in columns) {
|
|
125
|
-
if (columns.hasOwnProperty(key)) {
|
|
126
|
-
names.push(columns[key].name)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return names
|
|
130
|
-
}
|
|
131
|
-
includedSeries.push(...getColumnNames(config.columns))
|
|
132
|
-
includedSeries.push(...getColumnNames(config.columns))
|
|
133
|
-
|
|
134
|
-
const yScaleValues = getYScaleValues(closestXScaleValue, includedSeries)
|
|
135
103
|
const xScaleValues = data.filter(d => d[xAxis.dataKey] === getClosestYValue(y))
|
|
136
104
|
|
|
137
|
-
const resolvedScaleValues = orientation === 'vertical' ?
|
|
105
|
+
const resolvedScaleValues = orientation === 'vertical' ? getYScaleValues(closestXScaleValue) : xScaleValues
|
|
138
106
|
|
|
139
107
|
const getAxisPosition = seriesKey => {
|
|
140
108
|
const seriesObj = config.runtime.series.filter(s => s.dataKey === seriesKey)[0]
|
|
@@ -190,18 +158,36 @@ export const useTooltip = props => {
|
|
|
190
158
|
if (visualizationType !== 'Pie' && visualizationType !== 'Forest Plot' && !config.tooltips.singleSeries) {
|
|
191
159
|
tooltipItems.push(
|
|
192
160
|
...getIncludedTooltipSeries()
|
|
193
|
-
?.filter(
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
)
|
|
161
|
+
?.filter(seriesKey => {
|
|
162
|
+
const series = config.runtime.series?.find(
|
|
163
|
+
s => s.dataKey === seriesKey && s?.tooltip && !s.dynamicCategory
|
|
164
|
+
)
|
|
165
|
+
return series || config.xAxis?.dataKey == seriesKey || visualizationType === 'Forecasting'
|
|
166
|
+
})
|
|
199
167
|
?.flatMap(seriesKey => {
|
|
200
168
|
const value = resolvedScaleValues[0]?.[seriesKey]
|
|
201
169
|
const formattedValue = getFormattedValue(seriesKey, value, config, getAxisPosition)
|
|
202
|
-
|
|
170
|
+
if (
|
|
171
|
+
(value === null || value === undefined || value === '' || formattedValue === 'N/A') &&
|
|
172
|
+
config.general.hideNullValue
|
|
173
|
+
) {
|
|
174
|
+
return []
|
|
175
|
+
} else {
|
|
176
|
+
return [[seriesKey, formattedValue, getAxisPosition(seriesKey)]]
|
|
177
|
+
}
|
|
203
178
|
})
|
|
204
179
|
)
|
|
180
|
+
|
|
181
|
+
config.runtime.series?.forEach(series => {
|
|
182
|
+
if (series?.dynamicCategory) {
|
|
183
|
+
const seriesKey = series.dataKey
|
|
184
|
+
const resolvedScaleValue = resolvedScaleValues.find(v => v[series.dynamicCategory] === seriesKey)
|
|
185
|
+
if (!resolvedScaleValue) return
|
|
186
|
+
const value = resolvedScaleValue[series.originalDataKey]
|
|
187
|
+
const formattedValue = getFormattedValue(seriesKey, value, config, getAxisPosition)
|
|
188
|
+
tooltipItems.push([seriesKey, formattedValue, getAxisPosition(seriesKey)])
|
|
189
|
+
}
|
|
190
|
+
})
|
|
205
191
|
}
|
|
206
192
|
|
|
207
193
|
// handle tooltip for single hovered series
|
|
@@ -372,25 +358,48 @@ export const useTooltip = props => {
|
|
|
372
358
|
/**
|
|
373
359
|
* Provides an array of objects with the closest y series data items
|
|
374
360
|
* @param {String} closestXScaleValue
|
|
375
|
-
* @param {Array} includedSeries
|
|
376
361
|
* @returns an array of objects with the closest y series data items
|
|
377
362
|
*/
|
|
378
|
-
const getYScaleValues =
|
|
379
|
-
|
|
380
|
-
|
|
363
|
+
const getYScaleValues = closestXScaleValue => {
|
|
364
|
+
const runtimeSeries = config.runtime.series.filter(
|
|
365
|
+
series => visualizationType === 'Pie' || (series.tooltip === true && !series.dynamicCategory)
|
|
366
|
+
)
|
|
367
|
+
const includedSeries = runtimeSeries.map(item => item.dataKey)
|
|
368
|
+
includedSeries.push(config.xAxis.dataKey)
|
|
369
|
+
// get dynamic category series
|
|
370
|
+
const dynamicDataCategories = _.uniq(
|
|
371
|
+
config.runtime.series.flatMap(series => {
|
|
372
|
+
if (series.dynamicCategory) {
|
|
373
|
+
return [series.dynamicCategory, series.originalDataKey]
|
|
374
|
+
}
|
|
375
|
+
})
|
|
376
|
+
)
|
|
377
|
+
includedSeries.push(...dynamicDataCategories)
|
|
381
378
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
379
|
+
if (config.visualizationType === 'Forecasting') {
|
|
380
|
+
config.runtime.series.map(s => {
|
|
381
|
+
s.confidenceIntervals.map(c => {
|
|
382
|
+
if (c.showInTooltip) {
|
|
383
|
+
includedSeries.push(c.high)
|
|
384
|
+
includedSeries.push(c.low)
|
|
385
|
+
}
|
|
386
|
+
})
|
|
387
|
+
})
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const colNames = Object.values(config.columns).map(column => column.name)
|
|
391
|
+
// @ Murad why are we adding them twice?
|
|
392
|
+
includedSeries.push(...colNames, ...colNames)
|
|
393
|
+
|
|
394
|
+
try {
|
|
395
|
+
const dataToSearch = data.filter(d => d[xAxis.dataKey] === closestXScaleValue)
|
|
387
396
|
// Return an empty array if no matching data is found.
|
|
388
397
|
if (!dataToSearch || dataToSearch.length === 0) {
|
|
389
398
|
return []
|
|
390
399
|
}
|
|
391
400
|
|
|
392
401
|
const yScaleValues = dataToSearch.map(object => {
|
|
393
|
-
return
|
|
402
|
+
return _.pick(object, includedSeries)
|
|
394
403
|
})
|
|
395
404
|
return yScaleValues
|
|
396
405
|
} catch (error) {
|
package/src/index.jsx
CHANGED
package/src/scss/DataTable.scss
CHANGED
package/src/scss/main.scss
CHANGED
|
@@ -1,6 +1,50 @@
|
|
|
1
|
-
@
|
|
2
|
-
@
|
|
3
|
-
@
|
|
1
|
+
@mixin breakpoint($class) {
|
|
2
|
+
@if $class == xs {
|
|
3
|
+
@media (max-width: 767px) {
|
|
4
|
+
@content;
|
|
5
|
+
}
|
|
6
|
+
} @else if $class == sm {
|
|
7
|
+
@media (min-width: 768px) {
|
|
8
|
+
@content;
|
|
9
|
+
}
|
|
10
|
+
} @else if $class == md {
|
|
11
|
+
@media (min-width: 960px) {
|
|
12
|
+
@content;
|
|
13
|
+
}
|
|
14
|
+
} @else if $class == lg {
|
|
15
|
+
@media (min-width: 1300px) {
|
|
16
|
+
@content;
|
|
17
|
+
}
|
|
18
|
+
} @else {
|
|
19
|
+
@warn "Breakpoint mixin supports: xs, sm, md, lg";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@mixin breakpointClass($class) {
|
|
24
|
+
@if $class == xs {
|
|
25
|
+
&.xs,
|
|
26
|
+
&.xxs {
|
|
27
|
+
@content;
|
|
28
|
+
}
|
|
29
|
+
} @else if $class == sm {
|
|
30
|
+
&.sm,
|
|
31
|
+
&.md,
|
|
32
|
+
&.lg {
|
|
33
|
+
@content;
|
|
34
|
+
}
|
|
35
|
+
} @else if $class == md {
|
|
36
|
+
&.md,
|
|
37
|
+
&.lg {
|
|
38
|
+
@content;
|
|
39
|
+
}
|
|
40
|
+
} @else if $class == lg {
|
|
41
|
+
&.lg {
|
|
42
|
+
@content;
|
|
43
|
+
}
|
|
44
|
+
} @else {
|
|
45
|
+
@warn "Breakpoint Class mixin supports: xs, sm, md, lg";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
4
48
|
|
|
5
49
|
.form-container {
|
|
6
50
|
overflow-y: auto;
|
|
@@ -76,23 +120,6 @@
|
|
|
76
120
|
}
|
|
77
121
|
}
|
|
78
122
|
|
|
79
|
-
.btn {
|
|
80
|
-
background: #005eaa;
|
|
81
|
-
color: #fff !important;
|
|
82
|
-
border: 0;
|
|
83
|
-
padding: 0.4em 0.8em;
|
|
84
|
-
display: block;
|
|
85
|
-
border-radius: 5px;
|
|
86
|
-
transition: 0.1s all;
|
|
87
|
-
cursor: pointer;
|
|
88
|
-
|
|
89
|
-
&[disabled] {
|
|
90
|
-
opacity: 0.5;
|
|
91
|
-
z-index: -1;
|
|
92
|
-
position: relative;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
123
|
.warning-icon {
|
|
97
124
|
position: relative;
|
|
98
125
|
top: 2px;
|
|
@@ -129,11 +156,12 @@
|
|
|
129
156
|
padding: 15px;
|
|
130
157
|
vertical-align: top;
|
|
131
158
|
text-align: left;
|
|
132
|
-
border: 1px solid
|
|
159
|
+
border: 1px solid var(--lightGray);
|
|
133
160
|
position: relative;
|
|
134
161
|
|
|
135
162
|
&.no-border {
|
|
136
163
|
border: 1px solid transparent;
|
|
164
|
+
padding: 0;
|
|
137
165
|
}
|
|
138
166
|
|
|
139
167
|
&__inner {
|
|
@@ -147,7 +175,7 @@
|
|
|
147
175
|
&.vertical-sorted {
|
|
148
176
|
display: block;
|
|
149
177
|
|
|
150
|
-
@include breakpoint(
|
|
178
|
+
@include breakpoint(sm) {
|
|
151
179
|
column-count: 2;
|
|
152
180
|
column-width: 100%;
|
|
153
181
|
}
|
|
@@ -168,7 +196,6 @@
|
|
|
168
196
|
|
|
169
197
|
.legend-item {
|
|
170
198
|
text-align: left;
|
|
171
|
-
align-items: flex-start !important;
|
|
172
199
|
user-select: none;
|
|
173
200
|
white-space: nowrap;
|
|
174
201
|
|
|
@@ -177,10 +204,6 @@
|
|
|
177
204
|
white-space: pre-wrap;
|
|
178
205
|
word-break: break-word;
|
|
179
206
|
}
|
|
180
|
-
|
|
181
|
-
& > :first-child {
|
|
182
|
-
margin-top: 3px;
|
|
183
|
-
}
|
|
184
207
|
}
|
|
185
208
|
|
|
186
209
|
.vertical-sorted:not(.single-row) .legend-item {
|
|
@@ -199,11 +222,11 @@
|
|
|
199
222
|
|
|
200
223
|
h3,
|
|
201
224
|
p {
|
|
202
|
-
margin-bottom: 0.
|
|
225
|
+
margin-bottom: 0.4em;
|
|
203
226
|
}
|
|
204
227
|
|
|
205
228
|
& div.legend-item {
|
|
206
|
-
margin-bottom: 0.
|
|
229
|
+
margin-bottom: 0.2em !important;
|
|
207
230
|
|
|
208
231
|
&:last-child {
|
|
209
232
|
margin: 0 !important;
|
|
@@ -456,46 +479,6 @@
|
|
|
456
479
|
}
|
|
457
480
|
}
|
|
458
481
|
|
|
459
|
-
.filters-section {
|
|
460
|
-
margin-left: 1rem;
|
|
461
|
-
margin-right: 1rem;
|
|
462
|
-
|
|
463
|
-
&__title {
|
|
464
|
-
margin: 15px 0;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
&__wrapper {
|
|
468
|
-
hr {
|
|
469
|
-
margin-bottom: 20px;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
label {
|
|
473
|
-
display: inherit;
|
|
474
|
-
margin-bottom: 5px;
|
|
475
|
-
font-weight: 600;
|
|
476
|
-
font-size: 16px;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
@include breakpoint(md) {
|
|
481
|
-
display: flex;
|
|
482
|
-
gap: 30px;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
label:not(:empty) {
|
|
486
|
-
margin-right: 0.4em;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
select {
|
|
490
|
-
font-size: 1em;
|
|
491
|
-
padding-right: 5px;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
.single-filter {
|
|
495
|
-
margin-bottom: 0.5em;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
482
|
@include breakpointClass(xs) {
|
|
500
483
|
&.font-small {
|
|
501
484
|
font-size: 0.8em;
|
|
@@ -522,7 +505,7 @@
|
|
|
522
505
|
}
|
|
523
506
|
}
|
|
524
507
|
|
|
525
|
-
@include breakpointClass(
|
|
508
|
+
@include breakpointClass(sm) {
|
|
526
509
|
.chart-container {
|
|
527
510
|
.no-wrap {
|
|
528
511
|
flex-wrap: nowrap;
|
|
@@ -544,7 +527,11 @@
|
|
|
544
527
|
margin-left: 0;
|
|
545
528
|
}
|
|
546
529
|
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
547
532
|
|
|
533
|
+
@include breakpointClass(md) {
|
|
534
|
+
.chart-container {
|
|
548
535
|
> svg {
|
|
549
536
|
font-size: 16px;
|
|
550
537
|
width: 75%;
|