@cdc/chart 4.23.1 → 4.23.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.
- package/dist/cdcchart.js +56289 -702
- package/examples/Barchart_with_negative.json +34 -0
- package/examples/area-chart.json +187 -0
- package/examples/big-small-test-bar.json +328 -0
- package/examples/big-small-test-line.json +328 -0
- package/examples/big-small-test-negative.json +328 -0
- package/examples/box-plot.json +1 -2
- package/examples/dynamic-legends.json +1 -1
- package/examples/example-bar-chart-nonnumeric.json +36 -0
- package/examples/example-bar-chart.json +36 -0
- package/examples/example-combo-bar-nonnumeric.json +105 -0
- package/examples/example-sparkline.json +76 -0
- package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart.json +31 -172
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +1 -1
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-confidence.json +1 -0
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-with-confidence.json +96 -14
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart.json +2 -2
- package/examples/gallery/line/line.json +1 -0
- package/examples/gallery/paired-bar/paired-bar-chart.json +65 -13
- package/examples/horizontal-chart-max-increase.json +38 -0
- package/examples/line-chart-max-increase.json +32 -0
- package/examples/line-chart-nonnumeric.json +32 -0
- package/examples/line-chart.json +21 -63
- package/examples/newdata.json +1 -1
- package/examples/planet-combo-example-config.json +143 -20
- package/examples/planet-deviation-config.json +168 -0
- package/examples/planet-deviation-data.json +38 -0
- package/examples/planet-example-config.json +139 -20
- package/examples/planet-example-data-max-increase.json +56 -0
- package/examples/planet-example-data-nonnumeric.json +56 -0
- package/examples/planet-example-data.json +9 -9
- package/examples/planet-pie-example-config-nonnumeric.json +30 -0
- package/examples/scatterplot-continuous.csv +17 -0
- package/examples/scatterplot.json +136 -0
- package/examples/sparkline-chart-nonnumeric.json +76 -0
- package/examples/stacked-vertical-bar-example-negative.json +154 -0
- package/examples/stacked-vertical-bar-example-nonnumerics.json +154 -0
- package/index.html +91 -0
- package/package.json +33 -24
- package/src/{CdcChart.tsx → CdcChart.jsx} +196 -124
- package/src/components/AreaChart.jsx +198 -0
- package/src/components/{BarChart.tsx → BarChart.jsx} +154 -122
- package/src/components/BoxPlot.jsx +101 -0
- package/src/components/{DataTable.tsx → DataTable.jsx} +109 -28
- package/src/components/DeviationBar.jsx +191 -0
- package/src/components/{EditorPanel.js → EditorPanel.jsx} +676 -157
- package/src/components/{Filters.js → Filters.jsx} +6 -11
- package/src/components/Legend.jsx +316 -0
- package/src/components/{LineChart.tsx → LineChart.jsx} +22 -26
- package/src/components/{LinearChart.tsx → LinearChart.jsx} +214 -91
- package/src/components/{PairedBarChart.tsx → PairedBarChart.jsx} +44 -78
- package/src/components/{PieChart.tsx → PieChart.jsx} +26 -44
- package/src/components/ScatterPlot.jsx +51 -0
- package/src/components/SparkLine.jsx +218 -0
- package/src/components/{useIntersectionObserver.tsx → useIntersectionObserver.jsx} +2 -2
- package/src/data/initial-state.js +51 -5
- package/src/hooks/useColorPalette.js +68 -0
- package/src/hooks/{useReduceData.ts → useReduceData.js} +26 -16
- package/src/hooks/useRightAxis.js +3 -1
- package/src/index.jsx +16 -0
- package/src/scss/DataTable.scss +22 -0
- package/src/scss/editor-panel.scss +5 -0
- package/src/scss/main.scss +30 -10
- package/src/test/CdcChart.test.jsx +6 -0
- package/vite.config.js +4 -0
- package/dist/495.js +0 -3
- package/dist/703.js +0 -1
- package/src/components/BoxPlot.js +0 -92
- package/src/components/Legend.js +0 -291
- package/src/components/SparkLine.js +0 -185
- package/src/hooks/useColorPalette.ts +0 -76
- package/src/index.html +0 -67
- package/src/index.tsx +0 -18
- /package/src/{context.tsx → ConfigContext.jsx} +0 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import React, { useContext, useCallback } from 'react'
|
|
2
|
+
|
|
3
|
+
// cdc
|
|
4
|
+
import ConfigContext from '../ConfigContext'
|
|
5
|
+
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
6
|
+
import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
|
|
7
|
+
|
|
8
|
+
// visx & d3
|
|
9
|
+
import { AreaClosed, LinePath, Bar } from '@visx/shape'
|
|
10
|
+
import { Group } from '@visx/group'
|
|
11
|
+
import * as allCurves from '@visx/curve'
|
|
12
|
+
import { useTooltip, useTooltipInPortal, defaultStyles } from '@visx/tooltip'
|
|
13
|
+
import { localPoint } from '@visx/event'
|
|
14
|
+
import { bisector } from 'd3-array'
|
|
15
|
+
|
|
16
|
+
const CoveAreaChart = ({ xScale, yScale, yMax, xMax }) => {
|
|
17
|
+
// enable various console logs in the file
|
|
18
|
+
const DEBUG = false
|
|
19
|
+
|
|
20
|
+
// import data from context
|
|
21
|
+
const { transformedData: data, config, handleLineType, parseDate, formatDate, formatNumber, seriesHighlight } = useContext(ConfigContext)
|
|
22
|
+
|
|
23
|
+
// import tooltip helpers
|
|
24
|
+
const { tooltipData, showTooltip } = useTooltip()
|
|
25
|
+
|
|
26
|
+
// used for offset on tooltip hover
|
|
27
|
+
let isEditor = window.location.href.includes('editor=true')
|
|
28
|
+
|
|
29
|
+
// here we're inside of the svg,
|
|
30
|
+
// it appears we need to use TooltipInPortal.
|
|
31
|
+
const { TooltipInPortal } = useTooltipInPortal({
|
|
32
|
+
detectBounds: true,
|
|
33
|
+
// when tooltip containers are scrolled, this will correctly update the Tooltip position
|
|
34
|
+
scroll: true
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// Draw transparent bars over the chart to get tooltip data
|
|
38
|
+
// Turn DEBUG on for additional context.
|
|
39
|
+
let barThickness = xMax / config.data.length
|
|
40
|
+
let barThicknessAdjusted = barThickness * (config.barThickness || 0.8)
|
|
41
|
+
let offset = (barThickness * (1 - (config.barThickness || 0.8))) / 2
|
|
42
|
+
|
|
43
|
+
// Tooltip helper for getting data to the closest date/category hovered.
|
|
44
|
+
const getXValueFromCoordinate = x => {
|
|
45
|
+
if (config.xAxis.type === 'categorical') {
|
|
46
|
+
let eachBand = xScale.step()
|
|
47
|
+
let numerator = x
|
|
48
|
+
const index = Math.floor(Number(numerator) / eachBand)
|
|
49
|
+
return xScale.domain()[index - 1] // fixes off by 1 error
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (config.xAxis.type === 'date') {
|
|
53
|
+
const bisectDate = bisector(d => parseDate(d[config.xAxis.dataKey])).left
|
|
54
|
+
const x0 = xScale.invert(x)
|
|
55
|
+
const index = bisectDate(config.data, x0, 1)
|
|
56
|
+
const val = parseDate(config.data[index - 1][config.xAxis.dataKey])
|
|
57
|
+
return val
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const handleMouseOver = useCallback(
|
|
62
|
+
(e, data) => {
|
|
63
|
+
// get the svg coordinates of the mouse
|
|
64
|
+
// and get the closest values
|
|
65
|
+
const eventSvgCoords = localPoint(e)
|
|
66
|
+
const { x, y } = eventSvgCoords
|
|
67
|
+
|
|
68
|
+
let closestXScaleValue = getXValueFromCoordinate(x)
|
|
69
|
+
let formattedDate = formatDate(closestXScaleValue)
|
|
70
|
+
|
|
71
|
+
let yScaleValues
|
|
72
|
+
if (config.xAxis.type === 'categorical') {
|
|
73
|
+
yScaleValues = data.filter(d => d[config.xAxis.dataKey] === closestXScaleValue)
|
|
74
|
+
} else {
|
|
75
|
+
yScaleValues = data.filter(d => d[config.xAxis.dataKey] === formattedDate)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let seriesToInclude = []
|
|
79
|
+
let yScaleMaxValues = []
|
|
80
|
+
let itemsToLoop = [config.runtime.xAxis.dataKey, ...config.runtime.seriesKeys]
|
|
81
|
+
|
|
82
|
+
itemsToLoop.map(seriesKey => {
|
|
83
|
+
return Object.entries(yScaleValues[0]).forEach(item => item[0] === seriesKey && seriesToInclude.push(item))
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// filter out the series that aren't added to the map.
|
|
87
|
+
seriesToInclude.map(series => yScaleMaxValues.push(Number(yScaleValues[0][series])))
|
|
88
|
+
seriesToInclude = Object.fromEntries(seriesToInclude)
|
|
89
|
+
|
|
90
|
+
let tooltipData = {}
|
|
91
|
+
tooltipData.data = seriesToInclude
|
|
92
|
+
tooltipData.dataXPosition = isEditor ? 300 + x + 20 : x + 20
|
|
93
|
+
tooltipData.dataYPosition = y - 20
|
|
94
|
+
|
|
95
|
+
let tooltipInformation = {
|
|
96
|
+
tooltipData: tooltipData,
|
|
97
|
+
tooltipTop: 0,
|
|
98
|
+
tooltipValues: yScaleValues,
|
|
99
|
+
tooltipLeft: x
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
showTooltip(tooltipInformation)
|
|
103
|
+
},
|
|
104
|
+
[showTooltip] // eslint-disable-line
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
const TooltipListItem = ({ item }) => {
|
|
108
|
+
const [label, value] = item
|
|
109
|
+
return label === config.xAxis.dataKey ? `${label}: ${value}` : `${label}: ${formatNumber(value, 'left')}`
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const handleX = d => {
|
|
113
|
+
return config.xAxis.type === 'date' ? xScale(parseDate(d[config.xAxis.dataKey])) : xScale(d[config.xAxis.dataKey])
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const handleY = (d, index) => {
|
|
117
|
+
return yScale(d[config.series[index].dataKey])
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
data && (
|
|
122
|
+
<ErrorBoundary component='AreaChart'>
|
|
123
|
+
<Group className='area-chart' key='area-wrapper' left={config.yAxis.size}>
|
|
124
|
+
{config.series.map((s, index) => {
|
|
125
|
+
let seriesColor = colorPalettesChart[config.palette][index]
|
|
126
|
+
let curveType = allCurves[s.lineType]
|
|
127
|
+
let transparentArea = config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(s.dataKey) === -1
|
|
128
|
+
let displayArea = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(s.dataKey) !== -1
|
|
129
|
+
|
|
130
|
+
data.map(d => xScale(parseDate(d[config.xAxis.dataKey])))
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<>
|
|
134
|
+
{/* prettier-ignore */}
|
|
135
|
+
{/* this is the line that appears on top of the area chart */}
|
|
136
|
+
<LinePath data={data} x={d => handleX(d)} y={d => yScale(d[config.series[index].dataKey])} stroke={seriesColor} strokeWidth={2} strokeOpacity={1} shapeRendering='geometricPrecision' curve={curveType} strokeDasharray={s.type ? handleLineType(s.type) : 0} />
|
|
137
|
+
|
|
138
|
+
{/* prettier-ignore */}
|
|
139
|
+
{/* filled in sections */}
|
|
140
|
+
<AreaClosed key={'area-chart'} fill={displayArea ? seriesColor : 'transparent'} fillOpacity={transparentArea ? 0.25 : 0.5} data={data} x={d => handleX(d)} y={d => handleY(d, index)} yScale={yScale} curve={curveType} strokeDasharray={s.type ? handleLineType(s.typ) : 0} />
|
|
141
|
+
|
|
142
|
+
<Bar x={d => handleX(d)} y={d => yScale(d[config.series[index].dataKey])} yScale={yScale} width={xMax} height={yMax} fill={DEBUG ? 'red' : 'transparent'} fillOpacity={0.05} style={DEBUG ? { stroke: 'black', strokeWidth: 2 } : {}} onMouseMove={e => handleMouseOver(e, data)} />
|
|
143
|
+
|
|
144
|
+
{/* circles that appear on hover */}
|
|
145
|
+
{tooltipData && (
|
|
146
|
+
<circle
|
|
147
|
+
cx={config.xAxis.type === 'categorical' ? xScale(tooltipData.data[config.xAxis.dataKey]) : xScale(parseDate(tooltipData.data[config.xAxis.dataKey]))}
|
|
148
|
+
cy={yScale(tooltipData.data[s.dataKey])}
|
|
149
|
+
r={4.5}
|
|
150
|
+
opacity={1}
|
|
151
|
+
fillOpacity={1}
|
|
152
|
+
fill={seriesColor}
|
|
153
|
+
style={{ filter: 'unset', opacity: 1 }}
|
|
154
|
+
/>
|
|
155
|
+
)}
|
|
156
|
+
|
|
157
|
+
{/* bars to handle tooltips */}
|
|
158
|
+
{DEBUG &&
|
|
159
|
+
config.data.map((item, index) => {
|
|
160
|
+
return (
|
|
161
|
+
<Bar
|
|
162
|
+
className='bar-here'
|
|
163
|
+
x={barThickness * index + offset}
|
|
164
|
+
y={d => yScale(d[config.series[index].dataKey])}
|
|
165
|
+
yScale={yScale}
|
|
166
|
+
width={barThicknessAdjusted}
|
|
167
|
+
height={yMax}
|
|
168
|
+
fill={'transparent'}
|
|
169
|
+
fillOpacity={1}
|
|
170
|
+
style={{ stroke: 'black', strokeWidth: 2 }}
|
|
171
|
+
onMouseMove={e => handleMouseOver(e, data)}
|
|
172
|
+
/>
|
|
173
|
+
)
|
|
174
|
+
})}
|
|
175
|
+
|
|
176
|
+
{tooltipData && (
|
|
177
|
+
<TooltipInPortal key={Math.random()} top={tooltipData.dataYPosition} left={tooltipData.dataXPosition} style={defaultStyles}>
|
|
178
|
+
<Group x={config.yAxis.size + 10} y={0}>
|
|
179
|
+
<ul style={{ listStyle: 'none', paddingLeft: 'unset' }}>
|
|
180
|
+
{Object.entries(tooltipData.data).map(item => (
|
|
181
|
+
<li>
|
|
182
|
+
<TooltipListItem item={item} />
|
|
183
|
+
</li>
|
|
184
|
+
))}
|
|
185
|
+
</ul>
|
|
186
|
+
</Group>
|
|
187
|
+
</TooltipInPortal>
|
|
188
|
+
)}
|
|
189
|
+
</>
|
|
190
|
+
)
|
|
191
|
+
})}
|
|
192
|
+
</Group>
|
|
193
|
+
</ErrorBoundary>
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export default CoveAreaChart
|