@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.
- package/LICENSE +201 -0
- package/dist/cdcchart.js +44099 -44436
- package/examples/feature/__data__/area-chart-date-apple.json +1 -5073
- package/examples/feature/area/area-chart-date-apple.json +73 -10316
- package/examples/feature/area/area-chart-date-city-temperature.json +204 -80
- package/examples/feature/area/area-chart-stacked.json +239 -0
- package/examples/feature/filters/bar-filter.json +5027 -0
- package/examples/feature/legend-highlights/highlights.json +567 -0
- package/index.html +9 -6
- package/package.json +3 -2
- package/src/{CdcChart.jsx → CdcChart.tsx} +77 -71
- package/src/components/AreaChart.Stacked.jsx +73 -0
- package/src/components/AreaChart.jsx +24 -26
- package/src/components/DeviationBar.jsx +67 -13
- package/src/components/EditorPanel.jsx +483 -452
- package/src/components/Forecasting.jsx +5 -5
- package/src/components/Legend.jsx +6 -5
- package/src/components/LineChart.Circle.tsx +108 -0
- package/src/components/{LineChart.jsx → LineChart.tsx} +10 -42
- package/src/components/LinearChart.jsx +460 -443
- package/src/components/PieChart.jsx +54 -25
- package/src/components/Series.jsx +63 -17
- package/src/components/SparkLine.jsx +7 -19
- package/src/data/initial-state.js +6 -0
- package/src/hooks/useEditorPermissions.js +87 -24
- package/src/hooks/useReduceData.js +5 -0
- package/src/hooks/useScales.js +1 -1
- package/src/hooks/useTooltip.jsx +19 -6
- package/src/scss/main.scss +6 -12
- package/src/components/DataTable.jsx +0 -494
- /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[
|
|
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[
|
|
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 :
|
|
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` : `
|
|
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
|
|
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
|
-
|
|
88
|
-
|
|
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
|
-
|
|
124
|
-
?
|
|
125
|
-
:
|
|
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'
|