@cdc/chart 4.24.9 → 4.24.11

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 (95) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcchart.js +45911 -41739
  3. package/examples/feature/boxplot/boxplot-data.json +88 -22
  4. package/examples/feature/boxplot/boxplot.json +540 -16
  5. package/examples/feature/boxplot/testing.csv +7 -7
  6. package/examples/feature/sankey/sankey-example-data.json +0 -1
  7. package/examples/private/test.json +20092 -0
  8. package/index.html +4 -4
  9. package/package.json +2 -2
  10. package/src/CdcChart.tsx +209 -188
  11. package/src/_stories/Chart.CustomColors.stories.tsx +19 -0
  12. package/src/_stories/Chart.DynamicSeries.stories.tsx +27 -0
  13. package/src/_stories/Chart.Legend.Gradient.stories.tsx +74 -0
  14. package/src/_stories/Chart.stories.tsx +30 -3
  15. package/src/_stories/ChartAxisLabels.stories.tsx +20 -0
  16. package/src/_stories/ChartAxisTitles.stories.tsx +53 -0
  17. package/src/_stories/ChartEditor.stories.tsx +27 -0
  18. package/src/_stories/ChartLine.Suppression.stories.tsx +25 -0
  19. package/src/_stories/ChartPrefixSuffix.stories.tsx +159 -0
  20. package/src/_stories/_mock/boxplot_multiseries.json +647 -0
  21. package/src/_stories/_mock/dynamic_series_bar_config.json +723 -0
  22. package/src/_stories/_mock/dynamic_series_config.json +979 -0
  23. package/src/_stories/_mock/horizontal_bar.json +257 -0
  24. package/src/_stories/_mock/large_x_axis_labels.json +261 -0
  25. package/src/_stories/_mock/paired-bar.json +262 -0
  26. package/src/_stories/_mock/pie_with_data.json +255 -0
  27. package/{examples/feature/scatterplot/scatterplot.json → src/_stories/_mock/scatterplot_mock.json} +62 -92
  28. package/src/_stories/_mock/simplified_line.json +1510 -0
  29. package/src/_stories/_mock/suppression_mock.json +1549 -0
  30. package/src/components/Annotations/components/AnnotationDraggable.tsx +0 -3
  31. package/src/components/Annotations/components/AnnotationDropdown.tsx +1 -1
  32. package/src/components/Axis/Categorical.Axis.tsx +22 -4
  33. package/src/components/BarChart/components/BarChart.Horizontal.tsx +95 -16
  34. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +41 -17
  35. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +43 -9
  36. package/src/components/BarChart/components/BarChart.Vertical.tsx +123 -47
  37. package/src/components/BarChart/helpers/index.ts +23 -5
  38. package/src/components/BoxPlot/BoxPlot.tsx +189 -0
  39. package/src/components/BrushChart.tsx +3 -2
  40. package/src/components/DeviationBar.jsx +58 -8
  41. package/src/components/EditorPanel/EditorPanel.tsx +127 -102
  42. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +11 -28
  43. package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +51 -6
  44. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +21 -4
  45. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +40 -9
  46. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
  47. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +121 -56
  48. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +296 -35
  49. package/src/components/EditorPanel/components/panels.scss +4 -6
  50. package/src/components/EditorPanel/editor-panel.scss +0 -8
  51. package/src/components/EditorPanel/helpers/tests/updateFieldRankByValue.test.ts +38 -0
  52. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +42 -0
  53. package/src/components/EditorPanel/useEditorPermissions.ts +16 -1
  54. package/src/components/ForestPlot/ForestPlot.tsx +2 -3
  55. package/src/components/ForestPlot/ForestPlotProps.ts +2 -0
  56. package/src/components/Legend/Legend.Component.tsx +23 -24
  57. package/src/components/Legend/Legend.Suppression.tsx +25 -20
  58. package/src/components/Legend/Legend.tsx +16 -18
  59. package/src/components/Legend/helpers/index.ts +16 -19
  60. package/src/components/LegendWrapper.tsx +3 -1
  61. package/src/components/LineChart/components/LineChart.Circle.tsx +10 -0
  62. package/src/components/LineChart/helpers.ts +48 -43
  63. package/src/components/LineChart/index.tsx +88 -82
  64. package/src/components/LinearChart.tsx +747 -562
  65. package/src/components/PairedBarChart.jsx +50 -10
  66. package/src/components/PieChart/PieChart.tsx +1 -6
  67. package/src/components/Regions/components/Regions.tsx +33 -19
  68. package/src/components/Sankey/index.tsx +50 -32
  69. package/src/components/Sankey/sankey.scss +6 -5
  70. package/src/components/Sankey/useSankeyAlert.tsx +60 -0
  71. package/src/components/ScatterPlot/ScatterPlot.jsx +20 -4
  72. package/src/components/ZoomBrush.tsx +25 -6
  73. package/src/coreStyles_chart.scss +3 -0
  74. package/src/data/initial-state.js +8 -10
  75. package/src/helpers/configHelpers.ts +28 -0
  76. package/src/helpers/handleRankByValue.ts +15 -0
  77. package/src/helpers/sizeHelpers.ts +25 -0
  78. package/src/helpers/tests/handleRankByValue.test.ts +37 -0
  79. package/src/helpers/tests/sizeHelpers.test.ts +80 -0
  80. package/src/hooks/useColorPalette.js +10 -2
  81. package/src/hooks/useLegendClasses.ts +13 -22
  82. package/src/hooks/useMinMax.ts +27 -13
  83. package/src/hooks/useReduceData.ts +43 -10
  84. package/src/hooks/useScales.ts +87 -38
  85. package/src/hooks/useTooltip.tsx +62 -53
  86. package/src/index.jsx +1 -0
  87. package/src/scss/DataTable.scss +5 -4
  88. package/src/scss/main.scss +57 -70
  89. package/src/types/ChartConfig.ts +43 -34
  90. package/src/types/ChartContext.ts +22 -15
  91. package/src/types/ForestPlot.ts +8 -0
  92. package/src/_stories/Chart.Legend.Gradient.tsx +0 -19
  93. package/src/_stories/ChartBrush.stories.tsx +0 -19
  94. package/src/components/BoxPlot/BoxPlot.jsx +0 -111
  95. package/src/components/LinearChart.jsx +0 -817
@@ -17,6 +17,7 @@ import useRightAxis from '../../hooks/useRightAxis'
17
17
  import { filterCircles, createStyles, createDataSegments } from './helpers'
18
18
  import LineChartCircle from './components/LineChart.Circle'
19
19
  import LineChartBumpCircle from './components/LineChart.BumpCircle'
20
+ import isNumber from '@cdc/core/helpers/isNumber'
20
21
 
21
22
  // Types
22
23
  import { type ChartContext } from '../../types/ChartContext'
@@ -38,7 +39,7 @@ const LineChart = (props: LineChartProps) => {
38
39
  } = props
39
40
 
40
41
  // prettier-ignore
41
- const { colorScale, config, formatNumber, handleLineType, isNumber, parseDate, seriesHighlight, tableData, transformedData, updateConfig, brushConfig,clean } = useContext<ChartContext>(ConfigContext)
42
+ const { colorScale, config, formatNumber, handleLineType, parseDate, seriesHighlight, tableData, transformedData, updateConfig, brushConfig,clean } = useContext<ChartContext>(ConfigContext)
42
43
  const { yScaleRight } = useRightAxis({ config, yMax, data: transformedData, updateConfig })
43
44
  if (!handleTooltipMouseOver) return
44
45
 
@@ -51,53 +52,51 @@ const LineChart = (props: LineChartProps) => {
51
52
  data = clean(brushConfig.data)
52
53
  tableD = clean(brushConfig.data)
53
54
  }
55
+
56
+ const xPos = d => {
57
+ return xScale(getXAxisData(d)) + (xScale.bandwidth ? xScale.bandwidth() / 2 : 0)
58
+ }
59
+
54
60
  return (
55
61
  <ErrorBoundary component='LineChart'>
56
62
  <Group left={Number(config.runtime.yAxis.size)}>
57
63
  {' '}
58
64
  {/* left - expects a number not a string */}
59
65
  {(config.runtime.lineSeriesKeys || config.runtime.seriesKeys).map((seriesKey, index) => {
60
- let lineType = config.runtime.series.filter(item => item.dataKey === seriesKey)[0].type
61
- const seriesData = config.runtime.series.filter(item => item.dataKey === seriesKey)
62
- const seriesAxis = seriesData[0].axis ? seriesData[0].axis : 'left'
63
- let displayArea =
66
+ const seriesData = config.runtime.series.find(item => item.dataKey === seriesKey)
67
+ const lineType = seriesData.type
68
+ const seriesAxis = seriesData.axis || 'left'
69
+ const displayArea =
64
70
  legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1
65
- const circleData = filterCircles(config?.preliminaryData, tableD, seriesKey)
66
- // styles for preliminary Data items
67
- let styles = createStyles({
68
- preliminaryData: config.preliminaryData,
69
- data: tableD,
70
- stroke: colorScale(config.runtime.seriesLabels[seriesKey]),
71
- strokeWidth: seriesData[0].weight || 2,
72
- handleLineType,
73
- lineType,
74
- seriesKey
75
- })
71
+
76
72
  const suppressedSegments = createDataSegments(
77
73
  tableData,
78
74
  seriesKey,
79
75
  config.preliminaryData,
80
76
  config.xAxis.dataKey
81
77
  )
82
- const splittedData = config?.preliminaryData?.filter(pd => pd.style && !pd.style.includes('Circles'))
83
- let xPos = d => {
84
- return xScale(getXAxisData(d)) + (xScale.bandwidth ? xScale.bandwidth() / 2 : 0)
85
- }
78
+ const isSplitLine =
79
+ config?.preliminaryData?.filter(pd => pd.style && !pd.style.includes('Circles')).length > 0
86
80
 
81
+ const _data = seriesData.dynamicCategory
82
+ ? data.filter(d => d[seriesData.dynamicCategory] === seriesKey)
83
+ : data
84
+ const _seriesKey = seriesData.dynamicCategory ? seriesData.originalDataKey : seriesKey
85
+ const circleData = filterCircles(config?.preliminaryData, tableD, _seriesKey)
87
86
  return (
88
87
  <Group
89
- key={`series-${seriesKey}`}
88
+ key={`series-${seriesKey}-${index}`}
90
89
  opacity={
91
90
  legend.behavior === 'highlight' &&
92
91
  seriesHighlight.length > 0 &&
93
- seriesHighlight.indexOf(seriesKey) === -1
92
+ seriesHighlight.indexOf(_seriesKey) === -1
94
93
  ? 0.5
95
94
  : 1
96
95
  }
97
96
  display={
98
97
  legend.behavior === 'highlight' ||
99
98
  (seriesHighlight.length === 0 && !legend.dynamicLegend) ||
100
- seriesHighlight.indexOf(seriesKey) !== -1
99
+ seriesHighlight.indexOf(_seriesKey) !== -1
101
100
  ? 'block'
102
101
  : 'none'
103
102
  }
@@ -113,12 +112,9 @@ const LineChart = (props: LineChartProps) => {
113
112
  onMouseOut={handleTooltipMouseOff}
114
113
  onClick={e => handleTooltipClick(e, data)}
115
114
  />
116
- {data.map((d, dataIndex) => {
115
+ {_data.map((d, dataIndex) => {
117
116
  return (
118
- d[seriesKey] !== undefined &&
119
- d[seriesKey] !== '' &&
120
- d[seriesKey] !== null &&
121
- isNumber(d[seriesKey]) && (
117
+ isNumber(d[_seriesKey]) && (
122
118
  <React.Fragment key={`series-${seriesKey}-point-${dataIndex}`}>
123
119
  {/* Render label */}
124
120
  {config.labels && (
@@ -126,13 +122,13 @@ const LineChart = (props: LineChartProps) => {
126
122
  x={xPos(d)}
127
123
  y={
128
124
  seriesAxis === 'Right'
129
- ? yScaleRight(getYAxisData(d, seriesKey))
130
- : yScale(getYAxisData(d, seriesKey))
125
+ ? yScaleRight(getYAxisData(d, _seriesKey))
126
+ : yScale(getYAxisData(d, _seriesKey))
131
127
  }
132
128
  fill={'#000'}
133
129
  textAnchor='middle'
134
130
  >
135
- {formatNumber(d[seriesKey], 'left')}
131
+ {formatNumber(d[_seriesKey], 'left')}
136
132
  </Text>
137
133
  )}
138
134
 
@@ -142,10 +138,10 @@ const LineChart = (props: LineChartProps) => {
142
138
  dataIndex={dataIndex}
143
139
  circleData={circleData}
144
140
  tableData={tableData}
145
- data={data}
141
+ data={_data}
146
142
  d={d}
147
143
  config={config}
148
- seriesKey={seriesKey}
144
+ seriesKey={_seriesKey}
149
145
  displayArea={displayArea}
150
146
  tooltipData={tooltipData}
151
147
  xScale={xScale}
@@ -163,10 +159,10 @@ const LineChart = (props: LineChartProps) => {
163
159
  dataIndex={dataIndex}
164
160
  tableData={tableData}
165
161
  circleData={circleData}
166
- data={data}
162
+ data={_data}
167
163
  d={d}
168
164
  config={config}
169
- seriesKey={seriesKey}
165
+ seriesKey={_seriesKey}
170
166
  displayArea={displayArea}
171
167
  tooltipData={tooltipData}
172
168
  xScale={xScale}
@@ -188,7 +184,7 @@ const LineChart = (props: LineChartProps) => {
188
184
  dataIndex={0}
189
185
  mode='HOVER_POINTS'
190
186
  circleData={circleData}
191
- data={data}
187
+ data={_data}
192
188
  config={config}
193
189
  seriesKey={seriesKey}
194
190
  displayArea={displayArea}
@@ -203,78 +199,88 @@ const LineChart = (props: LineChartProps) => {
203
199
  )}
204
200
  </>
205
201
  {/* SPLIT LINE */}
206
- {splittedData.length > 0 ? (
202
+ {isSplitLine ? (
207
203
  <>
208
204
  <SplitLinePath
209
- curve={allCurves[seriesData[0].lineType]}
210
- segments={data.map(d => [d])}
205
+ curve={allCurves[seriesData.lineType]}
206
+ segments={_data.map(d => [d])}
211
207
  segmentation='x'
212
208
  x={d => xPos(d)}
213
209
  y={d =>
214
210
  seriesAxis === 'Right'
215
- ? yScaleRight(getYAxisData(d, seriesKey))
216
- : yScale(Number(getYAxisData(d, seriesKey)))
211
+ ? yScaleRight(getYAxisData(d, _seriesKey))
212
+ : yScale(Number(getYAxisData(d, _seriesKey)))
217
213
  }
218
- styles={styles}
214
+ styles={createStyles({
215
+ preliminaryData: config.preliminaryData,
216
+ data: tableD,
217
+ stroke: colorScale(config.runtime.seriesLabels[seriesKey]),
218
+ strokeWidth: seriesData.weight || 2,
219
+ handleLineType,
220
+ lineType,
221
+ seriesKey
222
+ })}
219
223
  defined={(item, i) => {
220
224
  return item[seriesKey] !== '' && item[seriesKey] !== null && item[seriesKey] !== undefined
221
225
  }}
222
226
  />
223
227
 
224
228
  {suppressedSegments.map((segment, index) => {
225
- return (
226
- <LinePath
227
- key={index}
228
- data={segment.data}
229
- x={d => xPos(d)}
230
- y={d =>
231
- seriesAxis === 'Right'
232
- ? yScaleRight(getYAxisData(d, seriesKey))
233
- : yScale(Number(getYAxisData(d, seriesKey)))
234
- }
235
- stroke={colorScale(config.runtime.seriesLabels[seriesKey])}
236
- strokeWidth={seriesData[0].weight || 2}
237
- strokeOpacity={1}
238
- shapeRendering='geometricPrecision'
239
- strokeDasharray={handleLineType(segment.style)}
240
- defined={(item, i) => {
241
- return item[seriesKey] !== '' && item[seriesKey] !== null && item[seriesKey] !== undefined
242
- }}
243
- />
244
- )
229
+ return Object.entries(segment.data).map(([key, value]) => {
230
+ return (
231
+ <LinePath
232
+ key={index}
233
+ data={value}
234
+ x={d => xPos(d)}
235
+ y={d =>
236
+ seriesAxis === 'Right'
237
+ ? yScaleRight(getYAxisData(d, seriesKey))
238
+ : yScale(Number(getYAxisData(d, seriesKey)))
239
+ }
240
+ stroke={colorScale(config.runtime.seriesLabels[seriesKey])}
241
+ strokeWidth={seriesData[0]?.weight || 2}
242
+ strokeOpacity={1}
243
+ shapeRendering='geometricPrecision'
244
+ strokeDasharray={handleLineType(segment.style)}
245
+ defined={(item, i) => {
246
+ return item[seriesKey] !== '' && item[seriesKey] !== null && item[seriesKey] !== undefined
247
+ }}
248
+ />
249
+ )
250
+ })
245
251
  })}
246
252
  </>
247
253
  ) : (
248
254
  <>
249
255
  {/* STANDARD LINE */}
250
256
  <LinePath
251
- curve={allCurves[seriesData[0].lineType]}
257
+ curve={allCurves[seriesData.lineType]}
252
258
  data={
253
259
  config.visualizationType == 'Bump Chart'
254
- ? data
260
+ ? _data
255
261
  : config.xAxis.type === 'date-time' || config.xAxis.type === 'date'
256
- ? data.sort((d1, d2) => {
262
+ ? _data.sort((d1, d2) => {
257
263
  let x1 = getXAxisData(d1)
258
264
  let x2 = getXAxisData(d2)
259
265
  if (x1 < x2) return -1
260
266
  if (x2 < x1) return 1
261
267
  return 0
262
268
  })
263
- : data
269
+ : _data
264
270
  }
265
271
  x={d => xPos(d)}
266
272
  y={d =>
267
273
  seriesAxis === 'Right'
268
- ? yScaleRight(getYAxisData(d, seriesKey))
269
- : yScale(Number(getYAxisData(d, seriesKey)))
274
+ ? yScaleRight(getYAxisData(d, _seriesKey))
275
+ : yScale(Number(getYAxisData(d, _seriesKey)))
270
276
  }
271
277
  stroke={colorScale(config.runtime.seriesLabels[seriesKey])}
272
- strokeWidth={seriesData[0].weight || 2}
278
+ strokeWidth={seriesData.weight || 2}
273
279
  strokeOpacity={1}
274
280
  shapeRendering='geometricPrecision'
275
281
  strokeDasharray={lineType ? handleLineType(lineType) : 0}
276
282
  defined={(item, i) => {
277
- return item[seriesKey] !== '' && item[seriesKey] !== null && item[seriesKey] !== undefined
283
+ return item[_seriesKey] !== '' && item[_seriesKey] !== null && item[_seriesKey] !== undefined
278
284
  }}
279
285
  />
280
286
  </>
@@ -288,11 +294,11 @@ const LineChart = (props: LineChartProps) => {
288
294
  cx={xPos(item.data)}
289
295
  cy={
290
296
  seriesAxis === 'Right'
291
- ? yScaleRight(getYAxisData(item.data, seriesKey))
292
- : yScale(Number(getYAxisData(item.data, seriesKey)))
297
+ ? yScaleRight(getYAxisData(item.data, _seriesKey))
298
+ : yScale(Number(getYAxisData(item.data, _seriesKey)))
293
299
  }
294
300
  r={item.size}
295
- strokeWidth={seriesData[0].weight || 2}
301
+ strokeWidth={seriesData.weight || 2}
296
302
  stroke={colorScale ? colorScale(config.runtime.seriesLabels[seriesKey]) : '#000'}
297
303
  fill={
298
304
  item.isFilled
@@ -309,13 +315,13 @@ const LineChart = (props: LineChartProps) => {
309
315
  {config.animate && (
310
316
  <LinePath
311
317
  className='animation'
312
- curve={allCurves[seriesData[0].lineType]}
313
- data={data}
318
+ curve={allCurves[seriesData.lineType]}
319
+ data={_data}
314
320
  x={d => xPos(d)}
315
321
  y={d =>
316
322
  seriesAxis === 'Right'
317
- ? yScaleRight(getYAxisData(d, seriesKey))
318
- : yScale(Number(getYAxisData(d, seriesKey)))
323
+ ? yScaleRight(getYAxisData(d, _seriesKey))
324
+ : yScale(Number(getYAxisData(d, _seriesKey)))
319
325
  }
320
326
  stroke='#fff'
321
327
  strokeWidth={3}
@@ -331,9 +337,9 @@ const LineChart = (props: LineChartProps) => {
331
337
  {showLineSeriesLabels &&
332
338
  (config.runtime.lineSeriesKeys || config.runtime.seriesKeys).map(seriesKey => {
333
339
  let lastDatum
334
- for (let i = data.length - 1; i >= 0; i--) {
335
- if (data[i][seriesKey]) {
336
- lastDatum = data[i]
340
+ for (let i = _data.length - 1; i >= 0; i--) {
341
+ if (_data[i][seriesKey]) {
342
+ lastDatum = _data[i]
337
343
  break
338
344
  }
339
345
  }
@@ -341,7 +347,7 @@ const LineChart = (props: LineChartProps) => {
341
347
  return <></>
342
348
  }
343
349
  return (
344
- <text
350
+ <Text
345
351
  x={xPos(lastDatum) + 5}
346
352
  y={yScale(getYAxisData(lastDatum, seriesKey))}
347
353
  alignmentBaseline='middle'
@@ -352,7 +358,7 @@ const LineChart = (props: LineChartProps) => {
352
358
  }
353
359
  >
354
360
  {config.runtime.seriesLabels[seriesKey] || seriesKey}
355
- </text>
361
+ </Text>
356
362
  )
357
363
  })}
358
364
  </Group>