@cdc/chart 4.24.2 → 4.24.4
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 +47933 -36918
- package/examples/chart-regression-1.json +378 -0
- package/examples/chart-regression-2.json +2360 -0
- package/examples/feature/filters/url-filter.json +1076 -0
- package/examples/feature/line/line-chart.json +362 -37
- package/examples/feature/regions/index.json +50 -4
- package/examples/feature/sankey/sankey-example-data.json +1364 -0
- package/examples/feature/sankey/sankey_chart_data.csv +20 -0
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +306 -19
- package/examples/region-issue.json +2065 -0
- package/examples/sparkline.json +868 -0
- package/examples/test.json +5409 -0
- package/index.html +130 -123
- package/package.json +4 -2
- package/src/CdcChart.tsx +178 -94
- package/src/_stories/ChartEditor.stories.tsx +14 -3
- package/src/_stories/_mock/url_filter.json +1076 -0
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +2 -1
- package/src/components/AreaChart/components/AreaChart.jsx +2 -1
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +46 -63
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +36 -56
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +32 -39
- package/src/components/BarChart/components/BarChart.Vertical.tsx +44 -59
- package/src/components/BoxPlot/BoxPlot.jsx +2 -1
- package/src/components/DeviationBar.jsx +3 -3
- package/src/components/EditorPanel/EditorPanel.tsx +1684 -1564
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +1 -1
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +107 -0
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +48 -4
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +41 -0
- package/src/components/EditorPanel/components/Panels/index.tsx +9 -7
- package/src/components/EditorPanel/components/panels.scss +11 -0
- package/src/components/EditorPanel/editor-panel.scss +0 -724
- package/src/components/EditorPanel/useEditorPermissions.js +40 -14
- package/src/components/Legend/Legend.Component.tsx +43 -63
- package/src/components/Legend/Legend.tsx +8 -4
- package/src/components/LineChart/LineChartProps.ts +1 -0
- package/src/components/LineChart/helpers.ts +2 -2
- package/src/components/LineChart/index.tsx +7 -7
- package/src/components/LinearChart.jsx +11 -31
- package/src/components/PairedBarChart.jsx +6 -10
- package/src/components/PieChart/PieChart.tsx +3 -3
- package/src/components/Regions/components/Regions.tsx +120 -78
- package/src/components/Sankey/index.tsx +434 -0
- package/src/components/Sankey/sankey.scss +153 -0
- package/src/components/Sankey/types/index.ts +16 -0
- package/src/components/ScatterPlot/ScatterPlot.jsx +1 -0
- package/src/components/Sparkline/{SparkLine.jsx → components/SparkLine.tsx} +14 -30
- package/src/components/Sparkline/index.scss +3 -0
- package/src/components/Sparkline/index.tsx +1 -1
- package/src/components/ZoomBrush.tsx +2 -1
- package/src/data/initial-state.js +46 -2
- package/src/helpers/computeMarginBottom.ts +2 -1
- package/src/helpers/tests/computeMarginBottom.test.ts +2 -1
- package/src/hooks/useBarChart.js +5 -2
- package/src/hooks/useScales.ts +47 -18
- package/src/hooks/useTooltip.tsx +9 -8
- package/src/scss/main.scss +33 -29
- package/src/types/ChartConfig.ts +32 -14
- package/src/types/ChartContext.ts +7 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useContext, useState, useEffect, useRef, useMemo } from 'react'
|
|
2
2
|
import { animated, useTransition, interpolate } from 'react-spring'
|
|
3
|
-
import chroma from 'chroma-js'
|
|
4
3
|
|
|
5
4
|
// visx
|
|
6
5
|
import { Pie } from '@visx/shape'
|
|
@@ -18,6 +17,7 @@ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
|
18
17
|
import LegendComponent from '../Legend/Legend.Component'
|
|
19
18
|
import { createFormatLabels } from '../Legend/helpers/createFormatLabels'
|
|
20
19
|
import { scaleOrdinal } from '@visx/scale'
|
|
20
|
+
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
21
21
|
|
|
22
22
|
const enterUpdateTransition = ({ startAngle, endAngle }) => ({
|
|
23
23
|
startAngle,
|
|
@@ -156,8 +156,8 @@ const PieChart = props => {
|
|
|
156
156
|
const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.1
|
|
157
157
|
|
|
158
158
|
let textColor = '#FFF'
|
|
159
|
-
if (_colorScale(arc.data[config.runtime.xAxis.dataKey])
|
|
160
|
-
textColor =
|
|
159
|
+
if (_colorScale(arc.data[config.runtime.xAxis.dataKey])) {
|
|
160
|
+
textColor = getContrastColor(textColor, _colorScale(arc.data[config.runtime.xAxis.dataKey]))
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
return (
|
|
@@ -1,102 +1,153 @@
|
|
|
1
|
-
import React, { useContext } from 'react'
|
|
2
|
-
import { ChartConfig } from '../../../types/ChartConfig'
|
|
1
|
+
import React, { MouseEventHandler, useContext } from 'react'
|
|
3
2
|
import ConfigContext from '../../../ConfigContext'
|
|
4
3
|
import { ChartContext } from '../../../types/ChartContext'
|
|
5
4
|
import { Text } from '@visx/text'
|
|
6
5
|
import { Group } from '@visx/group'
|
|
7
|
-
import
|
|
8
|
-
import { formatDate } from '@cdc/core/helpers/cove/date.js'
|
|
6
|
+
import { formatDate, isDateScale } from '@cdc/core/helpers/cove/date.js'
|
|
9
7
|
|
|
10
8
|
type RegionsProps = {
|
|
11
9
|
xScale: Function
|
|
12
10
|
yMax: number
|
|
13
11
|
barWidth?: number
|
|
14
12
|
totalBarsInGroup?: number
|
|
13
|
+
handleTooltipMouseOff: MouseEventHandler<SVGElement>
|
|
14
|
+
handleTooltipMouseOver: MouseEventHandler<SVGElement>
|
|
15
|
+
handleTooltipClick: MouseEventHandler<SVGElement>
|
|
16
|
+
tooltipData: unknown
|
|
17
|
+
showTooltip: Function
|
|
18
|
+
hideTooltip: Function
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
// TODO: should regions be removed on categorical axis?
|
|
22
|
+
const Regions: React.FC<RegionsProps> = ({ xScale, barWidth = 0, totalBarsInGroup = 1, yMax, handleTooltipMouseOff, handleTooltipMouseOver, handleTooltipClick, tooltipData, showTooltip, hideTooltip }) => {
|
|
18
23
|
const { parseDate, config } = useContext<ChartContext>(ConfigContext)
|
|
19
24
|
|
|
20
25
|
const { runtime, regions, visualizationType, orientation, xAxis } = config
|
|
26
|
+
const domain = xScale.domain()
|
|
21
27
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
let width
|
|
28
|
+
const getFromValue = region => {
|
|
29
|
+
let from
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
// Fixed Date
|
|
32
|
+
if (!region?.fromType || region.fromType === 'Fixed') {
|
|
33
|
+
const date = new Date(region.from)
|
|
34
|
+
const parsedDate = parseDate(formatDate(config.xAxis.dateParseFormat, date)).getTime()
|
|
35
|
+
from = xScale(parsedDate)
|
|
36
|
+
|
|
37
|
+
if (visualizationType === 'Bar' && xAxis.type === 'date-time') {
|
|
38
|
+
from = from - (barWidth * totalBarsInGroup) / 2
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Previous Date
|
|
43
|
+
if (region.fromType === 'Previous Days') {
|
|
44
|
+
const previousDays = Number(region.from) || 0
|
|
45
|
+
const categoricalDomain = domain.map(d => formatDate(config.xAxis.dateParseFormat, new Date(d)))
|
|
46
|
+
const d = region.toType === 'Last Date' ? new Date(domain[domain.length - 1]).getTime() : new Date(region.to) // on categorical charts force leading zero 03/15/2016 vs 3/15/2016 for valid date format
|
|
47
|
+
const to = config.xAxis.type === 'categorical' ? formatDate(config.xAxis.dateParseFormat, d) : formatDate(config.xAxis.dateParseFormat, d)
|
|
48
|
+
const toDate = new Date(to)
|
|
49
|
+
from = new Date(toDate.setDate(toDate.getDate() - Number(previousDays)))
|
|
50
|
+
|
|
51
|
+
if (xAxis.type === 'date') {
|
|
52
|
+
from = new Date(formatDate(xAxis.dateParseFormat, from)).getTime()
|
|
53
|
+
|
|
54
|
+
let closestDate = domain[0]
|
|
55
|
+
let minDiff = Math.abs(from - closestDate)
|
|
56
|
+
|
|
57
|
+
for (let i = 1; i < domain.length; i++) {
|
|
58
|
+
const diff = Math.abs(from - domain[i])
|
|
59
|
+
if (diff < minDiff) {
|
|
60
|
+
minDiff = diff
|
|
61
|
+
closestDate = domain[i]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
from = closestDate
|
|
32
65
|
}
|
|
33
66
|
|
|
67
|
+
// Here the domain is in the xScale.dateParseFormat
|
|
34
68
|
if (xAxis.type === 'categorical') {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
69
|
+
let closestDate = domain[0]
|
|
70
|
+
let minDiff = Math.abs(new Date(from).getTime() - new Date(closestDate).getTime())
|
|
71
|
+
|
|
72
|
+
for (let i = 1; i < domain.length; i++) {
|
|
73
|
+
const diff = Math.abs(new Date(from).getTime() - new Date(domain[i]).getTime())
|
|
74
|
+
if (diff < minDiff) {
|
|
75
|
+
minDiff = diff
|
|
76
|
+
closestDate = domain[i]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
from = closestDate
|
|
38
80
|
}
|
|
39
81
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
to = region.toType !== 'Last Date' ? xScale(parseDate(region.to).getTime()) + (barWidth * totalBarsInGroup) / 2 : null
|
|
82
|
+
from = xScale(from)
|
|
83
|
+
}
|
|
43
84
|
|
|
44
|
-
|
|
45
|
-
|
|
85
|
+
if (xAxis.type === 'categorical' && region.fromType !== 'Previous Days') {
|
|
86
|
+
from = xScale(region.from)
|
|
87
|
+
}
|
|
46
88
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
89
|
+
if (visualizationType === 'Line' || visualizationType === 'Area Chart') {
|
|
90
|
+
let scalePadding = Number(config.yAxis.size)
|
|
91
|
+
if (xScale.bandwidth) {
|
|
92
|
+
scalePadding += xScale.bandwidth() / 2
|
|
51
93
|
}
|
|
94
|
+
from = from + scalePadding
|
|
95
|
+
}
|
|
52
96
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
let domain = xScale.domain()
|
|
57
|
-
let bisectDate = d3.bisector(d => d).left
|
|
58
|
-
let closestValue
|
|
59
|
-
|
|
60
|
-
let previousDays = Number(region.from)
|
|
61
|
-
let lastDate = region.toType === 'Last Date' ? domain[domain.length - 1] : region.to
|
|
62
|
-
let toDate = new Date(lastDate)
|
|
63
|
-
|
|
64
|
-
from = new Date(toDate.setDate(toDate.getDate() - previousDays)).getTime()
|
|
65
|
-
let targetValue = from
|
|
66
|
-
|
|
67
|
-
let index = bisectDate(domain, targetValue)
|
|
68
|
-
if (index === 0) {
|
|
69
|
-
closestValue = domain[0]
|
|
70
|
-
} else if (index === domain.length) {
|
|
71
|
-
closestValue = domain[domain.length - 1]
|
|
72
|
-
} else {
|
|
73
|
-
let d0 = domain[index - 1]
|
|
74
|
-
let d1 = domain[index]
|
|
75
|
-
closestValue = targetValue - d0 > d1 - targetValue ? d1 : d0
|
|
76
|
-
}
|
|
77
|
-
from = Number(xScale(closestValue) - (visualizationType === 'Bar' || visualizationType === 'Combo' ? (barWidth * totalBarsInGroup) / 2 : 0))
|
|
97
|
+
if (visualizationType === 'Bar' && config.xAxis.type === 'date-time' && region.fromType === 'Previous Days') {
|
|
98
|
+
from = from - (barWidth * totalBarsInGroup) / 2
|
|
99
|
+
}
|
|
78
100
|
|
|
79
|
-
|
|
80
|
-
|
|
101
|
+
return from
|
|
102
|
+
}
|
|
81
103
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
104
|
+
const getToValue = region => {
|
|
105
|
+
let to
|
|
106
|
+
|
|
107
|
+
// when xScale is categorical leading zeros are removed, ie. 03/15/2016 is 3/15/2016
|
|
108
|
+
if (xAxis.type === 'categorical') {
|
|
109
|
+
to = xScale(region.to)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (isDateScale(xAxis)) {
|
|
113
|
+
if (!region?.toType || region.toType === 'Fixed') {
|
|
114
|
+
to = xScale(parseDate(region.to).getTime())
|
|
88
115
|
}
|
|
89
116
|
|
|
90
|
-
if (
|
|
91
|
-
|
|
92
|
-
let previousDays = Number(region.from)
|
|
93
|
-
let to = region.toType === 'Last Date' ? formatDate(config.xAxis.dateParseFormat, domain[domain.length - 1]) : region.to
|
|
94
|
-
let toDate = new Date(to)
|
|
95
|
-
from = new Date(toDate.setDate(toDate.getDate() - previousDays)).getTime()
|
|
96
|
-
from = xScale(from)
|
|
97
|
-
to = xScale(parseDate(to))
|
|
98
|
-
width = to - from
|
|
117
|
+
if (visualizationType === 'Bar' || config.visualizationType === 'Combo') {
|
|
118
|
+
to = region.toType !== 'Last Date' ? xScale(parseDate(region.to).getTime()) + barWidth * totalBarsInGroup : to
|
|
99
119
|
}
|
|
120
|
+
}
|
|
121
|
+
if (region.toType === 'Last Date') {
|
|
122
|
+
const lastDate = domain[domain.length - 1]
|
|
123
|
+
to = Number(xScale(lastDate) + ((visualizationType === 'Bar' || visualizationType === 'Combo') && config.xAxis.type === 'date' ? barWidth * totalBarsInGroup : 0))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (visualizationType === 'Line' || visualizationType === 'Area Chart') {
|
|
127
|
+
let scalePadding = Number(config.yAxis.size)
|
|
128
|
+
if (xScale.bandwidth) {
|
|
129
|
+
scalePadding += xScale.bandwidth() / 2
|
|
130
|
+
}
|
|
131
|
+
to = to + scalePadding
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (visualizationType === 'Bar' && config.xAxis.type === 'date-time' && region.toType !== 'Last Date') {
|
|
135
|
+
to = to - (barWidth * totalBarsInGroup) / 2
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if ((visualizationType === 'Bar' || visualizationType === 'Combo') && xAxis.type === 'categorical') {
|
|
139
|
+
to = to + (visualizationType === 'Bar' || visualizationType === 'Combo' ? barWidth * totalBarsInGroup : 0)
|
|
140
|
+
}
|
|
141
|
+
return to
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const getWidth = (to, from) => to - from
|
|
145
|
+
|
|
146
|
+
if (regions && orientation === 'vertical') {
|
|
147
|
+
return regions.map(region => {
|
|
148
|
+
const from = getFromValue(region)
|
|
149
|
+
const to = getToValue(region)
|
|
150
|
+
const width = getWidth(to, from)
|
|
100
151
|
|
|
101
152
|
if (!from) return null
|
|
102
153
|
if (!to) return null
|
|
@@ -120,16 +171,7 @@ const Regions = ({ xScale, barWidth = 0, totalBarsInGroup = 1, yMax, handleToolt
|
|
|
120
171
|
}
|
|
121
172
|
|
|
122
173
|
return (
|
|
123
|
-
<Group
|
|
124
|
-
className='regions regions-group--line'
|
|
125
|
-
left={config.visualizationType === 'Bar' || config.visualizationType === 'Combo' ? 0 : config?.visualizationType === 'Line' ? Number(runtime.yAxis.size) : 0}
|
|
126
|
-
key={region.label}
|
|
127
|
-
onMouseMove={handleTooltipMouseOver}
|
|
128
|
-
onMouseLeave={handleTooltipMouseOff}
|
|
129
|
-
handleTooltipClick={handleTooltipClick}
|
|
130
|
-
tooltipData={JSON.stringify(tooltipData)}
|
|
131
|
-
showTooltip={showTooltip}
|
|
132
|
-
>
|
|
174
|
+
<Group height={100} fill='red' className='regions regions-group--line zzz' key={region.label} onMouseMove={handleTooltipMouseOver} onMouseLeave={handleTooltipMouseOff} handleTooltipClick={handleTooltipClick} tooltipData={JSON.stringify(tooltipData)} showTooltip={showTooltip}>
|
|
133
175
|
<TopRegionBorderShape />
|
|
134
176
|
<HighlightedArea />
|
|
135
177
|
<Text x={from + width / 2} y={5} fill={region.color} verticalAnchor='start' textAnchor='middle'>
|