@cdc/chart 4.24.2 → 4.24.4

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 (60) hide show
  1. package/dist/cdcchart.js +47933 -36918
  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 +362 -37
  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/region-issue.json +2065 -0
  11. package/examples/sparkline.json +868 -0
  12. package/examples/test.json +5409 -0
  13. package/index.html +130 -123
  14. package/package.json +4 -2
  15. package/src/CdcChart.tsx +178 -94
  16. package/src/_stories/ChartEditor.stories.tsx +14 -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 -1
  20. package/src/components/BarChart/components/BarChart.Horizontal.tsx +46 -63
  21. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +36 -56
  22. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +32 -39
  23. package/src/components/BarChart/components/BarChart.Vertical.tsx +44 -59
  24. package/src/components/BoxPlot/BoxPlot.jsx +2 -1
  25. package/src/components/DeviationBar.jsx +3 -3
  26. package/src/components/EditorPanel/EditorPanel.tsx +1684 -1564
  27. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +1 -1
  28. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +107 -0
  29. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +48 -4
  30. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +41 -0
  31. package/src/components/EditorPanel/components/Panels/index.tsx +9 -7
  32. package/src/components/EditorPanel/components/panels.scss +11 -0
  33. package/src/components/EditorPanel/editor-panel.scss +0 -724
  34. package/src/components/EditorPanel/useEditorPermissions.js +40 -14
  35. package/src/components/Legend/Legend.Component.tsx +43 -63
  36. package/src/components/Legend/Legend.tsx +8 -4
  37. package/src/components/LineChart/LineChartProps.ts +1 -0
  38. package/src/components/LineChart/helpers.ts +2 -2
  39. package/src/components/LineChart/index.tsx +7 -7
  40. package/src/components/LinearChart.jsx +11 -31
  41. package/src/components/PairedBarChart.jsx +6 -10
  42. package/src/components/PieChart/PieChart.tsx +3 -3
  43. package/src/components/Regions/components/Regions.tsx +120 -78
  44. package/src/components/Sankey/index.tsx +434 -0
  45. package/src/components/Sankey/sankey.scss +153 -0
  46. package/src/components/Sankey/types/index.ts +16 -0
  47. package/src/components/ScatterPlot/ScatterPlot.jsx +1 -0
  48. package/src/components/Sparkline/{SparkLine.jsx → components/SparkLine.tsx} +14 -30
  49. package/src/components/Sparkline/index.scss +3 -0
  50. package/src/components/Sparkline/index.tsx +1 -1
  51. package/src/components/ZoomBrush.tsx +2 -1
  52. package/src/data/initial-state.js +46 -2
  53. package/src/helpers/computeMarginBottom.ts +2 -1
  54. package/src/helpers/tests/computeMarginBottom.test.ts +2 -1
  55. package/src/hooks/useBarChart.js +5 -2
  56. package/src/hooks/useScales.ts +47 -18
  57. package/src/hooks/useTooltip.tsx +9 -8
  58. package/src/scss/main.scss +33 -29
  59. package/src/types/ChartConfig.ts +32 -14
  60. package/src/types/ChartContext.ts +7 -0
@@ -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
@@ -238,6 +238,38 @@ const useScales = (properties: useScaleProps) => {
238
238
 
239
239
  export default useScales
240
240
 
241
+ export const getTickValues = (xAxisDataMapped, xScale, num) => {
242
+ const xDomain = xScale.domain();
243
+
244
+ if(xScale.type === 'time'){
245
+ const xDomainMax = xAxisDataMapped[xAxisDataMapped.length - 1];
246
+ const xDomainMin = xAxisDataMapped[0];
247
+ const step = (xDomainMax - xDomainMin) / (num - 1);
248
+ const tickValues = [];
249
+ for(let i = xDomainMax; i >= xDomainMin; i -= step){
250
+ tickValues.push(i);
251
+ }
252
+ if(tickValues[tickValues.length - 1] !== xDomainMin){
253
+ tickValues.push(xDomainMin);
254
+ }
255
+ tickValues.reverse();
256
+
257
+ return tickValues;
258
+ }
259
+
260
+ if(xDomain.length > 2){
261
+ const step = num || 1;
262
+ const tickValues = [];
263
+ for(let i = xDomain.length; i > 0; i -= step){
264
+ const adjustedIndex = Math.max(Math.round(i) - 1, 0);
265
+ tickValues.push(xDomain[adjustedIndex]);
266
+ }
267
+ tickValues.reverse();
268
+
269
+ return tickValues;
270
+ }
271
+ }
272
+
241
273
  /// helper functions
242
274
  const composeXScale = ({ min, max, xMax, config }) => {
243
275
  // Adjust min value if using logarithmic scale
@@ -249,8 +281,7 @@ const composeXScale = ({ min, max, xMax, config }) => {
249
281
  domain: [min, max],
250
282
  range: [0, xMax],
251
283
  nice: config.useLogScale,
252
- zero: config.useLogScale,
253
- type: config.useLogScale ? 'log' : 'linear'
284
+ zero: config.useLogScale
254
285
  })
255
286
  }
256
287
 
@@ -284,8 +315,7 @@ const composeScalePoint = (domain, range, padding = 0) => {
284
315
  return scalePoint({
285
316
  domain: domain,
286
317
  range: range,
287
- padding: padding,
288
- type: 'point'
318
+ padding: padding
289
319
  })
290
320
  }
291
321
 
@@ -293,7 +323,6 @@ const composeScaleBand = (domain, range, padding = 0) => {
293
323
  return scaleBand({
294
324
  domain: domain,
295
325
  range: range,
296
- padding: padding,
297
- type: 'band'
326
+ padding: padding
298
327
  })
299
328
  }
@@ -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;
@@ -154,6 +130,9 @@
154
130
  word-wrap: break-word;
155
131
  white-space: pre-wrap;
156
132
  word-break: break-word;
133
+ @include breakpoint(xs) {
134
+ font-size: $font-small;
135
+ }
157
136
  }
158
137
  }
159
138
 
@@ -167,13 +146,16 @@
167
146
  flex: 0 0 auto;
168
147
  }
169
148
 
170
- h2 {
171
- font-size: 1.3em;
149
+ h3 {
150
+ font-size: 1.3rem;
172
151
  }
173
152
 
174
- h2,
153
+ h3,
175
154
  p {
176
155
  margin-bottom: 0.8em;
156
+ @include breakpoint(xs) {
157
+ font-size: $font-small + 0.2em;
158
+ }
177
159
  }
178
160
  & div.legend-item {
179
161
  margin-bottom: 0.5em !important;
@@ -216,6 +198,20 @@
216
198
  }
217
199
  }
218
200
 
201
+ .legend-preliminary {
202
+ display: flex;
203
+ flex-direction: row;
204
+ align-items: center;
205
+ @include breakpoint(xs) {
206
+ font-size: $font-small;
207
+ }
208
+
209
+ & > svg {
210
+ width: 50px;
211
+ height: 23px;
212
+ }
213
+ }
214
+
219
215
  .visx-tooltip {
220
216
  z-index: 100000;
221
217
  }
@@ -528,21 +524,24 @@
528
524
  }
529
525
 
530
526
  &.animated {
527
+ .vertical path,
531
528
  .vertical rect,
532
529
  .vertical foreignObject div {
533
530
  opacity: 0;
534
- animation: growBar 0.5s linear forwards;
531
+ animation: growBar 0.5s linear both;
535
532
  animation-play-state: paused;
536
533
  }
537
534
 
535
+ .horizontal path,
538
536
  .horizontal rect,
539
537
  .horizontal foreignObject div {
540
538
  opacity: 0;
541
- animation: growBarH 0.5s linear forwards;
539
+ animation: growBarH 0.5s linear both;
542
540
  animation-play-state: paused;
543
541
  }
544
542
 
545
543
  &.animate {
544
+ path,
546
545
  rect,
547
546
  foreignObject div {
548
547
  animation-play-state: running;
@@ -620,6 +619,11 @@
620
619
  }
621
620
  }
622
621
 
622
+ .cdc-open-viz-module .is-editor .cdc-chart-inner-container {
623
+ overflow: hidden;
624
+ background-color: var(--white);
625
+ }
626
+
623
627
  .isEditor.type-sparkline .cdc-chart-inner-container {
624
628
  margin: 3em auto 0;
625
629
  max-width: 60%;
@@ -7,20 +7,14 @@ import { FilterBehavior } from '@cdc/core/types/FilterBehavior'
7
7
  import { Table } from '@cdc/core/types/Table'
8
8
  import { BoxPlot } from '@cdc/core/types/BoxPlot'
9
9
  import { General } from '@cdc/core/types/General'
10
+ import { type Link } from './../components/Sankey/types'
11
+ import { Legend } from '@cdc/core/types/Legend'
12
+ import { ConfidenceInterval } from '@cdc/core/types/ConfidenceInterval'
13
+ import { Region } from '@cdc/core/types/Region'
14
+ import { type PreliminaryDataItem } from '../components/LineChart/LineChartProps'
10
15
 
11
16
  export type ChartColumns = Record<string, Column>
12
17
 
13
- type Region = {
14
- from: string
15
- to: string
16
- fromType: 'Previous Days' | 'Fixed Date'
17
- toType: 'Last Date' | 'Fised Date'
18
- label: string
19
- color: string
20
- background: string
21
- range: 'Custom' | string
22
- }
23
-
24
18
  type DataFormat = {
25
19
  abbreviated: boolean
26
20
  bottomAbbreviated: boolean
@@ -58,9 +52,11 @@ type Filter = {
58
52
  }
59
53
 
60
54
  export type Legend = {
55
+ seriesHighlight: string[]
61
56
  additionalCategories: string[]
62
57
  // general legend onClick behavior
63
58
  behavior: 'highlight' | 'isolate' | string
59
+ axisAlign: boolean
64
60
  colorCode: string
65
61
  description: string
66
62
  // show or hide the legend
@@ -102,7 +98,7 @@ type AllChartsConfig = {
102
98
  color: string
103
99
  colorMatchLineSeriesLabels: boolean
104
100
  columns: ChartColumns
105
- confidenceKeys: Record<string, any>
101
+ confidenceKeys: ConfidenceInterval
106
102
  customColors: string[]
107
103
  data: Object[]
108
104
  dataUrl: string
@@ -139,6 +135,7 @@ type AllChartsConfig = {
139
135
  orientation: 'vertical' | 'horizontal'
140
136
  palette: string
141
137
  pieType?: string
138
+ preliminaryData: PreliminaryDataItem[]
142
139
  primary?: DataFormat
143
140
  roundingStyle: string
144
141
  runtime: Runtime
@@ -162,16 +159,37 @@ type AllChartsConfig = {
162
159
  }
163
160
  topAxis: { hasLine: boolean }
164
161
  twoColor: { palette: string }
165
- type: string
162
+ type: 'chart' | 'dashboard'
166
163
  useLogScale: boolean
167
164
  visual: Visual
168
- visualizationType: 'Area Chart' | 'Bar' | 'Box Plot' | 'Deviation Bar' | 'Forest Plot' | 'Line' | 'Paired Bar' | 'Pie' | 'Scatter Plot' | 'Spark Line' | 'Combo' | 'Forecasting'
165
+ visualizationType: 'Area Chart' | 'Bar' | 'Box Plot' | 'Deviation Bar' | 'Forest Plot' | 'Line' | 'Paired Bar' | 'Pie' | 'Scatter Plot' | 'Spark Line' | 'Combo' | 'Forecasting' | 'Sankey'
169
166
  visualizationSubType: string
170
167
  xAxis: Axis
171
168
  yAxis: Axis
172
169
  xScale: Function
173
170
  yScale: Function
174
171
  regions: Region[]
172
+ sankey: {
173
+ data: { links: Link[]; storyNodeText: Object[]; tooltips: Object[] }[]
174
+ nodePadding: number
175
+ iterations: number
176
+ nodeSize: {
177
+ nodeWidth: number
178
+ }
179
+ margin: { margin_x: number; margin_y: number }
180
+ nodeColor: { default: boolean; inactive: boolean }
181
+ opacity: { LinkOpacityInactive: string; LinkOpacityDefault: string; nodeOpacityInactive: boolean; nodeOpacityDefault: boolean }
182
+ rxValue: number
183
+ nodeFontColor: string
184
+ nodeValueStyle: {
185
+ textBefore: string
186
+ textAfter: string
187
+ }
188
+ linkColor: {
189
+ inactive: string
190
+ default: string
191
+ }
192
+ }
175
193
  }
176
194
 
177
195
  export type ForestPlotConfig = {