@cdc/chart 4.26.7 → 4.26.10
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 +502 -368
- package/dist/cdcchart-CYc-Kayd.es.js +22488 -0
- package/dist/cdcchart-DCKn8NmI.es.js +20695 -0
- package/dist/cdcchart-Dyui9nk_.es.js +45828 -0
- package/dist/cdcchart.js +4 -97406
- package/examples/__data__/vega-remote-data.json +6 -0
- package/examples/dendrogram.json +18 -0
- package/examples/feature/bar/bar-chart-race-annual-change.json +68 -0
- package/examples/feature/heatmap/average-age-categorical.json +2 -2
- package/examples/feature/heatmap/calendar-heatmap.json +2 -2
- package/examples/feature/heatmap/cell-values-bottom-axis.json +2 -2
- package/examples/feature/heatmap/sparse-aggregation.json +2 -2
- package/examples/feature/line/line-chart-race.json +50 -0
- package/examples/feature/sankey/sankey-example-data.json +40 -1441
- package/examples/feature/sankey/sankey_chart_data.csv +9 -20
- package/examples/network.json +31 -0
- package/examples/private/box-plot.json +573 -0
- package/examples/private/custom-color-issue.json +625 -0
- package/examples/private/custom-colors.json +522 -0
- package/examples/private/cutoff.json +10750 -0
- package/examples/private/overlay-legend.json +485 -0
- package/examples/private/pie-test.json +469 -0
- package/examples/vega-remote.json +22 -0
- package/package.json +11 -5
- package/src/CdcChartComponent.tsx +405 -208
- package/src/_stories/Chart.BarRace.stories.tsx +443 -0
- package/src/_stories/Chart.BoxPlotNumericSort.stories.tsx +189 -0
- package/src/_stories/Chart.BoxPlotStatsParity.stories.tsx +207 -0
- package/src/_stories/Chart.ColorDistribution.stories.tsx +299 -0
- package/src/_stories/Chart.DataCutoff.stories.tsx +147 -0
- package/src/_stories/Chart.DataTableDownloadFilename.stories.tsx +60 -0
- package/src/_stories/Chart.Dendrogram.stories.tsx +373 -0
- package/src/_stories/Chart.Forecast.CILegend.stories.tsx +236 -0
- package/src/_stories/Chart.HeatMap.stories.tsx +170 -4
- package/src/_stories/Chart.Legend.AxisAlign.stories.tsx +119 -0
- package/src/_stories/Chart.LineRace.stories.tsx +149 -0
- package/src/_stories/Chart.Network.stories.tsx +432 -0
- package/src/_stories/Chart.PaletteConversion.Editor.stories.tsx +208 -0
- package/src/_stories/Chart.Patterns.smoke.stories.tsx +31 -0
- package/src/_stories/Chart.PercentageValue.stories.tsx +311 -0
- package/src/_stories/Chart.PieLabels.stories.tsx +4 -2
- package/src/_stories/Chart.RankByValueMutation.stories.tsx +206 -0
- package/src/_stories/Chart.RequiredFields.stories.tsx +681 -0
- package/src/_stories/Chart.Sankey.stories.tsx +417 -0
- package/src/_stories/Chart.SeriesColorAssignments.stories.tsx +47 -0
- package/src/_stories/Chart.SmallMultiplesNumericTileOrder.stories.tsx +137 -0
- package/src/_stories/Chart.TooltipSeriesLabels.stories.tsx +43 -0
- package/src/_stories/Chart.VegaRemoteData.stories.tsx +31 -0
- package/src/_stories/ChartAnnotation.Editor.stories.tsx +78 -0
- package/src/_stories/ChartAxisTitles.smoke.stories.tsx +48 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +499 -57
- package/src/_stories/ChartBrush.Editor.stories.tsx +46 -67
- package/src/_stories/ChartBrush.Regression.stories.tsx +135 -0
- package/src/_stories/ChartEditor.Editor.stories.tsx +33 -3
- package/src/_stories/ChartLine.EffectCompatibility.stories.tsx +120 -0
- package/src/_stories/_mock/annotation_event-line_mock.json +2 -2
- package/src/_stories/_mock/editor-test-configs.json +20 -10
- package/src/_stories/_mock/editor-test-datasets.json +38 -42
- package/src/_stories/_mock/heatmap-average-age-categorical.json +2 -2
- package/src/_stories/_mock/heatmap.json +2 -2
- package/src/_stories/_mock/legend-axis-align-stacked-bar.json +125 -0
- package/src/_stories/_mock/sankey_mock.json +23 -27
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +52 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +639 -139
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +44 -4
- package/src/components/Annotations/components/AnnotationDropdown.tsx +10 -32
- package/src/components/Annotations/helpers/resolveAnnotationLayout.ts +212 -0
- package/src/components/Annotations/tests/AnnotationDraggable.test.tsx +612 -19
- package/src/components/Annotations/tests/AnnotationDropdown.test.tsx +50 -0
- package/src/components/Annotations/tests/resolveAnnotationLayout.test.ts +203 -0
- package/src/components/Axis/BottomAxis.tsx +1 -1
- package/src/components/Axis/Categorical.Axis.tsx +3 -3
- package/src/components/Axis/RightAxis.tsx +21 -13
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +38 -6
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +12 -3
- package/src/components/BarChart/components/BarChart.StackedVertical.test.ts +28 -0
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +29 -25
- package/src/components/BarChart/components/BarChart.Vertical.tsx +37 -26
- package/src/components/BarChart/components/PortionPatternOverlay.tsx +61 -0
- package/src/components/BarChart/components/context.tsx +6 -0
- package/src/components/BarChart/helpers/getPatternUrl.ts +2 -5
- package/src/components/BarChart/helpers/index.ts +8 -0
- package/src/components/BarChart/helpers/portionPattern.ts +243 -0
- package/src/components/BarChart/helpers/tests/addMinimumBarHeights.test.ts +42 -0
- package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +13 -0
- package/src/components/BarChart/helpers/tests/portionPattern.test.ts +280 -0
- package/src/components/BarChart/helpers/tests/useBarChart.test.tsx +104 -0
- package/src/components/BarChart/helpers/useBarChart.ts +60 -59
- package/src/components/BarChartRace/BarChartRace.test.tsx +199 -0
- package/src/components/BarChartRace/BarChartRace.tsx +230 -0
- package/src/components/BarChartRace/BarChartRaceFallback.test.tsx +61 -0
- package/src/components/BarChartRace/BarChartRaceFallback.tsx +48 -0
- package/src/components/BarChartRace/bar-chart-race.css +160 -0
- package/src/components/BarChartRace/helpers.test.ts +114 -0
- package/src/components/BarChartRace/helpers.ts +185 -0
- package/src/components/BarChartRace/index.ts +3 -0
- package/src/components/BoxPlot/helpers/index.ts +11 -82
- package/src/components/Brush/BrushSelector.tsx +9 -23
- package/src/components/Brush/MiniChartPreview.tsx +33 -1
- package/src/components/Dendrogram/components/Dendrogram.tsx +374 -0
- package/src/components/Dendrogram/dendrogram.css +113 -0
- package/src/components/Dendrogram/helpers/layoutDendrogram.test.ts +95 -0
- package/src/components/Dendrogram/helpers/layoutDendrogram.ts +56 -0
- package/src/components/Dendrogram/helpers/prepareDendrogramData.test.ts +135 -0
- package/src/components/Dendrogram/helpers/prepareDendrogramData.ts +166 -0
- package/src/components/Dendrogram/helpers/resolveDendrogramLabels.test.ts +196 -0
- package/src/components/Dendrogram/helpers/resolveDendrogramLabels.ts +418 -0
- package/src/components/Dendrogram/index.tsx +1 -0
- package/src/components/Dendrogram/types/index.ts +66 -0
- package/src/components/EditorPanel/EditorPanel.tsx +1005 -760
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +50 -5
- package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.css +10 -0
- package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.tsx +211 -0
- package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +19 -8
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +88 -54
- package/src/components/EditorPanel/components/Panels/Panel.Network.css +9 -0
- package/src/components/EditorPanel/components/Panels/Panel.Network.tsx +202 -0
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +233 -33
- package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +6 -1
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +89 -105
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +2 -2
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +1 -1
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +8 -66
- package/src/components/EditorPanel/components/Panels/RacingControls.test.tsx +131 -0
- package/src/components/EditorPanel/components/Panels/RacingControls.tsx +185 -0
- package/src/components/EditorPanel/components/Panels/SeriesColorAssignments.tsx +2 -2
- package/src/components/EditorPanel/components/Panels/index.tsx +4 -0
- package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +20 -7
- package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +25 -2
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +2 -1
- package/src/components/EditorPanel/useEditorPermissions.test.tsx +62 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +70 -24
- package/src/components/Forecasting/Forecasting.tsx +15 -6
- package/src/components/HeatMap/components/HeatMap.tsx +511 -178
- package/src/components/HeatMap/heatmap.constants.ts +7 -3
- package/src/components/HeatMap/heatmap.css +101 -2
- package/src/components/HeatMap/helpers/layout.ts +80 -0
- package/src/components/HeatMap/tests/HeatMap.test.tsx +339 -16
- package/src/components/HeatMap/tests/helpers.test.ts +79 -0
- package/src/components/Legend/Legend.Component.tsx +110 -90
- package/src/components/Legend/LegendGroup/LegendGroup.styles.css +7 -0
- package/src/components/Legend/LegendGroup/LegendGroup.tsx +47 -34
- package/src/components/Legend/helpers/createFormatLabels.test.ts +226 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +45 -51
- package/src/components/LineChart/components/LineChart.Circle.tsx +3 -3
- package/src/components/LineChart/helpers.test.ts +126 -38
- package/src/components/LineChart/helpers.ts +9 -4
- package/src/components/LineChart/index.tsx +69 -24
- package/src/components/LineChart/tests/LineChart.test.tsx +630 -0
- package/src/components/LineChartRace/LineChartRace.test.tsx +275 -0
- package/src/components/LineChartRace/LineChartRace.tsx +134 -0
- package/src/components/LineChartRace/LineChartRaceFallback.tsx +28 -0
- package/src/components/LineChartRace/helpers.test.ts +88 -0
- package/src/components/LineChartRace/helpers.ts +157 -0
- package/src/components/LineChartRace/index.ts +3 -0
- package/src/components/LineChartRace/line-chart-race.css +16 -0
- package/src/components/LinearChart/ValueAxisAnchors.tsx +58 -0
- package/src/components/LinearChart/VisualizationRenderer.tsx +18 -23
- package/src/components/LinearChart/tests/LinearChart.test.tsx +412 -13
- package/src/components/LinearChart/tests/VisualizationRenderer.test.tsx +141 -0
- package/src/components/LinearChart/tests/mockConfigContext.ts +2 -1
- package/src/components/LinearChart.tsx +81 -51
- package/src/components/Network/components/Network.tsx +372 -0
- package/src/components/Network/helpers/layoutNetwork.test.ts +68 -0
- package/src/components/Network/helpers/layoutNetwork.ts +87 -0
- package/src/components/Network/helpers/prepareNetworkData.test.ts +116 -0
- package/src/components/Network/helpers/prepareNetworkData.ts +101 -0
- package/src/components/Network/helpers/resolveNetworkLabels.test.ts +112 -0
- package/src/components/Network/helpers/resolveNetworkLabels.ts +206 -0
- package/src/components/Network/index.tsx +3 -0
- package/src/components/Network/network.css +107 -0
- package/src/components/Network/types/index.ts +56 -0
- package/src/components/PairedBarChart.jsx +5 -10
- package/src/components/PairedBarChart.test.tsx +71 -0
- package/src/components/PieChart/PieChart.tsx +9 -8
- package/src/components/RacingChartRenderer.tsx +44 -0
- package/src/components/Regions/components/Regions.test.tsx +179 -0
- package/src/components/Regions/components/Regions.tsx +104 -69
- package/src/components/Regions/index.tsx +2 -3
- package/src/components/Sankey/components/Sankey.tsx +264 -437
- package/src/components/Sankey/helpers/getSankeyTooltip.ts +12 -0
- package/src/components/Sankey/helpers/prepareSankeyData.test.ts +71 -0
- package/src/components/Sankey/helpers/prepareSankeyData.ts +90 -0
- package/src/components/Sankey/sankey.scss +57 -116
- package/src/components/Sankey/types/index.ts +41 -4
- package/src/components/ScatterPlot/ScatterPlot.jsx +28 -4
- package/src/components/ScatterPlot/ScatterPlot.test.tsx +60 -0
- package/src/components/SmallMultiples/SmallMultiples.tsx +12 -12
- package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +7 -5
- package/src/components/raceTiming.ts +18 -0
- package/src/data/initial-state.js +77 -31
- package/src/helpers/applyChartTypeContract.ts +71 -0
- package/src/helpers/axisLabelFontSize.ts +2 -2
- package/src/helpers/boxPlotStats.ts +60 -0
- package/src/helpers/bumpChartTooltip.ts +2 -1
- package/src/helpers/calculateHorizontalBarCategoryLabelWidth.test.ts +37 -0
- package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +3 -2
- package/src/helpers/calculateRightYAxisWidth.ts +60 -0
- package/src/helpers/colorAssignmentHelpers.ts +2 -2
- package/src/helpers/createNewChartConfig.ts +56 -0
- package/src/helpers/dataDrivenYAxisCategories.ts +18 -0
- package/src/helpers/forecastLegend.ts +88 -0
- package/src/helpers/getBoxPlotConfig.ts +18 -37
- package/src/helpers/getChartTypeDefaultPalette.ts +29 -0
- package/src/helpers/getColorCodeCategoryColorMap.ts +26 -0
- package/src/helpers/getColorScale.ts +31 -22
- package/src/helpers/getDynamicYAxisCategories.ts +69 -0
- package/src/helpers/getMinMax.ts +9 -7
- package/src/helpers/getNewRuntime.ts +5 -2
- package/src/helpers/getStackedSeriesMax.ts +51 -0
- package/src/helpers/getV21ChartDistributionColors.ts +57 -0
- package/src/helpers/handleRankByValue.ts +15 -15
- package/src/helpers/seriesColumnSettings.ts +10 -0
- package/src/helpers/sizeHelpers.ts +4 -3
- package/src/helpers/smallMultiplesHelpers.ts +10 -13
- package/src/helpers/tests/applyChartTypeContract.test.ts +37 -0
- package/src/helpers/tests/boxPlotStats.test.ts +31 -0
- package/src/helpers/tests/bumpChartTooltip.test.ts +29 -0
- package/src/helpers/tests/calculateRightYAxisWidth.test.ts +105 -0
- package/src/helpers/tests/countNumOfTicks.test.ts +11 -0
- package/src/helpers/tests/createNewChartConfig.test.ts +182 -0
- package/src/helpers/tests/getBoxPlotConfig.test.ts +70 -0
- package/src/helpers/tests/getColorCodeCategoryColorMap.test.ts +113 -0
- package/src/helpers/tests/getColorScale.test.ts +226 -1
- package/src/helpers/tests/getDynamicYAxisCategories.test.ts +61 -0
- package/src/helpers/tests/getMinMax.test.ts +41 -0
- package/src/helpers/tests/getStackedSeriesMax.test.ts +26 -0
- package/src/helpers/tests/handleRankByValue.test.ts +55 -37
- package/src/helpers/tests/seriesColumnSettings.test.ts +23 -0
- package/src/helpers/tests/smallMultiplesHelpers.test.ts +37 -1
- package/src/helpers/tests/tooltipHelpers.test.ts +80 -0
- package/src/helpers/tooltipHelpers.ts +16 -0
- package/src/hooks/tests/useTooltip.test.tsx +353 -1
- package/src/hooks/usePrefersReducedMotion.ts +22 -0
- package/src/hooks/useReduceData.ts +4 -36
- package/src/hooks/useScales.ts +15 -0
- package/src/hooks/useTooltip.tsx +95 -64
- package/src/scss/main.scss +74 -2
- package/src/store/chart.reducer.ts +4 -3
- package/src/test/CdcChart.dataTable.test.tsx +490 -16
- package/src/test/CdcChart.dendrogram.test.tsx +277 -0
- package/src/test/CdcChart.finalization.test.tsx +143 -0
- package/src/test/CdcChart.network.test.tsx +271 -0
- package/src/types/ChartConfig.ts +91 -54
- package/src/types/ChartContext.ts +5 -0
- package/src/types/Horizon.ts +1 -1
- package/LICENSE +0 -201
- package/dist/cdcchart-BWVIUcAF.es.js +0 -4802
- package/src/_stories/Chart.Sankey.smoke.stories.tsx +0 -62
- package/src/components/Sankey/components/ColumnList.tsx +0 -19
- package/src/components/Sankey/helpers/getSankeyTooltip.tsx +0 -34
- package/src/components/Sankey/useSankeyAlert.tsx +0 -60
- package/src/data/legacy-defaults.ts +0 -18
- package/src/helpers/ensureSpecialChartAxisTypes.ts +0 -11
- package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +0 -30
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.line-chart-race {
|
|
2
|
+
width: 100%;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.line-chart-race__header {
|
|
6
|
+
align-items: center;
|
|
7
|
+
display: flex;
|
|
8
|
+
gap: 0.75rem;
|
|
9
|
+
justify-content: flex-start;
|
|
10
|
+
margin-bottom: 1rem;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.line-chart-race__frame {
|
|
14
|
+
font-family: var(--fonts-nunito, var(--app-font-secondary));
|
|
15
|
+
font-size: 1.5rem;
|
|
16
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Line } from '@visx/shape'
|
|
3
|
+
import { type Anchor } from '@cdc/core/types/Axis'
|
|
4
|
+
|
|
5
|
+
const DEFAULT_ANCHOR_STROKE_WIDTH = 1
|
|
6
|
+
|
|
7
|
+
export const alignStrokeToPixel = (position: number, strokeWidth = DEFAULT_ANCHOR_STROKE_WIDTH) =>
|
|
8
|
+
strokeWidth % 2 === 1 ? Math.round(position - 0.5) + 0.5 : Math.round(position)
|
|
9
|
+
|
|
10
|
+
const hasAnchorValue = (value: Anchor['value']) => value !== undefined && value !== null && value !== ''
|
|
11
|
+
|
|
12
|
+
type ValueAxisAnchorsProps = {
|
|
13
|
+
anchors?: Anchor[]
|
|
14
|
+
className: string
|
|
15
|
+
getOffset?: () => number
|
|
16
|
+
handleLineType: (lineStyle: string) => string
|
|
17
|
+
keyPrefix: string
|
|
18
|
+
xEnd: number
|
|
19
|
+
xStart: number
|
|
20
|
+
yScale: (value: string) => number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const ValueAxisAnchors: React.FC<ValueAxisAnchorsProps> = ({
|
|
24
|
+
anchors,
|
|
25
|
+
className,
|
|
26
|
+
getOffset = () => 0,
|
|
27
|
+
handleLineType,
|
|
28
|
+
keyPrefix,
|
|
29
|
+
xEnd,
|
|
30
|
+
xStart,
|
|
31
|
+
yScale
|
|
32
|
+
}) => (
|
|
33
|
+
<>
|
|
34
|
+
{anchors?.map((anchor, index) => {
|
|
35
|
+
if (!hasAnchorValue(anchor.value)) return null
|
|
36
|
+
|
|
37
|
+
const position = yScale(anchor.value)
|
|
38
|
+
if (position === undefined || position === null || Number.isNaN(Number(position))) return null
|
|
39
|
+
|
|
40
|
+
const anchorYPosition = alignStrokeToPixel(position - getOffset())
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
// prettier-ignore
|
|
44
|
+
<Line
|
|
45
|
+
key={`${keyPrefix}-${anchor.value}--${index}`}
|
|
46
|
+
strokeDasharray={handleLineType(anchor.lineStyle)}
|
|
47
|
+
stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
|
|
48
|
+
fill={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
|
|
49
|
+
className={className}
|
|
50
|
+
from={{ x: xStart, y: anchorYPosition }}
|
|
51
|
+
to={{ x: xEnd, y: anchorYPosition }}
|
|
52
|
+
/>
|
|
53
|
+
)
|
|
54
|
+
})}
|
|
55
|
+
</>
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
export default ValueAxisAnchors
|
|
@@ -67,6 +67,19 @@ const VisualizationRenderer: React.FC<VisualizationRendererProps> = ({
|
|
|
67
67
|
}) => {
|
|
68
68
|
const { config, convertLineToBarGraph } = useContext(ConfigContext)
|
|
69
69
|
const { visualizationType, visualizationSubType } = config
|
|
70
|
+
const isCombo = visualizationType === VISUALIZATION_TYPES.COMBO
|
|
71
|
+
const comboHasAreaSeries = isCombo && Boolean(config.runtime?.areaSeriesKeys?.length)
|
|
72
|
+
const comboHasBarSeries = isCombo && Boolean(config.runtime?.barSeriesKeys?.length)
|
|
73
|
+
const comboHasLineSeries = isCombo && Boolean(config.runtime?.lineSeriesKeys?.length)
|
|
74
|
+
const comboHasForecastingSeries = isCombo && Boolean(config.runtime?.forecastingSeriesKeys?.length)
|
|
75
|
+
const shouldRenderAreaChart =
|
|
76
|
+
(visualizationType === VISUALIZATION_TYPES.AREA_CHART && visualizationSubType === 'stacked') || comboHasAreaSeries
|
|
77
|
+
const shouldRenderBarChart =
|
|
78
|
+
visualizationType === VISUALIZATION_TYPES.BAR || comboHasBarSeries || convertLineToBarGraph
|
|
79
|
+
const shouldRenderLineChart =
|
|
80
|
+
comboHasLineSeries ||
|
|
81
|
+
(!isCombo && !LINE_CHART_EXCLUDED_TYPES.includes(visualizationType as any) && !convertLineToBarGraph)
|
|
82
|
+
const shouldRenderForecasting = visualizationType === VISUALIZATION_TYPES.FORECASTING || comboHasForecastingSeries
|
|
70
83
|
|
|
71
84
|
return (
|
|
72
85
|
<>
|
|
@@ -155,8 +168,7 @@ const VisualizationRenderer: React.FC<VisualizationRendererProps> = ({
|
|
|
155
168
|
)}
|
|
156
169
|
|
|
157
170
|
{/* Area Chart (Stacked) - also used by Combo */}
|
|
158
|
-
{
|
|
159
|
-
visualizationType === 'Combo') && (
|
|
171
|
+
{shouldRenderAreaChart && (
|
|
160
172
|
<AreaChartStacked
|
|
161
173
|
xScale={xScale}
|
|
162
174
|
yScale={yScale}
|
|
@@ -174,7 +186,7 @@ const VisualizationRenderer: React.FC<VisualizationRendererProps> = ({
|
|
|
174
186
|
)}
|
|
175
187
|
|
|
176
188
|
{/* Bar Chart - also used by Combo and when line is converted to bar */}
|
|
177
|
-
{
|
|
189
|
+
{shouldRenderBarChart && (
|
|
178
190
|
<BarChart
|
|
179
191
|
xScale={xScale}
|
|
180
192
|
yScale={yScale}
|
|
@@ -195,8 +207,8 @@ const VisualizationRenderer: React.FC<VisualizationRendererProps> = ({
|
|
|
195
207
|
/>
|
|
196
208
|
)}
|
|
197
209
|
|
|
198
|
-
{/* Line Chart
|
|
199
|
-
{
|
|
210
|
+
{/* Line Chart */}
|
|
211
|
+
{shouldRenderLineChart && (
|
|
200
212
|
<LineChart
|
|
201
213
|
xScale={xScale}
|
|
202
214
|
yScale={yScale}
|
|
@@ -214,25 +226,8 @@ const VisualizationRenderer: React.FC<VisualizationRendererProps> = ({
|
|
|
214
226
|
/>
|
|
215
227
|
)}
|
|
216
228
|
|
|
217
|
-
{/* Line Chart for other visualization types */}
|
|
218
|
-
{!LINE_CHART_EXCLUDED_TYPES.includes(visualizationType as any) && !convertLineToBarGraph && (
|
|
219
|
-
<LineChart
|
|
220
|
-
xScale={xScale}
|
|
221
|
-
yScale={yScale}
|
|
222
|
-
yAxisWidth={yAxisWidth}
|
|
223
|
-
getXAxisData={getXAxisData}
|
|
224
|
-
getYAxisData={getYAxisData}
|
|
225
|
-
xMax={xMax}
|
|
226
|
-
yMax={yMax}
|
|
227
|
-
seriesStyle={config.runtime.series}
|
|
228
|
-
tooltipData={tooltipData}
|
|
229
|
-
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
230
|
-
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
231
|
-
/>
|
|
232
|
-
)}
|
|
233
|
-
|
|
234
229
|
{/* Forecasting - also used by Combo */}
|
|
235
|
-
{
|
|
230
|
+
{shouldRenderForecasting && (
|
|
236
231
|
<Forecasting
|
|
237
232
|
showTooltip={showTooltip}
|
|
238
233
|
tooltipData={tooltipData}
|
|
@@ -1,30 +1,54 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { render, screen } from '@testing-library/react'
|
|
3
|
-
import { describe, expect, it, vi, beforeAll } from 'vitest'
|
|
3
|
+
import { describe, expect, it, vi, beforeAll, beforeEach } from 'vitest'
|
|
4
4
|
import LinearChart from '../../LinearChart'
|
|
5
|
+
import RightAxis from '../../Axis/RightAxis'
|
|
5
6
|
import ConfigContext from '../../../ConfigContext'
|
|
6
7
|
import { createMockChartContext } from './mockConfigContext'
|
|
7
8
|
import forestPlotConfig from '../../../../examples/feature/forest-plot/forest-plot.json'
|
|
8
9
|
import * as suppressionHelpers from '../../../helpers/getHasBoundarySuppression'
|
|
10
|
+
import { scaleLinear } from '@visx/scale'
|
|
11
|
+
|
|
12
|
+
const visualizationRendererMockState = vi.hoisted(() => ({
|
|
13
|
+
renderActual: false
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
vi.mock('../../LinearChart/VisualizationRenderer', async importOriginal => {
|
|
17
|
+
const React = await import('react')
|
|
18
|
+
const actual = await importOriginal<typeof import('../../LinearChart/VisualizationRenderer')>()
|
|
19
|
+
|
|
20
|
+
const MockVisualizationRenderer = props => {
|
|
21
|
+
if (visualizationRendererMockState.renderActual) return React.createElement(actual.default, props)
|
|
22
|
+
return React.createElement('g', { 'data-testid': 'mock-visualization-renderer' })
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
default: MockVisualizationRenderer
|
|
27
|
+
}
|
|
28
|
+
})
|
|
9
29
|
|
|
10
30
|
// Mock ResizeObserver
|
|
11
31
|
vi.stubGlobal(
|
|
12
32
|
'ResizeObserver',
|
|
13
|
-
vi.fn(()
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
33
|
+
vi.fn(function ResizeObserver() {
|
|
34
|
+
return {
|
|
35
|
+
observe: vi.fn(),
|
|
36
|
+
unobserve: vi.fn(),
|
|
37
|
+
disconnect: vi.fn()
|
|
38
|
+
}
|
|
39
|
+
})
|
|
18
40
|
)
|
|
19
41
|
|
|
20
42
|
// Mock IntersectionObserver
|
|
21
43
|
vi.stubGlobal(
|
|
22
44
|
'IntersectionObserver',
|
|
23
|
-
vi.fn(()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
45
|
+
vi.fn(function IntersectionObserver() {
|
|
46
|
+
return {
|
|
47
|
+
observe: vi.fn(),
|
|
48
|
+
unobserve: vi.fn(),
|
|
49
|
+
disconnect: vi.fn()
|
|
50
|
+
}
|
|
51
|
+
})
|
|
28
52
|
)
|
|
29
53
|
|
|
30
54
|
// Mock canvas for text measurement
|
|
@@ -53,6 +77,10 @@ beforeAll(() => {
|
|
|
53
77
|
}))
|
|
54
78
|
})
|
|
55
79
|
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
visualizationRendererMockState.renderActual = false
|
|
82
|
+
})
|
|
83
|
+
|
|
56
84
|
// Helper to render LinearChart with context
|
|
57
85
|
const renderLinearChart = (
|
|
58
86
|
configOverrides = {},
|
|
@@ -73,6 +101,40 @@ const getLeftAxisLabelTransforms = container =>
|
|
|
73
101
|
.map(label => label.getAttribute('transform'))
|
|
74
102
|
.filter(Boolean)
|
|
75
103
|
|
|
104
|
+
const renderRightAxis = (yAxisOverrides, runtimeYAxisOverrides = {}, rightAxisWidth = 70) => {
|
|
105
|
+
const context = createMockChartContext({
|
|
106
|
+
yAxis: {
|
|
107
|
+
...createMockChartContext().config.yAxis,
|
|
108
|
+
rightLabel: 'Right Axis',
|
|
109
|
+
...yAxisOverrides
|
|
110
|
+
},
|
|
111
|
+
runtime: {
|
|
112
|
+
...createMockChartContext().config.runtime,
|
|
113
|
+
yAxis: {
|
|
114
|
+
...createMockChartContext().config.runtime.yAxis,
|
|
115
|
+
rightNumTicks: 4,
|
|
116
|
+
...runtimeYAxisOverrides
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
return render(
|
|
122
|
+
<ConfigContext.Provider value={context}>
|
|
123
|
+
<svg>
|
|
124
|
+
<RightAxis
|
|
125
|
+
yScaleRight={scaleLinear({ domain: [0, 100], range: [300, 0] })}
|
|
126
|
+
yMax={300}
|
|
127
|
+
xMax={400}
|
|
128
|
+
yAxisWidth={50}
|
|
129
|
+
rightAxisWidth={rightAxisWidth}
|
|
130
|
+
tickLabelFontSize={12}
|
|
131
|
+
axisLabelFontSize={14}
|
|
132
|
+
/>
|
|
133
|
+
</svg>
|
|
134
|
+
</ConfigContext.Provider>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
76
138
|
const sidePlacementYAxis = {
|
|
77
139
|
hideAxis: false,
|
|
78
140
|
hideLabel: false,
|
|
@@ -85,7 +147,8 @@ const sidePlacementYAxis = {
|
|
|
85
147
|
anchors: [],
|
|
86
148
|
axisPadding: 0,
|
|
87
149
|
labelPlacement: 'On Date/Category Axis',
|
|
88
|
-
rightAxisSize: 0
|
|
150
|
+
rightAxisSize: 0,
|
|
151
|
+
rightTitlePlacement: 'side'
|
|
89
152
|
}
|
|
90
153
|
|
|
91
154
|
describe('LinearChart', () => {
|
|
@@ -147,6 +210,112 @@ describe('LinearChart', () => {
|
|
|
147
210
|
const svg = container.querySelector('svg')
|
|
148
211
|
expect(svg?.classList.contains('animated')).toBe(false)
|
|
149
212
|
})
|
|
213
|
+
|
|
214
|
+
it('renders region fills above gridlines, below visualization marks, and keeps region labels above marks', () => {
|
|
215
|
+
const rows = [
|
|
216
|
+
{ Date: '2024-01-01', Value: 10 },
|
|
217
|
+
{ Date: '2024-01-08', Value: 20 }
|
|
218
|
+
]
|
|
219
|
+
const { container } = renderLinearChart(
|
|
220
|
+
{
|
|
221
|
+
regions: [
|
|
222
|
+
{
|
|
223
|
+
from: '2024-01-01',
|
|
224
|
+
to: '2024-01-08',
|
|
225
|
+
label: 'Reporting period',
|
|
226
|
+
background: '#005ea8',
|
|
227
|
+
color: '#1b1b1b'
|
|
228
|
+
}
|
|
229
|
+
],
|
|
230
|
+
series: [{ dataKey: 'Value', type: 'Line', axis: 'Left' }],
|
|
231
|
+
runtime: {
|
|
232
|
+
...createMockChartContext().config.runtime,
|
|
233
|
+
seriesKeys: ['Value'],
|
|
234
|
+
seriesLabels: { Value: 'Value' }
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
{ transformedData: rows, tableData: rows }
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
const fill = container.querySelector('.region-fill')
|
|
241
|
+
const gridlines = container.querySelector('.left-axis')
|
|
242
|
+
const visualization = container.querySelector('[data-testid="mock-visualization-renderer"]')
|
|
243
|
+
const label = container.querySelector('.region-label')
|
|
244
|
+
|
|
245
|
+
expect(fill).toBeTruthy()
|
|
246
|
+
expect(label).toBeTruthy()
|
|
247
|
+
expect(container.querySelectorAll('.region-fill')).toHaveLength(1)
|
|
248
|
+
expect(gridlines!.compareDocumentPosition(fill!) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy()
|
|
249
|
+
expect(fill!.compareDocumentPosition(visualization!) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy()
|
|
250
|
+
expect(visualization!.compareDocumentPosition(label!) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy()
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('uses band boundaries for converted lines and point boundaries for barless Combo charts', () => {
|
|
254
|
+
const rows = [
|
|
255
|
+
{ Date: '2024-01-01', Value: 10 },
|
|
256
|
+
{ Date: '2024-01-08', Value: 20 },
|
|
257
|
+
{ Date: '2024-01-15', Value: 30 }
|
|
258
|
+
]
|
|
259
|
+
const baseConfig = {
|
|
260
|
+
regions: [
|
|
261
|
+
{
|
|
262
|
+
from: '2024-01-01',
|
|
263
|
+
to: '2024-01-08',
|
|
264
|
+
label: 'Reporting period',
|
|
265
|
+
background: '#005ea8',
|
|
266
|
+
color: '#1b1b1b'
|
|
267
|
+
}
|
|
268
|
+
],
|
|
269
|
+
series: [{ dataKey: 'Value', type: 'Line', axis: 'Left' }],
|
|
270
|
+
runtime: {
|
|
271
|
+
...createMockChartContext().config.runtime,
|
|
272
|
+
seriesKeys: ['Value'],
|
|
273
|
+
seriesLabels: { Value: 'Value' }
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const converted = renderLinearChart(baseConfig, {
|
|
278
|
+
transformedData: rows,
|
|
279
|
+
tableData: rows,
|
|
280
|
+
convertLineToBarGraph: true
|
|
281
|
+
})
|
|
282
|
+
const convertedWidth = Number(converted.container.querySelector('.region-fill')?.getAttribute('width'))
|
|
283
|
+
converted.unmount()
|
|
284
|
+
|
|
285
|
+
const barlessCombo = renderLinearChart(
|
|
286
|
+
{
|
|
287
|
+
...baseConfig,
|
|
288
|
+
visualizationType: 'Combo',
|
|
289
|
+
runtime: {
|
|
290
|
+
...baseConfig.runtime,
|
|
291
|
+
barSeriesKeys: [],
|
|
292
|
+
lineSeriesKeys: ['Value']
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
{ transformedData: rows, tableData: rows }
|
|
296
|
+
)
|
|
297
|
+
const barlessComboWidth = Number(barlessCombo.container.querySelector('.region-fill')?.getAttribute('width'))
|
|
298
|
+
|
|
299
|
+
expect(convertedWidth).toBeGreaterThan(barlessComboWidth)
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
it('does not render regions for visualization types outside the explicit region whitelist', () => {
|
|
303
|
+
const { container } = renderLinearChart({
|
|
304
|
+
visualizationType: 'Scatter Plot',
|
|
305
|
+
regions: [
|
|
306
|
+
{
|
|
307
|
+
from: 'A',
|
|
308
|
+
to: 'B',
|
|
309
|
+
label: 'Reporting period',
|
|
310
|
+
background: '#005ea8',
|
|
311
|
+
color: '#1b1b1b'
|
|
312
|
+
}
|
|
313
|
+
]
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
expect(container.querySelector('.region-fill')).toBeNull()
|
|
317
|
+
expect(container.querySelector('.region-label')).toBeNull()
|
|
318
|
+
})
|
|
150
319
|
})
|
|
151
320
|
|
|
152
321
|
describe('empty data handling', () => {
|
|
@@ -200,6 +369,8 @@ describe('LinearChart', () => {
|
|
|
200
369
|
})
|
|
201
370
|
|
|
202
371
|
it('keeps forest plot lines inside the computed plot bounds at narrow and wide widths', () => {
|
|
372
|
+
visualizationRendererMockState.renderActual = true
|
|
373
|
+
|
|
203
374
|
const forestContextOverrides = {
|
|
204
375
|
transformedData: forestPlotConfig.data,
|
|
205
376
|
rawData: forestPlotConfig.data
|
|
@@ -242,6 +413,8 @@ describe('LinearChart', () => {
|
|
|
242
413
|
})
|
|
243
414
|
|
|
244
415
|
it('avoids rendering a duplicate manual bottom border when the forest plot x-axis is visible', () => {
|
|
416
|
+
visualizationRendererMockState.renderActual = true
|
|
417
|
+
|
|
245
418
|
const { container } = renderLinearChart(
|
|
246
419
|
forestPlotConfig as any,
|
|
247
420
|
{
|
|
@@ -258,6 +431,8 @@ describe('LinearChart', () => {
|
|
|
258
431
|
})
|
|
259
432
|
|
|
260
433
|
it('renders forest plot rows from transformedData instead of rawData', () => {
|
|
434
|
+
visualizationRendererMockState.renderActual = true
|
|
435
|
+
|
|
261
436
|
const filteredData = forestPlotConfig.data.slice(0, 2)
|
|
262
437
|
const { container } = renderLinearChart(
|
|
263
438
|
forestPlotConfig as any,
|
|
@@ -277,6 +452,206 @@ describe('LinearChart', () => {
|
|
|
277
452
|
})
|
|
278
453
|
|
|
279
454
|
describe('axis rendering', () => {
|
|
455
|
+
it('renders left y-axis and x-axis anchors independently', () => {
|
|
456
|
+
const data = [
|
|
457
|
+
{ Category: 'A', Value: 5 },
|
|
458
|
+
{ Category: 'B', Value: 10 }
|
|
459
|
+
]
|
|
460
|
+
|
|
461
|
+
const { container } = renderLinearChart(
|
|
462
|
+
{
|
|
463
|
+
visualizationType: 'Bar',
|
|
464
|
+
debugSvg: true,
|
|
465
|
+
data,
|
|
466
|
+
series: [{ dataKey: 'Value', axis: 'Left', type: 'Bar' }],
|
|
467
|
+
xAxis: {
|
|
468
|
+
...createMockChartContext().config.xAxis,
|
|
469
|
+
type: 'categorical',
|
|
470
|
+
dataKey: 'Category',
|
|
471
|
+
anchors: [{ value: 'A', color: '#135', lineStyle: 'dashed' }]
|
|
472
|
+
},
|
|
473
|
+
yAxis: {
|
|
474
|
+
...createMockChartContext().config.yAxis,
|
|
475
|
+
anchors: [{ value: '5', color: '#531', lineStyle: 'solid' }]
|
|
476
|
+
},
|
|
477
|
+
runtime: {
|
|
478
|
+
...createMockChartContext().config.runtime,
|
|
479
|
+
xAxis: {
|
|
480
|
+
...createMockChartContext().config.runtime.xAxis,
|
|
481
|
+
type: 'categorical',
|
|
482
|
+
dataKey: 'Category'
|
|
483
|
+
},
|
|
484
|
+
originalXAxis: {
|
|
485
|
+
dataKey: 'Category'
|
|
486
|
+
},
|
|
487
|
+
series: [{ dataKey: 'Value', axis: 'Left', type: 'Bar' }],
|
|
488
|
+
seriesKeys: ['Value']
|
|
489
|
+
}
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
transformedData: data,
|
|
493
|
+
tableData: data
|
|
494
|
+
}
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
expect(container.querySelectorAll('.anchor-y')).toHaveLength(1)
|
|
498
|
+
expect(container.querySelectorAll('.anchor-x')).toHaveLength(1)
|
|
499
|
+
expect(container.querySelector('.anchor-y-right')).toBeFalsy()
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
it('renders right y-axis anchors only when a vertical Combo chart has right-axis series', () => {
|
|
503
|
+
const data = [{ Date: '2024-01-01', LeftValue: 25, RightValue: 50 }]
|
|
504
|
+
const comboConfig = {
|
|
505
|
+
data,
|
|
506
|
+
visualizationType: 'Combo',
|
|
507
|
+
visualizationSubType: 'regular',
|
|
508
|
+
debugSvg: true,
|
|
509
|
+
orientation: 'vertical',
|
|
510
|
+
preliminaryData: [],
|
|
511
|
+
series: [
|
|
512
|
+
{ dataKey: 'LeftValue', axis: 'Left', type: 'Bar' },
|
|
513
|
+
{ dataKey: 'RightValue', axis: 'Right', type: 'Line' }
|
|
514
|
+
],
|
|
515
|
+
yAxis: {
|
|
516
|
+
...createMockChartContext().config.yAxis,
|
|
517
|
+
rightAnchors: [{ value: '50', color: '#246', lineStyle: 'dashed' }],
|
|
518
|
+
rightAxisSize: 60
|
|
519
|
+
},
|
|
520
|
+
runtime: {
|
|
521
|
+
...createMockChartContext().config.runtime,
|
|
522
|
+
series: [
|
|
523
|
+
{ dataKey: 'LeftValue', axis: 'Left', type: 'Bar' },
|
|
524
|
+
{ dataKey: 'RightValue', axis: 'Right', type: 'Line' }
|
|
525
|
+
],
|
|
526
|
+
seriesKeys: ['LeftValue', 'RightValue'],
|
|
527
|
+
areaSeriesKeys: [],
|
|
528
|
+
forecastingSeriesKeys: []
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const combo = renderLinearChart(comboConfig, {
|
|
533
|
+
transformedData: data,
|
|
534
|
+
tableData: data
|
|
535
|
+
})
|
|
536
|
+
const line = renderLinearChart(
|
|
537
|
+
{
|
|
538
|
+
...comboConfig,
|
|
539
|
+
visualizationType: 'Line'
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
transformedData: data,
|
|
543
|
+
tableData: data
|
|
544
|
+
}
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
expect(combo.container.querySelectorAll('.anchor-y-right')).toHaveLength(1)
|
|
548
|
+
expect(line.container.querySelector('.anchor-y-right')).toBeFalsy()
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
it('uses the right y-scale for right-axis anchors', () => {
|
|
552
|
+
const data = [{ Date: '2024-01-01', LeftValue: 50, RightValue: 80 }]
|
|
553
|
+
|
|
554
|
+
const { container } = renderLinearChart(
|
|
555
|
+
{
|
|
556
|
+
data,
|
|
557
|
+
visualizationType: 'Combo',
|
|
558
|
+
visualizationSubType: 'regular',
|
|
559
|
+
debugSvg: true,
|
|
560
|
+
orientation: 'vertical',
|
|
561
|
+
preliminaryData: [],
|
|
562
|
+
series: [
|
|
563
|
+
{ dataKey: 'LeftValue', axis: 'Left', type: 'Bar' },
|
|
564
|
+
{ dataKey: 'RightValue', axis: 'Right', type: 'Line' }
|
|
565
|
+
],
|
|
566
|
+
yAxis: {
|
|
567
|
+
...createMockChartContext().config.yAxis,
|
|
568
|
+
anchors: [{ value: '50', color: '#531', lineStyle: 'solid' }],
|
|
569
|
+
rightAnchors: [{ value: '50', color: '#246', lineStyle: 'dashed' }],
|
|
570
|
+
rightAxisSize: 60,
|
|
571
|
+
rightMin: '0',
|
|
572
|
+
rightMax: '100'
|
|
573
|
+
},
|
|
574
|
+
runtime: {
|
|
575
|
+
...createMockChartContext().config.runtime,
|
|
576
|
+
series: [
|
|
577
|
+
{ dataKey: 'LeftValue', axis: 'Left', type: 'Bar' },
|
|
578
|
+
{ dataKey: 'RightValue', axis: 'Right', type: 'Line' }
|
|
579
|
+
],
|
|
580
|
+
seriesKeys: ['LeftValue', 'RightValue'],
|
|
581
|
+
areaSeriesKeys: [],
|
|
582
|
+
forecastingSeriesKeys: []
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
transformedData: data,
|
|
587
|
+
tableData: data
|
|
588
|
+
}
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
const leftAnchorY = Number(container.querySelector('.anchor-y')?.getAttribute('y1'))
|
|
592
|
+
const rightAnchorY = Number(container.querySelector('.anchor-y-right')?.getAttribute('y1'))
|
|
593
|
+
|
|
594
|
+
expect(Number.isFinite(leftAnchorY)).toBe(true)
|
|
595
|
+
expect(Number.isFinite(rightAnchorY)).toBe(true)
|
|
596
|
+
expect(rightAnchorY).toBeGreaterThan(leftAnchorY)
|
|
597
|
+
})
|
|
598
|
+
|
|
599
|
+
it('renders numeric zero value-axis anchors', () => {
|
|
600
|
+
const data = [{ Date: '2024-01-01', LeftValue: 50, RightValue: 80 }]
|
|
601
|
+
|
|
602
|
+
const { container } = renderLinearChart(
|
|
603
|
+
{
|
|
604
|
+
data,
|
|
605
|
+
visualizationType: 'Combo',
|
|
606
|
+
visualizationSubType: 'regular',
|
|
607
|
+
debugSvg: true,
|
|
608
|
+
orientation: 'vertical',
|
|
609
|
+
preliminaryData: [],
|
|
610
|
+
series: [
|
|
611
|
+
{ dataKey: 'LeftValue', axis: 'Left', type: 'Bar' },
|
|
612
|
+
{ dataKey: 'RightValue', axis: 'Right', type: 'Line' }
|
|
613
|
+
],
|
|
614
|
+
yAxis: {
|
|
615
|
+
...createMockChartContext().config.yAxis,
|
|
616
|
+
anchors: [{ value: 0, color: '#531', lineStyle: 'solid' }],
|
|
617
|
+
rightAnchors: [{ value: 0, color: '#246', lineStyle: 'dashed' }],
|
|
618
|
+
rightAxisSize: 60,
|
|
619
|
+
rightMin: '0',
|
|
620
|
+
rightMax: '100'
|
|
621
|
+
},
|
|
622
|
+
runtime: {
|
|
623
|
+
...createMockChartContext().config.runtime,
|
|
624
|
+
series: [
|
|
625
|
+
{ dataKey: 'LeftValue', axis: 'Left', type: 'Bar' },
|
|
626
|
+
{ dataKey: 'RightValue', axis: 'Right', type: 'Line' }
|
|
627
|
+
],
|
|
628
|
+
seriesKeys: ['LeftValue', 'RightValue'],
|
|
629
|
+
areaSeriesKeys: [],
|
|
630
|
+
forecastingSeriesKeys: []
|
|
631
|
+
}
|
|
632
|
+
} as any,
|
|
633
|
+
{
|
|
634
|
+
transformedData: data,
|
|
635
|
+
tableData: data
|
|
636
|
+
}
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
expect(container.querySelectorAll('.anchor-y')).toHaveLength(1)
|
|
640
|
+
expect(container.querySelectorAll('.anchor-y-right')).toHaveLength(1)
|
|
641
|
+
})
|
|
642
|
+
|
|
643
|
+
it('does not error when configs omit rightAnchors', () => {
|
|
644
|
+
const { container } = renderLinearChart({
|
|
645
|
+
yAxis: {
|
|
646
|
+
...createMockChartContext().config.yAxis,
|
|
647
|
+
rightAnchors: undefined
|
|
648
|
+
}
|
|
649
|
+
})
|
|
650
|
+
|
|
651
|
+
expect(container.querySelector('svg')).toBeTruthy()
|
|
652
|
+
expect(container.querySelector('.anchor-y-right')).toBeFalsy()
|
|
653
|
+
})
|
|
654
|
+
|
|
280
655
|
it('renders left axis group', () => {
|
|
281
656
|
const { container } = renderLinearChart()
|
|
282
657
|
const leftAxis = container.querySelector('.left-axis')
|
|
@@ -304,6 +679,31 @@ describe('LinearChart', () => {
|
|
|
304
679
|
expect(container.querySelector('.left-axis text.y-label')).toBeTruthy()
|
|
305
680
|
})
|
|
306
681
|
|
|
682
|
+
it('renders the right axis side title when rightTitlePlacement is omitted', () => {
|
|
683
|
+
const { container } = renderRightAxis({ rightTitlePlacement: undefined })
|
|
684
|
+
|
|
685
|
+
expect(container.querySelector('.right-axis text.y-label')?.textContent).toBe('Right Axis')
|
|
686
|
+
})
|
|
687
|
+
|
|
688
|
+
it('renders the right axis side title from the processed runtime label when available', () => {
|
|
689
|
+
const { container } = renderRightAxis({ rightTitlePlacement: 'side' }, { rightLabel: 'Processed Right Axis' })
|
|
690
|
+
|
|
691
|
+
expect(container.querySelector('.right-axis text.y-label')?.textContent).toBe('Processed Right Axis')
|
|
692
|
+
})
|
|
693
|
+
|
|
694
|
+
it('places right tick labels after the tick margin and keeps the side title inside the measured gutter', () => {
|
|
695
|
+
const { container } = renderRightAxis({ rightTitlePlacement: 'side' }, {}, 73)
|
|
696
|
+
|
|
697
|
+
expect(container.querySelector('.right-axis .vx-axis-tick text')?.getAttribute('x')).toBe('12.5')
|
|
698
|
+
expect(container.querySelector('.right-axis text.y-label')?.getAttribute('transform')).toContain('translate(59,')
|
|
699
|
+
})
|
|
700
|
+
|
|
701
|
+
it('suppresses the right axis side title when rightTitlePlacement is top', () => {
|
|
702
|
+
const { container } = renderRightAxis({ rightTitlePlacement: 'top' })
|
|
703
|
+
|
|
704
|
+
expect(container.querySelector('.right-axis text.y-label')).toBeFalsy()
|
|
705
|
+
})
|
|
706
|
+
|
|
307
707
|
it('renders bottom axis group', () => {
|
|
308
708
|
const { container } = renderLinearChart()
|
|
309
709
|
const bottomAxis = container.querySelector('.bottom-axis')
|
|
@@ -515,7 +915,6 @@ describe('LinearChart', () => {
|
|
|
515
915
|
it('sets correct width based on parentWidth prop', () => {
|
|
516
916
|
const { container } = renderLinearChart({}, {}, { parentWidth: 600, parentHeight: 400 })
|
|
517
917
|
const svg = container.querySelector('svg')
|
|
518
|
-
// Width should include rightAxisSize (default 0)
|
|
519
918
|
expect(svg?.getAttribute('width')).toBe('600')
|
|
520
919
|
})
|
|
521
920
|
|