@cdc/chart 4.25.3 → 4.25.6
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 +46641 -42561
- package/index.html +130 -129
- package/package.json +22 -27
- package/src/CdcChartComponent.tsx +75 -35
- package/src/_stories/Chart.CI.stories.tsx +10 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +68 -49
- package/src/_stories/Chart.stories.tsx +99 -86
- package/src/_stories/ChartPrefixSuffix.stories.tsx +29 -32
- package/{examples/private/line-issue.json → src/_stories/_mock/barchart_labels.mock.json} +150 -35
- package/src/_stories/_mock/dynamic_series_bar_config.json +1 -1
- package/src/_stories/_mock/dynamic_series_suppression_mock.json +610 -0
- package/{examples/private/not-loading.json → src/_stories/_mock/pie_calculated_area.json} +152 -95
- package/src/components/Annotations/components/AnnotationDropdown.tsx +2 -2
- package/src/components/AreaChart/components/AreaChart.jsx +33 -5
- package/src/components/Axis/Categorical.Axis.tsx +2 -2
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +38 -37
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +18 -8
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +8 -8
- package/src/components/BarChart/components/BarChart.Vertical.tsx +47 -36
- package/src/components/BarChart/components/{BarChart.jsx → BarChart.tsx} +23 -5
- package/src/components/BarChart/components/context.tsx +20 -2
- package/src/components/BarChart/helpers/getBarHeights.ts +47 -0
- package/src/components/BarChart/helpers/index.ts +5 -2
- package/src/components/BarChart/helpers/tests/getBarHeights.test.ts +83 -0
- package/src/{hooks → components/BarChart/helpers}/useBarChart.ts +9 -46
- package/src/components/BoxPlot/BoxPlot.tsx +2 -1
- package/src/components/Brush/BrushChart.tsx +73 -0
- package/src/components/Brush/BrushController..tsx +39 -0
- package/src/components/DeviationBar.jsx +2 -2
- package/src/components/EditorPanel/EditorPanel.tsx +232 -147
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +36 -36
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +52 -25
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +12 -4
- package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +8 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +5 -5
- package/src/components/ForestPlot/ForestPlot.tsx +2 -2
- package/src/components/HoverLine/HoverLine.tsx +74 -0
- package/src/components/Legend/Legend.Component.tsx +1 -1
- package/src/components/Legend/Legend.Suppression.tsx +59 -25
- package/src/components/Legend/helpers/createFormatLabels.tsx +28 -0
- package/src/components/Legend/helpers/index.ts +1 -1
- package/src/components/LineChart/LineChartProps.ts +3 -1
- package/src/components/LineChart/components/LineChart.Circle.tsx +72 -119
- package/src/components/LineChart/helpers.ts +133 -56
- package/src/components/LineChart/index.tsx +106 -55
- package/src/components/LinearChart.tsx +178 -198
- package/src/components/PairedBarChart.jsx +3 -2
- package/src/components/PieChart/PieChart.tsx +127 -102
- package/src/components/ScatterPlot/ScatterPlot.jsx +5 -0
- package/src/components/Sparkline/components/SparkLine.tsx +80 -18
- package/src/data/initial-state.js +11 -6
- package/src/helpers/countNumOfTicks.ts +1 -1
- package/src/helpers/dataHelpers.ts +23 -2
- package/src/helpers/getNewRuntime.ts +35 -0
- package/src/helpers/getPiePercent.ts +22 -0
- package/src/helpers/getTransformedData.ts +22 -0
- package/src/helpers/sizeHelpers.ts +1 -1
- package/src/helpers/tests/getNewRuntime.test.ts +82 -0
- package/src/helpers/tests/getPiePercent.test.ts +38 -0
- package/src/hooks/useMinMax.ts +21 -28
- package/src/hooks/useRightAxis.ts +5 -3
- package/src/hooks/useScales.ts +15 -6
- package/src/hooks/useTooltip.tsx +218 -203
- package/src/index.jsx +2 -2
- package/src/scss/main.scss +13 -6
- package/src/store/chart.actions.ts +2 -6
- package/src/store/chart.reducer.ts +23 -23
- package/src/types/ChartConfig.ts +11 -3
- package/src/types/ChartContext.ts +0 -2
- package/examples/private/DEV-8850-2.json +0 -493
- package/examples/private/DEV-9822.json +0 -574
- package/examples/private/DEV-9840.json +0 -553
- package/examples/private/DEV-9850-3.json +0 -461
- package/examples/private/chart.json +0 -1084
- package/examples/private/ci_formatted.json +0 -202
- package/examples/private/ci_issue.json +0 -3016
- package/examples/private/completed.json +0 -634
- package/examples/private/dem-data-long.csv +0 -20
- package/examples/private/dem-data-long.json +0 -36
- package/examples/private/demographic_data.csv +0 -157
- package/examples/private/demographic_data.json +0 -2654
- package/examples/private/demographic_dynamic.json +0 -443
- package/examples/private/demographic_standard.json +0 -560
- package/examples/private/ehdi.json +0 -29939
- package/examples/private/test.json +0 -493
- package/src/components/ZoomBrush.tsx +0 -251
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { getNewRuntime } from '../getNewRuntime'
|
|
3
|
+
|
|
4
|
+
describe('getNewRuntime', () => {
|
|
5
|
+
it('should return a runtime object with default values when no data is provided', () => {
|
|
6
|
+
const visualizationConfig = { runtime: {} }
|
|
7
|
+
const newFilteredData = null
|
|
8
|
+
|
|
9
|
+
const result = getNewRuntime(visualizationConfig, newFilteredData)
|
|
10
|
+
|
|
11
|
+
expect(result.series).toEqual([])
|
|
12
|
+
expect(result.seriesLabels).toEqual({})
|
|
13
|
+
expect(result.seriesLabelsAll).toEqual([])
|
|
14
|
+
expect(result.seriesKeys).toEqual([])
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('should populate runtime.series with valid series from newFilteredData', () => {
|
|
18
|
+
const visualizationConfig = {
|
|
19
|
+
runtime: {},
|
|
20
|
+
filters: [],
|
|
21
|
+
columns: {},
|
|
22
|
+
dynamicSeriesType: 'bar',
|
|
23
|
+
dynamicSeriesLineType: 'solid',
|
|
24
|
+
xAxis: { dataKey: 'x' }
|
|
25
|
+
}
|
|
26
|
+
const newFilteredData = [
|
|
27
|
+
{ x: 1, y: 10, z: 20 },
|
|
28
|
+
{ x: 2, y: 15, z: 25 }
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
const result = getNewRuntime(visualizationConfig, newFilteredData)
|
|
32
|
+
|
|
33
|
+
expect(result.series).toEqual([
|
|
34
|
+
{ dataKey: 'y', type: 'bar', lineType: 'solid', tooltip: true },
|
|
35
|
+
{ dataKey: 'z', type: 'bar', lineType: 'solid', tooltip: true }
|
|
36
|
+
])
|
|
37
|
+
expect(result.seriesKeys).toEqual(['y', 'z'])
|
|
38
|
+
expect(result.seriesLabels).toEqual({ y: 'y', z: 'z' })
|
|
39
|
+
expect(result.seriesLabelsAll).toEqual(['y', 'z'])
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should exclude series keys that match filters or columns', () => {
|
|
43
|
+
const visualizationConfig = {
|
|
44
|
+
runtime: {},
|
|
45
|
+
filters: [{ columnName: 'y' }],
|
|
46
|
+
columns: { z: {} },
|
|
47
|
+
dynamicSeriesType: 'bar',
|
|
48
|
+
dynamicSeriesLineType: 'solid',
|
|
49
|
+
xAxis: { dataKey: 'x' }
|
|
50
|
+
}
|
|
51
|
+
const newFilteredData = [
|
|
52
|
+
{ x: 1, y: 10, z: 20, w: 30 },
|
|
53
|
+
{ x: 2, y: 15, z: 25, w: 35 }
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
const result = getNewRuntime(visualizationConfig, newFilteredData)
|
|
57
|
+
|
|
58
|
+
expect(result.series).toEqual([{ dataKey: 'w', type: 'bar', lineType: 'solid', tooltip: true }])
|
|
59
|
+
expect(result.seriesKeys).toEqual(['w'])
|
|
60
|
+
expect(result.seriesLabels).toEqual({ w: 'w' })
|
|
61
|
+
expect(result.seriesLabelsAll).toEqual(['w'])
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should handle empty newFilteredData gracefully', () => {
|
|
65
|
+
const visualizationConfig = {
|
|
66
|
+
runtime: {},
|
|
67
|
+
filters: [],
|
|
68
|
+
columns: {},
|
|
69
|
+
dynamicSeriesType: 'bar',
|
|
70
|
+
dynamicSeriesLineType: 'solid',
|
|
71
|
+
xAxis: { dataKey: 'x' }
|
|
72
|
+
}
|
|
73
|
+
const newFilteredData = []
|
|
74
|
+
|
|
75
|
+
const result = getNewRuntime(visualizationConfig, newFilteredData)
|
|
76
|
+
|
|
77
|
+
expect(result.series).toEqual([])
|
|
78
|
+
expect(result.seriesKeys).toEqual([])
|
|
79
|
+
expect(result.seriesLabels).toEqual({})
|
|
80
|
+
expect(result.seriesLabelsAll).toEqual([])
|
|
81
|
+
})
|
|
82
|
+
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// getPiePercent.test.ts
|
|
2
|
+
import { getPiePercent } from '../getPiePercent'
|
|
3
|
+
|
|
4
|
+
describe('getPiePercent', () => {
|
|
5
|
+
it('cgets percentages for purely numeric strings', () => {
|
|
6
|
+
const data = [{ A: '1' }, { A: '3' }, { A: '6' }]
|
|
7
|
+
const result = getPiePercent(data, 'A')
|
|
8
|
+
|
|
9
|
+
// sum = 1 + 3 + 6 = 10
|
|
10
|
+
expect(result[0].A).toBeCloseTo((1 / 10) * 100) // 10%
|
|
11
|
+
expect(result[1].A).toBeCloseTo((3 / 10) * 100) // 30%
|
|
12
|
+
expect(result[2].A).toBeCloseTo((6 / 10) * 100) // 60%
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('shandle non numbers like "ABC', () => {
|
|
16
|
+
const data = [{ A: '1' }, { A: 'ABC' }, { A: '2' }]
|
|
17
|
+
const result = getPiePercent(data, 'A')
|
|
18
|
+
|
|
19
|
+
expect(result[0].A).toBeCloseTo((1 / 3) * 100)
|
|
20
|
+
expect(result[1].A).toBe('ABC')
|
|
21
|
+
expect(result[2].A).toBeCloseTo((2 / 3) * 100)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('handles all-zero total by producing 0%', () => {
|
|
25
|
+
const data = [{ A: '0' }, { A: '0' }]
|
|
26
|
+
const result = getPiePercent(data, 'A')
|
|
27
|
+
expect(result[0].A).toBe(0)
|
|
28
|
+
expect(result[1].A).toBe(0)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('leaves rows missing the key entirely unchanged', () => {
|
|
32
|
+
const data = [{ A: '2' }, { B: 'foo' }]
|
|
33
|
+
const result = getPiePercent(data, 'A')
|
|
34
|
+
|
|
35
|
+
expect(result[0].A).toBeCloseTo(100)
|
|
36
|
+
expect(result[1]).toEqual({ B: 'foo' })
|
|
37
|
+
})
|
|
38
|
+
})
|
package/src/hooks/useMinMax.ts
CHANGED
|
@@ -46,7 +46,6 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
46
46
|
|
|
47
47
|
min = enteredMinValue && isMinValid ? Number(enteredMinValue) : minValue
|
|
48
48
|
max = enteredMaxValue && isMaxValid ? Number(enteredMaxValue) : Number.MIN_VALUE
|
|
49
|
-
|
|
50
49
|
const { lower, upper } = config?.confidenceKeys || {}
|
|
51
50
|
|
|
52
51
|
if (lower && upper && config.visualizationType === 'Bar') {
|
|
@@ -167,35 +166,28 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
167
166
|
}
|
|
168
167
|
|
|
169
168
|
if (config.visualizationType === 'Line' && !convertLineToBarGraph) {
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
// Return true if the value matches and it's either the first or the last item
|
|
186
|
-
return valueMatch && (index === 0 || index === tableData.length - 1)
|
|
169
|
+
const numEnteredMin = Number(enteredMinValue)
|
|
170
|
+
const isMinValid = isLogarithmicAxis ? numEnteredMin >= 0 && numEnteredMin < minValue : numEnteredMin < minValue
|
|
171
|
+
|
|
172
|
+
const suppressedMinValue = tableData?.some((item, i, arr) =>
|
|
173
|
+
config.preliminaryData?.some(({ type, style, column, value }) => {
|
|
174
|
+
if (type !== 'suppression' || !style) return false
|
|
175
|
+
|
|
176
|
+
const values = _.values(_.pick(item, config.runtime?.seriesKeys))
|
|
177
|
+
const dynamicCategory = config.series[0].dynamicCategory
|
|
178
|
+
|
|
179
|
+
const match = column ? item[column] === value : values.includes(value)
|
|
180
|
+
const dynamic = dynamicCategory && (item[dynamicCategory] === column || !column)
|
|
181
|
+
|
|
182
|
+
return (match || dynamic) && (i === 0 || i === arr.length - 1)
|
|
187
183
|
})
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
: suppressedMinValue
|
|
194
|
-
? 0
|
|
195
|
-
: isCategoricalAxis
|
|
196
|
-
? 0
|
|
197
|
-
: minValue
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
const isCategorical = config.yAxis.type === 'categorical'
|
|
187
|
+
|
|
188
|
+
min = enteredMinValue !== '' && isMinValid ? numEnteredMin : suppressedMinValue ? 0 : isCategorical ? 0 : minValue
|
|
198
189
|
}
|
|
190
|
+
|
|
199
191
|
//If data value max wasn't provided, calculate it
|
|
200
192
|
if (max === Number.MIN_VALUE) {
|
|
201
193
|
// if all values in data are negative set max = 0
|
|
@@ -241,6 +233,7 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
241
233
|
|
|
242
234
|
if (config.visualizationType === 'Scatter Plot') {
|
|
243
235
|
max = max * 1.1
|
|
236
|
+
min = min / 1.1
|
|
244
237
|
}
|
|
245
238
|
|
|
246
239
|
return { min, max, leftMax, rightMax }
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { scaleLinear } from '@visx/scale'
|
|
2
2
|
import useReduceData from './useReduceData'
|
|
3
|
+
import { TOP_PADDING } from './useScales'
|
|
3
4
|
|
|
4
|
-
export default function useRightAxis({ config, yMax = 0, data = []
|
|
5
|
+
export default function useRightAxis({ config, yMax = 0, data = [] }) {
|
|
5
6
|
const hasRightAxis = config.visualizationType === 'Combo' && config.orientation === 'vertical'
|
|
6
|
-
const rightSeriesKeys =
|
|
7
|
+
const rightSeriesKeys =
|
|
8
|
+
config.series && config.series.filter(series => series.axis === 'Right').map(key => key.dataKey)
|
|
7
9
|
let { minValue } = useReduceData(config, data)
|
|
8
10
|
|
|
9
11
|
const allRightAxisData = rightSeriesKeys => {
|
|
@@ -35,7 +37,7 @@ export default function useRightAxis({ config, yMax = 0, data = [], updateConfig
|
|
|
35
37
|
|
|
36
38
|
const yScaleRight = scaleLinear({
|
|
37
39
|
domain: [minValue, max],
|
|
38
|
-
range: [yMax,
|
|
40
|
+
range: [yMax, TOP_PADDING]
|
|
39
41
|
})
|
|
40
42
|
|
|
41
43
|
return { yScaleRight, hasRightAxis }
|
package/src/hooks/useScales.ts
CHANGED
|
@@ -22,6 +22,8 @@ const scaleTypes = {
|
|
|
22
22
|
BAND: 'band'
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export const TOP_PADDING = 10
|
|
26
|
+
|
|
25
27
|
type useScaleProps = {
|
|
26
28
|
config: ChartConfig // standard chart config
|
|
27
29
|
data: Object[] // standard data array
|
|
@@ -77,14 +79,21 @@ const useScales = (properties: useScaleProps) => {
|
|
|
77
79
|
xScale = composeScaleBand(xAxisDataMappedSorted, [0, xMax], 1 - config.barThickness)
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
// handle Linear scaled viz
|
|
83
|
+
if (config.xAxis.type === 'date' && !isHorizontal) {
|
|
84
|
+
const sorted = sortXAxisData(xAxisDataMapped, config.xAxis.sortByRecentDate)
|
|
85
|
+
|
|
86
|
+
xScale = composeScaleBand(sorted, [0, xMax], 1 - config.barThickness)
|
|
87
|
+
xScale.type = scaleTypes.BAND
|
|
88
|
+
}
|
|
89
|
+
|
|
80
90
|
if (xAxis.type === 'date-time' || xAxis.type === 'continuous') {
|
|
81
91
|
let xAxisMin = Math.min(...xAxisDataMapped.map(Number))
|
|
82
92
|
let xAxisMax = Math.max(...xAxisDataMapped.map(Number))
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
: (config.xAxis.padding ? config.xAxis.padding * 0.01 : 0) * (xAxisMax - xAxisMin)
|
|
93
|
+
let paddingRatio = config.xAxis.padding ? config.xAxis.padding * 0.01 : 0
|
|
94
|
+
|
|
95
|
+
xAxisMin -= paddingRatio * (xAxisMax - xAxisMin)
|
|
96
|
+
xAxisMax += visualizationType === 'Line' ? 0 : paddingRatio * (xAxisMax - xAxisMin)
|
|
88
97
|
const range = config.xAxis.sortByRecentDate ? [xMax, 0] : [0, xMax]
|
|
89
98
|
xScale = scaleTime({
|
|
90
99
|
domain: [xAxisMin, xAxisMax],
|
|
@@ -392,7 +401,7 @@ const composeYScale = ({ min, max, yMax, config, leftMax }) => {
|
|
|
392
401
|
|
|
393
402
|
// If the visualization type is a bump chart then the domain and range need different values
|
|
394
403
|
const domainSet = config.visualizationType === 'Bump Chart' ? [1, max] : [min, max]
|
|
395
|
-
const yRange = config.visualizationType === 'Bump Chart' ? [30, yMax] : [yMax,
|
|
404
|
+
const yRange = config.visualizationType === 'Bump Chart' ? [30, yMax] : [yMax, TOP_PADDING]
|
|
396
405
|
// Return the configured scale function
|
|
397
406
|
return scaleFunc({
|
|
398
407
|
domain: domainSet,
|