@cdc/chart 4.24.5 → 4.24.9
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 +44197 -38258
- package/examples/cases-year.json +13379 -0
- package/examples/feature/annotations/index.json +542 -0
- package/examples/gallery/bar-chart-vertical/combo-line-chart.json +76 -15
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +5 -5
- package/examples/xaxis.json +493 -0
- package/index.html +20 -10
- package/package.json +5 -4
- package/src/CdcChart.tsx +462 -172
- package/src/_stories/Chart.Legend.Gradient.tsx +19 -0
- package/src/_stories/Chart.stories.tsx +18 -171
- package/src/_stories/ChartAnnotation.stories.tsx +32 -0
- package/src/_stories/_mock/annotation_category_mock.json +473 -0
- package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
- package/{examples/feature/line/line-chart.json → src/_stories/_mock/annotation_date-time_mock.json} +150 -69
- package/src/_stories/_mock/legend.gradient_mock.json +236 -0
- package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
- package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
- package/src/_stories/_mock/lollipop.json +171 -0
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +207 -0
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
- package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
- package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
- package/src/components/Annotations/components/AnnotationList.tsx +42 -0
- package/src/components/Annotations/components/findNearestDatum.ts +138 -0
- package/src/components/Annotations/components/helpers/index.tsx +46 -0
- package/src/components/Annotations/index.tsx +13 -0
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
- package/src/components/AreaChart/components/AreaChart.jsx +1 -1
- package/src/components/Axis/Categorical.Axis.tsx +145 -0
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +47 -44
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +0 -1
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -14
- package/src/components/BarChart/components/BarChart.Vertical.tsx +67 -30
- package/src/components/BarChart/helpers/index.ts +91 -0
- package/src/components/BrushChart.tsx +205 -0
- package/src/components/EditorPanel/EditorPanel.tsx +1794 -403
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +320 -0
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +282 -18
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +43 -8
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -4
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +4 -13
- package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
- package/src/components/EditorPanel/components/panels.scss +4 -0
- package/src/components/EditorPanel/editor-panel.scss +35 -3
- package/src/components/EditorPanel/{useEditorPermissions.js → useEditorPermissions.ts} +105 -17
- package/src/components/Legend/Legend.Component.tsx +185 -194
- package/src/components/Legend/Legend.Suppression.tsx +146 -0
- package/src/components/Legend/Legend.tsx +21 -5
- package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
- package/src/components/Legend/helpers/index.ts +35 -0
- package/src/components/LegendWrapper.tsx +26 -0
- package/src/components/LineChart/LineChartProps.ts +1 -15
- package/src/components/LineChart/components/LineChart.BumpCircle.tsx +103 -0
- package/src/components/LineChart/components/LineChart.Circle.tsx +47 -8
- package/src/components/LineChart/helpers.ts +72 -14
- package/src/components/LineChart/index.tsx +117 -42
- package/src/components/LinearChart.jsx +179 -136
- package/src/components/LinearChart.tsx +1366 -0
- package/src/components/PairedBarChart.jsx +9 -9
- package/src/components/PieChart/PieChart.tsx +75 -18
- package/src/components/Sankey/index.tsx +89 -30
- package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
- package/src/components/Sparkline/components/SparkLine.tsx +2 -2
- package/src/components/ZoomBrush.tsx +90 -44
- package/src/data/initial-state.js +25 -7
- package/src/helpers/handleChartTabbing.ts +8 -0
- package/src/helpers/isConvertLineToBarGraph.ts +4 -0
- package/src/hooks/{useBarChart.js → useBarChart.ts} +2 -40
- package/src/hooks/useColorScale.ts +1 -1
- package/src/hooks/useLegendClasses.ts +68 -0
- package/src/hooks/useMinMax.ts +12 -7
- package/src/hooks/useScales.ts +58 -26
- package/src/hooks/useTooltip.tsx +135 -25
- package/src/scss/DataTable.scss +2 -1
- package/src/scss/main.scss +128 -28
- package/src/types/ChartConfig.ts +83 -10
- package/src/types/ChartContext.ts +14 -4
- package/tests-examples/helpers/testZeroValue.test.ts +30 -0
- package/LICENSE +0 -201
- package/src/components/BrushHandle.jsx +0 -17
- package/src/components/LineChart/index.scss +0 -1
- package/src/helpers/filterData.ts +0 -18
- package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
- package/src/hooks/useLegendClasses.js +0 -31
- /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
package/src/CdcChart.tsx
CHANGED
|
@@ -7,6 +7,9 @@ import * as d3 from 'd3-array'
|
|
|
7
7
|
import Layout from '@cdc/core/components/Layout'
|
|
8
8
|
import Button from '@cdc/core/components/elements/Button'
|
|
9
9
|
|
|
10
|
+
//types
|
|
11
|
+
import { DimensionsType } from '@cdc/core/types/Dimensions'
|
|
12
|
+
|
|
10
13
|
// External Libraries
|
|
11
14
|
import { scaleOrdinal } from '@visx/scale'
|
|
12
15
|
import ParentSize from '@visx/responsive/lib/components/ParentSize'
|
|
@@ -29,9 +32,9 @@ import Legend from './components/Legend'
|
|
|
29
32
|
import defaults from './data/initial-state'
|
|
30
33
|
import EditorPanel from './components/EditorPanel'
|
|
31
34
|
import { abbreviateNumber } from './helpers/abbreviateNumber'
|
|
35
|
+
import { handleChartTabbing } from './helpers/handleChartTabbing'
|
|
32
36
|
import { getQuartiles } from './helpers/getQuartiles'
|
|
33
37
|
import { sortAsc, sortDesc } from './helpers/sort'
|
|
34
|
-
import { filterData } from './helpers/filterData'
|
|
35
38
|
import { handleChartAriaLabels } from './helpers/handleChartAriaLabels'
|
|
36
39
|
import { lineOptions } from './helpers/lineOptions'
|
|
37
40
|
import { handleLineType } from './helpers/handleLineType'
|
|
@@ -39,8 +42,10 @@ import { generateColorsArray } from './helpers/generateColorsArray'
|
|
|
39
42
|
import Loading from '@cdc/core/components/Loading'
|
|
40
43
|
import Filters from '@cdc/core/components/Filters'
|
|
41
44
|
import MediaControls from '@cdc/core/components/MediaControls'
|
|
45
|
+
import Annotation from './components/Annotations'
|
|
42
46
|
|
|
43
47
|
// Helpers
|
|
48
|
+
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
44
49
|
import { publish, subscribe, unsubscribe } from '@cdc/core/helpers/events'
|
|
45
50
|
import useDataVizClasses from '@cdc/core/helpers/useDataVizClasses'
|
|
46
51
|
import numberFromString from '@cdc/core/helpers/numberFromString'
|
|
@@ -50,6 +55,7 @@ import cacheBustingString from '@cdc/core/helpers/cacheBustingString'
|
|
|
50
55
|
import isNumber from '@cdc/core/helpers/isNumber'
|
|
51
56
|
import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
|
|
52
57
|
import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
|
|
58
|
+
import { isConvertLineToBarGraph } from './helpers/isConvertLineToBarGraph'
|
|
53
59
|
|
|
54
60
|
import './scss/main.scss'
|
|
55
61
|
// load both then config below determines which to use
|
|
@@ -58,10 +64,26 @@ import { getFileExtension } from '@cdc/core/helpers/getFileExtension'
|
|
|
58
64
|
import Title from '@cdc/core/components/ui/Title'
|
|
59
65
|
import { ChartConfig } from './types/ChartConfig'
|
|
60
66
|
import { Label } from './types/Label'
|
|
67
|
+
import { type ViewportSize } from './types/ChartConfig'
|
|
61
68
|
import { isSolrCsv, isSolrJson } from '@cdc/core/helpers/isSolr'
|
|
62
69
|
import SkipTo from '@cdc/core/components/elements/SkipTo'
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
import { filterVizData } from '@cdc/core/helpers/filterVizData'
|
|
71
|
+
import LegendWrapper from './components/LegendWrapper'
|
|
72
|
+
|
|
73
|
+
export default function CdcChart({
|
|
74
|
+
configUrl,
|
|
75
|
+
config: configObj,
|
|
76
|
+
isEditor = false,
|
|
77
|
+
isDebug = false,
|
|
78
|
+
isDashboard = false,
|
|
79
|
+
setConfig: setParentConfig,
|
|
80
|
+
setEditing,
|
|
81
|
+
hostname,
|
|
82
|
+
link,
|
|
83
|
+
setSharedFilter,
|
|
84
|
+
setSharedFilterValue,
|
|
85
|
+
dashboardConfig
|
|
86
|
+
}) {
|
|
65
87
|
const transform = new DataTransform()
|
|
66
88
|
const [loading, setLoading] = useState(true)
|
|
67
89
|
const [colorScale, setColorScale] = useState(null)
|
|
@@ -69,12 +91,15 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
69
91
|
const [stateData, setStateData] = useState(config.data || [])
|
|
70
92
|
const [excludedData, setExcludedData] = useState<Record<string, number>[] | undefined>(undefined)
|
|
71
93
|
const [filteredData, setFilteredData] = useState<Record<string, any>[] | undefined>(undefined)
|
|
72
|
-
const [seriesHighlight, setSeriesHighlight] = useState<string[]>(
|
|
73
|
-
|
|
74
|
-
|
|
94
|
+
const [seriesHighlight, setSeriesHighlight] = useState<string[]>(
|
|
95
|
+
configObj && configObj?.legend?.seriesHighlight?.length ? [...configObj?.legend?.seriesHighlight] : []
|
|
96
|
+
)
|
|
97
|
+
const [currentViewport, setCurrentViewport] = useState<ViewportSize>('lg')
|
|
98
|
+
const [dimensions, setDimensions] = useState<DimensionsType>([0, 0])
|
|
75
99
|
const [externalFilters, setExternalFilters] = useState<any[]>()
|
|
76
100
|
const [container, setContainer] = useState()
|
|
77
101
|
const [coveLoadedEventRan, setCoveLoadedEventRan] = useState(false)
|
|
102
|
+
const [isDraggingAnnotation, setIsDraggingAnnotation] = useState(false)
|
|
78
103
|
const [dynamicLegendItems, setDynamicLegendItems] = useState<any[]>([])
|
|
79
104
|
const [imageId] = useState(`cove-${Math.random().toString(16).slice(-4)}`)
|
|
80
105
|
const [brushConfig, setBrushConfig] = useState({
|
|
@@ -82,11 +107,25 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
82
107
|
isActive: false,
|
|
83
108
|
isBrushing: false
|
|
84
109
|
})
|
|
110
|
+
|
|
111
|
+
let [width] = dimensions
|
|
112
|
+
const useVertical = config.orientation === 'vertical'
|
|
113
|
+
const useMobileVertical = config.heights?.mobileVertical && ['xs', 'xxs'].includes(currentViewport)
|
|
114
|
+
const responsiveVertical = useMobileVertical ? 'mobileVertical' : 'vertical'
|
|
115
|
+
const renderedOrientation = useVertical ? responsiveVertical : 'horizontal'
|
|
116
|
+
let height = config.aspectRatio ? width * config.aspectRatio : config?.heights?.[renderedOrientation]
|
|
117
|
+
if (config.visualizationType === 'Pie') height = config?.heights?.[renderedOrientation]
|
|
118
|
+
height = height + Number(config?.xAxis?.size) + 45
|
|
119
|
+
|
|
85
120
|
type Config = typeof config
|
|
86
121
|
let legendMemo = useRef(new Map()) // map collection
|
|
87
122
|
let innerContainerRef = useRef()
|
|
88
123
|
const legendRef = useRef(null)
|
|
89
124
|
|
|
125
|
+
const handleDragStateChange = isDragging => {
|
|
126
|
+
setIsDraggingAnnotation(isDragging)
|
|
127
|
+
}
|
|
128
|
+
|
|
90
129
|
if (isDebug) console.log('Chart config, isEditor', config, isEditor)
|
|
91
130
|
|
|
92
131
|
// Destructure items from config for more readable JSX
|
|
@@ -101,7 +140,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
101
140
|
|
|
102
141
|
const { barBorderClass, lineDatapointClass, contentClasses, sparkLineStyles } = useDataVizClasses(config)
|
|
103
142
|
const legendId = useId()
|
|
104
|
-
|
|
143
|
+
|
|
144
|
+
const checkLineToBarGraph = () => {
|
|
145
|
+
return isConvertLineToBarGraph(config.visualizationType, filteredData, config.allowLineToBarGraph)
|
|
146
|
+
}
|
|
105
147
|
|
|
106
148
|
const reloadURLData = async () => {
|
|
107
149
|
if (config.dataUrl) {
|
|
@@ -164,7 +206,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
164
206
|
if (data) {
|
|
165
207
|
setStateData(data)
|
|
166
208
|
setExcludedData(data)
|
|
167
|
-
setFilteredData(
|
|
209
|
+
setFilteredData(filterVizData(config.filters, data))
|
|
168
210
|
}
|
|
169
211
|
}
|
|
170
212
|
}
|
|
@@ -175,7 +217,11 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
175
217
|
// If data is included through a URL, fetch that and store
|
|
176
218
|
let data: any[] = response.data || []
|
|
177
219
|
|
|
178
|
-
const urlFilters = response.filters
|
|
220
|
+
const urlFilters = response.filters
|
|
221
|
+
? response.filters.filter(filter => filter.type === 'url').length > 0
|
|
222
|
+
? true
|
|
223
|
+
: false
|
|
224
|
+
: false
|
|
179
225
|
|
|
180
226
|
if (response.dataUrl && !urlFilters) {
|
|
181
227
|
try {
|
|
@@ -251,11 +297,11 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
251
297
|
if (!series.axis) series.axis = 'Left'
|
|
252
298
|
})
|
|
253
299
|
|
|
254
|
-
if (
|
|
300
|
+
if (data) {
|
|
255
301
|
newConfig.data = data
|
|
256
302
|
}
|
|
257
303
|
|
|
258
|
-
const processedConfig = { ...
|
|
304
|
+
const processedConfig = { ...coveUpdateWorker(newConfig) }
|
|
259
305
|
|
|
260
306
|
updateConfig(processedConfig, data)
|
|
261
307
|
}
|
|
@@ -275,7 +321,11 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
275
321
|
if (newConfig.exclusions && newConfig.exclusions.active) {
|
|
276
322
|
if (newConfig.xAxis.type === 'categorical' && newConfig.exclusions.keys?.length > 0) {
|
|
277
323
|
newExcludedData = data.filter(e => !newConfig.exclusions.keys.includes(e[newConfig.xAxis.dataKey]))
|
|
278
|
-
} else if (
|
|
324
|
+
} else if (
|
|
325
|
+
isDateScale(newConfig.xAxis) &&
|
|
326
|
+
(newConfig.exclusions.dateStart || newConfig.exclusions.dateEnd) &&
|
|
327
|
+
newConfig.xAxis.dateParseFormat
|
|
328
|
+
) {
|
|
279
329
|
// Filter dates
|
|
280
330
|
const timestamp = e => new Date(e).getTime()
|
|
281
331
|
|
|
@@ -286,7 +336,9 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
286
336
|
let endDateValid = undefined !== typeof endDate && false === isNaN(endDate)
|
|
287
337
|
|
|
288
338
|
if (startDateValid && endDateValid) {
|
|
289
|
-
newExcludedData = data.filter(
|
|
339
|
+
newExcludedData = data.filter(
|
|
340
|
+
e => timestamp(e[newConfig.xAxis.dataKey]) >= startDate && timestamp(e[newConfig.xAxis.dataKey]) <= endDate
|
|
341
|
+
)
|
|
290
342
|
} else if (startDateValid) {
|
|
291
343
|
newExcludedData = data.filter(e => timestamp(e[newConfig.xAxis.dataKey]) >= startDate)
|
|
292
344
|
} else if (endDateValid) {
|
|
@@ -305,38 +357,68 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
305
357
|
let currentData: any[] = []
|
|
306
358
|
if (newConfig.filters) {
|
|
307
359
|
newConfig.filters.forEach((filter, index) => {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
360
|
+
const filterValues =
|
|
361
|
+
filter.filterStyle === 'nested-dropdown'
|
|
362
|
+
? filter.values
|
|
363
|
+
: filter.orderedValues ||
|
|
364
|
+
generateValuesForFilter(filter.columnName, newExcludedData).sort(
|
|
365
|
+
filter.order === 'desc' ? sortDesc : sortAsc
|
|
366
|
+
)
|
|
311
367
|
|
|
312
368
|
newConfig.filters[index].values = filterValues
|
|
313
369
|
// Initial filter should be active
|
|
314
370
|
|
|
315
|
-
|
|
316
|
-
newConfig.filters[index].
|
|
371
|
+
const includes = (arr: any[], val: any): boolean => (arr || []).map(val => String(val)).includes(String(val))
|
|
372
|
+
newConfig.filters[index].active =
|
|
373
|
+
!newConfig.filters[index].active || !includes(filterValues, newConfig.filters[index].active)
|
|
374
|
+
? filterValues[0]
|
|
375
|
+
: newConfig.filters[index].active
|
|
376
|
+
newConfig.filters[index].filterStyle = newConfig.filters[index].filterStyle
|
|
377
|
+
? newConfig.filters[index].filterStyle
|
|
378
|
+
: 'dropdown'
|
|
317
379
|
})
|
|
318
|
-
currentData =
|
|
380
|
+
currentData = filterVizData(newConfig.filters, newExcludedData)
|
|
319
381
|
setFilteredData(currentData)
|
|
320
382
|
}
|
|
321
383
|
|
|
322
|
-
if (newConfig.xAxis.type === 'date-time' &&
|
|
323
|
-
newConfig.
|
|
324
|
-
} else if (newConfig.xAxis.type !== 'date-time' && newConfig.barThickness < 0.1) {
|
|
325
|
-
newConfig.barThickness = 0.35
|
|
384
|
+
if (newConfig.xAxis.type === 'date-time' && config.orientation === 'horizontal') {
|
|
385
|
+
newConfig.xAxis.type = 'date'
|
|
326
386
|
}
|
|
327
387
|
|
|
328
388
|
//Enforce default values that need to be calculated at runtime
|
|
329
389
|
newConfig.runtime = {}
|
|
390
|
+
newConfig.runtime.series = newConfig.dynamicSeries ? [] : newConfig.series
|
|
330
391
|
newConfig.runtime.seriesLabels = {}
|
|
331
392
|
newConfig.runtime.seriesLabelsAll = []
|
|
332
393
|
newConfig.runtime.originalXAxis = newConfig.xAxis
|
|
333
394
|
|
|
395
|
+
if (newConfig.dynamicSeries) {
|
|
396
|
+
let finalData = dataOverride || newConfig.formattedData || newConfig.data
|
|
397
|
+
if (finalData && finalData.length && finalData.length > 0) {
|
|
398
|
+
Object.keys(finalData[0]).forEach(seriesKey => {
|
|
399
|
+
if (
|
|
400
|
+
seriesKey !== newConfig.xAxis.dataKey &&
|
|
401
|
+
finalData[0][seriesKey] &&
|
|
402
|
+
(!newConfig.filters || newConfig.filters.filter(filter => filter.columnName === seriesKey).length === 0) &&
|
|
403
|
+
(!newConfig.columns || Object.keys(newConfig.columns).indexOf(seriesKey) === -1)
|
|
404
|
+
) {
|
|
405
|
+
newConfig.runtime.series.push({
|
|
406
|
+
dataKey: seriesKey,
|
|
407
|
+
type: newConfig.dynamicSeriesType,
|
|
408
|
+
lineType: newConfig.dynamicSeriesLineType,
|
|
409
|
+
tooltip: true
|
|
410
|
+
})
|
|
411
|
+
}
|
|
412
|
+
})
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
334
416
|
if (newConfig.visualizationType === 'Pie') {
|
|
335
417
|
newConfig.runtime.seriesKeys = (dataOverride || data).map(d => d[newConfig.xAxis.dataKey])
|
|
336
418
|
newConfig.runtime.seriesLabelsAll = newConfig.runtime.seriesKeys
|
|
337
419
|
} else {
|
|
338
|
-
newConfig.runtime.seriesKeys = newConfig.series
|
|
339
|
-
? newConfig.series.map(series => {
|
|
420
|
+
newConfig.runtime.seriesKeys = newConfig.runtime.series
|
|
421
|
+
? newConfig.runtime.series.map(series => {
|
|
340
422
|
newConfig.runtime.seriesLabels[series.dataKey] = series.name || series.label || series.dataKey
|
|
341
423
|
newConfig.runtime.seriesLabelsAll.push(series.name || series.dataKey)
|
|
342
424
|
return series.dataKey
|
|
@@ -345,8 +427,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
345
427
|
}
|
|
346
428
|
|
|
347
429
|
if (newConfig.visualizationType === 'Box Plot' && newConfig.series) {
|
|
348
|
-
let allKeys = newExcludedData
|
|
349
|
-
|
|
430
|
+
let allKeys = newExcludedData
|
|
431
|
+
? newExcludedData.map(d => d[newConfig.xAxis.dataKey])
|
|
432
|
+
: data.map(d => d[newConfig.xAxis.dataKey])
|
|
433
|
+
let allValues = newExcludedData
|
|
434
|
+
? newExcludedData.map(d => Number(d[newConfig?.series[0]?.dataKey]))
|
|
435
|
+
: data.map(d => Number(d[newConfig?.series[0]?.dataKey]))
|
|
350
436
|
|
|
351
437
|
const uniqueArray = function (arrArg) {
|
|
352
438
|
return arrArg.filter(function (elem, pos, arr) {
|
|
@@ -366,7 +452,9 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
366
452
|
if (!g) throw new Error('No groups resolved in box plots')
|
|
367
453
|
|
|
368
454
|
// filter data by group
|
|
369
|
-
let filteredData = newExcludedData
|
|
455
|
+
let filteredData = newExcludedData
|
|
456
|
+
? newExcludedData.filter(item => item[newConfig.xAxis.dataKey] === g)
|
|
457
|
+
: data.filter(item => item[newConfig.xAxis.dataKey] === g)
|
|
370
458
|
let filteredDataValues: number[] = filteredData.map(item => Number(item[newConfig?.series[0]?.dataKey]))
|
|
371
459
|
|
|
372
460
|
// Sort the data for upcoming functions.
|
|
@@ -454,7 +542,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
454
542
|
if (series.type === 'Bar' || series.type === 'Combo') {
|
|
455
543
|
newConfig.runtime.barSeriesKeys.push(series.dataKey)
|
|
456
544
|
}
|
|
457
|
-
if (
|
|
545
|
+
if (
|
|
546
|
+
series.type === 'Line' ||
|
|
547
|
+
series.type === 'dashed-sm' ||
|
|
548
|
+
series.type === 'dashed-md' ||
|
|
549
|
+
series.type === 'dashed-lg'
|
|
550
|
+
) {
|
|
458
551
|
newConfig.runtime.lineSeriesKeys.push(series.dataKey)
|
|
459
552
|
}
|
|
460
553
|
if (series.type === 'Combo') {
|
|
@@ -481,13 +574,21 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
481
574
|
})
|
|
482
575
|
}
|
|
483
576
|
|
|
484
|
-
if (
|
|
577
|
+
if (
|
|
578
|
+
(newConfig.visualizationType === 'Bar' && newConfig.orientation === 'horizontal') ||
|
|
579
|
+
['Deviation Bar', 'Paired Bar', 'Forest Plot'].includes(newConfig.visualizationType)
|
|
580
|
+
) {
|
|
485
581
|
newConfig.runtime.xAxis = newConfig.yAxis['yAxis'] ? newConfig.yAxis['yAxis'] : newConfig.yAxis
|
|
486
582
|
newConfig.runtime.yAxis = newConfig.xAxis['xAxis'] ? newConfig.xAxis['xAxis'] : newConfig.xAxis
|
|
487
583
|
|
|
488
584
|
newConfig.runtime.horizontal = false
|
|
489
585
|
newConfig.orientation = 'horizontal'
|
|
490
|
-
|
|
586
|
+
// remove after COVE supports categorical axis on horizonatal bars
|
|
587
|
+
newConfig.yAxis.type = newConfig.yAxis.type === 'categorical' ? 'linear' : newConfig.yAxis.type
|
|
588
|
+
} else if (
|
|
589
|
+
['Box Plot', 'Scatter Plot', 'Area Chart', 'Line', 'Forecasting'].includes(newConfig.visualizationType) &&
|
|
590
|
+
!checkLineToBarGraph()
|
|
591
|
+
) {
|
|
491
592
|
newConfig.runtime.xAxis = newConfig.xAxis
|
|
492
593
|
newConfig.runtime.yAxis = newConfig.yAxis
|
|
493
594
|
newConfig.runtime.horizontal = false
|
|
@@ -499,10 +600,16 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
499
600
|
}
|
|
500
601
|
|
|
501
602
|
newConfig.runtime.uniqueId = Date.now()
|
|
502
|
-
newConfig.runtime.editorErrorMessage =
|
|
603
|
+
newConfig.runtime.editorErrorMessage =
|
|
604
|
+
newConfig.visualizationType === 'Pie' && !newConfig.yAxis.dataKey
|
|
605
|
+
? 'Data Key property in Y Axis section must be set for pie charts.'
|
|
606
|
+
: ''
|
|
503
607
|
|
|
504
608
|
// Sankey Description box error message
|
|
505
|
-
newConfig.runtime.editorErrorMessage =
|
|
609
|
+
newConfig.runtime.editorErrorMessage =
|
|
610
|
+
newConfig.visualizationType === 'Sankey' && !newConfig.description
|
|
611
|
+
? 'SUBTEXT/CITATION field is empty: A description of the Sankey Diagram data must be inputted.'
|
|
612
|
+
: ''
|
|
506
613
|
|
|
507
614
|
if (newConfig.legend.seriesHighlight?.length) {
|
|
508
615
|
setSeriesHighlight(newConfig.legend?.seriesHighlight)
|
|
@@ -528,7 +635,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
528
635
|
|
|
529
636
|
// Sorts data series for horizontal bar charts
|
|
530
637
|
const sortData = (a, b) => {
|
|
531
|
-
let sortKey =
|
|
638
|
+
let sortKey =
|
|
639
|
+
config.visualizationType === 'Bar' && config.visualizationSubType === 'horizontal'
|
|
640
|
+
? config.xAxis.dataKey
|
|
641
|
+
: config.yAxis.sortKey
|
|
532
642
|
let aData = parseFloat(a[sortKey])
|
|
533
643
|
let bData = parseFloat(b[sortKey])
|
|
534
644
|
|
|
@@ -545,15 +655,14 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
545
655
|
const resizeObserver = new ResizeObserver(entries => {
|
|
546
656
|
for (let entry of entries) {
|
|
547
657
|
let { width, height } = entry.contentRect
|
|
548
|
-
let newViewport = getViewport(width)
|
|
549
658
|
let svgMarginWidth = 32
|
|
550
659
|
let editorWidth = 350
|
|
551
660
|
|
|
552
|
-
|
|
661
|
+
width = isEditor ? width - editorWidth : width
|
|
553
662
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
663
|
+
let newViewport = getViewport(width)
|
|
664
|
+
|
|
665
|
+
setCurrentViewport(newViewport)
|
|
557
666
|
|
|
558
667
|
if (entry.target.dataset.lollipop === 'true') {
|
|
559
668
|
width = width - 2.5
|
|
@@ -628,14 +737,19 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
628
737
|
let configCopy = { ...config }
|
|
629
738
|
delete configCopy['filters']
|
|
630
739
|
setConfig(configCopy)
|
|
631
|
-
setFilteredData(
|
|
740
|
+
setFilteredData(filterVizData(externalFilters, excludedData))
|
|
632
741
|
}
|
|
633
742
|
}
|
|
634
743
|
|
|
635
|
-
if (
|
|
744
|
+
if (
|
|
745
|
+
externalFilters &&
|
|
746
|
+
externalFilters.length > 0 &&
|
|
747
|
+
externalFilters.length > 0 &&
|
|
748
|
+
externalFilters[0].hasOwnProperty('active')
|
|
749
|
+
) {
|
|
636
750
|
let newConfigHere = { ...config, filters: externalFilters }
|
|
637
751
|
setConfig(newConfigHere)
|
|
638
|
-
setFilteredData(
|
|
752
|
+
setFilteredData(filterVizData(externalFilters, excludedData))
|
|
639
753
|
}
|
|
640
754
|
}, [externalFilters]) // eslint-disable-line
|
|
641
755
|
|
|
@@ -647,10 +761,25 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
647
761
|
}, [configObj.data]) // eslint-disable-line
|
|
648
762
|
}
|
|
649
763
|
|
|
764
|
+
// This will set the bump chart's default scaling type to date-time
|
|
765
|
+
useEffect(() => {
|
|
766
|
+
if (['Bump Chart'].includes(config.visualizationType)) {
|
|
767
|
+
setConfig({
|
|
768
|
+
...config,
|
|
769
|
+
xAxis: {
|
|
770
|
+
...config.xAxis,
|
|
771
|
+
type: 'date-time'
|
|
772
|
+
}
|
|
773
|
+
})
|
|
774
|
+
}
|
|
775
|
+
}, [config.visualizationType])
|
|
776
|
+
|
|
650
777
|
// Generates color palette to pass to child chart component
|
|
651
778
|
useEffect(() => {
|
|
652
779
|
if (stateData && config.xAxis && config.runtime?.seriesKeys) {
|
|
653
|
-
const configPalette = ['Paired Bar', 'Deviation Bar'].includes(config.visualizationType)
|
|
780
|
+
const configPalette = ['Paired Bar', 'Deviation Bar'].includes(config.visualizationType)
|
|
781
|
+
? config.twoColor.palette
|
|
782
|
+
: config.palette
|
|
654
783
|
const allPalettes: Record<string, string[]> = { ...colorPalettes, ...twoColorPalette }
|
|
655
784
|
let palette = config.customColors || allPalettes[configPalette]
|
|
656
785
|
let numberOfKeys = config.runtime.seriesKeys.length
|
|
@@ -754,19 +883,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
754
883
|
return timeFormat(config.tooltips.dateDisplayFormat)(date)
|
|
755
884
|
}
|
|
756
885
|
|
|
757
|
-
// function calculates the width of given text and its font-size
|
|
758
|
-
function getTextWidth(text: string, font: string): number | undefined {
|
|
759
|
-
const canvas = document.createElement('canvas')
|
|
760
|
-
const context = canvas.getContext('2d')
|
|
761
|
-
if (!context) {
|
|
762
|
-
console.error('2d context not found')
|
|
763
|
-
return
|
|
764
|
-
}
|
|
765
|
-
context.font = font || getComputedStyle(document.body).font
|
|
766
|
-
|
|
767
|
-
return Math.ceil(context.measureText(text).width)
|
|
768
|
-
}
|
|
769
|
-
|
|
770
886
|
// Format numeric data based on settings in config OR from passed in settings for Additional Columns
|
|
771
887
|
// - use only for old horizontal data - newer formatNumber is in helper/formatNumber
|
|
772
888
|
// TODO: we should combine various formatNumber functions across this project.
|
|
@@ -785,7 +901,20 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
785
901
|
|
|
786
902
|
// destructure dataFormat values
|
|
787
903
|
let {
|
|
788
|
-
dataFormat: {
|
|
904
|
+
dataFormat: {
|
|
905
|
+
commas,
|
|
906
|
+
abbreviated,
|
|
907
|
+
roundTo,
|
|
908
|
+
prefix,
|
|
909
|
+
suffix,
|
|
910
|
+
rightRoundTo,
|
|
911
|
+
bottomRoundTo,
|
|
912
|
+
rightPrefix,
|
|
913
|
+
rightSuffix,
|
|
914
|
+
bottomPrefix,
|
|
915
|
+
bottomSuffix,
|
|
916
|
+
bottomAbbreviated
|
|
917
|
+
}
|
|
789
918
|
} = config
|
|
790
919
|
|
|
791
920
|
// check if value contains comma and remove it. later will add comma below.
|
|
@@ -856,7 +985,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
856
985
|
// Edge case for small numbers with decimals
|
|
857
986
|
// - if roundTo undefined which means it is blank, then do not round
|
|
858
987
|
|
|
859
|
-
if (
|
|
988
|
+
if (
|
|
989
|
+
(axis === 'left' && commas && abbreviated && shouldAbbreviate) ||
|
|
990
|
+
(axis === 'bottom' && commas && abbreviated && shouldAbbreviate)
|
|
991
|
+
) {
|
|
860
992
|
num = num // eslint-disable-line
|
|
861
993
|
} else {
|
|
862
994
|
num = num.toLocaleString('en-US', stringFormattingOptions)
|
|
@@ -912,21 +1044,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
912
1044
|
return String(result)
|
|
913
1045
|
}
|
|
914
1046
|
|
|
915
|
-
// Select appropriate chart type
|
|
916
|
-
const chartComponents = {
|
|
917
|
-
'Paired Bar': <LinearChart />,
|
|
918
|
-
Forecasting: <LinearChart />,
|
|
919
|
-
Bar: <LinearChart />,
|
|
920
|
-
Line: <LinearChart />,
|
|
921
|
-
Combo: <LinearChart />,
|
|
922
|
-
Pie: <PieChart />,
|
|
923
|
-
'Box Plot': <LinearChart />,
|
|
924
|
-
'Area Chart': <LinearChart />,
|
|
925
|
-
'Scatter Plot': <LinearChart />,
|
|
926
|
-
'Deviation Bar': <LinearChart />,
|
|
927
|
-
'Forest Plot': <LinearChart />
|
|
928
|
-
}
|
|
929
|
-
|
|
930
1047
|
const missingRequiredSections = () => {
|
|
931
1048
|
if (config.visualizationType === 'Sankey') return false // skip checks for now
|
|
932
1049
|
if (config.visualizationType === 'Forecasting') return false // skip required checks for now.
|
|
@@ -936,7 +1053,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
936
1053
|
return true
|
|
937
1054
|
}
|
|
938
1055
|
} else {
|
|
939
|
-
if (undefined === config?.series || false === config?.series.length > 0) {
|
|
1056
|
+
if ((undefined === config?.series || false === config?.series.length > 0) && !config?.dynamicSeries) {
|
|
940
1057
|
return true
|
|
941
1058
|
}
|
|
942
1059
|
}
|
|
@@ -1043,7 +1160,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1043
1160
|
<section className='waiting-container'>
|
|
1044
1161
|
<h3>Finish Configuring</h3>
|
|
1045
1162
|
<p>Set all required options to the left and confirm below to display a preview of the chart.</p>
|
|
1046
|
-
<Button
|
|
1163
|
+
<Button
|
|
1164
|
+
className='btn'
|
|
1165
|
+
style={{ margin: '1em auto' }}
|
|
1166
|
+
disabled={missingRequiredSections()}
|
|
1167
|
+
onClick={e => confirmDone(e)}
|
|
1168
|
+
>
|
|
1047
1169
|
I'm Done
|
|
1048
1170
|
</Button>
|
|
1049
1171
|
</section>
|
|
@@ -1107,17 +1229,40 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1107
1229
|
// Prevent render if loading
|
|
1108
1230
|
let body = <Loading />
|
|
1109
1231
|
|
|
1232
|
+
const makeClassName = string => {
|
|
1233
|
+
if (!string || !string.toLowerCase) return
|
|
1234
|
+
return string.toLowerCase().replaceAll(/ /g, '-')
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1110
1237
|
const getChartWrapperClasses = () => {
|
|
1238
|
+
const isLegendOnBottom = legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
|
|
1111
1239
|
const classes = ['chart-container', 'p-relative']
|
|
1112
|
-
if (
|
|
1113
|
-
|
|
1240
|
+
if (legend?.position) {
|
|
1241
|
+
if (['sm', 'xs', 'xxs'].includes(currentViewport) && legend?.position !== 'top') {
|
|
1242
|
+
classes.push('legend-bottom')
|
|
1243
|
+
} else {
|
|
1244
|
+
classes.push(`legend-${legend.position}`)
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
if (legend?.hide) classes.push('legend-hidden')
|
|
1114
1248
|
if (lineDatapointClass) classes.push(lineDatapointClass)
|
|
1115
1249
|
if (!config.barHasBorder) classes.push('chart-bar--no-border')
|
|
1116
|
-
if (
|
|
1250
|
+
if (config.brush?.active && dashboardConfig?.type === 'dashboard' && (!isLegendOnBottom || legend.hide))
|
|
1251
|
+
classes.push('dashboard-brush')
|
|
1117
1252
|
classes.push(...contentClasses)
|
|
1118
1253
|
return classes
|
|
1119
1254
|
}
|
|
1120
1255
|
|
|
1256
|
+
const getChartSubTextClasses = () => {
|
|
1257
|
+
const classes = ['subtext ']
|
|
1258
|
+
const isLegendOnBottom = legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
|
|
1259
|
+
|
|
1260
|
+
if (config.isResponsiveTicks) classes.push('subtext--responsive-ticks ')
|
|
1261
|
+
if (config.brush?.active && !isLegendOnBottom) classes.push('subtext--brush-active ')
|
|
1262
|
+
if (config.brush?.active && config.legend.hide) classes.push('subtext--brush-active ')
|
|
1263
|
+
return classes
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1121
1266
|
if (!loading) {
|
|
1122
1267
|
const tableLink = (
|
|
1123
1268
|
<a href={`#data-table-${config.dataKey}`} className='margin-left-href'>
|
|
@@ -1131,70 +1276,202 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1131
1276
|
{config.newViz && <Confirm />}
|
|
1132
1277
|
{undefined === config.newViz && isEditor && config.runtime && config.runtime?.editorErrorMessage && <Error />}
|
|
1133
1278
|
{!missingRequiredSections() && !config.newViz && (
|
|
1134
|
-
<div
|
|
1135
|
-
|
|
1279
|
+
<div
|
|
1280
|
+
className={`cdc-chart-inner-container cove-component__content type-${makeClassName(
|
|
1281
|
+
config.visualizationType
|
|
1282
|
+
)}`}
|
|
1283
|
+
aria-label={handleChartAriaLabels(config)}
|
|
1284
|
+
tabIndex={0}
|
|
1285
|
+
>
|
|
1286
|
+
<Title
|
|
1287
|
+
showTitle={config.showTitle}
|
|
1288
|
+
isDashboard={isDashboard}
|
|
1289
|
+
title={title}
|
|
1290
|
+
superTitle={config.superTitle}
|
|
1291
|
+
classes={['chart-title', `${config.theme}`, 'cove-component__header']}
|
|
1292
|
+
style={undefined}
|
|
1293
|
+
/>
|
|
1294
|
+
{/* Intro Text/Message */}
|
|
1295
|
+
{config?.introText && config.visualizationType !== 'Spark Line' && (
|
|
1296
|
+
<section
|
|
1297
|
+
className={`introText legend_${config.legend.hide ? 'hidden' : 'visible'}_${config.legend.position} `}
|
|
1298
|
+
>
|
|
1299
|
+
{parse(config.introText)}
|
|
1300
|
+
</section>
|
|
1301
|
+
)}
|
|
1136
1302
|
|
|
1137
1303
|
{/* Filters */}
|
|
1138
|
-
{config.filters && !externalFilters && config.visualizationType !== 'Spark Line' &&
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1304
|
+
{config.filters && !externalFilters && config.visualizationType !== 'Spark Line' && (
|
|
1305
|
+
<Filters
|
|
1306
|
+
config={config}
|
|
1307
|
+
setConfig={setConfig}
|
|
1308
|
+
setFilteredData={setFilteredData}
|
|
1309
|
+
filteredData={filteredData}
|
|
1310
|
+
excludedData={excludedData}
|
|
1311
|
+
filterData={filterVizData}
|
|
1312
|
+
dimensions={dimensions}
|
|
1313
|
+
/>
|
|
1314
|
+
)}
|
|
1315
|
+
<SkipTo skipId={handleChartTabbing(config, legendId)} skipMessage='Skip Over Chart Container' />
|
|
1316
|
+
{config.annotations?.length > 0 && (
|
|
1317
|
+
<SkipTo
|
|
1318
|
+
skipId={handleChartTabbing(config, legendId)}
|
|
1319
|
+
skipMessage={`Skip over annotations`}
|
|
1320
|
+
key={`skip-annotations`}
|
|
1321
|
+
/>
|
|
1322
|
+
)}
|
|
1323
|
+
|
|
1324
|
+
{/* Visualization Wrapper */}
|
|
1142
1325
|
<div className={getChartWrapperClasses().join(' ')}>
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
<
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
{parse(description)}
|
|
1326
|
+
<LegendWrapper>
|
|
1327
|
+
<div
|
|
1328
|
+
className={
|
|
1329
|
+
legend.hide || ['xxs', 'xs', 'sm'].includes(currentViewport)
|
|
1330
|
+
? 'w-100'
|
|
1331
|
+
: legend.position === 'bottom' || legend.position === 'top' || visualizationType === 'Sankey'
|
|
1332
|
+
? 'w-100'
|
|
1333
|
+
: 'w-75'
|
|
1334
|
+
}
|
|
1335
|
+
>
|
|
1336
|
+
{/* All charts with LinearChart */}
|
|
1337
|
+
{!['Spark Line', 'Line', 'Sankey', 'Pie', 'Sankey'].includes(config.visualizationType) && (
|
|
1338
|
+
<div style={{ height, width: `100%` }}>
|
|
1339
|
+
<ParentSize>
|
|
1340
|
+
{parent => <LinearChart parentWidth={parent.width} parentHeight={parent.height} />}
|
|
1341
|
+
</ParentSize>
|
|
1160
1342
|
</div>
|
|
1161
1343
|
)}
|
|
1162
|
-
|
|
1344
|
+
|
|
1345
|
+
{config.visualizationType === 'Pie' && (
|
|
1346
|
+
<ParentSize className='justify-content-center d-flex' style={{ height, width: `100%` }}>
|
|
1347
|
+
{parent => <PieChart parentWidth={parent.width} parentHeight={parent.height} />}
|
|
1348
|
+
</ParentSize>
|
|
1349
|
+
)}
|
|
1350
|
+
{/* Line Chart */}
|
|
1351
|
+
{config.visualizationType === 'Line' &&
|
|
1352
|
+
(checkLineToBarGraph() ? (
|
|
1353
|
+
<div style={{ height: config?.heights?.vertical, width: `100%` }}>
|
|
1354
|
+
<ParentSize>
|
|
1355
|
+
{parent => <LinearChart parentWidth={parent.width} parentHeight={parent.height} />}
|
|
1356
|
+
</ParentSize>
|
|
1357
|
+
</div>
|
|
1358
|
+
) : (
|
|
1359
|
+
<div style={{ height, width: `100%` }}>
|
|
1360
|
+
<ParentSize>
|
|
1361
|
+
{parent => <LinearChart parentWidth={parent.width} parentHeight={parent.height} />}
|
|
1362
|
+
</ParentSize>
|
|
1363
|
+
</div>
|
|
1364
|
+
))}
|
|
1365
|
+
{/* Sparkline */}
|
|
1366
|
+
{config.visualizationType === 'Spark Line' && (
|
|
1367
|
+
<>
|
|
1368
|
+
<Filters
|
|
1369
|
+
config={config}
|
|
1370
|
+
setConfig={setConfig}
|
|
1371
|
+
setFilteredData={setFilteredData}
|
|
1372
|
+
filteredData={filteredData}
|
|
1373
|
+
excludedData={excludedData}
|
|
1374
|
+
filterData={filterVizData}
|
|
1375
|
+
dimensions={dimensions}
|
|
1376
|
+
/>
|
|
1377
|
+
{config?.introText && (
|
|
1378
|
+
<section className='introText' style={{ padding: '0px 0 35px' }}>
|
|
1379
|
+
{parse(config.introText)}
|
|
1380
|
+
</section>
|
|
1381
|
+
)}
|
|
1382
|
+
<div style={{ height: `100px`, width: `100%`, ...sparkLineStyles }}>
|
|
1383
|
+
<ParentSize>{parent => <SparkLine width={parent.width} height={parent.height} />}</ParentSize>
|
|
1384
|
+
</div>
|
|
1385
|
+
{description && (
|
|
1386
|
+
<div className='subtext' style={{ padding: '35px 0 15px' }}>
|
|
1387
|
+
{parse(description)}
|
|
1388
|
+
</div>
|
|
1389
|
+
)}
|
|
1390
|
+
</>
|
|
1391
|
+
)}
|
|
1392
|
+
{/* Sankey */}
|
|
1393
|
+
{config.visualizationType === 'Sankey' && (
|
|
1394
|
+
<ParentSize aria-hidden='true'>
|
|
1395
|
+
{parent => <SankeyChart runtime={config.runtime} width={parent.width} height={parent.height} />}
|
|
1396
|
+
</ParentSize>
|
|
1397
|
+
)}
|
|
1398
|
+
</div>
|
|
1399
|
+
{/* Legend */}
|
|
1400
|
+
{!config.legend.hide &&
|
|
1401
|
+
config.visualizationType !== 'Spark Line' &&
|
|
1402
|
+
config.visualizationType !== 'Sankey' && (
|
|
1403
|
+
<Legend ref={legendRef} skipId={handleChartTabbing(config, legendId)} />
|
|
1404
|
+
)}
|
|
1405
|
+
</LegendWrapper>
|
|
1406
|
+
{/* Link */}
|
|
1407
|
+
{isDashboard && config.table && config.table.show && config.table.showDataTableLink
|
|
1408
|
+
? tableLink
|
|
1409
|
+
: link && link}
|
|
1410
|
+
{/* Description */}
|
|
1411
|
+
|
|
1412
|
+
{description && config.visualizationType !== 'Spark Line' && (
|
|
1413
|
+
<div className={getChartSubTextClasses().join('')}>{parse(description)}</div>
|
|
1163
1414
|
)}
|
|
1164
|
-
{
|
|
1165
|
-
|
|
1166
|
-
{
|
|
1415
|
+
{false && <Annotation.List />}
|
|
1416
|
+
|
|
1417
|
+
{/* buttons */}
|
|
1418
|
+
<MediaControls.Section classes={['download-buttons']}>
|
|
1419
|
+
{config.table.showDownloadImgButton && (
|
|
1420
|
+
<MediaControls.Button
|
|
1421
|
+
text='Download Image'
|
|
1422
|
+
title='Download Chart as Image'
|
|
1423
|
+
type='image'
|
|
1424
|
+
state={config}
|
|
1425
|
+
elementToCapture={imageId}
|
|
1426
|
+
/>
|
|
1427
|
+
)}
|
|
1428
|
+
{config.table.showDownloadPdfButton && (
|
|
1429
|
+
<MediaControls.Button
|
|
1430
|
+
text='Download PDF'
|
|
1431
|
+
title='Download Chart as PDF'
|
|
1432
|
+
type='pdf'
|
|
1433
|
+
state={config}
|
|
1434
|
+
elementToCapture={imageId}
|
|
1435
|
+
/>
|
|
1436
|
+
)}
|
|
1437
|
+
</MediaControls.Section>
|
|
1438
|
+
{/* Data Table */}
|
|
1439
|
+
{((config.xAxis.dataKey &&
|
|
1440
|
+
config.table.show &&
|
|
1441
|
+
config.visualizationType !== 'Spark Line' &&
|
|
1442
|
+
config.visualizationType !== 'Sankey') ||
|
|
1443
|
+
(config.visualizationType === 'Sankey' && config.table.show)) && (
|
|
1444
|
+
<DataTable
|
|
1445
|
+
config={config}
|
|
1446
|
+
rawData={
|
|
1447
|
+
config.visualizationType === 'Sankey'
|
|
1448
|
+
? config?.data?.[0]?.tableData
|
|
1449
|
+
: config.table.customTableConfig
|
|
1450
|
+
? filterVizData(config.filters, config.data)
|
|
1451
|
+
: config.data
|
|
1452
|
+
}
|
|
1453
|
+
runtimeData={
|
|
1454
|
+
config.visualizationType === 'Sankey'
|
|
1455
|
+
? config?.data?.[0]?.tableData
|
|
1456
|
+
: filteredData || excludedData
|
|
1457
|
+
}
|
|
1458
|
+
expandDataTable={config.table.expanded}
|
|
1459
|
+
columns={config.columns}
|
|
1460
|
+
displayDataAsText={displayDataAsText}
|
|
1461
|
+
displayGeoName={displayGeoName}
|
|
1462
|
+
applyLegendToRow={applyLegendToRow}
|
|
1463
|
+
tableTitle={config.table.label}
|
|
1464
|
+
indexTitle={config.table.indexLabel}
|
|
1465
|
+
vizTitle={title}
|
|
1466
|
+
viewport={currentViewport}
|
|
1467
|
+
tabbingId={handleChartTabbing(config, legendId)}
|
|
1468
|
+
colorScale={colorScale}
|
|
1469
|
+
/>
|
|
1470
|
+
)}
|
|
1471
|
+
{config?.annotations?.length > 0 && <Annotation.Dropdown />}
|
|
1472
|
+
{/* show pdf or image button */}
|
|
1167
1473
|
</div>
|
|
1168
|
-
{/* Link */}
|
|
1169
|
-
{isDashboard && config.table && config.table.show && config.table.showDataTableLink ? tableLink : link && link}
|
|
1170
|
-
{/* Description */}
|
|
1171
|
-
{description && config.visualizationType !== 'Spark Line' && <div className={'column ' + config.isResponsiveTicks ? 'subtext--responsive-ticks' : 'subtext'}>{parse(description)}</div>}
|
|
1172
|
-
{/* buttons */}
|
|
1173
|
-
<MediaControls.Section classes={['download-buttons']}>
|
|
1174
|
-
{config.table.showDownloadImgButton && <MediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={config} elementToCapture={imageId} />}
|
|
1175
|
-
{config.table.showDownloadPdfButton && <MediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={config} elementToCapture={imageId} />}
|
|
1176
|
-
</MediaControls.Section>
|
|
1177
|
-
{/* Data Table */}
|
|
1178
|
-
{((config.xAxis.dataKey && config.table.show && config.visualizationType !== 'Spark Line' && config.visualizationType !== 'Sankey') || (config.visualizationType === 'Sankey' && config.table.show)) && (
|
|
1179
|
-
<DataTable
|
|
1180
|
-
config={config}
|
|
1181
|
-
rawData={config.visualizationType === 'Sankey' ? config?.data?.[0]?.tableData : config.table.customTableConfig ? filterData(config.filters, config.data) : config.data}
|
|
1182
|
-
runtimeData={config.visualizationType === 'Sankey' ? config?.data?.[0]?.tableData : filteredData || excludedData}
|
|
1183
|
-
expandDataTable={config.table.expanded}
|
|
1184
|
-
columns={config.columns}
|
|
1185
|
-
displayDataAsText={displayDataAsText}
|
|
1186
|
-
displayGeoName={displayGeoName}
|
|
1187
|
-
applyLegendToRow={applyLegendToRow}
|
|
1188
|
-
tableTitle={config.table.label}
|
|
1189
|
-
indexTitle={config.table.indexLabel}
|
|
1190
|
-
vizTitle={title}
|
|
1191
|
-
viewport={currentViewport}
|
|
1192
|
-
tabbingId={handleChartTabbing}
|
|
1193
|
-
colorScale={colorScale}
|
|
1194
|
-
/>
|
|
1195
|
-
)}
|
|
1196
1474
|
{config?.footnotes && <section className='footnotes'>{parse(config.footnotes)}</section>}
|
|
1197
|
-
{/* show pdf or image button */}
|
|
1198
1475
|
</div>
|
|
1199
1476
|
)}
|
|
1200
1477
|
</Layout.Responsive>
|
|
@@ -1202,7 +1479,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1202
1479
|
)
|
|
1203
1480
|
}
|
|
1204
1481
|
|
|
1205
|
-
const getXAxisData = d =>
|
|
1482
|
+
const getXAxisData = d =>
|
|
1483
|
+
isDateScale(config.runtime.xAxis)
|
|
1484
|
+
? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime()
|
|
1485
|
+
: d[config.runtime.originalXAxis.dataKey]
|
|
1206
1486
|
const getYAxisData = (d, seriesKey) => d[seriesKey]
|
|
1207
1487
|
|
|
1208
1488
|
const capitalize = str => {
|
|
@@ -1211,59 +1491,69 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1211
1491
|
|
|
1212
1492
|
const contextValues = {
|
|
1213
1493
|
brushConfig,
|
|
1214
|
-
setBrushConfig,
|
|
1215
1494
|
capitalize,
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
config,
|
|
1219
|
-
setConfig,
|
|
1220
|
-
rawData: stateData ?? {},
|
|
1221
|
-
excludedData: excludedData,
|
|
1222
|
-
transformedData: clean(filteredData || excludedData), // do this right before passing to components
|
|
1223
|
-
tableData: filteredData || excludedData, // do not clean table data
|
|
1224
|
-
unfilteredData: stateData,
|
|
1225
|
-
seriesHighlight,
|
|
1495
|
+
clean,
|
|
1496
|
+
colorPalettes,
|
|
1226
1497
|
colorScale,
|
|
1227
|
-
|
|
1498
|
+
config,
|
|
1228
1499
|
currentViewport,
|
|
1229
|
-
|
|
1500
|
+
dashboardConfig,
|
|
1501
|
+
debugSvg: isDebug,
|
|
1502
|
+
dimensions,
|
|
1503
|
+
dynamicLegendItems,
|
|
1504
|
+
excludedData: excludedData,
|
|
1230
1505
|
formatDate,
|
|
1231
|
-
formatTooltipsDate,
|
|
1232
1506
|
formatNumber,
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
setParentConfig,
|
|
1238
|
-
missingRequiredSections,
|
|
1239
|
-
setEditing,
|
|
1240
|
-
setFilteredData,
|
|
1507
|
+
formatTooltipsDate,
|
|
1508
|
+
getTextWidth,
|
|
1509
|
+
getXAxisData,
|
|
1510
|
+
getYAxisData,
|
|
1241
1511
|
handleChartAriaLabels,
|
|
1512
|
+
handleLineType,
|
|
1242
1513
|
highlight,
|
|
1243
1514
|
highlightReset,
|
|
1244
|
-
legend,
|
|
1245
|
-
setSeriesHighlight,
|
|
1246
|
-
dynamicLegendItems,
|
|
1247
|
-
setDynamicLegendItems,
|
|
1248
|
-
filterData,
|
|
1249
1515
|
imageId,
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
isNumber,
|
|
1253
|
-
getTextWidth,
|
|
1254
|
-
twoColorPalette,
|
|
1255
|
-
isEditor,
|
|
1516
|
+
isDashboard,
|
|
1517
|
+
isLegendBottom: legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport),
|
|
1256
1518
|
isDebug,
|
|
1519
|
+
isDraggingAnnotation,
|
|
1520
|
+
handleDragStateChange,
|
|
1521
|
+
isEditor,
|
|
1522
|
+
isNumber,
|
|
1523
|
+
legend,
|
|
1524
|
+
lineOptions,
|
|
1525
|
+
loading,
|
|
1526
|
+
missingRequiredSections,
|
|
1527
|
+
outerContainerRef,
|
|
1528
|
+
parseDate,
|
|
1529
|
+
rawData: stateData ?? {},
|
|
1530
|
+
seriesHighlight,
|
|
1531
|
+
setBrushConfig,
|
|
1532
|
+
setConfig,
|
|
1533
|
+
setDynamicLegendItems,
|
|
1534
|
+
setEditing,
|
|
1535
|
+
setFilteredData,
|
|
1536
|
+
setParentConfig,
|
|
1537
|
+
setSeriesHighlight,
|
|
1257
1538
|
setSharedFilter,
|
|
1258
1539
|
setSharedFilterValue,
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1540
|
+
tableData: filteredData || excludedData, // do not clean table data
|
|
1541
|
+
transformedData: clean(filteredData || excludedData), // do this right before passing to components
|
|
1542
|
+
twoColorPalette,
|
|
1543
|
+
unfilteredData: stateData,
|
|
1544
|
+
updateConfig
|
|
1262
1545
|
}
|
|
1263
1546
|
|
|
1264
1547
|
return (
|
|
1265
1548
|
<ConfigContext.Provider value={contextValues}>
|
|
1266
|
-
<Layout.VisualizationWrapper
|
|
1549
|
+
<Layout.VisualizationWrapper
|
|
1550
|
+
config={config}
|
|
1551
|
+
isEditor={isEditor}
|
|
1552
|
+
currentViewport={currentViewport}
|
|
1553
|
+
ref={outerContainerRef}
|
|
1554
|
+
imageId={imageId}
|
|
1555
|
+
showEditorPanel={config?.showEditorPanel}
|
|
1556
|
+
>
|
|
1267
1557
|
{body}
|
|
1268
1558
|
</Layout.VisualizationWrapper>
|
|
1269
1559
|
</ConfigContext.Provider>
|