@cdc/chart 1.3.3 → 4.22.10

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 (49) hide show
  1. package/dist/cdcchart.js +6 -6
  2. package/examples/cutoff-example-config.json +2 -0
  3. package/examples/cutoff-example-data.json +1 -1
  4. package/examples/dynamic-legends.json +125 -0
  5. package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart-with-numbers-on-bar.json +198 -0
  6. package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart.json +241 -0
  7. package/examples/gallery/bar-chart-horizontal/horizontal-stacked.json +248 -0
  8. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +137 -0
  9. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +80 -0
  10. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +81 -0
  11. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-with-confidence.json +68 -0
  12. package/examples/gallery/bar-chart-vertical/vertical-bar-chart.json +111 -0
  13. package/examples/gallery/lollipop/lollipop-style-horizontal.json +220 -0
  14. package/examples/gallery/paired-bar/paired-bar-chart.json +196 -0
  15. package/examples/horizontal-chart.json +3 -0
  16. package/examples/paired-bar-data.json +1 -1
  17. package/examples/paired-bar-example.json +2 -0
  18. package/examples/planet-combo-example-config.json +2 -0
  19. package/examples/planet-example-config.json +2 -2
  20. package/examples/planet-example-data.json +1 -1
  21. package/examples/planet-pie-example-config.json +2 -0
  22. package/examples/private/line-test-data.json +22 -0
  23. package/examples/private/line-test-two.json +216 -0
  24. package/examples/private/line-test.json +102 -0
  25. package/examples/private/shawn.json +1296 -0
  26. package/examples/private/yaxis-test.json +132 -0
  27. package/examples/private/yaxis-testing.csv +27 -0
  28. package/examples/private/yaxis.json +28 -0
  29. package/examples/stacked-vertical-bar-example.json +228 -0
  30. package/package.json +3 -3
  31. package/src/CdcChart.tsx +121 -168
  32. package/src/components/BarChart.tsx +92 -40
  33. package/src/components/DataTable.tsx +28 -13
  34. package/src/components/EditorPanel.js +286 -182
  35. package/src/components/Legend.js +334 -0
  36. package/src/components/LineChart.tsx +57 -17
  37. package/src/components/LinearChart.tsx +171 -77
  38. package/src/components/PairedBarChart.tsx +139 -42
  39. package/src/components/PieChart.tsx +33 -6
  40. package/src/components/SparkLine.js +28 -27
  41. package/src/components/useIntersectionObserver.tsx +30 -0
  42. package/src/data/initial-state.js +23 -7
  43. package/src/hooks/useChartClasses.js +35 -0
  44. package/src/hooks/useLegendClasses.js +20 -0
  45. package/src/hooks/useReduceData.ts +72 -24
  46. package/src/index.html +29 -30
  47. package/src/scss/editor-panel.scss +34 -4
  48. package/src/scss/main.scss +201 -5
  49. package/src/components/BarStackVertical.js +0 -0
@@ -13,8 +13,8 @@ import { BarStackHorizontal } from '@visx/shape';
13
13
 
14
14
 
15
15
 
16
- export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getXAxisData, getYAxisData }) {
17
- const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, updateConfig, setParentConfig, colorPalettes,formatDate,parseDate } = useContext<any>(Context);
16
+ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getXAxisData, getYAxisData, animatedChart, visible }) {
17
+ const { transformedData: data, colorScale, seriesHighlight, config, formatNumber, updateConfig, setParentConfig, colorPalettes, formatDate, parseDate } = useContext<any>(Context);
18
18
  const { orientation, visualizationSubType } = config;
19
19
  const isHorizontal = orientation === 'horizontal';
20
20
 
@@ -33,11 +33,12 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
33
33
  const tipRounding = config.tipRounding ;
34
34
  const radius = config.roundingStyle ==='standard' ? '8px' : config.roundingStyle ==='shallow' ? '5px': config.roundingStyle ==='finger' ? '15px':'0px';
35
35
  const stackCount = config.runtime.seriesKeys.length;
36
-
36
+ const barBorderWidth = 1;
37
+
37
38
  const applyRadius = (index:number)=>{
38
39
  if(index === undefined || index === null || !isRounded) return;
39
40
  let style = {};
40
-
41
+
41
42
  if((isStacked && index+1 === stackCount) || !isStacked){
42
43
  style = isHorizontal ? {borderRadius:`0 ${radius} ${radius} 0`} : {borderRadius:`${radius} ${radius} 0 0`};
43
44
  };
@@ -45,9 +46,9 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
45
46
  style = isHorizontal ? {borderRadius:`${radius} 0 0 ${radius}`} : {borderRadius:`0 0 ${radius} ${radius}`};
46
47
  };
47
48
  if(tipRounding === 'full' && ((isStacked && index === 0 && stackCount === 1) || !isStacked)){
48
- style = {borderRadius:radius};
49
+ style = {borderRadius:radius};
49
50
  };
50
-
51
+
51
52
  return style;
52
53
  }
53
54
 
@@ -81,6 +82,15 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
81
82
  })
82
83
  }
83
84
  }, []);
85
+
86
+ useEffect(()=>{
87
+ if(config.barStyle==='lollipop' && !config.isLollipopChart ){
88
+ updateConfig({ ...config, isLollipopChart:true })
89
+ }
90
+ if( isRounded || config.barStyle==='flat' ){
91
+ updateConfig({ ...config, isLollipopChart:false })
92
+ }
93
+ },[config.barStyle])
84
94
 
85
95
  // config.runtime.seriesKeys.sort().reverse();
86
96
 
@@ -113,8 +123,20 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
113
123
  let barThickness = xMax / barStack.bars.length;
114
124
  let barThicknessAdjusted = barThickness * (config.barThickness || 0.8);
115
125
  let offset = barThickness * (1 - (config.barThickness || 0.8)) / 2;
126
+ const style = applyRadius(barStack.index)
127
+
116
128
  return (
117
- <Group key={`bar-stack-${barStack.index}-${bar.index}`}>
129
+ <>
130
+ <style>
131
+ {`
132
+ #barStack${barStack.index}-${bar.index} rect,
133
+ #barStack${barStack.index}-${bar.index} foreignObject{
134
+ animation-delay: ${barStack.index}.2s;
135
+ transform-origin: ${barThicknessAdjusted/2}px ${bar.y + bar.height}px
136
+ }
137
+ `}
138
+ </style>
139
+ <Group key={`bar-stack-${barStack.index}-${bar.index}`} id={`barStack${barStack.index}-${bar.index}`} className='stack vertical'>
118
140
  <Text
119
141
  display={config.labels && displayBar ? 'block' : 'none'}
120
142
  opacity={transparentBar ? 0.5 : 1}
@@ -124,21 +146,20 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
124
146
  textAnchor="middle">
125
147
  {formatNumber(bar.bar ? bar.bar.data[bar.key] : 0)}
126
148
  </Text>
127
- <rect
128
- key={`bar-stack-${barStack.index}-${bar.index}`}
129
- x={barThickness * bar.index + offset}
130
- y={bar.y}
131
- height={bar.height}
132
- width={barThicknessAdjusted}
133
- fill={bar.color}
134
- stroke="#333"
135
- strokeWidth={config.barBorderThickness || 1}
136
- opacity={transparentBar ? 0.5 : 1}
137
- display={displayBar ? 'block' : 'none'}
138
- data-tip={tooltip}
139
- data-for={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
140
- />
149
+ <foreignObject
150
+ key={`bar-stack-${barStack.index}-${bar.index}`}
151
+ x={barThickness * bar.index + offset}
152
+ y={bar.y}
153
+ width={barThicknessAdjusted}
154
+ height={bar.height}
155
+ style={{background:bar.color,border:`${config.barHasBorder==='true' ? barBorderWidth: 0 }px solid #333`,...style}}
156
+ opacity={transparentBar ? 0.5 : 1}
157
+ display={displayBar ? 'block' : 'none'}
158
+ data-tip={tooltip}
159
+ data-for={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
160
+ > </foreignObject>
141
161
  </Group>
162
+ </>
142
163
  )}
143
164
  ))}
144
165
  </BarStack>
@@ -163,6 +184,8 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
163
184
  const xAxisValue = config.runtime.yAxis.type==='date' ? formatDate(parseDate(data[bar.index][config.runtime.originalXAxis.dataKey])) : data[bar.index][config.runtime.originalXAxis.dataKey]
164
185
  let yAxisTooltip = config.yAxis.label ? `${config.yAxis.label}: ${formatNumber(data[bar.index][bar.key])}` : `${bar.key}: ${formatNumber(data[bar.index][bar.key])}`
165
186
  let xAxisTooltip = config.xAxis.label ? `${config.xAxis.label}: ${xAxisValue}` : xAxisValue
187
+ // let yAxisTooltip = config.yAxis.label ? `${config.yAxis.label}: ${data[bar.index][bar.key]}` : `${bar.key}: ${data[bar.index][bar.key]}`
188
+ // let xAxisTooltip = config.xAxis.label ? `${config.xAxis.label}: ${data[bar.index][config.runtime.originalXAxis.dataKey]}` :`${data[bar.index].name}`
166
189
 
167
190
  const tooltip = `<div>
168
191
  ${yAxisTooltip}<br />
@@ -175,7 +198,8 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
175
198
  let barPadding = barHeight;
176
199
 
177
200
  config.barHeight = Number(config.barHeight)
178
-
201
+ const style = applyRadius(barStack.index);
202
+
179
203
  if (orientation=== "horizontal") {
180
204
 
181
205
  if(isLabelBelowBar || isLabelMissing || isLabelOnYAxis) {
@@ -201,20 +225,19 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
201
225
 
202
226
  return (
203
227
  <Group key={index}>
204
- <rect
228
+ <foreignObject
205
229
  key={`barstack-horizontal-${barStack.index}-${bar.index}-${index}`}
230
+ className={`animated-chart group ${animatedChart ? 'animated' : ''}`}
206
231
  x={bar.x}
207
232
  y={ bar.y - config.barPadding/2 - config.barHeight/2 }
208
233
  width={bar.width}
209
234
  height={config.barHeight}
210
- fill={bar.color}
211
- stroke="#333"
212
- strokeWidth={config.barBorderThickness || 1}
235
+ style={{background:bar.color,border:`${config.barHasBorder==='true' ? barBorderWidth: 0 }px solid #333`,...style}}
213
236
  opacity={transparentBar ? 0.5 : 1}
214
237
  display={displayBar ? 'block' : 'none'}
215
238
  data-tip={tooltip}
216
239
  data-for={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
217
- />
240
+ ></foreignObject>
218
241
 
219
242
  {(orientation === 'horizontal' && visualizationSubType === 'stacked') && isLabelBelowBar && barStack.index === 0 && !config.yAxis.hideLabel &&
220
243
  <Text
@@ -272,11 +295,12 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
272
295
  color={() => {return '';}}
273
296
  >
274
297
  {(barGroups) => {
275
-
298
+ let barType = 'vertical';
276
299
  if (orientation=== "horizontal") {
277
300
  const barsPerGroup = config.series.length;
278
301
  let barHeight = config.barHeight ? config.barHeight : 25;
279
302
  let barPadding = barHeight;
303
+ barType = 'horizontal';
280
304
 
281
305
  if(isLabelBelowBar || isLabelMissing || isLabelOnYAxis) {
282
306
  if(barHeight < 40) {
@@ -297,7 +321,7 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
297
321
 
298
322
  return barGroups.map((barGroup, index) => (
299
323
  <Group
300
- className={`bar-group-${barGroup.index}-${barGroup.x0}--${index}`}
324
+ className={`bar-group-${barGroup.index}-${barGroup.x0}--${index} ${barType}`}
301
325
  key={`bar-group-${barGroup.index}-${barGroup.x0}--${index}`}
302
326
  top={config.runtime.horizontal ? yMax / barGroups.length * barGroup.index : 0}
303
327
  left={config.runtime.horizontal ? 0 : xMax / barGroups.length * barGroup.index}>
@@ -314,10 +338,20 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
314
338
  if(config.isLollipopChart) {
315
339
  offset = ( (config.runtime.horizontal ? yMax : xMax) / barGroups.length / 2) - lollipopBarWidth / 2
316
340
  }
341
+
342
+ const set = new Set()
343
+ data.forEach(d=>set.add(d[config.legend.colorCode]));
344
+ const uniqValues = Array.from(set);
345
+
346
+ let palette = colorPalettes[config.palette].slice(0,uniqValues.length);
317
347
 
318
348
  let barWidth = config.isLollipopChart ? lollipopBarWidth : barGroupWidth / barGroup.bars.length;
319
349
  let barColor = config.runtime.seriesLabels && config.runtime.seriesLabels[bar.key] ? colorScale(config.runtime.seriesLabels[bar.key]) : colorScale(bar.key);
320
-
350
+ while( palette.length < barGroups.length ){
351
+ palette =palette.concat(palette)
352
+ }
353
+ if( config.legend.colorCode && config.series.length===1) barColor = palette[barGroup.index];
354
+
321
355
  let yAxisValue = formatNumber(bar.value);
322
356
  let xAxisValue = config.runtime[section].type==='date' ? formatDate(parseDate(data[barGroup.index][config.runtime.originalXAxis.dataKey])) : data[barGroup.index][config.runtime.originalXAxis.dataKey]
323
357
 
@@ -351,7 +385,19 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
351
385
  ${yAxisTooltip}<br />
352
386
  ${xAxisTooltip}<br />
353
387
  ${config.seriesLabel ? `${config.seriesLabel}: ${bar.key}` : ''}`
388
+ const style = applyRadius(index)
389
+
354
390
  return (
391
+ <>
392
+ {/* This feels gross but inline transition was not working well*/}
393
+ <style>
394
+ {`
395
+ .linear #barGroup${barGroup.index},
396
+ .Combo #barGroup${barGroup.index} {
397
+ transform-origin: 0 ${barY + barHeight}px;
398
+ }
399
+ `}
400
+ </style>
355
401
  <Group key={`bar-sub-group-${barGroup.index}-${barGroup.x0}-${barY}--${index}`}>
356
402
  <Text
357
403
  display={config.labels && displayBar ? 'block' : 'none'}
@@ -362,23 +408,26 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
362
408
  textAnchor="middle">
363
409
  {formatNumber(bar.value)}
364
410
  </Text>
365
- <rect
411
+ <foreignObject
412
+ id={`barGroup${barGroup.index}`}
366
413
  key={`bar-group-bar-${barGroup.index}-${bar.index}-${bar.value}-${bar.key}`}
367
414
  x={ config.runtime.horizontal ? 0 : barWidth * (barGroup.bars.length - bar.index - 1) + offset }
368
415
  y={config.runtime.horizontal ? barWidth * (barGroup.bars.length - bar.index - 1) + (config.isLollipopChart && isLabelOnYAxis ? offset : 0) : barY }
369
416
  width={config.runtime.horizontal ? bar.y : barWidth}
370
417
  height={config.runtime.horizontal ? barWidth : barHeight}
371
- fill={config.isLollipopChart && config.lollipopColorStyle === 'regular' ? barColor :
372
- config.isLollipopChart && config.lollipopColorStyle === 'two-tone' ? chroma(barColor).brighten(1) : barColor }
373
- stroke="#333"
374
- strokeWidth={config.isLollipopChart ? 0 : config.barBorderThickness || 1}
418
+ style={{
419
+ background:config.isLollipopChart && config.lollipopColorStyle === 'regular' ? barColor :
420
+ config.isLollipopChart && config.lollipopColorStyle === 'two-tone' ? chroma(barColor).brighten(1) : barColor ,
421
+ border:`${config.isLollipopChart ? 0 :config.barHasBorder==='true' ? barBorderWidth: 0 }px solid #333`,
422
+ ...style
423
+ }}
375
424
  opacity={transparentBar ? 0.5 : 1}
376
425
  display={displayBar ? 'block' : 'none'}
377
426
  data-tip={tooltip}
378
427
  data-for={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
379
- />
428
+ ></foreignObject>
380
429
  {config.isLollipopChart && config.lollipopShape === 'circle' &&
381
- <circle
430
+ <circle
382
431
  cx={orientation === 'horizontal' ? bar.y : barWidth * (barGroup.bars.length - bar.index - 1) + (isLabelBelowBar && orientation === 'horizontal' ? 0 : offset) + lollipopShapeSize/3.5}
383
432
  cy={orientation === 'horizontal' ? lollipopShapeSize/3.5 + (isLabelBelowBar && orientation === 'horizontal' ? 0: offset) : bar.y}
384
433
  r={lollipopShapeSize/2}
@@ -386,11 +435,11 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
386
435
  key={`circle--${bar.index}`}
387
436
  data-tip={tooltip}
388
437
  data-for={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
389
- style={{ 'opacity': 1, filter: 'unset' }}
438
+ style={{ filter: 'unset', opacity: 1 }}
390
439
  />
391
440
  }
392
441
  {config.isLollipopChart && config.lollipopShape === 'square' &&
393
- <rect
442
+ <rect
394
443
  x={
395
444
  (orientation === 'horizontal' && bar.y > 10) ? bar.y - lollipopShapeSize / 2 : (orientation === 'horizontal' && bar.y < 10) ? 0 :
396
445
  (orientation !== 'horizontal') ? offset - lollipopBarWidth / 2 : barWidth * (barGroup.bars.length - bar.index - 1) + offset - 5.25
@@ -404,7 +453,9 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
404
453
  data-tip={tooltip}
405
454
  data-for={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
406
455
  style={{ 'opacity': 1, filter: 'unset' }}
407
- />
456
+ >
457
+ <animate attributeName="height" values={`0, ${lollipopShapeSize}`} dur="2.5s"/>
458
+ </rect>
408
459
  }
409
460
  {orientation === "horizontal" && textWidth + 100 < bar.y ?
410
461
  config.yAxis.labelPlacement === "On Bar" &&
@@ -541,6 +592,7 @@ export default function BarChart({ xScale, yScale, seriesScale, xMax, yMax, getX
541
592
  </>
542
593
  }
543
594
  </Group>
595
+ </>
544
596
  )}
545
597
  )}
546
598
  </Group>
@@ -20,7 +20,7 @@ import LegendCircle from '@cdc/core/components/LegendCircle';
20
20
  import Context from '../context';
21
21
 
22
22
  export default function DataTable() {
23
- const { rawData, transformedData: data, config, colorScale, parseDate, formatDate, formatNumber:numberFormatter } = useContext<any>(Context);
23
+ const { rawData, transformedData: data, config, colorScale, parseDate, formatDate, formatNumber:numberFormatter, colorPalettes } = useContext<any>(Context);
24
24
 
25
25
  const legendGlyphSize = 15;
26
26
  const legendGlyphSizeHalf = legendGlyphSize / 2;
@@ -63,7 +63,15 @@ export default function DataTable() {
63
63
  const seriesLabel = config.runtime.seriesLabels ? config.runtime.seriesLabels[row.original] : row.original;
64
64
  return (
65
65
  <Fragment>
66
- {config.visualizationType !== 'Pie' && <LegendCircle fill={colorScale(seriesLabel)} />}
66
+ {config.visualizationType !== 'Pie' &&
67
+ <LegendCircle
68
+ fill={
69
+ // non dynamic leged
70
+ !config.legend.dynamicLegend ? colorScale(seriesLabel)
71
+ // dynamic legend
72
+ : config.legend.dynamicLegend ? colorPalettes[config.palette][row.index]
73
+ // fallback
74
+ : '#000'} />}
67
75
  <span>{seriesLabel}</span>
68
76
  </Fragment>
69
77
  )
@@ -141,13 +149,17 @@ export default function DataTable() {
141
149
  >
142
150
  {config.table.label}
143
151
  </div>
144
- <div className="table-container">
152
+ <div
153
+ className="table-container"
154
+ hidden={!tableExpanded}
155
+ style={ { maxHeight: config.table.limitHeight && `${config.table.height}px`, overflowY: 'scroll' } }
156
+ >
145
157
  <table
146
158
  className={tableExpanded ? 'data-table' : 'data-table cdcdataviz-sr-only'}
147
- hidden={!tableExpanded}
148
159
  {...getTableProps()}
149
160
  aria-rowcount={ config?.series?.length ? config?.series?.length : '-1' }
150
161
  >
162
+ <caption className='cdcdataviz-sr-only'>{config.table.caption ? config.table.caption : "" }</caption>
151
163
  <caption className="visually-hidden">{config.table.label}</caption>
152
164
  <thead>
153
165
  {headerGroups.map((headerGroup,index) => (
@@ -182,15 +194,18 @@ export default function DataTable() {
182
194
  prepareRow(row);
183
195
  return (
184
196
  <tr {...row.getRowProps()} key={`tbody__tr-${index}`}>
185
- {row.cells.map((cell, index) => (
186
- <td
187
- tabIndex="0"
188
- {...cell.getCellProps()}
189
- key={`tbody__tr__td-${index}`}
190
- role="gridcell">
191
- {cell.render('Cell')}
192
- </td>
193
- ))}
197
+ {row.cells.map((cell, index) => {
198
+ return (
199
+ <td
200
+ tabIndex="0"
201
+ {...cell.getCellProps()}
202
+ key={`tbody__tr__td-${index}`}
203
+ role="gridcell">
204
+ { cell.render('Cell') }
205
+ </td>
206
+ )
207
+ }
208
+ )}
194
209
  </tr>
195
210
  );
196
211
  })}