@cdc/chart 4.25.11 → 4.26.2
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/CLAUDE.local.md +79 -0
- package/dist/{cdcchart-dgT_1dIT.es.js → cdcchart-DQ00cQCm.es.js} +1 -20
- package/dist/cdcchart.js +51401 -50814
- package/examples/default.json +378 -0
- package/examples/feature/__data__/horizon-chart-data.json +373 -0
- package/examples/feature/annotations/index.json +3 -6
- package/examples/feature/horizon/horizon-chart.json +395 -0
- package/examples/feature/pie/planet-pie-example-config.json +48 -2
- package/examples/line-chart-states.json +1085 -0
- package/examples/private/123.json +694 -0
- package/examples/private/DEV-12100.json +1303 -0
- package/examples/private/anchor-issue.json +4094 -0
- package/examples/private/backwards-slider.json +10430 -0
- package/examples/private/cat-y.json +1235 -0
- package/examples/private/data-points.json +228 -0
- package/examples/private/georgia.csv +160 -0
- package/examples/private/height.json +3915 -0
- package/examples/private/links.json +569 -0
- package/examples/private/quadrant.txt +30 -0
- package/examples/private/test-forecast.json +5510 -0
- package/examples/private/timeline-data.json +1 -0
- package/examples/private/timeline.json +389 -0
- package/examples/private/warming-stripe-test.json +2578 -0
- package/examples/private/warming-stripes.json +4763 -0
- package/examples/radar-chart-simple.json +133 -0
- package/examples/radar-chart.json +148 -0
- package/examples/tech-adoption-with-links.json +560 -0
- package/index.html +1 -36
- package/package.json +59 -60
- package/src/CdcChartComponent.tsx +206 -89
- package/src/_stories/Chart.Anchors.stories.tsx +10 -0
- package/src/_stories/Chart.BoxPlot.stories.tsx +7 -0
- package/src/_stories/Chart.CI.stories.tsx +13 -0
- package/src/_stories/Chart.Combo.stories.tsx +17 -0
- package/src/_stories/Chart.CustomColors.stories.tsx +4 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +19 -0
- package/src/_stories/Chart.Filters.stories.tsx +4 -0
- package/src/_stories/Chart.Forecast.stories.tsx +4 -0
- package/src/_stories/Chart.HTMLInDataTable.stories.tsx +22 -0
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +28 -0
- package/src/_stories/Chart.Patterns.stories.tsx +4 -0
- package/src/_stories/Chart.PreserveDecimals.stories.tsx +25 -0
- package/src/_stories/Chart.Regions.Categorical.stories.tsx +161 -0
- package/src/_stories/Chart.Regions.DateScale.stories.tsx +216 -0
- package/src/_stories/Chart.Regions.DateTimeScale.stories.tsx +312 -0
- package/src/_stories/Chart.ScatterPlot.stories.tsx +4 -0
- package/src/_stories/Chart.SmallMultiples.stories.tsx +16 -0
- package/src/_stories/Chart.stories.tsx +45 -0
- package/src/_stories/Chart.tooltip.stories.tsx +7 -0
- package/src/_stories/ChartAnnotation.stories.tsx +10 -0
- package/src/_stories/ChartAxisLabels.stories.tsx +4 -0
- package/src/_stories/ChartAxisTitles.stories.tsx +10 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +11 -6
- package/src/_stories/ChartBrush.Editor.stories.tsx +295 -0
- package/src/_stories/ChartBrush.Matrix.Continuous.stories.tsx +41 -0
- package/src/_stories/ChartBrush.Matrix.Date.stories.tsx +114 -0
- package/src/_stories/ChartBrush.Matrix.DateTime.stories.tsx +78 -0
- package/src/_stories/ChartBrush.stories.tsx +57 -0
- package/src/_stories/ChartEditor.Editor.stories.tsx +3 -5
- package/src/_stories/ChartEditor.stories.tsx +7 -0
- package/src/_stories/ChartLine.QuadrantAngles.stories.tsx +89 -0
- package/src/_stories/ChartLine.Suppression.stories.tsx +7 -0
- package/src/_stories/ChartLine.Symbols.stories.tsx +4 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +46 -1
- package/src/_stories/TechAdoptionWithLinks.stories.tsx +34 -0
- package/src/_stories/_mock/brush_continuous.json +86 -0
- package/src/_stories/_mock/brush_date_large.json +176 -0
- package/src/_stories/_mock/brush_enabled.json +326 -0
- package/src/_stories/_mock/brush_mock.json +2 -69
- package/src/_stories/_mock/horizontal-bars-dynamic-y-axis.json +413 -0
- package/src/_stories/_mock/line_chart_angle_near_zero_fall.json +195 -0
- package/src/_stories/_mock/line_chart_angle_near_zero_rise.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q1_steep_upward.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q2_gentle_downward.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q3_steep_downward.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q4_gentle_upward.json +195 -0
- package/src/_stories/_mock/line_chart_quadrant_angles.json +264 -0
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +11 -17
- package/src/components/Annotations/components/AnnotationDraggable.tsx +240 -116
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -2
- package/src/components/Annotations/components/AnnotationDropdown.tsx +8 -12
- package/src/components/Annotations/components/AnnotationList.styles.css +4 -10
- package/src/components/Annotations/components/AnnotationList.tsx +5 -4
- package/src/components/Annotations/components/findNearestDatum.ts +75 -85
- package/src/components/Annotations/helpers/getVisibleAnnotations.ts +38 -0
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -2
- package/src/components/Axis/BottomAxis.tsx +270 -0
- package/src/components/Axis/Categorical.Axis.tsx +6 -7
- package/src/components/Axis/LeftAxis.tsx +404 -0
- package/src/components/Axis/LeftAxisGridlines.tsx +77 -0
- package/src/components/Axis/PairedBarAxis.tsx +186 -0
- package/src/components/Axis/README.md +94 -0
- package/src/components/Axis/RightAxis.tsx +108 -0
- package/src/components/Axis/axis.constants.ts +21 -0
- package/src/components/Axis/index.ts +7 -0
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +178 -24
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +3 -1
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +1 -0
- package/src/components/BarChart/components/BarChart.Vertical.tsx +6 -8
- package/src/components/BarChart/components/BarChart.tsx +7 -1
- package/src/components/BarChart/components/context.tsx +1 -0
- package/src/components/BarChart/helpers/useBarChart.ts +14 -2
- package/src/components/Brush/BrushSelector.tsx +1390 -0
- package/src/components/Brush/MiniChartPreview.tsx +400 -0
- package/src/components/DeviationBar.jsx +9 -7
- package/src/components/EditorPanel/EditorPanel.tsx +2734 -2595
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +60 -22
- package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +56 -34
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +137 -30
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +2 -0
- package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +353 -0
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +0 -1
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +30 -25
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +42 -28
- package/src/components/EditorPanel/components/Panels/index.tsx +2 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +81 -39
- package/src/components/HorizonChart/HorizonChart.tsx +131 -0
- package/src/components/HorizonChart/components/HorizonBand.tsx +160 -0
- package/src/components/HorizonChart/helpers/calculateHorizonBands.ts +27 -0
- package/src/components/HorizonChart/helpers/getHorizonLayerColors.ts +40 -0
- package/src/components/HorizonChart/index.tsx +3 -0
- package/src/components/Legend/Legend.Component.tsx +52 -4
- package/src/components/Legend/Legend.tsx +4 -3
- package/src/components/Legend/LegendValueRange.tsx +77 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +164 -2
- package/src/components/Legend/helpers/generateValueRanges.ts +92 -0
- package/src/components/Legend/helpers/index.ts +10 -6
- package/src/components/LineChart/helpers/README.md +292 -0
- package/src/components/LineChart/helpers/labelPositioning.test.ts +245 -0
- package/src/components/LineChart/helpers/labelPositioning.ts +304 -0
- package/src/components/LineChart/index.tsx +44 -8
- package/src/components/LinearChart/README.md +109 -0
- package/src/components/LinearChart/VisualizationRenderer.tsx +267 -0
- package/src/components/LinearChart/linearChart.constants.ts +84 -0
- package/src/components/LinearChart/tests/LinearChart.test.tsx +201 -0
- package/src/components/LinearChart/tests/mockConfigContext.ts +129 -0
- package/src/components/LinearChart/utils/tickFormatting.ts +146 -0
- package/src/components/LinearChart.tsx +338 -1082
- package/src/components/PairedBarChart.jsx +20 -3
- package/src/components/PieChart/PieChart.tsx +1 -1
- package/src/components/RadarChart/RadarAxis.tsx +78 -0
- package/src/components/RadarChart/RadarChart.tsx +298 -0
- package/src/components/RadarChart/RadarGrid.tsx +64 -0
- package/src/components/RadarChart/RadarPolygon.tsx +91 -0
- package/src/components/RadarChart/helpers.ts +83 -0
- package/src/components/RadarChart/index.tsx +3 -0
- package/src/components/Regions/components/Regions.tsx +365 -122
- package/src/components/ScatterPlot/ScatterPlot.jsx +2 -2
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +5 -1
- package/src/components/WarmingStripes/WarmingStripes.tsx +230 -0
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +35 -0
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +104 -0
- package/src/components/WarmingStripes/index.tsx +3 -0
- package/src/data/initial-state.js +17 -2
- package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +57 -0
- package/src/helpers/getExcludedData.ts +4 -0
- package/src/helpers/getMinMax.ts +12 -7
- package/src/helpers/handleChartAriaLabels.ts +19 -19
- package/src/helpers/handleLineType.ts +22 -18
- package/src/helpers/sizeHelpers.ts +0 -20
- package/src/helpers/smallMultiplesHelpers.ts +1 -1
- package/src/hooks/useChartHoverAnalytics.tsx +10 -9
- package/src/hooks/useProgrammaticTooltip.ts +23 -2
- package/src/hooks/useScales.ts +18 -1
- package/src/hooks/useTooltip.tsx +34 -10
- package/src/scss/DataTable.scss +0 -4
- package/src/scss/main.scss +22 -3
- package/src/selectors/README.md +68 -0
- package/src/store/chart.reducer.ts +2 -0
- package/src/test/CdcChart.test.jsx +1 -1
- package/src/types/ChartConfig.ts +21 -0
- package/src/types/ChartContext.ts +1 -0
- package/src/types/Horizon.ts +64 -0
- package/src/types/Label.ts +1 -0
- package/src/utils/analyticsTracking.ts +19 -0
- package/LICENSE +0 -201
- package/src/components/Annotations/components/helpers/index.tsx +0 -46
- package/src/components/Brush/BrushChart.tsx +0 -128
- package/src/components/Brush/BrushController.tsx +0 -71
- package/src/components/Brush/types.tsx +0 -8
- package/src/components/BrushChart.tsx +0 -223
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import ConfigContext from '../../ConfigContext'
|
|
3
|
+
import { VISUALIZATION_TYPES, LINE_CHART_EXCLUDED_TYPES } from './linearChart.constants'
|
|
4
|
+
|
|
5
|
+
// Visualization components
|
|
6
|
+
import { AreaChartStacked } from '../AreaChart'
|
|
7
|
+
import BarChart from '../BarChart'
|
|
8
|
+
import BoxPlotVertical from '../BoxPlot/BoxPlot.Vertical'
|
|
9
|
+
import BoxPlotHorizontal from '../BoxPlot/BoxPlot.Horizontal'
|
|
10
|
+
import DeviationBar from '../DeviationBar'
|
|
11
|
+
import Forecasting from '../Forecasting'
|
|
12
|
+
import ForestPlot from '../ForestPlot'
|
|
13
|
+
import { HorizonChart } from '../HorizonChart'
|
|
14
|
+
import LineChart from '../LineChart'
|
|
15
|
+
import PairedBarChart from '../PairedBarChart'
|
|
16
|
+
import ScatterPlot from '../ScatterPlot'
|
|
17
|
+
import WarmingStripes from '../WarmingStripes'
|
|
18
|
+
|
|
19
|
+
interface VisualizationRendererProps {
|
|
20
|
+
xScale: any
|
|
21
|
+
yScale: any
|
|
22
|
+
xMax: number
|
|
23
|
+
yMax: number
|
|
24
|
+
seriesScale: any
|
|
25
|
+
xScaleNoPadding: any
|
|
26
|
+
min: number
|
|
27
|
+
max: number
|
|
28
|
+
parentWidth: number
|
|
29
|
+
yAxisWidth: number
|
|
30
|
+
forestHeight: number
|
|
31
|
+
animatedChart: boolean
|
|
32
|
+
tooltipData: any
|
|
33
|
+
showTooltip: any
|
|
34
|
+
handleTooltipMouseOver: (e: any, additionalData?: any) => void
|
|
35
|
+
handleTooltipMouseOff: () => void
|
|
36
|
+
handleTooltipClick: (e: any) => void
|
|
37
|
+
getXAxisData: (d: any) => any
|
|
38
|
+
getYAxisData: (d: any, seriesKey: string) => any
|
|
39
|
+
svgRef: React.RefObject<any>
|
|
40
|
+
forestPlotRightLabelRef: React.RefObject<any>
|
|
41
|
+
synchronizedXValue?: any
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const VisualizationRenderer: React.FC<VisualizationRendererProps> = ({
|
|
45
|
+
xScale,
|
|
46
|
+
yScale,
|
|
47
|
+
xMax,
|
|
48
|
+
yMax,
|
|
49
|
+
seriesScale,
|
|
50
|
+
xScaleNoPadding,
|
|
51
|
+
min,
|
|
52
|
+
max,
|
|
53
|
+
parentWidth,
|
|
54
|
+
yAxisWidth,
|
|
55
|
+
forestHeight,
|
|
56
|
+
animatedChart,
|
|
57
|
+
tooltipData,
|
|
58
|
+
showTooltip,
|
|
59
|
+
handleTooltipMouseOver,
|
|
60
|
+
handleTooltipMouseOff,
|
|
61
|
+
handleTooltipClick,
|
|
62
|
+
getXAxisData,
|
|
63
|
+
getYAxisData,
|
|
64
|
+
svgRef,
|
|
65
|
+
forestPlotRightLabelRef,
|
|
66
|
+
synchronizedXValue
|
|
67
|
+
}) => {
|
|
68
|
+
const { config, convertLineToBarGraph } = useContext(ConfigContext)
|
|
69
|
+
const { visualizationType, visualizationSubType } = config
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<>
|
|
73
|
+
{/* Deviation Bar */}
|
|
74
|
+
{visualizationType === 'Deviation Bar' && config.runtime.series?.length === 1 && (
|
|
75
|
+
<DeviationBar animatedChart={animatedChart} xScale={xScale} yScale={yScale} width={xMax} height={yMax} />
|
|
76
|
+
)}
|
|
77
|
+
|
|
78
|
+
{/* Paired Bar Chart */}
|
|
79
|
+
{visualizationType === 'Paired Bar' && <PairedBarChart originalWidth={parentWidth} width={xMax} height={yMax} />}
|
|
80
|
+
|
|
81
|
+
{/* Scatter Plot */}
|
|
82
|
+
{visualizationType === 'Scatter Plot' && (
|
|
83
|
+
<ScatterPlot
|
|
84
|
+
xScale={xScale}
|
|
85
|
+
yScale={yScale}
|
|
86
|
+
getXAxisData={getXAxisData}
|
|
87
|
+
getYAxisData={getYAxisData}
|
|
88
|
+
xMax={xMax}
|
|
89
|
+
yMax={yMax}
|
|
90
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
91
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
92
|
+
handleTooltipClick={handleTooltipClick}
|
|
93
|
+
tooltipData={tooltipData}
|
|
94
|
+
showTooltip={showTooltip}
|
|
95
|
+
/>
|
|
96
|
+
)}
|
|
97
|
+
|
|
98
|
+
{/* Warming Stripes */}
|
|
99
|
+
{visualizationType === 'Warming Stripes' && (
|
|
100
|
+
<WarmingStripes
|
|
101
|
+
xScale={xScale}
|
|
102
|
+
yScale={yScale}
|
|
103
|
+
xMax={xMax}
|
|
104
|
+
yMax={yMax}
|
|
105
|
+
synchronizedXValue={synchronizedXValue}
|
|
106
|
+
showTooltip={showTooltip}
|
|
107
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
108
|
+
/>
|
|
109
|
+
)}
|
|
110
|
+
|
|
111
|
+
{/* Horizon Chart */}
|
|
112
|
+
{visualizationType === 'Horizon Chart' && (
|
|
113
|
+
<HorizonChart
|
|
114
|
+
xScale={xScale}
|
|
115
|
+
yScale={yScale}
|
|
116
|
+
xMax={xMax}
|
|
117
|
+
yMax={yMax}
|
|
118
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
119
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
120
|
+
tooltipData={tooltipData}
|
|
121
|
+
showTooltip={showTooltip}
|
|
122
|
+
/>
|
|
123
|
+
)}
|
|
124
|
+
|
|
125
|
+
{/* Box Plot - Vertical */}
|
|
126
|
+
{visualizationType === 'Box Plot' && config.orientation === 'vertical' && (
|
|
127
|
+
<BoxPlotVertical
|
|
128
|
+
seriesScale={seriesScale}
|
|
129
|
+
xMax={xMax}
|
|
130
|
+
yMax={yMax}
|
|
131
|
+
min={min}
|
|
132
|
+
max={max}
|
|
133
|
+
xScale={xScale}
|
|
134
|
+
yScale={yScale}
|
|
135
|
+
/>
|
|
136
|
+
)}
|
|
137
|
+
|
|
138
|
+
{/* Box Plot - Horizontal */}
|
|
139
|
+
{visualizationType === 'Box Plot' && config.orientation === 'horizontal' && (
|
|
140
|
+
<BoxPlotHorizontal
|
|
141
|
+
seriesScale={seriesScale}
|
|
142
|
+
xMax={xMax}
|
|
143
|
+
yMax={yMax}
|
|
144
|
+
min={min}
|
|
145
|
+
max={max}
|
|
146
|
+
xScale={xScale}
|
|
147
|
+
yScale={yScale}
|
|
148
|
+
/>
|
|
149
|
+
)}
|
|
150
|
+
|
|
151
|
+
{/* Area Chart (Stacked) - also used by Combo */}
|
|
152
|
+
{((visualizationType === 'Area Chart' && visualizationSubType === 'stacked') ||
|
|
153
|
+
visualizationType === 'Combo') && (
|
|
154
|
+
<AreaChartStacked
|
|
155
|
+
xScale={xScale}
|
|
156
|
+
yScale={yScale}
|
|
157
|
+
yMax={yMax}
|
|
158
|
+
xMax={xMax}
|
|
159
|
+
chartRef={svgRef}
|
|
160
|
+
width={xMax}
|
|
161
|
+
height={yMax}
|
|
162
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
163
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
164
|
+
tooltipData={tooltipData}
|
|
165
|
+
showTooltip={showTooltip}
|
|
166
|
+
/>
|
|
167
|
+
)}
|
|
168
|
+
|
|
169
|
+
{/* Bar Chart - also used by Combo and when line is converted to bar */}
|
|
170
|
+
{(visualizationType === 'Bar' || visualizationType === 'Combo' || convertLineToBarGraph) && (
|
|
171
|
+
<BarChart
|
|
172
|
+
xScale={xScale}
|
|
173
|
+
yScale={yScale}
|
|
174
|
+
seriesScale={seriesScale}
|
|
175
|
+
xMax={xMax}
|
|
176
|
+
yMax={yMax}
|
|
177
|
+
yAxisWidth={yAxisWidth}
|
|
178
|
+
getXAxisData={getXAxisData}
|
|
179
|
+
getYAxisData={getYAxisData}
|
|
180
|
+
animatedChart={animatedChart}
|
|
181
|
+
visible={animatedChart}
|
|
182
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
183
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
184
|
+
handleTooltipClick={handleTooltipClick}
|
|
185
|
+
tooltipData={tooltipData}
|
|
186
|
+
showTooltip={showTooltip}
|
|
187
|
+
chartRef={svgRef}
|
|
188
|
+
/>
|
|
189
|
+
)}
|
|
190
|
+
|
|
191
|
+
{/* Line Chart for Combo and Bump Chart */}
|
|
192
|
+
{(visualizationType === 'Combo' || visualizationType === 'Bump Chart') && (
|
|
193
|
+
<LineChart
|
|
194
|
+
xScale={xScale}
|
|
195
|
+
yScale={yScale}
|
|
196
|
+
getXAxisData={getXAxisData}
|
|
197
|
+
getYAxisData={getYAxisData}
|
|
198
|
+
xMax={xMax}
|
|
199
|
+
yMax={yMax}
|
|
200
|
+
seriesStyle={config.runtime.series}
|
|
201
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
202
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
203
|
+
handleTooltipClick={handleTooltipClick}
|
|
204
|
+
tooltipData={tooltipData}
|
|
205
|
+
showTooltip={showTooltip}
|
|
206
|
+
/>
|
|
207
|
+
)}
|
|
208
|
+
|
|
209
|
+
{/* Line Chart for other visualization types */}
|
|
210
|
+
{!LINE_CHART_EXCLUDED_TYPES.includes(visualizationType as any) && !convertLineToBarGraph && (
|
|
211
|
+
<LineChart
|
|
212
|
+
xScale={xScale}
|
|
213
|
+
yScale={yScale}
|
|
214
|
+
getXAxisData={getXAxisData}
|
|
215
|
+
getYAxisData={getYAxisData}
|
|
216
|
+
xMax={xMax}
|
|
217
|
+
yMax={yMax}
|
|
218
|
+
seriesStyle={config.runtime.series}
|
|
219
|
+
tooltipData={tooltipData}
|
|
220
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
221
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
222
|
+
/>
|
|
223
|
+
)}
|
|
224
|
+
|
|
225
|
+
{/* Forecasting - also used by Combo */}
|
|
226
|
+
{(visualizationType === 'Forecasting' || visualizationType === 'Combo') && (
|
|
227
|
+
<Forecasting
|
|
228
|
+
showTooltip={showTooltip}
|
|
229
|
+
tooltipData={tooltipData}
|
|
230
|
+
xScale={xScale}
|
|
231
|
+
yScale={yScale}
|
|
232
|
+
width={xMax}
|
|
233
|
+
height={yMax}
|
|
234
|
+
xScaleNoPadding={xScaleNoPadding}
|
|
235
|
+
chartRef={svgRef}
|
|
236
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
237
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
238
|
+
/>
|
|
239
|
+
)}
|
|
240
|
+
|
|
241
|
+
{/* Forest Plot */}
|
|
242
|
+
{visualizationType === 'Forest Plot' && (
|
|
243
|
+
<ForestPlot
|
|
244
|
+
xScale={xScale}
|
|
245
|
+
yScale={yScale}
|
|
246
|
+
seriesScale={seriesScale}
|
|
247
|
+
width={parentWidth}
|
|
248
|
+
height={forestHeight}
|
|
249
|
+
getXAxisData={getXAxisData}
|
|
250
|
+
getYAxisData={getYAxisData}
|
|
251
|
+
animatedChart={animatedChart}
|
|
252
|
+
visible={animatedChart}
|
|
253
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
254
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
255
|
+
handleTooltipClick={handleTooltipClick}
|
|
256
|
+
tooltipData={tooltipData}
|
|
257
|
+
showTooltip={showTooltip}
|
|
258
|
+
chartRef={svgRef}
|
|
259
|
+
config={config}
|
|
260
|
+
forestPlotRightLabelRef={forestPlotRightLabelRef}
|
|
261
|
+
/>
|
|
262
|
+
)}
|
|
263
|
+
</>
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export default VisualizationRenderer
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visualization type constants for LinearChart
|
|
3
|
+
* Centralizes string literals to prevent typos and enable type safety
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const VISUALIZATION_TYPES = {
|
|
7
|
+
AREA_CHART: 'Area Chart',
|
|
8
|
+
BAR: 'Bar',
|
|
9
|
+
BOX_PLOT: 'Box Plot',
|
|
10
|
+
BUMP_CHART: 'Bump Chart',
|
|
11
|
+
COMBO: 'Combo',
|
|
12
|
+
DEVIATION_BAR: 'Deviation Bar',
|
|
13
|
+
FORECASTING: 'Forecasting',
|
|
14
|
+
FOREST_PLOT: 'Forest Plot',
|
|
15
|
+
HORIZON_CHART: 'Horizon Chart',
|
|
16
|
+
LINE: 'Line',
|
|
17
|
+
PAIRED_BAR: 'Paired Bar',
|
|
18
|
+
RADAR: 'Radar',
|
|
19
|
+
SCATTER_PLOT: 'Scatter Plot',
|
|
20
|
+
SPARK_LINE: 'Spark Line',
|
|
21
|
+
WARMING_STRIPES: 'Warming Stripes'
|
|
22
|
+
} as const
|
|
23
|
+
|
|
24
|
+
export type VisualizationType = (typeof VISUALIZATION_TYPES)[keyof typeof VISUALIZATION_TYPES]
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Visualization types that don't display grid lines or standard axes
|
|
28
|
+
*/
|
|
29
|
+
export const TYPES_WITHOUT_GRID = [
|
|
30
|
+
VISUALIZATION_TYPES.FOREST_PLOT,
|
|
31
|
+
VISUALIZATION_TYPES.HORIZON_CHART,
|
|
32
|
+
VISUALIZATION_TYPES.SPARK_LINE,
|
|
33
|
+
VISUALIZATION_TYPES.WARMING_STRIPES
|
|
34
|
+
] as const
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Visualization types excluded from the generic LineChart rendering
|
|
38
|
+
* These types have their own dedicated rendering logic
|
|
39
|
+
*/
|
|
40
|
+
export const LINE_CHART_EXCLUDED_TYPES = [
|
|
41
|
+
VISUALIZATION_TYPES.AREA_CHART,
|
|
42
|
+
VISUALIZATION_TYPES.BAR,
|
|
43
|
+
VISUALIZATION_TYPES.BOX_PLOT,
|
|
44
|
+
VISUALIZATION_TYPES.DEVIATION_BAR,
|
|
45
|
+
VISUALIZATION_TYPES.FORECASTING,
|
|
46
|
+
VISUALIZATION_TYPES.HORIZON_CHART,
|
|
47
|
+
VISUALIZATION_TYPES.PAIRED_BAR,
|
|
48
|
+
VISUALIZATION_TYPES.SCATTER_PLOT,
|
|
49
|
+
VISUALIZATION_TYPES.WARMING_STRIPES
|
|
50
|
+
] as const
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Line-based visualization types (render with LineChart component)
|
|
54
|
+
*/
|
|
55
|
+
export const LINE_BASED_TYPES = [
|
|
56
|
+
VISUALIZATION_TYPES.AREA_CHART,
|
|
57
|
+
VISUALIZATION_TYPES.BUMP_CHART,
|
|
58
|
+
VISUALIZATION_TYPES.COMBO,
|
|
59
|
+
VISUALIZATION_TYPES.LINE
|
|
60
|
+
] as const
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Visualization types that support tooltip guides (hover lines)
|
|
64
|
+
*/
|
|
65
|
+
export const TYPES_WITH_TOOLTIP_GUIDES = [
|
|
66
|
+
VISUALIZATION_TYPES.AREA_CHART,
|
|
67
|
+
VISUALIZATION_TYPES.BAR,
|
|
68
|
+
VISUALIZATION_TYPES.COMBO,
|
|
69
|
+
VISUALIZATION_TYPES.LINE
|
|
70
|
+
] as const
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check if a visualization type should show grid lines
|
|
74
|
+
*/
|
|
75
|
+
export const shouldShowGrid = (visualizationType: string): boolean => {
|
|
76
|
+
return !TYPES_WITHOUT_GRID.includes(visualizationType as any)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if a visualization type uses the generic LineChart renderer
|
|
81
|
+
*/
|
|
82
|
+
export const usesLineChartRenderer = (visualizationType: string): boolean => {
|
|
83
|
+
return !LINE_CHART_EXCLUDED_TYPES.includes(visualizationType as any)
|
|
84
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { render, screen } from '@testing-library/react'
|
|
3
|
+
import { describe, expect, it, vi, beforeAll } from 'vitest'
|
|
4
|
+
import LinearChart from '../../LinearChart'
|
|
5
|
+
import ConfigContext from '../../../ConfigContext'
|
|
6
|
+
import { createMockChartContext } from './mockConfigContext'
|
|
7
|
+
|
|
8
|
+
// Mock ResizeObserver
|
|
9
|
+
vi.stubGlobal(
|
|
10
|
+
'ResizeObserver',
|
|
11
|
+
vi.fn(() => ({
|
|
12
|
+
observe: vi.fn(),
|
|
13
|
+
unobserve: vi.fn(),
|
|
14
|
+
disconnect: vi.fn()
|
|
15
|
+
}))
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
// Mock IntersectionObserver
|
|
19
|
+
vi.stubGlobal(
|
|
20
|
+
'IntersectionObserver',
|
|
21
|
+
vi.fn(() => ({
|
|
22
|
+
observe: vi.fn(),
|
|
23
|
+
unobserve: vi.fn(),
|
|
24
|
+
disconnect: vi.fn()
|
|
25
|
+
}))
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
// Mock canvas for text measurement
|
|
29
|
+
beforeAll(() => {
|
|
30
|
+
HTMLCanvasElement.prototype.getContext = vi.fn(() => ({
|
|
31
|
+
measureText: vi.fn(() => ({ width: 50 })),
|
|
32
|
+
fillText: vi.fn(),
|
|
33
|
+
fillRect: vi.fn(),
|
|
34
|
+
clearRect: vi.fn()
|
|
35
|
+
})) as any
|
|
36
|
+
|
|
37
|
+
// Mock SVG getBBox for axis measurements
|
|
38
|
+
const mockBBox = { x: 0, y: 0, width: 100, height: 50 }
|
|
39
|
+
// @ts-expect-error - mocking SVG method
|
|
40
|
+
SVGElement.prototype.getBBox = vi.fn(() => mockBBox)
|
|
41
|
+
// @ts-expect-error - mocking SVG method
|
|
42
|
+
SVGElement.prototype.getBoundingClientRect = vi.fn(() => ({
|
|
43
|
+
x: 0,
|
|
44
|
+
y: 0,
|
|
45
|
+
width: 100,
|
|
46
|
+
height: 50,
|
|
47
|
+
top: 0,
|
|
48
|
+
left: 0,
|
|
49
|
+
right: 100,
|
|
50
|
+
bottom: 50
|
|
51
|
+
}))
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// Helper to render LinearChart with context
|
|
55
|
+
const renderLinearChart = (
|
|
56
|
+
configOverrides = {},
|
|
57
|
+
contextOverrides = {},
|
|
58
|
+
props = { parentWidth: 800, parentHeight: 400 }
|
|
59
|
+
) => {
|
|
60
|
+
const context = createMockChartContext(configOverrides, contextOverrides)
|
|
61
|
+
|
|
62
|
+
return render(
|
|
63
|
+
<ConfigContext.Provider value={context}>
|
|
64
|
+
<LinearChart {...props} />
|
|
65
|
+
</ConfigContext.Provider>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
describe('LinearChart', () => {
|
|
70
|
+
describe('rendering', () => {
|
|
71
|
+
it('renders without crashing', () => {
|
|
72
|
+
const { container } = renderLinearChart()
|
|
73
|
+
expect(container).toBeTruthy()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('renders an SVG element', () => {
|
|
77
|
+
const { container } = renderLinearChart()
|
|
78
|
+
const svg = container.querySelector('svg')
|
|
79
|
+
expect(svg).toBeTruthy()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('renders with correct aria-label', () => {
|
|
83
|
+
const { container } = renderLinearChart()
|
|
84
|
+
const svg = container.querySelector('svg')
|
|
85
|
+
expect(svg?.getAttribute('aria-label')).toBe('Chart')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('applies animated class when config.animate is true', () => {
|
|
89
|
+
const { container } = renderLinearChart({ animate: true })
|
|
90
|
+
const svg = container.querySelector('svg')
|
|
91
|
+
expect(svg?.classList.contains('animated')).toBe(true)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('does not apply animated class when config.animate is false', () => {
|
|
95
|
+
const { container } = renderLinearChart({ animate: false })
|
|
96
|
+
const svg = container.querySelector('svg')
|
|
97
|
+
expect(svg?.classList.contains('animated')).toBe(false)
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
describe('empty data handling', () => {
|
|
102
|
+
it('renders no data message when filters result in empty data', () => {
|
|
103
|
+
const context = createMockChartContext(
|
|
104
|
+
{ filters: [{ columnName: 'test', active: 'test' }] },
|
|
105
|
+
{ transformedData: [] }
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
render(
|
|
109
|
+
<ConfigContext.Provider value={context}>
|
|
110
|
+
<LinearChart parentWidth={800} parentHeight={400} />
|
|
111
|
+
</ConfigContext.Provider>
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
expect(screen.getByText('No data available')).toBeTruthy()
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
describe('visualization types', () => {
|
|
119
|
+
it('renders Line chart type without crashing', () => {
|
|
120
|
+
const { container } = renderLinearChart({ visualizationType: 'Line' })
|
|
121
|
+
expect(container.querySelector('svg')).toBeTruthy()
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('handles Bar chart type without uncaught exceptions', () => {
|
|
125
|
+
// Bar charts require additional data/series setup - verify it renders without throwing
|
|
126
|
+
const { container } = renderLinearChart({
|
|
127
|
+
visualizationType: 'Bar',
|
|
128
|
+
orientation: 'vertical'
|
|
129
|
+
})
|
|
130
|
+
// ErrorBoundary will catch errors, so container should exist
|
|
131
|
+
expect(container).toBeTruthy()
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('handles horizontal Bar chart without uncaught exceptions', () => {
|
|
135
|
+
const { container } = renderLinearChart({
|
|
136
|
+
visualizationType: 'Bar',
|
|
137
|
+
orientation: 'horizontal'
|
|
138
|
+
})
|
|
139
|
+
expect(container).toBeTruthy()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('handles Area Chart type without uncaught exceptions', () => {
|
|
143
|
+
// Area charts require stacked data setup - verify it renders without throwing
|
|
144
|
+
const { container } = renderLinearChart({
|
|
145
|
+
visualizationType: 'Area Chart',
|
|
146
|
+
visualizationSubType: 'stacked'
|
|
147
|
+
})
|
|
148
|
+
expect(container).toBeTruthy()
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
describe('axis rendering', () => {
|
|
153
|
+
it('renders left axis group', () => {
|
|
154
|
+
const { container } = renderLinearChart()
|
|
155
|
+
const leftAxis = container.querySelector('.left-axis')
|
|
156
|
+
expect(leftAxis).toBeTruthy()
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('renders bottom axis group', () => {
|
|
160
|
+
const { container } = renderLinearChart()
|
|
161
|
+
const bottomAxis = container.querySelector('.bottom-axis')
|
|
162
|
+
expect(bottomAxis).toBeTruthy()
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('hides Y axis when hideAxis is true', () => {
|
|
166
|
+
const { container } = renderLinearChart({
|
|
167
|
+
yAxis: {
|
|
168
|
+
hideAxis: true,
|
|
169
|
+
hideLabel: false,
|
|
170
|
+
hideTicks: false,
|
|
171
|
+
size: '50',
|
|
172
|
+
gridLines: true,
|
|
173
|
+
label: 'Y-Axis',
|
|
174
|
+
tickRotation: 0,
|
|
175
|
+
anchors: [],
|
|
176
|
+
axisPadding: 0,
|
|
177
|
+
labelPlacement: 'On Date/Category Axis',
|
|
178
|
+
rightAxisSize: 0
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
// The axis line should be hidden, but grid lines may still render
|
|
182
|
+
expect(container.querySelector('svg')).toBeTruthy()
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
describe('SVG dimensions', () => {
|
|
187
|
+
it('sets correct width based on parentWidth prop', () => {
|
|
188
|
+
const { container } = renderLinearChart({}, {}, { parentWidth: 600, parentHeight: 400 })
|
|
189
|
+
const svg = container.querySelector('svg')
|
|
190
|
+
// Width should include rightAxisSize (default 0)
|
|
191
|
+
expect(svg?.getAttribute('width')).toBe('600')
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('returns empty fragment when parentWidth is NaN', () => {
|
|
195
|
+
const { container } = renderLinearChart({}, {}, { parentWidth: NaN, parentHeight: 400 })
|
|
196
|
+
// Should render an empty React.Fragment
|
|
197
|
+
const svg = container.querySelector('svg')
|
|
198
|
+
expect(svg).toBeFalsy()
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
})
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { ChartContext } from '../../../types/ChartContext'
|
|
2
|
+
import { ChartConfig } from '../../../types/ChartConfig'
|
|
3
|
+
|
|
4
|
+
// Minimal config for testing LinearChart
|
|
5
|
+
export const createMockConfig = (overrides: Partial<ChartConfig> = {}): ChartConfig =>
|
|
6
|
+
({
|
|
7
|
+
type: 'chart',
|
|
8
|
+
visualizationType: 'Line',
|
|
9
|
+
visualizationSubType: 'regular',
|
|
10
|
+
orientation: 'vertical',
|
|
11
|
+
animate: false,
|
|
12
|
+
heights: {
|
|
13
|
+
vertical: 300,
|
|
14
|
+
horizontal: 300,
|
|
15
|
+
mobileVertical: 200
|
|
16
|
+
},
|
|
17
|
+
xAxis: {
|
|
18
|
+
type: 'date',
|
|
19
|
+
dataKey: 'Date',
|
|
20
|
+
label: 'X-Axis',
|
|
21
|
+
hideAxis: false,
|
|
22
|
+
hideLabel: false,
|
|
23
|
+
hideTicks: false,
|
|
24
|
+
size: '50',
|
|
25
|
+
tickRotation: 0,
|
|
26
|
+
maxTickRotation: 90,
|
|
27
|
+
anchors: [],
|
|
28
|
+
axisPadding: 0
|
|
29
|
+
},
|
|
30
|
+
yAxis: {
|
|
31
|
+
hideAxis: false,
|
|
32
|
+
hideLabel: false,
|
|
33
|
+
hideTicks: false,
|
|
34
|
+
size: '50',
|
|
35
|
+
gridLines: true,
|
|
36
|
+
label: 'Y-Axis',
|
|
37
|
+
tickRotation: 0,
|
|
38
|
+
anchors: [],
|
|
39
|
+
axisPadding: 0,
|
|
40
|
+
labelPlacement: 'On Date/Category Axis',
|
|
41
|
+
rightAxisSize: 0
|
|
42
|
+
},
|
|
43
|
+
runtime: {
|
|
44
|
+
xAxis: {
|
|
45
|
+
type: 'date',
|
|
46
|
+
dataKey: 'Date',
|
|
47
|
+
label: 'X-Axis'
|
|
48
|
+
},
|
|
49
|
+
yAxis: {
|
|
50
|
+
size: 50,
|
|
51
|
+
label: 'Y-Axis',
|
|
52
|
+
gridLines: true
|
|
53
|
+
},
|
|
54
|
+
originalXAxis: {
|
|
55
|
+
dataKey: 'Date'
|
|
56
|
+
},
|
|
57
|
+
series: [],
|
|
58
|
+
seriesKeys: [],
|
|
59
|
+
seriesLabelsAll: [],
|
|
60
|
+
uniqueId: 'test-chart'
|
|
61
|
+
},
|
|
62
|
+
series: [],
|
|
63
|
+
data: [],
|
|
64
|
+
dataFormat: {
|
|
65
|
+
abbreviated: false,
|
|
66
|
+
roundTo: 0
|
|
67
|
+
},
|
|
68
|
+
legend: {
|
|
69
|
+
position: 'bottom'
|
|
70
|
+
},
|
|
71
|
+
tooltips: {
|
|
72
|
+
opacity: 90,
|
|
73
|
+
singleSeries: false
|
|
74
|
+
},
|
|
75
|
+
chartMessage: {
|
|
76
|
+
noData: 'No data available'
|
|
77
|
+
},
|
|
78
|
+
barThickness: 0.8,
|
|
79
|
+
barHeight: 25,
|
|
80
|
+
barSpace: 15,
|
|
81
|
+
isResponsiveTicks: true,
|
|
82
|
+
debugSvg: false,
|
|
83
|
+
filters: [],
|
|
84
|
+
topAxis: {
|
|
85
|
+
hasLine: false
|
|
86
|
+
},
|
|
87
|
+
hideXAxisLabel: false,
|
|
88
|
+
hideYAxisLabel: false,
|
|
89
|
+
...overrides
|
|
90
|
+
} as ChartConfig)
|
|
91
|
+
|
|
92
|
+
// Minimal chart context for testing
|
|
93
|
+
export const createMockChartContext = (
|
|
94
|
+
configOverrides: Partial<ChartConfig> = {},
|
|
95
|
+
contextOverrides: Partial<ChartContext> = {}
|
|
96
|
+
): ChartContext => {
|
|
97
|
+
const config = createMockConfig(configOverrides)
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
config,
|
|
101
|
+
colorScale: undefined,
|
|
102
|
+
convertLineToBarGraph: false,
|
|
103
|
+
currentViewport: 'lg',
|
|
104
|
+
vizViewport: 'lg',
|
|
105
|
+
dimensions: [800, 400],
|
|
106
|
+
formatDate: (date: any) => String(date),
|
|
107
|
+
formatNumber: (num: any) => String(num),
|
|
108
|
+
handleChartAriaLabels: () => 'Chart',
|
|
109
|
+
handleLineType: () => '',
|
|
110
|
+
handleDragStateChange: () => {},
|
|
111
|
+
interactionLabel: '',
|
|
112
|
+
isEditor: false,
|
|
113
|
+
isDraggingAnnotation: false,
|
|
114
|
+
legendRef: { current: null },
|
|
115
|
+
parentRef: { current: null },
|
|
116
|
+
parseDate: (date: any) => new Date(date),
|
|
117
|
+
seriesHighlight: [],
|
|
118
|
+
tableData: [],
|
|
119
|
+
transformedData: [],
|
|
120
|
+
annotations: [],
|
|
121
|
+
colorPalettes: {},
|
|
122
|
+
twoColorPalette: {},
|
|
123
|
+
capitalize: (s: string) => s,
|
|
124
|
+
clean: (s: any) => s,
|
|
125
|
+
formatTooltipsDate: (date: any) => String(date),
|
|
126
|
+
legendId: 'test-legend',
|
|
127
|
+
...contextOverrides
|
|
128
|
+
} as ChartContext
|
|
129
|
+
}
|