@cdc/chart 4.23.9 → 4.23.10

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 (31) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcchart.js +44099 -44436
  3. package/examples/feature/__data__/area-chart-date-apple.json +1 -5073
  4. package/examples/feature/area/area-chart-date-apple.json +73 -10316
  5. package/examples/feature/area/area-chart-date-city-temperature.json +204 -80
  6. package/examples/feature/area/area-chart-stacked.json +239 -0
  7. package/examples/feature/filters/bar-filter.json +5027 -0
  8. package/examples/feature/legend-highlights/highlights.json +567 -0
  9. package/index.html +9 -6
  10. package/package.json +3 -2
  11. package/src/{CdcChart.jsx → CdcChart.tsx} +77 -71
  12. package/src/components/AreaChart.Stacked.jsx +73 -0
  13. package/src/components/AreaChart.jsx +24 -26
  14. package/src/components/DeviationBar.jsx +67 -13
  15. package/src/components/EditorPanel.jsx +483 -452
  16. package/src/components/Forecasting.jsx +5 -5
  17. package/src/components/Legend.jsx +6 -5
  18. package/src/components/LineChart.Circle.tsx +108 -0
  19. package/src/components/{LineChart.jsx → LineChart.tsx} +10 -42
  20. package/src/components/LinearChart.jsx +460 -443
  21. package/src/components/PieChart.jsx +54 -25
  22. package/src/components/Series.jsx +63 -17
  23. package/src/components/SparkLine.jsx +7 -19
  24. package/src/data/initial-state.js +6 -0
  25. package/src/hooks/useEditorPermissions.js +87 -24
  26. package/src/hooks/useReduceData.js +5 -0
  27. package/src/hooks/useScales.js +1 -1
  28. package/src/hooks/useTooltip.jsx +19 -6
  29. package/src/scss/main.scss +6 -12
  30. package/src/components/DataTable.jsx +0 -494
  31. /package/src/{components → hooks}/useIntersectionObserver.jsx +0 -0
@@ -3,7 +3,7 @@ import React, { useContext } from 'react'
3
3
  // cdc
4
4
  import ConfigContext from '../ConfigContext'
5
5
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
6
- import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
6
+ import { colorPalettesChart, sequentialPalettes } from '@cdc/core/data/colorPalettes'
7
7
 
8
8
  // visx & d3
9
9
  import { curveMonotoneX } from '@visx/curve'
@@ -30,15 +30,15 @@ const Forecasting = ({ xScale, yScale, height, width, handleTooltipMouseOver, ha
30
30
  return (
31
31
  <Group className={`forecasting-areas-combo-${index}`} key={`forecasting-areas--stage-${stage.key.replaceAll(' ', '-')}-${index}`}>
32
32
  {group.confidenceIntervals?.map((ciGroup, ciGroupIndex) => {
33
- const palette = colorPalettesChart[stage.color]
33
+ const palette = sequentialPalettes[stage.color] || colorPalettesChart[stage.color] || false
34
34
 
35
35
  const getFill = () => {
36
- if (displayArea) return palette[ciGroupIndex] ? palette[ciGroupIndex] : 'transparent'
36
+ if (displayArea) return palette[2] ? palette[2] : 'transparent'
37
37
  return 'transparent'
38
38
  }
39
39
 
40
40
  const getStroke = () => {
41
- if (displayArea) return palette[2] ? palette[2] : 'transparent'
41
+ if (displayArea) return palette[1] ? palette[1] : 'transparent'
42
42
  return 'transparent'
43
43
  }
44
44
 
@@ -50,7 +50,7 @@ const Forecasting = ({ xScale, yScale, height, width, handleTooltipMouseOver, ha
50
50
  curve={curveMonotoneX}
51
51
  data={groupData}
52
52
  fill={getFill()}
53
- opacity={transparentArea ? 0.1 : 0.5}
53
+ opacity={transparentArea ? 0.1 : .5}
54
54
  x={d => xScale(Date.parse(d[xAxis.dataKey]))}
55
55
  y0={d => yScale(d[ciGroup.low])}
56
56
  y1={d => yScale(d[ciGroup.high])}
@@ -7,6 +7,7 @@ import LegendCircle from '@cdc/core/components/LegendCircle'
7
7
  import useLegendClasses from './../hooks/useLegendClasses'
8
8
  import { useHighlightedBars } from '../hooks/useHighlightedBars'
9
9
  import { Line } from '@visx/shape'
10
+ import { sequentialPalettes } from '@cdc/core/data/colorPalettes'
10
11
 
11
12
  // * FILE REVIEW *
12
13
  // TODO: fix eslint-disable jsxa11y issues
@@ -92,7 +93,7 @@ const Legend = () => {
92
93
  // loop through each stage/group/area on the chart and create a label
93
94
  config.runtime?.forecastingSeriesKeys?.map((outerGroup, index) => {
94
95
  return outerGroup?.stages?.map((stage, index) => {
95
- let colorValue = colorPalettes[stage.color]?.[2] ? colorPalettes[stage.color]?.[2] : '#ccc'
96
+ let colorValue = sequentialPalettes[stage.color]?.[2] ? sequentialPalettes[stage.color]?.[2] : colorPalettes[stage.color]?.[2] ? colorPalettes[stage.color]?.[2] : '#ccc'
96
97
 
97
98
  const newLabel = {
98
99
  datum: stage.key,
@@ -161,7 +162,7 @@ const Legend = () => {
161
162
 
162
163
  const legendClasses = {
163
164
  marginBottom: isBottomOrSmallViewport ? '15px' : '0px',
164
- marginTop: isBottomOrSmallViewport && orientation === 'horizontal' ? `${config.yAxis.label && config.isResponsiveTicks ? config.dynamicMarginTop : config.runtime.xAxis.size}px` : `0px`
165
+ marginTop: isBottomOrSmallViewport && orientation === 'horizontal' ? `${config.yAxis.label && config.isResponsiveTicks ? config.dynamicMarginTop : config.runtime.xAxis.size}px` : `${config.dynamicMarginTop + 15}px`
165
166
  }
166
167
 
167
168
  const { HighLightedBarUtils } = useHighlightedBars(config)
@@ -179,7 +180,7 @@ const Legend = () => {
179
180
  return (
180
181
  <div className={innerClasses.join(' ')}>
181
182
  {createLegendLabels(labels).map((label, i) => {
182
- let className = 'legend-item'
183
+ let className = ['legend-item', `legend-text--${label.text.replace(' ', '').toLowerCase()}`]
183
184
  let itemName = label.datum
184
185
 
185
186
  // Filter excluded data keys from legend
@@ -197,12 +198,12 @@ const Legend = () => {
197
198
  }
198
199
 
199
200
  if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
200
- className += ' inactive'
201
+ className.push('inactive')
201
202
  }
202
203
 
203
204
  return (
204
205
  <LegendItem
205
- className={className}
206
+ className={className.join(' ')}
206
207
  tabIndex={0}
207
208
  key={`legend-quantile-${i}`}
208
209
  onKeyPress={e => {
@@ -0,0 +1,108 @@
1
+ import React from 'react'
2
+
3
+ // todo: change this config obj to ChartConfig once its created
4
+ type LineChartCircleProps = {
5
+ config: {
6
+ xAxis: string
7
+ data: Object[]
8
+ lineDatapointStyle: string
9
+ runtime: Object
10
+ }
11
+ d?: Object
12
+ displayArea: boolean
13
+ seriesKey: string
14
+ tooltipData: {
15
+ data: []
16
+ tooltipDataX: number
17
+ tooltipDataY: number
18
+ }
19
+ xScale: any
20
+ yScale: any
21
+ yScaleRight: any
22
+ colorScale: any
23
+ parseDate: any
24
+ seriesAxis: string
25
+ }
26
+
27
+ const LineChartCircle = (props: LineChartCircleProps) => {
28
+ const { config, d, displayArea, seriesKey, tooltipData, xScale, yScale, colorScale, parseDate, yScaleRight } = props
29
+ const { lineDatapointStyle } = config
30
+ const filtered = config?.series.filter(s => s.dataKey === seriesKey)?.[0]
31
+
32
+ if (lineDatapointStyle === 'hidden') return null
33
+
34
+ const getColor = (displayArea, colorScale, config, seriesIndex, hoveredKey, seriesKey) => {
35
+ const customColors = config.customColors || []
36
+ const seriesLabels = config.runtime.seriesLabels || []
37
+ let color
38
+
39
+ const getIndex = seriesKey => config.runtime.seriesLabelsAll.indexOf(seriesKey)
40
+
41
+ if (displayArea) {
42
+ if (colorScale) {
43
+ if (getIndex(hoveredKey) === false) return
44
+ color = customColors.length > 0 ? customColors[getIndex(hoveredKey)] : colorScale(seriesLabels[hoveredKey] || seriesKey)
45
+ } else if (customColors) {
46
+ color = customColors.length > 0 ? customColors[getIndex(hoveredKey)] : 'transparent'
47
+ }
48
+ } else {
49
+ color = 'transparent'
50
+ }
51
+ console.log('color', color)
52
+ return color
53
+ }
54
+
55
+ if (lineDatapointStyle === 'always show') {
56
+ return (
57
+ <circle
58
+ cx={config.xAxis.type === 'categorical' ? xScale(d[config.xAxis.dataKey]) : xScale(parseDate(d[config.xAxis.dataKey]))}
59
+ cy={filtered.axis === 'Right' ? yScaleRight(d[filtered.dataKey]) : yScale(d[filtered.dataKey])}
60
+ r={4.5}
61
+ opacity={d[seriesKey] ? 1 : 0}
62
+ fillOpacity={1}
63
+ fill={displayArea ? (colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000') : 'transparent'}
64
+ style={{ filter: 'unset', opacity: 1 }}
65
+ />
66
+ )
67
+ }
68
+
69
+ if (lineDatapointStyle === 'hover') {
70
+ if (!tooltipData) return
71
+ if (!tooltipData.data) return
72
+ let hoveredXValue = tooltipData?.data?.[0]?.[1]
73
+ if (!hoveredXValue) return
74
+ let hoveredSeriesValue
75
+ let hoveredSeriesIndex
76
+ let hoveredSeriesData = tooltipData.data.filter(d => d[0] === seriesKey)
77
+ let hoveredSeriesKey = hoveredSeriesData?.[0]?.[0]
78
+ let hoveredSeriesAxis = hoveredSeriesData?.[0]?.[2]
79
+ hoveredSeriesIndex = tooltipData.data.indexOf(hoveredSeriesKey)
80
+ hoveredSeriesValue = hoveredSeriesData?.[0]?.[1]
81
+
82
+ console.log('hoveredSeriesKey', hoveredSeriesKey)
83
+ console.log('hoveredSeriesAxis', hoveredSeriesAxis)
84
+ console.log('hoveredSeriesValue', hoveredSeriesValue)
85
+
86
+ console.log('hoveredSeriesData', hoveredSeriesData)
87
+ return tooltipData.data.map((tooltipItem, index) => {
88
+ let seriesIndex = config.runtime.seriesLabelsAll.indexOf(hoveredXValue)
89
+
90
+ if (isNaN(hoveredSeriesValue)) return <></>
91
+ return (
92
+ <circle
93
+ cx={config.xAxis.type === 'categorical' ? xScale(hoveredXValue) : xScale(parseDate(hoveredXValue))}
94
+ cy={hoveredSeriesAxis === 'right' ? yScaleRight(hoveredSeriesValue) : yScale(hoveredSeriesValue)}
95
+ r={4.5}
96
+ opacity={1}
97
+ fillOpacity={1}
98
+ fill={getColor(displayArea, colorScale, config, seriesIndex, hoveredSeriesKey, seriesKey)}
99
+ style={{ filter: 'unset', opacity: 1 }}
100
+ />
101
+ )
102
+ })
103
+ }
104
+
105
+ return null
106
+ }
107
+
108
+ export default LineChartCircle
@@ -8,6 +8,7 @@ import { Text } from '@visx/text'
8
8
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
9
9
  import ConfigContext from '../ConfigContext'
10
10
  import useRightAxis from '../hooks/useRightAxis'
11
+ import LineChartCircle from './LineChart.Circle'
11
12
 
12
13
  const LineChart = ({ xScale, yScale, getXAxisData, getYAxisData, xMax, yMax, handleTooltipMouseOver, handleTooltipMouseOff, showTooltip, seriesStyle = 'Line', svgRef, handleTooltipClick, tooltipData }) => {
13
14
  // Not sure why there's a redraw here.
@@ -27,6 +28,7 @@ const LineChart = ({ xScale, yScale, getXAxisData, getYAxisData, xMax, yMax, han
27
28
  }
28
29
 
29
30
  const DEBUG = false
31
+
30
32
  return (
31
33
  <ErrorBoundary component='LineChart'>
32
34
  <Group left={config.runtime.yAxis.size ? parseInt(config.runtime.yAxis.size) : 66}>
@@ -58,18 +60,6 @@ const LineChart = ({ xScale, yScale, getXAxisData, getYAxisData, xMax, yMax, han
58
60
  // if has muiltiple series dont show legend value on tooltip
59
61
  if (!hasMultipleSeries) label = config.isLegendValue ? config.runtime.seriesLabels[seriesKey] : label
60
62
 
61
- let yAxisTooltip = handleAxisFormating(axis, label, yAxisValue)
62
- let xAxisTooltip = handleAxisFormating(axis, config.runtime.xAxis.label, xAxisValue)
63
-
64
- const tooltip = `<div>
65
- ${config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && Object.keys(config.runtime.seriesLabels).length > 1 ? `${config.runtime.seriesLabels[seriesKey] || ''}<br/>` : ''}
66
- ${yAxisTooltip}<br />
67
- ${xAxisTooltip}
68
- </div>`
69
-
70
- // TODO: move all instances of circleRadii to state.
71
- let circleRadii = 4.5
72
-
73
63
  return (
74
64
  d[seriesKey] !== undefined &&
75
65
  d[seriesKey] !== '' &&
@@ -84,46 +74,24 @@ const LineChart = ({ xScale, yScale, getXAxisData, getYAxisData, xMax, yMax, han
84
74
  {formatNumber(d[seriesKey], 'left')}
85
75
  </Text>
86
76
 
87
- <circle
88
- key={`${seriesKey}-${dataIndex}`}
89
- r={circleRadii}
90
- cx={Number(xScale(getXAxisData(d)))}
91
- cy={seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey))}
92
- fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
93
- style={{
94
- fill: colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'
95
- }}
96
- data-tooltip-html={tooltip}
97
- data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
98
- />
99
-
100
- {/* circles that appear on hover */}
101
- {/* todo: circle radii used here should be global with other circle radii */}
102
- {/* {tooltipData && Object.entries(tooltipData.data).length > 0 && isNumber(tooltipData.data[seriesKey]) && config.lineDatapointStyle === 'hover' && config.series.filter(s => s.type === 'Line') && (
103
- <circle
104
- cx={config.xAxis.type === 'categorical' ? xScale(tooltipData.data[config.xAxis.dataKey]) : xScale(parseDate(tooltipData.data[config.xAxis.dataKey]))}
105
- cy={yScale(tooltipData.data[seriesKey])}
106
- r={4.5}
107
- opacity={tooltipData[seriesKey] ? 1 : 0}
108
- fillOpacity={1}
109
- fill={displayArea ? (colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000') : 'transparent'}
110
- style={{ filter: 'unset', opacity: 1 }}
111
- />
112
- )} */}
77
+ {config.lineDatapointStyle === 'hidden' ||
78
+ (config.lineDatapointStyle === 'always show' && <LineChartCircle d={d} config={config} seriesKey={seriesKey} displayArea={displayArea} tooltipData={tooltipData} xScale={xScale} yScale={yScale} colorScale={colorScale} parseDate={parseDate} yScaleRight={yScaleRight} />)}
113
79
  </Group>
114
80
  )
115
81
  )
116
82
  })}
83
+
84
+ <>{config.lineDatapointStyle === 'hover' && <LineChartCircle config={config} seriesKey={seriesKey} displayArea={displayArea} tooltipData={tooltipData} xScale={xScale} yScale={yScale} colorScale={colorScale} parseDate={parseDate} yScaleRight={yScaleRight} seriesAxis={seriesAxis} />}</>
85
+
117
86
  <LinePath
118
87
  curve={allCurves[seriesData[0].lineType]}
119
88
  data={data}
120
89
  x={d => xScale(getXAxisData(d))}
121
90
  y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey)))}
122
91
  stroke={
123
- colorScale && !config.legend.dynamicLegend
124
- ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey)
125
- : // is dynamic legend
126
- config.legend.dynamicLegend
92
+ config.customColors
93
+ ? config.customColors[index]
94
+ : colorScale
127
95
  ? colorPalettes[config.palette][index]
128
96
  : // fallback
129
97
  '#000'