@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
|
@@ -2,6 +2,9 @@ import { DataItem, StyleProps, Style } from './LineChartProps'
|
|
|
2
2
|
import { PreliminaryDataItem } from '../../types/ChartConfig'
|
|
3
3
|
|
|
4
4
|
import _ from 'lodash'
|
|
5
|
+
|
|
6
|
+
const isCalculable = value => !isNaN(parseFloat(value)) && isFinite(value)
|
|
7
|
+
|
|
5
8
|
export const createStyles = (props: StyleProps): Style[] => {
|
|
6
9
|
const {
|
|
7
10
|
preliminaryData,
|
|
@@ -15,17 +18,18 @@ export const createStyles = (props: StyleProps): Style[] => {
|
|
|
15
18
|
originalSeriesKey
|
|
16
19
|
} = props
|
|
17
20
|
|
|
18
|
-
const dynamicSeriesKey = dynamicCategory ? originalSeriesKey : seriesKey
|
|
19
21
|
const validPreliminaryData: PreliminaryDataItem[] = preliminaryData.filter(
|
|
20
22
|
pd => pd.seriesKeys?.length && pd.column && pd.value && pd.type && pd.style && pd.type === 'effect'
|
|
21
23
|
)
|
|
22
24
|
const isEffectLine = (pd, dataPoint) => {
|
|
23
25
|
if (dynamicCategory) {
|
|
26
|
+
// In dynamic category mode, check the attribute column (pd.column) for the effect marker
|
|
27
|
+
// seriesKey is the runtime series key derived from the category column
|
|
24
28
|
return (
|
|
25
29
|
pd.type === 'effect' &&
|
|
26
30
|
pd.style !== 'Open Circles' &&
|
|
27
31
|
pd.seriesKeys.includes(seriesKey) &&
|
|
28
|
-
|
|
32
|
+
dataPoint[pd.column] === pd.value
|
|
29
33
|
)
|
|
30
34
|
} else {
|
|
31
35
|
return (
|
|
@@ -41,24 +45,29 @@ export const createStyles = (props: StyleProps): Style[] => {
|
|
|
41
45
|
validPreliminaryData.find(pd => isEffectLine(pd, point))
|
|
42
46
|
|
|
43
47
|
const styles: Style[] = []
|
|
44
|
-
const createStyle = (lineStyle): Style =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
const createStyle = (lineStyle, customWeight?: number | string): Style => {
|
|
49
|
+
const parsedWeight = Number(customWeight)
|
|
50
|
+
const hasValidWeight =
|
|
51
|
+
customWeight !== '' && customWeight != null && Number.isFinite(parsedWeight) && parsedWeight >= 1
|
|
52
|
+
return {
|
|
53
|
+
stroke: stroke,
|
|
54
|
+
strokeWidth: hasValidWeight ? parsedWeight : strokeWidth,
|
|
55
|
+
strokeDasharray: lineStyle
|
|
56
|
+
}
|
|
57
|
+
}
|
|
49
58
|
|
|
50
59
|
data.forEach((d, index) => {
|
|
51
60
|
const matchingPd: PreliminaryDataItem = getMatchingPd(d)
|
|
52
61
|
|
|
53
62
|
let style: Style = matchingPd
|
|
54
|
-
? createStyle(handleLineType(matchingPd.style))
|
|
63
|
+
? createStyle(handleLineType(matchingPd.style), matchingPd.weight)
|
|
55
64
|
: createStyle(handleLineType(lineType))
|
|
56
65
|
|
|
57
66
|
styles.push(style)
|
|
58
67
|
|
|
59
68
|
// If matchingPd exists, update the previous style if there is a previous element
|
|
60
69
|
if (matchingPd && index > 0) {
|
|
61
|
-
styles[index - 1] = createStyle(handleLineType(matchingPd.style))
|
|
70
|
+
styles[index - 1] = createStyle(handleLineType(matchingPd.style), matchingPd.weight)
|
|
62
71
|
}
|
|
63
72
|
})
|
|
64
73
|
return styles as Style[]
|
|
@@ -67,8 +76,17 @@ export const createStyles = (props: StyleProps): Style[] => {
|
|
|
67
76
|
export const filterCircles = (
|
|
68
77
|
preliminaryData: PreliminaryDataItem[],
|
|
69
78
|
data: DataItem[],
|
|
70
|
-
seriesKey: string
|
|
79
|
+
seriesKey: string,
|
|
80
|
+
dynamicCategory?: string,
|
|
81
|
+
originalSeriesKey?: string
|
|
71
82
|
): DataItem[] => {
|
|
83
|
+
// In dynamic category mode:
|
|
84
|
+
// - seriesKey is the runtime series key for matching against seriesKeys
|
|
85
|
+
// - originalSeriesKey is the data value column (e.g., "Value") for checking if data exists
|
|
86
|
+
// In standard mode:
|
|
87
|
+
// - seriesKey is used for both purposes
|
|
88
|
+
const valueKey = dynamicCategory ? originalSeriesKey : seriesKey
|
|
89
|
+
|
|
72
90
|
// Filter and map preliminaryData to get circlesFiltered
|
|
73
91
|
const circlesFiltered = preliminaryData
|
|
74
92
|
?.filter(item => item.style.includes('Circles') && item.type === 'effect' && item.seriesKeys?.length)
|
|
@@ -86,7 +104,7 @@ export const filterCircles = (
|
|
|
86
104
|
if (
|
|
87
105
|
item[fc.column] === fc.value &&
|
|
88
106
|
fc.seriesKeys.includes(seriesKey) &&
|
|
89
|
-
item[
|
|
107
|
+
isCalculable(item[valueKey]) &&
|
|
90
108
|
fc.style === 'Open Circles'
|
|
91
109
|
) {
|
|
92
110
|
const result = {
|
|
@@ -99,7 +117,7 @@ export const filterCircles = (
|
|
|
99
117
|
if (
|
|
100
118
|
(!fc.value || item[fc.column] === fc.value) &&
|
|
101
119
|
fc.seriesKeys.includes(seriesKey) &&
|
|
102
|
-
item[
|
|
120
|
+
isCalculable(item[valueKey]) &&
|
|
103
121
|
fc.style === 'Filled Circles'
|
|
104
122
|
) {
|
|
105
123
|
const result = {
|
|
@@ -115,7 +133,6 @@ export const filterCircles = (
|
|
|
115
133
|
return filteredData
|
|
116
134
|
}
|
|
117
135
|
|
|
118
|
-
const isCalculable = value => !isNaN(parseFloat(value)) && isFinite(value)
|
|
119
136
|
const handleFirstIndex = ({
|
|
120
137
|
data,
|
|
121
138
|
seriesKey,
|
|
@@ -129,7 +146,8 @@ const handleFirstIndex = ({
|
|
|
129
146
|
const result = {
|
|
130
147
|
data: { '0': [] },
|
|
131
148
|
style: '',
|
|
132
|
-
color: ''
|
|
149
|
+
color: '',
|
|
150
|
+
weight: undefined
|
|
133
151
|
}
|
|
134
152
|
|
|
135
153
|
// If data is empty, return the empty result
|
|
@@ -150,6 +168,7 @@ const handleFirstIndex = ({
|
|
|
150
168
|
|
|
151
169
|
result.data[pairCount].push(modifiedItem)
|
|
152
170
|
result.style = suppressionData.style
|
|
171
|
+
result.weight = suppressionData.weight
|
|
153
172
|
result.color = dynamicCategory && modifiedItem ? colorScale(modifiedItem[dynamicCategory]) : ''
|
|
154
173
|
|
|
155
174
|
// Find the next calculable item index
|
|
@@ -180,7 +199,8 @@ const handleLastIndex = ({
|
|
|
180
199
|
const result = {
|
|
181
200
|
data: { '0': [] },
|
|
182
201
|
style: '',
|
|
183
|
-
color: ''
|
|
202
|
+
color: '',
|
|
203
|
+
weight: undefined
|
|
184
204
|
}
|
|
185
205
|
const lastIndexDataItem = data[data.length - 1]
|
|
186
206
|
|
|
@@ -203,6 +223,7 @@ const handleLastIndex = ({
|
|
|
203
223
|
lastAddedIndex = prevIndex
|
|
204
224
|
}
|
|
205
225
|
result.style = pd.style
|
|
226
|
+
result.weight = pd.weight
|
|
206
227
|
result.color = colorScale(modifiedItem[dynamicCategory])
|
|
207
228
|
}
|
|
208
229
|
})
|
|
@@ -222,7 +243,8 @@ const handleMiddleIndices = ({
|
|
|
222
243
|
let result = {
|
|
223
244
|
data: {},
|
|
224
245
|
style: '',
|
|
225
|
-
color: 'red'
|
|
246
|
+
color: 'red',
|
|
247
|
+
weight: undefined
|
|
226
248
|
}
|
|
227
249
|
|
|
228
250
|
//skip processing if data or preliminaryData is not an array
|
|
@@ -260,6 +282,7 @@ const handleMiddleIndices = ({
|
|
|
260
282
|
// Only add siblings to results if both siblings are found
|
|
261
283
|
if (siblingBefore && siblingAfter) {
|
|
262
284
|
result.style = pd.style
|
|
285
|
+
result.weight = pd.weight
|
|
263
286
|
result.color = colorScale(item[dynamicCategory])
|
|
264
287
|
result.data[pairCount++] = [siblingBefore, siblingAfter]
|
|
265
288
|
}
|
|
@@ -18,6 +18,7 @@ import { filterCircles, createStyles, createDataSegments } from './helpers'
|
|
|
18
18
|
import { getLabelPositionForDataPoint } from './helpers/labelPositioning'
|
|
19
19
|
import LineChartCircle from './components/LineChart.Circle'
|
|
20
20
|
import LineChartBumpCircle from './components/LineChart.BumpCircle'
|
|
21
|
+
import { getYAxisDomainData } from '../../helpers/getYAxisDomainData'
|
|
21
22
|
import isNumber from '@cdc/core/helpers/isNumber'
|
|
22
23
|
|
|
23
24
|
// Types
|
|
@@ -39,9 +40,25 @@ const LineChart = (props: LineChartProps) => {
|
|
|
39
40
|
yAxisWidth,
|
|
40
41
|
} = props
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const {
|
|
44
|
+
colorScale,
|
|
45
|
+
config,
|
|
46
|
+
formatNumber,
|
|
47
|
+
handleLineType,
|
|
48
|
+
parseDate,
|
|
49
|
+
seriesHighlight,
|
|
50
|
+
tableData,
|
|
51
|
+
transformedData: data,
|
|
52
|
+
updateConfig,
|
|
53
|
+
yAxisDomainData
|
|
54
|
+
} = useContext<ChartContext>(ConfigContext)
|
|
55
|
+
const dataForRightAxis = getYAxisDomainData({
|
|
56
|
+
config,
|
|
57
|
+
data,
|
|
58
|
+
tableData,
|
|
59
|
+
fullEligibleDomainData: yAxisDomainData
|
|
60
|
+
})
|
|
61
|
+
const { yScaleRight } = useRightAxis({ config, yMax, data: dataForRightAxis, updateConfig })
|
|
45
62
|
const showSingleSeries = config.tooltips.singleSeries
|
|
46
63
|
if (!handleTooltipMouseOver) return
|
|
47
64
|
|
|
@@ -80,7 +97,13 @@ const LineChart = (props: LineChartProps) => {
|
|
|
80
97
|
const _data = seriesData.dynamicCategory
|
|
81
98
|
? data.filter(d => d[seriesData.dynamicCategory] === seriesKey)
|
|
82
99
|
: data
|
|
83
|
-
const circleData = filterCircles(
|
|
100
|
+
const circleData = filterCircles(
|
|
101
|
+
config?.preliminaryData,
|
|
102
|
+
tableData,
|
|
103
|
+
seriesKey,
|
|
104
|
+
seriesData.dynamicCategory,
|
|
105
|
+
_seriesKey
|
|
106
|
+
)
|
|
84
107
|
|
|
85
108
|
// Prepare sorted data for line and area rendering
|
|
86
109
|
const sortedData =
|
|
@@ -296,7 +319,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
296
319
|
? segment.color
|
|
297
320
|
: colorScale(config.runtime.seriesLabels[seriesKey])
|
|
298
321
|
}
|
|
299
|
-
strokeWidth={seriesData
|
|
322
|
+
strokeWidth={Number(segment.weight) || seriesData.weight || 2}
|
|
300
323
|
strokeOpacity={1}
|
|
301
324
|
shapeRendering='geometricPrecision'
|
|
302
325
|
strokeDasharray={handleLineType(segment.style)}
|
|
@@ -5,6 +5,7 @@ import LinearChart from '../../LinearChart'
|
|
|
5
5
|
import ConfigContext from '../../../ConfigContext'
|
|
6
6
|
import { createMockChartContext } from './mockConfigContext'
|
|
7
7
|
import forestPlotConfig from '../../../../examples/feature/forest-plot/forest-plot.json'
|
|
8
|
+
import * as suppressionHelpers from '../../../helpers/getHasBoundarySuppression'
|
|
8
9
|
|
|
9
10
|
// Mock ResizeObserver
|
|
10
11
|
vi.stubGlobal(
|
|
@@ -72,7 +73,51 @@ const getLeftAxisLabelTransforms = container =>
|
|
|
72
73
|
.map(label => label.getAttribute('transform'))
|
|
73
74
|
.filter(Boolean)
|
|
74
75
|
|
|
76
|
+
const sidePlacementYAxis = {
|
|
77
|
+
hideAxis: false,
|
|
78
|
+
hideLabel: false,
|
|
79
|
+
hideTicks: false,
|
|
80
|
+
size: '50',
|
|
81
|
+
gridLines: true,
|
|
82
|
+
label: 'Y-Axis',
|
|
83
|
+
titlePlacement: 'side',
|
|
84
|
+
tickRotation: 0,
|
|
85
|
+
anchors: [],
|
|
86
|
+
axisPadding: 0,
|
|
87
|
+
labelPlacement: 'On Date/Category Axis',
|
|
88
|
+
rightAxisSize: 0
|
|
89
|
+
}
|
|
90
|
+
|
|
75
91
|
describe('LinearChart', () => {
|
|
92
|
+
describe('suppression domain data source', () => {
|
|
93
|
+
it('uses excludedData fallback for suppression detection when Y-axis domain is stable and yAxisDomainData is not provided', () => {
|
|
94
|
+
const suppressionSpy = vi.spyOn(suppressionHelpers, 'getHasBoundarySuppression').mockReturnValue(false)
|
|
95
|
+
const excludedRows = [{ Date: '2020-01-01', Cases: 'SUPP' }]
|
|
96
|
+
const tableRows = [{ Date: '2020-01-01', Cases: 12 }]
|
|
97
|
+
|
|
98
|
+
renderLinearChart(
|
|
99
|
+
{
|
|
100
|
+
visualizationType: 'Line',
|
|
101
|
+
yAxis: {
|
|
102
|
+
...createMockChartContext().config.yAxis,
|
|
103
|
+
filterDomainBehavior: 'stable'
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
transformedData: tableRows as any,
|
|
108
|
+
tableData: tableRows as any,
|
|
109
|
+
excludedData: excludedRows as any,
|
|
110
|
+
yAxisDomainData: undefined
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
expect(suppressionSpy).toHaveBeenCalled()
|
|
115
|
+
expect(suppressionSpy.mock.calls[0][0].rows).toBe(excludedRows)
|
|
116
|
+
|
|
117
|
+
suppressionSpy.mockRestore()
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
|
|
76
121
|
describe('rendering', () => {
|
|
77
122
|
it('renders without crashing', () => {
|
|
78
123
|
const { container } = renderLinearChart()
|
|
@@ -238,26 +283,103 @@ describe('LinearChart', () => {
|
|
|
238
283
|
expect(leftAxis).toBeTruthy()
|
|
239
284
|
})
|
|
240
285
|
|
|
286
|
+
it('does not render the side y-axis title inside the svg when titlePlacement is top', () => {
|
|
287
|
+
const { container } = renderLinearChart()
|
|
288
|
+
|
|
289
|
+
expect(container.querySelector('.left-axis text.y-label')).toBeFalsy()
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('does not render the top y-axis title inside LinearChart when titlePlacement is top', () => {
|
|
293
|
+
const { container } = renderLinearChart()
|
|
294
|
+
|
|
295
|
+
expect(container.querySelector('.y-axis-top-title')).toBeFalsy()
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
it('renders the side y-axis title inside the svg when titlePlacement is side', () => {
|
|
299
|
+
const { container } = renderLinearChart({
|
|
300
|
+
yAxis: sidePlacementYAxis
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
expect(container.querySelector('.y-axis-top-title')).toBeFalsy()
|
|
304
|
+
expect(container.querySelector('.left-axis text.y-label')).toBeTruthy()
|
|
305
|
+
})
|
|
306
|
+
|
|
241
307
|
it('renders bottom axis group', () => {
|
|
242
308
|
const { container } = renderLinearChart()
|
|
243
309
|
const bottomAxis = container.querySelector('.bottom-axis')
|
|
244
310
|
expect(bottomAxis).toBeTruthy()
|
|
245
311
|
})
|
|
246
312
|
|
|
313
|
+
it('uses vizViewport for x-axis viewport tick overrides', () => {
|
|
314
|
+
const data = [
|
|
315
|
+
{ Date: '2024-01-01', Value: 1 },
|
|
316
|
+
{ Date: '2024-02-01', Value: 2 },
|
|
317
|
+
{ Date: '2024-03-01', Value: 3 },
|
|
318
|
+
{ Date: '2024-04-01', Value: 4 },
|
|
319
|
+
{ Date: '2024-05-01', Value: 5 },
|
|
320
|
+
{ Date: '2024-06-01', Value: 6 }
|
|
321
|
+
]
|
|
322
|
+
|
|
323
|
+
const { container } = renderLinearChart(
|
|
324
|
+
{
|
|
325
|
+
data,
|
|
326
|
+
preliminaryData: [],
|
|
327
|
+
series: [{ dataKey: 'Value', axis: 'Left' }],
|
|
328
|
+
xAxis: {
|
|
329
|
+
type: 'date',
|
|
330
|
+
dataKey: 'Date',
|
|
331
|
+
label: 'X-Axis',
|
|
332
|
+
hideAxis: false,
|
|
333
|
+
hideLabel: false,
|
|
334
|
+
hideTicks: false,
|
|
335
|
+
size: '50',
|
|
336
|
+
tickRotation: 0,
|
|
337
|
+
maxTickRotation: 90,
|
|
338
|
+
anchors: [],
|
|
339
|
+
axisPadding: 0,
|
|
340
|
+
dateParseFormat: '%Y-%m-%d',
|
|
341
|
+
dateDisplayFormat: '%b %Y'
|
|
342
|
+
},
|
|
343
|
+
runtime: {
|
|
344
|
+
xAxis: {
|
|
345
|
+
type: 'date',
|
|
346
|
+
dataKey: 'Date',
|
|
347
|
+
label: 'X-Axis',
|
|
348
|
+
numTicks: 6,
|
|
349
|
+
viewportNumTicks: { xxs: 2 }
|
|
350
|
+
},
|
|
351
|
+
yAxis: {
|
|
352
|
+
size: 50,
|
|
353
|
+
label: 'Y-Axis',
|
|
354
|
+
gridLines: true
|
|
355
|
+
},
|
|
356
|
+
originalXAxis: {
|
|
357
|
+
dataKey: 'Date'
|
|
358
|
+
},
|
|
359
|
+
series: [{ dataKey: 'Value', axis: 'Left' }],
|
|
360
|
+
seriesKeys: ['Value'],
|
|
361
|
+
seriesLabels: { Value: 'Value' },
|
|
362
|
+
seriesLabelsAll: ['Value'],
|
|
363
|
+
uniqueId: 'test-chart'
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
currentViewport: 'lg',
|
|
368
|
+
vizViewport: 'xxs',
|
|
369
|
+
colorScale: () => '#000',
|
|
370
|
+
transformedData: data,
|
|
371
|
+
tableData: data
|
|
372
|
+
}
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
expect(container.querySelectorAll('.bottom-axis .vx-axis-tick')).toHaveLength(2)
|
|
376
|
+
})
|
|
377
|
+
|
|
247
378
|
it('hides Y axis when hideAxis is true', () => {
|
|
248
379
|
const { container } = renderLinearChart({
|
|
249
380
|
yAxis: {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
hideTicks: false,
|
|
253
|
-
size: '50',
|
|
254
|
-
gridLines: true,
|
|
255
|
-
label: 'Y-Axis',
|
|
256
|
-
tickRotation: 0,
|
|
257
|
-
anchors: [],
|
|
258
|
-
axisPadding: 0,
|
|
259
|
-
labelPlacement: 'On Date/Category Axis',
|
|
260
|
-
rightAxisSize: 0
|
|
381
|
+
...sidePlacementYAxis,
|
|
382
|
+
hideAxis: true
|
|
261
383
|
}
|
|
262
384
|
})
|
|
263
385
|
// The axis line should be hidden, but grid lines may still render
|
|
@@ -288,34 +410,14 @@ describe('LinearChart', () => {
|
|
|
288
410
|
|
|
289
411
|
const baseRender = renderLinearChart({
|
|
290
412
|
yAxis: {
|
|
291
|
-
|
|
292
|
-
hideLabel: false,
|
|
293
|
-
hideTicks: false,
|
|
294
|
-
size: '50',
|
|
295
|
-
gridLines: true,
|
|
296
|
-
label: 'Y-Axis',
|
|
297
|
-
tickRotation: 0,
|
|
298
|
-
anchors: [],
|
|
299
|
-
axisPadding: 0,
|
|
300
|
-
labelPlacement: 'On Date/Category Axis',
|
|
301
|
-
rightAxisSize: 0,
|
|
413
|
+
...sidePlacementYAxis,
|
|
302
414
|
labelOffset: 0
|
|
303
415
|
},
|
|
304
416
|
runtime: baseRuntime
|
|
305
417
|
})
|
|
306
418
|
const offsetRender = renderLinearChart({
|
|
307
419
|
yAxis: {
|
|
308
|
-
|
|
309
|
-
hideLabel: false,
|
|
310
|
-
hideTicks: false,
|
|
311
|
-
size: '50',
|
|
312
|
-
gridLines: true,
|
|
313
|
-
label: 'Y-Axis',
|
|
314
|
-
tickRotation: 0,
|
|
315
|
-
anchors: [],
|
|
316
|
-
axisPadding: 0,
|
|
317
|
-
labelPlacement: 'On Date/Category Axis',
|
|
318
|
-
rightAxisSize: 0,
|
|
420
|
+
...sidePlacementYAxis,
|
|
319
421
|
labelOffset: 240
|
|
320
422
|
},
|
|
321
423
|
runtime: {
|
|
@@ -372,6 +474,7 @@ describe('LinearChart', () => {
|
|
|
372
474
|
axisPadding: 0,
|
|
373
475
|
labelOffset: 0
|
|
374
476
|
},
|
|
477
|
+
yAxis: sidePlacementYAxis,
|
|
375
478
|
runtime: baseRuntime
|
|
376
479
|
})
|
|
377
480
|
const offsetRender = renderLinearChart({
|
|
@@ -390,6 +493,7 @@ describe('LinearChart', () => {
|
|
|
390
493
|
axisPadding: 0,
|
|
391
494
|
labelOffset: 240
|
|
392
495
|
},
|
|
496
|
+
yAxis: sidePlacementYAxis,
|
|
393
497
|
runtime: {
|
|
394
498
|
...baseRuntime,
|
|
395
499
|
yAxis: {
|