@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
@@ -1,28 +1,29 @@
1
1
  import React, { useContext } from 'react'
2
-
3
2
  import * as allCurves from '@visx/curve'
4
3
  import { Group } from '@visx/group'
5
4
  import { LinePath } from '@visx/shape'
6
5
  import { Text } from '@visx/text'
7
6
  import { scaleLinear, scalePoint } from '@visx/scale'
8
7
  import { AxisBottom } from '@visx/axis'
9
-
10
8
  import { MarkerArrow } from '@visx/marker'
11
-
12
9
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
10
+ import useReduceData from '../../../hooks/useReduceData'
11
+ import ConfigContext from '../../../ConfigContext'
12
+ import './../index.scss'
13
13
 
14
- import useReduceData from '../../hooks/useReduceData'
15
-
16
- import ConfigContext from '../../ConfigContext'
14
+ type SparkLineProps = {
15
+ width: string | number
16
+ height: string | number
17
+ }
17
18
 
18
- const SparkLine = props => {
19
+ const SparkLine: React.FC<SparkLineProps> = props => {
19
20
  const { width: parentWidth, height: parentHeight } = props
20
21
  const { transformedData: data, config, parseDate, formatDate, seriesHighlight, formatNumber, colorScale, handleChartAriaLabels } = useContext(ConfigContext)
21
- let width = parentWidth
22
+ let width = Number(parentWidth)
22
23
  const { minValue, maxValue } = useReduceData(config, data, ConfigContext)
23
24
 
24
25
  const margin = { top: 5, right: 10, bottom: 10, left: 10 }
25
- const height = parentHeight
26
+ const height = Number(parentHeight)
26
27
 
27
28
  const xMax = width - config.runtime.yAxis.size
28
29
  const yMax = height - margin.top - 20
@@ -38,8 +39,8 @@ const SparkLine = props => {
38
39
  const isMinValid = Number(enteredMinValue) <= Number(minValue)
39
40
 
40
41
  if (data) {
41
- let min = enteredMinValue && isMinValid ? enteredMinValue : minValue
42
- let max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
42
+ let min = enteredMinValue && isMinValid ? Number(enteredMinValue) : Number(minValue)
43
+ let max = enteredMaxValue && isMaxValid ? Number(enteredMaxValue) : Number(Number.MIN_VALUE)
43
44
 
44
45
  if (max === Number.MIN_VALUE) {
45
46
  max = maxValue
@@ -94,6 +95,7 @@ const SparkLine = props => {
94
95
  return (
95
96
  <ErrorBoundary component='SparkLine'>
96
97
  <svg role='img' aria-label={handleChartAriaLabels(config)} width={parentWidth} height={100} className={'sparkline'} tabIndex={0}>
98
+ <title>{`Spark line graphic with the title ${config.title ? config.title : 'No Title Found'}`}</title>
97
99
  {config.runtime.lineSeriesKeys?.length > 0
98
100
  ? config.runtime.lineSeriesKeys
99
101
  : config.runtime.seriesKeys.map((seriesKey, index) => (
@@ -106,15 +108,6 @@ const SparkLine = props => {
106
108
  display={config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
107
109
  >
108
110
  {data.map((d, dataIndex) => {
109
- let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${formatNumber(getYAxisData(d, seriesKey))}` : formatNumber(getYAxisData(d, seriesKey))
110
- let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${d[config.runtime.xAxis.dataKey]}` : d[config.runtime.xAxis.dataKey]
111
-
112
- const tooltip = `<div>
113
- ${yAxisTooltip}<br />
114
- ${xAxisTooltip}<br />
115
- ${config.seriesLabel ? `${config.seriesLabel}: ${seriesKey}` : ''}
116
- </div>`
117
-
118
111
  return (
119
112
  <Group key={`series-${seriesKey}-point-${dataIndex}`}>
120
113
  <Text display={config.labels ? 'block' : 'none'} x={xScale(getXAxisData(d))} y={yScale(getYAxisData(d, seriesKey))} fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'} textAnchor='middle'>
@@ -134,16 +127,7 @@ const SparkLine = props => {
134
127
  shapeRendering='geometricPrecision'
135
128
  markerEnd={`url(#${'arrow'}--${index})`}
136
129
  />
137
- <MarkerArrow
138
- id={`arrow--${index}`}
139
- refX={2}
140
- size={6}
141
- markerEnd={`url(#${'arrow'}--${index})`}
142
- strokeOpacity={1}
143
- fillOpacity={1}
144
- // stroke={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
145
- fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
146
- />
130
+ <MarkerArrow id={`arrow--${index}`} refX={2} size={6} markerEnd={`url(#${'arrow'}--${index})`} strokeOpacity={1} fillOpacity={1} fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'} />
147
131
  </Group>
148
132
  <AxisBottom
149
133
  top={yMax + margin.top}
@@ -0,0 +1,3 @@
1
+ .cdc-open-viz-module.type-chart.lg.type-sparkline .filters-section {
2
+ margin: 0;
3
+ }
@@ -1,3 +1,3 @@
1
- import SparkLine from './SparkLine.jsx'
1
+ import SparkLine from './components/SparkLine'
2
2
 
3
3
  export default SparkLine
@@ -5,6 +5,7 @@ import { useBarChart } from '../hooks/useBarChart'
5
5
  import { FC, useContext, useEffect, useRef, useState } from 'react'
6
6
  import ConfigContext from '../ConfigContext'
7
7
  import { ScaleLinear, ScaleBand } from 'd3-scale'
8
+ import { isDateScale } from '@cdc/core/helpers/cove/date'
8
9
 
9
10
  interface Props {
10
11
  xScaleBrush: ScaleLinear<number, number>
@@ -55,7 +56,7 @@ const ZoomBrush: FC<Props> = props => {
55
56
  .find(item => item !== undefined)
56
57
  const startValue = xValues.find(item => item !== undefined)
57
58
 
58
- const formatIfDate = value => (config.runtime.xAxis.type === 'date' ? formatDate(parseDate(value)) : value)
59
+ const formatIfDate = value => (isDateScale(config.runtime.xAxis) ? formatDate(parseDate(value)) : value)
59
60
 
60
61
  setTextProps(prev => ({
61
62
  ...prev,
@@ -53,6 +53,7 @@ export default {
53
53
  rightAxisTickColor: '#333',
54
54
  numTicks: '',
55
55
  axisPadding: 0,
56
+ scalePadding: 10,
56
57
  tickRotation: 0,
57
58
  anchors: []
58
59
  },
@@ -115,7 +116,7 @@ export default {
115
116
  tickColor: '#333',
116
117
  numTicks: '',
117
118
  labelOffset: 65,
118
- axisPadding: 0,
119
+ axisPadding: 200,
119
120
  target: 0,
120
121
  maxTickRotation: 0
121
122
  },
@@ -129,7 +130,8 @@ export default {
129
130
  showDataTableLink: true,
130
131
  indexLabel: '',
131
132
  download: false,
132
- showVertical: true
133
+ showVertical: true,
134
+ dateDisplayFormat: ''
133
135
  },
134
136
  orientation: 'vertical',
135
137
  color: 'pinkpurple',
@@ -139,6 +141,7 @@ export default {
139
141
  legend: {
140
142
  hide: false,
141
143
  behavior: 'isolate',
144
+ axisAlign: true,
142
145
  singleRow: true,
143
146
  colorCode: '',
144
147
  reverseLabelOrder: false,
@@ -150,7 +153,8 @@ export default {
150
153
  dynamicLegendChartMessage: 'Select Options from the Legend',
151
154
  lineMode: false,
152
155
  verticalSorted: false,
153
- highlightOnHover: false
156
+ highlightOnHover: false,
157
+ seriesHighlight: []
154
158
  },
155
159
  brush: {
156
160
  height: 25,
@@ -191,7 +195,8 @@ export default {
191
195
  series: [],
192
196
  tooltips: {
193
197
  opacity: 90,
194
- singleSeries: false
198
+ singleSeries: false,
199
+ dateDisplayFormat: ''
195
200
  },
196
201
  forestPlot: {
197
202
  startAt: 0,
@@ -239,5 +244,47 @@ export default {
239
244
  },
240
245
  area: {
241
246
  isStacked: false
247
+ },
248
+ sankey: {
249
+ title: {
250
+ defaultColor: 'black'
251
+ },
252
+ iterations: 1,
253
+ rxValue: 0.9,
254
+ overallSize: {
255
+ width: 900,
256
+ height: 700
257
+ },
258
+ margin: {
259
+ margin_y: 25,
260
+ margin_x: 0
261
+ },
262
+ nodeSize: {
263
+ nodeWidth: 26,
264
+ nodeHeight: 40
265
+ },
266
+ nodePadding: 55,
267
+ nodeFontColor: 'black',
268
+ nodeColor: {
269
+ default: '#ff8500',
270
+ inactive: '#808080'
271
+ },
272
+ linkColor: {
273
+ default: '#ffc900',
274
+ inactive: '#D3D3D3'
275
+ },
276
+ opacity: {
277
+ nodeOpacityDefault: 1.0,
278
+ nodeOpacityInactive: 0.1,
279
+ LinkOpacityDefault: 1.0,
280
+ LinkOpacityInactive: 0.1
281
+ },
282
+ storyNodeFontColor: '#006778',
283
+ storyNodeText: [],
284
+ nodeValueStyle: {
285
+ textBefore: '(',
286
+ textAfter: ')'
287
+ },
288
+ data: []
242
289
  }
243
290
  }
@@ -1,12 +1,13 @@
1
- import { ChartConfig, Legend } from '../types/ChartConfig'
1
+ import { ChartConfig } from '../types/ChartConfig'
2
+ import { Legend } from '@cdc/core/types/Legend'
2
3
 
3
4
  export const computeMarginBottom = (config: ChartConfig, legend: Legend, currentViewport: string): string | number => {
4
5
  const isLegendBottom = legend.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
5
6
  const isHorizontal = config.orientation === 'horizontal'
6
7
  const tickRotation = Number(config.xAxis.tickRotation) > 0 ? Number(config.xAxis.tickRotation) : 0
7
- const isBrush = config.brush.active
8
+ const isBrush = config?.brush?.active
8
9
  const offset = 20
9
- const brushHeight = config.brush.height
10
+ const brushHeight = config?.brush?.height
10
11
  let bottom = 0
11
12
  if (!isLegendBottom && isHorizontal && !config.yAxis.label) {
12
13
  bottom = Number(config.xAxis.labelOffset)
@@ -1,4 +1,5 @@
1
- import { ChartConfig, Legend } from '../../types/ChartConfig'
1
+ import { ChartConfig } from '../../types/ChartConfig'
2
+ import { Legend } from '@cdc/core/types/Legend'
2
3
  import { computeMarginBottom } from '../computeMarginBottom'
3
4
 
4
5
  describe('computeMarginBottom', () => {
@@ -2,7 +2,7 @@ import React, { useContext, useEffect, useState } from 'react'
2
2
  import ConfigContext from '../ConfigContext'
3
3
  import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
4
4
  export const useBarChart = () => {
5
- const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate, setSeriesHighlight } = useContext(ConfigContext)
5
+ const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate, setSeriesHighlight, seriesHighlight } = useContext(ConfigContext)
6
6
  const { orientation } = config
7
7
  const [hoveredBar, setHoveredBar] = useState(null)
8
8
 
@@ -21,6 +21,8 @@ export const useBarChart = () => {
21
21
  const stackCount = config.runtime.seriesKeys.length
22
22
  const fontSize = { small: 16, medium: 18, large: 20 }
23
23
  const hasMultipleSeries = Object.keys(config.runtime.seriesLabels).length > 1
24
+ const isBarAndLegendIsolate = config.visualizationType === 'Bar' && config.legend.behavior === 'isolate' && config.legend.axisAlign
25
+ const barStackedSeriesKeys = isBarAndLegendIsolate && seriesHighlight?.length ? seriesHighlight : config.runtime.barSeriesKeys || config.runtime.seriesKeys
24
26
 
25
27
  useEffect(() => {
26
28
  if (orientation === 'horizontal' && !config.yAxis.labelPlacement) {
@@ -194,7 +196,7 @@ export const useBarChart = () => {
194
196
  return d[config.xAxis.dataKey] === xAxisDataValue
195
197
  }) || {}
196
198
  Object.keys(columns).forEach(colKeys => {
197
- if(series && config.columns[colKeys].series && config.columns[colKeys].series !== series) return
199
+ if (series && config.columns[colKeys].series && config.columns[colKeys].series !== series) return
198
200
  const formattingParams = {
199
201
  addColPrefix: config.columns[colKeys].prefix,
200
202
  addColSuffix: config.columns[colKeys].suffix,
@@ -235,6 +237,7 @@ export const useBarChart = () => {
235
237
  tipRounding,
236
238
  radius,
237
239
  stackCount,
240
+ barStackedSeriesKeys,
238
241
  fontSize,
239
242
  hasMultipleSeries,
240
243
  applyRadius,
@@ -127,7 +127,7 @@ export const useHighlightedBars = (config, updateConfig) => {
127
127
  */
128
128
  HighLightedBarUtils.findDuplicates = arr => {
129
129
  const duplicates = {}
130
- const result = arr.filter(obj => {
130
+ const result = arr?.filter(obj => {
131
131
  const { legendLabel } = obj
132
132
  if (!duplicates[legendLabel]) {
133
133
  duplicates[legendLabel] = true
@@ -181,10 +181,10 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
181
181
  if (config.yAxis.enablePadding) {
182
182
  if (min < 0) {
183
183
  // sets with negative data need more padding on the max
184
- max *= 1.2
185
- min *= 1.2
184
+ max *= 1 + (config.yAxis.scalePadding * 2) / 100
185
+ min *= 1 + (config.yAxis.scalePadding * 2) / 100
186
186
  } else {
187
- max *= 1.1
187
+ max *= 1 + config.yAxis.scalePadding / 100
188
188
  }
189
189
  }
190
190
 
@@ -4,6 +4,14 @@ import ConfigContext from '../ConfigContext'
4
4
  import { ChartConfig } from '../types/ChartConfig'
5
5
  import { ChartContext } from '../types/ChartContext'
6
6
 
7
+ const scaleTypes = {
8
+ TIME: 'time',
9
+ LOG: 'log',
10
+ POINT: 'point',
11
+ LINEAR: 'linear',
12
+ BAND: 'band'
13
+ }
14
+
7
15
  type useScaleProps = {
8
16
  config: ChartConfig // standard chart config
9
17
  data: Object[] // standard data array
@@ -37,14 +45,6 @@ const useScales = (properties: useScaleProps) => {
37
45
  let xScaleNoPadding = null
38
46
  let xScaleBrush = null
39
47
 
40
- const scaleTypes = {
41
- TIME: 'time',
42
- LOG: 'log',
43
- POINT: 'point',
44
- LINEAR: 'linear',
45
- BAND: 'band'
46
- }
47
-
48
48
  // handle Horizontal bars
49
49
  if (isHorizontal) {
50
50
  xScale = composeXScale({ min: min * 1.03, ...properties })
@@ -57,20 +57,24 @@ const useScales = (properties: useScaleProps) => {
57
57
  // handle Vertical bars
58
58
  if (!isHorizontal) {
59
59
  xScaleBrush = composeScalePoint(xAxisDataKeysMapped, [0, xMax], 0.5)
60
- xScale = composeScalePoint(xAxisDataMapped, [0, xMax], 0.5)
61
- xScale.type = scaleTypes.POINT
60
+ xScale = composeScaleBand(xAxisDataMapped, [0, xMax], 1 - config.barThickness)
62
61
  yScale = composeYScale(properties)
63
- seriesScale = composeScalePoint(seriesDomain, [0, xMax])
62
+ seriesScale = composeScaleBand(seriesDomain, [0, xScale.bandwidth()], 0)
64
63
  }
65
64
 
66
65
  // handle Area chart
67
- if (config.xAxis.type === 'date' && config.xAxis.sortDates) {
66
+ if (config.xAxis.type === 'date-time') {
67
+ let xAxisMin = Math.min(...xAxisDataMapped)
68
+ let xAxisMax = Math.max(...xAxisDataMapped)
69
+ xAxisMin -= (config.xAxis.padding ? config.xAxis.padding * 0.01 : 0) * (xAxisMax - xAxisMin)
70
+ xAxisMax += (config.xAxis.padding ? config.xAxis.padding * 0.01 : 0) * (xAxisMax - xAxisMin)
68
71
  xScale = scaleTime({
69
- domain: [Math.min(...xAxisDataMapped), Math.max(...xAxisDataMapped)],
72
+ domain: [xAxisMin, xAxisMax],
70
73
  range: [0, xMax]
71
74
  })
72
75
  xScaleBrush = xScale
73
- xScale.type = scaleTypes.LINEAR
76
+ xScale.type = scaleTypes.TIME
77
+ seriesScale = composeScaleBand(seriesDomain, [0, config.barThickness * xMax], 0)
74
78
  }
75
79
 
76
80
  // handle Deviation bar
@@ -245,8 +249,7 @@ const composeXScale = ({ min, max, xMax, config }) => {
245
249
  domain: [min, max],
246
250
  range: [0, xMax],
247
251
  nice: config.useLogScale,
248
- zero: config.useLogScale,
249
- type: config.useLogScale ? 'log' : 'linear'
252
+ zero: config.useLogScale
250
253
  })
251
254
  }
252
255
 
@@ -280,7 +283,14 @@ const composeScalePoint = (domain, range, padding = 0) => {
280
283
  return scalePoint({
281
284
  domain: domain,
282
285
  range: range,
283
- padding: padding,
284
- type: 'point'
286
+ padding: padding
287
+ })
288
+ }
289
+
290
+ const composeScaleBand = (domain, range, padding = 0) => {
291
+ return scaleBand({
292
+ domain: domain,
293
+ range: range,
294
+ padding: padding
285
295
  })
286
296
  }
@@ -9,11 +9,12 @@ import { DataTransform } from '@cdc/core/helpers/DataTransform'
9
9
  const transform = new DataTransform()
10
10
 
11
11
  import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
12
+ import { isDateScale } from '@cdc/core/helpers/cove/date'
12
13
 
13
14
  export const useTooltip = props => {
14
- const { tableData, config, formatNumber, capitalize, formatDate, parseDate, setSharedFilter } = useContext<ChartContext>(ConfigContext)
15
+ const { tableData, config, formatNumber, capitalize, formatDate, formatTooltipsDate, parseDate, setSharedFilter } = useContext<ChartContext>(ConfigContext)
15
16
  const { xScale, yScale, showTooltip, hideTooltip } = props
16
- const { xAxis, visualizationType, orientation, yAxis, runtime, barWidth } = config
17
+ const { xAxis, visualizationType, orientation, yAxis, runtime } = config
17
18
  const data = transform.applySuppression(tableData, config.suppressedData)
18
19
  /**
19
20
  * Provides the tooltip information based on the tooltip data array and svg cursor coordinates
@@ -188,7 +189,7 @@ export const useTooltip = props => {
188
189
  return xScale.domain()[index - 1] // fixes off by 1 error
189
190
  }
190
191
 
191
- if (config.xAxis.type === 'date' && config.visualizationType !== 'Combo') {
192
+ if (isDateScale(config.xAxis) && config.visualizationType !== 'Combo') {
192
193
  const bisectDate = bisector(d => parseDate(d[config.xAxis.dataKey])).left
193
194
  const x0 = xScale.invert(xScale(x))
194
195
  const index = bisectDate(config.data, x0, 1)
@@ -207,33 +208,35 @@ export const useTooltip = props => {
207
208
  if (orientation === 'horizontal') return
208
209
 
209
210
  // Check the type of x equal to point or if the type of xAxis is equal to continuous or date
210
- if (xScale.type === 'point' || xAxis.type === 'continuous' || xAxis.type === 'date') {
211
+ if (xScale.type === 'point' || xAxis.type === 'continuous' || isDateScale(xAxis)) {
211
212
  // Find the closest x value by calculating the minimum distance
212
213
  let closestX = null
213
214
  let minDistance = Number.MAX_VALUE
214
215
  let offset = x
215
216
 
216
217
  data.forEach(d => {
217
- const xPosition = xAxis.type === 'date' ? xScale(parseDate(d[xAxis.dataKey])) : xScale(d[xAxis.dataKey])
218
+ const xPosition = isDateScale(xAxis) ? xScale(parseDate(d[xAxis.dataKey])) : xScale(d[xAxis.dataKey])
218
219
  let bwOffset = config.barHeight
219
220
  const distance = Math.abs(Number(xPosition - offset + (isClick ? bwOffset * 2 : 0)))
220
221
 
221
222
  if (distance <= minDistance) {
222
223
  minDistance = distance
223
- closestX = xAxis.type === 'date' ? d[xAxis.dataKey] : d[xAxis.dataKey]
224
+ closestX = isDateScale(xAxis) ? d[xAxis.dataKey] : d[xAxis.dataKey]
224
225
  }
225
226
  })
226
227
  return closestX
227
228
  }
228
229
 
229
230
  if (config.xAxis.type === 'categorical' || (visualizationType === 'Combo' && orientation !== 'horizontal' && visualizationType !== 'Forest Plot')) {
230
- let eachBand = xScale.step()
231
+ let range = xScale.range()[1] - xScale.range()[0]
232
+ let eachBand = range / (xScale.domain().length + 1)
233
+
231
234
  let numerator = x
232
- const index = Math.floor(Number(numerator) / eachBand)
233
- return xScale.domain()[index - 1] // fixes off by 1 error
235
+ const index = Math.floor((Number(numerator) - eachBand / 2) / eachBand)
236
+ return xScale.domain()[index] // fixes off by 1 error
234
237
  }
235
238
 
236
- if (config.xAxis.type === 'date' && visualizationType !== 'Combo' && orientation !== 'horizontal') {
239
+ if (isDateScale(xAxis) && visualizationType !== 'Combo' && orientation !== 'horizontal') {
237
240
  const bisectDate = bisector(d => parseDate(d[config.xAxis.dataKey])).left
238
241
  const x0 = xScale.invert(x)
239
242
  const index = bisectDate(config.data, x0, 1)
@@ -278,7 +281,7 @@ export const useTooltip = props => {
278
281
  let closestXScaleValue = getXValueFromCoordinate(x, true)
279
282
  let datum = config.data?.filter(item => item[config.xAxis.dataKey] === closestXScaleValue)
280
283
  if (!closestXScaleValue) throw new Error('COVE: no closest x scale value in handleTooltipClick')
281
- if (xAxis.type === 'date' && closestXScaleValue) {
284
+ if (isDateScale(xAxis) && closestXScaleValue) {
282
285
  closestXScaleValue = new Date(closestXScaleValue)
283
286
  closestXScaleValue = formatDate(closestXScaleValue)
284
287
  datum = config.data?.filter(item => formatDate(new Date(item[config.xAxis.dataKey])) === closestXScaleValue)
@@ -423,13 +426,15 @@ export const useTooltip = props => {
423
426
  const [key, value, axisPosition] = additionalData
424
427
 
425
428
  if (visualizationType === 'Forest Plot') {
426
- if (key === config.xAxis.dataKey) return <li className='tooltip-heading'>{`${capitalize(config.xAxis.dataKey ? `${config.xAxis.dataKey}: ` : '')} ${config.yAxis.type === 'date' ? formatDate(parseDate(key, false)) : value}`}</li>
429
+ if (key === config.xAxis.dataKey) return <li className='tooltip-heading'>{`${capitalize(config.xAxis.dataKey ? `${config.xAxis.dataKey}: ` : '')} ${isDateScale(yAxis) ? formatDate(parseDate(key, false)) : value}`}</li>
427
430
  return <li className='tooltip-body'>{`${getSeriesNameFromLabel(key)}: ${formatNumber(value, 'left')}`}</li>
428
431
  }
432
+ const formattedDate = config.tooltips.dateDisplayFormat ? formatTooltipsDate(parseDate(value, false)) : formatDate(parseDate(value, false))
429
433
 
430
434
  // TOOLTIP HEADING
431
- if (visualizationType === 'Bar' && orientation === 'horizontal' && key === config.xAxis.dataKey) return <li className='tooltip-heading'>{`${capitalize(config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ` : '')} ${value}`}</li>
432
- if (key === config.xAxis.dataKey) return <li className='tooltip-heading'>{`${capitalize(config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ` : '')} ${config.xAxis.type === 'date' ? value : value}`}</li>
435
+ if (visualizationType === 'Bar' && orientation === 'horizontal' && key === config.xAxis.dataKey) return <li className='tooltip-heading'>{`${capitalize(config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ` : '')} ${config.xAxis.type === 'date' ? formattedDate : value}`}</li>
436
+
437
+ if (key === config.xAxis.dataKey) return <li className='tooltip-heading'>{`${capitalize(config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ` : '')} ${isDateScale(xAxis) ? formattedDate : value}`}</li>
433
438
 
434
439
  // TOOLTIP BODY
435
440
  return <li className='tooltip-body'>{`${getSeriesNameFromLabel(key)}: ${value}`}</li>
@@ -1,73 +1,6 @@
1
1
  @import '@cdc/core/styles/base';
2
2
  @import '@cdc/core/styles/heading-colors';
3
3
  @import '@cdc/core/styles/v2/themes/color-definitions';
4
- .dash-container {
5
- display: flex;
6
- align-items: center;
7
- flex-direction: row;
8
- }
9
-
10
- .legend-dash-left {
11
- margin-left: 8px !important;
12
- display: flex;
13
- flex-direction: column;
14
- }
15
-
16
- .dash-inner {
17
- width: 20px;
18
- margin-left: 0px !important;
19
- display: flex;
20
- align-items: center;
21
- justify-content: center;
22
- }
23
-
24
- .dashes {
25
- display: inline-block;
26
-
27
- &.open-circles {
28
- width: 12px;
29
- height: 12px;
30
- border: 2px solid currentColor;
31
- border-radius: 50%;
32
- }
33
-
34
- &.dashed-small {
35
- margin: 0 0px;
36
- font-size: 20px;
37
- }
38
-
39
- &.dashed-medium,
40
- &.dashed-large {
41
- span {
42
- display: inline-block;
43
- position: relative;
44
- margin-right: 12px;
45
- margin-left: 0 !important;
46
-
47
- &::before {
48
- content: '';
49
- display: block;
50
- height: 2px;
51
- background-color: currentColor;
52
- position: absolute;
53
- top: 50%;
54
- transform: translateY(-20%);
55
- width: 10px;
56
- }
57
- }
58
- }
59
-
60
- &.dashed-large {
61
- span {
62
- margin-right: 12px;
63
- margin-left: 0 !important;
64
-
65
- &::before {
66
- width: 13px;
67
- }
68
- }
69
- }
70
- }
71
4
 
72
5
  .form-container {
73
6
  overflow-y: auto;
@@ -187,30 +120,6 @@
187
120
  }
188
121
  }
189
122
 
190
- .legend-reset {
191
- font-size: 0.7em;
192
- color: rgba(0, 0, 0, 0.6);
193
- position: absolute;
194
- right: 1em;
195
- background: rgba(0, 0, 0, 0.1);
196
- text-transform: uppercase;
197
- transition: 0.3s all;
198
- padding: 0.375rem;
199
- top: 1em;
200
- border-radius: 3px;
201
-
202
- &--dynamic {
203
- position: relative;
204
- float: right;
205
- right: unset;
206
- }
207
-
208
- &:hover {
209
- background: rgba(0, 0, 0, 0.15);
210
- transition: 0.3s all;
211
- }
212
- }
213
-
214
123
  .legend-item {
215
124
  text-align: left;
216
125
  align-items: flex-start !important;
@@ -234,11 +143,11 @@
234
143
  flex: 0 0 auto;
235
144
  }
236
145
 
237
- h2 {
238
- font-size: 1.3em;
146
+ h3 {
147
+ font-size: 1.3rem;
239
148
  }
240
149
 
241
- h2,
150
+ h3,
242
151
  p {
243
152
  margin-bottom: 0.8em;
244
153
  }
@@ -595,21 +504,24 @@
595
504
  }
596
505
 
597
506
  &.animated {
507
+ .vertical path,
598
508
  .vertical rect,
599
509
  .vertical foreignObject div {
600
510
  opacity: 0;
601
- animation: growBar 0.5s linear forwards;
511
+ animation: growBar 0.5s linear both;
602
512
  animation-play-state: paused;
603
513
  }
604
514
 
515
+ .horizontal path,
605
516
  .horizontal rect,
606
517
  .horizontal foreignObject div {
607
518
  opacity: 0;
608
- animation: growBarH 0.5s linear forwards;
519
+ animation: growBarH 0.5s linear both;
609
520
  animation-play-state: paused;
610
521
  }
611
522
 
612
523
  &.animate {
524
+ path,
613
525
  rect,
614
526
  foreignObject div {
615
527
  animation-play-state: running;