@cdc/chart 4.23.11 → 4.24.2

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 (104) hide show
  1. package/dist/cdcchart.js +35740 -35027
  2. package/examples/feature/bar/additional-column-tooltip.json +446 -0
  3. package/examples/feature/bar/tall-data.json +98 -0
  4. package/examples/feature/forest-plot/forest-plot.json +63 -19
  5. package/examples/feature/forest-plot/linear.json +52 -3
  6. package/examples/feature/forest-plot/log.json +26 -0
  7. package/examples/feature/forest-plot/logarithmic.json +0 -35
  8. package/examples/feature/line/line-chart-preliminary.json +393 -0
  9. package/examples/feature/regions/index.json +9 -5
  10. package/examples/feature/scatterplot/scatterplot.json +272 -33
  11. package/index.html +10 -8
  12. package/package.json +2 -2
  13. package/src/CdcChart.tsx +70 -234
  14. package/src/ConfigContext.tsx +6 -0
  15. package/src/_stories/ChartEditor.stories.tsx +22 -0
  16. package/src/_stories/ChartLine.preliminary.tsx +19 -0
  17. package/src/_stories/_mock/pie_config.json +192 -0
  18. package/src/_stories/_mock/pie_data.json +218 -0
  19. package/src/_stories/_mock/preliminary_mock.json +346 -0
  20. package/src/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +2 -2
  21. package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +2 -26
  22. package/src/components/AreaChart/index.tsx +4 -0
  23. package/src/components/{BarChart.Horizontal.tsx → BarChart/components/BarChart.Horizontal.tsx} +8 -8
  24. package/src/components/{BarChart.StackedHorizontal.tsx → BarChart/components/BarChart.StackedHorizontal.tsx} +37 -7
  25. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +108 -0
  26. package/src/components/{BarChart.Vertical.tsx → BarChart/components/BarChart.Vertical.tsx} +53 -70
  27. package/src/components/BarChart/components/BarChart.jsx +39 -0
  28. package/src/components/{BarChartType.jsx → BarChart/components/BarChartType.jsx} +0 -2
  29. package/src/components/BarChart/components/context.tsx +13 -0
  30. package/src/components/BarChart/index.tsx +3 -0
  31. package/src/components/{BoxPlot.jsx → BoxPlot/BoxPlot.jsx} +10 -9
  32. package/src/components/BoxPlot/index.tsx +3 -0
  33. package/src/components/EditorPanel/EditorPanel.tsx +2776 -0
  34. package/src/components/EditorPanel/EditorPanelContext.ts +40 -0
  35. package/src/components/EditorPanel/components/PanelProps.ts +3 -0
  36. package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +148 -0
  37. package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx} +97 -167
  38. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +160 -0
  39. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +168 -0
  40. package/src/components/{Series.jsx → EditorPanel/components/Panels/Panel.Series.tsx} +4 -4
  41. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +297 -0
  42. package/src/components/EditorPanel/components/Panels/index.tsx +17 -0
  43. package/src/components/EditorPanel/components/panels.scss +72 -0
  44. package/src/components/EditorPanel/editor-panel.scss +739 -0
  45. package/src/components/EditorPanel/index.tsx +3 -0
  46. package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +34 -2
  47. package/src/components/{Forecasting.jsx → Forecasting/Forecasting.jsx} +1 -1
  48. package/src/components/Forecasting/index.tsx +3 -0
  49. package/src/components/ForestPlot/ForestPlot.tsx +254 -0
  50. package/src/components/ForestPlot/ForestPlotProps.ts +7 -0
  51. package/src/components/ForestPlot/index.tsx +1 -209
  52. package/src/components/Legend/Legend.Component.tsx +199 -0
  53. package/src/components/Legend/Legend.tsx +28 -0
  54. package/src/components/Legend/helpers/createFormatLabels.tsx +140 -0
  55. package/src/components/Legend/index.tsx +3 -0
  56. package/src/components/LineChart/LineChartProps.ts +29 -0
  57. package/src/components/LineChart/components/LineChart.Circle.tsx +147 -0
  58. package/src/components/LineChart/helpers.ts +45 -0
  59. package/src/components/LineChart/index.tsx +111 -23
  60. package/src/components/LinearChart.jsx +55 -72
  61. package/src/components/PairedBarChart.jsx +4 -2
  62. package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +93 -31
  63. package/src/components/PieChart/index.tsx +3 -0
  64. package/src/components/Regions/components/Regions.tsx +144 -0
  65. package/src/components/Regions/index.tsx +3 -0
  66. package/src/components/{ScatterPlot.jsx → ScatterPlot/ScatterPlot.jsx} +3 -3
  67. package/src/components/ScatterPlot/index.tsx +3 -0
  68. package/src/components/{SparkLine.jsx → Sparkline/SparkLine.jsx} +2 -2
  69. package/src/components/Sparkline/index.tsx +3 -0
  70. package/src/data/initial-state.js +10 -8
  71. package/src/helpers/abbreviateNumber.ts +17 -0
  72. package/src/helpers/computeMarginBottom.ts +55 -0
  73. package/src/helpers/filterData.ts +18 -0
  74. package/src/helpers/generateColorsArray.ts +8 -0
  75. package/src/helpers/getQuartiles.ts +30 -0
  76. package/src/helpers/handleChartAriaLabels.ts +19 -0
  77. package/src/helpers/handleLineType.ts +18 -0
  78. package/src/helpers/lineOptions.ts +18 -0
  79. package/src/helpers/sort.ts +7 -0
  80. package/src/helpers/tests/computeMarginBottom.test.ts +20 -0
  81. package/src/hooks/useBarChart.js +7 -6
  82. package/src/hooks/useHighlightedBars.js +1 -1
  83. package/src/hooks/useMinMax.ts +3 -3
  84. package/src/hooks/useScales.ts +19 -6
  85. package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +31 -25
  86. package/src/scss/main.scss +0 -3
  87. package/src/types/ChartConfig.ts +167 -23
  88. package/src/types/ChartContext.ts +34 -12
  89. package/src/types/ForestPlot.ts +7 -14
  90. package/src/types/Label.ts +7 -0
  91. package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
  92. package/src/ConfigContext.jsx +0 -5
  93. package/src/components/BarChart.StackedVertical.tsx +0 -91
  94. package/src/components/BarChart.jsx +0 -30
  95. package/src/components/EditorPanel.jsx +0 -3356
  96. package/src/components/ForestPlot/Readme.md +0 -0
  97. package/src/components/Legend.jsx +0 -310
  98. package/src/components/LineChart/LineChart.Circle.tsx +0 -105
  99. package/src/scss/LinearChart.scss +0 -0
  100. package/src/scss/editor-panel.scss +0 -745
  101. package/src/scss/legend.scss +0 -206
  102. package/src/scss/mixins.scss +0 -0
  103. package/src/scss/variables.scss +0 -1
  104. package/src/types/ChartProps.ts +0 -7
package/src/CdcChart.tsx CHANGED
@@ -12,19 +12,27 @@ import { timeParse, timeFormat } from 'd3-time-format'
12
12
  import Papa from 'papaparse'
13
13
  import parse from 'html-react-parser'
14
14
  import 'react-tooltip/dist/react-tooltip.css'
15
- import chroma from 'chroma-js'
16
15
 
17
16
  // Primary Components
18
17
  import ConfigContext from './ConfigContext'
19
- import PieChart from './components/PieChart'
18
+ import PieChart from './components/PieChart/PieChart'
20
19
  import LinearChart from './components/LinearChart'
21
20
 
22
21
  import { colorPalettesChart as colorPalettes, twoColorPalette } from '@cdc/core/data/colorPalettes'
23
22
 
24
- import SparkLine from './components/SparkLine'
23
+ import SparkLine from './components/Sparkline'
25
24
  import Legend from './components/Legend'
26
25
  import defaults from './data/initial-state'
27
26
  import EditorPanel from './components/EditorPanel'
27
+ import { abbreviateNumber } from './helpers/abbreviateNumber'
28
+ import { getQuartiles } from './helpers/getQuartiles'
29
+ import { sortAsc, sortDesc } from './helpers/sort'
30
+ import { filterData } from './helpers/filterData'
31
+ import { handleChartAriaLabels } from './helpers/handleChartAriaLabels'
32
+ import { lineOptions } from './helpers/lineOptions'
33
+ import { handleLineType } from './helpers/handleLineType'
34
+ import { generateColorsArray } from './helpers/generateColorsArray'
35
+ import { computeMarginBottom } from './helpers/computeMarginBottom'
28
36
  import Loading from '@cdc/core/components/Loading'
29
37
  import Filters from '@cdc/core/components/Filters'
30
38
  import MediaControls from '@cdc/core/components/MediaControls'
@@ -44,23 +52,19 @@ import './scss/main.scss'
44
52
  import DataTable from '@cdc/core/components/DataTable'
45
53
  import { getFileExtension } from '@cdc/core/helpers/getFileExtension'
46
54
  import Title from '@cdc/core/components/ui/Title'
47
-
48
- const generateColorsArray = (color = '#000000', special = false) => {
49
- let colorObj = chroma(color)
50
- let hoverColor = special ? colorObj.brighten(0.5).hex() : colorObj.saturate(1.3).hex()
51
-
52
- return [color, hoverColor, colorObj.darken(0.3).hex()]
53
- }
55
+ import { ChartConfig } from './types/ChartConfig'
56
+ import { Label } from './types/Label'
57
+ import { isSolrCsv, isSolrJson } from '@cdc/core/helpers/isSolr'
54
58
 
55
59
  export default function CdcChart({ configUrl, config: configObj, isEditor = false, isDebug = false, isDashboard = false, setConfig: setParentConfig, setEditing, hostname, link, setSharedFilter, setSharedFilterValue, dashboardConfig }) {
56
60
  const transform = new DataTransform()
57
61
  const [loading, setLoading] = useState(true)
58
62
  const [colorScale, setColorScale] = useState(null)
59
- const [config, setConfig] = useState<any>({})
63
+ const [config, setConfig] = useState<ChartConfig>({} as ChartConfig)
60
64
  const [stateData, setStateData] = useState(config.data || [])
61
65
  const [excludedData, setExcludedData] = useState<Record<string, number>[] | undefined>(undefined)
62
66
  const [filteredData, setFilteredData] = useState<Record<string, any>[] | undefined>(undefined)
63
- const [seriesHighlight, setSeriesHighlight] = useState<any[]>([])
67
+ const [seriesHighlight, setSeriesHighlight] = useState<string[]>([])
64
68
  const [currentViewport, setCurrentViewport] = useState('lg')
65
69
  const [dimensions, setDimensions] = useState<[number?, number?]>([])
66
70
  const [externalFilters, setExternalFilters] = useState<any[]>()
@@ -88,41 +92,13 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
88
92
 
89
93
  const handleChartTabbing = config.showSidebar ? `#legend` : config?.title ? `#dataTableSection__${config.title.replace(/\s/g, '')}` : `#dataTableSection`
90
94
 
91
- const sortAsc = (a, b) => {
92
- return a.toString().localeCompare(b.toString(), 'en', { numeric: true })
93
- }
94
-
95
- const sortDesc = (a, b) => {
96
- return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
97
- }
98
-
99
- const handleChartAriaLabels = (state, testing = false) => {
100
- if (testing) console.log(`handleChartAriaLabels Testing On:`, state) // eslint-disable-line
101
- try {
102
- if (!state.visualizationType) throw Error('handleChartAriaLabels: no visualization type found in state')
103
- let ariaLabel = ''
104
-
105
- if (state.visualizationType) {
106
- ariaLabel += `${state.visualizationType} chart`
107
- }
108
-
109
- if (state.title && state.visualizationType) {
110
- ariaLabel += ` with the title: ${state.title}`
111
- }
112
-
113
- return ariaLabel
114
- } catch (e) {
115
- console.error('COVE: ', e.message) // eslint-disable-line
116
- }
117
- }
118
-
119
95
  const reloadURLData = async () => {
120
96
  if (config.dataUrl) {
121
97
  const dataUrl = new URL(config.runtimeDataUrl || config.dataUrl, window.location.origin)
122
98
  let qsParams = Object.fromEntries(new URLSearchParams(dataUrl.search))
123
99
 
124
100
  let isUpdateNeeded = false
125
- config.filters.forEach(filter => {
101
+ config.filters?.forEach(filter => {
126
102
  if (filter.type === 'url' && qsParams[filter.queryParameter] !== decodeURIComponent(filter.active)) {
127
103
  qsParams[filter.queryParameter] = filter.active
128
104
  isUpdateNeeded = true
@@ -143,8 +119,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
143
119
  let data: any[] = []
144
120
 
145
121
  try {
146
- const ext = getFileExtension(dataUrl.pathname)
147
- if ('csv' === ext) {
122
+ const ext = getFileExtension(dataUrl.href)
123
+ if ('csv' === ext || isSolrCsv(dataUrlFinal)) {
148
124
  data = await fetch(dataUrlFinal)
149
125
  .then(response => response.text())
150
126
  .then(responseText => {
@@ -155,7 +131,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
155
131
  })
156
132
  return parsedCsv.data
157
133
  })
158
- } else if ('json' === ext) {
134
+ } else if ('json' === ext || isSolrJson(dataUrlFinal)) {
159
135
  data = await fetch(dataUrlFinal).then(response => response.json())
160
136
  } else {
161
137
  data = []
@@ -182,44 +158,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
182
158
  }
183
159
  }
184
160
 
185
- const handleLineType = lineType => {
186
- switch (lineType) {
187
- case 'dashed-sm':
188
- return '5 5'
189
- case 'Dashed Small':
190
- return '5 5'
191
- case 'dashed-md':
192
- return '10 5'
193
- case 'Dashed Medium':
194
- return '10 5'
195
- case 'dashed-lg':
196
- return '15 5'
197
- case 'Dashed Large':
198
- return '15 5'
199
- default:
200
- return 0
201
- }
202
- }
203
-
204
- const lineOptions = [
205
- {
206
- value: 'Dashed Small',
207
- key: 'dashed-sm'
208
- },
209
- {
210
- value: 'Dashed Medium',
211
- key: 'dashed-md'
212
- },
213
- {
214
- value: 'Dashed Large',
215
- key: 'dashed-lg'
216
- },
217
- {
218
- value: 'Solid Line',
219
- key: 'solid-line'
220
- }
221
- ]
222
-
223
161
  const loadConfig = async () => {
224
162
  let response = configObj || (await (await fetch(configUrl)).json())
225
163
 
@@ -231,7 +169,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
231
169
  if (response.dataUrl && !urlFilters) {
232
170
  try {
233
171
  const ext = getFileExtension(response.dataUrl)
234
- if ('csv' === ext) {
172
+ if ('csv' === ext || isSolrCsv(response.dataUrl)) {
235
173
  data = await fetch(response.dataUrl + `?v=${cacheBustingString()}`)
236
174
  .then(response => response.text())
237
175
  .then(responseText => {
@@ -254,7 +192,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
254
192
  })
255
193
  }
256
194
 
257
- if ('json' === ext) {
195
+ if ('json' === ext || isSolrJson(response.dataUrl)) {
258
196
  data = await fetch(response.dataUrl + `?v=${cacheBustingString()}`).then(response => response.json())
259
197
  }
260
198
  } catch {
@@ -359,6 +297,16 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
359
297
  setFilteredData(currentData)
360
298
  }
361
299
 
300
+ if (!['Area Chart', 'Bar', 'Line', 'Combo'].includes(newConfig.visualizationType) || newConfig.orientation === 'horizontal') {
301
+ newConfig.xAxis.sortDates = false
302
+ }
303
+
304
+ if (newConfig.xAxis.sortDates && newConfig.barThickness > 0.1) {
305
+ newConfig.barThickness = 0.035
306
+ } else if (!newConfig.xAxis.sortDates && newConfig.barThickness < 0.1) {
307
+ newConfig.barThickness = 0.35
308
+ }
309
+
362
310
  //Enforce default values that need to be calculated at runtime
363
311
  newConfig.runtime = {}
364
312
  newConfig.runtime.seriesLabels = {}
@@ -392,37 +340,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
392
340
  let tableData: any[] = []
393
341
  const plots: any[] = []
394
342
 
395
- /**
396
- * Calculates the first quartile (q1) and third quartile (q3) from an array of integers or decimals.
397
- *
398
- * @param {Array} arr - The array of integers or decimals.
399
- * @returns {Object} An object containing the q1 and q3 values.
400
- */
401
- const getQuartiles = arr => {
402
- arr.sort((a, b) => a - b)
403
-
404
- // Calculate the index of the median value of the array
405
- const medianIndex = Math.floor(arr.length / 2)
406
-
407
- // Check if the length of the array is even or odd
408
- const isEvenLength = arr.length % 2 === 0
409
-
410
- // Split the array into two subarrays based on the median index
411
- const q1Array = isEvenLength ? arr.slice(0, medianIndex) : arr.slice(0, medianIndex + 1)
412
- const q3Array = isEvenLength ? arr.slice(medianIndex) : arr.slice(medianIndex + 1)
413
-
414
- // Calculate the median of the first subarray to get the q1 value
415
- const q1Index = Math.floor(q1Array.length / 2)
416
- const q1 = isEvenLength ? (q1Array[q1Index - 1] + q1Array[q1Index]) / 2 : q1Array[q1Index]
417
-
418
- // Calculate the median of the second subarray to get the q3 value
419
- const q3Index = Math.floor(q3Array.length / 2)
420
- const q3 = isEvenLength ? (q3Array[q3Index - 1] + q3Array[q3Index]) / 2 : q3Array[q3Index]
421
-
422
- // Return an object containing the q1 and q3 values
423
- return { q1, q3 }
424
- }
425
-
426
343
  // group specific statistics
427
344
  // prevent re-renders
428
345
  if (!groups) return
@@ -522,6 +439,9 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
522
439
  if (series.type === 'Line' || series.type === 'dashed-sm' || series.type === 'dashed-md' || series.type === 'dashed-lg') {
523
440
  newConfig.runtime.lineSeriesKeys.push(series.dataKey)
524
441
  }
442
+ if (series.type === 'Combo') {
443
+ series.type = 'Bar'
444
+ }
525
445
  })
526
446
  }
527
447
 
@@ -544,14 +464,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
544
464
  }
545
465
 
546
466
  if ((newConfig.visualizationType === 'Bar' && newConfig.orientation === 'horizontal') || ['Deviation Bar', 'Paired Bar', 'Forest Plot'].includes(newConfig.visualizationType)) {
547
- newConfig.runtime.xAxis = newConfig.yAxis
548
- newConfig.runtime.yAxis = newConfig.xAxis
467
+ newConfig.runtime.xAxis = newConfig.yAxis['yAxis'] ? newConfig.yAxis['yAxis'] : newConfig.yAxis
468
+ newConfig.runtime.yAxis = newConfig.xAxis['xAxis'] ? newConfig.xAxis['xAxis'] : newConfig.xAxis
549
469
 
550
- if (newConfig.visualizationType === 'Forest Plot') {
551
- newConfig.runtime.xAxis.type = newConfig.forestPlot.type
552
- newConfig.runtime.xAxis.tickRotation = newConfig.xAxis.tickRotation
553
- }
554
- newConfig.runtime.horizontal = true
470
+ newConfig.runtime.horizontal = false
555
471
  newConfig.orientation = 'horizontal'
556
472
  } else if (['Box Plot', 'Scatter Plot', 'Area Chart', 'Line', 'Forecasting'].includes(newConfig.visualizationType)) {
557
473
  newConfig.runtime.xAxis = newConfig.xAxis
@@ -563,31 +479,13 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
563
479
  newConfig.runtime.yAxis = newConfig.yAxis
564
480
  newConfig.runtime.horizontal = false
565
481
  }
482
+
566
483
  newConfig.runtime.uniqueId = Date.now()
567
484
  newConfig.runtime.editorErrorMessage = newConfig.visualizationType === 'Pie' && !newConfig.yAxis.dataKey ? 'Data Key property in Y Axis section must be set for pie charts.' : ''
568
485
 
569
486
  setConfig(newConfig)
570
487
  }
571
488
 
572
- const filterData = (filters, data) => {
573
- let filteredData: any[] = []
574
-
575
- data.forEach(row => {
576
- let add = true
577
- filters
578
- .filter(filter => filter.type !== 'url')
579
- .forEach(filter => {
580
- if (row[filter.columnName] != filter.active) {
581
- add = false
582
- }
583
- })
584
-
585
- if (add) filteredData.push(row)
586
- })
587
-
588
- return filteredData
589
- }
590
-
591
489
  // Gets filter values from dataset
592
490
  const generateValuesForFilter = (columnName, data = this.state.data) => {
593
491
  const values: any[] = []
@@ -726,9 +624,9 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
726
624
 
727
625
  // Generates color palette to pass to child chart component
728
626
  useEffect(() => {
729
- if (stateData && config.xAxis && config.runtime.seriesKeys) {
730
- const configPalette = config.customColors ? config.customColors : config.visualizationType === 'Paired Bar' || config.visualizationType === 'Deviation Bar' ? config.twoColor.palette : config.palette
731
- const allPalettes = { ...colorPalettes, ...twoColorPalette }
627
+ if (stateData && config.xAxis && config.runtime?.seriesKeys) {
628
+ const configPalette = ['Paired Bar', 'Deviation Bar'].includes(config.visualizationType) ? config.twoColor.palette : config.palette
629
+ const allPalettes: Record<string, string[]> = { ...colorPalettes, ...twoColorPalette }
732
630
  let palette = config.customColors || allPalettes[configPalette]
733
631
  let numberOfKeys = config.runtime.seriesKeys.length
734
632
  let newColorScale
@@ -756,27 +654,22 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
756
654
  }, [config, stateData]) // eslint-disable-line
757
655
 
758
656
  // Called on legend click, highlights/unhighlights the data series with the given label
759
- const highlight = label => {
760
- const newSeriesHighlight: any[] = []
761
-
657
+ const highlight = (label: Label) => {
762
658
  // If we're highlighting all the series, reset them
763
659
  if (seriesHighlight.length + 1 === config.runtime.seriesKeys.length && config.visualizationType !== 'Forecasting') {
764
660
  highlightReset()
765
661
  return
766
662
  }
767
663
 
768
- seriesHighlight.forEach(value => {
769
- newSeriesHighlight.push(value)
770
- })
664
+ const newSeriesHighlight = [...seriesHighlight]
771
665
 
772
666
  let newHighlight = label.datum
773
667
  if (config.runtime.seriesLabels) {
774
- for (let i = 0; i < config.runtime.seriesKeys.length; i++) {
775
- if (config.runtime.seriesLabels[config.runtime.seriesKeys[i]] === label.datum) {
776
- newHighlight = config.runtime.seriesKeys[i]
777
- break
668
+ config.runtime.seriesKeys.forEach(key => {
669
+ if (config.runtime.seriesLabels[key] === label.datum) {
670
+ newHighlight = key
778
671
  }
779
- }
672
+ })
780
673
  }
781
674
 
782
675
  if (newSeriesHighlight.indexOf(newHighlight) !== -1) {
@@ -832,6 +725,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
832
725
  return timeFormat(config.runtime[section].dateDisplayFormat)(date)
833
726
  }
834
727
 
728
+ const formatTooltipsDate = date => {
729
+ return timeFormat(config.tooltips.dateDisplayFormat)(date)
730
+ }
731
+
835
732
  // function calculates the width of given text and its font-size
836
733
  function getTextWidth(text, font) {
837
734
  const canvas = document.createElement('canvas')
@@ -845,24 +742,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
845
742
  return Math.ceil(context.measureText(text).width)
846
743
  }
847
744
 
848
- const abbreviateNumber = num => {
849
- let unit = ''
850
- let absNum = Math.abs(num)
851
-
852
- if (absNum >= 1e9) {
853
- unit = 'B'
854
- num = num / 1e9
855
- } else if (absNum >= 1e6) {
856
- unit = 'M'
857
- num = num / 1e6
858
- } else if (absNum >= 1e3) {
859
- unit = 'K'
860
- num = num / 1e3
861
- }
862
-
863
- return num + unit
864
- }
865
-
866
745
  // Format numeric data based on settings in config OR from passed in settings for Additional Columns
867
746
  // - use only for old horizontal data - newer formatNumber is in helper/formatNumber
868
747
  // TODO: we should combine various formatNumber functions across this project.
@@ -914,11 +793,17 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
914
793
  }
915
794
  }
916
795
 
796
+ const resolveBottomTickRounding = () => {
797
+ if (config.forestPlot.type === 'Logarithmic' && !bottomRoundTo) return 2
798
+ if (Number(bottomRoundTo)) return Number(bottomRoundTo)
799
+ return 0
800
+ }
801
+
917
802
  if (axis === 'bottom') {
918
803
  stringFormattingOptions = {
919
804
  useGrouping: config.dataFormat.bottomCommas ? true : false,
920
- minimumFractionDigits: bottomRoundTo ? Number(bottomRoundTo) : 0,
921
- maximumFractionDigits: bottomRoundTo ? Number(bottomRoundTo) : 0
805
+ minimumFractionDigits: resolveBottomTickRounding(),
806
+ maximumFractionDigits: resolveBottomTickRounding()
922
807
  }
923
808
  }
924
809
 
@@ -1136,61 +1021,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1136
1021
  return key
1137
1022
  }
1138
1023
 
1139
- const computeMarginBottom = (config: Config): string => {
1140
- const isLegendBottom = legend.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
1141
- const isHorizontal = config.orientation === 'horizontal'
1142
- const tickRotation = Number(config.xAxis.tickRotation) > 0 ? Number(config.xAxis.tickRotation) : 0
1143
- const isBrush = config.brush.active
1144
- const offset = 20
1145
- const brushHeight = config.brush.height
1146
- let bottom = 0
1147
- if (!isLegendBottom && isHorizontal && !config.yAxis.label) {
1148
- bottom = Number(config.xAxis.labelOffset)
1149
- }
1150
- if (!isLegendBottom && isHorizontal && config.yAxis.label && !config.isResponsiveTicks) {
1151
- bottom = Number(config.runtime.xAxis.size) + Number(config.xAxis.labelOffset)
1152
- }
1153
- if (!isLegendBottom && isHorizontal && config.yAxis.label && config.isResponsiveTicks) {
1154
- bottom = config.dynamicMarginTop + offset
1155
- }
1156
- if (!isLegendBottom && isHorizontal && !config.yAxis.label && config.isResponsiveTicks) {
1157
- bottom = config.dynamicMarginTop ? config.dynamicMarginTop - offset : Number(config.xAxis.labelOffset) - offset
1158
- }
1159
- if (!isLegendBottom && isHorizontal && config.yAxis.label && config.isResponsiveTicks) {
1160
- bottom = config.dynamicMarginTop ? config.dynamicMarginTop + offset : Number(config.xAxis.labelOffset)
1161
- }
1162
-
1163
- if (!isHorizontal && !isLegendBottom && config.xAxis.label && tickRotation && !config.isResponsiveTicks) {
1164
- bottom = isBrush ? brushHeight + config.xAxis.tickWidthMax + -config.xAxis.size + config.xAxis.labelOffset + offset : config.xAxis.tickWidthMax + offset + -config.xAxis.size + config.xAxis.labelOffset
1165
- }
1166
- if (!isHorizontal && !isLegendBottom && !config.xAxis.label && tickRotation && !config.isResponsiveTicks) {
1167
- }
1168
- if (!isHorizontal && !isLegendBottom && !config.xAxis.label && tickRotation && !config.dynamicMarginTop && !config.isResponsiveTicks) {
1169
- bottom = isBrush ? config.xAxis.tickWidthMax + brushHeight + offset + -config.xAxis.size : 0
1170
- }
1171
-
1172
- if (!isHorizontal && !isLegendBottom && config.xAxis.label && !tickRotation && !config.isResponsiveTicks) {
1173
- bottom = isBrush ? brushHeight + -config.xAxis.size + config.xAxis.labelOffset + offset : -config.xAxis.size + config.xAxis.labelOffset + offset
1174
- }
1175
- if (!isHorizontal && !isLegendBottom && config.xAxis.label && config.dynamicMarginTop && config.isResponsiveTicks) {
1176
- bottom = isBrush ? brushHeight + config.xAxis.labelOffset + -config.xAxis.size + config.xAxis.tickWidthMax : config.dynamicMarginTop + -config.xAxis.size + offset
1177
- }
1178
- if (!isHorizontal && !isLegendBottom && !config.xAxis.label && config.dynamicMarginTop && config.isResponsiveTicks) {
1179
- bottom = isBrush ? brushHeight + config.xAxis.labelOffset + -config.xAxis.size + config.xAxis.tickWidthMax : config.dynamicMarginTop + -config.xAxis.size - offset
1180
- }
1181
- if (!isHorizontal && !isLegendBottom && config.xAxis.label && !config.dynamicMarginTop && config.isResponsiveTicks) {
1182
- bottom = isBrush ? brushHeight + config.xAxis.labelOffset + -config.xAxis.size + 25 : config.xAxis.labelOffset + -config.xAxis.size + offset
1183
- }
1184
- if (!isHorizontal && !isLegendBottom && !config.xAxis.label && !config.dynamicMarginTop && config.isResponsiveTicks) {
1185
- bottom = -config.xAxis.size + offset + config.xAxis.labelOffset
1186
- }
1187
- if (!isHorizontal && !isLegendBottom && !config.xAxis.label && !tickRotation && !config.dynamicMarginTop && !config.isResponsiveTicks) {
1188
- bottom = isBrush ? brushHeight + -config.xAxis.size + config.xAxis.labelOffset : 0
1189
- }
1190
-
1191
- return `${bottom}px`
1192
- }
1193
-
1194
1024
  // Prevent render if loading
1195
1025
  let body = <Loading />
1196
1026
 
@@ -1214,7 +1044,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1214
1044
  {config.filters && !externalFilters && <Filters config={config} setConfig={setConfig} setFilteredData={setFilteredData} filteredData={filteredData} excludedData={excludedData} filterData={filterData} dimensions={dimensions} />}
1215
1045
  {/* Visualization */}
1216
1046
  {config?.introText && config.visualizationType !== 'Spark Line' && <section className='introText'>{parse(config.introText)}</section>}
1217
- <div style={{ marginBottom: computeMarginBottom(config) }} className={`chart-container p-relative ${config.legend.position === 'bottom' ? 'bottom' : ''}${config.legend.hide ? ' legend-hidden' : ''}${lineDatapointClass}${barBorderClass} ${contentClasses.join(' ')}`}>
1047
+ <div
1048
+ style={{ marginBottom: computeMarginBottom(config, legend, currentViewport) }}
1049
+ className={`chart-container p-relative ${config.legend.position === 'bottom' ? 'bottom' : ''}${config.legend.hide ? ' legend-hidden' : ''}${lineDatapointClass}${barBorderClass} ${contentClasses.join(' ')} ${isDebug ? 'debug' : ''}`}
1050
+ >
1218
1051
  {/* All charts except sparkline */}
1219
1052
  {config.visualizationType !== 'Spark Line' && chartComponents[config.visualizationType]}
1220
1053
 
@@ -1236,7 +1069,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1236
1069
  )}
1237
1070
  </>
1238
1071
  )}
1239
- {!config.legend.hide && config.visualizationType !== 'Spark Line' && <Legend />}
1072
+ {!config.legend.hide && config.visualizationType !== 'Spark Line' && config.visualizationType !== 'Forest Plot' && <Legend />}
1240
1073
  </div>
1241
1074
  {/* Link */}
1242
1075
  {isDashboard && config.table && config.table.show && config.table.showDataTableLink ? tableLink : link && link}
@@ -1254,7 +1087,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1254
1087
  {config.xAxis.dataKey && config.table.show && config.visualizationType !== 'Spark Line' && (
1255
1088
  <DataTable
1256
1089
  config={config}
1257
- rawData={config.data}
1090
+ rawData={config.table.customTableConfig ? filterData(config.filters, config.data) : config.data}
1258
1091
  runtimeData={transform.applySuppression(filteredData || excludedData, config.suppressedData)}
1259
1092
  expandDataTable={config.table.expanded}
1260
1093
  columns={config.columns}
@@ -1286,6 +1119,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1286
1119
 
1287
1120
  const contextValues = {
1288
1121
  capitalize,
1122
+ computeMarginBottom,
1289
1123
  getXAxisData,
1290
1124
  getYAxisData,
1291
1125
  config,
@@ -1301,6 +1135,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1301
1135
  currentViewport,
1302
1136
  parseDate,
1303
1137
  formatDate,
1138
+ formatTooltipsDate,
1304
1139
  formatNumber,
1305
1140
  loading,
1306
1141
  updateConfig,
@@ -1328,7 +1163,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1328
1163
  isDebug,
1329
1164
  setSharedFilter,
1330
1165
  setSharedFilterValue,
1331
- dashboardConfig
1166
+ dashboardConfig,
1167
+ debugSvg: isDebug
1332
1168
  }
1333
1169
 
1334
1170
  const classes = ['cdc-open-viz-module', 'type-chart', `${currentViewport}`, `font-${config.fontSize}`, `${config.theme}`]
@@ -0,0 +1,6 @@
1
+ import { createContext } from 'react'
2
+ import { ChartContext } from './types/ChartContext'
3
+
4
+ const ConfigContext = createContext({} as ChartContext)
5
+
6
+ export default ConfigContext
@@ -0,0 +1,22 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+
3
+ import Chart from '../CdcChart'
4
+
5
+ import pieChartExample from './_mock/pie_config.json'
6
+ import pieData from './_mock/pie_data.json'
7
+
8
+ const meta: Meta<typeof Chart> = {
9
+ title: 'Components/Templates/Editor/Chart',
10
+ component: Chart
11
+ }
12
+
13
+ type Story = StoryObj<typeof Chart>
14
+
15
+ export const Primary: Story = {
16
+ args: {
17
+ config: { ...pieChartExample, data: pieData, columns: { someCol: { name: 'females', showInViz: true } } },
18
+ isEditor: true
19
+ }
20
+ }
21
+
22
+ export default meta
@@ -0,0 +1,19 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import chartLinepreliminary from './_mock/preliminary_mock.json'
3
+
4
+ import Chart from '../CdcChart'
5
+
6
+ const meta: Meta<typeof Chart> = {
7
+ title: 'Components/Templates/Chart/Line/Preliminary',
8
+ component: Chart
9
+ }
10
+
11
+ type Story = StoryObj<typeof Chart>
12
+
13
+ export const Line_Chart: Story = {
14
+ args: {
15
+ config: chartLinepreliminary
16
+ }
17
+ }
18
+
19
+ export default meta