@cdc/chart 4.23.9 → 4.23.10-alpha

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 (40) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcchart.js +44124 -44458
  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/bar/lollipop.json +156 -0
  8. package/examples/feature/combo/planet-combo-example-config.json +99 -9
  9. package/examples/feature/filters/bar-filter.json +5027 -0
  10. package/examples/feature/legend-highlights/highlights.json +567 -0
  11. package/examples/private/TESTING.json +0 -0
  12. package/examples/private/forest-plot.json +356 -0
  13. package/examples/private/full.json +45324 -0
  14. package/examples/private/missing-color.json +333 -0
  15. package/index.html +11 -7
  16. package/package.json +3 -2
  17. package/src/{CdcChart.jsx → CdcChart.tsx} +81 -74
  18. package/src/_stories/Chart.stories.tsx +188 -0
  19. package/src/components/AreaChart.Stacked.jsx +73 -0
  20. package/src/components/AreaChart.jsx +24 -26
  21. package/src/components/DeviationBar.jsx +67 -13
  22. package/src/components/EditorPanel.jsx +483 -452
  23. package/src/components/Forecasting.jsx +5 -5
  24. package/src/components/ForestPlotSettings.jsx +5 -6
  25. package/src/components/Legend.jsx +7 -6
  26. package/src/components/LineChart.Circle.tsx +102 -0
  27. package/src/components/{LineChart.jsx → LineChart.tsx} +9 -48
  28. package/src/components/LinearChart.jsx +460 -443
  29. package/src/components/PieChart.jsx +54 -25
  30. package/src/components/Series.jsx +63 -17
  31. package/src/components/SparkLine.jsx +7 -19
  32. package/src/data/initial-state.js +6 -0
  33. package/src/hooks/useBarChart.js +1 -1
  34. package/src/hooks/useEditorPermissions.js +87 -24
  35. package/src/hooks/useReduceData.js +5 -0
  36. package/src/hooks/useScales.js +3 -3
  37. package/src/hooks/useTooltip.jsx +19 -6
  38. package/src/scss/main.scss +6 -12
  39. package/src/components/DataTable.jsx +0 -494
  40. /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])}
@@ -401,7 +401,6 @@ const ForestPlotSettings = () => {
401
401
  options={getColumns(false)}
402
402
  />
403
403
 
404
- <CheckBox value={config.forestPlot.showZeroLine} section='forestPlot' fieldName='showZeroLine' label='Show Line on Zero' updateField={updateField} />
405
404
  <Select
406
405
  value={config.forestPlot.shape}
407
406
  label='Point Estimate Shape'
@@ -422,7 +421,7 @@ const ForestPlotSettings = () => {
422
421
  />
423
422
  <Select
424
423
  value={config.forestPlot.radius.scalingColumn}
425
- label='Scale Radius Column'
424
+ label='Weight Column'
426
425
  initial={'Select'}
427
426
  onChange={e => {
428
427
  if (e.target.value !== '' && e.target.value !== 'Select') {
@@ -491,10 +490,10 @@ const ForestPlotSettings = () => {
491
490
  <br />
492
491
  <hr />
493
492
  <br />
494
- <h4>Add Regression Line</h4>
495
- <TextField type='number' value={config.forestPlot?.regression?.upper || ''} updateField={updateField} section='forestPlot' subsection='regression' fieldName='upper' label='Upper Value' />
496
- <TextField type='number' value={config.forestPlot?.regression?.lower || ''} updateField={updateField} section='forestPlot' subsection='regression' fieldName='lower' label='Lower Value' />
497
- <TextField type='number' value={config.forestPlot?.regression?.estimateField || ''} updateField={updateField} section='forestPlot' subsection='regression' fieldName='estimateField' label='Estimate Value' />
493
+ {/* <h4>Add Regression Line</h4> */}
494
+ {/* <TextField type='number' value={config.forestPlot?.regression?.upper || ''} updateField={updateField} section='forestPlot' subsection='regression' fieldName='upper' label='Upper Value' /> */}
495
+ {/* <TextField type='number' value={config.forestPlot?.regression?.lower || ''} updateField={updateField} section='forestPlot' subsection='regression' fieldName='lower' label='Lower Value' /> */}
496
+ <TextField type='number' value={config.forestPlot?.regression?.estimateField || ''} updateField={updateField} section='forestPlot' subsection='regression' fieldName='estimateField' label='Line of No Effect' />
498
497
  <TextField type='text' value={config.forestPlot?.regression?.baseLineColor || 'black'} updateField={updateField} section='forestPlot' subsection='regression' fieldName='baseLineColor' label='Base Color' />
499
498
  <CheckBox value={config.forestPlot?.regression?.showBaseLine || false} section='forestPlot' subsection='regression' fieldName='showBaseLine' label='Show base line' updateField={updateField} />
500
499
  <CheckBox value={config.forestPlot?.regression?.showDiamond || false} section='forestPlot' subsection='regression' fieldName='showDiamond' label='Show Diamond' updateField={updateField} />
@@ -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,
@@ -146,7 +147,7 @@ const Legend = () => {
146
147
  datum: val,
147
148
  index: i,
148
149
  text: val,
149
- value: palette[i]
150
+ value: colorScale(val)
150
151
  }
151
152
  return newLabel
152
153
  })
@@ -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,102 @@
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 = colorScale(seriesLabels[hoveredKey] || seriesKey)
45
+ } else if (customColors) {
46
+ color = customColors.length > 0 ? colorScale(getIndex(hoveredKey)) : 'transparent'
47
+ }
48
+ } else {
49
+ color = 'transparent'
50
+ }
51
+ return color
52
+ }
53
+
54
+ if (lineDatapointStyle === 'always show') {
55
+ return (
56
+ <circle
57
+ cx={config.xAxis.type === 'categorical' ? xScale(d[config.xAxis.dataKey]) : xScale(parseDate(d[config.xAxis.dataKey]))}
58
+ cy={filtered.axis === 'Right' ? yScaleRight(d[filtered.dataKey]) : yScale(d[filtered.dataKey])}
59
+ r={4.5}
60
+ opacity={d[seriesKey] ? 1 : 0}
61
+ fillOpacity={1}
62
+ fill={displayArea ? (colorScale ? colorScale(seriesKey) : '#000') : 'transparent'}
63
+ style={{ filter: 'unset', opacity: 1 }}
64
+ />
65
+ )
66
+ }
67
+
68
+ if (lineDatapointStyle === 'hover') {
69
+ if (!tooltipData) return
70
+ if (!tooltipData.data) return
71
+ let hoveredXValue = tooltipData?.data?.[0]?.[1]
72
+ if (!hoveredXValue) return
73
+ let hoveredSeriesValue
74
+ let hoveredSeriesIndex
75
+ let hoveredSeriesData = tooltipData.data.filter(d => d[0] === seriesKey)
76
+ let hoveredSeriesKey = hoveredSeriesData?.[0]?.[0]
77
+ let hoveredSeriesAxis = hoveredSeriesData?.[0]?.[2]
78
+ hoveredSeriesIndex = tooltipData.data.indexOf(hoveredSeriesKey)
79
+ hoveredSeriesValue = hoveredSeriesData?.[0]?.[1]
80
+
81
+ return tooltipData.data.map((tooltipItem, index) => {
82
+ let seriesIndex = config.runtime.seriesLabelsAll.indexOf(hoveredXValue)
83
+
84
+ if (isNaN(hoveredSeriesValue)) return <></>
85
+ return (
86
+ <circle
87
+ cx={config.xAxis.type === 'categorical' ? xScale(hoveredXValue) : xScale(parseDate(hoveredXValue))}
88
+ cy={hoveredSeriesAxis === 'right' ? yScaleRight(hoveredSeriesValue) : yScale(hoveredSeriesValue)}
89
+ r={4.5}
90
+ opacity={1}
91
+ fillOpacity={1}
92
+ fill={getColor(displayArea, colorScale, config, seriesIndex, hoveredSeriesKey, seriesKey)}
93
+ style={{ filter: 'unset', opacity: 1 }}
94
+ />
95
+ )
96
+ })
97
+ }
98
+
99
+ return null
100
+ }
101
+
102
+ export default LineChartCircle
@@ -8,11 +8,12 @@ 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.
14
15
 
15
- const { colorPalettes, transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, isNumber, updateConfig, handleLineType, dashboardConfig, tableData } = useContext(ConfigContext)
16
+ const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, isNumber, updateConfig, handleLineType, tableData } = useContext(ConfigContext)
16
17
  const { yScaleRight } = useRightAxis({ config, yMax, data, updateConfig })
17
18
 
18
19
  if (!handleTooltipMouseOver) return
@@ -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,50 +74,20 @@ 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
+ <>{config.lineDatapointStyle === 'hover' && <LineChartCircle config={config} seriesKey={seriesKey} displayArea={displayArea} tooltipData={tooltipData} xScale={xScale} yScale={yScale} colorScale={colorScale} parseDate={parseDate} yScaleRight={yScaleRight} seriesAxis={seriesAxis} />}</>
84
+ {/* STANDARD LINE */}
117
85
  <LinePath
118
86
  curve={allCurves[seriesData[0].lineType]}
119
87
  data={data}
120
88
  x={d => xScale(getXAxisData(d))}
121
89
  y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey)))}
122
- stroke={
123
- colorScale && !config.legend.dynamicLegend
124
- ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey)
125
- : // is dynamic legend
126
- config.legend.dynamicLegend
127
- ? colorPalettes[config.palette][index]
128
- : // fallback
129
- '#000'
130
- }
90
+ stroke={colorScale ? colorScale(config.runtime.seriesLabels[seriesKey]) : '#000'}
131
91
  strokeWidth={2}
132
92
  strokeOpacity={1}
133
93
  strokeDasharray={lineType ? handleLineType(lineType) : 0}
@@ -135,6 +95,7 @@ const LineChart = ({ xScale, yScale, getXAxisData, getYAxisData, xMax, yMax, han
135
95
  return item[seriesKey] !== '' && item[seriesKey] !== null && item[seriesKey] !== undefined
136
96
  }}
137
97
  />
98
+ {/* ANIMATED LINE */}
138
99
  {config.animate && (
139
100
  <LinePath
140
101
  className='animation'