@cdc/chart 4.24.11 → 4.24.12-2

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 (65) hide show
  1. package/dist/cdcchart.js +32134 -32039
  2. package/examples/feature/sankey/sankey-example-data.json +126 -13
  3. package/examples/feature/tests-date-exclusions/date-exclusions-config.json +372 -12
  4. package/examples/private/DEV-8850-2.json +493 -0
  5. package/examples/private/DEV-9822.json +574 -0
  6. package/examples/private/DEV-9840.json +553 -0
  7. package/examples/private/DEV-9850-3.json +461 -0
  8. package/examples/private/chart.json +1084 -0
  9. package/examples/private/ci_formatted.json +202 -0
  10. package/examples/private/ci_issue.json +3016 -0
  11. package/examples/private/completed.json +634 -0
  12. package/examples/private/dem-data-long.csv +20 -0
  13. package/examples/private/dem-data-long.json +36 -0
  14. package/examples/private/demographic_data.csv +157 -0
  15. package/examples/private/demographic_data.json +2654 -0
  16. package/examples/private/demographic_dynamic.json +443 -0
  17. package/examples/private/demographic_standard.json +560 -0
  18. package/examples/private/ehdi.json +29939 -0
  19. package/examples/private/test.json +448 -20047
  20. package/index.html +9 -6
  21. package/package.json +2 -2
  22. package/src/CdcChart.tsx +62 -82
  23. package/src/_stories/Chart.Anchors.stories.tsx +31 -0
  24. package/src/_stories/Chart.DynamicSeries.stories.tsx +8 -1
  25. package/src/_stories/Chart.stories.tsx +32 -0
  26. package/src/_stories/ChartAxisLabels.stories.tsx +4 -1
  27. package/{examples/feature/area/area-chart-date-city-temperature.json → src/_stories/_mock/area_chart_stacked.json} +125 -27
  28. package/src/_stories/_mock/line_chart_dynamic_ci.json +493 -0
  29. package/src/_stories/_mock/line_chart_non_dynamic_ci.json +522 -0
  30. package/src/_stories/_mock/short_dates.json +288 -0
  31. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +15 -3
  32. package/src/components/Axis/Categorical.Axis.tsx +2 -2
  33. package/src/components/BarChart/components/BarChart.Horizontal.tsx +46 -37
  34. package/src/components/BarChart/components/BarChart.Vertical.tsx +28 -40
  35. package/src/components/BarChart/helpers/getBarData.ts +28 -0
  36. package/src/components/BarChart/helpers/tests/getBarData.test.ts +74 -0
  37. package/src/components/BoxPlot/BoxPlot.tsx +12 -70
  38. package/src/components/BoxPlot/helpers/index.ts +54 -0
  39. package/src/components/BrushChart.tsx +23 -26
  40. package/src/components/EditorPanel/EditorPanel.tsx +55 -79
  41. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +1 -0
  42. package/src/components/EditorPanel/useEditorPermissions.ts +5 -1
  43. package/src/components/Legend/Legend.Component.tsx +2 -2
  44. package/src/{hooks/useLegendClasses.ts → components/Legend/helpers/getLegendClasses.ts} +5 -5
  45. package/src/components/Legend/helpers/index.ts +2 -1
  46. package/src/components/Legend/tests/getLegendClasses.test.ts +115 -0
  47. package/src/components/LineChart/components/LineChart.Circle.tsx +1 -1
  48. package/src/components/LineChart/helpers.ts +1 -0
  49. package/src/components/LineChart/index.tsx +47 -1
  50. package/src/components/LinearChart.tsx +180 -172
  51. package/src/components/PieChart/PieChart.tsx +7 -1
  52. package/src/components/Sankey/components/ColumnList.tsx +19 -0
  53. package/src/components/Sankey/components/Sankey.tsx +479 -0
  54. package/src/components/Sankey/helpers/getSankeyTooltip.tsx +33 -0
  55. package/src/components/Sankey/index.tsx +1 -510
  56. package/src/components/Sankey/sankey.scss +16 -16
  57. package/src/components/Sankey/types/index.ts +1 -1
  58. package/src/data/initial-state.js +4 -3
  59. package/src/helpers/countNumOfTicks.ts +57 -0
  60. package/src/helpers/getQuartiles.ts +15 -18
  61. package/src/hooks/useMinMax.ts +18 -4
  62. package/src/hooks/useScales.ts +38 -4
  63. package/src/hooks/useTooltip.tsx +5 -1
  64. package/src/scss/DataTable.scss +5 -0
  65. package/src/scss/main.scss +6 -2
@@ -4,27 +4,24 @@
4
4
  * @param {Array} arr - The array of integers or decimals.
5
5
  * @returns {Object} An object containing the q1 and q3 values.
6
6
  */
7
- export const getQuartiles = arr => {
8
- arr.sort((a, b) => a - b)
7
+ import _ from 'lodash'
9
8
 
10
- // Calculate the index of the median value of the array
11
- const medianIndex = Math.floor(arr.length / 2)
9
+ export const getQuartiles = (values: number[]): { q1: number; q3: number } => {
10
+ const sortedData: number[] = _.sortBy(values)
12
11
 
13
- // Check if the length of the array is even or odd
14
- const isEvenLength = arr.length % 2 === 0
12
+ const quantile = (sortedData: number[], q: number): number => {
13
+ const position: number = (sortedData.length - 1) * q
14
+ const base: number = Math.floor(position)
15
+ const rest: number = position - base
16
+ if (sortedData[base + 1] !== undefined) {
17
+ return sortedData[base] + rest * (sortedData[base + 1] - sortedData[base])
18
+ } else {
19
+ return sortedData[base]
20
+ }
21
+ }
15
22
 
16
- // Split the array into two subarrays based on the median index
17
- const q1Array = isEvenLength ? arr.slice(0, medianIndex) : arr.slice(0, medianIndex + 1)
18
- const q3Array = isEvenLength ? arr.slice(medianIndex) : arr.slice(medianIndex + 1)
23
+ const q1: number = quantile(sortedData, 0.25)
24
+ const q3: number = quantile(sortedData, 0.75)
19
25
 
20
- // Calculate the median of the first subarray to get the q1 value
21
- const q1Index = Math.floor(q1Array.length / 2)
22
- const q1 = isEvenLength ? (q1Array[q1Index - 1] + q1Array[q1Index]) / 2 : q1Array[q1Index]
23
-
24
- // Calculate the median of the second subarray to get the q3 value
25
- const q3Index = Math.floor(q3Array.length / 2)
26
- const q3 = isEvenLength ? (q3Array[q3Index - 1] + q3Array[q3Index]) / 2 : q3Array[q3Index]
27
-
28
- // Return an object containing the q1 and q3 values
29
26
  return { q1, q3 }
30
27
  }
@@ -37,7 +37,7 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
37
37
 
38
38
  const { visualizationType, series } = config
39
39
  const { max: enteredMaxValue, min: enteredMinValue } = config.runtime.yAxis
40
- const minRequiredCIPadding = 1.15 // regardless of Editor if CI data, there must be 10% padding added
40
+ const paddingAddedToAxis = config.yAxis.enablePadding ? 1 + config.yAxis.scalePadding / 100 : 1
41
41
  const isLogarithmicAxis = config.yAxis.type === 'logarithmic'
42
42
  // do validation bafore applying t0 charts
43
43
  const isMaxValid = existPositiveValue ? Number(enteredMaxValue) >= maxValue : Number(enteredMaxValue) >= 0
@@ -52,8 +52,10 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
52
52
 
53
53
  if (lower && upper && config.visualizationType === 'Bar') {
54
54
  const buffer = min < 0 ? 1.1 : 0
55
- max = Math.max(maxValue, Math.max(...data.flatMap(d => [d[upper], d[lower]])) * 1.15)
56
- min = Math.min(minValue, Math.min(...data.flatMap(d => [d[upper], d[lower]])) * 1.15) * buffer
55
+ const maxValueWithCI = Math.max(...data.flatMap(d => [d[upper], d[lower]])) * paddingAddedToAxis
56
+ const minValueWithCIPlusBuffer = Math.min(...data.flatMap(d => [d[upper], d[lower]])) * paddingAddedToAxis * buffer
57
+ max = max > maxValueWithCI ? max : maxValueWithCI
58
+ min = min < minValueWithCIPlusBuffer ? min : minValueWithCIPlusBuffer
57
59
  }
58
60
 
59
61
  if (config.series.filter(s => s?.type === 'Forecasting')) {
@@ -185,7 +187,15 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
185
187
  return valueMatch && (index === 0 || index === tableData.length - 1)
186
188
  })
187
189
  })
188
- min = Number(enteredMinValue) && isMinValid ? Number(enteredMinValue) : suppressedMinValue ? 0 : minValue
190
+ let isCategoricalAxis = config.yAxis.type === 'categorical'
191
+ min =
192
+ enteredMinValue !== '' && isMinValid
193
+ ? Number(enteredMinValue)
194
+ : suppressedMinValue
195
+ ? 0
196
+ : isCategoricalAxis
197
+ ? 0
198
+ : minValue
189
199
  }
190
200
  //If data value max wasn't provided, calculate it
191
201
  if (max === Number.MIN_VALUE) {
@@ -226,6 +236,10 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
226
236
  }
227
237
  }
228
238
 
239
+ if (config.visualizationType === 'Area Chart' && config.visualizationSubType === 'stacked') {
240
+ min = 0
241
+ }
242
+
229
243
  return { min, max, leftMax, rightMax }
230
244
  }
231
245
  export default useMinMax
@@ -1,8 +1,18 @@
1
- import { LinearScaleConfig, LogScaleConfig, scaleBand, scaleLinear, scaleLog, scalePoint, scaleTime } from '@visx/scale'
1
+ import {
2
+ LinearScaleConfig,
3
+ LogScaleConfig,
4
+ scaleBand,
5
+ scaleLinear,
6
+ scaleLog,
7
+ scalePoint,
8
+ scaleTime,
9
+ getTicks
10
+ } from '@visx/scale'
2
11
  import { useContext } from 'react'
3
12
  import ConfigContext from '../ConfigContext'
4
13
  import { ChartConfig } from '../types/ChartConfig'
5
14
  import { ChartContext } from '../types/ChartContext'
15
+ import _ from 'lodash'
6
16
 
7
17
  const scaleTypes = {
8
18
  TIME: 'time',
@@ -154,6 +164,9 @@ const useScales = (properties: useScaleProps) => {
154
164
 
155
165
  // Set Scales
156
166
 
167
+ const categories = _.uniq(data.map(d => d[config.xAxis.dataKey]))
168
+ const range = [0, config.barThickness * 100 || 1]
169
+ const domain = _.map(config.series, 'dataKey')
157
170
  yScale = scaleLinear({
158
171
  range: [yMax, 0],
159
172
  round: true,
@@ -161,11 +174,10 @@ const useScales = (properties: useScaleProps) => {
161
174
  })
162
175
  xScale = scaleBand({
163
176
  range: [0, xMax],
164
- domain: config.boxplot.categories
177
+ domain: categories
165
178
  })
166
179
  xScale.type = scaleTypes.BAND
167
-
168
- seriesScale = composeScalePoint(seriesDomain, [0, yMax])
180
+ seriesScale = composeScaleBand(domain, range)
169
181
  }
170
182
 
171
183
  // handle Paired bar
@@ -327,6 +339,28 @@ export const getTickValues = (xAxisDataMapped, xScale, num, config) => {
327
339
  }
328
340
  }
329
341
 
342
+ // Ensure that the last tick is shown for charts with a "Date (Linear Scale)" scale
343
+ export const filterAndShiftLinearDateTicks = (config, axisProps, xAxisDataMapped, formatDate) => {
344
+ let ticks = axisProps.ticks
345
+ const filteredTickValues = getTicks(axisProps.scale, axisProps.numTicks)
346
+ if (filteredTickValues.length < xAxisDataMapped.length) {
347
+ let shift = 0
348
+ const lastIdx = xAxisDataMapped.indexOf(filteredTickValues[filteredTickValues.length - 1])
349
+ if (lastIdx < xAxisDataMapped.length - 1) {
350
+ shift = !config.xAxis.sortByRecentDate
351
+ ? xAxisDataMapped.length - 1 - lastIdx
352
+ : xAxisDataMapped.indexOf(filteredTickValues[0]) * -1
353
+ }
354
+ ticks = filteredTickValues.map(value => {
355
+ return axisProps.ticks[axisProps.ticks.findIndex(tick => tick.value === value) + shift]
356
+ })
357
+ }
358
+ ticks.forEach((tick, i) => {
359
+ tick.formattedValue = formatDate(tick.value, i, ticks)
360
+ })
361
+ return ticks
362
+ }
363
+
330
364
  /// helper functions
331
365
  const composeXScale = ({ min, max, xMax, config }) => {
332
366
  // Adjust min value if using logarithmic scale
@@ -553,9 +553,13 @@ export const useTooltip = props => {
553
553
  (!pd.column || key === pd.column)
554
554
  )) ||
555
555
  {}
556
- const newValue = label || value
556
+ let newValue = label || value
557
557
  const style = displayGray ? { color: '#8b8b8a' } : {}
558
558
 
559
+ if (index == 1 && config.dataFormat.onlyShowTopPrefixSuffix) {
560
+ newValue = `${config.dataFormat.prefix}${newValue}${config.dataFormat.suffix}`
561
+ }
562
+
559
563
  return <li style={style} className='tooltip-body'>{`${getSeriesNameFromLabel(key)}: ${newValue}`}</li>
560
564
  }
561
565
 
@@ -1,5 +1,10 @@
1
1
  .data-table-container {
2
2
  margin: 20px 0 0;
3
+
4
+ &.brush-active {
5
+ margin: 80px 0 0;
6
+ }
7
+
3
8
  width: 100%;
4
9
  &.download-link-above {
5
10
  margin: 0;
@@ -140,7 +140,7 @@
140
140
  .subtext--responsive-ticks,
141
141
  .section-subtext {
142
142
  &--brush-active {
143
- margin-top: 2.5em;
143
+ margin-top: 3em !important;
144
144
  }
145
145
  }
146
146
 
@@ -159,7 +159,7 @@
159
159
  border: 1px solid var(--lightGray);
160
160
  position: relative;
161
161
 
162
- &.no-border {
162
+ &.border-0 {
163
163
  border: 1px solid transparent;
164
164
  padding: 0;
165
165
  }
@@ -261,6 +261,10 @@
261
261
  }
262
262
  }
263
263
 
264
+ .legend-container__inner.flex-column-reverse div.legend-item:last-child {
265
+ margin-bottom: 0.2rem !important;
266
+ }
267
+
264
268
  .dynamic-legend-list {
265
269
  // overide traditional legend item that uses !important
266
270
  .legend-item {