@cdc/chart 4.23.5 → 4.23.6
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 +29056 -27475
- package/examples/feature/__data__/planet-example-data.json +15 -16
- package/examples/feature/bar/new.json +561 -0
- package/examples/feature/combo/right-issues.json +190 -0
- package/examples/feature/forecasting/combo-forecasting.json +245 -0
- package/examples/feature/forecasting/forecasting.json +5325 -0
- package/examples/feature/forecasting/index.json +203 -0
- package/examples/feature/line/line-chart.json +1 -1
- package/examples/gallery/line/line.json +173 -1
- package/index.html +12 -6
- package/package.json +2 -2
- package/src/CdcChart.jsx +48 -11
- package/src/components/AreaChart.jsx +8 -23
- package/src/components/BarChart.jsx +65 -3
- package/src/components/DataTable.jsx +30 -12
- package/src/components/EditorPanel.jsx +1803 -1948
- package/src/components/Forecasting.jsx +147 -0
- package/src/components/Legend.jsx +188 -274
- package/src/components/LineChart.jsx +3 -1
- package/src/components/LinearChart.jsx +145 -18
- package/src/components/Series.jsx +518 -0
- package/src/components/SparkLine.jsx +3 -3
- package/src/data/initial-state.js +7 -3
- package/src/hooks/useMinMax.js +36 -0
- package/src/hooks/useRightAxis.js +8 -2
- package/src/hooks/useScales.js +7 -13
|
@@ -27,7 +27,7 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
|
|
|
27
27
|
const tooltip_id = `cdc-open-viz-tooltip-${config.runtime.uniqueId}`
|
|
28
28
|
|
|
29
29
|
// import tooltip helpers
|
|
30
|
-
const { tooltipData, showTooltip } = useTooltip()
|
|
30
|
+
const { tooltipData, showTooltip, hideTooltip } = useTooltip()
|
|
31
31
|
|
|
32
32
|
// here we're inside of the svg,
|
|
33
33
|
// it appears we need to use TooltipInPortal.
|
|
@@ -41,8 +41,6 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
|
|
|
41
41
|
// Turn DEBUG on for additional context.
|
|
42
42
|
if (!data) return
|
|
43
43
|
let barThickness = xMax / data.length
|
|
44
|
-
let barThicknessAdjusted = barThickness * (config.barThickness || 0.8)
|
|
45
|
-
let offset = (barThickness * (1 - (config.barThickness || 0.8))) / 2
|
|
46
44
|
|
|
47
45
|
// Tooltip helper for getting data to the closest date/category hovered.
|
|
48
46
|
const getXValueFromCoordinate = x => {
|
|
@@ -87,6 +85,9 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
|
|
|
87
85
|
if (!yScaleValues[0]) return
|
|
88
86
|
for (const item of Object.entries(yScaleValues[0])) {
|
|
89
87
|
if (item[0] === seriesKey) {
|
|
88
|
+
// let userUpdatedSeriesName = config.series.filter(series => series.dataKey === item[0])?.[0]?.name
|
|
89
|
+
// if (userUpdatedSeriesName) item[0] = userUpdatedSeriesName
|
|
90
|
+
|
|
90
91
|
seriesToInclude.push(item)
|
|
91
92
|
}
|
|
92
93
|
}
|
|
@@ -95,6 +96,7 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
|
|
|
95
96
|
// filter out the series that aren't added to the map.
|
|
96
97
|
seriesToInclude.map(series => yScaleMaxValues.push(Number(yScaleValues[0][series])))
|
|
97
98
|
if (!seriesToInclude) return
|
|
99
|
+
|
|
98
100
|
let tooltipDataFromSeries = Object.fromEntries(seriesToInclude) ? Object.fromEntries(seriesToInclude) : {}
|
|
99
101
|
|
|
100
102
|
let tooltipData = {}
|
|
@@ -143,8 +145,9 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
|
|
|
143
145
|
|
|
144
146
|
if (config.xAxis.type === 'date') {
|
|
145
147
|
data.map(d => xScale(parseDate(d[config.xAxis.dataKey])))
|
|
148
|
+
} else {
|
|
149
|
+
data.map(d => xScale(d[config.xAxis.dataKey]))
|
|
146
150
|
}
|
|
147
|
-
|
|
148
151
|
return (
|
|
149
152
|
<React.Fragment key={index}>
|
|
150
153
|
{/* prettier-ignore */}
|
|
@@ -182,6 +185,7 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
|
|
|
182
185
|
fillOpacity={0.05}
|
|
183
186
|
style={DEBUG ? { stroke: 'black', strokeWidth: 2 } : {}}
|
|
184
187
|
onMouseMove={e => handleMouseOver(e, data)}
|
|
188
|
+
onMouseOut={hideTooltip}
|
|
185
189
|
/>
|
|
186
190
|
|
|
187
191
|
{/* circles that appear on hover */}
|
|
@@ -197,25 +201,6 @@ const CoveAreaChart = ({ xScale, yScale, yMax, xMax, chartRef }) => {
|
|
|
197
201
|
/>
|
|
198
202
|
)}
|
|
199
203
|
|
|
200
|
-
{/* another tool for showing bars during debug mode. */}
|
|
201
|
-
{DEBUG &&
|
|
202
|
-
data.map((item, index) => {
|
|
203
|
-
return (
|
|
204
|
-
<Bar
|
|
205
|
-
className='bar-here'
|
|
206
|
-
x={Number(barThickness * index)}
|
|
207
|
-
y={d => Number(yScale(d[config.series[index].dataKey]))}
|
|
208
|
-
yScale={yScale}
|
|
209
|
-
width={Number(barThickness)}
|
|
210
|
-
height={yMax}
|
|
211
|
-
fill={DEBUG ? 'red' : 'transparent'}
|
|
212
|
-
fillOpacity={1}
|
|
213
|
-
style={{ stroke: 'black', strokeWidth: 2 }}
|
|
214
|
-
onMouseMove={e => handleMouseOver(e, data)}
|
|
215
|
-
/>
|
|
216
|
-
)
|
|
217
|
-
})}
|
|
218
|
-
|
|
219
204
|
{tooltipData && Object.entries(tooltipData.data).length > 0 && (
|
|
220
205
|
<TooltipInPortal key={Math.random()} top={tooltipData.dataYPosition + chartPosition?.top} left={tooltipData.dataXPosition + chartPosition?.left} style={defaultStyles}>
|
|
221
206
|
<ul style={{ listStyle: 'none', paddingLeft: 'unset', fontFamily: 'sans-serif', margin: 'auto', lineHeight: '1rem' }} data-tooltip-id={tooltip_id}>
|
|
@@ -7,9 +7,10 @@ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
|
7
7
|
import ConfigContext from '../ConfigContext'
|
|
8
8
|
import { BarStackHorizontal } from '@visx/shape'
|
|
9
9
|
import { useHighlightedBars } from '../hooks/useHighlightedBars'
|
|
10
|
+
import { act } from 'react-dom/test-utils'
|
|
10
11
|
|
|
11
12
|
export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getXAxisData, getYAxisData, animatedChart, visible }) {
|
|
12
|
-
const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, updateConfig, colorPalettes, tableData, formatDate, isNumber, getTextWidth, parseDate } = useContext(ConfigContext)
|
|
13
|
+
const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, updateConfig, colorPalettes, tableData, formatDate, isNumber, getTextWidth, parseDate, setSharedFilter, setSharedFilterValue, dashboardConfig } = useContext(ConfigContext)
|
|
13
14
|
const { HighLightedBarUtils } = useHighlightedBars(config)
|
|
14
15
|
const { orientation, visualizationSubType } = config
|
|
15
16
|
const isHorizontal = orientation === 'horizontal'
|
|
@@ -202,6 +203,13 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
202
203
|
display={displayBar ? 'block' : 'none'}
|
|
203
204
|
data-tooltip-html={tooltip}
|
|
204
205
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
206
|
+
onClick={e => {
|
|
207
|
+
e.preventDefault()
|
|
208
|
+
if (setSharedFilter) {
|
|
209
|
+
bar[config.xAxis.dataKey] = xAxisValue
|
|
210
|
+
setSharedFilter(config.uid, bar)
|
|
211
|
+
}
|
|
212
|
+
}}
|
|
205
213
|
></foreignObject>
|
|
206
214
|
</Group>
|
|
207
215
|
</Group>
|
|
@@ -266,6 +274,13 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
266
274
|
display={displayBar ? 'block' : 'none'}
|
|
267
275
|
data-tooltip-html={tooltip}
|
|
268
276
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
277
|
+
onClick={e => {
|
|
278
|
+
e.preventDefault()
|
|
279
|
+
if (setSharedFilter) {
|
|
280
|
+
bar[config.xAxis.dataKey] = xAxisValue
|
|
281
|
+
setSharedFilter(config.uid, bar)
|
|
282
|
+
}
|
|
283
|
+
}}
|
|
269
284
|
></foreignObject>
|
|
270
285
|
|
|
271
286
|
{orientation === 'horizontal' && visualizationSubType === 'stacked' && isLabelBelowBar && barStack.index === 0 && !config.yAxis.hideLabel && (
|
|
@@ -447,14 +462,54 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
447
462
|
const highlightedBarColor = config.orientation === 'vertical' ? getHighlightedBarColorByValue(xAxisValue) : getHighlightedBarColorByValue(yAxisValue)
|
|
448
463
|
const highlightedBar = config.orientation === 'vertical' ? getHighlightedBarByValue(xAxisValue) : getHighlightedBarByValue(yAxisValue)
|
|
449
464
|
|
|
450
|
-
const background =
|
|
465
|
+
const background = () => {
|
|
466
|
+
if (isRegularLollipopColor) return barColor
|
|
467
|
+
if (isTwoToneLollipopColor) return chroma(barColor).brighten(1)
|
|
468
|
+
if (isHighlightedBar) return 'transparent'
|
|
469
|
+
// loop through shared filters and get active values
|
|
470
|
+
if (dashboardConfig && dashboardConfig?.dashboard.sharedFilters?.length > 0) {
|
|
471
|
+
let activeFilters = []
|
|
472
|
+
let backgroundColor = barColor
|
|
473
|
+
|
|
474
|
+
const checkForResetValue = () => {
|
|
475
|
+
return dashboardConfig.dashboard.sharedFilters?.map((filter, index) => {
|
|
476
|
+
if (filter.resetLabel === filter.active) {
|
|
477
|
+
backgroundColor = barColor
|
|
478
|
+
} else {
|
|
479
|
+
return backgroundColor
|
|
480
|
+
}
|
|
481
|
+
})
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
dashboardConfig.dashboard.sharedFilters?.forEach((filter, index) => {
|
|
485
|
+
activeFilters.push(filter.active)
|
|
486
|
+
})
|
|
487
|
+
|
|
488
|
+
// if reset value is found use that.
|
|
489
|
+
|
|
490
|
+
if (config.orientation === 'horizontal') {
|
|
491
|
+
if (!activeFilters.includes(yAxisValue)) {
|
|
492
|
+
backgroundColor = '#ccc'
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (config.orientation !== 'horizontal') {
|
|
497
|
+
if (!activeFilters.includes(xAxisValue)) {
|
|
498
|
+
backgroundColor = '#ccc'
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
checkForResetValue()
|
|
502
|
+
return backgroundColor
|
|
503
|
+
}
|
|
504
|
+
return barColor
|
|
505
|
+
}
|
|
451
506
|
|
|
452
507
|
const borderColor = isHighlightedBar ? highlightedBarColor : config.barHasBorder === 'true' ? '#000' : 'transparent'
|
|
453
508
|
|
|
454
509
|
const borderWidth = isHighlightedBar ? highlightedBar.borderWidth : config.isLollipopChart ? 0 : config.barHasBorder === 'true' ? barBorderWidth : 0
|
|
455
510
|
|
|
456
511
|
const finalStyle = {
|
|
457
|
-
background,
|
|
512
|
+
background: background(),
|
|
458
513
|
borderColor,
|
|
459
514
|
borderStyle: 'solid',
|
|
460
515
|
borderWidth,
|
|
@@ -485,6 +540,13 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
485
540
|
display={displayBar ? 'block' : 'none'}
|
|
486
541
|
data-tooltip-html={tooltip}
|
|
487
542
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
543
|
+
onClick={e => {
|
|
544
|
+
e.preventDefault()
|
|
545
|
+
if (setSharedFilter) {
|
|
546
|
+
bar[config.xAxis.dataKey] = config.orientation === 'horizontal' ? yAxisValue : xAxisValue
|
|
547
|
+
setSharedFilter(config.uid, bar)
|
|
548
|
+
}
|
|
549
|
+
}}
|
|
488
550
|
></foreignObject>
|
|
489
551
|
{orientation === 'horizontal' && !config.isLollipopChart && displayNumbersOnBar && (
|
|
490
552
|
<Text // prettier-ignore
|
|
@@ -2,6 +2,7 @@ import React, { useContext, useEffect, useState, useMemo } from 'react'
|
|
|
2
2
|
import { useTable, useSortBy, useResizeColumns, useBlockLayout } from 'react-table'
|
|
3
3
|
import Papa from 'papaparse'
|
|
4
4
|
import { Base64 } from 'js-base64'
|
|
5
|
+
import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
|
|
5
6
|
|
|
6
7
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
7
8
|
import LegendCircle from '@cdc/core/components/LegendCircle'
|
|
@@ -9,7 +10,7 @@ import Icon from '@cdc/core/components/ui/Icon'
|
|
|
9
10
|
|
|
10
11
|
import ConfigContext from '../ConfigContext'
|
|
11
12
|
|
|
12
|
-
import
|
|
13
|
+
import MediaControls from '@cdc/core/components/MediaControls'
|
|
13
14
|
|
|
14
15
|
export default function DataTable() {
|
|
15
16
|
const { rawData, tableData: data, config, colorScale, parseDate, formatDate, formatNumber: numberFormatter, colorPalettes } = useContext(ConfigContext)
|
|
@@ -93,24 +94,29 @@ export default function DataTable() {
|
|
|
93
94
|
{
|
|
94
95
|
Header: '',
|
|
95
96
|
Cell: ({ row }) => {
|
|
96
|
-
const
|
|
97
|
+
const getSeriesLabel = () => {
|
|
98
|
+
let userUpdatedSeriesName = config.series.filter(series => series.dataKey === row.original)?.[0]?.name
|
|
99
|
+
|
|
100
|
+
if (userUpdatedSeriesName) return userUpdatedSeriesName
|
|
101
|
+
if (config.runtimeSeriesLabels) return config.runtime.seriesLabels[row.original]
|
|
102
|
+
return row.original
|
|
103
|
+
}
|
|
97
104
|
return (
|
|
98
105
|
<>
|
|
99
106
|
{config.visualizationType !== 'Pie' && (
|
|
100
107
|
<LegendCircle
|
|
101
108
|
fill={
|
|
102
|
-
// non-dynamic
|
|
103
|
-
!config.legend.dynamicLegend
|
|
104
|
-
? colorScale(
|
|
105
|
-
:
|
|
106
|
-
config.legend.dynamicLegend
|
|
109
|
+
// non-dynamic legend
|
|
110
|
+
!config.legend.dynamicLegend && config.visualizationType !== 'Forecasting'
|
|
111
|
+
? colorScale(getSeriesLabel())
|
|
112
|
+
: config.legend.dynamicLegend
|
|
107
113
|
? colorPalettes[config.palette][row.index]
|
|
108
114
|
: // fallback
|
|
109
115
|
'#000'
|
|
110
116
|
}
|
|
111
117
|
/>
|
|
112
118
|
)}
|
|
113
|
-
<span>{
|
|
119
|
+
<span>{getSeriesLabel()}</span>
|
|
114
120
|
</>
|
|
115
121
|
)
|
|
116
122
|
},
|
|
@@ -127,7 +133,19 @@ export default function DataTable() {
|
|
|
127
133
|
const newCol = {
|
|
128
134
|
Header: resolveTableHeader(),
|
|
129
135
|
Cell: ({ row }) => {
|
|
130
|
-
|
|
136
|
+
let leftAxisItems = config.series.filter(item => item?.axis === 'Left')
|
|
137
|
+
let rightAxisItems = config.series.filter(item => item?.axis === 'Right')
|
|
138
|
+
let resolvedAxis = ''
|
|
139
|
+
|
|
140
|
+
leftAxisItems.map(leftSeriesItem => {
|
|
141
|
+
if (leftSeriesItem.dataKey === row.original) resolvedAxis = 'left'
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
rightAxisItems.map(rightSeriesItem => {
|
|
145
|
+
if (rightSeriesItem.dataKey === row.original) resolvedAxis = 'right'
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
return <>{numberFormatter(d[row.original], resolvedAxis)}</>
|
|
131
149
|
},
|
|
132
150
|
id: `${d[config.runtime.originalXAxis.dataKey]}--${index}`,
|
|
133
151
|
canSort: true
|
|
@@ -210,10 +228,10 @@ export default function DataTable() {
|
|
|
210
228
|
|
|
211
229
|
return (
|
|
212
230
|
<ErrorBoundary component='DataTable'>
|
|
213
|
-
<
|
|
214
|
-
<
|
|
231
|
+
<MediaControls.Section classes={['download-links']}>
|
|
232
|
+
<MediaControls.Link config={config} />
|
|
215
233
|
{config.table.download && <DownloadButton data={rawData} type='link' />}
|
|
216
|
-
</
|
|
234
|
+
</MediaControls.Section>
|
|
217
235
|
|
|
218
236
|
<section id={config?.title ? `dataTableSection__${config?.title.replace(/\s/g, '')}` : `dataTableSection`} className={`data-table-container`} aria-label={accessibilityLabel}>
|
|
219
237
|
<div
|