@cdc/chart 4.23.4 → 4.23.5

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 (31) hide show
  1. package/dist/cdcchart.js +52384 -50875
  2. package/examples/feature/__data__/planet-example-data.json +2 -19
  3. package/examples/feature/__data__/planet-logaritmic-data.json +56 -0
  4. package/examples/feature/area/area-chart-category.json +240 -0
  5. package/examples/feature/bar/example-bar-chart.json +544 -22
  6. package/examples/feature/bar/planet-chart-logaritmic-config.json +170 -0
  7. package/examples/feature/boxplot/valid-boxplot.csv +17 -0
  8. package/examples/feature/filters/filter-testing.json +37 -3
  9. package/examples/feature/forecasting/random_data.csv +366 -0
  10. package/examples/feature/line/line-chart.json +2 -2
  11. package/examples/feature/test-highlight/test-highlight-2.json +789 -0
  12. package/examples/feature/test-highlight/test-highlight-vertical.json +561 -0
  13. package/examples/feature/test-highlight/test-highlight.json +100 -0
  14. package/examples/feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json +1 -2
  15. package/examples/gallery/bar-chart-horizontal/horizontal-highlight.json +345 -0
  16. package/index.html +8 -8
  17. package/package.json +2 -2
  18. package/src/CdcChart.jsx +294 -14
  19. package/src/components/AreaChart.jsx +27 -20
  20. package/src/components/BarChart.jsx +85 -25
  21. package/src/components/DeviationBar.jsx +32 -32
  22. package/src/components/EditorPanel.jsx +1105 -184
  23. package/src/components/Legend.jsx +39 -3
  24. package/src/components/LineChart.jsx +1 -8
  25. package/src/components/LinearChart.jsx +121 -270
  26. package/src/data/initial-state.js +18 -3
  27. package/src/hooks/useHighlightedBars.js +154 -0
  28. package/src/hooks/useMinMax.js +92 -0
  29. package/src/hooks/useReduceData.js +31 -57
  30. package/src/hooks/useScales.js +202 -0
  31. /package/examples/feature/area/{area-chart.json → area-chart-date.json} +0 -0
package/src/CdcChart.jsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect, useCallback } from 'react'
1
+ import React, { useState, useEffect, useCallback, useRef } from 'react'
2
2
 
3
3
  // IE11
4
4
  import ResizeObserver from 'resize-observer-polyfill'
@@ -9,10 +9,10 @@ import * as d3 from 'd3-array'
9
9
  import { scaleOrdinal } from '@visx/scale'
10
10
  import ParentSize from '@visx/responsive/lib/components/ParentSize'
11
11
  import { timeParse, timeFormat } from 'd3-time-format'
12
- import { format } from 'd3-format'
13
12
  import Papa from 'papaparse'
14
13
  import parse from 'html-react-parser'
15
14
  import 'react-tooltip/dist/react-tooltip.css'
15
+ import chroma from 'chroma-js'
16
16
 
17
17
  // Primary Components
18
18
  import ConfigContext from './ConfigContext'
@@ -27,7 +27,6 @@ import useDataVizClasses from '@cdc/core/helpers/useDataVizClasses'
27
27
 
28
28
  import SparkLine from './components/SparkLine'
29
29
  import Legend from './components/Legend'
30
- import DataTable from './components/DataTable'
31
30
  import defaults from './data/initial-state'
32
31
  import EditorPanel from './components/EditorPanel'
33
32
  import Loading from '@cdc/core/components/Loading'
@@ -42,6 +41,36 @@ import cacheBustingString from '@cdc/core/helpers/cacheBustingString'
42
41
  import isNumber from '@cdc/core/helpers/isNumber'
43
42
 
44
43
  import './scss/main.scss'
44
+ // load both then config below determines which to use
45
+ import DataTable_horiz from './components/DataTable'
46
+ import DataTable_vert from '@cdc/core/components/DataTable'
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
+ }
54
+ const hashObj = row => {
55
+ try {
56
+ if (!row) throw new Error('No row supplied to hashObj')
57
+
58
+ let str = JSON.stringify(row)
59
+ let hash = 0
60
+
61
+ if (str.length === 0) return hash
62
+
63
+ for (let i = 0; i < str.length; i++) {
64
+ let char = str.charCodeAt(i)
65
+ hash = (hash << 5) - hash + char
66
+ hash = hash & hash
67
+ }
68
+
69
+ return hash
70
+ } catch (e) {
71
+ console.error('COVE: ', e) // eslint-disable-line
72
+ }
73
+ }
45
74
 
46
75
  export default function CdcChart({ configUrl, config: configObj, isEditor = false, isDebug = false, isDashboard = false, setConfig: setParentConfig, setEditing, hostname, link }) {
47
76
  const transform = new DataTransform()
@@ -60,6 +89,13 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
60
89
  const [dynamicLegendItems, setDynamicLegendItems] = useState([])
61
90
  const [imageId] = useState(`cove-${Math.random().toString(16).slice(-4)}`)
62
91
 
92
+ let legendMemo = useRef(new Map()) // map collection
93
+ let innerContainerRef = useRef()
94
+
95
+ if (isDebug) console.log('Chart config', config)
96
+
97
+ const DataTable = config?.table?.showVertical ? DataTable_vert : DataTable_horiz
98
+
63
99
  // Destructure items from config for more readable JSX
64
100
  let { legend, title, description, visualizationType } = config
65
101
 
@@ -102,26 +138,121 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
102
138
  }
103
139
  }
104
140
 
141
+ const reloadURLData = async () => {
142
+ if (config.dataUrl) {
143
+ const dataUrl = new URL(config.runtimeDataUrl || config.dataUrl)
144
+ let qsParams = Object.fromEntries(new URLSearchParams(dataUrl.search))
145
+
146
+ let isUpdateNeeded = false
147
+ config.filters.forEach(filter => {
148
+ if (filter.type === 'url' && qsParams[filter.queryParameter] !== decodeURIComponent(filter.active)) {
149
+ qsParams[filter.queryParameter] = filter.active
150
+ isUpdateNeeded = true
151
+ }
152
+ })
153
+
154
+ if ((!config.formattedData || config.formattedData.urlFiltered) && !isUpdateNeeded) return
155
+
156
+ let dataUrlFinal = `${dataUrl.origin}${dataUrl.pathname}${Object.keys(qsParams)
157
+ .map((param, i) => {
158
+ let qs = i === 0 ? '?' : '&'
159
+ qs += param + '='
160
+ qs += qsParams[param]
161
+ return qs
162
+ })
163
+ .join('')}`
164
+
165
+ let data
166
+
167
+ try {
168
+ const regex = /(?:\.([^.]+))?$/
169
+
170
+ const ext = regex.exec(dataUrl.pathname)[1]
171
+ if ('csv' === ext) {
172
+ data = await fetch(dataUrlFinal)
173
+ .then(response => response.text())
174
+ .then(responseText => {
175
+ const parsedCsv = Papa.parse(responseText, {
176
+ header: true,
177
+ dynamicTyping: true,
178
+ skipEmptyLines: true
179
+ })
180
+ return parsedCsv.data
181
+ })
182
+ } else if ('json' === ext) {
183
+ data = await fetch(dataUrlFinal).then(response => response.json())
184
+ } else {
185
+ data = []
186
+ }
187
+ } catch {
188
+ console.error(`Cannot parse URL: ${dataUrlFinal}`)
189
+ data = []
190
+ }
191
+
192
+ if (config.dataDescription) {
193
+ data = transform.autoStandardize(data)
194
+ data = transform.developerStandardize(data, config.dataDescription)
195
+ }
196
+
197
+ Object.assign(data, { urlFiltered: true })
198
+
199
+ updateConfig({ ...config, runtimeDataUrl: dataUrlFinal, data, formattedData: data })
200
+
201
+ if (data) {
202
+ setStateData(data)
203
+ setExcludedData(data)
204
+ setFilteredData(filterData(config.filters, data))
205
+ }
206
+ }
207
+ }
208
+
105
209
  const handleLineType = lineType => {
106
210
  switch (lineType) {
107
211
  case 'dashed-sm':
108
212
  return '5 5'
213
+ case 'Dashed Small':
214
+ return '5 5'
109
215
  case 'dashed-md':
110
216
  return '10 5'
217
+ case 'Dashed Medium':
218
+ return '10 5'
111
219
  case 'dashed-lg':
112
220
  return '15 5'
221
+ case 'Dashed Large':
222
+ return '15 5'
113
223
  default:
114
224
  return 0
115
225
  }
116
226
  }
117
227
 
228
+ const lineOptions = [
229
+ {
230
+ value: 'Dashed Small',
231
+ key: 'dashed-sm'
232
+ },
233
+ {
234
+ value: 'Dashed Medium',
235
+ key: 'dashed-md'
236
+ },
237
+ {
238
+ value: 'Dashed Large',
239
+ key: 'dashed-lg'
240
+ },
241
+ {
242
+ value: 'Solid Line',
243
+ key: 'solid-line'
244
+ }
245
+ ]
246
+
118
247
  const loadConfig = async () => {
119
248
  let response = configObj || (await (await fetch(configUrl)).json())
120
249
 
121
250
  // If data is included through a URL, fetch that and store
122
251
  let data = response.formattedData || response.data || {}
123
252
 
124
- if (response.dataUrl) {
253
+ const urlFilters = response.filters ? (response.filters.filter(filter => filter.type === 'url').length > 0 ? true : false) : false
254
+
255
+ if (response.dataUrl && !urlFilters) {
125
256
  try {
126
257
  const regex = /(?:\.([^.]+))?$/
127
258
 
@@ -158,6 +289,13 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
158
289
  setExcludedData(data)
159
290
  }
160
291
 
292
+ // force showVertical for data tables false if it does not exist
293
+ if (response !== undefined && response.table !== undefined) {
294
+ if (!response.table || !response.table.showVertical) {
295
+ response.table = response.table || {}
296
+ response.table.showVertical = false
297
+ }
298
+ }
161
299
  let newConfig = { ...defaults, ...response }
162
300
  if (newConfig.visualizationType === 'Box Plot') {
163
301
  newConfig.legend.hide = true
@@ -219,7 +357,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
219
357
 
220
358
  newConfig.filters[index].values = filterValues
221
359
  // Initial filter should be active
222
- newConfig.filters[index].active = filterValues[0]
360
+
361
+ newConfig.filters[index].active = newConfig.filters[index].active || filterValues[0]
223
362
  newConfig.filters[index].filterStyle = newConfig.filters[index].filterStyle ? newConfig.filters[index].filterStyle : 'dropdown'
224
363
  })
225
364
  currentData = filterData(newConfig.filters, newExcludedData)
@@ -416,14 +555,17 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
416
555
 
417
556
  data.forEach(row => {
418
557
  let add = true
419
- filters.forEach(filter => {
420
- if (row[filter.columnName] !== filter.active) {
421
- add = false
422
- }
423
- })
558
+ filters
559
+ .filter(filter => filter.type !== 'url')
560
+ .forEach(filter => {
561
+ if (row[filter.columnName] != filter.active) {
562
+ add = false
563
+ }
564
+ })
424
565
 
425
566
  if (add) filteredData.push(row)
426
567
  })
568
+
427
569
  return filteredData
428
570
  }
429
571
 
@@ -498,6 +640,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
498
640
  loadConfig()
499
641
  }, []) // eslint-disable-line
500
642
 
643
+ useEffect(() => {
644
+ reloadURLData()
645
+ }, [JSON.stringify(config.filters)])
646
+
501
647
  /**
502
648
  * When cove has a config and container ref publish the cove_loaded event.
503
649
  */
@@ -632,10 +778,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
632
778
 
633
779
  const section = config.orientation === 'horizontal' ? 'yAxis' : 'xAxis'
634
780
 
635
- const parseDate = dateString => {
781
+ const parseDate = (dateString, showError = true) => {
636
782
  let date = timeParse(config.runtime[section].dateParseFormat)(dateString)
637
783
  if (!date) {
638
- config.runtime.editorErrorMessage = `Error parsing date "${dateString}". Try reviewing your data and date parse settings in the X Axis section.`
784
+ if (showError) {
785
+ config.runtime.editorErrorMessage = `Error parsing date "${dateString}". Try reviewing your data and date parse settings in the X Axis section.`
786
+ }
639
787
  return new Date()
640
788
  } else {
641
789
  return date
@@ -822,10 +970,113 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
822
970
  return false
823
971
  }
824
972
 
973
+ // used for Additional Column
974
+ const displayDataAsText = (value, columnName) => {
975
+ if (value === null || value === '' || value === undefined) {
976
+ return ''
977
+ }
978
+
979
+ if (typeof value === 'string' && value.length > 0 && config.legend.type === 'equalnumber') {
980
+ return value
981
+ }
982
+
983
+ let formattedValue = value
984
+
985
+ let columnObj //= config.columns[columnName]
986
+ // config.columns not an array but a hash of objects
987
+ if (Object.keys(config.columns).length > 0) {
988
+ Object.keys(config.columns).forEach(function (key) {
989
+ var column = config.columns[key]
990
+ // add if not the index AND it is enabled to be added to data table
991
+ if (column.name === columnName) {
992
+ columnObj = column
993
+ }
994
+ })
995
+ }
996
+
997
+ if (columnObj === undefined) {
998
+ // then use left axis config
999
+ columnObj = config.type === 'chart' ? config.dataFormat : config.primary
1000
+ // NOTE: Left Value Axis uses different names
1001
+ // so map them below so the code below works
1002
+ // - copy commas to useCommas to work below
1003
+ columnObj['useCommas'] = columnObj.commas
1004
+ // - copy roundTo to roundToPlace to work below
1005
+ columnObj['roundToPlace'] = columnObj.roundTo ? columnObj.roundTo : ''
1006
+ }
1007
+
1008
+ if (columnObj) {
1009
+ // If value is a number, apply specific formattings
1010
+ let hasDecimal = false
1011
+ let decimalPoint = 0
1012
+ if (Number(value)) {
1013
+ if (columnObj.roundToPlace >= 0) {
1014
+ hasDecimal = columnObj.roundToPlace ? columnObj.roundToPlace !== '' || columnObj.roundToPlace !== null : false
1015
+ decimalPoint = columnObj.roundToPlace ? Number(columnObj.roundToPlace) : 0
1016
+
1017
+ // Rounding
1018
+ if (columnObj.hasOwnProperty('roundToPlace') && hasDecimal) {
1019
+ formattedValue = Number(value).toFixed(decimalPoint)
1020
+ }
1021
+ }
1022
+
1023
+ if (columnObj.hasOwnProperty('useCommas') && columnObj.useCommas === true) {
1024
+ // Formats number to string with commas - allows up to 5 decimal places, if rounding is not defined.
1025
+ // Otherwise, uses the rounding value set at 'columnObj.roundToPlace'.
1026
+ formattedValue = Number(value).toLocaleString('en-US', {
1027
+ style: 'decimal',
1028
+ minimumFractionDigits: hasDecimal ? decimalPoint : 0,
1029
+ maximumFractionDigits: hasDecimal ? decimalPoint : 5
1030
+ })
1031
+ }
1032
+ }
1033
+
1034
+ // add prefix and suffix if set
1035
+ formattedValue = (columnObj.prefix || '') + formattedValue + (columnObj.suffix || '')
1036
+ }
1037
+
1038
+ return formattedValue
1039
+ }
1040
+
1041
+ // this is passed DOWN into the various components
1042
+ // then they do a lookup based on the bin number as index into here (TT)
1043
+ const applyLegendToRow = rowObj => {
1044
+ try {
1045
+ if (!rowObj) throw new Error('COVE: No rowObj in applyLegendToRow')
1046
+ // Navigation map
1047
+ if ('navigation' === config.type) {
1048
+ let mapColorPalette = colorPalettes[config.color] || colorPalettes['bluegreenreverse']
1049
+ return generateColorsArray(mapColorPalette[3])
1050
+ }
1051
+
1052
+ let hash = hashObj(rowObj)
1053
+
1054
+ if (legendMemo.current.has(hash)) {
1055
+ let idx = legendMemo.current.get(hash)
1056
+ if (runtimeLegend[idx]?.disabled) return false
1057
+
1058
+ // DEV-784 changed to use bin prop to get color instead of idx
1059
+ // bc we re-order legend when showSpecialClassesLast is checked
1060
+ let legendBinColor = runtimeLegend.find(o => o.bin === idx)?.color
1061
+ return generateColorsArray(legendBinColor, runtimeLegend[idx]?.special)
1062
+ }
1063
+
1064
+ // Fail state
1065
+ return generateColorsArray()
1066
+ } catch (e) {
1067
+ console.error('COVE: ', e) // eslint-disable-line
1068
+ }
1069
+ }
1070
+
825
1071
  const clean = data => {
826
1072
  return config?.xAxis?.dataKey ? transform.cleanData(data, config.xAxis.dataKey) : data
827
1073
  }
828
1074
 
1075
+ // required for DataTable
1076
+ const displayGeoName = key => {
1077
+ return key
1078
+ }
1079
+
829
1080
  // Prevent render if loading
830
1081
  let body = <Loading />
831
1082
 
@@ -841,7 +1092,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
841
1092
  {!missingRequiredSections() && !config.newViz && (
842
1093
  <div className='cdc-chart-inner-container'>
843
1094
  {/* Title */}
844
-
845
1095
  {title && config.showTitle && (
846
1096
  <div role='heading' className={`chart-title ${config.theme} cove-component__header`} aria-level={2}>
847
1097
  {config && <sup className='superTitle'>{parse(config.superTitle || '')}</sup>}
@@ -892,7 +1142,36 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
892
1142
  </CoveMediaControls.Section>
893
1143
 
894
1144
  {/* Data Table */}
895
- {config.xAxis.dataKey && config.table.show && config.visualizationType !== 'Spark Line' && <DataTable />}
1145
+ {config.xAxis.dataKey && config.table.show && config.visualizationType !== 'Spark Line' && (
1146
+ <DataTable
1147
+ config={config}
1148
+ rawData={config.data}
1149
+ runtimeData={filteredData || excludedData}
1150
+ //navigationHandler={navigationHandler}
1151
+ expandDataTable={config.table.expanded}
1152
+ //headerColor={general.headerColor}
1153
+ columns={config.columns}
1154
+ showDownloadButton={config.general.showDownloadButton}
1155
+ runtimeLegend={dynamicLegendItems}
1156
+ displayDataAsText={displayDataAsText}
1157
+ displayGeoName={displayGeoName}
1158
+ applyLegendToRow={applyLegendToRow}
1159
+ tableTitle={config.table.label}
1160
+ indexTitle={config.table.indexLabel}
1161
+ vizTitle={title}
1162
+ viewport={currentViewport}
1163
+ parseDate={parseDate}
1164
+ formatDate={formatDate}
1165
+ formatNumber={formatNumber}
1166
+ tabbingId={handleChartTabbing}
1167
+ showDownloadImgButton={config.showDownloadImgButton}
1168
+ showDownloadPdfButton={config.showDownloadPdfButton}
1169
+ innerContainerRef={innerContainerRef}
1170
+ outerContainerRef={outerContainerRef}
1171
+ imageRef={imageId}
1172
+ isDebug={isDebug}
1173
+ />
1174
+ )}
896
1175
  {config?.footnotes && <section className='footnotes'>{parse(config.footnotes)}</section>}
897
1176
  {/* show pdf or image button */}
898
1177
  </div>
@@ -939,6 +1218,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
939
1218
  filterData,
940
1219
  imageId,
941
1220
  handleLineType,
1221
+ lineOptions,
942
1222
  isNumber,
943
1223
  getTextWidth,
944
1224
  twoColorPalette,
@@ -40,23 +40,21 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
40
40
  // Draw transparent bars over the chart to get tooltip data
41
41
  // Turn DEBUG on for additional context.
42
42
  if (!data) return
43
- let barThickness = xMax / data
43
+ let barThickness = xMax / data.length
44
44
  let barThicknessAdjusted = barThickness * (config.barThickness || 0.8)
45
45
  let offset = (barThickness * (1 - (config.barThickness || 0.8))) / 2
46
46
 
47
47
  // Tooltip helper for getting data to the closest date/category hovered.
48
48
  const getXValueFromCoordinate = x => {
49
- if (config.xAxis.type === 'categorical') {
49
+ if (config.xAxis.type === 'categorical' || config.visualizationType === 'Combo') {
50
50
  let eachBand = xScale.step()
51
51
  let numerator = x
52
52
  const index = Math.floor(Number(numerator) / eachBand)
53
53
  return xScale.domain()[index - 1] // fixes off by 1 error
54
54
  }
55
55
 
56
- if (config.xAxis.type === 'date') {
56
+ if (config.xAxis.type === 'date' && config.visualizationType !== 'Combo') {
57
57
  const bisectDate = bisector(d => parseDate(d[config.xAxis.dataKey])).left
58
- if (!x) return
59
- if (!xScale) return
60
58
  const x0 = xScale.invert(x)
61
59
  const index = bisectDate(config.data, x0, 1)
62
60
  const val = parseDate(config.data[index - 1][config.xAxis.dataKey])
@@ -123,8 +121,8 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
123
121
  return config.xAxis.type === 'date' ? xScale(parseDate(d[config.xAxis.dataKey])) : xScale(d[config.xAxis.dataKey])
124
122
  }
125
123
 
126
- const handleY = (d, index) => {
127
- return yScale(d[config.series[index].dataKey])
124
+ const handleY = (d, index, s = undefined) => {
125
+ return yScale(d[s.dataKey])
128
126
  }
129
127
 
130
128
  return (
@@ -132,21 +130,29 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
132
130
  <ErrorBoundary component='AreaChart'>
133
131
  <Group className='area-chart' key='area-wrapper' left={Number(config.yAxis.size)}>
134
132
  {(config.runtime.areaSeriesKeys || config.runtime.seriesKeys).map((s, index) => {
135
- let seriesColor = colorPalettesChart[config.palette][index]
133
+ let seriesData = data.map(d => {
134
+ return {
135
+ [config.xAxis.dataKey]: d[config.xAxis.dataKey],
136
+ [s.dataKey]: d[s.dataKey]
137
+ }
138
+ })
139
+
136
140
  let curveType = allCurves[s.lineType]
137
141
  let transparentArea = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(s.dataKey) === -1
138
142
  let displayArea = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(s.dataKey) !== -1
139
143
 
140
- data.map(d => xScale(parseDate(d[config.xAxis.dataKey])))
144
+ if (config.xAxis.type === 'date') {
145
+ data.map(d => xScale(parseDate(d[config.xAxis.dataKey])))
146
+ }
141
147
 
142
148
  return (
143
149
  <React.Fragment key={index}>
144
150
  {/* prettier-ignore */}
145
151
  <LinePath
146
- data={data}
152
+ data={seriesData}
147
153
  x={d => handleX(d)}
148
- y={d => yScale(d[config.series[index].dataKey])}
149
- stroke={displayArea ? seriesColor : 'transparent'}
154
+ y={d => handleY(d, index, s)}
155
+ stroke={displayArea ? colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[s.dataKey] : s.dataKey) : '#000' : 'transparent'}
150
156
  strokeWidth={2}
151
157
  strokeOpacity={1}
152
158
  shapeRendering='geometricPrecision'
@@ -159,11 +165,12 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
159
165
  key={'area-chart'}
160
166
  fill={ displayArea ? colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[s.dataKey] : s.dataKey) : '#000' : 'transparent'}
161
167
  fillOpacity={transparentArea ? 0.25 : 0.5}
162
- data={data} x={d => handleX(d)}
163
- y={d => handleY(d, index)}
168
+ data={seriesData}
169
+ x={d => handleX(d)}
170
+ y={d => handleY(d, index, s)}
164
171
  yScale={yScale}
165
172
  curve={curveType}
166
- strokeDasharray={s.type ? handleLineType(s.typ) : 0}
173
+ strokeDasharray={s.type ? handleLineType(s.type) : 0}
167
174
  />
168
175
 
169
176
  {/* Transparent bar for tooltips */}
@@ -178,7 +185,7 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
178
185
  />
179
186
 
180
187
  {/* circles that appear on hover */}
181
- {tooltipData && (
188
+ {tooltipData && Object.entries(tooltipData.data).length > 0 && (
182
189
  <circle
183
190
  cx={config.xAxis.type === 'categorical' ? xScale(tooltipData.data[config.xAxis.dataKey]) : xScale(parseDate(tooltipData.data[config.xAxis.dataKey]))}
184
191
  cy={yScale(tooltipData.data[s.dataKey])}
@@ -196,12 +203,12 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
196
203
  return (
197
204
  <Bar
198
205
  className='bar-here'
199
- x={Number(barThickness * index + offset)}
206
+ x={Number(barThickness * index)}
200
207
  y={d => Number(yScale(d[config.series[index].dataKey]))}
201
208
  yScale={yScale}
202
- width={barThicknessAdjusted}
209
+ width={Number(barThickness)}
203
210
  height={yMax}
204
- fill={'transparent'}
211
+ fill={DEBUG ? 'red' : 'transparent'}
205
212
  fillOpacity={1}
206
213
  style={{ stroke: 'black', strokeWidth: 2 }}
207
214
  onMouseMove={e => handleMouseOver(e, data)}
@@ -209,7 +216,7 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
209
216
  )
210
217
  })}
211
218
 
212
- {tooltipData && (
219
+ {tooltipData && Object.entries(tooltipData.data).length > 0 && (
213
220
  <TooltipInPortal key={Math.random()} top={tooltipData.dataYPosition + chartPosition?.top} left={tooltipData.dataXPosition + chartPosition?.left} style={defaultStyles}>
214
221
  <ul style={{ listStyle: 'none', paddingLeft: 'unset', fontFamily: 'sans-serif', margin: 'auto', lineHeight: '1rem' }} data-tooltip-id={tooltip_id}>
215
222
  {typeof tooltipData === 'object' &&