@cdc/chart 4.26.5 → 4.26.7
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/CONFIG.md +20 -14
- package/dist/{cdcchart-DQ00cQCm.es.js → cdcchart-BWVIUcAF.es.js} +5 -55
- package/dist/cdcchart.js +36554 -34320
- package/examples/feature/heatmap/average-age-categorical.json +1 -1
- package/examples/feature/heatmap/calendar-heatmap.json +1 -1
- package/examples/feature/heatmap/sparse-aggregation.json +1 -1
- package/examples/private/bad-y-axis.json +543 -0
- package/examples/private/bar-chart-negative-value.json +455 -0
- package/examples/private/flueview.json +22051 -0
- package/package.json +3 -11
- package/src/CdcChart.tsx +7 -6
- package/src/CdcChartComponent.tsx +71 -18
- package/src/_stories/Chart.PieLabels.stories.tsx +47 -0
- package/src/_stories/Chart.SeriesColorAssignments.stories.tsx +183 -0
- package/src/_stories/Chart.YAxisAutoMaxStrategy.stories.tsx +52 -0
- package/src/_stories/Chart.YAxisFilterDomain.stories.tsx +90 -0
- package/src/_stories/Chart.smoke.stories.tsx +14 -1
- package/src/_stories/ChartAnnotation.smoke.stories.tsx +10 -0
- package/src/_stories/ChartPrefixSuffix.smoke.stories.tsx +28 -1
- package/src/_stories/_mock/annotation_event-line_mock.json +1064 -0
- package/src/_stories/_mock/heatmap-average-age-categorical.json +1 -1
- package/src/_stories/_mock/heatmap.json +1 -1
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +22 -1
- package/src/components/Annotations/components/AnnotationDraggable.tsx +219 -119
- package/src/components/Annotations/tests/AnnotationDraggable.test.tsx +198 -0
- package/src/components/Axis/LeftAxis.tsx +3 -0
- package/src/components/Axis/LeftAxisGridlines.tsx +8 -1
- package/src/components/Axis/RightAxis.tsx +3 -0
- package/src/components/Axis/tests/AxisTickValues.test.tsx +86 -0
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +1 -2
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -1
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +1 -1
- package/src/components/BarChart/components/BarChart.Vertical.tsx +1 -2
- package/src/components/BarChart/helpers/getBarData.ts +28 -28
- package/src/components/BarChart/helpers/getBarHeights.ts +2 -2
- package/src/components/BarChart/helpers/tests/useBarChart.test.tsx +98 -0
- package/src/components/BarChart/helpers/useBarChart.ts +4 -5
- package/src/components/BoxPlot/BoxPlot.Horizontal.tsx +7 -6
- package/src/components/BoxPlot/BoxPlot.Vertical.tsx +9 -7
- package/src/components/BoxPlot/helpers/index.ts +4 -3
- package/src/components/Brush/BrushSelector.tsx +1 -1
- package/src/components/EditorPanel/EditorPanel.tsx +457 -165
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +101 -52
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +16 -7
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +3 -3
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +19 -4
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.test.tsx +98 -0
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +10 -0
- package/src/components/EditorPanel/components/Panels/Panel.Visual.test.tsx +484 -0
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +3 -0
- package/src/components/EditorPanel/components/Panels/SeriesColorAssignments.tsx +262 -0
- package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +32 -0
- package/src/components/EditorPanel/components/panels.scss +10 -0
- package/src/components/EditorPanel/editor-panel.scss +47 -0
- package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +6 -2
- package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +20 -2
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +3 -3
- package/src/components/EditorPanel/useEditorPermissions.test.tsx +288 -11
- package/src/components/EditorPanel/useEditorPermissions.ts +46 -6
- package/src/components/Forecasting/Forecasting.tsx +1 -1
- package/src/components/HeatMap/components/HeatMap.tsx +22 -17
- package/src/components/HeatMap/components/HeatMapGradientLegend.tsx +29 -15
- package/src/components/HeatMap/heatmap.css +15 -1
- package/src/components/HeatMap/tests/HeatMap.test.tsx +39 -7
- package/src/components/Legend/Legend.Component.tsx +56 -61
- package/src/components/Legend/Legend.Suppression.tsx +3 -3
- package/src/components/Legend/LegendGroup/LegendGroup.tsx +0 -1
- package/src/components/Legend/helpers/createFormatLabels.test.ts +88 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +6 -1
- package/src/components/LineChart/helpers.test.ts +230 -1
- package/src/components/LineChart/helpers.ts +25 -14
- package/src/components/LineChart/index.tsx +23 -7
- package/src/components/LinearChart/tests/LinearChart.test.tsx +30 -0
- package/src/components/LinearChart.tsx +51 -15
- package/src/components/PieChart/PieChart.tsx +117 -10
- package/src/components/PieChart/helpers/labelPlacement.test.ts +88 -0
- package/src/components/PieChart/helpers/labelPlacement.ts +95 -0
- package/src/components/ScatterPlot/ScatterPlot.jsx +5 -13
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +3 -0
- package/src/components/SmallMultiples/SmallMultiples.tsx +38 -9
- package/src/components/SmallMultiples/tests/SmallMultiples.test.tsx +170 -0
- package/src/data/initial-state.js +3 -0
- package/src/helpers/calculateLeftYAxisWidth.ts +9 -3
- package/src/helpers/categoryOrder.ts +60 -0
- package/src/helpers/colorAssignmentHelpers.ts +102 -0
- package/src/helpers/dataHelpers.ts +11 -1
- package/src/helpers/getAxisMaxOverride.ts +16 -0
- package/src/helpers/getBoxPlotConfig.ts +19 -12
- package/src/helpers/getCleanTopTickMax.ts +64 -0
- package/src/helpers/getColorScale.ts +18 -9
- package/src/helpers/getComboChartConfig.ts +0 -1
- package/src/helpers/getExcludedData.ts +0 -1
- package/src/helpers/getHasBoundarySuppression.ts +27 -0
- package/src/helpers/getMinMax.ts +25 -22
- package/src/helpers/getNewRuntime.ts +0 -2
- package/src/helpers/getPiePercent.ts +3 -3
- package/src/helpers/getYAxisDomainData.ts +38 -0
- package/src/helpers/getYAxisFinalizationEligibility.ts +40 -0
- package/src/helpers/hasSpacedInlineLabel.ts +10 -0
- package/src/helpers/isConvertLineToBarGraph.ts +2 -2
- package/src/helpers/seriesColumnSettings.ts +26 -7
- package/src/helpers/sizeHelpers.ts +0 -2
- package/src/helpers/smallMultiplesHelpers.ts +5 -0
- package/src/helpers/tests/calculateLeftYAxisWidth.test.ts +38 -0
- package/src/helpers/tests/categoryOrder.test.ts +57 -0
- package/src/helpers/tests/dataHelpers.test.ts +135 -0
- package/src/helpers/tests/getAxisMaxOverride.test.ts +36 -0
- package/src/helpers/tests/getCleanTopTickMax.test.ts +165 -0
- package/src/helpers/tests/getColorScale.test.ts +197 -0
- package/src/helpers/tests/getMinMax.test.ts +211 -0
- package/src/helpers/tests/getYAxisDomainData.test.ts +119 -0
- package/src/helpers/tests/getYAxisFinalizationEligibility.test.ts +185 -0
- package/src/helpers/tests/{getYAxisAutoPaddingMode.test.ts → hasSpacedInlineLabel.test.ts} +17 -32
- package/src/helpers/tests/seriesColumnSettings.test.ts +76 -2
- package/src/helpers/tests/smallMultiplesHelpers.test.ts +99 -0
- package/src/hooks/tests/useRightAxis.test.tsx +268 -0
- package/src/hooks/tests/useScales.test.tsx +302 -27
- package/src/hooks/tests/useTooltip.test.tsx +132 -0
- package/src/hooks/useRightAxis.ts +35 -25
- package/src/hooks/useScales.ts +45 -28
- package/src/hooks/useTooltip.tsx +17 -11
- package/src/store/chart.reducer.ts +0 -1
- package/src/test/CdcChart.dataTable.test.tsx +216 -0
- package/src/types/ChartConfig.ts +6 -1
- package/src/types/ChartContext.ts +3 -0
- package/src/helpers/getYAxisAutoPadding.ts +0 -55
- package/src/helpers/getYAxisAutoPaddingMode.ts +0 -17
- package/src/helpers/tests/getYAxisAutoPadding.test.ts +0 -37
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
2
|
+
import { hasSpacedInlineLabel } from '../hasSpacedInlineLabel'
|
|
3
3
|
import { createMockConfig } from '../../components/LinearChart/tests/mockConfigContext'
|
|
4
4
|
|
|
5
|
-
describe('
|
|
6
|
-
it('returns
|
|
5
|
+
describe('hasSpacedInlineLabel', () => {
|
|
6
|
+
it('returns true for multi-word inline labels', () => {
|
|
7
7
|
expect(
|
|
8
|
-
|
|
8
|
+
hasSpacedInlineLabel(
|
|
9
9
|
createMockConfig({
|
|
10
10
|
yAxis: {
|
|
11
11
|
...createMockConfig().yAxis,
|
|
@@ -14,12 +14,12 @@ describe('getYAxisAutoPaddingMode', () => {
|
|
|
14
14
|
}
|
|
15
15
|
})
|
|
16
16
|
)
|
|
17
|
-
).toBe(
|
|
17
|
+
).toBe(true)
|
|
18
18
|
})
|
|
19
19
|
|
|
20
|
-
it('returns
|
|
20
|
+
it('returns true for inline labels with leading space', () => {
|
|
21
21
|
expect(
|
|
22
|
-
|
|
22
|
+
hasSpacedInlineLabel(
|
|
23
23
|
createMockConfig({
|
|
24
24
|
yAxis: {
|
|
25
25
|
...createMockConfig().yAxis,
|
|
@@ -28,12 +28,12 @@ describe('getYAxisAutoPaddingMode', () => {
|
|
|
28
28
|
}
|
|
29
29
|
})
|
|
30
30
|
)
|
|
31
|
-
).toBe(
|
|
31
|
+
).toBe(true)
|
|
32
32
|
})
|
|
33
33
|
|
|
34
|
-
it('returns
|
|
34
|
+
it('returns false for top y-axis titles with empty inline labels', () => {
|
|
35
35
|
expect(
|
|
36
|
-
|
|
36
|
+
hasSpacedInlineLabel(
|
|
37
37
|
createMockConfig({
|
|
38
38
|
yAxis: {
|
|
39
39
|
...createMockConfig().yAxis,
|
|
@@ -42,27 +42,12 @@ describe('getYAxisAutoPaddingMode', () => {
|
|
|
42
42
|
}
|
|
43
43
|
})
|
|
44
44
|
)
|
|
45
|
-
).toBe(
|
|
45
|
+
).toBe(false)
|
|
46
46
|
})
|
|
47
47
|
|
|
48
|
-
it('returns
|
|
48
|
+
it('returns false when the inline label has no spaces', () => {
|
|
49
49
|
expect(
|
|
50
|
-
|
|
51
|
-
createMockConfig({
|
|
52
|
-
hideYAxisLabel: true,
|
|
53
|
-
yAxis: {
|
|
54
|
-
...createMockConfig().yAxis,
|
|
55
|
-
titlePlacement: 'top',
|
|
56
|
-
inlineLabel: ''
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
)
|
|
60
|
-
).toBe('none')
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('returns none when neither trigger applies', () => {
|
|
64
|
-
expect(
|
|
65
|
-
getYAxisAutoPaddingMode(
|
|
50
|
+
hasSpacedInlineLabel(
|
|
66
51
|
createMockConfig({
|
|
67
52
|
yAxis: {
|
|
68
53
|
...createMockConfig().yAxis,
|
|
@@ -71,12 +56,12 @@ describe('getYAxisAutoPaddingMode', () => {
|
|
|
71
56
|
}
|
|
72
57
|
})
|
|
73
58
|
)
|
|
74
|
-
).toBe(
|
|
59
|
+
).toBe(false)
|
|
75
60
|
})
|
|
76
61
|
|
|
77
|
-
it('returns inline
|
|
62
|
+
it('returns true for spaced inline labels regardless of title placement', () => {
|
|
78
63
|
expect(
|
|
79
|
-
|
|
64
|
+
hasSpacedInlineLabel(
|
|
80
65
|
createMockConfig({
|
|
81
66
|
yAxis: {
|
|
82
67
|
...createMockConfig().yAxis,
|
|
@@ -85,6 +70,6 @@ describe('getYAxisAutoPaddingMode', () => {
|
|
|
85
70
|
}
|
|
86
71
|
})
|
|
87
72
|
)
|
|
88
|
-
).toBe(
|
|
73
|
+
).toBe(true)
|
|
89
74
|
})
|
|
90
75
|
})
|
|
@@ -53,6 +53,23 @@ describe('seriesColumnSettings', () => {
|
|
|
53
53
|
})
|
|
54
54
|
})
|
|
55
55
|
|
|
56
|
+
it('removes fields when an update value is undefined', () => {
|
|
57
|
+
const updatedColumns = upsertSeriesColumnConfig(
|
|
58
|
+
{
|
|
59
|
+
cases: { name: 'cases', label: 'Cases', roundToPlace: 2 }
|
|
60
|
+
},
|
|
61
|
+
'cases',
|
|
62
|
+
{ roundToPlace: undefined }
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
expect(updatedColumns).toEqual({
|
|
66
|
+
cases: {
|
|
67
|
+
name: 'cases',
|
|
68
|
+
label: 'Cases'
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
56
73
|
it('does not persist display defaults when creating a new series-owned column config', () => {
|
|
57
74
|
expect(upsertSeriesColumnConfig({}, 'cases', { label: 'Cases' })).toEqual({
|
|
58
75
|
cases: {
|
|
@@ -62,22 +79,79 @@ describe('seriesColumnSettings', () => {
|
|
|
62
79
|
})
|
|
63
80
|
})
|
|
64
81
|
|
|
82
|
+
it('does not create a rounding override when updating unrelated display settings', () => {
|
|
83
|
+
expect(upsertSeriesColumnConfig({}, 'cases', { dataTable: false })).toEqual({
|
|
84
|
+
cases: {
|
|
85
|
+
name: 'cases',
|
|
86
|
+
dataTable: false
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
65
91
|
it('only returns explicit formatting overrides and preserves falsey values', () => {
|
|
66
92
|
expect(
|
|
67
93
|
getSeriesColumnFormattingParams({
|
|
68
|
-
prefix: '',
|
|
94
|
+
prefix: '$',
|
|
69
95
|
suffix: ' units',
|
|
70
96
|
roundToPlace: 0,
|
|
71
97
|
commas: false
|
|
72
98
|
})
|
|
73
99
|
).toEqual({
|
|
74
|
-
addColPrefix: '',
|
|
100
|
+
addColPrefix: '$',
|
|
75
101
|
addColSuffix: ' units',
|
|
76
102
|
addColRoundTo: 0,
|
|
77
103
|
addColCommas: false
|
|
78
104
|
})
|
|
79
105
|
})
|
|
80
106
|
|
|
107
|
+
it('ignores blank prefix and suffix overrides so global formatting can apply', () => {
|
|
108
|
+
expect(
|
|
109
|
+
getSeriesColumnFormattingParams({
|
|
110
|
+
prefix: '',
|
|
111
|
+
suffix: '',
|
|
112
|
+
roundToPlace: 0,
|
|
113
|
+
commas: false
|
|
114
|
+
})
|
|
115
|
+
).toEqual({
|
|
116
|
+
addColRoundTo: 0,
|
|
117
|
+
addColCommas: false
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('ignores blank rounding overrides so global formatting can apply', () => {
|
|
122
|
+
expect(
|
|
123
|
+
getSeriesColumnFormattingParams({
|
|
124
|
+
roundToPlace: '',
|
|
125
|
+
commas: false
|
|
126
|
+
})
|
|
127
|
+
).toEqual({
|
|
128
|
+
addColCommas: false
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('normalizes string rounding overrides to numbers', () => {
|
|
133
|
+
expect(
|
|
134
|
+
getSeriesColumnFormattingParams({
|
|
135
|
+
roundToPlace: '0',
|
|
136
|
+
commas: false
|
|
137
|
+
})
|
|
138
|
+
).toEqual({
|
|
139
|
+
addColRoundTo: 0,
|
|
140
|
+
addColCommas: false
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('ignores invalid rounding overrides so global formatting can apply', () => {
|
|
145
|
+
expect(
|
|
146
|
+
getSeriesColumnFormattingParams({
|
|
147
|
+
roundToPlace: 'abc',
|
|
148
|
+
commas: false
|
|
149
|
+
})
|
|
150
|
+
).toEqual({
|
|
151
|
+
addColCommas: false
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
81
155
|
it('returns undefined when no formatting overrides were explicitly configured', () => {
|
|
82
156
|
expect(getSeriesColumnFormattingParams({ label: 'Cases' })).toBeUndefined()
|
|
83
157
|
})
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { scaleOrdinal } from '@visx/scale'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { createTileColorScale } from '../smallMultiplesHelpers'
|
|
4
|
+
import { ChartConfig } from '../../types/ChartConfig'
|
|
5
|
+
|
|
6
|
+
const buildConfig = (overrides: Partial<ChartConfig> = {}): ChartConfig =>
|
|
7
|
+
({
|
|
8
|
+
visualizationType: 'Line',
|
|
9
|
+
visualizationSubType: 'regular',
|
|
10
|
+
general: {
|
|
11
|
+
palette: {
|
|
12
|
+
customColors: ['#111111', '#222222'],
|
|
13
|
+
colorAssignmentMode: 'ordered'
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
legend: {},
|
|
17
|
+
smallMultiples: {
|
|
18
|
+
mode: 'by-series',
|
|
19
|
+
colorMode: 'same'
|
|
20
|
+
},
|
|
21
|
+
series: [
|
|
22
|
+
{ dataKey: 'series_a', name: 'Series A', type: 'Line' },
|
|
23
|
+
{ dataKey: 'series_b', name: 'Series B', type: 'Line' }
|
|
24
|
+
],
|
|
25
|
+
runtime: {
|
|
26
|
+
seriesKeys: ['series_a', 'series_b'],
|
|
27
|
+
seriesLabels: {
|
|
28
|
+
series_a: 'Series A',
|
|
29
|
+
series_b: 'Series B'
|
|
30
|
+
},
|
|
31
|
+
seriesLabelsAll: ['Series A', 'Series B']
|
|
32
|
+
},
|
|
33
|
+
...overrides
|
|
34
|
+
} as ChartConfig)
|
|
35
|
+
|
|
36
|
+
describe('smallMultiplesHelpers createTileColorScale', () => {
|
|
37
|
+
it('uses the same first color for by-series tiles in same color mode by default', () => {
|
|
38
|
+
const colorScale = scaleOrdinal({
|
|
39
|
+
domain: ['Series A', 'Series B'],
|
|
40
|
+
range: ['#111111', '#222222'],
|
|
41
|
+
unknown: null
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const tileScale = createTileColorScale(
|
|
45
|
+
{ mode: 'by-series', seriesKey: 'series_b' },
|
|
46
|
+
buildConfig(),
|
|
47
|
+
colorScale,
|
|
48
|
+
1,
|
|
49
|
+
2
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
expect(tileScale('Series A')).toBe('#111111')
|
|
53
|
+
expect(tileScale('Series B')).toBe('#111111')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('lets by-value assignments override same color mode for by-series tiles', () => {
|
|
57
|
+
const colorScale = scaleOrdinal({
|
|
58
|
+
domain: ['Series A', 'Series B'],
|
|
59
|
+
range: ['#aa0000', '#00bb00'],
|
|
60
|
+
unknown: null
|
|
61
|
+
})
|
|
62
|
+
const config = buildConfig({
|
|
63
|
+
general: {
|
|
64
|
+
palette: {
|
|
65
|
+
customColors: ['#111111', '#222222'],
|
|
66
|
+
colorAssignmentMode: 'by-value',
|
|
67
|
+
colorAssignments: [{ key: 'series_b', color: '#00bb00' }]
|
|
68
|
+
}
|
|
69
|
+
} as any
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const tileScale = createTileColorScale({ mode: 'by-series', seriesKey: 'series_b' }, config, colorScale, 1, 2)
|
|
73
|
+
|
|
74
|
+
expect(tileScale('Series A')).toBe('#aa0000')
|
|
75
|
+
expect(tileScale('Series B')).toBe('#00bb00')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('ignores stale by-value assignments when deciding whether to override same color mode', () => {
|
|
79
|
+
const colorScale = scaleOrdinal({
|
|
80
|
+
domain: ['Series A', 'Series B'],
|
|
81
|
+
range: ['#aa0000', '#00bb00'],
|
|
82
|
+
unknown: null
|
|
83
|
+
})
|
|
84
|
+
const config = buildConfig({
|
|
85
|
+
general: {
|
|
86
|
+
palette: {
|
|
87
|
+
customColors: ['#111111', '#222222'],
|
|
88
|
+
colorAssignmentMode: 'by-value',
|
|
89
|
+
colorAssignments: [{ key: 'removed_series', color: '#00bb00' }]
|
|
90
|
+
}
|
|
91
|
+
} as any
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const tileScale = createTileColorScale({ mode: 'by-series', seriesKey: 'series_b' }, config, colorScale, 1, 2)
|
|
95
|
+
|
|
96
|
+
expect(tileScale('Series A')).toBe('#aa0000')
|
|
97
|
+
expect(tileScale('Series B')).toBe('#aa0000')
|
|
98
|
+
})
|
|
99
|
+
})
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import useRightAxis from '../useRightAxis'
|
|
4
|
+
import { createMockConfig } from '../../components/LinearChart/tests/mockConfigContext'
|
|
5
|
+
|
|
6
|
+
const leftSeries = { dataKey: 'Cases', type: 'Bar', axis: 'Left', tooltip: true }
|
|
7
|
+
const rightSeries = { dataKey: 'Rate', type: 'Line', axis: 'Right', tooltip: true }
|
|
8
|
+
|
|
9
|
+
const createComboConfig = () =>
|
|
10
|
+
createMockConfig({
|
|
11
|
+
visualizationType: 'Combo',
|
|
12
|
+
orientation: 'vertical',
|
|
13
|
+
series: [leftSeries, rightSeries],
|
|
14
|
+
yAxis: {
|
|
15
|
+
...createMockConfig().yAxis,
|
|
16
|
+
rightMax: undefined,
|
|
17
|
+
rightMin: undefined,
|
|
18
|
+
smallestRightAxisMax: undefined
|
|
19
|
+
},
|
|
20
|
+
runtime: {
|
|
21
|
+
...createMockConfig().runtime,
|
|
22
|
+
series: [leftSeries, rightSeries],
|
|
23
|
+
seriesKeys: ['Cases', 'Rate'],
|
|
24
|
+
barSeriesKeys: ['Cases'],
|
|
25
|
+
lineSeriesKeys: ['Rate']
|
|
26
|
+
} as any
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe('useRightAxis', () => {
|
|
30
|
+
it('does not render a right axis for vertical Combo charts with only left-axis series', () => {
|
|
31
|
+
const config = createMockConfig({
|
|
32
|
+
visualizationType: 'Combo',
|
|
33
|
+
orientation: 'vertical',
|
|
34
|
+
series: [leftSeries],
|
|
35
|
+
yAxis: {
|
|
36
|
+
...createMockConfig().yAxis,
|
|
37
|
+
rightHideAxis: true
|
|
38
|
+
},
|
|
39
|
+
runtime: {
|
|
40
|
+
...createMockConfig().runtime,
|
|
41
|
+
series: [leftSeries],
|
|
42
|
+
seriesKeys: ['Cases'],
|
|
43
|
+
barSeriesKeys: ['Cases'],
|
|
44
|
+
lineSeriesKeys: []
|
|
45
|
+
} as any
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const result = renderHook(() => useRightAxis({ config, yMax: 100, data: [{ Cases: 5 }] }))
|
|
49
|
+
|
|
50
|
+
expect(result.result.current.hasRightAxis).toBe(false)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('builds the right-axis scale from the supplied domain rows', () => {
|
|
54
|
+
const config = createComboConfig()
|
|
55
|
+
const renderedRows = [{ Cases: 5, Rate: 12 }]
|
|
56
|
+
const stableDomainRows = [
|
|
57
|
+
{ Cases: 5, Rate: 12 },
|
|
58
|
+
{ Cases: 10, Rate: 90 }
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
const renderedResult = renderHook(() => useRightAxis({ config, yMax: 100, data: renderedRows }))
|
|
62
|
+
const stableResult = renderHook(() => useRightAxis({ config, yMax: 100, data: stableDomainRows }))
|
|
63
|
+
|
|
64
|
+
expect(renderedResult.result.current.yScaleRight.domain()).toEqual([0, 12])
|
|
65
|
+
expect(stableResult.result.current.yScaleRight.domain()).toEqual([0, 90])
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('uses clean-top-tick for the automatic right-axis max when shared auto max strategy is enabled', () => {
|
|
69
|
+
const config = {
|
|
70
|
+
...createComboConfig(),
|
|
71
|
+
yAxis: {
|
|
72
|
+
...createComboConfig().yAxis,
|
|
73
|
+
autoMaxStrategy: 'clean-top-tick' as const
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const result = renderHook(() => useRightAxis({ config, yMax: 100, data: [{ Cases: 5, Rate: 101 }] }))
|
|
78
|
+
|
|
79
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([0, 120])
|
|
80
|
+
expect(result.result.current.rightTickValues).toBeUndefined()
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('auto-starts positive right-axis data at zero without depending on series-type runtime keys', () => {
|
|
84
|
+
const config = {
|
|
85
|
+
...createComboConfig(),
|
|
86
|
+
runtime: {
|
|
87
|
+
...createComboConfig().runtime,
|
|
88
|
+
barSeriesKeys: [],
|
|
89
|
+
lineSeriesKeys: []
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const result = renderHook(() => useRightAxis({ config, yMax: 100, data: [{ Cases: 5, Rate: 25 }] }))
|
|
94
|
+
|
|
95
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([0, 25])
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('does not let left-axis negative data affect the right-axis minimum', () => {
|
|
99
|
+
const config = {
|
|
100
|
+
...createComboConfig(),
|
|
101
|
+
yAxis: {
|
|
102
|
+
...createComboConfig().yAxis,
|
|
103
|
+
autoMaxStrategy: 'clean-top-tick' as const
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const result = renderHook(() => useRightAxis({ config, yMax: 100, data: [{ Cases: -100, Rate: 6 }] }))
|
|
108
|
+
|
|
109
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([0, 6])
|
|
110
|
+
expect(result.result.current.rightTickValues).toBeUndefined()
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('honors a positive rightMin below the right-axis data minimum', () => {
|
|
114
|
+
const config = {
|
|
115
|
+
...createComboConfig(),
|
|
116
|
+
yAxis: {
|
|
117
|
+
...createComboConfig().yAxis,
|
|
118
|
+
rightMin: '90',
|
|
119
|
+
rightMax: '100'
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const result = renderHook(() => useRightAxis({ config, yMax: 100, data: [{ Cases: 5, Rate: 95 }] }))
|
|
124
|
+
|
|
125
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([90, 100])
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('uses formatted numeric strings when calculating the right-axis domain', () => {
|
|
129
|
+
const config = createComboConfig()
|
|
130
|
+
|
|
131
|
+
const result = renderHook(() =>
|
|
132
|
+
useRightAxis({
|
|
133
|
+
config,
|
|
134
|
+
yMax: 100,
|
|
135
|
+
data: [
|
|
136
|
+
{ Cases: 5, Rate: '$1,000' },
|
|
137
|
+
{ Cases: 6, Rate: '2,500' }
|
|
138
|
+
]
|
|
139
|
+
})
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([0, 2500])
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('ignores a rightMin above the right-axis data minimum and defaults positive domains to zero', () => {
|
|
146
|
+
const config = {
|
|
147
|
+
...createComboConfig(),
|
|
148
|
+
yAxis: {
|
|
149
|
+
...createComboConfig().yAxis,
|
|
150
|
+
rightMin: '98'
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const result = renderHook(() => useRightAxis({ config, yMax: 100, data: [{ Cases: 5, Rate: 95 }] }))
|
|
155
|
+
|
|
156
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([0, 95])
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('uses a negative minimum when right-axis data is negative', () => {
|
|
160
|
+
const config = createComboConfig()
|
|
161
|
+
|
|
162
|
+
const result = renderHook(() =>
|
|
163
|
+
useRightAxis({
|
|
164
|
+
config,
|
|
165
|
+
yMax: 100,
|
|
166
|
+
data: [
|
|
167
|
+
{ Cases: 5, Rate: -12 },
|
|
168
|
+
{ Cases: 6, Rate: -3 }
|
|
169
|
+
]
|
|
170
|
+
})
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([-12, -3])
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('ignores values that are empty after right-axis numeric cleanup', () => {
|
|
177
|
+
const config = createComboConfig()
|
|
178
|
+
|
|
179
|
+
const result = renderHook(() =>
|
|
180
|
+
useRightAxis({
|
|
181
|
+
config,
|
|
182
|
+
yMax: 100,
|
|
183
|
+
data: [
|
|
184
|
+
{ Cases: 5, Rate: '$' },
|
|
185
|
+
{ Cases: 6, Rate: '-12' }
|
|
186
|
+
]
|
|
187
|
+
})
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([-12, -12])
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it('honors a negative rightMin below the right-axis data minimum', () => {
|
|
194
|
+
const config = {
|
|
195
|
+
...createComboConfig(),
|
|
196
|
+
yAxis: {
|
|
197
|
+
...createComboConfig().yAxis,
|
|
198
|
+
rightMin: '-20'
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const result = renderHook(() =>
|
|
203
|
+
useRightAxis({
|
|
204
|
+
config,
|
|
205
|
+
yMax: 100,
|
|
206
|
+
data: [
|
|
207
|
+
{ Cases: 5, Rate: -12 },
|
|
208
|
+
{ Cases: 6, Rate: -3 }
|
|
209
|
+
]
|
|
210
|
+
})
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([-20, -3])
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('does not round the right-axis max when rightMax is explicit', () => {
|
|
217
|
+
const config = {
|
|
218
|
+
...createComboConfig(),
|
|
219
|
+
yAxis: {
|
|
220
|
+
...createComboConfig().yAxis,
|
|
221
|
+
autoMaxStrategy: 'clean-top-tick' as const,
|
|
222
|
+
rightMax: '100'
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const result = renderHook(() => useRightAxis({ config, yMax: 100, data: [{ Cases: 5, Rate: 25 }] }))
|
|
227
|
+
|
|
228
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([0, 100])
|
|
229
|
+
expect(result.result.current.rightTickValues).toBeUndefined()
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it('uses clean-top-tick when rightMax is invalid', () => {
|
|
233
|
+
const createConfigWithRightMax = (rightMax: string) => ({
|
|
234
|
+
...createComboConfig(),
|
|
235
|
+
yAxis: {
|
|
236
|
+
...createComboConfig().yAxis,
|
|
237
|
+
autoMaxStrategy: 'clean-top-tick' as const,
|
|
238
|
+
rightMax
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
const lowerThanData = renderHook(() =>
|
|
243
|
+
useRightAxis({ config: createConfigWithRightMax('50'), yMax: 100, data: [{ Cases: 5, Rate: 101 }] })
|
|
244
|
+
)
|
|
245
|
+
const nonNumeric = renderHook(() =>
|
|
246
|
+
useRightAxis({ config: createConfigWithRightMax('not-a-number'), yMax: 100, data: [{ Cases: 5, Rate: 101 }] })
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
expect(lowerThanData.result.current.yScaleRight.domain()).toEqual([0, 120])
|
|
250
|
+
expect(lowerThanData.result.current.rightTickValues).toBeUndefined()
|
|
251
|
+
expect(nonNumeric.result.current.yScaleRight.domain()).toEqual([0, 120])
|
|
252
|
+
expect(nonNumeric.result.current.rightTickValues).toBeUndefined()
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
it('ignores a non-numeric rightMin value', () => {
|
|
256
|
+
const config = {
|
|
257
|
+
...createComboConfig(),
|
|
258
|
+
yAxis: {
|
|
259
|
+
...createComboConfig().yAxis,
|
|
260
|
+
rightMin: 'not-a-number'
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const result = renderHook(() => useRightAxis({ config, yMax: 100, data: [{ Cases: 5, Rate: 25 }] }))
|
|
265
|
+
|
|
266
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([0, 25])
|
|
267
|
+
})
|
|
268
|
+
})
|