@cdc/chart 4.26.5 → 4.26.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/CONFIG.md +12 -8
- package/dist/{cdcchart-DQ00cQCm.es.js → cdcchart-BWVIUcAF.es.js} +5 -55
- package/dist/cdcchart.js +34093 -32767
- 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/package.json +3 -3
- package/src/CdcChart.tsx +5 -4
- package/src/CdcChartComponent.tsx +32 -9
- 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/ChartAnnotation.smoke.stories.tsx +10 -0
- 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/EditorPanel/EditorPanel.tsx +367 -136
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +101 -52
- 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/useEditorPermissions.test.tsx +256 -11
- package/src/components/EditorPanel/useEditorPermissions.ts +41 -5
- 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 +2 -2
- 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 +26 -13
- package/src/components/LineChart/index.tsx +21 -4
- package/src/components/LinearChart/tests/LinearChart.test.tsx +30 -0
- package/src/components/LinearChart.tsx +50 -15
- package/src/components/ScatterPlot/ScatterPlot.jsx +3 -3
- 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 +1 -0
- package/src/helpers/calculateLeftYAxisWidth.ts +9 -3
- package/src/helpers/colorAssignmentHelpers.ts +102 -0
- package/src/helpers/dataHelpers.ts +11 -1
- package/src/helpers/getAxisMaxOverride.ts +16 -0
- package/src/helpers/getCleanTopTickMax.ts +64 -0
- package/src/helpers/getColorScale.ts +18 -9
- package/src/helpers/getHasBoundarySuppression.ts +26 -0
- package/src/helpers/getMinMax.ts +24 -22
- package/src/helpers/getYAxisDomainData.ts +38 -0
- package/src/helpers/getYAxisFinalizationEligibility.ts +40 -0
- package/src/helpers/hasSpacedInlineLabel.ts +10 -0
- package/src/helpers/seriesColumnSettings.ts +5 -4
- package/src/helpers/smallMultiplesHelpers.ts +5 -0
- package/src/helpers/tests/calculateLeftYAxisWidth.test.ts +38 -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 +16 -2
- package/src/helpers/tests/smallMultiplesHelpers.test.ts +99 -0
- package/src/hooks/tests/useRightAxis.test.tsx +127 -0
- package/src/hooks/tests/useScales.test.tsx +307 -29
- package/src/hooks/useRightAxis.ts +30 -13
- package/src/hooks/useScales.ts +45 -28
- package/src/hooks/useTooltip.tsx +3 -1
- package/src/test/CdcChart.dataTable.test.tsx +122 -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
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import getMinMax from '../getMinMax'
|
|
3
|
+
import { createMockConfig } from '../../components/LinearChart/tests/mockConfigContext'
|
|
4
|
+
import { ChartConfig } from '../../types/ChartConfig'
|
|
5
|
+
|
|
6
|
+
const leftSeries = { dataKey: 'Cases', type: 'Line', axis: 'Left', tooltip: true }
|
|
7
|
+
const rightSeries = { dataKey: 'Rate', type: 'Line', axis: 'Right', tooltip: true }
|
|
8
|
+
|
|
9
|
+
const createConfig = (overrides: Partial<ChartConfig> = {}) =>
|
|
10
|
+
createMockConfig({
|
|
11
|
+
visualizationType: 'Line',
|
|
12
|
+
visualizationSubType: 'regular',
|
|
13
|
+
yAxis: {
|
|
14
|
+
...createMockConfig().yAxis,
|
|
15
|
+
type: 'linear',
|
|
16
|
+
enablePadding: false,
|
|
17
|
+
scalePadding: 0,
|
|
18
|
+
min: '',
|
|
19
|
+
max: '',
|
|
20
|
+
autoMaxStrategy: 'clean-top-tick'
|
|
21
|
+
},
|
|
22
|
+
runtime: {
|
|
23
|
+
...createMockConfig().runtime,
|
|
24
|
+
yAxis: {
|
|
25
|
+
...createMockConfig().runtime.yAxis,
|
|
26
|
+
min: '',
|
|
27
|
+
max: ''
|
|
28
|
+
},
|
|
29
|
+
series: [leftSeries],
|
|
30
|
+
seriesKeys: ['Cases']
|
|
31
|
+
} as any,
|
|
32
|
+
series: [leftSeries],
|
|
33
|
+
...overrides
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const getResult = (config: ChartConfig, maxValue = 25) =>
|
|
37
|
+
getMinMax({
|
|
38
|
+
config,
|
|
39
|
+
minValue: 0,
|
|
40
|
+
maxValue,
|
|
41
|
+
existPositiveValue: maxValue > 0,
|
|
42
|
+
data: [{ Cases: maxValue }],
|
|
43
|
+
tableData: [{ Cases: maxValue }],
|
|
44
|
+
isAllLine: true
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
describe('getMinMax automatic max strategy', () => {
|
|
48
|
+
it('uses clean-top-tick for automatic left-axis max when enabled', () => {
|
|
49
|
+
expect(getResult(createConfig(), 25).max).toBe(25)
|
|
50
|
+
expect(getResult(createConfig(), 101).max).toBe(120)
|
|
51
|
+
expect(getResult(createConfig(), 1434).max).toBe(1500)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('uses the raw automatic max when strategy is omitted or default', () => {
|
|
55
|
+
expect(getResult(createConfig({ yAxis: { ...createConfig().yAxis, autoMaxStrategy: undefined } }), 101).max).toBe(
|
|
56
|
+
101
|
|
57
|
+
)
|
|
58
|
+
expect(getResult(createConfig({ yAxis: { ...createConfig().yAxis, autoMaxStrategy: 'default' } }), 101).max).toBe(
|
|
59
|
+
101
|
|
60
|
+
)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('bypasses clean-top-tick when the left-axis max is explicit', () => {
|
|
64
|
+
const config = createConfig({
|
|
65
|
+
yAxis: {
|
|
66
|
+
...createConfig().yAxis,
|
|
67
|
+
max: '100'
|
|
68
|
+
},
|
|
69
|
+
runtime: {
|
|
70
|
+
...createConfig().runtime,
|
|
71
|
+
yAxis: {
|
|
72
|
+
...createConfig().runtime.yAxis,
|
|
73
|
+
min: '',
|
|
74
|
+
max: '100'
|
|
75
|
+
}
|
|
76
|
+
} as any
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
expect(getResult(config, 25).max).toBe(100)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('uses clean-top-tick when the entered left-axis max is invalid', () => {
|
|
83
|
+
const createConfigWithRuntimeMax = (max: string) =>
|
|
84
|
+
createConfig({
|
|
85
|
+
yAxis: {
|
|
86
|
+
...createConfig().yAxis,
|
|
87
|
+
max
|
|
88
|
+
},
|
|
89
|
+
runtime: {
|
|
90
|
+
...createConfig().runtime,
|
|
91
|
+
yAxis: {
|
|
92
|
+
...createConfig().runtime.yAxis,
|
|
93
|
+
min: '',
|
|
94
|
+
max
|
|
95
|
+
}
|
|
96
|
+
} as any
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
expect(getResult(createConfigWithRuntimeMax('50'), 101).max).toBe(120)
|
|
100
|
+
expect(getResult(createConfigWithRuntimeMax('not-a-number'), 101).max).toBe(120)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('applies smallestLeftAxisMax as a final author-provided floor without cleaning it', () => {
|
|
104
|
+
const config = createConfig({
|
|
105
|
+
yAxis: {
|
|
106
|
+
...createConfig().yAxis,
|
|
107
|
+
autoMaxStrategy: 'clean-top-tick',
|
|
108
|
+
smallestLeftAxisMax: 12
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
expect(getResult(config, 3).max).toBe(12)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('allows axis padding to increase the domain after the automatic max strategy', () => {
|
|
116
|
+
const config = createConfig({
|
|
117
|
+
yAxis: {
|
|
118
|
+
...createConfig().yAxis,
|
|
119
|
+
autoMaxStrategy: 'clean-top-tick',
|
|
120
|
+
enablePadding: true,
|
|
121
|
+
scalePadding: 10
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
expect(getResult(config, 25).max).toBeCloseTo(27.5)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('uses clean-top-tick for combo left-axis max while leaving right-axis strategy to useRightAxis', () => {
|
|
129
|
+
const config = createConfig({
|
|
130
|
+
visualizationType: 'Combo',
|
|
131
|
+
series: [leftSeries, rightSeries],
|
|
132
|
+
runtime: {
|
|
133
|
+
...createConfig().runtime,
|
|
134
|
+
yAxis: {
|
|
135
|
+
...createConfig().runtime.yAxis,
|
|
136
|
+
min: '',
|
|
137
|
+
max: ''
|
|
138
|
+
},
|
|
139
|
+
series: [leftSeries, rightSeries],
|
|
140
|
+
seriesKeys: ['Cases', 'Rate']
|
|
141
|
+
} as any
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const result = getMinMax({
|
|
145
|
+
config,
|
|
146
|
+
minValue: 0,
|
|
147
|
+
maxValue: 101,
|
|
148
|
+
existPositiveValue: true,
|
|
149
|
+
data: [{ Cases: 101, Rate: 89 }],
|
|
150
|
+
tableData: [{ Cases: 101, Rate: 89 }],
|
|
151
|
+
isAllLine: false
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
expect(result.leftMax).toBe(120)
|
|
155
|
+
expect(result.rightMax).toBe(89)
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('forces a zero min for line charts when boundary suppression is present', () => {
|
|
159
|
+
const config = createConfig({
|
|
160
|
+
visualizationType: 'Line',
|
|
161
|
+
runtime: {
|
|
162
|
+
...createConfig().runtime,
|
|
163
|
+
yAxis: {
|
|
164
|
+
...createConfig().runtime.yAxis,
|
|
165
|
+
min: '',
|
|
166
|
+
max: ''
|
|
167
|
+
}
|
|
168
|
+
} as any
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
const result = getMinMax({
|
|
172
|
+
config,
|
|
173
|
+
minValue: 70,
|
|
174
|
+
maxValue: 120,
|
|
175
|
+
existPositiveValue: true,
|
|
176
|
+
data: [{ Cases: 70 }, { Cases: 120 }],
|
|
177
|
+
tableData: [{ Cases: 70 }, { Cases: 120 }],
|
|
178
|
+
isAllLine: true,
|
|
179
|
+
hasBoundarySuppression: true
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
expect(result.min).toBe(0)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('keeps data min for line charts when boundary suppression is not present', () => {
|
|
186
|
+
const config = createConfig({
|
|
187
|
+
visualizationType: 'Line',
|
|
188
|
+
runtime: {
|
|
189
|
+
...createConfig().runtime,
|
|
190
|
+
yAxis: {
|
|
191
|
+
...createConfig().runtime.yAxis,
|
|
192
|
+
min: '',
|
|
193
|
+
max: ''
|
|
194
|
+
}
|
|
195
|
+
} as any
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
const result = getMinMax({
|
|
199
|
+
config,
|
|
200
|
+
minValue: 70,
|
|
201
|
+
maxValue: 120,
|
|
202
|
+
existPositiveValue: true,
|
|
203
|
+
data: [{ Cases: 70 }, { Cases: 120 }],
|
|
204
|
+
tableData: [{ Cases: 70 }, { Cases: 120 }],
|
|
205
|
+
isAllLine: true,
|
|
206
|
+
hasBoundarySuppression: false
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
expect(result.min).toBe(70)
|
|
210
|
+
})
|
|
211
|
+
})
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { getYAxisDomainData, getYAxisFilterDomainBehavior } from '../getYAxisDomainData'
|
|
3
|
+
import { createMockConfig } from '../../components/LinearChart/tests/mockConfigContext'
|
|
4
|
+
|
|
5
|
+
describe('getYAxisDomainData', () => {
|
|
6
|
+
const filteredRows = [{ value: 3 }]
|
|
7
|
+
const tableRows = [{ value: 3 }, { value: 9 }]
|
|
8
|
+
const postExclusionRows = [{ value: 3 }, { value: 12 }]
|
|
9
|
+
|
|
10
|
+
const buildConfig = ({
|
|
11
|
+
brushActive = false,
|
|
12
|
+
brushDynamicYAxis = false,
|
|
13
|
+
filterDomainBehavior
|
|
14
|
+
}: {
|
|
15
|
+
brushActive?: boolean
|
|
16
|
+
brushDynamicYAxis?: boolean
|
|
17
|
+
filterDomainBehavior?: 'dynamic' | 'stable'
|
|
18
|
+
}) => {
|
|
19
|
+
const baseConfig = createMockConfig()
|
|
20
|
+
return createMockConfig({
|
|
21
|
+
xAxis: {
|
|
22
|
+
...baseConfig.xAxis,
|
|
23
|
+
brushActive,
|
|
24
|
+
brushDynamicYAxis
|
|
25
|
+
},
|
|
26
|
+
yAxis: {
|
|
27
|
+
...baseConfig.yAxis,
|
|
28
|
+
filterDomainBehavior
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
it('defaults omitted filterDomainBehavior to dynamic', () => {
|
|
34
|
+
const config = createMockConfig()
|
|
35
|
+
|
|
36
|
+
expect(getYAxisFilterDomainBehavior(config)).toBe('dynamic')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it.each([
|
|
40
|
+
{
|
|
41
|
+
name: 'brush off with dynamic filter domains',
|
|
42
|
+
configOptions: { brushActive: false, filterDomainBehavior: 'dynamic' as const },
|
|
43
|
+
expectedRows: filteredRows
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'brush off with stable filter domains',
|
|
47
|
+
configOptions: { brushActive: false, filterDomainBehavior: 'stable' as const },
|
|
48
|
+
expectedRows: postExclusionRows
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'dynamic brush Y-axis with dynamic filter domains',
|
|
52
|
+
configOptions: { brushActive: true, brushDynamicYAxis: true, filterDomainBehavior: 'dynamic' as const },
|
|
53
|
+
expectedRows: filteredRows
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'dynamic brush Y-axis with stable filter domains',
|
|
57
|
+
configOptions: { brushActive: true, brushDynamicYAxis: true, filterDomainBehavior: 'stable' as const },
|
|
58
|
+
expectedRows: filteredRows
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'non-dynamic brush Y-axis with dynamic filter domains',
|
|
62
|
+
configOptions: { brushActive: true, brushDynamicYAxis: false, filterDomainBehavior: 'dynamic' as const },
|
|
63
|
+
expectedRows: tableRows
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'non-dynamic brush Y-axis with stable filter domains',
|
|
67
|
+
configOptions: { brushActive: true, brushDynamicYAxis: false, filterDomainBehavior: 'stable' as const },
|
|
68
|
+
expectedRows: postExclusionRows
|
|
69
|
+
}
|
|
70
|
+
])('selects the expected domain data for $name', ({ configOptions, expectedRows }) => {
|
|
71
|
+
expect(
|
|
72
|
+
getYAxisDomainData({
|
|
73
|
+
config: buildConfig(configOptions),
|
|
74
|
+
data: filteredRows,
|
|
75
|
+
tableData: tableRows,
|
|
76
|
+
fullEligibleDomainData: postExclusionRows
|
|
77
|
+
})
|
|
78
|
+
).toBe(expectedRows)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('falls back to rendered data for non-dynamic brush domains when table data is unavailable', () => {
|
|
82
|
+
expect(
|
|
83
|
+
getYAxisDomainData({
|
|
84
|
+
config: buildConfig({
|
|
85
|
+
brushActive: true,
|
|
86
|
+
brushDynamicYAxis: false,
|
|
87
|
+
filterDomainBehavior: 'dynamic'
|
|
88
|
+
}),
|
|
89
|
+
data: filteredRows
|
|
90
|
+
})
|
|
91
|
+
).toBe(filteredRows)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('falls back to rendered data for stable filter domains when full eligible data is unavailable', () => {
|
|
95
|
+
expect(
|
|
96
|
+
getYAxisDomainData({
|
|
97
|
+
config: buildConfig({
|
|
98
|
+
filterDomainBehavior: 'stable'
|
|
99
|
+
}),
|
|
100
|
+
data: filteredRows,
|
|
101
|
+
tableData: tableRows
|
|
102
|
+
})
|
|
103
|
+
).toBe(filteredRows)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('falls back to table data for non-dynamic brush stable domains when full eligible data is unavailable', () => {
|
|
107
|
+
expect(
|
|
108
|
+
getYAxisDomainData({
|
|
109
|
+
config: buildConfig({
|
|
110
|
+
brushActive: true,
|
|
111
|
+
brushDynamicYAxis: false,
|
|
112
|
+
filterDomainBehavior: 'stable'
|
|
113
|
+
}),
|
|
114
|
+
data: filteredRows,
|
|
115
|
+
tableData: tableRows
|
|
116
|
+
})
|
|
117
|
+
).toBe(tableRows)
|
|
118
|
+
})
|
|
119
|
+
})
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { createMockConfig } from '../../components/LinearChart/tests/mockConfigContext'
|
|
3
|
+
import { getYAxisFinalizationEligibility } from '../getYAxisFinalizationEligibility'
|
|
4
|
+
|
|
5
|
+
const getEligibility = (overrides = {}) =>
|
|
6
|
+
getYAxisFinalizationEligibility({
|
|
7
|
+
config: createMockConfig(overrides),
|
|
8
|
+
hasSpacedInlineLabel: false,
|
|
9
|
+
hasValidExplicitLeftMax: false,
|
|
10
|
+
isHorizontal: false
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
describe('getYAxisFinalizationEligibility', () => {
|
|
14
|
+
it('finalizes automatic clean-top-tick linear Y axes', () => {
|
|
15
|
+
const result = getEligibility({
|
|
16
|
+
yAxis: {
|
|
17
|
+
...createMockConfig().yAxis,
|
|
18
|
+
type: 'linear',
|
|
19
|
+
autoMaxStrategy: 'clean-top-tick'
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
expect(result.shouldUseCleanTopTick).toBe(true)
|
|
24
|
+
expect(result.shouldFinalizePrimaryYAxis).toBe(true)
|
|
25
|
+
expect(result.shouldUseInlineLabelHeadroom).toBe(false)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('uses spaced inline labels as headroom for standard non-Combo Y axes', () => {
|
|
29
|
+
const result = getYAxisFinalizationEligibility({
|
|
30
|
+
config: createMockConfig({
|
|
31
|
+
yAxis: {
|
|
32
|
+
...createMockConfig().yAxis,
|
|
33
|
+
type: 'linear',
|
|
34
|
+
autoMaxStrategy: 'default',
|
|
35
|
+
inlineLabel: ' percent'
|
|
36
|
+
}
|
|
37
|
+
}),
|
|
38
|
+
hasSpacedInlineLabel: true,
|
|
39
|
+
hasValidExplicitLeftMax: false,
|
|
40
|
+
isHorizontal: false
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
expect(result.shouldUseInlineLabelHeadroom).toBe(true)
|
|
44
|
+
expect(result.shouldUseCleanTopTick).toBe(true)
|
|
45
|
+
expect(result.shouldFinalizePrimaryYAxis).toBe(true)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('does not use spaced inline labels as headroom for Combo charts', () => {
|
|
49
|
+
const result = getYAxisFinalizationEligibility({
|
|
50
|
+
config: createMockConfig({
|
|
51
|
+
visualizationType: 'Combo',
|
|
52
|
+
yAxis: {
|
|
53
|
+
...createMockConfig().yAxis,
|
|
54
|
+
type: 'linear',
|
|
55
|
+
autoMaxStrategy: 'default',
|
|
56
|
+
inlineLabel: ' percent'
|
|
57
|
+
}
|
|
58
|
+
}),
|
|
59
|
+
hasSpacedInlineLabel: true,
|
|
60
|
+
hasValidExplicitLeftMax: false,
|
|
61
|
+
isHorizontal: false
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
expect(result.supportsInlineLabelHeadroom).toBe(false)
|
|
65
|
+
expect(result.shouldUseInlineLabelHeadroom).toBe(false)
|
|
66
|
+
expect(result.shouldFinalizePrimaryYAxis).toBe(false)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('still finalizes Combo charts when clean-top-tick is explicitly enabled', () => {
|
|
70
|
+
const result = getEligibility({
|
|
71
|
+
visualizationType: 'Combo',
|
|
72
|
+
yAxis: {
|
|
73
|
+
...createMockConfig().yAxis,
|
|
74
|
+
type: 'linear',
|
|
75
|
+
autoMaxStrategy: 'clean-top-tick'
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
expect(result.shouldUseCleanTopTick).toBe(true)
|
|
80
|
+
expect(result.shouldFinalizePrimaryYAxis).toBe(true)
|
|
81
|
+
expect(result.shouldUseInlineLabelHeadroom).toBe(false)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('treats a valid explicit left max as a full finalization bypass', () => {
|
|
85
|
+
const result = getYAxisFinalizationEligibility({
|
|
86
|
+
config: createMockConfig({
|
|
87
|
+
yAxis: {
|
|
88
|
+
...createMockConfig().yAxis,
|
|
89
|
+
type: 'linear',
|
|
90
|
+
autoMaxStrategy: 'clean-top-tick',
|
|
91
|
+
inlineLabel: ' percent'
|
|
92
|
+
}
|
|
93
|
+
}),
|
|
94
|
+
hasSpacedInlineLabel: true,
|
|
95
|
+
hasValidExplicitLeftMax: true,
|
|
96
|
+
isHorizontal: false
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
expect(result.shouldUseCleanTopTick).toBe(false)
|
|
100
|
+
expect(result.shouldFinalizePrimaryYAxis).toBe(false)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('does not finalize horizontal, categorical, logarithmic, or special chart Y axes', () => {
|
|
104
|
+
expect(
|
|
105
|
+
getYAxisFinalizationEligibility({
|
|
106
|
+
config: createMockConfig({
|
|
107
|
+
yAxis: {
|
|
108
|
+
...createMockConfig().yAxis,
|
|
109
|
+
type: 'linear',
|
|
110
|
+
autoMaxStrategy: 'clean-top-tick'
|
|
111
|
+
}
|
|
112
|
+
}),
|
|
113
|
+
hasSpacedInlineLabel: false,
|
|
114
|
+
hasValidExplicitLeftMax: false,
|
|
115
|
+
isHorizontal: true
|
|
116
|
+
}).shouldFinalizePrimaryYAxis
|
|
117
|
+
).toBe(false)
|
|
118
|
+
|
|
119
|
+
expect(
|
|
120
|
+
getEligibility({
|
|
121
|
+
yAxis: {
|
|
122
|
+
...createMockConfig().yAxis,
|
|
123
|
+
type: 'categorical',
|
|
124
|
+
autoMaxStrategy: 'clean-top-tick'
|
|
125
|
+
}
|
|
126
|
+
}).shouldFinalizePrimaryYAxis
|
|
127
|
+
).toBe(false)
|
|
128
|
+
|
|
129
|
+
expect(
|
|
130
|
+
getEligibility({
|
|
131
|
+
yAxis: {
|
|
132
|
+
...createMockConfig().yAxis,
|
|
133
|
+
type: 'logarithmic',
|
|
134
|
+
autoMaxStrategy: 'clean-top-tick'
|
|
135
|
+
}
|
|
136
|
+
}).shouldFinalizePrimaryYAxis
|
|
137
|
+
).toBe(false)
|
|
138
|
+
|
|
139
|
+
expect(
|
|
140
|
+
getEligibility({
|
|
141
|
+
visualizationType: 'Box Plot',
|
|
142
|
+
yAxis: {
|
|
143
|
+
...createMockConfig().yAxis,
|
|
144
|
+
type: 'linear',
|
|
145
|
+
autoMaxStrategy: 'clean-top-tick'
|
|
146
|
+
}
|
|
147
|
+
}).shouldFinalizePrimaryYAxis
|
|
148
|
+
).toBe(false)
|
|
149
|
+
|
|
150
|
+
expect(
|
|
151
|
+
getEligibility({
|
|
152
|
+
visualizationType: 'Bump Chart',
|
|
153
|
+
yAxis: {
|
|
154
|
+
...createMockConfig().yAxis,
|
|
155
|
+
type: 'linear',
|
|
156
|
+
autoMaxStrategy: 'clean-top-tick'
|
|
157
|
+
}
|
|
158
|
+
}).shouldFinalizePrimaryYAxis
|
|
159
|
+
).toBe(false)
|
|
160
|
+
|
|
161
|
+
expect(
|
|
162
|
+
getEligibility({
|
|
163
|
+
visualizationType: 'Forest Plot',
|
|
164
|
+
yAxis: {
|
|
165
|
+
...createMockConfig().yAxis,
|
|
166
|
+
type: 'linear',
|
|
167
|
+
autoMaxStrategy: 'clean-top-tick'
|
|
168
|
+
}
|
|
169
|
+
}).shouldFinalizePrimaryYAxis
|
|
170
|
+
).toBe(false)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('treats omitted legacy Y-axis type as linear', () => {
|
|
174
|
+
const result = getEligibility({
|
|
175
|
+
yAxis: {
|
|
176
|
+
...createMockConfig().yAxis,
|
|
177
|
+
type: undefined,
|
|
178
|
+
autoMaxStrategy: 'clean-top-tick'
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
expect(result.hasStandardLinearPrimaryYAxis).toBe(true)
|
|
183
|
+
expect(result.shouldFinalizePrimaryYAxis).toBe(true)
|
|
184
|
+
})
|
|
185
|
+
})
|
|
@@ -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
|
})
|
|
@@ -65,19 +65,33 @@ describe('seriesColumnSettings', () => {
|
|
|
65
65
|
it('only returns explicit formatting overrides and preserves falsey values', () => {
|
|
66
66
|
expect(
|
|
67
67
|
getSeriesColumnFormattingParams({
|
|
68
|
-
prefix: '',
|
|
68
|
+
prefix: '$',
|
|
69
69
|
suffix: ' units',
|
|
70
70
|
roundToPlace: 0,
|
|
71
71
|
commas: false
|
|
72
72
|
})
|
|
73
73
|
).toEqual({
|
|
74
|
-
addColPrefix: '',
|
|
74
|
+
addColPrefix: '$',
|
|
75
75
|
addColSuffix: ' units',
|
|
76
76
|
addColRoundTo: 0,
|
|
77
77
|
addColCommas: false
|
|
78
78
|
})
|
|
79
79
|
})
|
|
80
80
|
|
|
81
|
+
it('ignores blank prefix and suffix overrides so global formatting can apply', () => {
|
|
82
|
+
expect(
|
|
83
|
+
getSeriesColumnFormattingParams({
|
|
84
|
+
prefix: '',
|
|
85
|
+
suffix: '',
|
|
86
|
+
roundToPlace: 0,
|
|
87
|
+
commas: false
|
|
88
|
+
})
|
|
89
|
+
).toEqual({
|
|
90
|
+
addColRoundTo: 0,
|
|
91
|
+
addColCommas: false
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
81
95
|
it('returns undefined when no formatting overrides were explicitly configured', () => {
|
|
82
96
|
expect(getSeriesColumnFormattingParams({ label: 'Cases' })).toBeUndefined()
|
|
83
97
|
})
|