@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.
Files changed (181) hide show
  1. package/CLAUDE.local.md +79 -0
  2. package/dist/{cdcchart-dgT_1dIT.es.js → cdcchart-DQ00cQCm.es.js} +1 -20
  3. package/dist/cdcchart.js +51401 -50814
  4. package/examples/default.json +378 -0
  5. package/examples/feature/__data__/horizon-chart-data.json +373 -0
  6. package/examples/feature/annotations/index.json +3 -6
  7. package/examples/feature/horizon/horizon-chart.json +395 -0
  8. package/examples/feature/pie/planet-pie-example-config.json +48 -2
  9. package/examples/line-chart-states.json +1085 -0
  10. package/examples/private/123.json +694 -0
  11. package/examples/private/DEV-12100.json +1303 -0
  12. package/examples/private/anchor-issue.json +4094 -0
  13. package/examples/private/backwards-slider.json +10430 -0
  14. package/examples/private/cat-y.json +1235 -0
  15. package/examples/private/data-points.json +228 -0
  16. package/examples/private/georgia.csv +160 -0
  17. package/examples/private/height.json +3915 -0
  18. package/examples/private/links.json +569 -0
  19. package/examples/private/quadrant.txt +30 -0
  20. package/examples/private/test-forecast.json +5510 -0
  21. package/examples/private/timeline-data.json +1 -0
  22. package/examples/private/timeline.json +389 -0
  23. package/examples/private/warming-stripe-test.json +2578 -0
  24. package/examples/private/warming-stripes.json +4763 -0
  25. package/examples/radar-chart-simple.json +133 -0
  26. package/examples/radar-chart.json +148 -0
  27. package/examples/tech-adoption-with-links.json +560 -0
  28. package/index.html +1 -36
  29. package/package.json +59 -60
  30. package/src/CdcChartComponent.tsx +206 -89
  31. package/src/_stories/Chart.Anchors.stories.tsx +10 -0
  32. package/src/_stories/Chart.BoxPlot.stories.tsx +7 -0
  33. package/src/_stories/Chart.CI.stories.tsx +13 -0
  34. package/src/_stories/Chart.Combo.stories.tsx +17 -0
  35. package/src/_stories/Chart.CustomColors.stories.tsx +4 -0
  36. package/src/_stories/Chart.DynamicSeries.stories.tsx +19 -0
  37. package/src/_stories/Chart.Filters.stories.tsx +4 -0
  38. package/src/_stories/Chart.Forecast.stories.tsx +4 -0
  39. package/src/_stories/Chart.HTMLInDataTable.stories.tsx +22 -0
  40. package/src/_stories/Chart.Legend.Gradient.stories.tsx +28 -0
  41. package/src/_stories/Chart.Patterns.stories.tsx +4 -0
  42. package/src/_stories/Chart.PreserveDecimals.stories.tsx +25 -0
  43. package/src/_stories/Chart.Regions.Categorical.stories.tsx +161 -0
  44. package/src/_stories/Chart.Regions.DateScale.stories.tsx +216 -0
  45. package/src/_stories/Chart.Regions.DateTimeScale.stories.tsx +312 -0
  46. package/src/_stories/Chart.ScatterPlot.stories.tsx +4 -0
  47. package/src/_stories/Chart.SmallMultiples.stories.tsx +16 -0
  48. package/src/_stories/Chart.stories.tsx +45 -0
  49. package/src/_stories/Chart.tooltip.stories.tsx +7 -0
  50. package/src/_stories/ChartAnnotation.stories.tsx +10 -0
  51. package/src/_stories/ChartAxisLabels.stories.tsx +4 -0
  52. package/src/_stories/ChartAxisTitles.stories.tsx +10 -0
  53. package/src/_stories/ChartBar.Editor.stories.tsx +11 -6
  54. package/src/_stories/ChartBrush.Editor.stories.tsx +295 -0
  55. package/src/_stories/ChartBrush.Matrix.Continuous.stories.tsx +41 -0
  56. package/src/_stories/ChartBrush.Matrix.Date.stories.tsx +114 -0
  57. package/src/_stories/ChartBrush.Matrix.DateTime.stories.tsx +78 -0
  58. package/src/_stories/ChartBrush.stories.tsx +57 -0
  59. package/src/_stories/ChartEditor.Editor.stories.tsx +3 -5
  60. package/src/_stories/ChartEditor.stories.tsx +7 -0
  61. package/src/_stories/ChartLine.QuadrantAngles.stories.tsx +89 -0
  62. package/src/_stories/ChartLine.Suppression.stories.tsx +7 -0
  63. package/src/_stories/ChartLine.Symbols.stories.tsx +4 -0
  64. package/src/_stories/ChartPrefixSuffix.stories.tsx +46 -1
  65. package/src/_stories/TechAdoptionWithLinks.stories.tsx +34 -0
  66. package/src/_stories/_mock/brush_continuous.json +86 -0
  67. package/src/_stories/_mock/brush_date_large.json +176 -0
  68. package/src/_stories/_mock/brush_enabled.json +326 -0
  69. package/src/_stories/_mock/brush_mock.json +2 -69
  70. package/src/_stories/_mock/horizontal-bars-dynamic-y-axis.json +413 -0
  71. package/src/_stories/_mock/line_chart_angle_near_zero_fall.json +195 -0
  72. package/src/_stories/_mock/line_chart_angle_near_zero_rise.json +195 -0
  73. package/src/_stories/_mock/line_chart_angle_q1_steep_upward.json +195 -0
  74. package/src/_stories/_mock/line_chart_angle_q2_gentle_downward.json +195 -0
  75. package/src/_stories/_mock/line_chart_angle_q3_steep_downward.json +195 -0
  76. package/src/_stories/_mock/line_chart_angle_q4_gentle_upward.json +195 -0
  77. package/src/_stories/_mock/line_chart_quadrant_angles.json +264 -0
  78. package/src/components/Annotations/components/AnnotationDraggable.styles.css +11 -17
  79. package/src/components/Annotations/components/AnnotationDraggable.tsx +240 -116
  80. package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -2
  81. package/src/components/Annotations/components/AnnotationDropdown.tsx +8 -12
  82. package/src/components/Annotations/components/AnnotationList.styles.css +4 -10
  83. package/src/components/Annotations/components/AnnotationList.tsx +5 -4
  84. package/src/components/Annotations/components/findNearestDatum.ts +75 -85
  85. package/src/components/Annotations/helpers/getVisibleAnnotations.ts +38 -0
  86. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -2
  87. package/src/components/Axis/BottomAxis.tsx +270 -0
  88. package/src/components/Axis/Categorical.Axis.tsx +6 -7
  89. package/src/components/Axis/LeftAxis.tsx +404 -0
  90. package/src/components/Axis/LeftAxisGridlines.tsx +77 -0
  91. package/src/components/Axis/PairedBarAxis.tsx +186 -0
  92. package/src/components/Axis/README.md +94 -0
  93. package/src/components/Axis/RightAxis.tsx +108 -0
  94. package/src/components/Axis/axis.constants.ts +21 -0
  95. package/src/components/Axis/index.ts +7 -0
  96. package/src/components/BarChart/components/BarChart.Horizontal.tsx +178 -24
  97. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +3 -1
  98. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +1 -0
  99. package/src/components/BarChart/components/BarChart.Vertical.tsx +6 -8
  100. package/src/components/BarChart/components/BarChart.tsx +7 -1
  101. package/src/components/BarChart/components/context.tsx +1 -0
  102. package/src/components/BarChart/helpers/useBarChart.ts +14 -2
  103. package/src/components/Brush/BrushSelector.tsx +1390 -0
  104. package/src/components/Brush/MiniChartPreview.tsx +400 -0
  105. package/src/components/DeviationBar.jsx +9 -7
  106. package/src/components/EditorPanel/EditorPanel.tsx +2734 -2595
  107. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +60 -22
  108. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +56 -34
  109. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +137 -30
  110. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +2 -0
  111. package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +353 -0
  112. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +0 -1
  113. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +30 -25
  114. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +42 -28
  115. package/src/components/EditorPanel/components/Panels/index.tsx +2 -0
  116. package/src/components/EditorPanel/useEditorPermissions.ts +81 -39
  117. package/src/components/HorizonChart/HorizonChart.tsx +131 -0
  118. package/src/components/HorizonChart/components/HorizonBand.tsx +160 -0
  119. package/src/components/HorizonChart/helpers/calculateHorizonBands.ts +27 -0
  120. package/src/components/HorizonChart/helpers/getHorizonLayerColors.ts +40 -0
  121. package/src/components/HorizonChart/index.tsx +3 -0
  122. package/src/components/Legend/Legend.Component.tsx +52 -4
  123. package/src/components/Legend/Legend.tsx +4 -3
  124. package/src/components/Legend/LegendValueRange.tsx +77 -0
  125. package/src/components/Legend/helpers/createFormatLabels.tsx +164 -2
  126. package/src/components/Legend/helpers/generateValueRanges.ts +92 -0
  127. package/src/components/Legend/helpers/index.ts +10 -6
  128. package/src/components/LineChart/helpers/README.md +292 -0
  129. package/src/components/LineChart/helpers/labelPositioning.test.ts +245 -0
  130. package/src/components/LineChart/helpers/labelPositioning.ts +304 -0
  131. package/src/components/LineChart/index.tsx +44 -8
  132. package/src/components/LinearChart/README.md +109 -0
  133. package/src/components/LinearChart/VisualizationRenderer.tsx +267 -0
  134. package/src/components/LinearChart/linearChart.constants.ts +84 -0
  135. package/src/components/LinearChart/tests/LinearChart.test.tsx +201 -0
  136. package/src/components/LinearChart/tests/mockConfigContext.ts +129 -0
  137. package/src/components/LinearChart/utils/tickFormatting.ts +146 -0
  138. package/src/components/LinearChart.tsx +338 -1082
  139. package/src/components/PairedBarChart.jsx +20 -3
  140. package/src/components/PieChart/PieChart.tsx +1 -1
  141. package/src/components/RadarChart/RadarAxis.tsx +78 -0
  142. package/src/components/RadarChart/RadarChart.tsx +298 -0
  143. package/src/components/RadarChart/RadarGrid.tsx +64 -0
  144. package/src/components/RadarChart/RadarPolygon.tsx +91 -0
  145. package/src/components/RadarChart/helpers.ts +83 -0
  146. package/src/components/RadarChart/index.tsx +3 -0
  147. package/src/components/Regions/components/Regions.tsx +365 -122
  148. package/src/components/ScatterPlot/ScatterPlot.jsx +2 -2
  149. package/src/components/SmallMultiples/SmallMultipleTile.tsx +5 -1
  150. package/src/components/WarmingStripes/WarmingStripes.tsx +230 -0
  151. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +35 -0
  152. package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +104 -0
  153. package/src/components/WarmingStripes/index.tsx +3 -0
  154. package/src/data/initial-state.js +17 -2
  155. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +57 -0
  156. package/src/helpers/getExcludedData.ts +4 -0
  157. package/src/helpers/getMinMax.ts +12 -7
  158. package/src/helpers/handleChartAriaLabels.ts +19 -19
  159. package/src/helpers/handleLineType.ts +22 -18
  160. package/src/helpers/sizeHelpers.ts +0 -20
  161. package/src/helpers/smallMultiplesHelpers.ts +1 -1
  162. package/src/hooks/useChartHoverAnalytics.tsx +10 -9
  163. package/src/hooks/useProgrammaticTooltip.ts +23 -2
  164. package/src/hooks/useScales.ts +18 -1
  165. package/src/hooks/useTooltip.tsx +34 -10
  166. package/src/scss/DataTable.scss +0 -4
  167. package/src/scss/main.scss +22 -3
  168. package/src/selectors/README.md +68 -0
  169. package/src/store/chart.reducer.ts +2 -0
  170. package/src/test/CdcChart.test.jsx +1 -1
  171. package/src/types/ChartConfig.ts +21 -0
  172. package/src/types/ChartContext.ts +1 -0
  173. package/src/types/Horizon.ts +64 -0
  174. package/src/types/Label.ts +1 -0
  175. package/src/utils/analyticsTracking.ts +19 -0
  176. package/LICENSE +0 -201
  177. package/src/components/Annotations/components/helpers/index.tsx +0 -46
  178. package/src/components/Brush/BrushChart.tsx +0 -128
  179. package/src/components/Brush/BrushController.tsx +0 -71
  180. package/src/components/Brush/types.tsx +0 -8
  181. 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
+ }