@cdc/chart 4.26.4 → 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 +353 -29
- package/LICENSE +201 -0
- package/dist/{cdcchart-DQ00cQCm.es.js → cdcchart-BWVIUcAF.es.js} +5 -55
- package/dist/cdcchart.js +37885 -35117
- package/examples/feature/heatmap/average-age-categorical.json +313 -0
- package/examples/feature/heatmap/calendar-heatmap.json +134 -0
- package/examples/feature/heatmap/cell-values-bottom-axis.json +181 -0
- package/examples/feature/heatmap/sparse-aggregation.json +158 -0
- package/examples/private/bad-y-axis.json +543 -0
- package/examples/private/bar-chart-negative-value.json +455 -0
- package/examples/private/bump-chart.json +1116 -0
- package/examples/private/test-123.json +624 -0
- package/package.json +4 -3
- package/src/CdcChart.tsx +5 -4
- package/src/CdcChartComponent.tsx +166 -62
- package/src/_stories/Chart.AltText.stories.tsx +136 -0
- package/src/_stories/Chart.HeatMap.stories.tsx +187 -0
- package/src/_stories/Chart.Sankey.smoke.stories.tsx +62 -0
- package/src/_stories/Chart.SeriesColorAssignments.stories.tsx +183 -0
- package/src/_stories/Chart.SmallMultiples.smoke.stories.tsx +29 -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/ChartAxisTitles.smoke.stories.tsx +13 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +63 -0
- package/src/_stories/ChartBrush.Interaction.stories.tsx +112 -0
- package/src/_stories/ChartBrush.smoke.stories.tsx +13 -4
- package/src/_stories/_mock/alt_text_metadata.json +38 -0
- package/src/_stories/_mock/annotation_event-line_mock.json +1064 -0
- package/src/_stories/_mock/heatmap-average-age-categorical.json +315 -0
- package/src/_stories/_mock/heatmap.json +156 -0
- package/src/_stories/_mock/reported_pertussis_cases.json +1643 -0
- package/src/_stories/_mock/sankey_mock.json +78 -0
- package/src/_stories/_mock/small_multiples/small_multiples_lines.json +1 -1
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +22 -1
- package/src/components/Annotations/components/AnnotationDraggable.tsx +220 -120
- package/src/components/Annotations/components/AnnotationDropdown.tsx +5 -2
- package/src/components/Annotations/tests/AnnotationDraggable.test.tsx +198 -0
- package/src/components/Axis/LeftAxis.tsx +17 -11
- package/src/components/Axis/LeftAxisGridlines.tsx +22 -12
- package/src/components/Axis/RightAxis.tsx +3 -0
- package/src/components/Axis/tests/AxisTickValues.test.tsx +86 -0
- package/src/components/Brush/BrushSelector.tsx +88 -187
- package/src/components/Brush/brushInteraction.ts +22 -0
- package/src/components/Brush/tests/brushInteraction.test.ts +38 -0
- package/src/components/EditorPanel/EditorPanel.tsx +677 -185
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +114 -52
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +105 -13
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +1 -0
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +5 -0
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +2 -2
- 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 +4 -1
- 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 +61 -0
- package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +74 -0
- package/src/components/EditorPanel/useEditorPermissions.test.tsx +315 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +77 -21
- package/src/components/HeatMap/components/HeatMap.tsx +767 -0
- package/src/components/HeatMap/components/HeatMapGradientLegend.tsx +128 -0
- package/src/components/HeatMap/heatmap.constants.ts +13 -0
- package/src/components/HeatMap/heatmap.css +85 -0
- package/src/components/HeatMap/helpers/index.ts +261 -0
- package/src/components/HeatMap/index.tsx +5 -0
- package/src/components/HeatMap/tests/HeatMap.test.tsx +1072 -0
- package/src/components/HeatMap/tests/helpers.test.ts +167 -0
- 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/components/LineChart.BumpCircle.tsx +19 -22
- package/src/components/LineChart/helpers.test.ts +574 -0
- package/src/components/LineChart/helpers.ts +39 -16
- package/src/components/LineChart/index.tsx +28 -5
- package/src/components/LinearChart/tests/LinearChart.test.tsx +137 -33
- package/src/components/LinearChart/tests/mockConfigContext.ts +1 -0
- package/src/components/LinearChart.tsx +103 -47
- package/src/components/PieChart/PieChart.tsx +4 -1
- package/src/components/RadarChart/RadarChart.tsx +3 -1
- package/src/components/Regions/components/Regions.tsx +195 -41
- package/src/components/Sankey/components/Sankey.tsx +7 -3
- package/src/components/Sankey/helpers/getSankeyTooltip.tsx +7 -6
- package/src/components/ScatterPlot/ScatterPlot.jsx +3 -3
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +33 -3
- package/src/components/SmallMultiples/SmallMultiples.css +12 -0
- package/src/components/SmallMultiples/SmallMultiples.tsx +50 -15
- package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +152 -0
- package/src/components/SmallMultiples/tests/SmallMultiples.test.tsx +170 -0
- package/src/components/Sparkline/components/SparkLine.tsx +4 -9
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +1 -1
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +2 -2
- package/src/data/initial-state.js +12 -0
- package/src/helpers/axisLabelFontSize.ts +8 -0
- package/src/helpers/bumpChartTooltip.ts +54 -0
- package/src/helpers/calculateLeftYAxisWidth.ts +13 -3
- package/src/helpers/colorAssignmentHelpers.ts +102 -0
- package/src/helpers/dataHelpers.ts +11 -1
- package/src/helpers/ensureSpecialChartAxisTypes.ts +11 -0
- 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 +26 -25
- package/src/helpers/getYAxisDomainData.ts +38 -0
- package/src/helpers/getYAxisFinalizationEligibility.ts +40 -0
- package/src/helpers/handleChartAriaLabels.ts +23 -10
- package/src/helpers/hasSpacedInlineLabel.ts +10 -0
- package/src/helpers/seriesColumnSettings.ts +5 -4
- package/src/helpers/seriesHighlight.ts +26 -0
- package/src/helpers/smallMultiplesHelpers.ts +5 -0
- package/src/helpers/tests/axisLabelFontSize.test.ts +15 -0
- package/src/helpers/tests/bumpChartTooltip.test.ts +75 -0
- package/src/helpers/tests/calculateLeftYAxisWidth.test.ts +38 -0
- package/src/helpers/tests/dataHelpers.test.ts +135 -0
- package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +30 -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/handleChartAriaLabels.test.ts +71 -0
- package/src/helpers/tests/hasSpacedInlineLabel.test.ts +75 -0
- package/src/helpers/tests/seriesColumnSettings.test.ts +16 -2
- package/src/helpers/tests/seriesHighlight.test.ts +61 -0
- 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 +383 -0
- package/src/hooks/useRightAxis.ts +30 -13
- package/src/hooks/useScales.ts +48 -21
- package/src/hooks/useTooltip.tsx +3 -1
- package/src/index.jsx +5 -2
- package/src/scss/main.scss +32 -49
- package/src/test/CdcChart.dataTable.test.tsx +122 -0
- package/src/types/ChartConfig.ts +24 -1
- package/src/types/ChartContext.ts +5 -0
- package/src/helpers/getYAxisAutoPadding.ts +0 -53
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { handleChartAriaLabels } from '../handleChartAriaLabels'
|
|
2
|
+
|
|
3
|
+
describe('handleChartAriaLabels', () => {
|
|
4
|
+
it('returns auto-generated title from visualizationType and title', () => {
|
|
5
|
+
const result = handleChartAriaLabels({
|
|
6
|
+
visualizationType: 'Line',
|
|
7
|
+
title: 'COVID-19 Cases'
|
|
8
|
+
})
|
|
9
|
+
expect(result).toBe('Line chart with the title: COVID-19 Cases')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('returns visualizationType-only title when chart title is absent', () => {
|
|
13
|
+
const result = handleChartAriaLabels({
|
|
14
|
+
visualizationType: 'Bar'
|
|
15
|
+
})
|
|
16
|
+
expect(result).toBe('Bar chart')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('returns fallback when visualizationType is missing', () => {
|
|
20
|
+
const result = handleChartAriaLabels({})
|
|
21
|
+
expect(result).toBe('Data visualization container')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe('description', () => {
|
|
25
|
+
it('concatenates static description after title', () => {
|
|
26
|
+
const result = handleChartAriaLabels({
|
|
27
|
+
visualizationType: 'Line',
|
|
28
|
+
title: 'Trends',
|
|
29
|
+
altText: { type: 'static', value: 'Cases rose steadily over 8 weeks.' }
|
|
30
|
+
})
|
|
31
|
+
expect(result).toBe('Line chart with the title: Trends. Cases rose steadily over 8 weeks.')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('concatenates description from metadata after title', () => {
|
|
35
|
+
const result = handleChartAriaLabels({
|
|
36
|
+
visualizationType: 'Line',
|
|
37
|
+
title: 'Trends',
|
|
38
|
+
altText: { type: 'metadata', metadataKey: 'altDescription' },
|
|
39
|
+
dataMetadata: { altDescription: 'Positivity increased from 4% to 12%.' }
|
|
40
|
+
})
|
|
41
|
+
expect(result).toBe('Line chart with the title: Trends. Positivity increased from 4% to 12%.')
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('returns title only when metadata key is missing', () => {
|
|
45
|
+
const result = handleChartAriaLabels({
|
|
46
|
+
visualizationType: 'Line',
|
|
47
|
+
title: 'Trends',
|
|
48
|
+
altText: { type: 'metadata', metadataKey: 'missing' },
|
|
49
|
+
dataMetadata: { other: 'value' }
|
|
50
|
+
})
|
|
51
|
+
expect(result).toBe('Line chart with the title: Trends')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('returns title only when not configured', () => {
|
|
55
|
+
const result = handleChartAriaLabels({
|
|
56
|
+
visualizationType: 'Line',
|
|
57
|
+
title: 'Trends'
|
|
58
|
+
})
|
|
59
|
+
expect(result).toBe('Line chart with the title: Trends')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('returns title only when altText is empty object', () => {
|
|
63
|
+
const result = handleChartAriaLabels({
|
|
64
|
+
visualizationType: 'Line',
|
|
65
|
+
title: 'Trends',
|
|
66
|
+
altText: {}
|
|
67
|
+
})
|
|
68
|
+
expect(result).toBe('Line chart with the title: Trends')
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
})
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { hasSpacedInlineLabel } from '../hasSpacedInlineLabel'
|
|
3
|
+
import { createMockConfig } from '../../components/LinearChart/tests/mockConfigContext'
|
|
4
|
+
|
|
5
|
+
describe('hasSpacedInlineLabel', () => {
|
|
6
|
+
it('returns true for multi-word inline labels', () => {
|
|
7
|
+
expect(
|
|
8
|
+
hasSpacedInlineLabel(
|
|
9
|
+
createMockConfig({
|
|
10
|
+
yAxis: {
|
|
11
|
+
...createMockConfig().yAxis,
|
|
12
|
+
titlePlacement: 'side',
|
|
13
|
+
inlineLabel: 'per 100k'
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
)
|
|
17
|
+
).toBe(true)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('returns true for inline labels with leading space', () => {
|
|
21
|
+
expect(
|
|
22
|
+
hasSpacedInlineLabel(
|
|
23
|
+
createMockConfig({
|
|
24
|
+
yAxis: {
|
|
25
|
+
...createMockConfig().yAxis,
|
|
26
|
+
titlePlacement: 'side',
|
|
27
|
+
inlineLabel: ' cases'
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
)
|
|
31
|
+
).toBe(true)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('returns false for top y-axis titles with empty inline labels', () => {
|
|
35
|
+
expect(
|
|
36
|
+
hasSpacedInlineLabel(
|
|
37
|
+
createMockConfig({
|
|
38
|
+
yAxis: {
|
|
39
|
+
...createMockConfig().yAxis,
|
|
40
|
+
titlePlacement: 'top',
|
|
41
|
+
inlineLabel: ''
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
)
|
|
45
|
+
).toBe(false)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('returns false when the inline label has no spaces', () => {
|
|
49
|
+
expect(
|
|
50
|
+
hasSpacedInlineLabel(
|
|
51
|
+
createMockConfig({
|
|
52
|
+
yAxis: {
|
|
53
|
+
...createMockConfig().yAxis,
|
|
54
|
+
titlePlacement: 'side',
|
|
55
|
+
inlineLabel: 'Rate'
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
)
|
|
59
|
+
).toBe(false)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('returns true for spaced inline labels regardless of title placement', () => {
|
|
63
|
+
expect(
|
|
64
|
+
hasSpacedInlineLabel(
|
|
65
|
+
createMockConfig({
|
|
66
|
+
yAxis: {
|
|
67
|
+
...createMockConfig().yAxis,
|
|
68
|
+
titlePlacement: 'top',
|
|
69
|
+
inlineLabel: 'per 100k'
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
)
|
|
73
|
+
).toBe(true)
|
|
74
|
+
})
|
|
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
|
})
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { getLegendHighlightKey, shouldResetSeriesHighlight } from '../seriesHighlight'
|
|
3
|
+
|
|
4
|
+
describe('seriesHighlight helpers', () => {
|
|
5
|
+
const seriesLabels = {
|
|
6
|
+
nonfatal_rate_alldrugs: 'All drugs',
|
|
7
|
+
opioid_nonfatal_rate: 'All opioids',
|
|
8
|
+
stimulant_nonfatal_rate: 'All stimulants'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
it('normalizes a legend label to the stored series highlight key', () => {
|
|
12
|
+
expect(getLegendHighlightKey(seriesLabels, 'All opioids')).toBe('opioid_nonfatal_rate')
|
|
13
|
+
expect(getLegendHighlightKey(seriesLabels, 'unmapped_series')).toBe('unmapped_series')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('accepts legend label objects and legacy raw string callers', () => {
|
|
17
|
+
expect(
|
|
18
|
+
getLegendHighlightKey(seriesLabels, {
|
|
19
|
+
datum: 'All stimulants',
|
|
20
|
+
index: 2,
|
|
21
|
+
text: 'All stimulants',
|
|
22
|
+
value: '#075290'
|
|
23
|
+
})
|
|
24
|
+
).toBe('stimulant_nonfatal_rate')
|
|
25
|
+
|
|
26
|
+
expect(getLegendHighlightKey(seriesLabels, 'Highlighted bar')).toBe('Highlighted bar')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('does not reset when removing one selected item from a three-series legend', () => {
|
|
30
|
+
expect(
|
|
31
|
+
shouldResetSeriesHighlight(
|
|
32
|
+
['nonfatal_rate_alldrugs', 'opioid_nonfatal_rate'],
|
|
33
|
+
Object.keys(seriesLabels),
|
|
34
|
+
'opioid_nonfatal_rate',
|
|
35
|
+
'Line'
|
|
36
|
+
)
|
|
37
|
+
).toBe(false)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('resets when selecting the only remaining unselected item', () => {
|
|
41
|
+
expect(
|
|
42
|
+
shouldResetSeriesHighlight(
|
|
43
|
+
['nonfatal_rate_alldrugs', 'opioid_nonfatal_rate'],
|
|
44
|
+
Object.keys(seriesLabels),
|
|
45
|
+
'stimulant_nonfatal_rate',
|
|
46
|
+
'Line'
|
|
47
|
+
)
|
|
48
|
+
).toBe(true)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('does not reset when the clicked item is not a known series key', () => {
|
|
52
|
+
expect(
|
|
53
|
+
shouldResetSeriesHighlight(
|
|
54
|
+
['nonfatal_rate_alldrugs', 'opioid_nonfatal_rate'],
|
|
55
|
+
Object.keys(seriesLabels),
|
|
56
|
+
'suppressed-data-note',
|
|
57
|
+
'Line'
|
|
58
|
+
)
|
|
59
|
+
).toBe(false)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -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,127 @@
|
|
|
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('builds the right-axis scale from the supplied domain rows', () => {
|
|
31
|
+
const config = createComboConfig()
|
|
32
|
+
const renderedRows = [{ Cases: 5, Rate: 12 }]
|
|
33
|
+
const stableDomainRows = [
|
|
34
|
+
{ Cases: 5, Rate: 12 },
|
|
35
|
+
{ Cases: 10, Rate: 90 }
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
const renderedResult = renderHook(() => useRightAxis({ config, yMax: 100, data: renderedRows }))
|
|
39
|
+
const stableResult = renderHook(() => useRightAxis({ config, yMax: 100, data: stableDomainRows }))
|
|
40
|
+
|
|
41
|
+
expect(renderedResult.result.current.yScaleRight.domain()).toEqual([0, 12])
|
|
42
|
+
expect(stableResult.result.current.yScaleRight.domain()).toEqual([0, 90])
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('uses clean-top-tick for the automatic right-axis max when shared auto max strategy is enabled', () => {
|
|
46
|
+
const config = {
|
|
47
|
+
...createComboConfig(),
|
|
48
|
+
yAxis: {
|
|
49
|
+
...createComboConfig().yAxis,
|
|
50
|
+
autoMaxStrategy: 'clean-top-tick' as const
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const result = renderHook(() => useRightAxis({ config, yMax: 100, data: [{ Cases: 5, Rate: 101 }] }))
|
|
55
|
+
|
|
56
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([0, 120])
|
|
57
|
+
expect(result.result.current.rightTickValues).toBeUndefined()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('does not finalize right-axis ticks from the inherited mixed Combo min', () => {
|
|
61
|
+
const config = {
|
|
62
|
+
...createComboConfig(),
|
|
63
|
+
yAxis: {
|
|
64
|
+
...createComboConfig().yAxis,
|
|
65
|
+
autoMaxStrategy: 'clean-top-tick' as const
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const result = renderHook(() => useRightAxis({ config, yMax: 100, data: [{ Cases: -100, Rate: 6 }] }))
|
|
70
|
+
|
|
71
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([-100, 6])
|
|
72
|
+
expect(result.result.current.rightTickValues).toBeUndefined()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('does not round the right-axis max when rightMax is explicit', () => {
|
|
76
|
+
const config = {
|
|
77
|
+
...createComboConfig(),
|
|
78
|
+
yAxis: {
|
|
79
|
+
...createComboConfig().yAxis,
|
|
80
|
+
autoMaxStrategy: 'clean-top-tick' as const,
|
|
81
|
+
rightMax: '100'
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const result = renderHook(() => useRightAxis({ config, yMax: 100, data: [{ Cases: 5, Rate: 25 }] }))
|
|
86
|
+
|
|
87
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([0, 100])
|
|
88
|
+
expect(result.result.current.rightTickValues).toBeUndefined()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('uses clean-top-tick when rightMax is invalid', () => {
|
|
92
|
+
const createConfigWithRightMax = (rightMax: string) => ({
|
|
93
|
+
...createComboConfig(),
|
|
94
|
+
yAxis: {
|
|
95
|
+
...createComboConfig().yAxis,
|
|
96
|
+
autoMaxStrategy: 'clean-top-tick' as const,
|
|
97
|
+
rightMax
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
const lowerThanData = renderHook(() =>
|
|
102
|
+
useRightAxis({ config: createConfigWithRightMax('50'), yMax: 100, data: [{ Cases: 5, Rate: 101 }] })
|
|
103
|
+
)
|
|
104
|
+
const nonNumeric = renderHook(() =>
|
|
105
|
+
useRightAxis({ config: createConfigWithRightMax('not-a-number'), yMax: 100, data: [{ Cases: 5, Rate: 101 }] })
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
expect(lowerThanData.result.current.yScaleRight.domain()).toEqual([0, 120])
|
|
109
|
+
expect(lowerThanData.result.current.rightTickValues).toBeUndefined()
|
|
110
|
+
expect(nonNumeric.result.current.yScaleRight.domain()).toEqual([0, 120])
|
|
111
|
+
expect(nonNumeric.result.current.rightTickValues).toBeUndefined()
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('ignores a non-numeric rightMin value', () => {
|
|
115
|
+
const config = {
|
|
116
|
+
...createComboConfig(),
|
|
117
|
+
yAxis: {
|
|
118
|
+
...createComboConfig().yAxis,
|
|
119
|
+
rightMin: 'not-a-number'
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const result = renderHook(() => useRightAxis({ config, yMax: 100, data: [{ Cases: 5, Rate: 25 }] }))
|
|
124
|
+
|
|
125
|
+
expect(result.result.current.yScaleRight.domain()).toEqual([0, 25])
|
|
126
|
+
})
|
|
127
|
+
})
|