@cdc/chart 4.23.5 → 4.23.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.html CHANGED
@@ -34,22 +34,27 @@
34
34
  -->
35
35
 
36
36
  <!-- GENERIC CHART TYPES -->
37
- <div class="react-container" data-config="/examples/feature/filters/filter-testing.json"></div>
37
+ <!-- <div class="react-container" data-config="/examples/private/datatable-issue.json"></div> -->
38
+ <!-- <div class="react-container" data-config="/examples/feature/filters/filter-testing.json"></div> -->
38
39
  <!-- <div class="react-container" data-config="/examples/feature/pie/planet-pie-example-config.json"></div> -->
39
40
  <!-- <div class="react-container" data-config="/examples/feature/line/line-chart.json"></div> -->
40
- <!-- <div class="react-container" data-config="/examples/feature/test-highlight/test-highlight.json"></div> -->
41
- <!-- <div class="react-container" data-config="/examples/feature/test-highlight/test-highlight-vertical.json"></div> -->
42
- <!-- <div class="react-container" data-config="/examples/feature/area/area-chart-category.json"></div> -->
41
+ <div class="react-container" data-config="/examples/feature/forecasting/index.json"></div>
42
+ <!-- <div class="react-container" data-config="/examples/feature/forecasting/forecasting.json"></div> -->
43
+ <!-- <div class="react-container" data-config="/examples/feature/forecasting/combo-forecasting.json"></div> -->
44
+ <!-- <div class="react-container" data-config="/examples/feature/forecasting/effective_reproduction.json"></div> -->
43
45
  <!-- <div class="react-container" data-config="/examples/feature/area/area-chart-date.json"></div> -->
46
+ <!-- <div class="react-container" data-config="/examples/feature/area/area-chart-category.json"></div> -->
44
47
  <!-- <div class="react-container" data-config="/examples/feature/scatterplot/scatterplot.json"></div> -->
45
48
  <!-- <div class="react-container" data-config="/examples/feature/deviation/planet-deviation-config.json"></div> -->
46
49
  <!-- <div class="react-container" data-config="/examples/feature/boxplot/boxplot.json"></div> -->
47
50
  <!-- <div class="react-container" data-config="/examples/feature/combo/planet-combo-example-config.json"></div> -->
51
+ <!-- <div class="react-container" data-config="/examples/feature/combo/right-issues.json"></div> -->
48
52
  <!-- <div class="react-container" data-config="/examples/feature/paired-bar/paired-bar-example.json"></div> -->
49
53
 
50
54
  <!-- BAR -->
51
55
  <!-- <div class="react-container" data-config="/examples/feature/bar/planet-example-config.json"></div> -->
52
56
  <!-- <div class="react-container" data-config="/examples/feature/bar/planet-chart-horizontal-example-config.json"></div> -->
57
+ <!-- <div class="react-container" data-config="/examples/feature/bar/new.json"></div> -->
53
58
  <!-- <div class="react-container" data-config="/examples/feature/bar/example-bar-chart.json"></div> -->
54
59
  <!-- <div class="react-container" data-config="/examples/feature/bar/horizontal-chart-max-increase.json"></div> -->
55
60
  <!-- <div class="react-container" data-config="/examples/feature/bar/horizontal-chart.json"></div> -->
@@ -60,11 +65,11 @@
60
65
  <!-- <div class="react-container" data-config="/examples/feature/sparkline/example-sparkline.json"></div> -->
61
66
 
62
67
  <!-- TESTS DATE EXCLUSIONS -->
63
- <!-- <div class="react-container" data-config="/examples/feature/tests-date-exclusions/date-exclusions-config.json"></div> -->
68
+ <div class="react-container" data-config="/examples/feature/tests-date-exclusions/date-exclusions-config.json"></div>
64
69
  <!-- <div class="react-container" data-config="/examples/feature/tests-case-rate/case-rate-example-config.json"></div> -->
65
70
 
66
71
  <!-- TESTS BIG SMALL-->
67
- <!-- <div class="react-container" data-config="/examples/feature/tests-big-small/big-small-test-line.json"></div> -->
72
+ <!-- <div class="react-container" data-config="/examples/feature/tests-big-small/big-small-test-line.json"></div> -->
68
73
  <!-- <div class="react-container" data-config="/examples/feature/tests-big-small/big-small-test-bar.json"></div> -->
69
74
  <!-- <div class="react-container" data-config="/examples/feature/tests-big-small/big-small-test-negative.json"></div> -->
70
75
  <!-- <div class="react-container" data-config="/examples/feature/tests-big-small/line-chart-max-increase.json"></div> -->
@@ -74,7 +79,7 @@
74
79
  <!-- <div class="react-container" data-config="/examples/feature/tests-non-numerics/example-combo-bar-nonnumeric.json"></div> -->
75
80
  <!-- <div class="react-container" data-config="/examples/feature/tests-non-numerics/example-bar-chart-nonnumeric.json"></div> -->
76
81
  <!-- <div class="react-container" data-config="/examples/feature/tests-non-numerics/sparkline-chart-nonnumeric.json"></div> -->
77
- <div class="react-container" data-config="/examples/feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json"></div>
82
+ <!-- <div class="react-container" data-config="/examples/feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json"></div> -->
78
83
 
79
84
  <!-- TESTS CUTOFF -->
80
85
  <!-- <div class="react-container" data-config="/examples/feature/tests-cutoff/cutoff-example-config.json"></div> -->
@@ -100,7 +105,8 @@
100
105
 
101
106
  <!-- VERTICAL BAR CHARTS -->
102
107
  <!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/combo-line-chart.json"></div> -->
103
- <!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json"></div> -->
108
+ <!-- <div class="react-container" data-config="/examples/private/problem.json"></div> -->
109
+ <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json"></div>
104
110
  <!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json"></div> -->
105
111
  <!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-confidence.json"></div> -->
106
112
  <!-- <div class="react-container" data-config="/examples/gallery/bar-chart-vertical/vertical-bar-chart-confidence.json"></div> -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdc/chart",
3
- "version": "4.23.5",
3
+ "version": "4.23.7",
4
4
  "description": "React component for visualizing tabular data in various types of charts",
5
5
  "moduleName": "CdcChart",
6
6
  "main": "dist/cdcchart",
@@ -57,7 +57,7 @@
57
57
  "react": "^18.2.0",
58
58
  "react-dom": "^18.2.0"
59
59
  },
60
- "gitHead": "34add3436994ca3cf13e51f313add4d70377f53e",
60
+ "gitHead": "6c7ac5215dcf3bc1cc7d199089c8c2e75f53a93e",
61
61
  "devDependencies": {
62
62
  "resize-observer-polyfill": "^1.5.1"
63
63
  }
package/src/CdcChart.jsx CHANGED
@@ -31,7 +31,7 @@ import defaults from './data/initial-state'
31
31
  import EditorPanel from './components/EditorPanel'
32
32
  import Loading from '@cdc/core/components/Loading'
33
33
  import Filters from '@cdc/core/components/Filters'
34
- import CoveMediaControls from '@cdc/core/components/CoveMediaControls'
34
+ import MediaControls from '@cdc/core/components/MediaControls'
35
35
 
36
36
  // Helpers
37
37
  import numberFromString from '@cdc/core/helpers/numberFromString'
@@ -39,6 +39,7 @@ import getViewport from '@cdc/core/helpers/getViewport'
39
39
  import { DataTransform } from '@cdc/core/helpers/DataTransform'
40
40
  import cacheBustingString from '@cdc/core/helpers/cacheBustingString'
41
41
  import isNumber from '@cdc/core/helpers/isNumber'
42
+ import coveUpdateWorker from '@cdc/core/helpers/coveUpdateWorker'
42
43
 
43
44
  import './scss/main.scss'
44
45
  // load both then config below determines which to use
@@ -72,7 +73,10 @@ const hashObj = row => {
72
73
  }
73
74
  }
74
75
 
75
- export default function CdcChart({ configUrl, config: configObj, isEditor = false, isDebug = false, isDashboard = false, setConfig: setParentConfig, setEditing, hostname, link }) {
76
+ // * FILE REVIEW
77
+ // TODO: @tturnerswdev33 - remove/fix mentions of runtimeLegend that were added
78
+
79
+ export default function CdcChart({ configUrl, config: configObj, isEditor = false, isDebug = false, isDashboard = false, setConfig: setParentConfig, setEditing, hostname, link, setSharedFilter, setSharedFilterValue, dashboardConfig }) {
76
80
  const transform = new DataTransform()
77
81
  const [loading, setLoading] = useState(true)
78
82
  const [colorScale, setColorScale] = useState(null)
@@ -261,10 +265,20 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
261
265
  data = await fetch(response.dataUrl + `?v=${cacheBustingString()}`)
262
266
  .then(response => response.text())
263
267
  .then(responseText => {
268
+ // for every comma NOT inside quotes, replace with a pipe delimiter
269
+ // - this will let commas inside the quotes not be parsed as a new column
270
+ // - Limitation: if a delimiter other than comma is used in the csv this will break
271
+ // Examples of other delimiters that would break: tab
272
+ responseText = responseText.replace(/(".*?")|,/g, (...m) => m[1] || '|')
273
+ // now strip the double quotes
274
+ responseText = responseText.replace(/["]+/g, '')
264
275
  const parsedCsv = Papa.parse(responseText, {
276
+ //quotes: "true", // dont need these
277
+ //quoteChar: "'", // has no effect that I can tell
265
278
  header: true,
266
279
  dynamicTyping: true,
267
- skipEmptyLines: true
280
+ skipEmptyLines: true,
281
+ delimiter: '|' // we are using pipe symbol as delimiter so setting this explicitly for Papa.parse
268
282
  })
269
283
  return parsedCsv.data
270
284
  })
@@ -302,7 +316,13 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
302
316
  }
303
317
  if (undefined === newConfig.table.show) newConfig.table.show = !isDashboard
304
318
 
305
- updateConfig(newConfig, data)
319
+ newConfig.series.map(series => {
320
+ if (!series.tooltip) series.tooltip = true
321
+ })
322
+
323
+ const processedConfig = { ...(await coveUpdateWorker(newConfig)) }
324
+
325
+ updateConfig(processedConfig, data)
306
326
  }
307
327
 
308
328
  const updateConfig = (newConfig, dataOverride = undefined) => {
@@ -315,7 +335,6 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
315
335
  }
316
336
  })
317
337
 
318
- // Loop through and set initial data with exclusions - this should persist through any following data transformations (ie. filters)
319
338
  let newExcludedData
320
339
 
321
340
  if (newConfig.exclusions && newConfig.exclusions.active) {
@@ -378,7 +397,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
378
397
  newConfig.runtime.seriesKeys = newConfig.series
379
398
  ? newConfig.series.map(series => {
380
399
  newConfig.runtime.seriesLabels[series.dataKey] = series.label || series.dataKey
381
- newConfig.runtime.seriesLabelsAll.push(series.label || series.dataKey)
400
+ newConfig.runtime.seriesLabelsAll.push(series.name || series.label || series.dataKey)
382
401
  return series.dataKey
383
402
  })
384
403
  : []
@@ -512,11 +531,15 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
512
531
  newConfig.runtime.barSeriesKeys = []
513
532
  newConfig.runtime.lineSeriesKeys = []
514
533
  newConfig.runtime.areaSeriesKeys = []
534
+ newConfig.runtime.forecastingSeriesKeys = []
515
535
 
516
536
  newConfig.series.forEach(series => {
517
537
  if (series.type === 'Area Chart') {
518
538
  newConfig.runtime.areaSeriesKeys.push(series)
519
539
  }
540
+ if (series.type === 'Forecasting') {
541
+ newConfig.runtime.forecastingSeriesKeys.push(series)
542
+ }
520
543
  if (series.type === 'Bar') {
521
544
  newConfig.runtime.barSeriesKeys.push(series.dataKey)
522
545
  }
@@ -525,6 +548,17 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
525
548
  }
526
549
  })
527
550
  }
551
+
552
+ if (newConfig.visualizationType === 'Forecasting' && newConfig.series) {
553
+ newConfig.runtime.forecastingSeriesKeys = []
554
+
555
+ newConfig.series.forEach(series => {
556
+ if (series.type === 'Forecasting') {
557
+ newConfig.runtime.forecastingSeriesKeys.push(series)
558
+ }
559
+ })
560
+ }
561
+
528
562
  if (newConfig.visualizationType === 'Area Chart' && newConfig.series) {
529
563
  newConfig.runtime.areaSeriesKeys = []
530
564
 
@@ -535,10 +569,11 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
535
569
  })
536
570
  }
537
571
 
538
- if (((newConfig.visualizationType === 'Bar' || newConfig.visualizationType === 'Deviation Bar') && newConfig.orientation === 'horizontal') || newConfig.visualizationType === 'Paired Bar') {
572
+ if ((newConfig.visualizationType === 'Bar' && newConfig.orientation === 'horizontal') || ['Deviation Bar', 'Paired Bar'].includes(newConfig.visualizationType)) {
539
573
  newConfig.runtime.xAxis = newConfig.yAxis
540
574
  newConfig.runtime.yAxis = newConfig.xAxis
541
575
  newConfig.runtime.horizontal = true
576
+ newConfig.orientation = 'horizontal'
542
577
  } else {
543
578
  newConfig.runtime.xAxis = newConfig.xAxis
544
579
  newConfig.runtime.yAxis = newConfig.yAxis
@@ -740,7 +775,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
740
775
  const newSeriesHighlight = []
741
776
 
742
777
  // If we're highlighting all the series, reset them
743
- if (seriesHighlight.length + 1 === config.runtime.seriesKeys.length && !config.legend.dynamicLegend) {
778
+ if (seriesHighlight.length + 1 === config.runtime.seriesKeys.length && !config.legend.dynamicLegend && config.visualizationType !== 'Forecasting') {
744
779
  highlightReset()
745
780
  return
746
781
  }
@@ -764,6 +799,21 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
764
799
  } else {
765
800
  newSeriesHighlight.push(newHighlight)
766
801
  }
802
+
803
+ /**
804
+ * pushDataKeyBySeriesName
805
+ * - pushes series.dataKey into the series highlight based on the found series.name
806
+ * @param {String} value
807
+ */
808
+ const pushDataKeyBySeriesName = value => {
809
+ let matchingSeries = config.series.filter(series => series.name === value.text)
810
+ if (matchingSeries?.length > 0) {
811
+ newSeriesHighlight.push(matchingSeries[0].dataKey)
812
+ }
813
+ }
814
+
815
+ pushDataKeyBySeriesName(label)
816
+
767
817
  setSeriesHighlight(newSeriesHighlight)
768
818
  }
769
819
 
@@ -822,8 +872,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
822
872
  return num + unit
823
873
  }
824
874
 
825
- // Format numeric data based on settings in config
826
- const formatNumber = (num, axis, shouldAbbreviate = false) => {
875
+ // Format numeric data based on settings in config OR from passed in settings for Additional Columns
876
+ const formatNumber = (num, axis, shouldAbbreviate = false, addColPrefix, addColSuffix, addColRoundTo) => {
827
877
  // if num is NaN return num
828
878
  if (isNaN(num) || !num) return num
829
879
  // Check if the input number is negative
@@ -845,10 +895,17 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
845
895
  let original = num
846
896
  let stringFormattingOptions
847
897
  if (axis === 'left') {
898
+ let roundToPlace
899
+ if (addColRoundTo !== undefined) {
900
+ // if its an Additional Column
901
+ roundToPlace = addColRoundTo ? Number(addColRoundTo) : 0
902
+ } else {
903
+ roundToPlace = roundTo ? Number(roundTo) : 0
904
+ }
848
905
  stringFormattingOptions = {
849
- useGrouping: config.dataFormat.commas ? true : false,
850
- minimumFractionDigits: roundTo ? Number(roundTo) : 0,
851
- maximumFractionDigits: roundTo ? Number(roundTo) : 0
906
+ useGrouping: addColRoundTo ? true : config.dataFormat.commas ? true : false,
907
+ minimumFractionDigits: roundToPlace,
908
+ maximumFractionDigits: roundToPlace
852
909
  }
853
910
  }
854
911
 
@@ -907,8 +964,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
907
964
  num = abbreviateNumber(parseFloat(num))
908
965
  }
909
966
 
910
- if (prefix && axis === 'left') {
911
- result += prefix
967
+ if (addColPrefix && axis === 'left') {
968
+ result = addColPrefix + result
969
+ } else {
970
+ if (prefix && axis === 'left') {
971
+ result = prefix + result
972
+ }
912
973
  }
913
974
 
914
975
  if (rightPrefix && axis === 'right') {
@@ -921,8 +982,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
921
982
 
922
983
  result += num
923
984
 
924
- if (suffix && axis === 'left') {
925
- result += suffix
985
+ if (addColSuffix && axis === 'left') {
986
+ result += addColSuffix
987
+ } else {
988
+ if (suffix && axis === 'left') {
989
+ result += suffix
990
+ }
926
991
  }
927
992
 
928
993
  if (rightSuffix && axis === 'right') {
@@ -942,6 +1007,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
942
1007
  // Select appropriate chart type
943
1008
  const chartComponents = {
944
1009
  'Paired Bar': <LinearChart />,
1010
+ Forecasting: <LinearChart />,
945
1011
  Bar: <LinearChart />,
946
1012
  Line: <LinearChart />,
947
1013
  Combo: <LinearChart />,
@@ -953,6 +1019,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
953
1019
  }
954
1020
 
955
1021
  const missingRequiredSections = () => {
1022
+ if (config.visualizationType === 'Forecasting') return false // skip required checks for now.
956
1023
  if (config.visualizationType === 'Pie') {
957
1024
  if (undefined === config?.yAxis.dataKey) {
958
1025
  return true
@@ -1069,6 +1136,8 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1069
1136
  }
1070
1137
 
1071
1138
  const clean = data => {
1139
+ // cleaning is deleting data we need in forecasting charts.
1140
+ if (config.visualizationType === 'Forecasting') return data
1072
1141
  return config?.xAxis?.dataKey ? transform.cleanData(data, config.xAxis.dataKey) : data
1073
1142
  }
1074
1143
 
@@ -1107,10 +1176,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1107
1176
  {config?.introText && <section className='introText'>{parse(config.introText)}</section>}
1108
1177
  <div
1109
1178
  style={{ marginBottom: config.legend.position !== 'bottom' && config.orientation === 'horizontal' ? `${config.runtime.xAxis.size}px` : '0px' }}
1110
- className={`chart-container ${config.legend.position === 'bottom' ? 'bottom' : ''}${config.legend.hide ? ' legend-hidden' : ''}${lineDatapointClass}${barBorderClass} ${contentClasses.join(' ')}`}
1179
+ className={`chart-container p-relative ${config.legend.position === 'bottom' ? 'bottom' : ''}${config.legend.hide ? ' legend-hidden' : ''}${lineDatapointClass}${barBorderClass} ${contentClasses.join(' ')}`}
1111
1180
  >
1112
1181
  {/* All charts except sparkline */}
1113
- {config.visualizationType !== 'Spark Line' && chartComponents[visualizationType]}
1182
+ {config.visualizationType !== 'Spark Line' && chartComponents[config.visualizationType]}
1114
1183
 
1115
1184
  {/* Sparkline */}
1116
1185
  {config.visualizationType === 'Spark Line' && (
@@ -1136,10 +1205,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1136
1205
  {description && config.visualizationType !== 'Spark Line' && <div className='subtext'>{parse(description)}</div>}
1137
1206
 
1138
1207
  {/* buttons */}
1139
- <CoveMediaControls.Section classes={['download-buttons']}>
1140
- {config.table.showDownloadImgButton && <CoveMediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={config} elementToCapture={imageId} />}
1141
- {config.table.showDownloadPdfButton && <CoveMediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={config} elementToCapture={imageId} />}
1142
- </CoveMediaControls.Section>
1208
+ <MediaControls.Section classes={['download-buttons']}>
1209
+ {config.table.showDownloadImgButton && <MediaControls.Button text='Download Image' title='Download Chart as Image' type='image' state={config} elementToCapture={imageId} />}
1210
+ {config.table.showDownloadPdfButton && <MediaControls.Button text='Download PDF' title='Download Chart as PDF' type='pdf' state={config} elementToCapture={imageId} />}
1211
+ </MediaControls.Section>
1143
1212
 
1144
1213
  {/* Data Table */}
1145
1214
  {config.xAxis.dataKey && config.table.show && config.visualizationType !== 'Spark Line' && (
@@ -1147,9 +1216,9 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1147
1216
  config={config}
1148
1217
  rawData={config.data}
1149
1218
  runtimeData={filteredData || excludedData}
1150
- //navigationHandler={navigationHandler}
1219
+ //navigationHandler={navigationHandler} // do we need this? What does it do?
1151
1220
  expandDataTable={config.table.expanded}
1152
- //headerColor={general.headerColor}
1221
+ //headerColor={general.headerColor} // have this in map but not chart
1153
1222
  columns={config.columns}
1154
1223
  showDownloadButton={config.general.showDownloadButton}
1155
1224
  runtimeLegend={dynamicLegendItems}
@@ -1170,6 +1239,7 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1170
1239
  outerContainerRef={outerContainerRef}
1171
1240
  imageRef={imageId}
1172
1241
  isDebug={isDebug}
1242
+ isEditor={isEditor}
1173
1243
  />
1174
1244
  )}
1175
1245
  {config?.footnotes && <section className='footnotes'>{parse(config.footnotes)}</section>}
@@ -1183,7 +1253,12 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1183
1253
  const getXAxisData = d => (config.runtime.xAxis.type === 'date' ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
1184
1254
  const getYAxisData = (d, seriesKey) => d[seriesKey]
1185
1255
 
1256
+ const capitalize = str => {
1257
+ return str.charAt(0).toUpperCase() + str.slice(1)
1258
+ }
1259
+
1186
1260
  const contextValues = {
1261
+ capitalize,
1187
1262
  getXAxisData,
1188
1263
  getYAxisData,
1189
1264
  config,
@@ -1222,7 +1297,10 @@ export default function CdcChart({ configUrl, config: configObj, isEditor = fals
1222
1297
  isNumber,
1223
1298
  getTextWidth,
1224
1299
  twoColorPalette,
1225
- isDebug
1300
+ isDebug,
1301
+ setSharedFilter,
1302
+ setSharedFilterValue,
1303
+ dashboardConfig
1226
1304
  }
1227
1305
 
1228
1306
  const classes = ['cdc-open-viz-module', 'type-chart', `${currentViewport}`, `font-${config.fontSize}`, `${config.theme}`]
@@ -1,124 +1,27 @@
1
- import React, { useContext, useEffect, useState } from 'react'
1
+ import React, { useContext, memo } from 'react'
2
2
 
3
3
  // cdc
4
4
  import ConfigContext from '../ConfigContext'
5
5
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
6
- import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
7
6
 
8
7
  // visx & d3
9
8
  import * as allCurves from '@visx/curve'
10
9
  import { AreaClosed, LinePath, Bar } from '@visx/shape'
11
10
  import { Group } from '@visx/group'
12
- import { useTooltip, useTooltipInPortal, defaultStyles, Tooltip } from '@visx/tooltip'
13
- import { localPoint } from '@visx/event'
14
- import { bisector } from 'd3-array'
15
11
 
16
- const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
12
+ const AreaChart = ({ xScale, yScale, yMax, xMax, chartRef, handleTooltipMouseOver, handleTooltipMouseOff, tooltipData }) => {
17
13
  // enable various console logs in the file
18
14
  const DEBUG = false
19
- const [chartPosition, setChartPosition] = useState(null)
20
-
21
- useEffect(() => {
22
- setChartPosition(chartRef.current.getBoundingClientRect())
23
- }, [chartRef])
24
15
 
25
16
  // import data from context
26
- const { transformedData: data, config, handleLineType, parseDate, formatDate, formatNumber, seriesHighlight, colorScale } = useContext(ConfigContext)
27
- const tooltip_id = `cdc-open-viz-tooltip-${config.runtime.uniqueId}`
28
-
29
- // import tooltip helpers
30
- const { tooltipData, showTooltip } = useTooltip()
31
-
32
- // here we're inside of the svg,
33
- // it appears we need to use TooltipInPortal.
34
- const { TooltipInPortal } = useTooltipInPortal({
35
- detectBounds: true,
36
- // when tooltip containers are scrolled, this will correctly update the Tooltip position
37
- scroll: true
38
- })
17
+ const { transformedData: data, config, handleLineType, parseDate, seriesHighlight, colorScale, rawData } = useContext(ConfigContext)
39
18
 
40
19
  // Draw transparent bars over the chart to get tooltip data
41
20
  // Turn DEBUG on for additional context.
42
21
  if (!data) return
43
- let barThickness = xMax / data.length
44
- let barThicknessAdjusted = barThickness * (config.barThickness || 0.8)
45
- let offset = (barThickness * (1 - (config.barThickness || 0.8))) / 2
46
-
47
- // Tooltip helper for getting data to the closest date/category hovered.
48
- const getXValueFromCoordinate = x => {
49
- if (config.xAxis.type === 'categorical' || config.visualizationType === 'Combo') {
50
- let eachBand = xScale.step()
51
- let numerator = x
52
- const index = Math.floor(Number(numerator) / eachBand)
53
- return xScale.domain()[index - 1] // fixes off by 1 error
54
- }
55
-
56
- if (config.xAxis.type === 'date' && config.visualizationType !== 'Combo') {
57
- const bisectDate = bisector(d => parseDate(d[config.xAxis.dataKey])).left
58
- const x0 = xScale.invert(x)
59
- const index = bisectDate(config.data, x0, 1)
60
- const val = parseDate(config.data[index - 1][config.xAxis.dataKey])
61
- return val
62
- }
63
- }
64
-
65
- const handleMouseOver = (e, data) => {
66
- // get the svg coordinates of the mouse
67
- // and get the closest values
68
- const eventSvgCoords = localPoint(e)
69
- const { x, y } = eventSvgCoords
70
-
71
- let closestXScaleValue = getXValueFromCoordinate(x)
72
- let formattedDate = formatDate(closestXScaleValue)
73
-
74
- let yScaleValues
75
- if (config.xAxis.type === 'categorical') {
76
- yScaleValues = data.filter(d => d[config.xAxis.dataKey] === closestXScaleValue)
77
- } else {
78
- yScaleValues = data.filter(d => formatDate(parseDate(d[config.xAxis.dataKey])) === formattedDate)
79
- }
80
-
81
- let seriesToInclude = []
82
- let yScaleMaxValues = []
83
- let itemsToLoop = [config.runtime.xAxis.dataKey, ...config.runtime.seriesKeys]
84
-
85
- itemsToLoop.map(seriesKey => {
86
- if (!seriesKey) return
87
- if (!yScaleValues[0]) return
88
- for (const item of Object.entries(yScaleValues[0])) {
89
- if (item[0] === seriesKey) {
90
- seriesToInclude.push(item)
91
- }
92
- }
93
- })
94
-
95
- // filter out the series that aren't added to the map.
96
- seriesToInclude.map(series => yScaleMaxValues.push(Number(yScaleValues[0][series])))
97
- if (!seriesToInclude) return
98
- let tooltipDataFromSeries = Object.fromEntries(seriesToInclude) ? Object.fromEntries(seriesToInclude) : {}
99
-
100
- let tooltipData = {}
101
- tooltipData.data = tooltipDataFromSeries
102
- tooltipData.dataXPosition = x + 20
103
- tooltipData.dataYPosition = y - 100
104
-
105
- let tooltipInformation = {
106
- tooltipData: tooltipData,
107
- tooltipTop: 0,
108
- tooltipValues: yScaleValues,
109
- tooltipLeft: x
110
- }
111
-
112
- showTooltip(tooltipInformation)
113
- }
114
-
115
- const TooltipListItem = ({ item }) => {
116
- const [label, value] = item
117
- return label === config.xAxis.dataKey ? `${label}: ${value}` : `${label}: ${formatNumber(value, 'left')}`
118
- }
119
22
 
120
23
  const handleX = d => {
121
- return config.xAxis.type === 'date' ? xScale(parseDate(d[config.xAxis.dataKey])) : xScale(d[config.xAxis.dataKey])
24
+ return config.xAxis.type === 'date' ? xScale(parseDate(d[config.xAxis.dataKey], false)) : xScale(d[config.xAxis.dataKey])
122
25
  }
123
26
 
124
27
  const handleY = (d, index, s = undefined) => {
@@ -143,6 +46,8 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
143
46
 
144
47
  if (config.xAxis.type === 'date') {
145
48
  data.map(d => xScale(parseDate(d[config.xAxis.dataKey])))
49
+ } else {
50
+ data.map(d => xScale(d[config.xAxis.dataKey]))
146
51
  }
147
52
 
148
53
  return (
@@ -171,70 +76,39 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
171
76
  yScale={yScale}
172
77
  curve={curveType}
173
78
  strokeDasharray={s.type ? handleLineType(s.type) : 0}
174
- />
175
-
176
- {/* Transparent bar for tooltips */}
177
- {/* prettier-ignore */}
178
- <Bar
179
- width={ Number(xMax)}
180
- height={ Number(yMax)}
181
- fill={DEBUG ? 'red' : 'transparent'}
182
- fillOpacity={0.05}
183
- style={DEBUG ? { stroke: 'black', strokeWidth: 2 } : {}}
184
- onMouseMove={e => handleMouseOver(e, data)}
185
- />
79
+ />
186
80
 
187
81
  {/* circles that appear on hover */}
188
- {tooltipData && Object.entries(tooltipData.data).length > 0 && (
82
+ {/* {tooltipData && Object.entries(tooltipData.data).length > 0 && (
189
83
  <circle
190
84
  cx={config.xAxis.type === 'categorical' ? xScale(tooltipData.data[config.xAxis.dataKey]) : xScale(parseDate(tooltipData.data[config.xAxis.dataKey]))}
191
- cy={yScale(tooltipData.data[s.dataKey])}
85
+ cy={yScale(tooltipData.data[index][1])}
192
86
  r={4.5}
193
87
  opacity={1}
194
88
  fillOpacity={1}
195
89
  fill={displayArea ? (colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[s.dataKey] : s.dataKey) : '#000') : 'transparent'}
196
90
  style={{ filter: 'unset', opacity: 1 }}
197
91
  />
198
- )}
199
-
200
- {/* another tool for showing bars during debug mode. */}
201
- {DEBUG &&
202
- data.map((item, index) => {
203
- return (
204
- <Bar
205
- className='bar-here'
206
- x={Number(barThickness * index)}
207
- y={d => Number(yScale(d[config.series[index].dataKey]))}
208
- yScale={yScale}
209
- width={Number(barThickness)}
210
- height={yMax}
211
- fill={DEBUG ? 'red' : 'transparent'}
212
- fillOpacity={1}
213
- style={{ stroke: 'black', strokeWidth: 2 }}
214
- onMouseMove={e => handleMouseOver(e, data)}
215
- />
216
- )
217
- })}
218
-
219
- {tooltipData && Object.entries(tooltipData.data).length > 0 && (
220
- <TooltipInPortal key={Math.random()} top={tooltipData.dataYPosition + chartPosition?.top} left={tooltipData.dataXPosition + chartPosition?.left} style={defaultStyles}>
221
- <ul style={{ listStyle: 'none', paddingLeft: 'unset', fontFamily: 'sans-serif', margin: 'auto', lineHeight: '1rem' }} data-tooltip-id={tooltip_id}>
222
- {typeof tooltipData === 'object' &&
223
- Object.entries(tooltipData.data).map(item => (
224
- <li style={{ padding: '2.5px 0' }}>
225
- <TooltipListItem item={item} />
226
- </li>
227
- ))}
228
- </ul>
229
- </TooltipInPortal>
230
- )}
92
+ )} */}
231
93
  </React.Fragment>
232
94
  )
233
95
  })}
96
+
97
+ {/* Transparent bar for tooltips */}
98
+ {/* prettier-ignore */}
99
+ <Bar
100
+ width={ Number(xMax)}
101
+ height={ Number(yMax)}
102
+ fill={DEBUG ? 'red' : 'transparent'}
103
+ fillOpacity={0.05}
104
+ style={DEBUG ? { stroke: 'black', strokeWidth: 2 } : {}}
105
+ onMouseMove={e => handleTooltipMouseOver(e, rawData)}
106
+ onMouseLeave={ handleTooltipMouseOff }
107
+ />
234
108
  </Group>
235
109
  </ErrorBoundary>
236
110
  )
237
111
  )
238
112
  }
239
113
 
240
- export default CoveAreaChart
114
+ export default memo(AreaChart)