@cdc/chart 4.23.5 → 4.23.7
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 +29320 -28775
- 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 +271 -0
- package/examples/feature/forecasting/effective_reproduction.json +57 -8
- package/examples/feature/forecasting/forecasting.json +5334 -0
- package/examples/feature/forecasting/index.json +203 -0
- package/examples/feature/line/line-chart.json +12 -12
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +167 -20
- package/examples/gallery/line/line.json +173 -1
- package/index.html +14 -8
- package/package.json +2 -2
- package/src/CdcChart.jsx +104 -26
- package/src/components/AreaChart.jsx +23 -149
- package/src/components/BarChart.jsx +87 -15
- package/src/components/DataTable.jsx +35 -14
- package/src/components/EditorPanel.jsx +1829 -1954
- package/src/components/Forecasting.jsx +84 -0
- package/src/components/Legend.jsx +191 -275
- package/src/components/LineChart.jsx +34 -7
- package/src/components/LinearChart.jsx +510 -101
- package/src/components/Series.jsx +554 -0
- package/src/components/SparkLine.jsx +3 -3
- package/src/data/initial-state.js +13 -5
- package/src/hooks/useMinMax.js +37 -0
- package/src/hooks/useRightAxis.js +9 -2
- package/src/hooks/useScales.js +7 -13
- package/src/scss/main.scss +4 -17
- package/LICENSE +0 -201
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useContext, useState, useEffect } from 'react'
|
|
2
2
|
import { Group } from '@visx/group'
|
|
3
|
-
import { BarGroup, BarStack } from '@visx/shape'
|
|
3
|
+
import { BarGroup, BarStack, Bar } from '@visx/shape'
|
|
4
4
|
import { Text } from '@visx/text'
|
|
5
5
|
import chroma from 'chroma-js'
|
|
6
6
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
@@ -8,8 +8,8 @@ import ConfigContext from '../ConfigContext'
|
|
|
8
8
|
import { BarStackHorizontal } from '@visx/shape'
|
|
9
9
|
import { useHighlightedBars } from '../hooks/useHighlightedBars'
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, updateConfig, colorPalettes, tableData, formatDate, isNumber, getTextWidth, parseDate } = useContext(ConfigContext)
|
|
11
|
+
const BarChart = ({ xScale, yScale, seriesScale, xMax, yMax, getXAxisData, getYAxisData, animatedChart, visible, handleTooltipMouseOver, handleTooltipMouseOff, handleTooltipClick }) => {
|
|
12
|
+
const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, updateConfig, colorPalettes, tableData, formatDate, isNumber, getTextWidth, parseDate, setSharedFilter, setSharedFilterValue, dashboardConfig } = useContext(ConfigContext)
|
|
13
13
|
const { HighLightedBarUtils } = useHighlightedBars(config)
|
|
14
14
|
const { orientation, visualizationSubType } = config
|
|
15
15
|
const isHorizontal = orientation === 'horizontal'
|
|
@@ -63,7 +63,7 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
63
63
|
if (!colorMap.has(values[i])) {
|
|
64
64
|
colorMap.set(values[i], palettesArr[colorMap.size % palettesArr.length])
|
|
65
65
|
}
|
|
66
|
-
// push the
|
|
66
|
+
// push the color to the result array
|
|
67
67
|
result.push(colorMap.get(values[i]))
|
|
68
68
|
}
|
|
69
69
|
return result
|
|
@@ -171,11 +171,17 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
171
171
|
yAxisTooltip = config.isLegendValue ? `${bar.key}: ${yAxisValue}` : config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
const
|
|
175
|
-
|
|
174
|
+
const {
|
|
175
|
+
legend: { showLegendValuesTooltip },
|
|
176
|
+
runtime: { seriesLabels }
|
|
177
|
+
} = config
|
|
178
|
+
|
|
179
|
+
const barStackTooltip = `<div>
|
|
180
|
+
<p class="tooltip-heading"><strong>${xAxisTooltip}</strong></p>
|
|
181
|
+
${showLegendValuesTooltip && seriesLabels && hasMultipleSeries ? `${seriesLabels[bar.key] || ''}<br/>` : ''}
|
|
176
182
|
${yAxisTooltip}<br />
|
|
177
|
-
${xAxisTooltip}
|
|
178
183
|
</div>`
|
|
184
|
+
|
|
179
185
|
return (
|
|
180
186
|
<Group key={`${barStack.index}--${bar.index}--${orientation}`}>
|
|
181
187
|
<style>
|
|
@@ -200,8 +206,15 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
200
206
|
style={{ background: bar.color, border: `${config.barHasBorder === 'true' ? barBorderWidth : 0}px solid #333`, ...style }}
|
|
201
207
|
opacity={transparentBar ? 0.5 : 1}
|
|
202
208
|
display={displayBar ? 'block' : 'none'}
|
|
203
|
-
data-tooltip-html={
|
|
209
|
+
data-tooltip-html={barStackTooltip}
|
|
204
210
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
211
|
+
onClick={e => {
|
|
212
|
+
e.preventDefault()
|
|
213
|
+
if (setSharedFilter) {
|
|
214
|
+
bar[config.xAxis.dataKey] = xAxisValue
|
|
215
|
+
setSharedFilter(config.uid, bar)
|
|
216
|
+
}
|
|
217
|
+
}}
|
|
205
218
|
></foreignObject>
|
|
206
219
|
</Group>
|
|
207
220
|
</Group>
|
|
@@ -266,6 +279,13 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
266
279
|
display={displayBar ? 'block' : 'none'}
|
|
267
280
|
data-tooltip-html={tooltip}
|
|
268
281
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
282
|
+
onClick={e => {
|
|
283
|
+
e.preventDefault()
|
|
284
|
+
if (setSharedFilter) {
|
|
285
|
+
bar[config.xAxis.dataKey] = xAxisValue
|
|
286
|
+
setSharedFilter(config.uid, bar)
|
|
287
|
+
}
|
|
288
|
+
}}
|
|
269
289
|
></foreignObject>
|
|
270
290
|
|
|
271
291
|
{orientation === 'horizontal' && visualizationSubType === 'stacked' && isLabelBelowBar && barStack.index === 0 && !config.yAxis.hideLabel && (
|
|
@@ -429,17 +449,17 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
429
449
|
let yAxisTooltip = config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
|
|
430
450
|
let xAxisTooltip = config.runtime.xAxis.label ? `${config.runtime.xAxis.label}: ${xAxisValue}` : xAxisValue
|
|
431
451
|
if (!hasMultipleSeries && config.runtime.horizontal) {
|
|
432
|
-
xAxisTooltip = config.isLegendValue ?
|
|
452
|
+
xAxisTooltip = config.isLegendValue ? `<p className="tooltip-heading">${bar.key}: ${xAxisValue}</p>` : config.runtime.xAxis.label ? `<p className="tooltip-heading">${config.runtime.xAxis.label}: ${xAxisValue}</p>` : xAxisValue
|
|
433
453
|
}
|
|
434
454
|
if (!hasMultipleSeries && !config.runtime.horizontal) {
|
|
435
455
|
yAxisTooltip = config.isLegendValue ? `${bar.key}: ${yAxisValue}` : config.runtime.yAxis.label ? `${config.runtime.yAxis.label}: ${yAxisValue}` : yAxisValue
|
|
436
456
|
}
|
|
437
457
|
|
|
438
|
-
const tooltip = `<
|
|
458
|
+
const tooltip = `<ul>
|
|
439
459
|
${config.legend.showLegendValuesTooltip && config.runtime.seriesLabels && hasMultipleSeries ? `${config.runtime.seriesLabels[bar.key] || ''}<br/>` : ''}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
</
|
|
460
|
+
<li class="tooltip-heading">${yAxisTooltip}</li>
|
|
461
|
+
<li class="tooltip-body">${xAxisTooltip}</li>
|
|
462
|
+
</li></ul>`
|
|
443
463
|
|
|
444
464
|
const isRegularLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'regular'
|
|
445
465
|
const isTwoToneLollipopColor = config.isLollipopChart && config.lollipopColorStyle === 'two-tone'
|
|
@@ -447,14 +467,54 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
447
467
|
const highlightedBarColor = config.orientation === 'vertical' ? getHighlightedBarColorByValue(xAxisValue) : getHighlightedBarColorByValue(yAxisValue)
|
|
448
468
|
const highlightedBar = config.orientation === 'vertical' ? getHighlightedBarByValue(xAxisValue) : getHighlightedBarByValue(yAxisValue)
|
|
449
469
|
|
|
450
|
-
const background =
|
|
470
|
+
const background = () => {
|
|
471
|
+
if (isRegularLollipopColor) return barColor
|
|
472
|
+
if (isTwoToneLollipopColor) return chroma(barColor).brighten(1)
|
|
473
|
+
if (isHighlightedBar) return 'transparent'
|
|
474
|
+
// loop through shared filters and get active values
|
|
475
|
+
/* if (dashboardConfig && dashboardConfig?.dashboard.sharedFilters?.length > 0) {
|
|
476
|
+
let activeFilters = []
|
|
477
|
+
let backgroundColor = barColor
|
|
478
|
+
|
|
479
|
+
const checkForResetValue = () => {
|
|
480
|
+
return dashboardConfig.dashboard.sharedFilters?.map((filter, index) => {
|
|
481
|
+
if (filter.resetLabel === filter.active) {
|
|
482
|
+
backgroundColor = barColor
|
|
483
|
+
} else {
|
|
484
|
+
return backgroundColor
|
|
485
|
+
}
|
|
486
|
+
})
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
dashboardConfig.dashboard.sharedFilters?.forEach((filter, index) => {
|
|
490
|
+
activeFilters.push(filter.active)
|
|
491
|
+
})
|
|
492
|
+
|
|
493
|
+
// if reset value is found use that.
|
|
494
|
+
|
|
495
|
+
if (config.orientation === 'horizontal') {
|
|
496
|
+
if (!activeFilters.includes(yAxisValue)) {
|
|
497
|
+
backgroundColor = '#ccc'
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (config.orientation !== 'horizontal') {
|
|
502
|
+
if (!activeFilters.includes(xAxisValue)) {
|
|
503
|
+
backgroundColor = '#ccc'
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
checkForResetValue()
|
|
507
|
+
return backgroundColor
|
|
508
|
+
} */
|
|
509
|
+
return barColor
|
|
510
|
+
}
|
|
451
511
|
|
|
452
512
|
const borderColor = isHighlightedBar ? highlightedBarColor : config.barHasBorder === 'true' ? '#000' : 'transparent'
|
|
453
513
|
|
|
454
514
|
const borderWidth = isHighlightedBar ? highlightedBar.borderWidth : config.isLollipopChart ? 0 : config.barHasBorder === 'true' ? barBorderWidth : 0
|
|
455
515
|
|
|
456
516
|
const finalStyle = {
|
|
457
|
-
background,
|
|
517
|
+
background: background(),
|
|
458
518
|
borderColor,
|
|
459
519
|
borderStyle: 'solid',
|
|
460
520
|
borderWidth,
|
|
@@ -485,6 +545,13 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
485
545
|
display={displayBar ? 'block' : 'none'}
|
|
486
546
|
data-tooltip-html={tooltip}
|
|
487
547
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
548
|
+
onClick={e => {
|
|
549
|
+
e.preventDefault()
|
|
550
|
+
if (setSharedFilter) {
|
|
551
|
+
bar[config.xAxis.dataKey] = config.orientation === 'horizontal' ? yAxisValue : xAxisValue
|
|
552
|
+
setSharedFilter(config.uid, bar)
|
|
553
|
+
}
|
|
554
|
+
}}
|
|
488
555
|
></foreignObject>
|
|
489
556
|
{orientation === 'horizontal' && !config.isLollipopChart && displayNumbersOnBar && (
|
|
490
557
|
<Text // prettier-ignore
|
|
@@ -612,7 +679,12 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
|
|
|
612
679
|
: ''}
|
|
613
680
|
</Group>
|
|
614
681
|
)}
|
|
682
|
+
|
|
683
|
+
{/* tooltips */}
|
|
684
|
+
{orientation !== 'horizontal' && <Bar key={'bars'} width={Number(xMax)} height={Number(yMax)} fill={false ? 'red' : 'transparent'} fillOpacity={0.05} onMouseMove={e => handleTooltipMouseOver(e, data)} onMouseOut={handleTooltipMouseOff} onClick={e => handleTooltipClick(e, data)} />}
|
|
615
685
|
</Group>
|
|
616
686
|
</ErrorBoundary>
|
|
617
687
|
)
|
|
618
688
|
}
|
|
689
|
+
|
|
690
|
+
export default BarChart
|
|
@@ -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,14 +10,15 @@ 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
|
-
const { rawData, tableData: data, config, colorScale, parseDate, formatDate, formatNumber: numberFormatter, colorPalettes } = useContext(ConfigContext)
|
|
16
|
+
const { rawData, tableData: data, config, colorScale, parseDate, formatDate, formatNumber: numberFormatter, colorPalettes, currentViewport } = useContext(ConfigContext)
|
|
16
17
|
|
|
17
18
|
const section = config.orientation === 'horizontal' ? 'yAxis' : 'xAxis'
|
|
18
19
|
const [tableExpanded, setTableExpanded] = useState(config.table.expanded)
|
|
19
20
|
const [accessibilityLabel, setAccessibilityLabel] = useState('')
|
|
21
|
+
const isLegendBottom = ['sm', 'xs', 'xxs'].includes(currentViewport)
|
|
20
22
|
|
|
21
23
|
const DownloadButton = ({ data }, type) => {
|
|
22
24
|
const fileName = `${config.title.substring(0, 50)}.csv`
|
|
@@ -93,24 +95,29 @@ export default function DataTable() {
|
|
|
93
95
|
{
|
|
94
96
|
Header: '',
|
|
95
97
|
Cell: ({ row }) => {
|
|
96
|
-
const
|
|
98
|
+
const getSeriesLabel = () => {
|
|
99
|
+
let userUpdatedSeriesName = config.series.filter(series => series.dataKey === row.original)?.[0]?.name
|
|
100
|
+
|
|
101
|
+
if (userUpdatedSeriesName) return userUpdatedSeriesName
|
|
102
|
+
if (config.runtimeSeriesLabels) return config.runtime.seriesLabels[row.original]
|
|
103
|
+
return row.original
|
|
104
|
+
}
|
|
97
105
|
return (
|
|
98
106
|
<>
|
|
99
107
|
{config.visualizationType !== 'Pie' && (
|
|
100
108
|
<LegendCircle
|
|
101
109
|
fill={
|
|
102
|
-
// non-dynamic
|
|
103
|
-
!config.legend.dynamicLegend
|
|
104
|
-
? colorScale(
|
|
105
|
-
:
|
|
106
|
-
config.legend.dynamicLegend
|
|
110
|
+
// non-dynamic legend
|
|
111
|
+
!config.legend.dynamicLegend && config.visualizationType !== 'Forecasting'
|
|
112
|
+
? colorScale(getSeriesLabel())
|
|
113
|
+
: config.legend.dynamicLegend
|
|
107
114
|
? colorPalettes[config.palette][row.index]
|
|
108
115
|
: // fallback
|
|
109
116
|
'#000'
|
|
110
117
|
}
|
|
111
118
|
/>
|
|
112
119
|
)}
|
|
113
|
-
<span>{
|
|
120
|
+
<span>{getSeriesLabel()}</span>
|
|
114
121
|
</>
|
|
115
122
|
)
|
|
116
123
|
},
|
|
@@ -127,7 +134,21 @@ export default function DataTable() {
|
|
|
127
134
|
const newCol = {
|
|
128
135
|
Header: resolveTableHeader(),
|
|
129
136
|
Cell: ({ row }) => {
|
|
130
|
-
|
|
137
|
+
let leftAxisItems = config.series.filter(item => item?.axis === 'Left')
|
|
138
|
+
let rightAxisItems = config.series.filter(item => item?.axis === 'Right')
|
|
139
|
+
let resolvedAxis = ''
|
|
140
|
+
|
|
141
|
+
leftAxisItems.map(leftSeriesItem => {
|
|
142
|
+
if (leftSeriesItem.dataKey === row.original) resolvedAxis = 'left'
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
rightAxisItems.map(rightSeriesItem => {
|
|
146
|
+
if (rightSeriesItem.dataKey === row.original) resolvedAxis = 'right'
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
if (config.visualizationType !== 'Combo') resolvedAxis = 'left'
|
|
150
|
+
|
|
151
|
+
return <>{numberFormatter(d[row.original], resolvedAxis)}</>
|
|
131
152
|
},
|
|
132
153
|
id: `${d[config.runtime.originalXAxis.dataKey]}--${index}`,
|
|
133
154
|
canSort: true
|
|
@@ -210,12 +231,12 @@ export default function DataTable() {
|
|
|
210
231
|
|
|
211
232
|
return (
|
|
212
233
|
<ErrorBoundary component='DataTable'>
|
|
213
|
-
<
|
|
214
|
-
<
|
|
234
|
+
<MediaControls.Section classes={['download-links']}>
|
|
235
|
+
<MediaControls.Link config={config} />
|
|
215
236
|
{config.table.download && <DownloadButton data={rawData} type='link' />}
|
|
216
|
-
</
|
|
237
|
+
</MediaControls.Section>
|
|
217
238
|
|
|
218
|
-
<section id={config?.title ? `dataTableSection__${config?.title.replace(/\s/g, '')}` : `dataTableSection`} className={`data-table-container`} aria-label={accessibilityLabel}>
|
|
239
|
+
<section style={{ marginTop: !isLegendBottom ? config.dynamicMarginTop + 'px' : '0px' }} id={config?.title ? `dataTableSection__${config?.title.replace(/\s/g, '')}` : `dataTableSection`} className={`data-table-container`} aria-label={accessibilityLabel}>
|
|
219
240
|
<div
|
|
220
241
|
role='button'
|
|
221
242
|
className={tableExpanded ? 'data-table-heading' : 'collapsed data-table-heading'}
|