@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.
Files changed (87) hide show
  1. package/dist/cdcchart.js +44197 -38258
  2. package/examples/cases-year.json +13379 -0
  3. package/examples/feature/annotations/index.json +542 -0
  4. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +76 -15
  5. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +5 -5
  6. package/examples/xaxis.json +493 -0
  7. package/index.html +20 -10
  8. package/package.json +5 -4
  9. package/src/CdcChart.tsx +462 -172
  10. package/src/_stories/Chart.Legend.Gradient.tsx +19 -0
  11. package/src/_stories/Chart.stories.tsx +18 -171
  12. package/src/_stories/ChartAnnotation.stories.tsx +32 -0
  13. package/src/_stories/_mock/annotation_category_mock.json +473 -0
  14. package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
  15. package/{examples/feature/line/line-chart.json → src/_stories/_mock/annotation_date-time_mock.json} +150 -69
  16. package/src/_stories/_mock/legend.gradient_mock.json +236 -0
  17. package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
  18. package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
  19. package/src/_stories/_mock/lollipop.json +171 -0
  20. package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
  21. package/src/components/Annotations/components/AnnotationDraggable.tsx +207 -0
  22. package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
  23. package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
  24. package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
  25. package/src/components/Annotations/components/AnnotationList.tsx +42 -0
  26. package/src/components/Annotations/components/findNearestDatum.ts +138 -0
  27. package/src/components/Annotations/components/helpers/index.tsx +46 -0
  28. package/src/components/Annotations/index.tsx +13 -0
  29. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
  30. package/src/components/AreaChart/components/AreaChart.jsx +1 -1
  31. package/src/components/Axis/Categorical.Axis.tsx +145 -0
  32. package/src/components/BarChart/components/BarChart.Horizontal.tsx +47 -44
  33. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +0 -1
  34. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -14
  35. package/src/components/BarChart/components/BarChart.Vertical.tsx +67 -30
  36. package/src/components/BarChart/helpers/index.ts +91 -0
  37. package/src/components/BrushChart.tsx +205 -0
  38. package/src/components/EditorPanel/EditorPanel.tsx +1794 -403
  39. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +320 -0
  40. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +282 -18
  41. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +43 -8
  42. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -4
  43. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +4 -13
  44. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  45. package/src/components/EditorPanel/components/panels.scss +4 -0
  46. package/src/components/EditorPanel/editor-panel.scss +35 -3
  47. package/src/components/EditorPanel/{useEditorPermissions.js → useEditorPermissions.ts} +105 -17
  48. package/src/components/Legend/Legend.Component.tsx +185 -194
  49. package/src/components/Legend/Legend.Suppression.tsx +146 -0
  50. package/src/components/Legend/Legend.tsx +21 -5
  51. package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
  52. package/src/components/Legend/helpers/index.ts +35 -0
  53. package/src/components/LegendWrapper.tsx +26 -0
  54. package/src/components/LineChart/LineChartProps.ts +1 -15
  55. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +103 -0
  56. package/src/components/LineChart/components/LineChart.Circle.tsx +47 -8
  57. package/src/components/LineChart/helpers.ts +72 -14
  58. package/src/components/LineChart/index.tsx +117 -42
  59. package/src/components/LinearChart.jsx +179 -136
  60. package/src/components/LinearChart.tsx +1366 -0
  61. package/src/components/PairedBarChart.jsx +9 -9
  62. package/src/components/PieChart/PieChart.tsx +75 -18
  63. package/src/components/Sankey/index.tsx +89 -30
  64. package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
  65. package/src/components/Sparkline/components/SparkLine.tsx +2 -2
  66. package/src/components/ZoomBrush.tsx +90 -44
  67. package/src/data/initial-state.js +25 -7
  68. package/src/helpers/handleChartTabbing.ts +8 -0
  69. package/src/helpers/isConvertLineToBarGraph.ts +4 -0
  70. package/src/hooks/{useBarChart.js → useBarChart.ts} +2 -40
  71. package/src/hooks/useColorScale.ts +1 -1
  72. package/src/hooks/useLegendClasses.ts +68 -0
  73. package/src/hooks/useMinMax.ts +12 -7
  74. package/src/hooks/useScales.ts +58 -26
  75. package/src/hooks/useTooltip.tsx +135 -25
  76. package/src/scss/DataTable.scss +2 -1
  77. package/src/scss/main.scss +128 -28
  78. package/src/types/ChartConfig.ts +83 -10
  79. package/src/types/ChartContext.ts +14 -4
  80. package/tests-examples/helpers/testZeroValue.test.ts +30 -0
  81. package/LICENSE +0 -201
  82. package/src/components/BrushHandle.jsx +0 -17
  83. package/src/components/LineChart/index.scss +0 -1
  84. package/src/helpers/filterData.ts +0 -18
  85. package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
  86. package/src/hooks/useLegendClasses.js +0 -31
  87. /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
- export default function CdcChart({ configUrl, config: configObj, isEditor = false, isDebug = false, isDashboard = false, setConfig: setParentConfig, setEditing, hostname, link, setSharedFilter, setSharedFilterValue, dashboardConfig }) {
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[]>(configObj && configObj?.legend?.seriesHighlight?.length ? [...configObj?.legend?.seriesHighlight] : [])
73
- const [currentViewport, setCurrentViewport] = useState('lg')
74
- const [dimensions, setDimensions] = useState<[number?, number?]>([])
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
- const handleChartTabbing = !config.legend?.hide ? legendId : config?.title ? `dataTableSection__${config.title.replace(/\s/g, '')}` : `dataTableSection`
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(filterData(config.filters, data))
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 ? (response.filters.filter(filter => filter.type === 'url').length > 0 ? true : false) : false
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 (!newConfig.data && data) {
300
+ if (data) {
255
301
  newConfig.data = data
256
302
  }
257
303
 
258
- const processedConfig = { ...(await coveUpdateWorker(newConfig)) }
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 (isDateScale(newConfig.xAxis) && (newConfig.exclusions.dateStart || newConfig.exclusions.dateEnd) && newConfig.xAxis.dateParseFormat) {
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(e => timestamp(e[newConfig.xAxis.dataKey]) >= startDate && timestamp(e[newConfig.xAxis.dataKey]) <= endDate)
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
- let filterValues = []
309
-
310
- filterValues = filter.orderedValues || generateValuesForFilter(filter.columnName, newExcludedData).sort(filter.order === 'desc' ? sortDesc : sortAsc)
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
- newConfig.filters[index].active = newConfig.filters[index].active || filterValues[0]
316
- newConfig.filters[index].filterStyle = newConfig.filters[index].filterStyle ? newConfig.filters[index].filterStyle : 'dropdown'
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 = filterData(newConfig.filters, newExcludedData)
380
+ currentData = filterVizData(newConfig.filters, newExcludedData)
319
381
  setFilteredData(currentData)
320
382
  }
321
383
 
322
- if (newConfig.xAxis.type === 'date-time' && newConfig.barThickness > 0.1) {
323
- newConfig.barThickness = 0.035
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 ? newExcludedData.map(d => d[newConfig.xAxis.dataKey]) : data.map(d => d[newConfig.xAxis.dataKey])
349
- let allValues = newExcludedData ? newExcludedData.map(d => Number(d[newConfig?.series[0]?.dataKey])) : data.map(d => Number(d[newConfig?.series[0]?.dataKey]))
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 ? newExcludedData.filter(item => item[newConfig.xAxis.dataKey] === g) : data.filter(item => item[newConfig.xAxis.dataKey] === g)
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 (series.type === 'Line' || series.type === 'dashed-sm' || series.type === 'dashed-md' || series.type === 'dashed-lg') {
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 ((newConfig.visualizationType === 'Bar' && newConfig.orientation === 'horizontal') || ['Deviation Bar', 'Paired Bar', 'Forest Plot'].includes(newConfig.visualizationType)) {
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
- } else if (['Box Plot', 'Scatter Plot', 'Area Chart', 'Line', 'Forecasting'].includes(newConfig.visualizationType)) {
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 = newConfig.visualizationType === 'Pie' && !newConfig.yAxis.dataKey ? 'Data Key property in Y Axis section must be set for pie charts.' : ''
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 = newConfig.visualizationType === 'Sankey' && !newConfig.description ? 'SUBTEXT/CITATION field is empty: A description of the Sankey Diagram data must be inputted.' : ''
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 = config.visualizationType === 'Bar' && config.visualizationSubType === 'horizontal' ? config.xAxis.dataKey : config.yAxis.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
- setCurrentViewport(newViewport)
661
+ width = isEditor ? width - editorWidth : width
553
662
 
554
- if (isEditor) {
555
- width = width - editorWidth
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(filterData(externalFilters, excludedData))
740
+ setFilteredData(filterVizData(externalFilters, excludedData))
632
741
  }
633
742
  }
634
743
 
635
- if (externalFilters && externalFilters.length > 0 && externalFilters.length > 0 && externalFilters[0].hasOwnProperty('active')) {
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(filterData(externalFilters, excludedData))
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) ? config.twoColor.palette : config.palette
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: { commas, abbreviated, roundTo, prefix, suffix, rightRoundTo, bottomRoundTo, rightPrefix, rightSuffix, bottomPrefix, bottomSuffix, bottomAbbreviated }
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 ((axis === 'left' && commas && abbreviated && shouldAbbreviate) || (axis === 'bottom' && commas && abbreviated && shouldAbbreviate)) {
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 className='btn' style={{ margin: '1em auto' }} disabled={missingRequiredSections()} onClick={e => confirmDone(e)}>
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 (config.legend.position === 'bottom') classes.push('bottom')
1113
- if (config.legend.hide) classes.push('legend-hidden')
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 (isDebug) classes.push('debug')
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 className='cdc-chart-inner-container cove-component__content' aria-label={handleChartAriaLabels(config)} tabIndex={0}>
1135
- <Title showTitle={config.showTitle} isDashboard={isDashboard} title={title} superTitle={config.superTitle} classes={['chart-title', `${config.theme}`, 'cove-component__header']} style={undefined} />
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' && <Filters config={config} setConfig={setConfig} setFilteredData={setFilteredData} filteredData={filteredData} excludedData={excludedData} filterData={filterData} dimensions={dimensions} />}
1139
- <SkipTo skipId={handleChartTabbing} skipMessage='Skip Over Chart Container' />
1140
- {/* Visualization */}
1141
- {config?.introText && config.visualizationType !== 'Spark Line' && <section className='introText'>{parse(config.introText)}</section>}
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
- {/* All charts except sparkline */}
1144
- {config.visualizationType !== 'Spark Line' && chartComponents[config.visualizationType]}
1145
- {/* Sparkline */}
1146
- {config.visualizationType === 'Spark Line' && (
1147
- <>
1148
- <Filters config={config} setConfig={setConfig} setFilteredData={setFilteredData} filteredData={filteredData} excludedData={excludedData} filterData={filterData} dimensions={dimensions} />
1149
- {config?.introText && (
1150
- <section className='introText' style={{ padding: '0px 0 35px' }}>
1151
- {parse(config.introText)}
1152
- </section>
1153
- )}
1154
- <div style={{ height: `100px`, width: `100%`, ...sparkLineStyles }}>
1155
- <ParentSize>{parent => <SparkLine width={parent.width} height={parent.height} />}</ParentSize>
1156
- </div>
1157
- {description && (
1158
- <div className='subtext' style={{ padding: '35px 0 15px' }}>
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
- {/* Sankey */}
1165
- {config.visualizationType === 'Sankey' && <ParentSize aria-hidden='true'>{parent => <SankeyChart runtime={config.runtime} width={parent.width} height={parent.height} />}</ParentSize>}
1166
- {!config.legend.hide && config.visualizationType !== 'Spark Line' && config.visualizationType !== 'Sankey' && <Legend ref={legendRef} />}
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 => (isDateScale(config.runtime.xAxis) ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
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
- getXAxisData,
1217
- getYAxisData,
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
- dimensions,
1498
+ config,
1228
1499
  currentViewport,
1229
- parseDate,
1500
+ dashboardConfig,
1501
+ debugSvg: isDebug,
1502
+ dimensions,
1503
+ dynamicLegendItems,
1504
+ excludedData: excludedData,
1230
1505
  formatDate,
1231
- formatTooltipsDate,
1232
1506
  formatNumber,
1233
- loading,
1234
- updateConfig,
1235
- colorPalettes,
1236
- isDashboard,
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
- handleLineType,
1251
- lineOptions,
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
- dashboardConfig,
1260
- debugSvg: isDebug,
1261
- clean
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 config={config} isEditor={isEditor} currentViewport={currentViewport} ref={outerContainerRef} imageId={imageId} showEditorPanel={config?.showEditorPanel}>
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>