@cdc/chart 4.24.2 → 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 (57) hide show
  1. package/dist/cdcchart.js +47386 -36618
  2. package/examples/chart-regression-1.json +378 -0
  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.json +2 -1
  6. package/examples/feature/regions/index.json +50 -4
  7. package/examples/feature/sankey/sankey-example-data.json +1364 -0
  8. package/examples/feature/sankey/sankey_chart_data.csv +20 -0
  9. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +306 -19
  10. package/examples/sparkline.json +868 -0
  11. package/index.html +128 -123
  12. package/package.json +4 -2
  13. package/src/CdcChart.tsx +40 -22
  14. package/src/_stories/ChartEditor.stories.tsx +14 -3
  15. package/src/_stories/_mock/url_filter.json +1076 -0
  16. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +2 -1
  17. package/src/components/AreaChart/components/AreaChart.jsx +2 -1
  18. package/src/components/BarChart/components/BarChart.Horizontal.tsx +39 -49
  19. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +36 -56
  20. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +32 -39
  21. package/src/components/BarChart/components/BarChart.Vertical.tsx +40 -55
  22. package/src/components/BoxPlot/BoxPlot.jsx +2 -1
  23. package/src/components/DeviationBar.jsx +3 -3
  24. package/src/components/EditorPanel/EditorPanel.tsx +167 -15
  25. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +1 -1
  26. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +108 -0
  27. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +48 -4
  28. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +41 -0
  29. package/src/components/EditorPanel/components/Panels/index.tsx +9 -7
  30. package/src/components/EditorPanel/components/panels.scss +11 -0
  31. package/src/components/EditorPanel/useEditorPermissions.js +40 -14
  32. package/src/components/Legend/Legend.Component.tsx +23 -15
  33. package/src/components/Legend/Legend.tsx +4 -4
  34. package/src/components/LineChart/LineChartProps.ts +1 -0
  35. package/src/components/LineChart/helpers.ts +2 -2
  36. package/src/components/LineChart/index.tsx +7 -7
  37. package/src/components/LinearChart.jsx +9 -30
  38. package/src/components/PairedBarChart.jsx +6 -10
  39. package/src/components/PieChart/PieChart.tsx +3 -3
  40. package/src/components/Regions/components/Regions.tsx +120 -78
  41. package/src/components/Sankey/index.tsx +434 -0
  42. package/src/components/Sankey/sankey.scss +153 -0
  43. package/src/components/Sankey/types/index.ts +16 -0
  44. package/src/components/ScatterPlot/ScatterPlot.jsx +1 -0
  45. package/src/components/Sparkline/{SparkLine.jsx → components/SparkLine.tsx} +14 -30
  46. package/src/components/Sparkline/index.scss +3 -0
  47. package/src/components/Sparkline/index.tsx +1 -1
  48. package/src/components/ZoomBrush.tsx +2 -1
  49. package/src/data/initial-state.js +46 -2
  50. package/src/helpers/computeMarginBottom.ts +2 -1
  51. package/src/helpers/tests/computeMarginBottom.test.ts +2 -1
  52. package/src/hooks/useBarChart.js +5 -2
  53. package/src/hooks/useScales.ts +15 -18
  54. package/src/hooks/useTooltip.tsx +9 -8
  55. package/src/scss/main.scss +8 -29
  56. package/src/types/ChartConfig.ts +32 -14
  57. package/src/types/ChartContext.ts +7 -0
@@ -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'
@@ -19,7 +20,7 @@ const AreaChart = props => {
19
20
  if (!data) return
20
21
 
21
22
  const handleX = d => {
22
- return (config.xAxis.type === 'date' ? xScale(parseDate(d[config.xAxis.dataKey], false)) : xScale(d[config.xAxis.dataKey])) + (xScale.bandwidth ? xScale.bandwidth() / 2 : 0)
23
+ return (isDateScale(config.xAxis) ? xScale(parseDate(d[config.xAxis.dataKey], false)) : xScale(d[config.xAxis.dataKey])) + (xScale.bandwidth ? xScale.bandwidth() / 2 : 0)
23
24
  }
24
25
 
25
26
  const handleY = (d, index, s = undefined) => {
@@ -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,14 +22,14 @@ 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 = config.xAxis.type === 'date' && config.xAxis.sortDates ? (config.barThickness * (xScale.range()[1] - xScale.range()[0])) : xMax / barStack.bars.length
29
- let barThicknessAdjusted = barThickness * (config.xAxis.type === 'date' && config.xAxis.sortDates ? 1 : (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
35
  const rawXValue = bar.bar.data[config.runtime.xAxis.dataKey]
@@ -48,50 +51,40 @@ const BarChartStackedVertical = () => {
48
51
 
49
52
  return (
50
53
  <Group key={`${barStack.index}--${bar.index}--${orientation}`}>
51
- <style>
52
- {`
53
- #barStack${barStack.index}-${bar.index} rect,
54
- #barStack${barStack.index}-${bar.index} foreignObject div{
55
- animation-delay: ${barStack.index * 0.5}s;
56
- transform-origin: ${barThicknessAdjusted / 2}px ${bar.y + bar.height}px
57
- }
58
- `}
59
- </style>
60
54
  <Group key={`bar-stack-${barStack.index}-${bar.index}`} id={`barStack${barStack.index}-${bar.index}`} className='stack vertical'>
61
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'>
62
56
  {yAxisValue}
63
57
  </Text>
64
- <foreignObject
65
- onMouseOver={() => onMouseOverBar(xAxisValue, bar.key)}
66
- onMouseLeave={onMouseLeaveBar}
67
- key={`bar-stack-${barStack.index}-${bar.index}`}
68
- x={barX}
69
- y={bar.y}
70
- width={barThicknessAdjusted}
71
- height={bar.height}
72
- display={displayBar ? 'block' : 'none'}
73
- data-tooltip-html={tooltip}
74
- data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
75
- 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 => {
76
75
  e.preventDefault()
77
76
  if (setSharedFilter) {
78
77
  bar[config.xAxis.dataKey] = xAxisValue
79
78
  setSharedFilter(config.uid, bar)
80
79
  }
81
- }}
82
- >
83
- <div
84
- style={{
85
- transition: 'all 0.2s linear',
86
- opacity: transparentBar ? 0.2 : 1,
87
- width: barThicknessAdjusted,
88
- height: bar.height,
89
- background: colorScale(config.runtime.seriesLabels[bar.key]),
90
- border: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`,
91
- ...style
92
- }}
93
- ></div>
94
- </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
+ })}
95
88
  </Group>
96
89
  </Group>
97
90
  )
@@ -8,6 +8,9 @@ import { BarGroup } from '@visx/shape'
8
8
  import { useHighlightedBars } from '../../../hooks/useHighlightedBars'
9
9
  import { FaStar } from 'react-icons/fa'
10
10
  import Regions from './../../Regions'
11
+ import { isDateScale } from '@cdc/core/helpers/cove/date'
12
+
13
+ import createBarElement from '@cdc/core/components/createBarElement'
11
14
 
12
15
  // third party
13
16
  import chroma from 'chroma-js'
@@ -75,7 +78,7 @@ export const BarChartVertical = () => {
75
78
  height={yMax}
76
79
  x0={d => {
77
80
  const rawXValue = d[config.runtime.originalXAxis.dataKey]
78
- return config.runtime.xAxis.type === 'date' ? parseDate(rawXValue) : rawXValue
81
+ return isDateScale(config.runtime.xAxis) ? parseDate(rawXValue) : rawXValue
79
82
  }}
80
83
  x0Scale={xScale}
81
84
  x1Scale={seriesScale}
@@ -102,7 +105,7 @@ export const BarChartVertical = () => {
102
105
  let barGroupWidth = seriesScale.range()[1]
103
106
 
104
107
  let barWidth = config.isLollipopChart ? lollipopBarWidth : barGroupWidth / barGroup.bars.length
105
- let barX = bar.x + (config.isLollipopChart ? (((barGroupWidth / barGroup.bars.length) - lollipopBarWidth) / 2) : 0) - (config.xAxis.type === 'date' && config.xAxis.sortDates ? barGroupWidth / 2 : 0)
108
+ let barX = bar.x + (config.isLollipopChart ? (barGroupWidth / barGroup.bars.length - lollipopBarWidth) / 2 : 0) - (config.xAxis.type === 'date-time' ? barGroupWidth / 2 : 0)
106
109
  setBarWidth(barWidth)
107
110
  setTotalBarsInGroup(barGroup.bars.length)
108
111
 
@@ -111,7 +114,6 @@ export const BarChartVertical = () => {
111
114
 
112
115
  // create new Index for bars with negative values
113
116
  const newIndex = bar.value < 0 ? -1 : index
114
- const borderRadius = applyRadius(newIndex)
115
117
  // tooltips
116
118
 
117
119
  const additionalColTooltip = getAdditionalColumn(bar.key, data[barGroup.index][config.runtime.originalXAxis.dataKey])
@@ -179,57 +181,43 @@ export const BarChartVertical = () => {
179
181
  return _barColor
180
182
  }
181
183
 
182
- const getLeft = () => {
183
- if (barWidth < 50 && barWidth > 15) return barWidth / 2.5
184
- if (barWidth < 15 && barWidth > 5) return barWidth / 6
185
- if (barWidth < 5) return 0
186
- return barWidth / 2
187
- }
188
- const iconStyle: { [key: string]: any } = {
189
- position: 'absolute',
190
- top: bar.value >= 0 && isNumber(bar.value) ? -suppresedBarHeight : undefined,
191
- bottom: bar.value >= 0 && isNumber(bar.value) ? undefined : `-${suppresedBarHeight}px`,
192
- left: getLeft()
193
- }
194
-
195
- if (config.isLollipopChart) {
196
- iconStyle.left = 0
197
- iconStyle.transform = `translateX(0)`
198
- }
199
-
200
- const finalStyle = {
201
- background: getBarBackgroundColor(barColor),
202
- borderColor,
203
- borderStyle: 'solid',
204
- borderWidth: `${borderWidth}px`,
205
- width: barWidth,
206
- height: barHeight,
207
- ...borderRadius,
208
- cursor: dashboardConfig ? 'pointer' : 'default'
209
- }
210
-
211
184
  return (
212
185
  <Group key={`${barGroup.index}--${index}`}>
213
- {/* This feels gross but inline transition was not working well*/}
214
- <style>
215
- {`
216
- .linear #barGroup${barGroup.index} div,
217
- .Combo #barGroup${barGroup.index} div {
218
- transform-origin: 0 ${barY + barHeight}px;
219
- }
220
- `}
221
- </style>
222
186
  <Group key={`bar-sub-group-${barGroup.index}-${barGroup.x0}-${barY}--${index}`}>
223
- <foreignObject
187
+ {createBarElement({
188
+ config: config,
189
+ index: newIndex,
190
+ id: `barGroup${barGroup.index}`,
191
+ background: getBarBackgroundColor(barColor),
192
+ borderColor,
193
+ borderStyle: 'solid',
194
+ borderWidth: `${borderWidth}px`,
195
+ width: barWidth,
196
+ height: barHeight,
197
+ x: barX,
198
+ y: barY,
199
+ onMouseOver: () => onMouseOverBar(xAxisValue, bar.key),
200
+ onMouseLeave: onMouseLeaveBar,
201
+ tooltipHtml: tooltip,
202
+ tooltipId: `cdc-open-viz-tooltip-${config.runtime.uniqueId}`,
203
+ onClick: e => {
204
+ e.preventDefault()
205
+ if (setSharedFilter) {
206
+ bar[config.xAxis.dataKey] = xAxisValue
207
+ setSharedFilter(config.uid, bar)
208
+ }
209
+ },
210
+ styleOverrides: {
211
+ transformOrigin: `0 ${barY + barHeight}px`,
212
+ opacity: transparentBar ? 0.2 : 1,
213
+ display: displayBar ? 'block' : 'none',
214
+ cursor: dashboardConfig ? 'pointer' : 'default'
215
+ }
216
+ })}
217
+ <g
218
+ transform={`translate(${barX},${yMax - suppresedBarHeight})`}
224
219
  onMouseOver={() => onMouseOverBar(xAxisValue, bar.key)}
225
220
  onMouseLeave={onMouseLeaveBar}
226
- style={{ overflow: 'visible', transition: 'all 0.2s linear' }}
227
- id={`barGroup${barGroup.index}`}
228
- key={`bar-group-bar-${barGroup.index}-${bar.index}-${bar.value}-${bar.key}`}
229
- x={barX}
230
- y={barY}
231
- width={barWidth}
232
- height={barHeight}
233
221
  opacity={transparentBar ? 0.2 : 1}
234
222
  display={displayBar ? 'block' : 'none'}
235
223
  data-tooltip-html={tooltip}
@@ -242,11 +230,8 @@ export const BarChartVertical = () => {
242
230
  }
243
231
  }}
244
232
  >
245
- <div style={{ position: 'relative' }}>
246
- <div style={iconStyle}>{getIcon(bar, barWidth)}</div>
247
- <div style={{ ...finalStyle }}></div>
248
- </div>
249
- </foreignObject>
233
+ {getIcon(bar, barWidth)}
234
+ </g>
250
235
 
251
236
  <Text // prettier-ignore
252
237
  display={config.labels && displayBar ? 'block' : 'none'}
@@ -303,7 +288,7 @@ export const BarChartVertical = () => {
303
288
  let upperPos
304
289
  let lowerPos
305
290
  let tickWidth = 5
306
- xPos = xScale(getXAxisData(d)) + (config.xAxis.type !== 'date' || !config.xAxis.sortDates ? seriesScale.range()[1] / 2 : 0)
291
+ xPos = xScale(getXAxisData(d)) + (config.xAxis.type !== 'date' || config.xAxis.type !== 'date-time' ? seriesScale.range()[1] / 2 : 0)
307
292
  upperPos = yScale(getYAxisData(d, config.confidenceKeys.lower))
308
293
  lowerPos = yScale(getYAxisData(d, config.confidenceKeys.upper))
309
294
  return (
@@ -96,7 +96,8 @@ const CoveBoxPlot = ({ xScale, yScale }) => {
96
96
  container
97
97
  containerProps={{
98
98
  'data-tooltip-html': handleTooltip(d),
99
- 'data-tooltip-id': tooltip_id
99
+ 'data-tooltip-id': tooltip_id,
100
+ tabIndex: -1
100
101
  }}
101
102
  />
102
103
  </Group>
@@ -4,8 +4,8 @@ import { useContext, useEffect, useRef, useState } from 'react'
4
4
  import ConfigContext from '../ConfigContext'
5
5
  import { Text } from '@visx/text'
6
6
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
7
- import chroma from 'chroma-js'
8
7
  import useIntersectionObserver from '../hooks/useIntersectionObserver'
8
+ import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
9
9
 
10
10
  export default function DeviationBar({ height, xScale }) {
11
11
  const { transformedData: data, config, formatNumber, twoColorPalette, getTextWidth, updateConfig, parseDate, formatDate, currentViewport } = useContext(ConfigContext)
@@ -160,8 +160,7 @@ export default function DeviationBar({ height, xScale }) {
160
160
  // colors
161
161
  const [leftColor, rightColor] = twoColorPalette[twoColor.palette]
162
162
  const barColor = { left: leftColor, right: rightColor }
163
- const isBarColorDark = chroma.contrast('#000000', barColor[barPosition]) < 4.9
164
- const fill = isBarColorDark ? '#FFFFFF' : '#000000'
163
+ const fill = getContrastColor('#000', barColor[barPosition])
165
164
 
166
165
  let textProps = getTextProps(config.isLollipopChart, textFits, lollipopShapeSize, fill)
167
166
  // tooltips
@@ -187,6 +186,7 @@ export default function DeviationBar({ height, xScale }) {
187
186
  height={barHeight}
188
187
  data-tooltip-html={tooltip}
189
188
  data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
189
+ tabIndex={-1}
190
190
  >
191
191
  <div style={{ width: barWidth, height: barHeight, border: `${borderWidth}px solid #333`, backgroundColor: barColor[barPosition], ...borderRadius }}></div>
192
192
  </foreignObject>