@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.
Files changed (57) hide show
  1. package/dist/cdcchart.js +47386 -36618
  2. package/examples/chart-regression-1.json +378 -0
  3. package/examples/chart-regression-2.json +2360 -0
  4. package/examples/feature/filters/url-filter.json +1076 -0
  5. package/examples/feature/line/line-chart.json +2 -1
  6. package/examples/feature/regions/index.json +50 -4
  7. package/examples/feature/sankey/sankey-example-data.json +1364 -0
  8. package/examples/feature/sankey/sankey_chart_data.csv +20 -0
  9. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +306 -19
  10. package/examples/sparkline.json +868 -0
  11. package/index.html +128 -123
  12. package/package.json +4 -2
  13. package/src/CdcChart.tsx +40 -22
  14. package/src/_stories/ChartEditor.stories.tsx +14 -3
  15. package/src/_stories/_mock/url_filter.json +1076 -0
  16. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +2 -1
  17. package/src/components/AreaChart/components/AreaChart.jsx +2 -1
  18. package/src/components/BarChart/components/BarChart.Horizontal.tsx +39 -49
  19. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +36 -56
  20. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +32 -39
  21. package/src/components/BarChart/components/BarChart.Vertical.tsx +40 -55
  22. package/src/components/BoxPlot/BoxPlot.jsx +2 -1
  23. package/src/components/DeviationBar.jsx +3 -3
  24. package/src/components/EditorPanel/EditorPanel.tsx +167 -15
  25. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +1 -1
  26. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +108 -0
  27. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +48 -4
  28. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +41 -0
  29. package/src/components/EditorPanel/components/Panels/index.tsx +9 -7
  30. package/src/components/EditorPanel/components/panels.scss +11 -0
  31. package/src/components/EditorPanel/useEditorPermissions.js +40 -14
  32. package/src/components/Legend/Legend.Component.tsx +23 -15
  33. package/src/components/Legend/Legend.tsx +4 -4
  34. package/src/components/LineChart/LineChartProps.ts +1 -0
  35. package/src/components/LineChart/helpers.ts +2 -2
  36. package/src/components/LineChart/index.tsx +7 -7
  37. package/src/components/LinearChart.jsx +9 -30
  38. package/src/components/PairedBarChart.jsx +6 -10
  39. package/src/components/PieChart/PieChart.tsx +3 -3
  40. package/src/components/Regions/components/Regions.tsx +120 -78
  41. package/src/components/Sankey/index.tsx +434 -0
  42. package/src/components/Sankey/sankey.scss +153 -0
  43. package/src/components/Sankey/types/index.ts +16 -0
  44. package/src/components/ScatterPlot/ScatterPlot.jsx +1 -0
  45. package/src/components/Sparkline/{SparkLine.jsx → components/SparkLine.tsx} +14 -30
  46. package/src/components/Sparkline/index.scss +3 -0
  47. package/src/components/Sparkline/index.tsx +1 -1
  48. package/src/components/ZoomBrush.tsx +2 -1
  49. package/src/data/initial-state.js +46 -2
  50. package/src/helpers/computeMarginBottom.ts +2 -1
  51. package/src/helpers/tests/computeMarginBottom.test.ts +2 -1
  52. package/src/hooks/useBarChart.js +5 -2
  53. package/src/hooks/useScales.ts +15 -18
  54. package/src/hooks/useTooltip.tsx +9 -8
  55. package/src/scss/main.scss +8 -29
  56. package/src/types/ChartConfig.ts +32 -14
  57. 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' && config.xAxis.sortDates
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 && <h2>{parse(legend.label)}</h2>}
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
- onKeyPress={e => {
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
- onKeyPress={e => {
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 === 'Line' && (
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 style={{}}> {pd.label}</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
@@ -22,6 +22,7 @@ export interface PreliminaryDataItem {
22
22
  column: string
23
23
  value: string
24
24
  seriesKey: string
25
+ label: string
25
26
  }
26
27
 
27
28
  export interface DataItem {
@@ -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: 2,
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 ? parseInt(config.runtime.yAxis.size) : 66}>
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' && config.xAxis.sortDates
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' && config.xAxis.sortDates
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.type === 'date' ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
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.type === 'date') return formatDate(parseDate(tick))
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.type === 'date' && config.visualizationType !== 'Forest Plot') return formatDate(tick)
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 + Number(config.xAxis.axisPadding)}
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.type === 'date' ? formatDate : formatNumber} scale={g1xScale} stroke='#333' tickStroke='#333' numTicks={runtime.xAxis.numTicks || undefined}>
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.type === 'date' ? formatDate : runtime.xAxis.dataKey !== 'Year' ? formatNumber : tick => tick}
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.type === 'date' ? xScale(parseDate(anchor.value, false)) : xScale(anchor.value)
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 chroma from 'chroma-js'
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
- let labelColor = '#000000'
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]) && chroma.contrast(textColor, _colorScale(arc.data[config.runtime.xAxis.dataKey])) < 3.5) {
160
- textColor = '000'
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 (