@cdc/chart 4.24.5 → 4.24.9-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.js +50526 -42181
- 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 +461 -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 +57 -8
- package/src/components/LineChart/helpers.ts +72 -14
- package/src/components/LineChart/index.tsx +117 -42
- 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/src/components/BrushHandle.jsx +0 -17
- package/src/components/LineChart/index.scss +0 -1
- package/src/components/LinearChart.jsx +0 -774
- 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.orientation === 'horizontal' ? config.yAxis.size : 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,67 @@ 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
|
+
(!newConfig.filters || newConfig.filters.filter(filter => filter.columnName === seriesKey).length === 0) &&
|
|
402
|
+
(!newConfig.columns || Object.keys(newConfig.columns).indexOf(seriesKey) === -1)
|
|
403
|
+
) {
|
|
404
|
+
newConfig.runtime.series.push({
|
|
405
|
+
dataKey: seriesKey,
|
|
406
|
+
type: newConfig.dynamicSeriesType,
|
|
407
|
+
lineType: newConfig.dynamicSeriesLineType,
|
|
408
|
+
tooltip: true
|
|
409
|
+
})
|
|
410
|
+
}
|
|
411
|
+
})
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
334
415
|
if (newConfig.visualizationType === 'Pie') {
|
|
335
416
|
newConfig.runtime.seriesKeys = (dataOverride || data).map(d => d[newConfig.xAxis.dataKey])
|
|
336
417
|
newConfig.runtime.seriesLabelsAll = newConfig.runtime.seriesKeys
|
|
337
418
|
} else {
|
|
338
|
-
newConfig.runtime.seriesKeys = newConfig.series
|
|
339
|
-
? newConfig.series.map(series => {
|
|
419
|
+
newConfig.runtime.seriesKeys = newConfig.runtime.series
|
|
420
|
+
? newConfig.runtime.series.map(series => {
|
|
340
421
|
newConfig.runtime.seriesLabels[series.dataKey] = series.name || series.label || series.dataKey
|
|
341
422
|
newConfig.runtime.seriesLabelsAll.push(series.name || series.dataKey)
|
|
342
423
|
return series.dataKey
|
|
@@ -345,8 +426,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
345
426
|
}
|
|
346
427
|
|
|
347
428
|
if (newConfig.visualizationType === 'Box Plot' && newConfig.series) {
|
|
348
|
-
let allKeys = newExcludedData
|
|
349
|
-
|
|
429
|
+
let allKeys = newExcludedData
|
|
430
|
+
? newExcludedData.map(d => d[newConfig.xAxis.dataKey])
|
|
431
|
+
: data.map(d => d[newConfig.xAxis.dataKey])
|
|
432
|
+
let allValues = newExcludedData
|
|
433
|
+
? newExcludedData.map(d => Number(d[newConfig?.series[0]?.dataKey]))
|
|
434
|
+
: data.map(d => Number(d[newConfig?.series[0]?.dataKey]))
|
|
350
435
|
|
|
351
436
|
const uniqueArray = function (arrArg) {
|
|
352
437
|
return arrArg.filter(function (elem, pos, arr) {
|
|
@@ -366,7 +451,9 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
366
451
|
if (!g) throw new Error('No groups resolved in box plots')
|
|
367
452
|
|
|
368
453
|
// filter data by group
|
|
369
|
-
let filteredData = newExcludedData
|
|
454
|
+
let filteredData = newExcludedData
|
|
455
|
+
? newExcludedData.filter(item => item[newConfig.xAxis.dataKey] === g)
|
|
456
|
+
: data.filter(item => item[newConfig.xAxis.dataKey] === g)
|
|
370
457
|
let filteredDataValues: number[] = filteredData.map(item => Number(item[newConfig?.series[0]?.dataKey]))
|
|
371
458
|
|
|
372
459
|
// Sort the data for upcoming functions.
|
|
@@ -454,7 +541,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
454
541
|
if (series.type === 'Bar' || series.type === 'Combo') {
|
|
455
542
|
newConfig.runtime.barSeriesKeys.push(series.dataKey)
|
|
456
543
|
}
|
|
457
|
-
if (
|
|
544
|
+
if (
|
|
545
|
+
series.type === 'Line' ||
|
|
546
|
+
series.type === 'dashed-sm' ||
|
|
547
|
+
series.type === 'dashed-md' ||
|
|
548
|
+
series.type === 'dashed-lg'
|
|
549
|
+
) {
|
|
458
550
|
newConfig.runtime.lineSeriesKeys.push(series.dataKey)
|
|
459
551
|
}
|
|
460
552
|
if (series.type === 'Combo') {
|
|
@@ -481,13 +573,21 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
481
573
|
})
|
|
482
574
|
}
|
|
483
575
|
|
|
484
|
-
if (
|
|
576
|
+
if (
|
|
577
|
+
(newConfig.visualizationType === 'Bar' && newConfig.orientation === 'horizontal') ||
|
|
578
|
+
['Deviation Bar', 'Paired Bar', 'Forest Plot'].includes(newConfig.visualizationType)
|
|
579
|
+
) {
|
|
485
580
|
newConfig.runtime.xAxis = newConfig.yAxis['yAxis'] ? newConfig.yAxis['yAxis'] : newConfig.yAxis
|
|
486
581
|
newConfig.runtime.yAxis = newConfig.xAxis['xAxis'] ? newConfig.xAxis['xAxis'] : newConfig.xAxis
|
|
487
582
|
|
|
488
583
|
newConfig.runtime.horizontal = false
|
|
489
584
|
newConfig.orientation = 'horizontal'
|
|
490
|
-
|
|
585
|
+
// remove after COVE supports categorical axis on horizonatal bars
|
|
586
|
+
newConfig.yAxis.type = newConfig.yAxis.type === 'categorical' ? 'linear' : newConfig.yAxis.type
|
|
587
|
+
} else if (
|
|
588
|
+
['Box Plot', 'Scatter Plot', 'Area Chart', 'Line', 'Forecasting'].includes(newConfig.visualizationType) &&
|
|
589
|
+
!checkLineToBarGraph()
|
|
590
|
+
) {
|
|
491
591
|
newConfig.runtime.xAxis = newConfig.xAxis
|
|
492
592
|
newConfig.runtime.yAxis = newConfig.yAxis
|
|
493
593
|
newConfig.runtime.horizontal = false
|
|
@@ -499,10 +599,16 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
499
599
|
}
|
|
500
600
|
|
|
501
601
|
newConfig.runtime.uniqueId = Date.now()
|
|
502
|
-
newConfig.runtime.editorErrorMessage =
|
|
602
|
+
newConfig.runtime.editorErrorMessage =
|
|
603
|
+
newConfig.visualizationType === 'Pie' && !newConfig.yAxis.dataKey
|
|
604
|
+
? 'Data Key property in Y Axis section must be set for pie charts.'
|
|
605
|
+
: ''
|
|
503
606
|
|
|
504
607
|
// Sankey Description box error message
|
|
505
|
-
newConfig.runtime.editorErrorMessage =
|
|
608
|
+
newConfig.runtime.editorErrorMessage =
|
|
609
|
+
newConfig.visualizationType === 'Sankey' && !newConfig.description
|
|
610
|
+
? 'SUBTEXT/CITATION field is empty: A description of the Sankey Diagram data must be inputted.'
|
|
611
|
+
: ''
|
|
506
612
|
|
|
507
613
|
if (newConfig.legend.seriesHighlight?.length) {
|
|
508
614
|
setSeriesHighlight(newConfig.legend?.seriesHighlight)
|
|
@@ -528,7 +634,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
528
634
|
|
|
529
635
|
// Sorts data series for horizontal bar charts
|
|
530
636
|
const sortData = (a, b) => {
|
|
531
|
-
let sortKey =
|
|
637
|
+
let sortKey =
|
|
638
|
+
config.visualizationType === 'Bar' && config.visualizationSubType === 'horizontal'
|
|
639
|
+
? config.xAxis.dataKey
|
|
640
|
+
: config.yAxis.sortKey
|
|
532
641
|
let aData = parseFloat(a[sortKey])
|
|
533
642
|
let bData = parseFloat(b[sortKey])
|
|
534
643
|
|
|
@@ -545,15 +654,14 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
545
654
|
const resizeObserver = new ResizeObserver(entries => {
|
|
546
655
|
for (let entry of entries) {
|
|
547
656
|
let { width, height } = entry.contentRect
|
|
548
|
-
let newViewport = getViewport(width)
|
|
549
657
|
let svgMarginWidth = 32
|
|
550
658
|
let editorWidth = 350
|
|
551
659
|
|
|
552
|
-
|
|
660
|
+
width = isEditor ? width - editorWidth : width
|
|
553
661
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
662
|
+
let newViewport = getViewport(width)
|
|
663
|
+
|
|
664
|
+
setCurrentViewport(newViewport)
|
|
557
665
|
|
|
558
666
|
if (entry.target.dataset.lollipop === 'true') {
|
|
559
667
|
width = width - 2.5
|
|
@@ -628,14 +736,19 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
628
736
|
let configCopy = { ...config }
|
|
629
737
|
delete configCopy['filters']
|
|
630
738
|
setConfig(configCopy)
|
|
631
|
-
setFilteredData(
|
|
739
|
+
setFilteredData(filterVizData(externalFilters, excludedData))
|
|
632
740
|
}
|
|
633
741
|
}
|
|
634
742
|
|
|
635
|
-
if (
|
|
743
|
+
if (
|
|
744
|
+
externalFilters &&
|
|
745
|
+
externalFilters.length > 0 &&
|
|
746
|
+
externalFilters.length > 0 &&
|
|
747
|
+
externalFilters[0].hasOwnProperty('active')
|
|
748
|
+
) {
|
|
636
749
|
let newConfigHere = { ...config, filters: externalFilters }
|
|
637
750
|
setConfig(newConfigHere)
|
|
638
|
-
setFilteredData(
|
|
751
|
+
setFilteredData(filterVizData(externalFilters, excludedData))
|
|
639
752
|
}
|
|
640
753
|
}, [externalFilters]) // eslint-disable-line
|
|
641
754
|
|
|
@@ -647,10 +760,25 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
647
760
|
}, [configObj.data]) // eslint-disable-line
|
|
648
761
|
}
|
|
649
762
|
|
|
763
|
+
// This will set the bump chart's default scaling type to date-time
|
|
764
|
+
useEffect(() => {
|
|
765
|
+
if (['Bump Chart'].includes(config.visualizationType)) {
|
|
766
|
+
setConfig({
|
|
767
|
+
...config,
|
|
768
|
+
xAxis: {
|
|
769
|
+
...config.xAxis,
|
|
770
|
+
type: 'date-time'
|
|
771
|
+
}
|
|
772
|
+
})
|
|
773
|
+
}
|
|
774
|
+
}, [config.visualizationType])
|
|
775
|
+
|
|
650
776
|
// Generates color palette to pass to child chart component
|
|
651
777
|
useEffect(() => {
|
|
652
778
|
if (stateData && config.xAxis && config.runtime?.seriesKeys) {
|
|
653
|
-
const configPalette = ['Paired Bar', 'Deviation Bar'].includes(config.visualizationType)
|
|
779
|
+
const configPalette = ['Paired Bar', 'Deviation Bar'].includes(config.visualizationType)
|
|
780
|
+
? config.twoColor.palette
|
|
781
|
+
: config.palette
|
|
654
782
|
const allPalettes: Record<string, string[]> = { ...colorPalettes, ...twoColorPalette }
|
|
655
783
|
let palette = config.customColors || allPalettes[configPalette]
|
|
656
784
|
let numberOfKeys = config.runtime.seriesKeys.length
|
|
@@ -754,19 +882,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
754
882
|
return timeFormat(config.tooltips.dateDisplayFormat)(date)
|
|
755
883
|
}
|
|
756
884
|
|
|
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
885
|
// Format numeric data based on settings in config OR from passed in settings for Additional Columns
|
|
771
886
|
// - use only for old horizontal data - newer formatNumber is in helper/formatNumber
|
|
772
887
|
// TODO: we should combine various formatNumber functions across this project.
|
|
@@ -785,7 +900,20 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
785
900
|
|
|
786
901
|
// destructure dataFormat values
|
|
787
902
|
let {
|
|
788
|
-
dataFormat: {
|
|
903
|
+
dataFormat: {
|
|
904
|
+
commas,
|
|
905
|
+
abbreviated,
|
|
906
|
+
roundTo,
|
|
907
|
+
prefix,
|
|
908
|
+
suffix,
|
|
909
|
+
rightRoundTo,
|
|
910
|
+
bottomRoundTo,
|
|
911
|
+
rightPrefix,
|
|
912
|
+
rightSuffix,
|
|
913
|
+
bottomPrefix,
|
|
914
|
+
bottomSuffix,
|
|
915
|
+
bottomAbbreviated
|
|
916
|
+
}
|
|
789
917
|
} = config
|
|
790
918
|
|
|
791
919
|
// check if value contains comma and remove it. later will add comma below.
|
|
@@ -856,7 +984,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
856
984
|
// Edge case for small numbers with decimals
|
|
857
985
|
// - if roundTo undefined which means it is blank, then do not round
|
|
858
986
|
|
|
859
|
-
if (
|
|
987
|
+
if (
|
|
988
|
+
(axis === 'left' && commas && abbreviated && shouldAbbreviate) ||
|
|
989
|
+
(axis === 'bottom' && commas && abbreviated && shouldAbbreviate)
|
|
990
|
+
) {
|
|
860
991
|
num = num // eslint-disable-line
|
|
861
992
|
} else {
|
|
862
993
|
num = num.toLocaleString('en-US', stringFormattingOptions)
|
|
@@ -912,21 +1043,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
912
1043
|
return String(result)
|
|
913
1044
|
}
|
|
914
1045
|
|
|
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
1046
|
const missingRequiredSections = () => {
|
|
931
1047
|
if (config.visualizationType === 'Sankey') return false // skip checks for now
|
|
932
1048
|
if (config.visualizationType === 'Forecasting') return false // skip required checks for now.
|
|
@@ -936,7 +1052,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
936
1052
|
return true
|
|
937
1053
|
}
|
|
938
1054
|
} else {
|
|
939
|
-
if (undefined === config?.series || false === config?.series.length > 0) {
|
|
1055
|
+
if ((undefined === config?.series || false === config?.series.length > 0) && !config?.dynamicSeries) {
|
|
940
1056
|
return true
|
|
941
1057
|
}
|
|
942
1058
|
}
|
|
@@ -1043,7 +1159,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1043
1159
|
<section className='waiting-container'>
|
|
1044
1160
|
<h3>Finish Configuring</h3>
|
|
1045
1161
|
<p>Set all required options to the left and confirm below to display a preview of the chart.</p>
|
|
1046
|
-
<Button
|
|
1162
|
+
<Button
|
|
1163
|
+
className='btn'
|
|
1164
|
+
style={{ margin: '1em auto' }}
|
|
1165
|
+
disabled={missingRequiredSections()}
|
|
1166
|
+
onClick={e => confirmDone(e)}
|
|
1167
|
+
>
|
|
1047
1168
|
I'm Done
|
|
1048
1169
|
</Button>
|
|
1049
1170
|
</section>
|
|
@@ -1107,17 +1228,40 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1107
1228
|
// Prevent render if loading
|
|
1108
1229
|
let body = <Loading />
|
|
1109
1230
|
|
|
1231
|
+
const makeClassName = string => {
|
|
1232
|
+
if (!string || !string.toLowerCase) return
|
|
1233
|
+
return string.toLowerCase().replaceAll(/ /g, '-')
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1110
1236
|
const getChartWrapperClasses = () => {
|
|
1237
|
+
const isLegendOnBottom = legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
|
|
1111
1238
|
const classes = ['chart-container', 'p-relative']
|
|
1112
|
-
if (
|
|
1113
|
-
|
|
1239
|
+
if (legend?.position) {
|
|
1240
|
+
if (['sm', 'xs', 'xxs'].includes(currentViewport) && legend?.position !== 'top') {
|
|
1241
|
+
classes.push('legend-bottom')
|
|
1242
|
+
} else {
|
|
1243
|
+
classes.push(`legend-${legend.position}`)
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
if (legend?.hide) classes.push('legend-hidden')
|
|
1114
1247
|
if (lineDatapointClass) classes.push(lineDatapointClass)
|
|
1115
1248
|
if (!config.barHasBorder) classes.push('chart-bar--no-border')
|
|
1116
|
-
if (
|
|
1249
|
+
if (config.brush?.active && dashboardConfig?.type === 'dashboard' && (!isLegendOnBottom || legend.hide))
|
|
1250
|
+
classes.push('dashboard-brush')
|
|
1117
1251
|
classes.push(...contentClasses)
|
|
1118
1252
|
return classes
|
|
1119
1253
|
}
|
|
1120
1254
|
|
|
1255
|
+
const getChartSubTextClasses = () => {
|
|
1256
|
+
const classes = ['subtext ']
|
|
1257
|
+
const isLegendOnBottom = legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
|
|
1258
|
+
|
|
1259
|
+
if (config.isResponsiveTicks) classes.push('subtext--responsive-ticks ')
|
|
1260
|
+
if (config.brush?.active && !isLegendOnBottom) classes.push('subtext--brush-active ')
|
|
1261
|
+
if (config.brush?.active && config.legend.hide) classes.push('subtext--brush-active ')
|
|
1262
|
+
return classes
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1121
1265
|
if (!loading) {
|
|
1122
1266
|
const tableLink = (
|
|
1123
1267
|
<a href={`#data-table-${config.dataKey}`} className='margin-left-href'>
|
|
@@ -1131,70 +1275,202 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1131
1275
|
{config.newViz && <Confirm />}
|
|
1132
1276
|
{undefined === config.newViz && isEditor && config.runtime && config.runtime?.editorErrorMessage && <Error />}
|
|
1133
1277
|
{!missingRequiredSections() && !config.newViz && (
|
|
1134
|
-
<div
|
|
1135
|
-
|
|
1278
|
+
<div
|
|
1279
|
+
className={`cdc-chart-inner-container cove-component__content type-${makeClassName(
|
|
1280
|
+
config.visualizationType
|
|
1281
|
+
)}`}
|
|
1282
|
+
aria-label={handleChartAriaLabels(config)}
|
|
1283
|
+
tabIndex={0}
|
|
1284
|
+
>
|
|
1285
|
+
<Title
|
|
1286
|
+
showTitle={config.showTitle}
|
|
1287
|
+
isDashboard={isDashboard}
|
|
1288
|
+
title={title}
|
|
1289
|
+
superTitle={config.superTitle}
|
|
1290
|
+
classes={['chart-title', `${config.theme}`, 'cove-component__header']}
|
|
1291
|
+
style={undefined}
|
|
1292
|
+
/>
|
|
1293
|
+
{/* Intro Text/Message */}
|
|
1294
|
+
{config?.introText && config.visualizationType !== 'Spark Line' && (
|
|
1295
|
+
<section
|
|
1296
|
+
className={`introText legend_${config.legend.hide ? 'hidden' : 'visible'}_${config.legend.position} `}
|
|
1297
|
+
>
|
|
1298
|
+
{parse(config.introText)}
|
|
1299
|
+
</section>
|
|
1300
|
+
)}
|
|
1136
1301
|
|
|
1137
1302
|
{/* Filters */}
|
|
1138
|
-
{config.filters && !externalFilters && config.visualizationType !== 'Spark Line' &&
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1303
|
+
{config.filters && !externalFilters && config.visualizationType !== 'Spark Line' && (
|
|
1304
|
+
<Filters
|
|
1305
|
+
config={config}
|
|
1306
|
+
setConfig={setConfig}
|
|
1307
|
+
setFilteredData={setFilteredData}
|
|
1308
|
+
filteredData={filteredData}
|
|
1309
|
+
excludedData={excludedData}
|
|
1310
|
+
filterData={filterVizData}
|
|
1311
|
+
dimensions={dimensions}
|
|
1312
|
+
/>
|
|
1313
|
+
)}
|
|
1314
|
+
<SkipTo skipId={handleChartTabbing(config, legendId)} skipMessage='Skip Over Chart Container' />
|
|
1315
|
+
{config.annotations?.length > 0 && (
|
|
1316
|
+
<SkipTo
|
|
1317
|
+
skipId={handleChartTabbing(config, legendId)}
|
|
1318
|
+
skipMessage={`Skip over annotations`}
|
|
1319
|
+
key={`skip-annotations`}
|
|
1320
|
+
/>
|
|
1321
|
+
)}
|
|
1322
|
+
|
|
1323
|
+
{/* Visualization Wrapper */}
|
|
1142
1324
|
<div className={getChartWrapperClasses().join(' ')}>
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
<
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
{parse(description)}
|
|
1325
|
+
<LegendWrapper>
|
|
1326
|
+
<div
|
|
1327
|
+
className={
|
|
1328
|
+
legend.hide || ['xxs', 'xs', 'sm'].includes(currentViewport)
|
|
1329
|
+
? 'w-100'
|
|
1330
|
+
: legend.position === 'bottom' || legend.position === 'top' || visualizationType === 'Sankey'
|
|
1331
|
+
? 'w-100'
|
|
1332
|
+
: 'w-75'
|
|
1333
|
+
}
|
|
1334
|
+
>
|
|
1335
|
+
{/* All charts with LinearChart */}
|
|
1336
|
+
{!['Spark Line', 'Line', 'Sankey', 'Pie', 'Sankey'].includes(config.visualizationType) && (
|
|
1337
|
+
<div style={{ height, width: `100%` }}>
|
|
1338
|
+
<ParentSize>
|
|
1339
|
+
{parent => <LinearChart parentWidth={parent.width} parentHeight={parent.height} />}
|
|
1340
|
+
</ParentSize>
|
|
1160
1341
|
</div>
|
|
1161
1342
|
)}
|
|
1162
|
-
|
|
1343
|
+
|
|
1344
|
+
{config.visualizationType === 'Pie' && (
|
|
1345
|
+
<ParentSize className='justify-content-center d-flex' style={{ height, width: `100%` }}>
|
|
1346
|
+
{parent => <PieChart parentWidth={parent.width} parentHeight={parent.height} />}
|
|
1347
|
+
</ParentSize>
|
|
1348
|
+
)}
|
|
1349
|
+
{/* Line Chart */}
|
|
1350
|
+
{config.visualizationType === 'Line' &&
|
|
1351
|
+
(checkLineToBarGraph() ? (
|
|
1352
|
+
<div style={{ height: config?.heights?.vertical, width: `100%` }}>
|
|
1353
|
+
<ParentSize>
|
|
1354
|
+
{parent => <LinearChart parentWidth={parent.width} parentHeight={parent.height} />}
|
|
1355
|
+
</ParentSize>
|
|
1356
|
+
</div>
|
|
1357
|
+
) : (
|
|
1358
|
+
<div style={{ height, width: `100%` }}>
|
|
1359
|
+
<ParentSize>
|
|
1360
|
+
{parent => <LinearChart parentWidth={parent.width} parentHeight={parent.height} />}
|
|
1361
|
+
</ParentSize>
|
|
1362
|
+
</div>
|
|
1363
|
+
))}
|
|
1364
|
+
{/* Sparkline */}
|
|
1365
|
+
{config.visualizationType === 'Spark Line' && (
|
|
1366
|
+
<>
|
|
1367
|
+
<Filters
|
|
1368
|
+
config={config}
|
|
1369
|
+
setConfig={setConfig}
|
|
1370
|
+
setFilteredData={setFilteredData}
|
|
1371
|
+
filteredData={filteredData}
|
|
1372
|
+
excludedData={excludedData}
|
|
1373
|
+
filterData={filterVizData}
|
|
1374
|
+
dimensions={dimensions}
|
|
1375
|
+
/>
|
|
1376
|
+
{config?.introText && (
|
|
1377
|
+
<section className='introText' style={{ padding: '0px 0 35px' }}>
|
|
1378
|
+
{parse(config.introText)}
|
|
1379
|
+
</section>
|
|
1380
|
+
)}
|
|
1381
|
+
<div style={{ height: `100px`, width: `100%`, ...sparkLineStyles }}>
|
|
1382
|
+
<ParentSize>{parent => <SparkLine width={parent.width} height={parent.height} />}</ParentSize>
|
|
1383
|
+
</div>
|
|
1384
|
+
{description && (
|
|
1385
|
+
<div className='subtext' style={{ padding: '35px 0 15px' }}>
|
|
1386
|
+
{parse(description)}
|
|
1387
|
+
</div>
|
|
1388
|
+
)}
|
|
1389
|
+
</>
|
|
1390
|
+
)}
|
|
1391
|
+
{/* Sankey */}
|
|
1392
|
+
{config.visualizationType === 'Sankey' && (
|
|
1393
|
+
<ParentSize aria-hidden='true'>
|
|
1394
|
+
{parent => <SankeyChart runtime={config.runtime} width={parent.width} height={parent.height} />}
|
|
1395
|
+
</ParentSize>
|
|
1396
|
+
)}
|
|
1397
|
+
</div>
|
|
1398
|
+
{/* Legend */}
|
|
1399
|
+
{!config.legend.hide &&
|
|
1400
|
+
config.visualizationType !== 'Spark Line' &&
|
|
1401
|
+
config.visualizationType !== 'Sankey' && (
|
|
1402
|
+
<Legend ref={legendRef} skipId={handleChartTabbing(config, legendId)} />
|
|
1403
|
+
)}
|
|
1404
|
+
</LegendWrapper>
|
|
1405
|
+
{/* Link */}
|
|
1406
|
+
{isDashboard && config.table && config.table.show && config.table.showDataTableLink
|
|
1407
|
+
? tableLink
|
|
1408
|
+
: link && link}
|
|
1409
|
+
{/* Description */}
|
|
1410
|
+
|
|
1411
|
+
{description && config.visualizationType !== 'Spark Line' && (
|
|
1412
|
+
<div className={getChartSubTextClasses().join('')}>{parse(description)}</div>
|
|
1163
1413
|
)}
|
|
1164
|
-
{
|
|
1165
|
-
|
|
1166
|
-
{
|
|
1414
|
+
{false && <Annotation.List />}
|
|
1415
|
+
|
|
1416
|
+
{/* buttons */}
|
|
1417
|
+
<MediaControls.Section classes={['download-buttons']}>
|
|
1418
|
+
{config.table.showDownloadImgButton && (
|
|
1419
|
+
<MediaControls.Button
|
|
1420
|
+
text='Download Image'
|
|
1421
|
+
title='Download Chart as Image'
|
|
1422
|
+
type='image'
|
|
1423
|
+
state={config}
|
|
1424
|
+
elementToCapture={imageId}
|
|
1425
|
+
/>
|
|
1426
|
+
)}
|
|
1427
|
+
{config.table.showDownloadPdfButton && (
|
|
1428
|
+
<MediaControls.Button
|
|
1429
|
+
text='Download PDF'
|
|
1430
|
+
title='Download Chart as PDF'
|
|
1431
|
+
type='pdf'
|
|
1432
|
+
state={config}
|
|
1433
|
+
elementToCapture={imageId}
|
|
1434
|
+
/>
|
|
1435
|
+
)}
|
|
1436
|
+
</MediaControls.Section>
|
|
1437
|
+
{/* Data Table */}
|
|
1438
|
+
{((config.xAxis.dataKey &&
|
|
1439
|
+
config.table.show &&
|
|
1440
|
+
config.visualizationType !== 'Spark Line' &&
|
|
1441
|
+
config.visualizationType !== 'Sankey') ||
|
|
1442
|
+
(config.visualizationType === 'Sankey' && config.table.show)) && (
|
|
1443
|
+
<DataTable
|
|
1444
|
+
config={config}
|
|
1445
|
+
rawData={
|
|
1446
|
+
config.visualizationType === 'Sankey'
|
|
1447
|
+
? config?.data?.[0]?.tableData
|
|
1448
|
+
: config.table.customTableConfig
|
|
1449
|
+
? filterVizData(config.filters, config.data)
|
|
1450
|
+
: config.data
|
|
1451
|
+
}
|
|
1452
|
+
runtimeData={
|
|
1453
|
+
config.visualizationType === 'Sankey'
|
|
1454
|
+
? config?.data?.[0]?.tableData
|
|
1455
|
+
: filteredData || excludedData
|
|
1456
|
+
}
|
|
1457
|
+
expandDataTable={config.table.expanded}
|
|
1458
|
+
columns={config.columns}
|
|
1459
|
+
displayDataAsText={displayDataAsText}
|
|
1460
|
+
displayGeoName={displayGeoName}
|
|
1461
|
+
applyLegendToRow={applyLegendToRow}
|
|
1462
|
+
tableTitle={config.table.label}
|
|
1463
|
+
indexTitle={config.table.indexLabel}
|
|
1464
|
+
vizTitle={title}
|
|
1465
|
+
viewport={currentViewport}
|
|
1466
|
+
tabbingId={handleChartTabbing(config, legendId)}
|
|
1467
|
+
colorScale={colorScale}
|
|
1468
|
+
/>
|
|
1469
|
+
)}
|
|
1470
|
+
{config?.annotations?.length > 0 && <Annotation.Dropdown />}
|
|
1471
|
+
{/* show pdf or image button */}
|
|
1167
1472
|
</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
1473
|
{config?.footnotes && <section className='footnotes'>{parse(config.footnotes)}</section>}
|
|
1197
|
-
{/* show pdf or image button */}
|
|
1198
1474
|
</div>
|
|
1199
1475
|
)}
|
|
1200
1476
|
</Layout.Responsive>
|
|
@@ -1202,7 +1478,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1202
1478
|
)
|
|
1203
1479
|
}
|
|
1204
1480
|
|
|
1205
|
-
const getXAxisData = d =>
|
|
1481
|
+
const getXAxisData = d =>
|
|
1482
|
+
isDateScale(config.runtime.xAxis)
|
|
1483
|
+
? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime()
|
|
1484
|
+
: d[config.runtime.originalXAxis.dataKey]
|
|
1206
1485
|
const getYAxisData = (d, seriesKey) => d[seriesKey]
|
|
1207
1486
|
|
|
1208
1487
|
const capitalize = str => {
|
|
@@ -1211,59 +1490,69 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
|
|
|
1211
1490
|
|
|
1212
1491
|
const contextValues = {
|
|
1213
1492
|
brushConfig,
|
|
1214
|
-
setBrushConfig,
|
|
1215
1493
|
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,
|
|
1494
|
+
clean,
|
|
1495
|
+
colorPalettes,
|
|
1226
1496
|
colorScale,
|
|
1227
|
-
|
|
1497
|
+
config,
|
|
1228
1498
|
currentViewport,
|
|
1229
|
-
|
|
1499
|
+
dashboardConfig,
|
|
1500
|
+
debugSvg: isDebug,
|
|
1501
|
+
dimensions,
|
|
1502
|
+
dynamicLegendItems,
|
|
1503
|
+
excludedData: excludedData,
|
|
1230
1504
|
formatDate,
|
|
1231
|
-
formatTooltipsDate,
|
|
1232
1505
|
formatNumber,
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
setParentConfig,
|
|
1238
|
-
missingRequiredSections,
|
|
1239
|
-
setEditing,
|
|
1240
|
-
setFilteredData,
|
|
1506
|
+
formatTooltipsDate,
|
|
1507
|
+
getTextWidth,
|
|
1508
|
+
getXAxisData,
|
|
1509
|
+
getYAxisData,
|
|
1241
1510
|
handleChartAriaLabels,
|
|
1511
|
+
handleLineType,
|
|
1242
1512
|
highlight,
|
|
1243
1513
|
highlightReset,
|
|
1244
|
-
legend,
|
|
1245
|
-
setSeriesHighlight,
|
|
1246
|
-
dynamicLegendItems,
|
|
1247
|
-
setDynamicLegendItems,
|
|
1248
|
-
filterData,
|
|
1249
1514
|
imageId,
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
isNumber,
|
|
1253
|
-
getTextWidth,
|
|
1254
|
-
twoColorPalette,
|
|
1255
|
-
isEditor,
|
|
1515
|
+
isDashboard,
|
|
1516
|
+
isLegendBottom: legend?.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport),
|
|
1256
1517
|
isDebug,
|
|
1518
|
+
isDraggingAnnotation,
|
|
1519
|
+
handleDragStateChange,
|
|
1520
|
+
isEditor,
|
|
1521
|
+
isNumber,
|
|
1522
|
+
legend,
|
|
1523
|
+
lineOptions,
|
|
1524
|
+
loading,
|
|
1525
|
+
missingRequiredSections,
|
|
1526
|
+
outerContainerRef,
|
|
1527
|
+
parseDate,
|
|
1528
|
+
rawData: stateData ?? {},
|
|
1529
|
+
seriesHighlight,
|
|
1530
|
+
setBrushConfig,
|
|
1531
|
+
setConfig,
|
|
1532
|
+
setDynamicLegendItems,
|
|
1533
|
+
setEditing,
|
|
1534
|
+
setFilteredData,
|
|
1535
|
+
setParentConfig,
|
|
1536
|
+
setSeriesHighlight,
|
|
1257
1537
|
setSharedFilter,
|
|
1258
1538
|
setSharedFilterValue,
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1539
|
+
tableData: filteredData || excludedData, // do not clean table data
|
|
1540
|
+
transformedData: clean(filteredData || excludedData), // do this right before passing to components
|
|
1541
|
+
twoColorPalette,
|
|
1542
|
+
unfilteredData: stateData,
|
|
1543
|
+
updateConfig
|
|
1262
1544
|
}
|
|
1263
1545
|
|
|
1264
1546
|
return (
|
|
1265
1547
|
<ConfigContext.Provider value={contextValues}>
|
|
1266
|
-
<Layout.VisualizationWrapper
|
|
1548
|
+
<Layout.VisualizationWrapper
|
|
1549
|
+
config={config}
|
|
1550
|
+
isEditor={isEditor}
|
|
1551
|
+
currentViewport={currentViewport}
|
|
1552
|
+
ref={outerContainerRef}
|
|
1553
|
+
imageId={imageId}
|
|
1554
|
+
showEditorPanel={config?.showEditorPanel}
|
|
1555
|
+
>
|
|
1267
1556
|
{body}
|
|
1268
1557
|
</Layout.VisualizationWrapper>
|
|
1269
1558
|
</ConfigContext.Provider>
|