@cdc/chart 4.24.1 → 4.24.3

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 (82) hide show
  1. package/dist/cdcchart.js +48948 -37923
  2. package/examples/{private/combo.json → chart-regression-1.json} +40 -31
  3. package/examples/chart-regression-2.json +2360 -0
  4. package/examples/feature/filters/url-filter.json +1076 -0
  5. package/examples/feature/line/line-chart-preliminary.json +84 -37
  6. package/examples/feature/line/line-chart.json +2 -1
  7. package/examples/feature/regions/index.json +55 -5
  8. package/examples/feature/sankey/sankey-example-data.json +1364 -0
  9. package/examples/feature/sankey/sankey_chart_data.csv +20 -0
  10. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +306 -19
  11. package/examples/sparkline.json +868 -0
  12. package/index.html +128 -121
  13. package/package.json +4 -2
  14. package/src/CdcChart.tsx +73 -38
  15. package/src/_stories/ChartEditor.stories.tsx +15 -4
  16. package/src/_stories/_mock/pie_config.json +4 -3
  17. package/src/_stories/_mock/url_filter.json +1076 -0
  18. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +2 -1
  19. package/src/components/AreaChart/components/AreaChart.jsx +2 -25
  20. package/src/components/BarChart/components/BarChart.Horizontal.tsx +39 -49
  21. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +36 -56
  22. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +36 -41
  23. package/src/components/BarChart/components/BarChart.Vertical.tsx +48 -64
  24. package/src/components/BoxPlot/BoxPlot.jsx +11 -9
  25. package/src/components/DeviationBar.jsx +3 -3
  26. package/src/components/EditorPanel/EditorPanel.tsx +1717 -1961
  27. package/src/components/EditorPanel/EditorPanelContext.ts +40 -0
  28. package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +148 -0
  29. package/src/components/EditorPanel/components/{Panel.ForestPlotSettings.tsx → Panels/Panel.ForestPlotSettings.tsx} +16 -7
  30. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +160 -0
  31. package/src/components/EditorPanel/components/{Panel.Regions.tsx → Panels/Panel.Regions.tsx} +6 -6
  32. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +108 -0
  33. package/src/components/EditorPanel/components/{Panel.Series.tsx → Panels/Panel.Series.tsx} +50 -6
  34. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +338 -0
  35. package/src/components/EditorPanel/components/Panels/index.tsx +19 -0
  36. package/src/components/EditorPanel/components/panels.scss +11 -0
  37. package/src/components/EditorPanel/editor-panel.scss +1 -13
  38. package/src/components/EditorPanel/useEditorPermissions.js +44 -13
  39. package/src/components/Legend/Legend.Component.tsx +207 -0
  40. package/src/components/Legend/Legend.tsx +8 -327
  41. package/src/components/Legend/helpers/createFormatLabels.tsx +140 -0
  42. package/src/components/LineChart/LineChartProps.ts +2 -1
  43. package/src/components/LineChart/components/LineChart.Circle.tsx +85 -52
  44. package/src/components/LineChart/helpers.ts +3 -3
  45. package/src/components/LineChart/index.tsx +99 -23
  46. package/src/components/LinearChart.jsx +12 -33
  47. package/src/components/PairedBarChart.jsx +10 -12
  48. package/src/components/PieChart/PieChart.tsx +80 -27
  49. package/src/components/Regions/components/Regions.tsx +120 -69
  50. package/src/components/Sankey/index.tsx +434 -0
  51. package/src/components/Sankey/sankey.scss +153 -0
  52. package/src/components/Sankey/types/index.ts +16 -0
  53. package/src/components/ScatterPlot/ScatterPlot.jsx +1 -0
  54. package/src/components/Sparkline/{SparkLine.jsx → components/SparkLine.tsx} +14 -30
  55. package/src/components/Sparkline/index.scss +3 -0
  56. package/src/components/Sparkline/index.tsx +1 -1
  57. package/src/components/ZoomBrush.tsx +2 -1
  58. package/src/data/initial-state.js +51 -4
  59. package/src/helpers/computeMarginBottom.ts +4 -3
  60. package/src/helpers/tests/computeMarginBottom.test.ts +2 -1
  61. package/src/hooks/useBarChart.js +5 -2
  62. package/src/hooks/useHighlightedBars.js +1 -1
  63. package/src/hooks/useMinMax.ts +3 -3
  64. package/src/hooks/useScales.ts +28 -18
  65. package/src/hooks/useTooltip.tsx +19 -14
  66. package/src/scss/main.scss +8 -96
  67. package/src/types/ChartConfig.ts +47 -20
  68. package/src/types/ChartContext.ts +17 -4
  69. package/src/types/Label.ts +7 -0
  70. package/examples/private/chart-t.json +0 -3740
  71. package/examples/private/epi-data.csv +0 -13
  72. package/examples/private/epi-data.json +0 -62
  73. package/examples/private/epi.json +0 -403
  74. package/examples/private/occupancy.json +0 -109283
  75. package/examples/private/prod-line-config.json +0 -401
  76. package/examples/private/region-data.json +0 -822
  77. package/examples/private/region-testing.json +0 -312
  78. package/examples/private/scaling.json +0 -45325
  79. package/examples/private/testing-data.json +0 -1739
  80. package/examples/private/testing.json +0 -816
  81. package/src/components/EditorPanel/components/Panel.DateHighlighting.tsx +0 -109
  82. package/src/components/EditorPanel/components/Panels.tsx +0 -13
@@ -3,6 +3,7 @@ import React, { useContext, memo } from 'react'
3
3
  // cdc
4
4
  import ConfigContext from '../../../ConfigContext'
5
5
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
6
+ import { isDateScale } from '@cdc/core/helpers/cove/date'
6
7
 
7
8
  // visx & d3
8
9
  import * as allCurves from '@visx/curve'
@@ -20,7 +21,7 @@ const AreaChartStacked = ({ xScale, yScale, yMax, xMax, handleTooltipMouseOver,
20
21
 
21
22
  const handleDateCategory = value => {
22
23
  if (config.xAxis.type === 'categorical') return xScale(value)
23
- if (config.xAxis.type === 'date') {
24
+ if (isDateScale(config.xAxis)) {
24
25
  let date = new Date(value)
25
26
  return xScale(date)
26
27
  }
@@ -3,6 +3,7 @@ import React, { useContext, memo } from 'react'
3
3
  // cdc
4
4
  import ConfigContext from '../../../ConfigContext'
5
5
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
6
+ import { isDateScale } from '@cdc/core/helpers/cove/date'
6
7
 
7
8
  // visx & d3
8
9
  import * as allCurves from '@visx/curve'
@@ -18,26 +19,8 @@ const AreaChart = props => {
18
19
 
19
20
  if (!data) return
20
21
 
21
- // Tooltip helper for getting data to the closest date/category hovered.
22
- const getXValueFromCoordinate = x => {
23
- if (config.xAxis.type === 'categorical' || config.visualizationType === 'Combo') {
24
- let eachBand = xScale.step()
25
- let numerator = x
26
- const index = Math.floor(Number(numerator) / eachBand)
27
- return xScale.domain()[index - 1] // fixes off by 1 error
28
- }
29
-
30
- if (config.xAxis.type === 'date' && config.visualizationType !== 'Combo') {
31
- const bisectDate = bisector(d => parseDate(d[config.xAxis.dataKey])).left
32
- const x0 = xScale.invert(x)
33
- const index = bisectDate(config.data, x0, 1)
34
- const val = parseDate(config.data[index - 1][config.xAxis.dataKey])
35
- return val
36
- }
37
- }
38
-
39
22
  const handleX = d => {
40
- return config.xAxis.type === 'date' ? xScale(parseDate(d[config.xAxis.dataKey], false)) : xScale(d[config.xAxis.dataKey])
23
+ return (isDateScale(config.xAxis) ? xScale(parseDate(d[config.xAxis.dataKey], false)) : xScale(d[config.xAxis.dataKey])) + (xScale.bandwidth ? xScale.bandwidth() / 2 : 0)
41
24
  }
42
25
 
43
26
  const handleY = (d, index, s = undefined) => {
@@ -61,12 +44,6 @@ const AreaChart = props => {
61
44
  let transparentArea = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(s.dataKey) === -1
62
45
  let displayArea = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(s.dataKey) !== -1
63
46
 
64
- if (config.xAxis.type === 'date') {
65
- data.map(d => xScale(parseDate(d[config.xAxis.dataKey])))
66
- } else {
67
- data.map(d => xScale(d[config.xAxis.dataKey]))
68
- }
69
-
70
47
  return (
71
48
  <React.Fragment key={index}>
72
49
  {/* prettier-ignore */}
@@ -6,19 +6,21 @@ import { Text } from '@visx/text'
6
6
  import { BarGroup } from '@visx/shape'
7
7
  import { useHighlightedBars } from '../../../hooks/useHighlightedBars'
8
8
  import { FaStar } from 'react-icons/fa'
9
+ import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
9
10
 
10
11
  // third party
11
12
  import chroma from 'chroma-js'
12
13
  import BarChartContext, { BarChartContextValues } from './context'
13
14
  import { ChartContext } from '../../../types/ChartContext'
14
15
 
16
+ import createBarElement from '@cdc/core/components/createBarElement'
17
+
15
18
  export const BarChartHorizontal = () => {
16
19
  const { xScale, yScale, yMax, seriesScale } = useContext<BarChartContextValues>(BarChartContext)
17
20
  const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, setSharedFilter, isNumber, getTextWidth, getYAxisData, getXAxisData } = useContext<ChartContext>(ConfigContext)
18
21
  const {
19
22
  isHorizontal,
20
23
  barBorderWidth,
21
- applyRadius,
22
24
  updateBars,
23
25
  assignColorsToValues,
24
26
  section,
@@ -112,7 +114,6 @@ export const BarChartHorizontal = () => {
112
114
 
113
115
  // create new Index for bars with negative values
114
116
  const newIndex = bar.value < 0 ? -1 : index
115
- const borderRadius = applyRadius(newIndex)
116
117
 
117
118
  let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${xAxisValue}` : xAxisValue
118
119
  const additionalColTooltip = getAdditionalColumn(hoveredBar)
@@ -137,10 +138,8 @@ export const BarChartHorizontal = () => {
137
138
  const borderWidth = isHighlightedBar ? highlightedBar.borderWidth : config.isLollipopChart ? 0 : config.barHasBorder === 'true' ? barBorderWidth : 0
138
139
  const displaylollipopShape = config.suppressedData.some(d => bar.key === d.column && bar.value === d.value) ? 'none' : 'block'
139
140
  // update label color
140
- if (barColor && labelColor) {
141
- if (chroma.contrast(labelColor, barColor) < 4.9) {
142
- labelColor = textFits ? '#FFFFFF' : '#000000'
143
- }
141
+ if (barColor && labelColor && textFits) {
142
+ labelColor = getContrastColor('#000', barColor)
144
143
  }
145
144
  const getTop = () => {
146
145
  if (Number(barHeight) < 20) return -4
@@ -154,15 +153,6 @@ export const BarChartHorizontal = () => {
154
153
  return 12
155
154
  }
156
155
  }
157
- const iconStyle: { [key: string]: any } = {
158
- position: 'absolute',
159
- top: getTop(),
160
- left: suppresedBarWidth * 1.2
161
- }
162
-
163
- if (config.isLollipopChart) {
164
- iconStyle.top = -9
165
- }
166
156
  const background = () => {
167
157
  if (isRegularLollipopColor) return barColor
168
158
  if (isTwoToneLollipopColor) return chroma(barColor).brighten(1)
@@ -170,39 +160,42 @@ export const BarChartHorizontal = () => {
170
160
  return barColor
171
161
  }
172
162
 
173
- const finalStyle = {
174
- background: background(),
175
- borderColor,
176
- borderStyle: 'solid',
177
- borderWidth,
178
- width: barWidth,
179
- transition: 'all 0.2s linear',
180
- height: !config.isLollipopChart ? barHeight : lollipopBarWidth,
181
- ...borderRadius
182
- }
183
-
184
163
  return (
185
164
  <Group key={`${barGroup.index}--${index}`}>
186
- {/* This feels gross but inline transition was not working well*/}
187
- <style>
188
- {`
189
- .linear #barGroup${barGroup.index} div,
190
- .Combo #barGroup${barGroup.index} div {
191
- transform-origin: 0 ${barY + barHeight}px;
192
- }
193
- `}
194
- </style>
195
165
  <Group key={`bar-sub-group-${barGroup.index}-${barGroup.x0}-${barY}--${index}`}>
196
- <foreignObject
166
+ {createBarElement({
167
+ config: config,
168
+ index: newIndex,
169
+ id: `barGroup${barGroup.index}`,
170
+ background: background(),
171
+ borderColor,
172
+ borderStyle: 'solid',
173
+ borderWidth: `${borderWidth}px`,
174
+ width: barWidth,
175
+ height: !config.isLollipopChart ? barHeight : lollipopBarWidth,
176
+ x: barX,
177
+ y: barHeight * bar.index,
178
+ onMouseOver: () => onMouseOverBar(xAxisValue, bar.key),
179
+ onMouseLeave: onMouseLeaveBar,
180
+ tooltipHtml: tooltip,
181
+ tooltipId: `cdc-open-viz-tooltip-${config.runtime.uniqueId}`,
182
+ onClick: e => {
183
+ e.preventDefault()
184
+ if (setSharedFilter) {
185
+ bar[config.xAxis.dataKey] = yAxisValue
186
+ setSharedFilter(config.uid, bar)
187
+ }
188
+ },
189
+ styleOverrides: {
190
+ transformOrigin: `0 ${barY + barHeight}px`,
191
+ opacity: transparentBar ? 0.2 : 1,
192
+ display: displayBar ? 'block' : 'none'
193
+ }
194
+ })}
195
+ <g
196
+ transform={`translate(${barX},${barHeight * bar.index})`}
197
197
  onMouseOver={() => onMouseOverBar(xAxisValue, bar.key)}
198
198
  onMouseLeave={onMouseLeaveBar}
199
- id={`barGroup${barGroup.index}`}
200
- key={`bar-group-bar-${barGroup.index}-${bar.index}-${bar.value}-${bar.key}`}
201
- x={barX}
202
- style={{ overflow: 'visible', ...finalStyle }}
203
- y={barHeight * bar.index}
204
- height={!config.isLollipopChart ? barHeight : lollipopBarWidth}
205
- width={barWidth}
206
199
  opacity={transparentBar ? 0.2 : 1}
207
200
  display={displayBar ? 'block' : 'none'}
208
201
  data-tooltip-html={tooltip}
@@ -215,11 +208,8 @@ export const BarChartHorizontal = () => {
215
208
  }
216
209
  }}
217
210
  >
218
- <div style={{ position: 'relative' }}>
219
- <div style={iconStyle}>{getIcon(bar, barWidth)}</div>
220
- <div style={{ ...finalStyle }}></div>
221
- </div>
222
- </foreignObject>
211
+ {getIcon(bar, barWidth)}
212
+ </g>
223
213
 
224
214
  {!config.isLollipopChart && displayNumbersOnBar && (
225
215
  <Text // prettier-ignore
@@ -262,7 +252,7 @@ export const BarChartHorizontal = () => {
262
252
  <circle
263
253
  display={displaylollipopShape}
264
254
  cx={bar.y}
265
- cy={0 + lollipopBarWidth / 2}
255
+ cy={barHeight * bar.index + lollipopBarWidth / 2}
266
256
  r={lollipopShapeSize / 2}
267
257
  fill={barColor}
268
258
  key={`circle--${bar.index}`}
@@ -4,14 +4,14 @@ import { useBarChart } from '../../../hooks/useBarChart'
4
4
  import { BarStackHorizontal } from '@visx/shape'
5
5
  import { Group } from '@visx/group'
6
6
  import { Text } from '@visx/text'
7
-
8
- // third party
9
- import chroma from 'chroma-js'
7
+ import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
10
8
 
11
9
  // types
12
10
  import BarChartContext, { type BarChartContextValues } from './context'
13
11
  import { type ChartContext } from '../../../types/ChartContext'
14
12
 
13
+ import createBarElement from '@cdc/core/components/createBarElement'
14
+
15
15
  const BarChartStackedHorizontal = () => {
16
16
  const { yMax, yScale, xScale } = useContext<BarChartContextValues>(BarChartContext)
17
17
 
@@ -30,40 +30,26 @@ const BarChartStackedHorizontal = () => {
30
30
  } = useContext<ChartContext>(ConfigContext)
31
31
 
32
32
  // prettier-ignore
33
- const {
34
- applyRadius,
35
- barBorderWidth,
36
- displayNumbersOnBar,
37
- fontSize,
38
- getAdditionalColumn,
39
- hoveredBar,
40
- isHorizontal,
41
- isLabelBelowBar,
42
- onMouseLeaveBar,
43
- onMouseOverBar,
44
- updateBars
45
- } = useBarChart()
33
+ const { barBorderWidth, displayNumbersOnBar, fontSize, getAdditionalColumn, hoveredBar, isHorizontal, isLabelBelowBar, onMouseLeaveBar, onMouseOverBar, updateBars, barStackedSeriesKeys } = useBarChart()
46
34
 
47
35
  const { orientation, visualizationSubType } = config
48
-
49
36
  return (
50
37
  config.visualizationSubType === 'stacked' &&
51
38
  isHorizontal && (
52
39
  <>
53
- <BarStackHorizontal data={data} keys={config.runtime.barSeriesKeys || config.runtime.seriesKeys} height={yMax} y={d => d[config.runtime.yAxis.dataKey]} xScale={xScale} yScale={yScale} color={colorScale} offset='none'>
40
+ <BarStackHorizontal data={data} keys={barStackedSeriesKeys} height={yMax} y={d => d[config.runtime.yAxis.dataKey]} xScale={xScale} yScale={yScale} color={colorScale} offset='none'>
54
41
  {barStacks =>
55
42
  barStacks.map(barStack =>
56
43
  updateBars(barStack.bars).map((bar, index) => {
57
- let transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
58
- let displayBar = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(bar.key) !== -1
44
+ const transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
45
+ const displayBar = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(bar.key) !== -1
59
46
  config.barHeight = Number(config.barHeight)
60
- let labelColor = '#000000'
47
+ const labelColor = getContrastColor('#000', colorScale(config.runtime.seriesLabels[bar.key]))
61
48
  // tooltips
62
49
  const xAxisValue = formatNumber(data[bar.index][bar.key], 'left')
63
50
  const yAxisValue = config.runtime.yAxis.type === 'date' ? formatDate(parseDate(data[bar.index][config.runtime.originalXAxis.dataKey])) : data[bar.index][config.runtime.originalXAxis.dataKey]
64
- const style = applyRadius(barStack.index)
65
- let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
66
- let textWidth = getTextWidth(xAxisValue, `normal ${fontSize[config.fontSize]}px sans-serif`)
51
+ const yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
52
+ const textWidth = getTextWidth(xAxisValue, `normal ${fontSize[config.fontSize]}px sans-serif`)
67
53
 
68
54
  const additionalColTooltip = getAdditionalColumn(hoveredBar)
69
55
  const tooltipBody = `${config.runtime.seriesLabels[bar.key]}: ${xAxisValue}`
@@ -73,46 +59,40 @@ const BarChartStackedHorizontal = () => {
73
59
  <li class="tooltip-body ">${additionalColTooltip}</li>
74
60
  </li></ul>`
75
61
 
76
- if (chroma.contrast(labelColor, colorScale(config.runtime.seriesLabels[bar.key])) < 4.9) {
77
- labelColor = '#FFFFFF'
78
- }
79
-
80
62
  return (
81
63
  <>
82
- <style>
83
- {`
84
- #barStack${barStack.index}-${bar.index} rect,
85
- #barStack${barStack.index}-${bar.index} foreignObject div{
86
- animation-delay: ${barStack.index * 0.5}s;
87
- transform-origin: ${bar.x}px
88
- }
89
- `}
90
- </style>
91
64
  <Group key={index} id={`barStack${barStack.index}-${bar.index}`} className='stack horizontal'>
92
- <foreignObject
93
- onMouseOver={() => onMouseOverBar(yAxisValue, bar.key)}
94
- onMouseLeave={onMouseLeaveBar}
95
- key={`barstack-horizontal-${barStack.index}-${bar.index}-${index}`}
96
- className={`animated-chart group ${animatedChart ? 'animated' : ''}`}
97
- x={bar.x}
98
- y={bar.y}
99
- style={{ transition: 'all 0.2s linear' }}
100
- width={bar.width}
101
- height={bar.height}
102
- opacity={transparentBar ? 0.2 : 1}
103
- display={displayBar ? 'block' : 'none'}
104
- data-tooltip-html={tooltip}
105
- data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
106
- onClick={e => {
65
+ {createBarElement({
66
+ config: config,
67
+ seriesHighlight,
68
+ index: barStack.index,
69
+ className: `animated-chart group ${animatedChart ? 'animated' : ''}`,
70
+ background: colorScale(config.runtime.seriesLabels[bar.key]),
71
+ borderColor: '#333',
72
+ borderStyle: 'solid',
73
+ borderWidth: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px`,
74
+ width: bar.width,
75
+ height: bar.height,
76
+ x: bar.x,
77
+ y: bar.y,
78
+ onMouseOver: () => onMouseOverBar(yAxisValue, bar.key),
79
+ onMouseLeave: onMouseLeaveBar,
80
+ tooltipHtml: tooltip,
81
+ tooltipId: `cdc-open-viz-tooltip-${config.runtime.uniqueId}`,
82
+ onClick: e => {
107
83
  e.preventDefault()
108
84
  if (setSharedFilter) {
109
85
  bar[config.xAxis.dataKey] = xAxisValue
110
86
  setSharedFilter(config.uid, bar)
111
87
  }
112
- }}
113
- >
114
- <div style={{ width: bar.width, height: bar.height, background: colorScale(config.runtime.seriesLabels[bar.key]), border: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`, ...style }}></div>
115
- </foreignObject>
88
+ },
89
+ styleOverrides: {
90
+ animationDelay: `${barStack.index * 0.5}s`,
91
+ transformOrigin: `${bar.x}px 0`,
92
+ opacity: transparentBar ? 0.2 : 1,
93
+ display: displayBar ? 'block' : 'none'
94
+ }
95
+ })}
116
96
 
117
97
  {orientation === 'horizontal' && visualizationSubType === 'stacked' && isLabelBelowBar && barStack.index === 0 && !config.yAxis.hideLabel && (
118
98
  <Text
@@ -6,12 +6,15 @@ import { Group } from '@visx/group'
6
6
  import { Text } from '@visx/text'
7
7
  import BarChartContext from './context'
8
8
  import Regions from '../../Regions'
9
+ import { isDateScale } from '@cdc/core/helpers/cove/date'
10
+
11
+ import createBarElement from '@cdc/core/components/createBarElement'
9
12
 
10
13
  const BarChartStackedVertical = () => {
11
14
  const [barWidth, setBarWidth] = useState(0)
12
15
  const { xScale, yScale, xMax, yMax } = useContext(BarChartContext)
13
16
  const { transformedData, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, setSharedFilter } = useContext(ConfigContext)
14
- const { isHorizontal, barBorderWidth, applyRadius, hoveredBar, getAdditionalColumn, onMouseLeaveBar, onMouseOverBar } = useBarChart()
17
+ const { isHorizontal, barBorderWidth, applyRadius, hoveredBar, getAdditionalColumn, onMouseLeaveBar, onMouseOverBar, barStackedSeriesKeys } = useBarChart()
15
18
  const { orientation } = config
16
19
  const data = config.brush.active && config.brush.data?.length ? config.brush.data : transformedData
17
20
 
@@ -19,19 +22,21 @@ const BarChartStackedVertical = () => {
19
22
  config.visualizationSubType === 'stacked' &&
20
23
  !isHorizontal && (
21
24
  <>
22
- <BarStack data={data} keys={config.runtime.barSeriesKeys || config.runtime.seriesKeys} x={d => d[config.runtime.xAxis.dataKey]} xScale={xScale} yScale={yScale} color={colorScale}>
25
+ <BarStack data={data} keys={barStackedSeriesKeys} x={d => d[config.runtime.xAxis.dataKey]} xScale={xScale} yScale={yScale} color={colorScale}>
23
26
  {barStacks =>
24
27
  barStacks.reverse().map(barStack =>
25
28
  barStack.bars.map(bar => {
26
29
  let transparentBar = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(bar.key) === -1
27
30
  let displayBar = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(bar.key) !== -1
28
- let barThickness = xMax / barStack.bars.length
29
- let barThicknessAdjusted = barThickness * (config.barThickness || 0.8)
31
+ let barThickness = config.xAxis.type === 'date-time' ? config.barThickness * (xScale.range()[1] - xScale.range()[0]) : xMax / barStack.bars.length
32
+ let barThicknessAdjusted = barThickness * (config.xAxis.type === 'date-time' ? 1 : config.barThickness || 0.8)
30
33
  let offset = (barThickness * (1 - (config.barThickness || 0.8))) / 2
31
34
  // tooltips
32
- const xAxisValue = config.runtime.xAxis.type === 'date' ? formatDate(parseDate(data[bar.index][config.runtime.xAxis.dataKey])) : data[bar.index][config.runtime.xAxis.dataKey]
35
+ const rawXValue = bar.bar.data[config.runtime.xAxis.dataKey]
36
+ const xAxisValue = config.runtime.xAxis.type === 'date' ? formatDate(parseDate(rawXValue)) : rawXValue
33
37
  const yAxisValue = formatNumber(bar.bar ? bar.bar.data[bar.key] : 0, 'left')
34
38
  if (!yAxisValue) return
39
+ const barX = xScale(config.runtime.xAxis.type === 'date' ? parseDate(rawXValue) : rawXValue) - (config.xAxis.type === 'date' && config.xAxis.sortDates ? barThicknessAdjusted / 2 : 0)
35
40
  const style = applyRadius(barStack.index)
36
41
  const xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
37
42
  const additionalColTooltip = getAdditionalColumn(hoveredBar)
@@ -46,50 +51,40 @@ const BarChartStackedVertical = () => {
46
51
 
47
52
  return (
48
53
  <Group key={`${barStack.index}--${bar.index}--${orientation}`}>
49
- <style>
50
- {`
51
- #barStack${barStack.index}-${bar.index} rect,
52
- #barStack${barStack.index}-${bar.index} foreignObject div{
53
- animation-delay: ${barStack.index * 0.5}s;
54
- transform-origin: ${barThicknessAdjusted / 2}px ${bar.y + bar.height}px
55
- }
56
- `}
57
- </style>
58
54
  <Group key={`bar-stack-${barStack.index}-${bar.index}`} id={`barStack${barStack.index}-${bar.index}`} className='stack vertical'>
59
- <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'>
55
+ <Text display={config.labels && displayBar ? 'block' : 'none'} opacity={transparentBar ? 0.5 : 1} x={barX + barWidth / 2} y={bar.y - 5} fill={'#000'} textAnchor='middle'>
60
56
  {yAxisValue}
61
57
  </Text>
62
- <foreignObject
63
- onMouseOver={() => onMouseOverBar(xAxisValue, bar.key)}
64
- onMouseLeave={onMouseLeaveBar}
65
- key={`bar-stack-${barStack.index}-${bar.index}`}
66
- x={barThickness * bar.index + offset}
67
- y={bar.y}
68
- width={barThicknessAdjusted}
69
- height={bar.height}
70
- display={displayBar ? 'block' : 'none'}
71
- data-tooltip-html={tooltip}
72
- data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
73
- onClick={e => {
58
+ {createBarElement({
59
+ config: config,
60
+ seriesHighlight,
61
+ index: barStack.index,
62
+ background: colorScale(config.runtime.seriesLabels[bar.key]),
63
+ borderColor: '#333',
64
+ borderStyle: 'solid',
65
+ borderWidth: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px`,
66
+ width: barThicknessAdjusted,
67
+ height: bar.height,
68
+ x: barX,
69
+ y: bar.y,
70
+ onMouseOver: () => onMouseOverBar(xAxisValue, bar.key),
71
+ onMouseLeave: onMouseLeaveBar,
72
+ tooltipHtml: tooltip,
73
+ tooltipId: `cdc-open-viz-tooltip-${config.runtime.uniqueId}`,
74
+ onClick: e => {
74
75
  e.preventDefault()
75
76
  if (setSharedFilter) {
76
77
  bar[config.xAxis.dataKey] = xAxisValue
77
78
  setSharedFilter(config.uid, bar)
78
79
  }
79
- }}
80
- >
81
- <div
82
- style={{
83
- transition: 'all 0.2s linear',
84
- opacity: transparentBar ? 0.2 : 1,
85
- width: barThicknessAdjusted,
86
- height: bar.height,
87
- background: colorScale(config.runtime.seriesLabels[bar.key]),
88
- border: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`,
89
- ...style
90
- }}
91
- ></div>
92
- </foreignObject>
80
+ },
81
+ styleOverrides: {
82
+ animationDelay: `${barStack.index * 0.5}s`,
83
+ transformOrigin: `${barThicknessAdjusted / 2}px ${bar.y + bar.height}px`,
84
+ opacity: transparentBar ? 0.2 : 1,
85
+ display: displayBar ? 'block' : 'none'
86
+ }
87
+ })}
93
88
  </Group>
94
89
  </Group>
95
90
  )