@cdc/chart 4.24.2 → 4.24.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 +47386 -36618
- 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 +2 -1
- 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/sparkline.json +868 -0
- package/index.html +128 -123
- package/package.json +4 -2
- package/src/CdcChart.tsx +40 -22
- 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 +39 -49
- 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 +40 -55
- package/src/components/BoxPlot/BoxPlot.jsx +2 -1
- package/src/components/DeviationBar.jsx +3 -3
- package/src/components/EditorPanel/EditorPanel.tsx +167 -15
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +1 -1
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +108 -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/useEditorPermissions.js +40 -14
- package/src/components/Legend/Legend.Component.tsx +23 -15
- package/src/components/Legend/Legend.tsx +4 -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 +9 -30
- 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 +15 -18
- package/src/hooks/useTooltip.tsx +9 -8
- package/src/scss/main.scss +8 -29
- package/src/types/ChartConfig.ts +32 -14
- package/src/types/ChartContext.ts +7 -0
|
@@ -14,18 +14,19 @@ export const useEditorPermissions = () => {
|
|
|
14
14
|
'Combo',
|
|
15
15
|
'Deviation Bar',
|
|
16
16
|
'Forecasting',
|
|
17
|
-
'Forest Plot',
|
|
17
|
+
// 'Forest Plot',
|
|
18
18
|
'Line',
|
|
19
19
|
'Paired Bar',
|
|
20
20
|
'Pie',
|
|
21
21
|
'Scatter Plot',
|
|
22
|
-
'Spark Line'
|
|
22
|
+
'Spark Line',
|
|
23
|
+
'Sankey'
|
|
23
24
|
]
|
|
24
25
|
|
|
25
26
|
const headerColors = ['theme-blue', 'theme-purple', 'theme-brown', 'theme-teal', 'theme-pink', 'theme-orange', 'theme-slate', 'theme-indigo', 'theme-cyan', 'theme-green', 'theme-amber']
|
|
26
27
|
|
|
27
28
|
const visSupportsDateCategoryAxis = () => {
|
|
28
|
-
const disabledCharts = ['Forest Plot']
|
|
29
|
+
const disabledCharts = ['Forest Plot', 'Sankey']
|
|
29
30
|
if (disabledCharts.includes(visualizationType)) return false
|
|
30
31
|
return true
|
|
31
32
|
}
|
|
@@ -43,13 +44,13 @@ export const useEditorPermissions = () => {
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
const visHasLabelOnData = () => {
|
|
46
|
-
const disabledCharts = ['Area Chart', 'Box Plot', 'Pie', 'Scatter Plot', 'Forest Plot', 'Spark Line']
|
|
47
|
+
const disabledCharts = ['Area Chart', 'Box Plot', 'Pie', 'Scatter Plot', 'Forest Plot', 'Spark Line', 'Sankey']
|
|
47
48
|
if (disabledCharts.includes(visualizationType)) return false
|
|
48
49
|
return true
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
const visCanAnimate = () => {
|
|
52
|
-
const disabledCharts = ['Area Chart', 'Scatter Plot', 'Box Plot', 'Forest Plot', 'Spark Line']
|
|
53
|
+
const disabledCharts = ['Area Chart', 'Scatter Plot', 'Box Plot', 'Forest Plot', 'Spark Line', 'Sankey']
|
|
53
54
|
if (disabledCharts.includes(visualizationType)) return false
|
|
54
55
|
return true
|
|
55
56
|
}
|
|
@@ -62,6 +63,8 @@ export const useEditorPermissions = () => {
|
|
|
62
63
|
return false
|
|
63
64
|
case 'Spark Line':
|
|
64
65
|
return false
|
|
66
|
+
case 'Sankey':
|
|
67
|
+
return false
|
|
65
68
|
default:
|
|
66
69
|
return true
|
|
67
70
|
}
|
|
@@ -107,6 +110,8 @@ export const useEditorPermissions = () => {
|
|
|
107
110
|
|
|
108
111
|
const visHasDataCutoff = () => {
|
|
109
112
|
switch (visualizationType) {
|
|
113
|
+
case 'Sankey':
|
|
114
|
+
return false
|
|
110
115
|
case 'Forest Plot':
|
|
111
116
|
return false
|
|
112
117
|
case 'Box Plot':
|
|
@@ -120,8 +125,13 @@ export const useEditorPermissions = () => {
|
|
|
120
125
|
}
|
|
121
126
|
}
|
|
122
127
|
|
|
128
|
+
const visHasSelectableLegendValues = !['Box Plot', 'Forest Plot', 'Spark Line'].includes(visualizationType)
|
|
129
|
+
const visHasLegendAxisAlign = () => {
|
|
130
|
+
return visualizationType === 'Bar' && visualizationSubType === 'stacked' && config.legend.behavior === 'isolate'
|
|
131
|
+
}
|
|
132
|
+
|
|
123
133
|
const visSupportsTooltipOpacity = () => {
|
|
124
|
-
const disabledCharts = ['Spark Line']
|
|
134
|
+
const disabledCharts = ['Spark Line', 'Sankey']
|
|
125
135
|
if (disabledCharts.includes(visualizationType)) return false
|
|
126
136
|
return true
|
|
127
137
|
}
|
|
@@ -133,19 +143,19 @@ export const useEditorPermissions = () => {
|
|
|
133
143
|
}
|
|
134
144
|
|
|
135
145
|
const visSupportsSequentialPallete = () => {
|
|
136
|
-
const disabledCharts = ['Paired Bar', 'Deviation Bar', 'Forest Plot', 'Forecasting']
|
|
146
|
+
const disabledCharts = ['Paired Bar', 'Deviation Bar', 'Forest Plot', 'Forecasting', 'Sankey']
|
|
137
147
|
if (disabledCharts.includes(visualizationType)) return false
|
|
138
148
|
return true
|
|
139
149
|
}
|
|
140
150
|
|
|
141
151
|
const visSupportsNonSequentialPallete = () => {
|
|
142
|
-
const disabledCharts = ['Paired Bar', 'Deviation Bar', 'Forest Plot', 'Forecasting']
|
|
152
|
+
const disabledCharts = ['Paired Bar', 'Deviation Bar', 'Forest Plot', 'Forecasting', 'Sankey']
|
|
143
153
|
if (disabledCharts.includes(visualizationType)) return false
|
|
144
154
|
return true
|
|
145
155
|
}
|
|
146
156
|
|
|
147
157
|
const visSupportsReverseColorPalette = () => {
|
|
148
|
-
const disabledCharts = ['Forest Plot', 'Paired Bar', 'Deviation Bar']
|
|
158
|
+
const disabledCharts = ['Forest Plot', 'Paired Bar', 'Deviation Bar', 'Sankey']
|
|
149
159
|
if (disabledCharts.includes(visualizationType)) return false
|
|
150
160
|
return true
|
|
151
161
|
}
|
|
@@ -187,7 +197,7 @@ export const useEditorPermissions = () => {
|
|
|
187
197
|
}
|
|
188
198
|
|
|
189
199
|
const visSupportsRegions = () => {
|
|
190
|
-
const disabledCharts = ['Forest Plot', 'Pie', 'Paired Bar', 'Spark Line']
|
|
200
|
+
const disabledCharts = ['Forest Plot', 'Pie', 'Paired Bar', 'Spark Line', 'Sankey']
|
|
191
201
|
if (disabledCharts.includes(visualizationType)) return false
|
|
192
202
|
return true
|
|
193
203
|
}
|
|
@@ -205,7 +215,7 @@ export const useEditorPermissions = () => {
|
|
|
205
215
|
}
|
|
206
216
|
|
|
207
217
|
const visSupportsFilters = () => {
|
|
208
|
-
const disabledCharts = ['Forest Plot']
|
|
218
|
+
const disabledCharts = ['Forest Plot', 'Sankey']
|
|
209
219
|
if (disabledCharts.includes(visualizationType)) return false
|
|
210
220
|
return true
|
|
211
221
|
}
|
|
@@ -258,7 +268,7 @@ export const useEditorPermissions = () => {
|
|
|
258
268
|
}
|
|
259
269
|
|
|
260
270
|
const visSupportsLeftValueAxis = () => {
|
|
261
|
-
const disabledCharts = ['Spark Line']
|
|
271
|
+
const disabledCharts = ['Spark Line', 'Sankey']
|
|
262
272
|
if (disabledCharts.includes(visualizationType)) return false
|
|
263
273
|
return true
|
|
264
274
|
}
|
|
@@ -270,13 +280,13 @@ export const useEditorPermissions = () => {
|
|
|
270
280
|
}
|
|
271
281
|
|
|
272
282
|
const visSupportsDateCategoryHeight = () => {
|
|
273
|
-
const disabledCharts = ['Spark Line']
|
|
283
|
+
const disabledCharts = ['Spark Line', 'Sankey']
|
|
274
284
|
if (disabledCharts.includes(visualizationType)) return false
|
|
275
285
|
return true
|
|
276
286
|
}
|
|
277
287
|
|
|
278
288
|
const visSupportsDateCategoryAxisPadding = () => {
|
|
279
|
-
return config.xAxis.type === 'date'
|
|
289
|
+
return config.xAxis.type === 'date-time'
|
|
280
290
|
}
|
|
281
291
|
|
|
282
292
|
const visSupportsReactTooltip = () => {
|
|
@@ -285,6 +295,19 @@ export const useEditorPermissions = () => {
|
|
|
285
295
|
}
|
|
286
296
|
}
|
|
287
297
|
|
|
298
|
+
const visSupportsPreliminaryData = () => {
|
|
299
|
+
// check if Line added in Combo
|
|
300
|
+
const lineExist = config?.series.some(item => ['Line', 'dashed-sm', 'dashed-md', 'dashed-lg'].includes(item?.type))
|
|
301
|
+
if (visualizationType === 'Line') {
|
|
302
|
+
return true
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (visualizationType === 'Combo' && lineExist) {
|
|
306
|
+
return true
|
|
307
|
+
}
|
|
308
|
+
return false
|
|
309
|
+
}
|
|
310
|
+
|
|
288
311
|
return {
|
|
289
312
|
enabledChartTypes,
|
|
290
313
|
headerColors,
|
|
@@ -295,6 +318,7 @@ export const useEditorPermissions = () => {
|
|
|
295
318
|
visHasLabelOnData,
|
|
296
319
|
visHasDataSuppression,
|
|
297
320
|
visHasLegend,
|
|
321
|
+
visHasLegendAxisAlign,
|
|
298
322
|
visHasBrushChart,
|
|
299
323
|
visHasNumbersOnBars,
|
|
300
324
|
visSupportsBarSpace,
|
|
@@ -312,6 +336,7 @@ export const useEditorPermissions = () => {
|
|
|
312
336
|
visSupportsFootnotes,
|
|
313
337
|
visSupportsLeftValueAxis,
|
|
314
338
|
visSupportsNonSequentialPallete,
|
|
339
|
+
visSupportsPreliminaryData,
|
|
315
340
|
visSupportsRankByValue,
|
|
316
341
|
visSupportsRegions,
|
|
317
342
|
visSupportsResponsiveTicks,
|
|
@@ -319,6 +344,7 @@ export const useEditorPermissions = () => {
|
|
|
319
344
|
visSupportsSequentialPallete,
|
|
320
345
|
visSupportsSuperTitle,
|
|
321
346
|
visSupportsTooltipLines,
|
|
347
|
+
visHasSelectableLegendValues,
|
|
322
348
|
visSupportsTooltipOpacity,
|
|
323
349
|
visSupportsValueAxisGridLines,
|
|
324
350
|
visSupportsValueAxisLabels,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import parse from 'html-react-parser'
|
|
2
2
|
import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend'
|
|
3
3
|
import LegendCircle from '@cdc/core/components/LegendCircle'
|
|
4
|
+
import Button from '@cdc/core/components/elements/Button'
|
|
4
5
|
|
|
5
6
|
import useLegendClasses from '../../hooks/useLegendClasses'
|
|
6
7
|
import { useHighlightedBars } from '../../hooks/useHighlightedBars'
|
|
@@ -11,6 +12,7 @@ import { Label } from '../../types/Label'
|
|
|
11
12
|
import { ChartConfig } from '../../types/ChartConfig'
|
|
12
13
|
import { ColorScale } from '../../types/ChartContext'
|
|
13
14
|
import { Group } from '@visx/group'
|
|
15
|
+
import { forwardRef } from 'react'
|
|
14
16
|
|
|
15
17
|
interface LegendProps {
|
|
16
18
|
config: ChartConfig
|
|
@@ -20,10 +22,11 @@ interface LegendProps {
|
|
|
20
22
|
highlightReset: Function
|
|
21
23
|
currentViewport: string
|
|
22
24
|
formatLabels: (labels: Label[]) => Label[]
|
|
25
|
+
ref: React.Ref<() => void>
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
/* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
|
|
26
|
-
const Legend: React.FC<LegendProps> = ({ config, colorScale, seriesHighlight, highlight, highlightReset, currentViewport, formatLabels }) => {
|
|
29
|
+
const Legend: React.FC<LegendProps> = forwardRef(({ config, colorScale, seriesHighlight, highlight, highlightReset, currentViewport, formatLabels }, ref) => {
|
|
27
30
|
const { innerClasses, containerClasses } = useLegendClasses(config)
|
|
28
31
|
const { runtime, orientation, legend } = config
|
|
29
32
|
if (!legend) return null
|
|
@@ -66,8 +69,8 @@ const Legend: React.FC<LegendProps> = ({ config, colorScale, seriesHighlight, hi
|
|
|
66
69
|
let highLightedLegendItems = HighLightedBarUtils.findDuplicates(config.highlightedBarValues)
|
|
67
70
|
|
|
68
71
|
return (
|
|
69
|
-
<aside style={legendClasses} id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
|
|
70
|
-
{legend.label && <
|
|
72
|
+
<aside ref={ref} style={legendClasses} id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
|
|
73
|
+
{legend.label && <h3>{parse(legend.label)}</h3>}
|
|
71
74
|
{legend.description && <p>{parse(legend.description)}</p>}
|
|
72
75
|
<LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
|
|
73
76
|
{labels => {
|
|
@@ -101,14 +104,17 @@ const Legend: React.FC<LegendProps> = ({ config, colorScale, seriesHighlight, hi
|
|
|
101
104
|
className={className.join(' ')}
|
|
102
105
|
tabIndex={0}
|
|
103
106
|
key={`legend-quantile-${i}`}
|
|
104
|
-
|
|
107
|
+
onKeyDown={e => {
|
|
105
108
|
if (e.key === 'Enter') {
|
|
109
|
+
e.preventDefault()
|
|
106
110
|
highlight(label)
|
|
107
111
|
}
|
|
108
112
|
}}
|
|
109
|
-
onClick={
|
|
113
|
+
onClick={e => {
|
|
114
|
+
e.preventDefault()
|
|
110
115
|
highlight(label)
|
|
111
116
|
}}
|
|
117
|
+
role='button'
|
|
112
118
|
>
|
|
113
119
|
{config.visualizationType === 'Line' && config.legend.lineMode ? (
|
|
114
120
|
<svg width={40} height={20}>
|
|
@@ -142,12 +148,14 @@ const Legend: React.FC<LegendProps> = ({ config, colorScale, seriesHighlight, hi
|
|
|
142
148
|
className={className}
|
|
143
149
|
tabIndex={0}
|
|
144
150
|
key={`legend-quantile-${i}`}
|
|
145
|
-
|
|
151
|
+
onKeyDown={e => {
|
|
146
152
|
if (e.key === 'Enter') {
|
|
153
|
+
e.preventDefault()
|
|
147
154
|
highlight(bar.legendLabel)
|
|
148
155
|
}
|
|
149
156
|
}}
|
|
150
|
-
onClick={
|
|
157
|
+
onClick={e => {
|
|
158
|
+
e.preventDefault()
|
|
151
159
|
highlight(bar.legendLabel)
|
|
152
160
|
}}
|
|
153
161
|
>
|
|
@@ -158,15 +166,10 @@ const Legend: React.FC<LegendProps> = ({ config, colorScale, seriesHighlight, hi
|
|
|
158
166
|
</LegendItem>
|
|
159
167
|
)
|
|
160
168
|
})}
|
|
161
|
-
{seriesHighlight.length > 0 && (
|
|
162
|
-
<button className={`legend-reset ${config.theme}`} onClick={labels => highlightReset(labels)} tabIndex={0}>
|
|
163
|
-
Reset
|
|
164
|
-
</button>
|
|
165
|
-
)}
|
|
166
169
|
</div>
|
|
167
170
|
|
|
168
171
|
<>
|
|
169
|
-
{config?.preliminaryData?.some(pd => pd.label) && config.visualizationType
|
|
172
|
+
{config?.preliminaryData?.some(pd => pd.label) && ['Line', 'Combo'].includes(config.visualizationType) && (
|
|
170
173
|
<>
|
|
171
174
|
<hr></hr>
|
|
172
175
|
<div className={config.legend.singleRow && isBottomOrSmallViewport ? 'legend-container__inner bottom single-row' : ''}>
|
|
@@ -178,7 +181,7 @@ const Legend: React.FC<LegendProps> = ({ config, colorScale, seriesHighlight, hi
|
|
|
178
181
|
<svg style={{ width: '50px' }} key={index} height={'23px'}>
|
|
179
182
|
{pd.style.includes('Dashed') ? <Line from={{ x: 10, y: 10 }} to={{ x: 40, y: 10 }} stroke={'#000'} strokeWidth={2} strokeDasharray={handleLineType(pd.style)} /> : <circle r={6} strokeWidth={2} stroke={'#000'} cx={22} cy={10} fill='transparent' />}
|
|
180
183
|
</svg>
|
|
181
|
-
<span
|
|
184
|
+
<span> {pd.label}</span>
|
|
182
185
|
</div>
|
|
183
186
|
)}
|
|
184
187
|
</>
|
|
@@ -192,8 +195,13 @@ const Legend: React.FC<LegendProps> = ({ config, colorScale, seriesHighlight, hi
|
|
|
192
195
|
)
|
|
193
196
|
}}
|
|
194
197
|
</LegendOrdinal>
|
|
198
|
+
{seriesHighlight.length > 0 && (
|
|
199
|
+
<Button onClick={labels => highlightReset(labels)} style={{ marginTop: '1rem' }}>
|
|
200
|
+
Reset
|
|
201
|
+
</Button>
|
|
202
|
+
)}
|
|
195
203
|
</aside>
|
|
196
204
|
)
|
|
197
|
-
}
|
|
205
|
+
})
|
|
198
206
|
|
|
199
207
|
export default Legend
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { useContext } from 'react'
|
|
1
|
+
import { useContext, forwardRef } from 'react'
|
|
2
2
|
import ConfigContext from '../../ConfigContext'
|
|
3
3
|
import LegendComponent from './Legend.Component'
|
|
4
4
|
import { createFormatLabels } from './helpers/createFormatLabels'
|
|
5
5
|
|
|
6
6
|
/* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
|
|
7
|
-
const Legend = () => {
|
|
7
|
+
const Legend = forwardRef((props, ref) => {
|
|
8
8
|
// prettier-ignore
|
|
9
9
|
const {
|
|
10
10
|
config,
|
|
@@ -22,7 +22,7 @@ const Legend = () => {
|
|
|
22
22
|
|
|
23
23
|
const createLegendLabels = createFormatLabels(config, tableData, data, colorScale)
|
|
24
24
|
|
|
25
|
-
return !['Box Plot', 'Pie'].includes(config.visualizationType) && <LegendComponent config={config} colorScale={colorScale} seriesHighlight={seriesHighlight} highlight={highlight} highlightReset={highlightReset} currentViewport={currentViewport} formatLabels={createLegendLabels} />
|
|
26
|
-
}
|
|
25
|
+
return !['Box Plot', 'Pie'].includes(config.visualizationType) && <LegendComponent ref={ref} config={config} colorScale={colorScale} seriesHighlight={seriesHighlight} highlight={highlight} highlightReset={highlightReset} currentViewport={currentViewport} formatLabels={createLegendLabels} />
|
|
26
|
+
})
|
|
27
27
|
|
|
28
28
|
export default Legend
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type PreliminaryDataItem, DataItem, StyleProps, Style } from './LineChartProps'
|
|
2
2
|
|
|
3
3
|
export const createStyles = (props: StyleProps): Style[] => {
|
|
4
|
-
const { preliminaryData, data, stroke, handleLineType, lineType, seriesKey } = props
|
|
4
|
+
const { preliminaryData, data, stroke, strokeWidth, handleLineType, lineType, seriesKey } = props
|
|
5
5
|
|
|
6
6
|
const validPreliminaryData: PreliminaryDataItem[] = preliminaryData.filter(pd => pd.seriesKey && pd.column && pd.value && pd.type && pd.style)
|
|
7
7
|
const getMatchingPd = (point: DataItem): PreliminaryDataItem => validPreliminaryData.find(pd => pd.seriesKey === seriesKey && point[pd.column] === pd.value && pd.type === 'effect' && pd.style !== 'Open Circles')
|
|
@@ -9,7 +9,7 @@ export const createStyles = (props: StyleProps): Style[] => {
|
|
|
9
9
|
let styles: Style[] = []
|
|
10
10
|
const createStyle = (lineStyle): Style => ({
|
|
11
11
|
stroke: stroke,
|
|
12
|
-
strokeWidth:
|
|
12
|
+
strokeWidth: strokeWidth,
|
|
13
13
|
strokeDasharray: lineStyle
|
|
14
14
|
})
|
|
15
15
|
|
|
@@ -52,7 +52,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
52
52
|
|
|
53
53
|
return (
|
|
54
54
|
<ErrorBoundary component='LineChart'>
|
|
55
|
-
<Group left={config.runtime.yAxis.size
|
|
55
|
+
<Group left={config.runtime.yAxis.size}>
|
|
56
56
|
{' '}
|
|
57
57
|
{/* left - expects a number not a string */}
|
|
58
58
|
{(config.runtime.lineSeriesKeys || config.runtime.seriesKeys).map((seriesKey, index) => {
|
|
@@ -62,7 +62,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
62
62
|
let displayArea = legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1
|
|
63
63
|
const circleData = filterCircles(config.preliminaryData, rawData, seriesKey)
|
|
64
64
|
// styles for preliminary Data items
|
|
65
|
-
let styles = createStyles({ preliminaryData: config.preliminaryData, data: tableData, stroke: colorScale(config.runtime.seriesLabels[seriesKey]), handleLineType, lineType, seriesKey })
|
|
65
|
+
let styles = createStyles({ preliminaryData: config.preliminaryData, data: tableData, stroke: colorScale(config.runtime.seriesLabels[seriesKey]), strokeWidth: seriesData[0].weight || 2, handleLineType, lineType, seriesKey })
|
|
66
66
|
|
|
67
67
|
let xPos = d => {
|
|
68
68
|
return xScale(getXAxisData(d)) + (xScale.bandwidth ? xScale.bandwidth() / 2 : 0)
|
|
@@ -166,7 +166,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
166
166
|
{config?.preliminaryData?.some(d => d.value && d.column) ? (
|
|
167
167
|
<SplitLinePath
|
|
168
168
|
curve={allCurves[seriesData[0].lineType]}
|
|
169
|
-
segments={(config.xAxis.type === 'date'
|
|
169
|
+
segments={(config.xAxis.type === 'date-time'
|
|
170
170
|
? data.sort((d1, d2) => {
|
|
171
171
|
let x1 = getXAxisData(d1)
|
|
172
172
|
let x2 = getXAxisData(d2)
|
|
@@ -190,7 +190,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
190
190
|
<LinePath
|
|
191
191
|
curve={allCurves[seriesData[0].lineType]}
|
|
192
192
|
data={
|
|
193
|
-
config.xAxis.type === 'date'
|
|
193
|
+
config.xAxis.type === 'date-time'
|
|
194
194
|
? data.sort((d1, d2) => {
|
|
195
195
|
let x1 = getXAxisData(d1)
|
|
196
196
|
let x2 = getXAxisData(d2)
|
|
@@ -203,7 +203,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
203
203
|
x={d => xPos(d)}
|
|
204
204
|
y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(Number(getYAxisData(d, seriesKey))))}
|
|
205
205
|
stroke={colorScale(config.runtime.seriesLabels[seriesKey])}
|
|
206
|
-
strokeWidth={2}
|
|
206
|
+
strokeWidth={seriesData[0].weight || 2}
|
|
207
207
|
strokeOpacity={1}
|
|
208
208
|
shapeRendering='geometricPrecision'
|
|
209
209
|
strokeDasharray={lineType ? handleLineType(lineType) : 0}
|
|
@@ -216,14 +216,14 @@ const LineChart = (props: LineChartProps) => {
|
|
|
216
216
|
|
|
217
217
|
{/* circles for preliminaryData data */}
|
|
218
218
|
{circleData.map((d, i) => {
|
|
219
|
-
return <circle key={i} cx={xPos(d)} cy={yScale(Number(getYAxisData(d, seriesKey)))} r={6} strokeWidth={2} stroke={colorScale ? colorScale(config.runtime.seriesLabels[seriesKey]) : '#000'} fill='#fff' />
|
|
219
|
+
return <circle key={i} cx={xPos(d)} cy={seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(Number(getYAxisData(d, seriesKey)))} r={6} strokeWidth={seriesData[0].weight || 2} stroke={colorScale ? colorScale(config.runtime.seriesLabels[seriesKey]) : '#000'} fill='#fff' />
|
|
220
220
|
})}
|
|
221
221
|
|
|
222
222
|
{/* ANIMATED LINE */}
|
|
223
223
|
{config.animate && (
|
|
224
224
|
<LinePath
|
|
225
225
|
className='animation'
|
|
226
|
-
curve={seriesData.lineType}
|
|
226
|
+
curve={allCurves[seriesData[0].lineType]}
|
|
227
227
|
data={data}
|
|
228
228
|
x={d => xPos(d)}
|
|
229
229
|
y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(Number(getYAxisData(d, seriesKey))))}
|
|
@@ -7,6 +7,7 @@ import { Line, Bar } from '@visx/shape'
|
|
|
7
7
|
import { Text } from '@visx/text'
|
|
8
8
|
import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
9
9
|
import { useTooltip, TooltipWithBounds } from '@visx/tooltip'
|
|
10
|
+
import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
10
11
|
|
|
11
12
|
// CDC Components
|
|
12
13
|
import { AreaChart, AreaChartStacked } from './AreaChart'
|
|
@@ -36,32 +37,10 @@ import { useEditorPermissions } from './EditorPanel/useEditorPermissions'
|
|
|
36
37
|
import ZoomBrush from './ZoomBrush'
|
|
37
38
|
|
|
38
39
|
const LinearChart = props => {
|
|
39
|
-
const {
|
|
40
|
-
isEditor,
|
|
41
|
-
isDashboard,
|
|
42
|
-
computeMarginBottom,
|
|
43
|
-
transformedData: data,
|
|
44
|
-
dimensions,
|
|
45
|
-
config,
|
|
46
|
-
parseDate,
|
|
47
|
-
formatDate,
|
|
48
|
-
currentViewport,
|
|
49
|
-
formatNumber,
|
|
50
|
-
handleChartAriaLabels,
|
|
51
|
-
updateConfig,
|
|
52
|
-
handleLineType,
|
|
53
|
-
rawData,
|
|
54
|
-
capitalize,
|
|
55
|
-
setSharedFilter,
|
|
56
|
-
setSharedFilterValue,
|
|
57
|
-
getTextWidth,
|
|
58
|
-
isDebug
|
|
59
|
-
} = useContext(ConfigContext)
|
|
40
|
+
const { transformedData: data, dimensions, config, parseDate, formatDate, currentViewport, formatNumber, handleChartAriaLabels, updateConfig, handleLineType, getTextWidth } = useContext(ConfigContext)
|
|
60
41
|
// todo: start destructuring this file for conciseness
|
|
61
42
|
const { visualizationType, visualizationSubType, orientation, xAxis, yAxis, runtime, debugSvg } = config
|
|
62
43
|
|
|
63
|
-
const getDate = d => new Date(d[config.xAxis.dataKey])
|
|
64
|
-
|
|
65
44
|
// configure width
|
|
66
45
|
let [width] = dimensions
|
|
67
46
|
if (config && config.legend && !config.legend.hide && config.legend.position !== 'bottom' && ['lg', 'md'].includes(currentViewport)) {
|
|
@@ -99,7 +78,7 @@ const LinearChart = props => {
|
|
|
99
78
|
})
|
|
100
79
|
|
|
101
80
|
// getters & functions
|
|
102
|
-
const getXAxisData = d => (config.runtime.xAxis
|
|
81
|
+
const getXAxisData = d => (isDateScale(config.runtime.xAxis) ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
|
|
103
82
|
const getYAxisData = (d, seriesKey) => d[seriesKey]
|
|
104
83
|
const xAxisDataMapped = config.brush.active && config.brush.data?.length ? config.brush.data.map(d => getXAxisData(d)) : data.map(d => getXAxisData(d))
|
|
105
84
|
const section = config.orientation === 'horizontal' || config.visualizationType === 'Forest Plot' ? 'yAxis' : 'xAxis'
|
|
@@ -122,7 +101,7 @@ const LinearChart = props => {
|
|
|
122
101
|
|
|
123
102
|
if (config.data && !config.data[index] && visualizationType === 'Forest Plot') return
|
|
124
103
|
if (config.visualizationType === 'Forest Plot') return config.data[index][config.xAxis.dataKey]
|
|
125
|
-
if (runtime.yAxis
|
|
104
|
+
if (isDateScale(runtime.yAxis)) return formatDate(parseDate(tick))
|
|
126
105
|
if (orientation === 'vertical') return formatNumber(tick, 'left', shouldAbbreviate)
|
|
127
106
|
return tick
|
|
128
107
|
}
|
|
@@ -133,7 +112,7 @@ const LinearChart = props => {
|
|
|
133
112
|
tick = 0
|
|
134
113
|
}
|
|
135
114
|
|
|
136
|
-
if (runtime.xAxis
|
|
115
|
+
if (isDateScale(runtime.xAxis) && config.visualizationType !== 'Forest Plot') return formatDate(tick)
|
|
137
116
|
if (orientation === 'horizontal' && config.visualizationType !== 'Forest Plot') return formatNumber(tick, 'left', shouldAbbreviate)
|
|
138
117
|
if (config.xAxis.type === 'continuous' && config.visualizationType !== 'Forest Plot') return formatNumber(tick, 'bottom', shouldAbbreviate)
|
|
139
118
|
if (config.visualizationType === 'Forest Plot') return formatNumber(tick, 'left', config.dataFormat.abbreviated, config.runtime.xAxis.prefix, config.runtime.xAxis.suffix, Number(config.dataFormat.roundTo))
|
|
@@ -388,7 +367,7 @@ const LinearChart = props => {
|
|
|
388
367
|
{/* X axis */}
|
|
389
368
|
{visualizationType !== 'Paired Bar' && visualizationType !== 'Spark Line' && (
|
|
390
369
|
<AxisBottom
|
|
391
|
-
top={runtime.horizontal && config.visualizationType !== 'Forest Plot' ? Number(heightHorizontal) + Number(config.xAxis.axisPadding) : config.visualizationType === 'Forest Plot' ? yMax + Number(config.xAxis.axisPadding) : yMax
|
|
370
|
+
top={runtime.horizontal && config.visualizationType !== 'Forest Plot' ? Number(heightHorizontal) + Number(config.xAxis.axisPadding) : config.visualizationType === 'Forest Plot' ? yMax + Number(config.xAxis.axisPadding) : yMax}
|
|
392
371
|
left={config.visualizationType !== 'Forest Plot' ? Number(runtime.yAxis.size) : 0}
|
|
393
372
|
label={runtime.xAxis.label}
|
|
394
373
|
tickFormat={handleBottomTickFormatting}
|
|
@@ -502,7 +481,7 @@ const LinearChart = props => {
|
|
|
502
481
|
)}
|
|
503
482
|
{visualizationType === 'Paired Bar' && (
|
|
504
483
|
<>
|
|
505
|
-
<AxisBottom top={yMax} left={Number(runtime.yAxis.size)} label={runtime.xAxis.label} tickFormat={runtime.xAxis
|
|
484
|
+
<AxisBottom top={yMax} left={Number(runtime.yAxis.size)} label={runtime.xAxis.label} tickFormat={isDateScale(runtime.xAxis) ? formatDate : formatNumber} scale={g1xScale} stroke='#333' tickStroke='#333' numTicks={runtime.xAxis.numTicks || undefined}>
|
|
506
485
|
{props => {
|
|
507
486
|
return (
|
|
508
487
|
<Group className='bottom-axis'>
|
|
@@ -529,7 +508,7 @@ const LinearChart = props => {
|
|
|
529
508
|
top={yMax}
|
|
530
509
|
left={Number(runtime.yAxis.size)}
|
|
531
510
|
label={runtime.xAxis.label}
|
|
532
|
-
tickFormat={runtime.xAxis
|
|
511
|
+
tickFormat={isDateScale(runtime.xAxis) ? formatDate : runtime.xAxis.dataKey !== 'Year' ? formatNumber : tick => tick}
|
|
533
512
|
scale={g2xScale}
|
|
534
513
|
stroke='#333'
|
|
535
514
|
tickStroke='#333'
|
|
@@ -708,7 +687,7 @@ const LinearChart = props => {
|
|
|
708
687
|
newX = yAxis
|
|
709
688
|
}
|
|
710
689
|
|
|
711
|
-
let anchorPosition = newX
|
|
690
|
+
let anchorPosition = isDateScale(newX) ? xScale(parseDate(anchor.value, false)) : xScale(anchor.value)
|
|
712
691
|
|
|
713
692
|
// have to move up
|
|
714
693
|
// const padding = orientation === 'horizontal' ? Number(config.xAxis.size) : Number(config.yAxis.size)
|
|
@@ -5,7 +5,7 @@ import { scaleLinear } from '@visx/scale'
|
|
|
5
5
|
import { Text } from '@visx/text'
|
|
6
6
|
|
|
7
7
|
import ConfigContext from '../ConfigContext'
|
|
8
|
-
import
|
|
8
|
+
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
9
9
|
|
|
10
10
|
const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
11
11
|
const { config, colorScale, transformedData: data, formatNumber, seriesHighlight, getTextWidth } = useContext(ConfigContext)
|
|
@@ -47,15 +47,8 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
// Set label color
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (groupOne.color && chroma.contrast(labelColor, groupOne.color) < 4.9) {
|
|
53
|
-
groupOne.labelColor = '#FFFFFF'
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (groupTwo.color && chroma.contrast(labelColor, groupTwo.color) < 4.9) {
|
|
57
|
-
groupTwo.labelColor = '#FFFFFF'
|
|
58
|
-
}
|
|
50
|
+
groupOne.labelColor = groupOne.color ? getContrastColor('#000', groupOne.color) : '#000'
|
|
51
|
+
groupTwo.labelColor = groupTwo.color ? getContrastColor('#000', groupTwo.color) : '#000'
|
|
59
52
|
|
|
60
53
|
const label = config.yAxis.label ? `${config.yAxis.label}: ` : ''
|
|
61
54
|
|
|
@@ -87,6 +80,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
87
80
|
`}
|
|
88
81
|
</style>
|
|
89
82
|
<svg id='cdc-visualization__paired-bar-chart' width={originalWidth} height={height} viewBox={`0 0 ${width + Number(config.runtime.yAxis.size)} ${height}`} role='img' tabIndex={0}>
|
|
83
|
+
<title>{`Paired bar chart graphic with the title ${config.title ? config.title : 'No Title Found'}`}</title>
|
|
90
84
|
<Group top={0} left={Number(config.xAxis.size)}>
|
|
91
85
|
{data
|
|
92
86
|
.filter(item => config.series[0].dataKey === groupOne.dataKey)
|
|
@@ -122,6 +116,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
122
116
|
strokeWidth={borderWidth}
|
|
123
117
|
opacity={transparentBar ? 0.5 : 1}
|
|
124
118
|
display={displayBar ? 'block' : 'none'}
|
|
119
|
+
tabIndex={-1}
|
|
125
120
|
/>
|
|
126
121
|
{config.yAxis.displayNumbersOnBar && displayBar && (
|
|
127
122
|
<Text textAnchor={textFits ? 'start' : 'end'} dx={textFits ? 5 : -5} verticalAnchor='middle' x={halfWidth - barWidth} y={y + config.barHeight / 2} fill={textFits ? groupOne.labelColor : '#000'}>
|
|
@@ -173,6 +168,7 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
173
168
|
stroke='#333'
|
|
174
169
|
opacity={transparentBar ? 0.5 : 1}
|
|
175
170
|
display={displayBar ? 'block' : 'none'}
|
|
171
|
+
tabIndex={-1}
|
|
176
172
|
/>
|
|
177
173
|
{config.yAxis.displayNumbersOnBar && displayBar && (
|
|
178
174
|
<Text textAnchor={isTextFits ? 'end' : 'start'} dx={isTextFits ? -5 : 5} verticalAnchor='middle' x={halfWidth + barWidth} y={y + config.barHeight / 2} fill={isTextFits ? groupTwo.labelColor : '#000'}>
|
|
@@ -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 (
|