@cdc/chart 4.23.4 → 4.23.6

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 (42) hide show
  1. package/dist/cdcchart.js +54845 -51755
  2. package/examples/feature/__data__/planet-example-data.json +14 -32
  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/new.json +561 -0
  7. package/examples/feature/bar/planet-chart-logaritmic-config.json +170 -0
  8. package/examples/feature/boxplot/valid-boxplot.csv +17 -0
  9. package/examples/feature/combo/right-issues.json +190 -0
  10. package/examples/feature/filters/filter-testing.json +37 -3
  11. package/examples/feature/forecasting/combo-forecasting.json +245 -0
  12. package/examples/feature/forecasting/forecasting.json +5325 -0
  13. package/examples/feature/forecasting/index.json +203 -0
  14. package/examples/feature/forecasting/random_data.csv +366 -0
  15. package/examples/feature/line/line-chart.json +3 -3
  16. package/examples/feature/test-highlight/test-highlight-2.json +789 -0
  17. package/examples/feature/test-highlight/test-highlight-vertical.json +561 -0
  18. package/examples/feature/test-highlight/test-highlight.json +100 -0
  19. package/examples/feature/tests-non-numerics/stacked-vertical-bar-example-nonnumerics.json +1 -2
  20. package/examples/gallery/bar-chart-horizontal/horizontal-highlight.json +345 -0
  21. package/examples/gallery/line/line.json +173 -1
  22. package/index.html +14 -8
  23. package/package.json +2 -2
  24. package/src/CdcChart.jsx +342 -25
  25. package/src/components/AreaChart.jsx +32 -40
  26. package/src/components/BarChart.jsx +147 -25
  27. package/src/components/DataTable.jsx +30 -12
  28. package/src/components/DeviationBar.jsx +32 -32
  29. package/src/components/EditorPanel.jsx +1902 -1126
  30. package/src/components/Forecasting.jsx +147 -0
  31. package/src/components/Legend.jsx +193 -243
  32. package/src/components/LineChart.jsx +4 -9
  33. package/src/components/LinearChart.jsx +263 -285
  34. package/src/components/Series.jsx +518 -0
  35. package/src/components/SparkLine.jsx +3 -3
  36. package/src/data/initial-state.js +24 -5
  37. package/src/hooks/useHighlightedBars.js +154 -0
  38. package/src/hooks/useMinMax.js +128 -0
  39. package/src/hooks/useReduceData.js +31 -57
  40. package/src/hooks/useRightAxis.js +8 -2
  41. package/src/hooks/useScales.js +196 -0
  42. /package/examples/feature/area/{area-chart.json → area-chart-date.json} +0 -0
@@ -27,7 +27,7 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
27
27
  const tooltip_id = `cdc-open-viz-tooltip-${config.runtime.uniqueId}`
28
28
 
29
29
  // import tooltip helpers
30
- const { tooltipData, showTooltip } = useTooltip()
30
+ const { tooltipData, showTooltip, hideTooltip } = useTooltip()
31
31
 
32
32
  // here we're inside of the svg,
33
33
  // it appears we need to use TooltipInPortal.
@@ -40,23 +40,19 @@ 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
44
- let barThicknessAdjusted = barThickness * (config.barThickness || 0.8)
45
- let offset = (barThickness * (1 - (config.barThickness || 0.8))) / 2
43
+ let barThickness = xMax / data.length
46
44
 
47
45
  // Tooltip helper for getting data to the closest date/category hovered.
48
46
  const getXValueFromCoordinate = x => {
49
- if (config.xAxis.type === 'categorical') {
47
+ if (config.xAxis.type === 'categorical' || config.visualizationType === 'Combo') {
50
48
  let eachBand = xScale.step()
51
49
  let numerator = x
52
50
  const index = Math.floor(Number(numerator) / eachBand)
53
51
  return xScale.domain()[index - 1] // fixes off by 1 error
54
52
  }
55
53
 
56
- if (config.xAxis.type === 'date') {
54
+ if (config.xAxis.type === 'date' && config.visualizationType !== 'Combo') {
57
55
  const bisectDate = bisector(d => parseDate(d[config.xAxis.dataKey])).left
58
- if (!x) return
59
- if (!xScale) return
60
56
  const x0 = xScale.invert(x)
61
57
  const index = bisectDate(config.data, x0, 1)
62
58
  const val = parseDate(config.data[index - 1][config.xAxis.dataKey])
@@ -89,6 +85,9 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
89
85
  if (!yScaleValues[0]) return
90
86
  for (const item of Object.entries(yScaleValues[0])) {
91
87
  if (item[0] === seriesKey) {
88
+ // let userUpdatedSeriesName = config.series.filter(series => series.dataKey === item[0])?.[0]?.name
89
+ // if (userUpdatedSeriesName) item[0] = userUpdatedSeriesName
90
+
92
91
  seriesToInclude.push(item)
93
92
  }
94
93
  }
@@ -97,6 +96,7 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
97
96
  // filter out the series that aren't added to the map.
98
97
  seriesToInclude.map(series => yScaleMaxValues.push(Number(yScaleValues[0][series])))
99
98
  if (!seriesToInclude) return
99
+
100
100
  let tooltipDataFromSeries = Object.fromEntries(seriesToInclude) ? Object.fromEntries(seriesToInclude) : {}
101
101
 
102
102
  let tooltipData = {}
@@ -123,8 +123,8 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
123
123
  return config.xAxis.type === 'date' ? xScale(parseDate(d[config.xAxis.dataKey])) : xScale(d[config.xAxis.dataKey])
124
124
  }
125
125
 
126
- const handleY = (d, index) => {
127
- return yScale(d[config.series[index].dataKey])
126
+ const handleY = (d, index, s = undefined) => {
127
+ return yScale(d[s.dataKey])
128
128
  }
129
129
 
130
130
  return (
@@ -132,21 +132,30 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
132
132
  <ErrorBoundary component='AreaChart'>
133
133
  <Group className='area-chart' key='area-wrapper' left={Number(config.yAxis.size)}>
134
134
  {(config.runtime.areaSeriesKeys || config.runtime.seriesKeys).map((s, index) => {
135
- let seriesColor = colorPalettesChart[config.palette][index]
135
+ let seriesData = data.map(d => {
136
+ return {
137
+ [config.xAxis.dataKey]: d[config.xAxis.dataKey],
138
+ [s.dataKey]: d[s.dataKey]
139
+ }
140
+ })
141
+
136
142
  let curveType = allCurves[s.lineType]
137
143
  let transparentArea = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(s.dataKey) === -1
138
144
  let displayArea = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(s.dataKey) !== -1
139
145
 
140
- data.map(d => xScale(parseDate(d[config.xAxis.dataKey])))
141
-
146
+ if (config.xAxis.type === 'date') {
147
+ data.map(d => xScale(parseDate(d[config.xAxis.dataKey])))
148
+ } else {
149
+ data.map(d => xScale(d[config.xAxis.dataKey]))
150
+ }
142
151
  return (
143
152
  <React.Fragment key={index}>
144
153
  {/* prettier-ignore */}
145
154
  <LinePath
146
- data={data}
155
+ data={seriesData}
147
156
  x={d => handleX(d)}
148
- y={d => yScale(d[config.series[index].dataKey])}
149
- stroke={displayArea ? seriesColor : 'transparent'}
157
+ y={d => handleY(d, index, s)}
158
+ stroke={displayArea ? colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[s.dataKey] : s.dataKey) : '#000' : 'transparent'}
150
159
  strokeWidth={2}
151
160
  strokeOpacity={1}
152
161
  shapeRendering='geometricPrecision'
@@ -159,11 +168,12 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
159
168
  key={'area-chart'}
160
169
  fill={ displayArea ? colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[s.dataKey] : s.dataKey) : '#000' : 'transparent'}
161
170
  fillOpacity={transparentArea ? 0.25 : 0.5}
162
- data={data} x={d => handleX(d)}
163
- y={d => handleY(d, index)}
171
+ data={seriesData}
172
+ x={d => handleX(d)}
173
+ y={d => handleY(d, index, s)}
164
174
  yScale={yScale}
165
175
  curve={curveType}
166
- strokeDasharray={s.type ? handleLineType(s.typ) : 0}
176
+ strokeDasharray={s.type ? handleLineType(s.type) : 0}
167
177
  />
168
178
 
169
179
  {/* Transparent bar for tooltips */}
@@ -175,10 +185,11 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
175
185
  fillOpacity={0.05}
176
186
  style={DEBUG ? { stroke: 'black', strokeWidth: 2 } : {}}
177
187
  onMouseMove={e => handleMouseOver(e, data)}
188
+ onMouseOut={hideTooltip}
178
189
  />
179
190
 
180
191
  {/* circles that appear on hover */}
181
- {tooltipData && (
192
+ {tooltipData && Object.entries(tooltipData.data).length > 0 && (
182
193
  <circle
183
194
  cx={config.xAxis.type === 'categorical' ? xScale(tooltipData.data[config.xAxis.dataKey]) : xScale(parseDate(tooltipData.data[config.xAxis.dataKey]))}
184
195
  cy={yScale(tooltipData.data[s.dataKey])}
@@ -190,26 +201,7 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
190
201
  />
191
202
  )}
192
203
 
193
- {/* another tool for showing bars during debug mode. */}
194
- {DEBUG &&
195
- data.map((item, index) => {
196
- return (
197
- <Bar
198
- className='bar-here'
199
- x={Number(barThickness * index + offset)}
200
- y={d => Number(yScale(d[config.series[index].dataKey]))}
201
- yScale={yScale}
202
- width={barThicknessAdjusted}
203
- height={yMax}
204
- fill={'transparent'}
205
- fillOpacity={1}
206
- style={{ stroke: 'black', strokeWidth: 2 }}
207
- onMouseMove={e => handleMouseOver(e, data)}
208
- />
209
- )
210
- })}
211
-
212
- {tooltipData && (
204
+ {tooltipData && Object.entries(tooltipData.data).length > 0 && (
213
205
  <TooltipInPortal key={Math.random()} top={tooltipData.dataYPosition + chartPosition?.top} left={tooltipData.dataXPosition + chartPosition?.left} style={defaultStyles}>
214
206
  <ul style={{ listStyle: 'none', paddingLeft: 'unset', fontFamily: 'sans-serif', margin: 'auto', lineHeight: '1rem' }} data-tooltip-id={tooltip_id}>
215
207
  {typeof tooltipData === 'object' &&
@@ -6,13 +6,15 @@ import chroma from 'chroma-js'
6
6
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
7
7
  import ConfigContext from '../ConfigContext'
8
8
  import { BarStackHorizontal } from '@visx/shape'
9
+ import { useHighlightedBars } from '../hooks/useHighlightedBars'
10
+ import { act } from 'react-dom/test-utils'
9
11
 
10
12
  export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getXAxisData, getYAxisData, animatedChart, visible }) {
11
- const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, updateConfig, colorPalettes, formatDate, isNumber, getTextWidth, parseDate } = useContext(ConfigContext)
12
-
13
+ const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, updateConfig, colorPalettes, tableData, formatDate, isNumber, getTextWidth, parseDate, setSharedFilter, setSharedFilterValue, dashboardConfig } = useContext(ConfigContext)
14
+ const { HighLightedBarUtils } = useHighlightedBars(config)
13
15
  const { orientation, visualizationSubType } = config
14
16
  const isHorizontal = orientation === 'horizontal'
15
-
17
+ const barBorderWidth = 1
16
18
  const lollipopBarWidth = config.lollipopSize === 'large' ? 7 : config.lollipopSize === 'medium' ? 6 : 5
17
19
  const lollipopShapeSize = config.lollipopSize === 'large' ? 14 : config.lollipopSize === 'medium' ? 12 : 10
18
20
 
@@ -25,12 +27,11 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
25
27
  const tipRounding = config.tipRounding
26
28
  const radius = config.roundingStyle === 'standard' ? '8px' : config.roundingStyle === 'shallow' ? '5px' : config.roundingStyle === 'finger' ? '15px' : '0px'
27
29
  const stackCount = config.runtime.seriesKeys.length
28
- const barBorderWidth = 1
29
30
  const fontSize = { small: 16, medium: 18, large: 20 }
30
31
  const hasMultipleSeries = Object.keys(config.runtime.seriesLabels).length > 1
31
32
 
32
33
  const applyRadius = index => {
33
- if (index === undefined || index === null || !isRounded) return
34
+ if (index === undefined || index === null || !isRounded) return {}
34
35
  let style = {}
35
36
 
36
37
  if ((isStacked && index + 1 === stackCount) || !isStacked) {
@@ -48,6 +49,27 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
48
49
  return style
49
50
  }
50
51
 
52
+ const assignColorsToValues = () => {
53
+ const palettesArr = colorPalettes[config.palette]
54
+ const values = tableData.map(d => {
55
+ return d[config.legend.colorCode]
56
+ })
57
+ // Map to hold unique values and their colors
58
+ let colorMap = new Map()
59
+ // Resultant array to hold colors to the values
60
+ let result = []
61
+
62
+ for (let i = 0; i < values.length; i++) {
63
+ // If value not in map, add it and assign a color
64
+ if (!colorMap.has(values[i])) {
65
+ colorMap.set(values[i], palettesArr[colorMap.size % palettesArr.length])
66
+ }
67
+ // push the colosr to the result array
68
+ result.push(colorMap.get(values[i]))
69
+ }
70
+ return result
71
+ }
72
+
51
73
  const updateBars = defaultBars => {
52
74
  // function updates stacked && regular && lollipop horizontal bars
53
75
  if (config.visualizationType !== 'Bar' && !isHorizontal) return defaultBars
@@ -167,7 +189,7 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
167
189
  `}
168
190
  </style>
169
191
  <Group key={`bar-stack-${barStack.index}-${bar.index}`} id={`barStack${barStack.index}-${bar.index}`} className='stack vertical'>
170
- <Text display={config.labels && displayBar ? 'block' : 'none'} opacity={transparentBar ? 0.5 : 1} x={barThickness * bar.index + offset} y={bar.y - 5} fill={bar.color} textAnchor='middle'>
192
+ <Text display={config.labels && displayBar ? 'block' : 'none'} opacity={transparentBar ? 0.5 : 1} x={barThickness * bar.index + offset} y={bar.y - 5} fill={'#000'} textAnchor='middle'>
171
193
  {yAxisValue}
172
194
  </Text>
173
195
  <foreignObject
@@ -181,6 +203,13 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
181
203
  display={displayBar ? 'block' : 'none'}
182
204
  data-tooltip-html={tooltip}
183
205
  data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
206
+ onClick={e => {
207
+ e.preventDefault()
208
+ if (setSharedFilter) {
209
+ bar[config.xAxis.dataKey] = xAxisValue
210
+ setSharedFilter(config.uid, bar)
211
+ }
212
+ }}
184
213
  ></foreignObject>
185
214
  </Group>
186
215
  </Group>
@@ -245,6 +274,13 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
245
274
  display={displayBar ? 'block' : 'none'}
246
275
  data-tooltip-html={tooltip}
247
276
  data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
277
+ onClick={e => {
278
+ e.preventDefault()
279
+ if (setSharedFilter) {
280
+ bar[config.xAxis.dataKey] = xAxisValue
281
+ setSharedFilter(config.uid, bar)
282
+ }
283
+ }}
248
284
  ></foreignObject>
249
285
 
250
286
  {orientation === 'horizontal' && visualizationSubType === 'stacked' && isLabelBelowBar && barStack.index === 0 && !config.yAxis.hideLabel && (
@@ -313,23 +349,44 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
313
349
  left={config.runtime.horizontal ? 0 : (xMax / barGroups.length) * barGroup.index}
314
350
  >
315
351
  {barGroup.bars.map((bar, index) => {
352
+ const scaleVal = config.useLogScale ? 0.1 : 0
353
+ const getHighlightedBarColorByValue = value => {
354
+ const match = config?.highlightedBarValues.filter(item => {
355
+ if (!item.value) return
356
+ return config.xAxis.type === 'date' ? formatDate(parseDate(item.value)) === value : item.value === value
357
+ })[0]
358
+
359
+ if (!match?.color) return `rgba(255, 102, 1)`
360
+ return match.color
361
+ }
362
+
363
+ const getHighlightedBarByValue = value => {
364
+ const match = config?.highlightedBarValues.filter(item => {
365
+ if (!item.value) return
366
+ return config.xAxis.type === 'date' ? formatDate(parseDate(item.value)) === value : item.value === value
367
+ })[0]
368
+
369
+ if (!match?.color) return false
370
+ return match
371
+ }
372
+
373
+ let highlightedBarValues = config.highlightedBarValues.map(item => item.value).filter(item => item !== ('' || undefined))
374
+
375
+ highlightedBarValues = config.xAxis.type === 'date' ? HighLightedBarUtils.formatDates(highlightedBarValues) : highlightedBarValues
376
+
316
377
  let transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
317
378
  let displayBar = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(bar.key) !== -1
318
- let barHeight = orientation === 'horizontal' ? config.barHeight : isNumber(Math.abs(yScale(bar.value) - yScale(0))) ? Math.abs(yScale(bar.value) - yScale(0)) : 0
379
+ let barHeight = orientation === 'horizontal' ? config.barHeight : isNumber(Math.abs(yScale(bar.value) - yScale(scaleVal))) ? Math.abs(yScale(bar.value) - yScale(scaleVal)) : 0
319
380
  let barY = bar.value >= 0 && isNumber(bar.value) ? bar.y : yScale(0)
320
381
  let barGroupWidth = ((config.runtime.horizontal ? yMax : xMax) / barGroups.length) * (config.barThickness || 0.8)
321
382
  let offset = (((config.runtime.horizontal ? yMax : xMax) / barGroups.length) * (1 - (config.barThickness || 0.8))) / 2
322
383
  const barX = bar.value < 0 ? Math.abs(xScale(bar.value)) : xScale(0)
323
- const barWidthHorizontal = Math.abs(xScale(bar.value) - xScale(0))
384
+ const barWidthHorizontal = Math.abs(xScale(bar.value) - xScale(scaleVal))
324
385
  // ! Unsure if this should go back.
325
386
  if (config.isLollipopChart) {
326
387
  offset = (config.runtime.horizontal ? yMax : xMax) / barGroups.length / 2 - lollipopBarWidth / 2
327
388
  }
328
- const set = new Set()
329
- data.forEach(d => set.add(d[config.legend.colorCode]))
330
- const uniqValues = Array.from(set)
331
-
332
- let palette = colorPalettes[config.palette].slice(0, uniqValues.length)
389
+ let palette = assignColorsToValues()
333
390
 
334
391
  let barWidth = config.isLollipopChart ? lollipopBarWidth : barGroupWidth / barGroup.bars.length
335
392
  let barColor = config.runtime.seriesLabels && config.runtime.seriesLabels[bar.key] ? colorScale(config.runtime.seriesLabels[bar.key]) : colorScale(bar.key)
@@ -349,7 +406,6 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
349
406
  }
350
407
 
351
408
  const barPosition = bar.value < 0 ? 'below' : 'above'
352
- const textX = barPosition === 'below' ? 0 : 0
353
409
 
354
410
  // check if bar text/value string fits into each bars.
355
411
  let textWidth = getTextWidth(xAxisValue, `normal ${fontSize[config.fontSize]}px sans-serif`)
@@ -357,10 +413,15 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
357
413
  let labelColor = '#000000'
358
414
 
359
415
  // Set label color
360
- if (chroma.contrast(labelColor, barColor) < 4.9) {
361
- textFits ? (labelColor = '#FFFFFF') : '#000000'
416
+ if (barColor && labelColor) {
417
+ if (chroma.contrast(labelColor, barColor) < 4.9) {
418
+ labelColor = textFits ? '#FFFFFF' : '#000000'
419
+ }
362
420
  }
363
421
 
422
+ // Set if background is transparent'
423
+ labelColor = HighLightedBarUtils.checkFontColor(yAxisValue, highlightedBarValues, labelColor)
424
+
364
425
  // control text position
365
426
  let textAnchor = textFits ? 'end' : 'start'
366
427
  let textAnchorLollipop = 'start'
@@ -395,6 +456,66 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
395
456
  ${xAxisTooltip}
396
457
  </div>`
397
458
 
459
+ const isRegularLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'regular'
460
+ const isTwoToneLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'two-tone'
461
+ const isHighlightedBar = config.orientation === 'vertical' ? highlightedBarValues?.includes(xAxisValue) : highlightedBarValues?.includes(yAxisValue)
462
+ const highlightedBarColor = config.orientation === 'vertical' ? getHighlightedBarColorByValue(xAxisValue) : getHighlightedBarColorByValue(yAxisValue)
463
+ const highlightedBar = config.orientation === 'vertical' ? getHighlightedBarByValue(xAxisValue) : getHighlightedBarByValue(yAxisValue)
464
+
465
+ const background = () => {
466
+ if (isRegularLollipopColor) return barColor
467
+ if (isTwoToneLollipopColor) return chroma(barColor).brighten(1)
468
+ if (isHighlightedBar) return 'transparent'
469
+ // loop through shared filters and get active values
470
+ if (dashboardConfig && dashboardConfig?.dashboard.sharedFilters?.length > 0) {
471
+ let activeFilters = []
472
+ let backgroundColor = barColor
473
+
474
+ const checkForResetValue = () => {
475
+ return dashboardConfig.dashboard.sharedFilters?.map((filter, index) => {
476
+ if (filter.resetLabel === filter.active) {
477
+ backgroundColor = barColor
478
+ } else {
479
+ return backgroundColor
480
+ }
481
+ })
482
+ }
483
+
484
+ dashboardConfig.dashboard.sharedFilters?.forEach((filter, index) => {
485
+ activeFilters.push(filter.active)
486
+ })
487
+
488
+ // if reset value is found use that.
489
+
490
+ if (config.orientation === 'horizontal') {
491
+ if (!activeFilters.includes(yAxisValue)) {
492
+ backgroundColor = '#ccc'
493
+ }
494
+ }
495
+
496
+ if (config.orientation !== 'horizontal') {
497
+ if (!activeFilters.includes(xAxisValue)) {
498
+ backgroundColor = '#ccc'
499
+ }
500
+ }
501
+ checkForResetValue()
502
+ return backgroundColor
503
+ }
504
+ return barColor
505
+ }
506
+
507
+ const borderColor = isHighlightedBar ? highlightedBarColor : config.barHasBorder === 'true' ? '#000' : 'transparent'
508
+
509
+ const borderWidth = isHighlightedBar ? highlightedBar.borderWidth : config.isLollipopChart ? 0 : config.barHasBorder === 'true' ? barBorderWidth : 0
510
+
511
+ const finalStyle = {
512
+ background: background(),
513
+ borderColor,
514
+ borderStyle: 'solid',
515
+ borderWidth,
516
+ ...style
517
+ }
518
+
398
519
  return (
399
520
  <Group key={`${barGroup.index}--${index}--${orientation}`}>
400
521
  {/* This feels gross but inline transition was not working well*/}
@@ -414,15 +535,18 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
414
535
  y={config.runtime.horizontal ? barWidth * bar.index : barY}
415
536
  width={config.runtime.horizontal ? barWidthHorizontal : barWidth}
416
537
  height={isHorizontal && !config.isLollipopChart ? barWidth : isHorizontal && config.isLollipopChart ? lollipopBarWidth : barHeight}
417
- style={{
418
- background: config.isLollipopChart && config.lollipopColorStyle === 'regular' ? barColor : config.isLollipopChart && config.lollipopColorStyle === 'two-tone' ? chroma(barColor).brighten(1) : barColor,
419
- border: `${config.isLollipopChart ? 0 : config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`,
420
- ...style
421
- }}
538
+ style={finalStyle}
422
539
  opacity={transparentBar ? 0.5 : 1}
423
540
  display={displayBar ? 'block' : 'none'}
424
541
  data-tooltip-html={tooltip}
425
542
  data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
543
+ onClick={e => {
544
+ e.preventDefault()
545
+ if (setSharedFilter) {
546
+ bar[config.xAxis.dataKey] = config.orientation === 'horizontal' ? yAxisValue : xAxisValue
547
+ setSharedFilter(config.uid, bar)
548
+ }
549
+ }}
426
550
  ></foreignObject>
427
551
  {orientation === 'horizontal' && !config.isLollipopChart && displayNumbersOnBar && (
428
552
  <Text // prettier-ignore
@@ -437,7 +561,6 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
437
561
  {xAxisValue}
438
562
  </Text>
439
563
  )}
440
- ;
441
564
  {orientation === 'horizontal' && config.isLollipopChart && displayNumbersOnBar && (
442
565
  <Text
443
566
  display={displayBar ? 'block' : 'none'}
@@ -461,13 +584,12 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
461
584
  : formatNumber(data[barGroup.index][config.runtime.originalXAxis.dataKey])}
462
585
  </Text>
463
586
  )}
464
- ;
587
+
465
588
  {orientation === 'vertical' && (
466
- <Text display={config.labels && displayBar ? 'block' : 'none'} opacity={transparentBar ? 0.5 : 1} x={barWidth * (bar.index + 0.5) + offset} y={barY - 5} fill={barColor} textAnchor='middle'>
589
+ <Text display={config.labels && displayBar ? 'block' : 'none'} opacity={transparentBar ? 0.5 : 1} x={barWidth * (bar.index + 0.5) + offset} y={barY - 5} fill={labelColor} textAnchor='middle'>
467
590
  {yAxisValue}
468
591
  </Text>
469
592
  )}
470
- ;
471
593
  {config.isLollipopChart && config.lollipopShape === 'circle' && (
472
594
  <circle
473
595
  cx={orientation === 'horizontal' ? bar.y : barWidth * (barGroup.bars.length - bar.index - 1) + (isLabelBelowBar && orientation === 'horizontal' ? 0 : offset) + lollipopShapeSize / 3.5}
@@ -2,6 +2,7 @@ import React, { useContext, useEffect, useState, useMemo } from 'react'
2
2
  import { useTable, useSortBy, useResizeColumns, useBlockLayout } from 'react-table'
3
3
  import Papa from 'papaparse'
4
4
  import { Base64 } from 'js-base64'
5
+ import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
5
6
 
6
7
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
7
8
  import LegendCircle from '@cdc/core/components/LegendCircle'
@@ -9,7 +10,7 @@ import Icon from '@cdc/core/components/ui/Icon'
9
10
 
10
11
  import ConfigContext from '../ConfigContext'
11
12
 
12
- import CoveMediaControls from '@cdc/core/components/CoveMediaControls'
13
+ import MediaControls from '@cdc/core/components/MediaControls'
13
14
 
14
15
  export default function DataTable() {
15
16
  const { rawData, tableData: data, config, colorScale, parseDate, formatDate, formatNumber: numberFormatter, colorPalettes } = useContext(ConfigContext)
@@ -93,24 +94,29 @@ export default function DataTable() {
93
94
  {
94
95
  Header: '',
95
96
  Cell: ({ row }) => {
96
- const seriesLabel = config.runtime.seriesLabels ? config.runtime.seriesLabels[row.original] : row.original
97
+ const getSeriesLabel = () => {
98
+ let userUpdatedSeriesName = config.series.filter(series => series.dataKey === row.original)?.[0]?.name
99
+
100
+ if (userUpdatedSeriesName) return userUpdatedSeriesName
101
+ if (config.runtimeSeriesLabels) return config.runtime.seriesLabels[row.original]
102
+ return row.original
103
+ }
97
104
  return (
98
105
  <>
99
106
  {config.visualizationType !== 'Pie' && (
100
107
  <LegendCircle
101
108
  fill={
102
- // non-dynamic leged
103
- !config.legend.dynamicLegend
104
- ? colorScale(seriesLabel)
105
- : // dynamic legend
106
- config.legend.dynamicLegend
109
+ // non-dynamic legend
110
+ !config.legend.dynamicLegend && config.visualizationType !== 'Forecasting'
111
+ ? colorScale(getSeriesLabel())
112
+ : config.legend.dynamicLegend
107
113
  ? colorPalettes[config.palette][row.index]
108
114
  : // fallback
109
115
  '#000'
110
116
  }
111
117
  />
112
118
  )}
113
- <span>{seriesLabel}</span>
119
+ <span>{getSeriesLabel()}</span>
114
120
  </>
115
121
  )
116
122
  },
@@ -127,7 +133,19 @@ export default function DataTable() {
127
133
  const newCol = {
128
134
  Header: resolveTableHeader(),
129
135
  Cell: ({ row }) => {
130
- return <>{numberFormatter(d[row.original], 'left')}</>
136
+ let leftAxisItems = config.series.filter(item => item?.axis === 'Left')
137
+ let rightAxisItems = config.series.filter(item => item?.axis === 'Right')
138
+ let resolvedAxis = ''
139
+
140
+ leftAxisItems.map(leftSeriesItem => {
141
+ if (leftSeriesItem.dataKey === row.original) resolvedAxis = 'left'
142
+ })
143
+
144
+ rightAxisItems.map(rightSeriesItem => {
145
+ if (rightSeriesItem.dataKey === row.original) resolvedAxis = 'right'
146
+ })
147
+
148
+ return <>{numberFormatter(d[row.original], resolvedAxis)}</>
131
149
  },
132
150
  id: `${d[config.runtime.originalXAxis.dataKey]}--${index}`,
133
151
  canSort: true
@@ -210,10 +228,10 @@ export default function DataTable() {
210
228
 
211
229
  return (
212
230
  <ErrorBoundary component='DataTable'>
213
- <CoveMediaControls.Section classes={['download-links']}>
214
- <CoveMediaControls.Link config={config} />
231
+ <MediaControls.Section classes={['download-links']}>
232
+ <MediaControls.Link config={config} />
215
233
  {config.table.download && <DownloadButton data={rawData} type='link' />}
216
- </CoveMediaControls.Section>
234
+ </MediaControls.Section>
217
235
 
218
236
  <section id={config?.title ? `dataTableSection__${config?.title.replace(/\s/g, '')}` : `dataTableSection`} className={`data-table-container`} aria-label={accessibilityLabel}>
219
237
  <div
@@ -6,38 +6,7 @@ import { Text } from '@visx/text'
6
6
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
7
7
  import chroma from 'chroma-js'
8
8
 
9
- // create function to position text based where bar is located left/or right
10
- function getTextProps(isLollipopChart, textFits, lollipopShapeSize, fill) {
11
- if (isLollipopChart) {
12
- return {
13
- right: {
14
- textAnchor: 'start',
15
- dx: lollipopShapeSize + 6,
16
- fill: '#000000'
17
- },
18
- left: {
19
- textAnchor: 'end',
20
- dx: -lollipopShapeSize,
21
- fill: '#000000'
22
- }
23
- }
24
- } else {
25
- return {
26
- right: {
27
- textAnchor: textFits ? 'end' : 'start',
28
- dx: textFits ? -6 : 6,
29
- fill: textFits ? fill : '#000000'
30
- },
31
- left: {
32
- textAnchor: textFits ? 'start' : 'end',
33
- dx: textFits ? 6 : -6,
34
- fill: textFits ? fill : '#000000'
35
- }
36
- }
37
- }
38
- }
39
-
40
- export function DeviationBar({ height, xScale }) {
9
+ export default function DeviationBar({ height, xScale }) {
41
10
  const { transformedData: data, config, formatNumber, twoColorPalette, getTextWidth, updateConfig, parseDate, formatDate } = useContext(ConfigContext)
42
11
 
43
12
  if (!config || config?.series?.length !== 1 || config.orientation !== 'horizontal') return
@@ -189,3 +158,34 @@ export function DeviationBar({ height, xScale }) {
189
158
  </ErrorBoundary>
190
159
  )
191
160
  }
161
+
162
+ // create function to position text based where bar is located left/or right
163
+ function getTextProps(isLollipopChart, textFits, lollipopShapeSize, fill) {
164
+ if (isLollipopChart) {
165
+ return {
166
+ right: {
167
+ textAnchor: 'start',
168
+ dx: lollipopShapeSize + 6,
169
+ fill: '#000000'
170
+ },
171
+ left: {
172
+ textAnchor: 'end',
173
+ dx: -lollipopShapeSize,
174
+ fill: '#000000'
175
+ }
176
+ }
177
+ } else {
178
+ return {
179
+ right: {
180
+ textAnchor: textFits ? 'end' : 'start',
181
+ dx: textFits ? -6 : 6,
182
+ fill: textFits ? fill : '#000000'
183
+ },
184
+ left: {
185
+ textAnchor: textFits ? 'start' : 'end',
186
+ dx: textFits ? 6 : -6,
187
+ fill: textFits ? fill : '#000000'
188
+ }
189
+ }
190
+ }
191
+ }