@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
@@ -0,0 +1,153 @@
1
+ /* KPI */
2
+ .kpis-container {
3
+ display: flex;
4
+ flex-direction: row;
5
+ column-gap: 30px;
6
+ }
7
+
8
+ .cdc-open-viz-module .sankey-chart {
9
+ --font-size-small: 12px;
10
+ --font-size-medium: 14px;
11
+ --font-size-large: 18px;
12
+ --font-size-xl: 24px;
13
+
14
+ --storynode-font-size--small: 24px;
15
+ --storynode-font-size--medium: 28px;
16
+ --storynode-font-size--large: 32px;
17
+
18
+ --font-weight-normal: 400;
19
+ --font-weight-bold: 700;
20
+
21
+ overflow: visible;
22
+
23
+ .divider {
24
+ border-top: 1px solid #000;
25
+ margin: 10px 0;
26
+ }
27
+
28
+ svg.sankey-chart__diagram {
29
+ position:relative;
30
+ font-family: 'Roboto', sans-serif;
31
+ height: auto;
32
+ width: 100%;
33
+ }
34
+
35
+ .node-id {
36
+ font-weight: var(--font-weight-bold);
37
+ }
38
+
39
+ .node-value {
40
+ font-weight: var(--font-weight-normal);
41
+ }
42
+
43
+ .node-text {
44
+ font-weight: var(--font-weight-normal);
45
+ }
46
+
47
+ .node-value--storynode {
48
+ font-weight: var(--font-weight-bold);
49
+ }
50
+
51
+ /* Hover card */
52
+ .sankey-chart__tooltip {
53
+ color: black;
54
+ display: flex;
55
+ flex-direction: column;
56
+ margin: 10px;
57
+ &--tooltip-header {
58
+ font-weight: var(--font-weight-bold);
59
+ }
60
+ &--info-section {
61
+ column-gap: 10px;
62
+ display: flex;
63
+ flex-direction: row;
64
+ justify-content: space-between;
65
+ }
66
+ }
67
+
68
+ span {
69
+ max-width: 500px;
70
+ word-wrap: break-word;
71
+ }
72
+
73
+ /* Autoscaling */
74
+ //large - default
75
+ @media only screen and (min-width: 1200px) {
76
+ min-width: none;
77
+ .node-text {
78
+ font-size: var(--font-size-xl);
79
+ }
80
+ .node-value--storynode {
81
+ font-size: var(--storynode-font-size--large);
82
+ }
83
+ .node-id {
84
+ font-size: var(--font-size-large);
85
+ }
86
+ .node-value {
87
+ font-size: var(--font-size-large);
88
+ }
89
+ }
90
+ //medium
91
+ @media only screen and (max-width: 1199px) {
92
+ .node-text {
93
+ font-size: var(--font-size-medium);
94
+ }
95
+ .node-value--storynode {
96
+ font-size: var(--storynode-font-size--medium);
97
+ }
98
+ .node-id {
99
+ font-size: var(--font-size-medium);
100
+ }
101
+ .node-value {
102
+ font-size: var(--font-size-medium);
103
+ }
104
+ }
105
+
106
+ //small
107
+ @media only screen and (max-width: 799px) {
108
+ .node-text {
109
+ font-size: var(--font-size-small);
110
+ }
111
+ .node-value--storynode {
112
+ font-size: var(--storynode-font-size--small);
113
+ }
114
+ .node-id {
115
+ font-size: var(--font-size-small);
116
+ }
117
+ .node-value {
118
+ font-size: var(--font-size-small);
119
+ }
120
+ }
121
+
122
+ //x-small
123
+ @media only screen and (max-width: 600px) {
124
+ .popup {
125
+ display: block; /* Show the popup on smaller screens */
126
+ }
127
+ .sankey-chart__diagram {
128
+ opacity: .1;
129
+ }
130
+ }
131
+ }
132
+
133
+ /* Pop up */
134
+
135
+ .popup {
136
+ position: absolute;
137
+ top: 50%;
138
+ left: 50%;
139
+ transform: translate(-50%, -50%);
140
+ background-color: beige;
141
+ border: 2px solid gray !important;
142
+ border-radius: 8px;
143
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
144
+ width: 80%;
145
+ z-index: 999;
146
+ display: none;
147
+ }
148
+
149
+ .popup-content {
150
+ font-size: 30px;
151
+ padding: 10px;
152
+ text-align: center;
153
+ }
@@ -0,0 +1,16 @@
1
+ export type Link = { source: string; target: string; value: number }
2
+
3
+ export type Data = {
4
+ links: Link[]
5
+ }
6
+
7
+ export type SankeyNode = {
8
+ id: string
9
+ }
10
+
11
+ export type SankeyProps = {
12
+ width: number
13
+ height: number
14
+ data: Data
15
+ runtime: any
16
+ }
@@ -41,6 +41,7 @@ const ScatterPlot = ({ xScale, yScale, getXAxisData, getYAxisData }) => {
41
41
  style={pointStyles}
42
42
  data-tooltip-html={handleTooltip(item, s)}
43
43
  data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
44
+ tabIndex={-1}
44
45
  />
45
46
  )
46
47
  })
@@ -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,
@@ -116,7 +116,7 @@ export default {
116
116
  tickColor: '#333',
117
117
  numTicks: '',
118
118
  labelOffset: 65,
119
- axisPadding: 0,
119
+ axisPadding: 200,
120
120
  target: 0,
121
121
  maxTickRotation: 0
122
122
  },
@@ -141,6 +141,7 @@ export default {
141
141
  legend: {
142
142
  hide: false,
143
143
  behavior: 'isolate',
144
+ axisAlign: true,
144
145
  singleRow: true,
145
146
  colorCode: '',
146
147
  reverseLabelOrder: false,
@@ -152,7 +153,8 @@ export default {
152
153
  dynamicLegendChartMessage: 'Select Options from the Legend',
153
154
  lineMode: false,
154
155
  verticalSorted: false,
155
- highlightOnHover: false
156
+ highlightOnHover: false,
157
+ seriesHighlight: []
156
158
  },
157
159
  brush: {
158
160
  height: 25,
@@ -242,5 +244,47 @@ export default {
242
244
  },
243
245
  area: {
244
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: []
245
289
  }
246
290
  }
@@ -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
 
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)
@@ -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,
@@ -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 })
@@ -56,14 +56,14 @@ const useScales = (properties: useScaleProps) => {
56
56
 
57
57
  // handle Vertical bars
58
58
  if (!isHorizontal) {
59
- xScaleBrush = composeScalePoint(xAxisDataKeysMapped, [0, xMax], .5)
59
+ xScaleBrush = composeScalePoint(xAxisDataKeysMapped, [0, xMax], 0.5)
60
60
  xScale = composeScaleBand(xAxisDataMapped, [0, xMax], 1 - config.barThickness)
61
61
  yScale = composeYScale(properties)
62
62
  seriesScale = composeScaleBand(seriesDomain, [0, xScale.bandwidth()], 0)
63
63
  }
64
64
 
65
65
  // handle Area chart
66
- if (config.xAxis.type === 'date' && config.xAxis.sortDates) {
66
+ if (config.xAxis.type === 'date-time') {
67
67
  let xAxisMin = Math.min(...xAxisDataMapped)
68
68
  let xAxisMax = Math.max(...xAxisDataMapped)
69
69
  xAxisMin -= (config.xAxis.padding ? config.xAxis.padding * 0.01 : 0) * (xAxisMax - xAxisMin)
@@ -73,8 +73,8 @@ const useScales = (properties: useScaleProps) => {
73
73
  range: [0, xMax]
74
74
  })
75
75
  xScaleBrush = xScale
76
- xScale.type = scaleTypes.LINEAR
77
- seriesScale = composeScaleBand(seriesDomain, [0, config.barThickness * (xMax)], 0)
76
+ xScale.type = scaleTypes.TIME
77
+ seriesScale = composeScaleBand(seriesDomain, [0, config.barThickness * xMax], 0)
78
78
  }
79
79
 
80
80
  // handle Deviation bar
@@ -249,8 +249,7 @@ const composeXScale = ({ min, max, xMax, config }) => {
249
249
  domain: [min, max],
250
250
  range: [0, xMax],
251
251
  nice: config.useLogScale,
252
- zero: config.useLogScale,
253
- type: config.useLogScale ? 'log' : 'linear'
252
+ zero: config.useLogScale
254
253
  })
255
254
  }
256
255
 
@@ -284,8 +283,7 @@ const composeScalePoint = (domain, range, padding = 0) => {
284
283
  return scalePoint({
285
284
  domain: domain,
286
285
  range: range,
287
- padding: padding,
288
- type: 'point'
286
+ padding: padding
289
287
  })
290
288
  }
291
289
 
@@ -293,7 +291,6 @@ const composeScaleBand = (domain, range, padding = 0) => {
293
291
  return scaleBand({
294
292
  domain: domain,
295
293
  range: range,
296
- padding: padding,
297
- type: 'band'
294
+ padding: padding
298
295
  })
299
296
  }
@@ -9,6 +9,7 @@ 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
15
  const { tableData, config, formatNumber, capitalize, formatDate, formatTooltipsDate, parseDate, setSharedFilter } = useContext<ChartContext>(ConfigContext)
@@ -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,20 +208,20 @@ 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
@@ -235,7 +236,7 @@ export const useTooltip = props => {
235
236
  return xScale.domain()[index] // fixes off by 1 error
236
237
  }
237
238
 
238
- if (config.xAxis.type === 'date' && visualizationType !== 'Combo' && orientation !== 'horizontal') {
239
+ if (isDateScale(xAxis) && visualizationType !== 'Combo' && orientation !== 'horizontal') {
239
240
  const bisectDate = bisector(d => parseDate(d[config.xAxis.dataKey])).left
240
241
  const x0 = xScale.invert(x)
241
242
  const index = bisectDate(config.data, x0, 1)
@@ -280,7 +281,7 @@ export const useTooltip = props => {
280
281
  let closestXScaleValue = getXValueFromCoordinate(x, true)
281
282
  let datum = config.data?.filter(item => item[config.xAxis.dataKey] === closestXScaleValue)
282
283
  if (!closestXScaleValue) throw new Error('COVE: no closest x scale value in handleTooltipClick')
283
- if (xAxis.type === 'date' && closestXScaleValue) {
284
+ if (isDateScale(xAxis) && closestXScaleValue) {
284
285
  closestXScaleValue = new Date(closestXScaleValue)
285
286
  closestXScaleValue = formatDate(closestXScaleValue)
286
287
  datum = config.data?.filter(item => formatDate(new Date(item[config.xAxis.dataKey])) === closestXScaleValue)
@@ -425,7 +426,7 @@ export const useTooltip = props => {
425
426
  const [key, value, axisPosition] = additionalData
426
427
 
427
428
  if (visualizationType === 'Forest Plot') {
428
- 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>
429
430
  return <li className='tooltip-body'>{`${getSeriesNameFromLabel(key)}: ${formatNumber(value, 'left')}`}</li>
430
431
  }
431
432
  const formattedDate = config.tooltips.dateDisplayFormat ? formatTooltipsDate(parseDate(value, false)) : formatDate(parseDate(value, false))
@@ -433,7 +434,7 @@ export const useTooltip = props => {
433
434
  // TOOLTIP HEADING
434
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>
435
436
 
436
- if (key === config.xAxis.dataKey) return <li className='tooltip-heading'>{`${capitalize(config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ` : '')} ${config.xAxis.type === 'date' ? formattedDate : value}`}</li>
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>
437
438
 
438
439
  // TOOLTIP BODY
439
440
  return <li className='tooltip-body'>{`${getSeriesNameFromLabel(key)}: ${value}`}</li>
@@ -120,30 +120,6 @@
120
120
  }
121
121
  }
122
122
 
123
- .legend-reset {
124
- font-size: 0.7em;
125
- color: rgba(0, 0, 0, 0.6);
126
- position: absolute;
127
- right: 1em;
128
- background: rgba(0, 0, 0, 0.1);
129
- text-transform: uppercase;
130
- transition: 0.3s all;
131
- padding: 0.375rem;
132
- top: 1em;
133
- border-radius: 3px;
134
-
135
- &--dynamic {
136
- position: relative;
137
- float: right;
138
- right: unset;
139
- }
140
-
141
- &:hover {
142
- background: rgba(0, 0, 0, 0.15);
143
- transition: 0.3s all;
144
- }
145
- }
146
-
147
123
  .legend-item {
148
124
  text-align: left;
149
125
  align-items: flex-start !important;
@@ -167,11 +143,11 @@
167
143
  flex: 0 0 auto;
168
144
  }
169
145
 
170
- h2 {
171
- font-size: 1.3em;
146
+ h3 {
147
+ font-size: 1.3rem;
172
148
  }
173
149
 
174
- h2,
150
+ h3,
175
151
  p {
176
152
  margin-bottom: 0.8em;
177
153
  }
@@ -528,21 +504,24 @@
528
504
  }
529
505
 
530
506
  &.animated {
507
+ .vertical path,
531
508
  .vertical rect,
532
509
  .vertical foreignObject div {
533
510
  opacity: 0;
534
- animation: growBar 0.5s linear forwards;
511
+ animation: growBar 0.5s linear both;
535
512
  animation-play-state: paused;
536
513
  }
537
514
 
515
+ .horizontal path,
538
516
  .horizontal rect,
539
517
  .horizontal foreignObject div {
540
518
  opacity: 0;
541
- animation: growBarH 0.5s linear forwards;
519
+ animation: growBarH 0.5s linear both;
542
520
  animation-play-state: paused;
543
521
  }
544
522
 
545
523
  &.animate {
524
+ path,
546
525
  rect,
547
526
  foreignObject div {
548
527
  animation-play-state: running;