@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
|
@@ -6,9 +6,12 @@ import { useEditorPermissions } from './useEditorPermissions'
|
|
|
6
6
|
import { createMockChartContext, createMockConfig } from '../LinearChart/tests/mockConfigContext'
|
|
7
7
|
import { ChartConfig } from '../../types/ChartConfig'
|
|
8
8
|
|
|
9
|
-
const renderUseEditorPermissions = (
|
|
9
|
+
const renderUseEditorPermissions = (
|
|
10
|
+
configOverrides: Partial<ChartConfig>,
|
|
11
|
+
contextOverrides: Partial<ReturnType<typeof createMockChartContext>> = {}
|
|
12
|
+
) => {
|
|
10
13
|
const config = createMockConfig(configOverrides)
|
|
11
|
-
const context = createMockChartContext(config)
|
|
14
|
+
const context = createMockChartContext(config, contextOverrides)
|
|
12
15
|
const wrapper = ({ children }: React.PropsWithChildren) => (
|
|
13
16
|
<ConfigContext.Provider value={context}>{children}</ConfigContext.Provider>
|
|
14
17
|
)
|
|
@@ -17,7 +20,7 @@ const renderUseEditorPermissions = (configOverrides: Partial<ChartConfig>) => {
|
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
describe('useEditorPermissions', () => {
|
|
20
|
-
it('shows manual y-axis padding controls when
|
|
23
|
+
it('shows manual y-axis padding controls when there is no spaced inline label', () => {
|
|
21
24
|
const { result } = renderUseEditorPermissions({
|
|
22
25
|
yAxis: {
|
|
23
26
|
...createMockConfig().yAxis,
|
|
@@ -29,7 +32,7 @@ describe('useEditorPermissions', () => {
|
|
|
29
32
|
expect(result.current.visSupportsYPadding()).toBe(true)
|
|
30
33
|
})
|
|
31
34
|
|
|
32
|
-
it('hides manual y-axis padding controls for inline
|
|
35
|
+
it('hides manual y-axis padding controls for spaced inline labels when renderer headroom applies', () => {
|
|
33
36
|
const { result } = renderUseEditorPermissions({
|
|
34
37
|
yAxis: {
|
|
35
38
|
...createMockConfig().yAxis,
|
|
@@ -41,22 +44,34 @@ describe('useEditorPermissions', () => {
|
|
|
41
44
|
expect(result.current.visSupportsYPadding()).toBe(false)
|
|
42
45
|
})
|
|
43
46
|
|
|
44
|
-
it('
|
|
47
|
+
it('shows manual y-axis padding controls for horizontal charts with spaced inline labels', () => {
|
|
45
48
|
const { result } = renderUseEditorPermissions({
|
|
49
|
+
orientation: 'horizontal',
|
|
46
50
|
yAxis: {
|
|
47
51
|
...createMockConfig().yAxis,
|
|
48
|
-
titlePlacement: '
|
|
49
|
-
|
|
50
|
-
inlineLabel: ''
|
|
52
|
+
titlePlacement: 'side',
|
|
53
|
+
inlineLabel: 'per 100k'
|
|
51
54
|
}
|
|
52
55
|
})
|
|
53
56
|
|
|
54
|
-
expect(result.current.visSupportsYPadding()).toBe(
|
|
57
|
+
expect(result.current.visSupportsYPadding()).toBe(true)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('shows manual y-axis padding controls for Combo charts with spaced inline labels', () => {
|
|
61
|
+
const { result } = renderUseEditorPermissions({
|
|
62
|
+
visualizationType: 'Combo',
|
|
63
|
+
yAxis: {
|
|
64
|
+
...createMockConfig().yAxis,
|
|
65
|
+
titlePlacement: 'side',
|
|
66
|
+
inlineLabel: 'per 100k'
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
expect(result.current.visSupportsYPadding()).toBe(true)
|
|
55
71
|
})
|
|
56
72
|
|
|
57
|
-
it('shows manual y-axis padding controls
|
|
73
|
+
it('shows manual y-axis padding controls for top y-axis titles', () => {
|
|
58
74
|
const { result } = renderUseEditorPermissions({
|
|
59
|
-
hideYAxisLabel: true,
|
|
60
75
|
yAxis: {
|
|
61
76
|
...createMockConfig().yAxis,
|
|
62
77
|
titlePlacement: 'top',
|
|
@@ -67,4 +82,266 @@ describe('useEditorPermissions', () => {
|
|
|
67
82
|
|
|
68
83
|
expect(result.current.visSupportsYPadding()).toBe(true)
|
|
69
84
|
})
|
|
85
|
+
|
|
86
|
+
it('supports series color assignments only for eligible authored-series charts', () => {
|
|
87
|
+
const supported = renderUseEditorPermissions({
|
|
88
|
+
visualizationType: 'Line',
|
|
89
|
+
series: [{ dataKey: 'cases', type: 'Line' }] as any
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
expect(supported.result.current.visSupportsSeriesColorAssignments()).toBe(true)
|
|
93
|
+
|
|
94
|
+
const unsupported = renderUseEditorPermissions({
|
|
95
|
+
visualizationType: 'Pie',
|
|
96
|
+
series: [{ dataKey: 'cases', type: 'Pie' }] as any
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
expect(unsupported.result.current.visSupportsSeriesColorAssignments()).toBe(false)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('shows the data-label control for pie charts', () => {
|
|
103
|
+
const { result } = renderUseEditorPermissions({
|
|
104
|
+
visualizationType: 'Pie'
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
expect(result.current.visHasLabelOnData()).toBe(true)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('shows right value axis controls for vertical Combo charts without requiring a right-axis series', () => {
|
|
111
|
+
const { result } = renderUseEditorPermissions({
|
|
112
|
+
visualizationType: 'Combo',
|
|
113
|
+
orientation: 'vertical',
|
|
114
|
+
series: [{ dataKey: 'Cases', type: 'Bar', axis: 'Left' }] as any
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
expect(result.current.visSupportsRightValueAxis()).toBe(true)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('hides right value axis controls outside vertical Combo charts', () => {
|
|
121
|
+
const horizontalCombo = renderUseEditorPermissions({
|
|
122
|
+
visualizationType: 'Combo',
|
|
123
|
+
orientation: 'horizontal'
|
|
124
|
+
})
|
|
125
|
+
const verticalBar = renderUseEditorPermissions({
|
|
126
|
+
visualizationType: 'Bar',
|
|
127
|
+
orientation: 'vertical'
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
expect(horizontalCombo.result.current.visSupportsRightValueAxis()).toBe(false)
|
|
131
|
+
expect(verticalBar.result.current.visSupportsRightValueAxis()).toBe(false)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('shows filter-domain behavior when a standalone chart has visible filters and automatic max', () => {
|
|
135
|
+
const { result } = renderUseEditorPermissions({
|
|
136
|
+
filters: [{ columnName: 'Region', showDropdown: true } as any],
|
|
137
|
+
yAxis: {
|
|
138
|
+
...createMockConfig().yAxis,
|
|
139
|
+
max: '',
|
|
140
|
+
type: 'linear'
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
expect(result.current.visSupportsFilterDomainBehavior()).toBe(true)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('shows filter-domain behavior for horizontal bar charts when automatic max can respond to filters', () => {
|
|
148
|
+
const { result } = renderUseEditorPermissions({
|
|
149
|
+
visualizationType: 'Bar',
|
|
150
|
+
orientation: 'horizontal',
|
|
151
|
+
filters: [{ columnName: 'Region', showDropdown: true } as any],
|
|
152
|
+
yAxis: {
|
|
153
|
+
...createMockConfig().yAxis,
|
|
154
|
+
max: '',
|
|
155
|
+
type: 'linear'
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
expect(result.current.visSupportsFilterDomainBehavior()).toBe(true)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('shows filter-domain behavior for dashboard charts without local filters', () => {
|
|
163
|
+
const { result } = renderUseEditorPermissions(
|
|
164
|
+
{
|
|
165
|
+
filters: [],
|
|
166
|
+
yAxis: {
|
|
167
|
+
...createMockConfig().yAxis,
|
|
168
|
+
max: '',
|
|
169
|
+
type: 'linear'
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
{ isDashboard: true }
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
expect(result.current.visSupportsFilterDomainBehavior()).toBe(true)
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('hides filter-domain behavior when vertical value-axis max is explicit', () => {
|
|
179
|
+
const { result } = renderUseEditorPermissions(
|
|
180
|
+
{
|
|
181
|
+
filters: [{ columnName: 'Region', showDropdown: true } as any],
|
|
182
|
+
yAxis: {
|
|
183
|
+
...createMockConfig().yAxis,
|
|
184
|
+
max: '100',
|
|
185
|
+
type: 'linear'
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
{ isDashboard: true }
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
expect(result.current.visSupportsFilterDomainBehavior()).toBe(false)
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('hides filter-domain behavior for horizontal charts when value-axis max is explicit', () => {
|
|
195
|
+
const { result } = renderUseEditorPermissions(
|
|
196
|
+
{
|
|
197
|
+
visualizationType: 'Bar',
|
|
198
|
+
orientation: 'horizontal',
|
|
199
|
+
filters: [{ columnName: 'Region', showDropdown: true } as any],
|
|
200
|
+
xAxis: {
|
|
201
|
+
...createMockConfig().xAxis,
|
|
202
|
+
max: '100'
|
|
203
|
+
},
|
|
204
|
+
yAxis: {
|
|
205
|
+
...createMockConfig().yAxis,
|
|
206
|
+
max: '',
|
|
207
|
+
type: 'linear'
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
{ isDashboard: true }
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
expect(result.current.visSupportsFilterDomainBehavior()).toBe(false)
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('hides filter-domain behavior for unfiltered standalone charts', () => {
|
|
217
|
+
const { result } = renderUseEditorPermissions({
|
|
218
|
+
filters: [],
|
|
219
|
+
yAxis: {
|
|
220
|
+
...createMockConfig().yAxis,
|
|
221
|
+
max: '',
|
|
222
|
+
type: 'linear'
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
expect(result.current.visSupportsFilterDomainBehavior()).toBe(false)
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('hides filter-domain behavior for charts outside the value-axis allowlist', () => {
|
|
230
|
+
const { result } = renderUseEditorPermissions(
|
|
231
|
+
{
|
|
232
|
+
visualizationType: 'Bump Chart',
|
|
233
|
+
filters: [{ columnName: 'Region', showDropdown: true } as any],
|
|
234
|
+
yAxis: {
|
|
235
|
+
...createMockConfig().yAxis,
|
|
236
|
+
max: '',
|
|
237
|
+
type: 'linear'
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
{ isDashboard: true }
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
expect(result.current.visSupportsFilterDomainBehavior()).toBe(false)
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it('hides filter-domain behavior for chart types with specialized value scales', () => {
|
|
247
|
+
const { result } = renderUseEditorPermissions(
|
|
248
|
+
{
|
|
249
|
+
visualizationType: 'Paired Bar',
|
|
250
|
+
filters: [{ columnName: 'Region', showDropdown: true } as any],
|
|
251
|
+
yAxis: {
|
|
252
|
+
...createMockConfig().yAxis,
|
|
253
|
+
max: '',
|
|
254
|
+
type: 'linear'
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
{ isDashboard: true }
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
expect(result.current.visSupportsFilterDomainBehavior()).toBe(false)
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
it('shows automatic max strategy for automatic numeric value axes', () => {
|
|
264
|
+
const { result } = renderUseEditorPermissions({
|
|
265
|
+
yAxis: {
|
|
266
|
+
...createMockConfig().yAxis,
|
|
267
|
+
max: '',
|
|
268
|
+
type: 'linear'
|
|
269
|
+
}
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
expect(result.current.visSupportsAutoMaxStrategy()).toBe(true)
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
it('shows automatic max strategy for horizontal charts with automatic value axes', () => {
|
|
276
|
+
const { result } = renderUseEditorPermissions({
|
|
277
|
+
visualizationType: 'Bar',
|
|
278
|
+
orientation: 'horizontal',
|
|
279
|
+
xAxis: {
|
|
280
|
+
...createMockConfig().xAxis,
|
|
281
|
+
max: ''
|
|
282
|
+
},
|
|
283
|
+
yAxis: {
|
|
284
|
+
...createMockConfig().yAxis,
|
|
285
|
+
max: '',
|
|
286
|
+
type: 'linear'
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
expect(result.current.visSupportsAutoMaxStrategy()).toBe(true)
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('hides automatic max strategy when vertical value-axis max is explicit', () => {
|
|
294
|
+
const { result } = renderUseEditorPermissions({
|
|
295
|
+
yAxis: {
|
|
296
|
+
...createMockConfig().yAxis,
|
|
297
|
+
max: '100',
|
|
298
|
+
type: 'linear'
|
|
299
|
+
}
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
expect(result.current.visSupportsAutoMaxStrategy()).toBe(false)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('hides automatic max strategy for horizontal charts when value-axis max is explicit', () => {
|
|
306
|
+
const { result } = renderUseEditorPermissions({
|
|
307
|
+
visualizationType: 'Bar',
|
|
308
|
+
orientation: 'horizontal',
|
|
309
|
+
xAxis: {
|
|
310
|
+
...createMockConfig().xAxis,
|
|
311
|
+
max: '100'
|
|
312
|
+
},
|
|
313
|
+
yAxis: {
|
|
314
|
+
...createMockConfig().yAxis,
|
|
315
|
+
max: '',
|
|
316
|
+
type: 'linear'
|
|
317
|
+
}
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
expect(result.current.visSupportsAutoMaxStrategy()).toBe(false)
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('hides automatic max strategy for categorical value axes', () => {
|
|
324
|
+
const { result } = renderUseEditorPermissions({
|
|
325
|
+
yAxis: {
|
|
326
|
+
...createMockConfig().yAxis,
|
|
327
|
+
max: '',
|
|
328
|
+
type: 'categorical'
|
|
329
|
+
}
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
expect(result.current.visSupportsAutoMaxStrategy()).toBe(false)
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
it('hides automatic max strategy for chart types with specialized value scales', () => {
|
|
336
|
+
const { result } = renderUseEditorPermissions({
|
|
337
|
+
visualizationType: 'Paired Bar',
|
|
338
|
+
yAxis: {
|
|
339
|
+
...createMockConfig().yAxis,
|
|
340
|
+
max: '',
|
|
341
|
+
type: 'linear'
|
|
342
|
+
}
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
expect(result.current.visSupportsAutoMaxStrategy()).toBe(false)
|
|
346
|
+
})
|
|
70
347
|
})
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import React, { useContext } from 'react'
|
|
2
2
|
import ConfigContext from '../../ConfigContext'
|
|
3
|
-
import {
|
|
3
|
+
import { getYAxisFinalizationEligibility } from '../../helpers/getYAxisFinalizationEligibility'
|
|
4
|
+
import { hasSpacedInlineLabel } from '../../helpers/hasSpacedInlineLabel'
|
|
5
|
+
import { supportsSeriesColorAssignments } from '../../helpers/colorAssignmentHelpers'
|
|
6
|
+
import { hasVisibleVizFilters } from '@cdc/core/helpers/filterVisibility'
|
|
4
7
|
|
|
5
8
|
export const useEditorPermissions = () => {
|
|
6
|
-
const { config } = useContext(ConfigContext)
|
|
9
|
+
const { config, isDashboard } = useContext(ConfigContext)
|
|
7
10
|
const { visualizationType, series, orientation, visualizationSubType } = config
|
|
8
11
|
|
|
9
12
|
// Overall support for the chart types
|
|
@@ -79,7 +82,6 @@ export const useEditorPermissions = () => {
|
|
|
79
82
|
'Forest Plot',
|
|
80
83
|
'HeatMap',
|
|
81
84
|
'Horizon Chart',
|
|
82
|
-
'Pie',
|
|
83
85
|
'Radar',
|
|
84
86
|
'Sankey',
|
|
85
87
|
'Scatter Plot',
|
|
@@ -254,13 +256,13 @@ export const useEditorPermissions = () => {
|
|
|
254
256
|
}
|
|
255
257
|
|
|
256
258
|
const visSupportsDateCategoryAxisLine = () => {
|
|
257
|
-
const disabledCharts = ['Forest Plot', '
|
|
259
|
+
const disabledCharts = ['Forest Plot', 'Spark Line', 'Warming Stripes']
|
|
258
260
|
if (disabledCharts.includes(visualizationType)) return false
|
|
259
261
|
return true
|
|
260
262
|
}
|
|
261
263
|
|
|
262
264
|
const visSupportsDateCategoryAxisTicks = () => {
|
|
263
|
-
const disabledCharts = ['Forest Plot', '
|
|
265
|
+
const disabledCharts = ['Forest Plot', 'Spark Line', 'Warming Stripes']
|
|
264
266
|
if (disabledCharts.includes(visualizationType)) return false
|
|
265
267
|
return true
|
|
266
268
|
}
|
|
@@ -317,6 +319,27 @@ export const useEditorPermissions = () => {
|
|
|
317
319
|
return true
|
|
318
320
|
}
|
|
319
321
|
|
|
322
|
+
const hasExplicitValueAxisMax = () => {
|
|
323
|
+
const valueAxisMax = orientation === 'horizontal' ? config.xAxis?.max : config.yAxis?.max
|
|
324
|
+
return valueAxisMax !== undefined && valueAxisMax !== null && valueAxisMax !== ''
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const visSupportsAutomaticValueDomain = () => {
|
|
328
|
+
const enabledCharts = ['Area Chart', 'Bar', 'Combo', 'Deviation Bar', 'Forecasting', 'Line', 'Scatter Plot']
|
|
329
|
+
if (!enabledCharts.includes(visualizationType)) return false
|
|
330
|
+
if (config.yAxis?.type === 'categorical') return false
|
|
331
|
+
if (hasExplicitValueAxisMax()) return false
|
|
332
|
+
return true
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const visSupportsAutoMaxStrategy = () => {
|
|
336
|
+
return visSupportsAutomaticValueDomain()
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const visSupportsFilterDomainBehavior = () => {
|
|
340
|
+
return visSupportsAutomaticValueDomain() && Boolean(isDashboard || hasVisibleVizFilters(config.filters))
|
|
341
|
+
}
|
|
342
|
+
|
|
320
343
|
const visSupportsValueAxisGridLines = () => {
|
|
321
344
|
const disabledCharts = ['Forest Plot', 'HeatMap']
|
|
322
345
|
if (disabledCharts.includes(visualizationType)) return false
|
|
@@ -375,6 +398,10 @@ export const useEditorPermissions = () => {
|
|
|
375
398
|
return true
|
|
376
399
|
}
|
|
377
400
|
|
|
401
|
+
const visSupportsRightValueAxis = () => {
|
|
402
|
+
return visualizationType === 'Combo' && orientation === 'vertical'
|
|
403
|
+
}
|
|
404
|
+
|
|
378
405
|
const visSupportsRankByValue = () => {
|
|
379
406
|
const disabledCharts = ['HeatMap', 'Spark Line']
|
|
380
407
|
if (disabledCharts.includes(visualizationType)) return false
|
|
@@ -426,8 +453,17 @@ export const useEditorPermissions = () => {
|
|
|
426
453
|
return false
|
|
427
454
|
}
|
|
428
455
|
|
|
456
|
+
const visSupportsSeriesColorAssignments = () => supportsSeriesColorAssignments(config)
|
|
457
|
+
|
|
429
458
|
const visSupportsYPadding = () => {
|
|
430
|
-
|
|
459
|
+
const { shouldUseInlineLabelHeadroom } = getYAxisFinalizationEligibility({
|
|
460
|
+
config,
|
|
461
|
+
hasSpacedInlineLabel: hasSpacedInlineLabel(config),
|
|
462
|
+
hasValidExplicitLeftMax: false,
|
|
463
|
+
isHorizontal: orientation === 'horizontal'
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
return !shouldUseInlineLabelHeadroom
|
|
431
467
|
}
|
|
432
468
|
|
|
433
469
|
const visHasSingleSeriesTooltip = () => {
|
|
@@ -486,6 +522,8 @@ export const useEditorPermissions = () => {
|
|
|
486
522
|
visSupportsDateCategoryTickRotation,
|
|
487
523
|
visSupportsDynamicSeries,
|
|
488
524
|
visSupportsFilters,
|
|
525
|
+
visSupportsAutoMaxStrategy,
|
|
526
|
+
visSupportsFilterDomainBehavior,
|
|
489
527
|
visSupportsFootnotes,
|
|
490
528
|
visSupportsLeftValueAxis,
|
|
491
529
|
visSupportsMobileChartHeight,
|
|
@@ -495,8 +533,10 @@ export const useEditorPermissions = () => {
|
|
|
495
533
|
visSupportsReactTooltip,
|
|
496
534
|
visSupportsRegions,
|
|
497
535
|
visSupportsResponsiveTicks,
|
|
536
|
+
visSupportsRightValueAxis,
|
|
498
537
|
visSupportsReverseColorPalette,
|
|
499
538
|
visSupportsSequentialPallete,
|
|
539
|
+
visSupportsSeriesColorAssignments,
|
|
500
540
|
visSupportsSmallMultiples,
|
|
501
541
|
visSupportsSuperTitle,
|
|
502
542
|
visSupportsTooltipLines,
|
|
@@ -64,7 +64,7 @@ type HeatMapLayout = {
|
|
|
64
64
|
const AXIS_TOP_WITH_TICKS = 36
|
|
65
65
|
const AXIS_TOP_WITHOUT_TICKS = 24
|
|
66
66
|
const AXIS_TOP_LABEL_SPACE = 28
|
|
67
|
-
const AXIS_TICK_FONT_SIZE =
|
|
67
|
+
const AXIS_TICK_FONT_SIZE = 16
|
|
68
68
|
const AXIS_TITLE_SPACE = 30
|
|
69
69
|
const AXIS_TOP_TITLE_BASELINE = 18
|
|
70
70
|
const X_AXIS_TITLE_LABEL_SPACE = 32
|
|
@@ -82,6 +82,7 @@ const EMPTY_VALUE_LABEL = 'No data'
|
|
|
82
82
|
const MIN_CELL_VALUE_WIDTH = 18
|
|
83
83
|
const MIN_CELL_VALUE_HEIGHT = 14
|
|
84
84
|
const SIDE_Y_AXIS_TITLE_GAP = 20
|
|
85
|
+
const AXIS_TITLE_FONT_WEIGHT = 'bold'
|
|
85
86
|
|
|
86
87
|
const clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max)
|
|
87
88
|
|
|
@@ -129,7 +130,7 @@ const getXAxisTickLabelProps = (xAxisPosition: HeatMapXAxisPosition, xTickRotati
|
|
|
129
130
|
|
|
130
131
|
if (!xTickRotation) {
|
|
131
132
|
return {
|
|
132
|
-
fontSize:
|
|
133
|
+
fontSize: AXIS_TICK_FONT_SIZE,
|
|
133
134
|
textAnchor: 'middle',
|
|
134
135
|
angle: 0,
|
|
135
136
|
dx: 0,
|
|
@@ -138,7 +139,7 @@ const getXAxisTickLabelProps = (xAxisPosition: HeatMapXAxisPosition, xTickRotati
|
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
return {
|
|
141
|
-
fontSize:
|
|
142
|
+
fontSize: AXIS_TICK_FONT_SIZE,
|
|
142
143
|
textAnchor: xAxisPosition === 'top' ? 'start' : 'end',
|
|
143
144
|
angle: xTickRotation,
|
|
144
145
|
dx: isSteepRotation || xAxisPosition === 'top' ? 0 : '-0.5em',
|
|
@@ -313,20 +314,16 @@ const getTooltipColumns = (
|
|
|
313
314
|
xDataKey: string | undefined,
|
|
314
315
|
heatMapSeries: { dataKey: string }[]
|
|
315
316
|
): TooltipColumn[] =>
|
|
316
|
-
Object.entries(config.columns || {}).reduce<TooltipColumn[]>((tooltipColumns, [
|
|
317
|
-
|
|
318
|
-
if (value.
|
|
319
|
-
if (
|
|
317
|
+
Object.entries(config.columns || {}).reduce<TooltipColumn[]>((tooltipColumns, [columnKey, value]) => {
|
|
318
|
+
const columnName = value.name || columnKey
|
|
319
|
+
if (!value.tooltips || !columnName) return tooltipColumns
|
|
320
|
+
if (columnName === xDataKey) return tooltipColumns
|
|
321
|
+
if (heatMapSeries.some(series => series.dataKey === columnName)) return tooltipColumns
|
|
320
322
|
|
|
321
323
|
tooltipColumns.push({
|
|
322
|
-
label: value.label ||
|
|
323
|
-
name:
|
|
324
|
-
options: {
|
|
325
|
-
addColPrefix: value.prefix,
|
|
326
|
-
addColSuffix: value.suffix,
|
|
327
|
-
addColRoundTo: value.roundToPlace,
|
|
328
|
-
addColCommas: value.commas
|
|
329
|
-
}
|
|
324
|
+
label: value.label || columnName,
|
|
325
|
+
name: columnName,
|
|
326
|
+
options: getSeriesColumnFormattingParams(value) || {}
|
|
330
327
|
})
|
|
331
328
|
|
|
332
329
|
return tooltipColumns
|
|
@@ -601,6 +598,7 @@ const HeatMap: React.FC<HeatMapProps> = ({ parentWidth, parentHeight }) => {
|
|
|
601
598
|
x={xOffset + gridWidth / 2}
|
|
602
599
|
y={xAxisPosition === 'top' ? -xAxisTitleDistance : gridHeight + xAxisTitleDistance}
|
|
603
600
|
textAnchor='middle'
|
|
601
|
+
fontWeight={AXIS_TITLE_FONT_WEIGHT}
|
|
604
602
|
>
|
|
605
603
|
{xAxisLabel}
|
|
606
604
|
</text>
|
|
@@ -611,7 +609,13 @@ const HeatMap: React.FC<HeatMapProps> = ({ parentWidth, parentHeight }) => {
|
|
|
611
609
|
|
|
612
610
|
if (showTopYAxisTitle) {
|
|
613
611
|
return (
|
|
614
|
-
<text
|
|
612
|
+
<text
|
|
613
|
+
className='cdc-heatmap__axis-title'
|
|
614
|
+
x={rowLabelTitleX}
|
|
615
|
+
y={topYAxisTitleY}
|
|
616
|
+
textAnchor='start'
|
|
617
|
+
fontWeight={AXIS_TITLE_FONT_WEIGHT}
|
|
618
|
+
>
|
|
615
619
|
{yAxisLabel}
|
|
616
620
|
</text>
|
|
617
621
|
)
|
|
@@ -622,6 +626,7 @@ const HeatMap: React.FC<HeatMapProps> = ({ parentWidth, parentHeight }) => {
|
|
|
622
626
|
className='cdc-heatmap__axis-title'
|
|
623
627
|
transform={`translate(${sideYAxisTitleX}, ${yOffset + gridHeight / 2}) rotate(-90)`}
|
|
624
628
|
textAnchor='middle'
|
|
629
|
+
fontWeight={AXIS_TITLE_FONT_WEIGHT}
|
|
625
630
|
>
|
|
626
631
|
{yAxisLabel}
|
|
627
632
|
</text>
|
|
@@ -737,7 +742,7 @@ const HeatMap: React.FC<HeatMapProps> = ({ parentWidth, parentHeight }) => {
|
|
|
737
742
|
hideAxisLine={Boolean(config.yAxis?.hideAxis)}
|
|
738
743
|
hideTicks={Boolean(config.yAxis?.hideTicks)}
|
|
739
744
|
tickLabelProps={() => ({
|
|
740
|
-
fontSize:
|
|
745
|
+
fontSize: AXIS_TICK_FONT_SIZE,
|
|
741
746
|
textAnchor: 'end',
|
|
742
747
|
angle: yTickRotation,
|
|
743
748
|
dx: yTickRotation ? '-0.25em' : 0,
|
|
@@ -62,6 +62,10 @@ const HeatMapGradientLegend = () => {
|
|
|
62
62
|
const ranges = getHeatMapDataGroupRanges(config, minValue, maxValue, formatNumber)
|
|
63
63
|
const displayRanges = config.legend?.reverseLabelOrder ? [...ranges].reverse() : ranges
|
|
64
64
|
const displayPalette = config.legend?.reverseLabelOrder ? [...palette].reverse() : palette
|
|
65
|
+
const displayLegendItems = displayRanges.map((range, index) => ({
|
|
66
|
+
color: displayPalette[index % displayPalette.length],
|
|
67
|
+
range
|
|
68
|
+
}))
|
|
65
69
|
|
|
66
70
|
if (config.legend?.hide) return null
|
|
67
71
|
|
|
@@ -72,34 +76,44 @@ const HeatMapGradientLegend = () => {
|
|
|
72
76
|
|
|
73
77
|
{isGradientLegend ? (
|
|
74
78
|
<div className='cdc-heatmap__legend-scale'>
|
|
75
|
-
<svg className='cdc-heatmap__legend-svg' height='
|
|
79
|
+
<svg className='cdc-heatmap__legend-svg' height='25' width='100%'>
|
|
76
80
|
<rect x='0' y='0' width='100%' height='25' fill='#d3d3d3' />
|
|
77
|
-
{
|
|
81
|
+
{displayLegendItems.map(({ color, range }, index) => (
|
|
78
82
|
<rect
|
|
79
|
-
key={
|
|
80
|
-
x={`${(index /
|
|
83
|
+
key={`heatmap-gradient-block-${range.min}-${range.max}-${color}`}
|
|
84
|
+
x={`${(index / displayLegendItems.length) * 100}%`}
|
|
81
85
|
y='1'
|
|
82
|
-
width={`${100 /
|
|
86
|
+
width={`${100 / displayLegendItems.length}%`}
|
|
83
87
|
height='23'
|
|
84
88
|
fill={color}
|
|
85
89
|
/>
|
|
86
90
|
))}
|
|
87
|
-
|
|
88
|
-
<text x='0' y='40' fontSize='14' textAnchor='start' fill='#333'>
|
|
89
|
-
{displayRanges[0]?.label.split('\u2013')[0]}
|
|
90
|
-
</text>
|
|
91
|
-
<text x='100%' y='40' fontSize='14' textAnchor='end' fill='#333'>
|
|
92
|
-
{displayRanges[displayRanges.length - 1]?.label.split('\u2013').pop()}
|
|
93
|
-
</text>
|
|
94
91
|
</svg>
|
|
92
|
+
<div
|
|
93
|
+
className='cdc-heatmap__legend-range-labels'
|
|
94
|
+
style={{ gridTemplateColumns: `repeat(${Math.max(displayLegendItems.length, 1)}, minmax(0, 1fr))` }}
|
|
95
|
+
>
|
|
96
|
+
{displayLegendItems.map(({ range }) => (
|
|
97
|
+
<span
|
|
98
|
+
className='cdc-heatmap__legend-range-label'
|
|
99
|
+
key={`heatmap-gradient-label-${range.min}-${range.max}`}
|
|
100
|
+
>
|
|
101
|
+
{range.label}
|
|
102
|
+
</span>
|
|
103
|
+
))}
|
|
104
|
+
</div>
|
|
95
105
|
|
|
96
106
|
{valueLabel && <div className='cdc-heatmap__legend-series-label'>{valueLabel}</div>}
|
|
97
107
|
</div>
|
|
98
108
|
) : (
|
|
99
109
|
<div className={innerClasses.join(' ')}>
|
|
100
|
-
{
|
|
101
|
-
<LegendItem
|
|
102
|
-
|
|
110
|
+
{displayLegendItems.map(({ color, range }) => (
|
|
111
|
+
<LegendItem
|
|
112
|
+
className='legend-item not-clickable'
|
|
113
|
+
key={`heatmap-legend-range-${range.min}-${range.max}`}
|
|
114
|
+
tabIndex={-1}
|
|
115
|
+
>
|
|
116
|
+
<LegendShape shape={shape} fill={color} />
|
|
103
117
|
<LegendLabel align='left' className='m-0'>
|
|
104
118
|
{range.label}
|
|
105
119
|
</LegendLabel>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
--cove-heatmap-cell-stroke-color: rgba(255, 255, 255, 0.7);
|
|
4
4
|
--cove-heatmap-cell-stroke-width: 0.1px;
|
|
5
5
|
--cove-heatmap-axis-title-color: var(--darkGray, #333);
|
|
6
|
-
--cove-heatmap-axis-title-font-size:
|
|
6
|
+
--cove-heatmap-axis-title-font-size: 18px;
|
|
7
7
|
|
|
8
8
|
width: 100%;
|
|
9
9
|
}
|
|
@@ -63,6 +63,20 @@
|
|
|
63
63
|
overflow: visible;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
.cove-visualization .cdc-heatmap__legend .cdc-heatmap__legend-range-labels {
|
|
67
|
+
color: #333;
|
|
68
|
+
display: grid;
|
|
69
|
+
font-size: 0.875rem;
|
|
70
|
+
line-height: 1.2;
|
|
71
|
+
margin-top: 0.35rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.cove-visualization .cdc-heatmap__legend .cdc-heatmap__legend-range-label {
|
|
75
|
+
min-width: 0;
|
|
76
|
+
overflow-wrap: anywhere;
|
|
77
|
+
text-align: center;
|
|
78
|
+
}
|
|
79
|
+
|
|
66
80
|
.cove-visualization .cdc-heatmap__legend .cdc-heatmap__legend-series-label {
|
|
67
81
|
color: #333;
|
|
68
82
|
font-weight: 600;
|