@cdc/chart 4.26.6 → 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.
Files changed (276) hide show
  1. package/CONFIG.md +504 -368
  2. package/dist/cdcchart-CYc-Kayd.es.js +22488 -0
  3. package/dist/cdcchart-DCKn8NmI.es.js +20695 -0
  4. package/dist/cdcchart-Dyui9nk_.es.js +45828 -0
  5. package/dist/cdcchart.js +4 -96498
  6. package/examples/__data__/vega-remote-data.json +6 -0
  7. package/examples/dendrogram.json +18 -0
  8. package/examples/feature/bar/bar-chart-race-annual-change.json +68 -0
  9. package/examples/feature/heatmap/average-age-categorical.json +2 -2
  10. package/examples/feature/heatmap/calendar-heatmap.json +2 -2
  11. package/examples/feature/heatmap/cell-values-bottom-axis.json +2 -2
  12. package/examples/feature/heatmap/sparse-aggregation.json +2 -2
  13. package/examples/feature/line/line-chart-race.json +50 -0
  14. package/examples/feature/sankey/sankey-example-data.json +40 -1441
  15. package/examples/feature/sankey/sankey_chart_data.csv +9 -20
  16. package/examples/network.json +31 -0
  17. package/examples/private/box-plot.json +573 -0
  18. package/examples/private/custom-color-issue.json +625 -0
  19. package/examples/private/custom-colors.json +522 -0
  20. package/examples/private/cutoff.json +10750 -0
  21. package/examples/private/flueview.json +22051 -0
  22. package/examples/private/overlay-legend.json +485 -0
  23. package/examples/private/pie-test.json +469 -0
  24. package/examples/vega-remote.json +22 -0
  25. package/package.json +11 -13
  26. package/src/CdcChart.tsx +2 -2
  27. package/src/CdcChartComponent.tsx +444 -217
  28. package/src/_stories/Chart.BarRace.stories.tsx +443 -0
  29. package/src/_stories/Chart.BoxPlotNumericSort.stories.tsx +189 -0
  30. package/src/_stories/Chart.BoxPlotStatsParity.stories.tsx +207 -0
  31. package/src/_stories/Chart.ColorDistribution.stories.tsx +299 -0
  32. package/src/_stories/Chart.DataCutoff.stories.tsx +147 -0
  33. package/src/_stories/Chart.DataTableDownloadFilename.stories.tsx +60 -0
  34. package/src/_stories/Chart.Dendrogram.stories.tsx +373 -0
  35. package/src/_stories/Chart.Forecast.CILegend.stories.tsx +236 -0
  36. package/src/_stories/Chart.HeatMap.stories.tsx +170 -4
  37. package/src/_stories/Chart.Legend.AxisAlign.stories.tsx +119 -0
  38. package/src/_stories/Chart.LineRace.stories.tsx +149 -0
  39. package/src/_stories/Chart.Network.stories.tsx +432 -0
  40. package/src/_stories/Chart.PaletteConversion.Editor.stories.tsx +208 -0
  41. package/src/_stories/Chart.Patterns.smoke.stories.tsx +31 -0
  42. package/src/_stories/Chart.PercentageValue.stories.tsx +311 -0
  43. package/src/_stories/Chart.PieLabels.stories.tsx +49 -0
  44. package/src/_stories/Chart.RankByValueMutation.stories.tsx +206 -0
  45. package/src/_stories/Chart.RequiredFields.stories.tsx +681 -0
  46. package/src/_stories/Chart.Sankey.stories.tsx +417 -0
  47. package/src/_stories/Chart.SeriesColorAssignments.stories.tsx +47 -0
  48. package/src/_stories/Chart.SmallMultiplesNumericTileOrder.stories.tsx +137 -0
  49. package/src/_stories/Chart.TooltipSeriesLabels.stories.tsx +43 -0
  50. package/src/_stories/Chart.VegaRemoteData.stories.tsx +31 -0
  51. package/src/_stories/Chart.smoke.stories.tsx +14 -1
  52. package/src/_stories/ChartAnnotation.Editor.stories.tsx +78 -0
  53. package/src/_stories/ChartAxisTitles.smoke.stories.tsx +48 -0
  54. package/src/_stories/ChartBar.Editor.stories.tsx +499 -57
  55. package/src/_stories/ChartBrush.Editor.stories.tsx +46 -67
  56. package/src/_stories/ChartBrush.Regression.stories.tsx +135 -0
  57. package/src/_stories/ChartEditor.Editor.stories.tsx +33 -3
  58. package/src/_stories/ChartLine.EffectCompatibility.stories.tsx +120 -0
  59. package/src/_stories/ChartPrefixSuffix.smoke.stories.tsx +28 -1
  60. package/src/_stories/_mock/annotation_event-line_mock.json +2 -2
  61. package/src/_stories/_mock/editor-test-configs.json +20 -10
  62. package/src/_stories/_mock/editor-test-datasets.json +38 -42
  63. package/src/_stories/_mock/heatmap-average-age-categorical.json +2 -2
  64. package/src/_stories/_mock/heatmap.json +2 -2
  65. package/src/_stories/_mock/legend-axis-align-stacked-bar.json +125 -0
  66. package/src/_stories/_mock/sankey_mock.json +23 -27
  67. package/src/components/Annotations/components/AnnotationDraggable.styles.css +52 -0
  68. package/src/components/Annotations/components/AnnotationDraggable.tsx +639 -139
  69. package/src/components/Annotations/components/AnnotationDropdown.styles.css +44 -4
  70. package/src/components/Annotations/components/AnnotationDropdown.tsx +10 -32
  71. package/src/components/Annotations/helpers/resolveAnnotationLayout.ts +212 -0
  72. package/src/components/Annotations/tests/AnnotationDraggable.test.tsx +612 -19
  73. package/src/components/Annotations/tests/AnnotationDropdown.test.tsx +50 -0
  74. package/src/components/Annotations/tests/resolveAnnotationLayout.test.ts +203 -0
  75. package/src/components/Axis/BottomAxis.tsx +1 -1
  76. package/src/components/Axis/Categorical.Axis.tsx +3 -3
  77. package/src/components/Axis/RightAxis.tsx +21 -13
  78. package/src/components/BarChart/components/BarChart.Horizontal.tsx +39 -8
  79. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +13 -4
  80. package/src/components/BarChart/components/BarChart.StackedVertical.test.ts +28 -0
  81. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +30 -26
  82. package/src/components/BarChart/components/BarChart.Vertical.tsx +38 -28
  83. package/src/components/BarChart/components/PortionPatternOverlay.tsx +61 -0
  84. package/src/components/BarChart/components/context.tsx +6 -0
  85. package/src/components/BarChart/helpers/getBarData.ts +28 -28
  86. package/src/components/BarChart/helpers/getBarHeights.ts +2 -2
  87. package/src/components/BarChart/helpers/getPatternUrl.ts +2 -5
  88. package/src/components/BarChart/helpers/index.ts +8 -0
  89. package/src/components/BarChart/helpers/portionPattern.ts +243 -0
  90. package/src/components/BarChart/helpers/tests/addMinimumBarHeights.test.ts +42 -0
  91. package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +13 -0
  92. package/src/components/BarChart/helpers/tests/portionPattern.test.ts +280 -0
  93. package/src/components/BarChart/helpers/tests/useBarChart.test.tsx +202 -0
  94. package/src/components/BarChart/helpers/useBarChart.ts +60 -60
  95. package/src/components/BarChartRace/BarChartRace.test.tsx +199 -0
  96. package/src/components/BarChartRace/BarChartRace.tsx +230 -0
  97. package/src/components/BarChartRace/BarChartRaceFallback.test.tsx +61 -0
  98. package/src/components/BarChartRace/BarChartRaceFallback.tsx +48 -0
  99. package/src/components/BarChartRace/bar-chart-race.css +160 -0
  100. package/src/components/BarChartRace/helpers.test.ts +114 -0
  101. package/src/components/BarChartRace/helpers.ts +185 -0
  102. package/src/components/BarChartRace/index.ts +3 -0
  103. package/src/components/BoxPlot/BoxPlot.Horizontal.tsx +7 -6
  104. package/src/components/BoxPlot/BoxPlot.Vertical.tsx +9 -7
  105. package/src/components/BoxPlot/helpers/index.ts +13 -83
  106. package/src/components/Brush/BrushSelector.tsx +10 -24
  107. package/src/components/Brush/MiniChartPreview.tsx +33 -1
  108. package/src/components/Dendrogram/components/Dendrogram.tsx +374 -0
  109. package/src/components/Dendrogram/dendrogram.css +113 -0
  110. package/src/components/Dendrogram/helpers/layoutDendrogram.test.ts +95 -0
  111. package/src/components/Dendrogram/helpers/layoutDendrogram.ts +56 -0
  112. package/src/components/Dendrogram/helpers/prepareDendrogramData.test.ts +135 -0
  113. package/src/components/Dendrogram/helpers/prepareDendrogramData.ts +166 -0
  114. package/src/components/Dendrogram/helpers/resolveDendrogramLabels.test.ts +196 -0
  115. package/src/components/Dendrogram/helpers/resolveDendrogramLabels.ts +418 -0
  116. package/src/components/Dendrogram/index.tsx +1 -0
  117. package/src/components/Dendrogram/types/index.ts +66 -0
  118. package/src/components/EditorPanel/EditorPanel.tsx +1111 -805
  119. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +50 -5
  120. package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.css +10 -0
  121. package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.tsx +211 -0
  122. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +19 -8
  123. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +104 -61
  124. package/src/components/EditorPanel/components/Panels/Panel.Network.css +9 -0
  125. package/src/components/EditorPanel/components/Panels/Panel.Network.tsx +202 -0
  126. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +236 -36
  127. package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +6 -1
  128. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +89 -105
  129. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +21 -6
  130. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +1 -1
  131. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +8 -66
  132. package/src/components/EditorPanel/components/Panels/RacingControls.test.tsx +131 -0
  133. package/src/components/EditorPanel/components/Panels/RacingControls.tsx +185 -0
  134. package/src/components/EditorPanel/components/Panels/SeriesColorAssignments.tsx +2 -2
  135. package/src/components/EditorPanel/components/Panels/index.tsx +4 -0
  136. package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +20 -7
  137. package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +25 -2
  138. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +5 -4
  139. package/src/components/EditorPanel/useEditorPermissions.test.tsx +94 -0
  140. package/src/components/EditorPanel/useEditorPermissions.ts +75 -25
  141. package/src/components/Forecasting/Forecasting.tsx +16 -7
  142. package/src/components/HeatMap/components/HeatMap.tsx +511 -178
  143. package/src/components/HeatMap/heatmap.constants.ts +7 -3
  144. package/src/components/HeatMap/heatmap.css +101 -2
  145. package/src/components/HeatMap/helpers/layout.ts +80 -0
  146. package/src/components/HeatMap/tests/HeatMap.test.tsx +339 -16
  147. package/src/components/HeatMap/tests/helpers.test.ts +79 -0
  148. package/src/components/Legend/Legend.Component.tsx +163 -148
  149. package/src/components/Legend/Legend.Suppression.tsx +3 -3
  150. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +7 -0
  151. package/src/components/Legend/LegendGroup/LegendGroup.tsx +47 -35
  152. package/src/components/Legend/helpers/createFormatLabels.test.ts +226 -0
  153. package/src/components/Legend/helpers/createFormatLabels.tsx +45 -51
  154. package/src/components/LineChart/components/LineChart.Circle.tsx +3 -3
  155. package/src/components/LineChart/helpers.test.ts +126 -38
  156. package/src/components/LineChart/helpers.ts +9 -6
  157. package/src/components/LineChart/index.tsx +71 -27
  158. package/src/components/LineChart/tests/LineChart.test.tsx +630 -0
  159. package/src/components/LineChartRace/LineChartRace.test.tsx +275 -0
  160. package/src/components/LineChartRace/LineChartRace.tsx +134 -0
  161. package/src/components/LineChartRace/LineChartRaceFallback.tsx +28 -0
  162. package/src/components/LineChartRace/helpers.test.ts +88 -0
  163. package/src/components/LineChartRace/helpers.ts +157 -0
  164. package/src/components/LineChartRace/index.ts +3 -0
  165. package/src/components/LineChartRace/line-chart-race.css +16 -0
  166. package/src/components/LinearChart/ValueAxisAnchors.tsx +58 -0
  167. package/src/components/LinearChart/VisualizationRenderer.tsx +18 -23
  168. package/src/components/LinearChart/tests/LinearChart.test.tsx +412 -13
  169. package/src/components/LinearChart/tests/VisualizationRenderer.test.tsx +141 -0
  170. package/src/components/LinearChart/tests/mockConfigContext.ts +2 -1
  171. package/src/components/LinearChart.tsx +85 -54
  172. package/src/components/Network/components/Network.tsx +372 -0
  173. package/src/components/Network/helpers/layoutNetwork.test.ts +68 -0
  174. package/src/components/Network/helpers/layoutNetwork.ts +87 -0
  175. package/src/components/Network/helpers/prepareNetworkData.test.ts +116 -0
  176. package/src/components/Network/helpers/prepareNetworkData.ts +101 -0
  177. package/src/components/Network/helpers/resolveNetworkLabels.test.ts +112 -0
  178. package/src/components/Network/helpers/resolveNetworkLabels.ts +206 -0
  179. package/src/components/Network/index.tsx +3 -0
  180. package/src/components/Network/network.css +107 -0
  181. package/src/components/Network/types/index.ts +56 -0
  182. package/src/components/PairedBarChart.jsx +5 -10
  183. package/src/components/PairedBarChart.test.tsx +71 -0
  184. package/src/components/PieChart/PieChart.tsx +121 -13
  185. package/src/components/PieChart/helpers/labelPlacement.test.ts +88 -0
  186. package/src/components/PieChart/helpers/labelPlacement.ts +95 -0
  187. package/src/components/RacingChartRenderer.tsx +44 -0
  188. package/src/components/Regions/components/Regions.test.tsx +179 -0
  189. package/src/components/Regions/components/Regions.tsx +104 -69
  190. package/src/components/Regions/index.tsx +2 -3
  191. package/src/components/Sankey/components/Sankey.tsx +264 -437
  192. package/src/components/Sankey/helpers/getSankeyTooltip.ts +12 -0
  193. package/src/components/Sankey/helpers/prepareSankeyData.test.ts +71 -0
  194. package/src/components/Sankey/helpers/prepareSankeyData.ts +90 -0
  195. package/src/components/Sankey/sankey.scss +57 -116
  196. package/src/components/Sankey/types/index.ts +41 -4
  197. package/src/components/ScatterPlot/ScatterPlot.jsx +28 -12
  198. package/src/components/ScatterPlot/ScatterPlot.test.tsx +60 -0
  199. package/src/components/SmallMultiples/SmallMultiples.tsx +12 -12
  200. package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +7 -5
  201. package/src/components/raceTiming.ts +18 -0
  202. package/src/data/initial-state.js +79 -31
  203. package/src/helpers/applyChartTypeContract.ts +71 -0
  204. package/src/helpers/axisLabelFontSize.ts +2 -2
  205. package/src/helpers/boxPlotStats.ts +60 -0
  206. package/src/helpers/bumpChartTooltip.ts +2 -1
  207. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.test.ts +37 -0
  208. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +3 -2
  209. package/src/helpers/calculateRightYAxisWidth.ts +60 -0
  210. package/src/helpers/categoryOrder.ts +60 -0
  211. package/src/helpers/colorAssignmentHelpers.ts +2 -2
  212. package/src/helpers/createNewChartConfig.ts +56 -0
  213. package/src/helpers/dataDrivenYAxisCategories.ts +18 -0
  214. package/src/helpers/forecastLegend.ts +88 -0
  215. package/src/helpers/getBoxPlotConfig.ts +26 -38
  216. package/src/helpers/getChartTypeDefaultPalette.ts +29 -0
  217. package/src/helpers/getColorCodeCategoryColorMap.ts +26 -0
  218. package/src/helpers/getColorScale.ts +31 -22
  219. package/src/helpers/getComboChartConfig.ts +0 -1
  220. package/src/helpers/getDynamicYAxisCategories.ts +69 -0
  221. package/src/helpers/getExcludedData.ts +0 -1
  222. package/src/helpers/getHasBoundarySuppression.ts +4 -3
  223. package/src/helpers/getMinMax.ts +11 -8
  224. package/src/helpers/getNewRuntime.ts +4 -3
  225. package/src/helpers/getPiePercent.ts +3 -3
  226. package/src/helpers/getStackedSeriesMax.ts +51 -0
  227. package/src/helpers/getV21ChartDistributionColors.ts +57 -0
  228. package/src/helpers/handleRankByValue.ts +15 -15
  229. package/src/helpers/isConvertLineToBarGraph.ts +2 -2
  230. package/src/helpers/seriesColumnSettings.ts +31 -3
  231. package/src/helpers/sizeHelpers.ts +4 -5
  232. package/src/helpers/smallMultiplesHelpers.ts +10 -13
  233. package/src/helpers/tests/applyChartTypeContract.test.ts +37 -0
  234. package/src/helpers/tests/boxPlotStats.test.ts +31 -0
  235. package/src/helpers/tests/bumpChartTooltip.test.ts +29 -0
  236. package/src/helpers/tests/calculateRightYAxisWidth.test.ts +105 -0
  237. package/src/helpers/tests/categoryOrder.test.ts +57 -0
  238. package/src/helpers/tests/countNumOfTicks.test.ts +11 -0
  239. package/src/helpers/tests/createNewChartConfig.test.ts +182 -0
  240. package/src/helpers/tests/getBoxPlotConfig.test.ts +70 -0
  241. package/src/helpers/tests/getColorCodeCategoryColorMap.test.ts +113 -0
  242. package/src/helpers/tests/getColorScale.test.ts +226 -1
  243. package/src/helpers/tests/getDynamicYAxisCategories.test.ts +61 -0
  244. package/src/helpers/tests/getMinMax.test.ts +41 -0
  245. package/src/helpers/tests/getStackedSeriesMax.test.ts +26 -0
  246. package/src/helpers/tests/handleRankByValue.test.ts +55 -37
  247. package/src/helpers/tests/seriesColumnSettings.test.ts +83 -0
  248. package/src/helpers/tests/smallMultiplesHelpers.test.ts +37 -1
  249. package/src/helpers/tests/tooltipHelpers.test.ts +80 -0
  250. package/src/helpers/tooltipHelpers.ts +16 -0
  251. package/src/hooks/tests/useRightAxis.test.tsx +143 -2
  252. package/src/hooks/tests/useScales.test.tsx +3 -6
  253. package/src/hooks/tests/useTooltip.test.tsx +484 -0
  254. package/src/hooks/usePrefersReducedMotion.ts +22 -0
  255. package/src/hooks/useReduceData.ts +4 -36
  256. package/src/hooks/useRightAxis.ts +19 -26
  257. package/src/hooks/useScales.ts +15 -0
  258. package/src/hooks/useTooltip.tsx +106 -71
  259. package/src/scss/main.scss +74 -2
  260. package/src/store/chart.reducer.ts +4 -4
  261. package/src/test/CdcChart.dataTable.test.tsx +582 -14
  262. package/src/test/CdcChart.dendrogram.test.tsx +277 -0
  263. package/src/test/CdcChart.finalization.test.tsx +143 -0
  264. package/src/test/CdcChart.network.test.tsx +271 -0
  265. package/src/types/ChartConfig.ts +91 -54
  266. package/src/types/ChartContext.ts +5 -0
  267. package/src/types/Horizon.ts +1 -1
  268. package/LICENSE +0 -201
  269. package/dist/cdcchart-BWVIUcAF.es.js +0 -4802
  270. package/src/_stories/Chart.Sankey.smoke.stories.tsx +0 -62
  271. package/src/components/Sankey/components/ColumnList.tsx +0 -19
  272. package/src/components/Sankey/helpers/getSankeyTooltip.tsx +0 -34
  273. package/src/components/Sankey/useSankeyAlert.tsx +0 -60
  274. package/src/data/legacy-defaults.ts +0 -18
  275. package/src/helpers/ensureSpecialChartAxisTypes.ts +0 -11
  276. package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +0 -30
@@ -0,0 +1,681 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { expect, userEvent, within } from 'storybook/test'
3
+ import {
4
+ assertVisualizationRendered,
5
+ performAndAssert,
6
+ waitForAbsence,
7
+ waitForEditor,
8
+ waitForPresence
9
+ } from '@cdc/core/helpers/testing'
10
+
11
+ import Chart from '../CdcChartComponent'
12
+ import dendrogramConfig from '../../examples/dendrogram.json'
13
+ import deviationBarConfig from '../../examples/feature/deviation/planet-deviation-config.json'
14
+ import forestPlotConfig from '../../examples/feature/forest-plot/forest-plot.json'
15
+ import networkConfig from '../../examples/network.json'
16
+ import radarConfig from '../../examples/radar-chart-simple.json'
17
+ import heatMapConfig from './_mock/heatmap-average-age-categorical.json'
18
+ import lollipopConfig from './_mock/lollipop.json'
19
+ import pairedBarConfig from './_mock/paired-bar.json'
20
+ import pieConfig from './_mock/pie_config.json'
21
+ import sankeyConfig from './_mock/sankey_mock.json'
22
+ import scatterPlotConfig from './_mock/scatterplot_mock.json'
23
+
24
+ const meta: Meta<typeof Chart> = {
25
+ title: 'Components/Templates/Chart/Required Fields',
26
+ component: Chart
27
+ }
28
+
29
+ type Story = StoryObj<typeof Chart>
30
+
31
+ const getRequiredAlertText = (canvasElement: HTMLElement) =>
32
+ Array.from(canvasElement.querySelectorAll('.chart-required-fields-alerts .alert-info'))
33
+ .map(alert => alert.textContent?.trim() || '')
34
+ .join(' ')
35
+
36
+ const expectStructuredRequiredFields = (
37
+ canvasElement: HTMLElement,
38
+ expectedFields: Array<{ section: string; field: string }>
39
+ ) => {
40
+ const alerts = Array.from(canvasElement.querySelectorAll('.chart-required-fields-alerts .alert-info'))
41
+ expect(alerts).toHaveLength(expectedFields.length)
42
+ expectedFields.forEach(({ section, field }, index) => {
43
+ expect(alerts[index]).toHaveTextContent(`Missing field: ${field}. More information`)
44
+ expect(
45
+ within(alerts[index] as HTMLElement).getByRole('button', {
46
+ name: `Open ${section} and focus ${field}`
47
+ })
48
+ ).toBeVisible()
49
+ })
50
+ }
51
+
52
+ const assertAlertNavigates = async (
53
+ canvasElement: HTMLElement,
54
+ section: string,
55
+ field: string,
56
+ target: string,
57
+ sectionTarget = target
58
+ ) => {
59
+ const canvas = within(canvasElement)
60
+ const link = canvas.getByRole('button', { name: `Open ${section} and focus ${field}` })
61
+
62
+ await performAndAssert(
63
+ `The ${field} alert opens its editor section and focuses the field`,
64
+ () => ({
65
+ expanded:
66
+ canvasElement
67
+ .querySelector(`[data-required-field-section="${sectionTarget}"]`)
68
+ ?.getAttribute('aria-expanded') === 'true',
69
+ focused: document.activeElement?.getAttribute('data-required-field-control'),
70
+ panelVisible: !canvasElement.querySelector('.editor-panel__toggle.collapsed')
71
+ }),
72
+ async () => userEvent.click(link),
73
+ (_before, after) => after.expanded && after.focused === target && after.panelVisible
74
+ )
75
+ }
76
+
77
+ export const CompletesDataSeries: Story = {
78
+ args: {
79
+ config: {
80
+ ...scatterPlotConfig,
81
+ newViz: true,
82
+ series: [],
83
+ xAxis: { ...scatterPlotConfig.xAxis, dataKey: '' }
84
+ },
85
+ isEditor: true
86
+ },
87
+ play: async ({ canvasElement }) => {
88
+ const canvas = within(canvasElement)
89
+ await waitForEditor(canvas)
90
+ await waitForPresence('.alert-info', canvasElement)
91
+
92
+ expectStructuredRequiredFields(canvasElement, [
93
+ { section: 'Data Series', field: 'Add Data Series' },
94
+ { section: 'Date/Category Axis', field: 'Data Key' }
95
+ ])
96
+ expect(canvasElement.querySelector('.cove-visualization__body')).toBeNull()
97
+ expect(canvas.queryByText("I'm Done")).not.toBeInTheDocument()
98
+ expect(canvasElement.querySelector('.alert-info button[aria-label="Close"]')).toBeNull()
99
+
100
+ await assertAlertNavigates(canvasElement, 'Data Series', 'Add Data Series', 'data-series')
101
+
102
+ const seriesSelect = canvasElement.querySelector('[data-required-field-control="data-series"]') as HTMLSelectElement
103
+ await performAndAssert(
104
+ 'Completing the series requirement leaves only the category-axis alert',
105
+ () => getRequiredAlertText(canvasElement),
106
+ async () => userEvent.selectOptions(seriesSelect, 'y1'),
107
+ (before, after) =>
108
+ before.includes('Add Data Series') &&
109
+ before.includes('Data Key') &&
110
+ !after.includes('Add Data Series') &&
111
+ after.includes('Data Key')
112
+ )
113
+ expectStructuredRequiredFields(canvasElement, [{ section: 'Date/Category Axis', field: 'Data Key' }])
114
+ }
115
+ }
116
+
117
+ export const CompletesCategoryAxis: Story = {
118
+ args: {
119
+ config: {
120
+ ...scatterPlotConfig,
121
+ newViz: true,
122
+ series: scatterPlotConfig.series.map(series => ({ ...series })),
123
+ xAxis: { ...scatterPlotConfig.xAxis, dataKey: '' }
124
+ },
125
+ isEditor: true
126
+ },
127
+ play: async ({ canvasElement }) => {
128
+ const canvas = within(canvasElement)
129
+ await waitForEditor(canvas)
130
+ await waitForPresence('.alert-info', canvasElement)
131
+
132
+ expectStructuredRequiredFields(canvasElement, [{ section: 'Date/Category Axis', field: 'Data Key' }])
133
+ await userEvent.click(canvas.getByTitle('Collapse Editor'))
134
+ expect(canvas.getByTitle('Expand Editor')).toBeVisible()
135
+ await assertAlertNavigates(canvasElement, 'Date/Category Axis', 'Data Key', 'date-category-axis')
136
+
137
+ const dataKeySelect = canvasElement.querySelector(
138
+ '[data-required-field-control="date-category-axis"]'
139
+ ) as HTMLSelectElement
140
+ await performAndAssert(
141
+ 'Completing the final requirement renders the chart automatically',
142
+ () => ({
143
+ alert: getRequiredAlertText(canvasElement),
144
+ hasChart: Boolean(canvasElement.querySelector('.tooltip-boundary svg[role="img"], svg.linear'))
145
+ }),
146
+ async () => userEvent.selectOptions(dataKeySelect, 'x'),
147
+ (before, after) => Boolean(before.alert) && !after.alert && after.hasChart
148
+ )
149
+
150
+ await assertVisualizationRendered(canvasElement)
151
+ }
152
+ }
153
+
154
+ export const PieDataColumnRequired: Story = {
155
+ args: {
156
+ config: {
157
+ ...pieConfig,
158
+ yAxis: { ...pieConfig.yAxis, dataKey: '' },
159
+ xAxis: { ...pieConfig.xAxis }
160
+ },
161
+ isEditor: true
162
+ },
163
+ play: async ({ canvasElement }) => {
164
+ await waitForPresence('.alert-info', canvasElement)
165
+ expectStructuredRequiredFields(canvasElement, [{ section: 'Data Format', field: 'Data Column' }])
166
+ await assertAlertNavigates(canvasElement, 'Data Format', 'Data Column', 'pie-data-column')
167
+ }
168
+ }
169
+
170
+ export const PieSegmentLabelsRequired: Story = {
171
+ args: {
172
+ config: {
173
+ ...pieConfig,
174
+ yAxis: { ...pieConfig.yAxis },
175
+ xAxis: { ...pieConfig.xAxis, dataKey: '' }
176
+ },
177
+ isEditor: true
178
+ },
179
+ play: async ({ canvasElement }) => {
180
+ await waitForPresence('.alert-info', canvasElement)
181
+ expectStructuredRequiredFields(canvasElement, [{ section: 'Segments', field: 'Segment Labels' }])
182
+ await assertAlertNavigates(canvasElement, 'Segments', 'Segment Labels', 'pie-segments')
183
+ }
184
+ }
185
+
186
+ export const HeatMapRequiredFields: Story = {
187
+ args: {
188
+ config: {
189
+ ...heatMapConfig,
190
+ series: [],
191
+ xAxis: { ...heatMapConfig.xAxis, dataKey: '' }
192
+ },
193
+ isEditor: true
194
+ },
195
+ play: async ({ canvasElement }) => {
196
+ await waitForPresence('.alert-info', canvasElement)
197
+ expectStructuredRequiredFields(canvasElement, [
198
+ { section: 'Data Series', field: 'Add Data Series' },
199
+ { section: 'Date/Category Axis', field: 'Data Key' }
200
+ ])
201
+ expect(canvasElement.querySelector('.cove-visualization__body')).toBeNull()
202
+ }
203
+ }
204
+
205
+ export const HeatMapDataSeriesNavigation: Story = {
206
+ args: {
207
+ config: {
208
+ ...heatMapConfig,
209
+ series: [],
210
+ xAxis: { ...heatMapConfig.xAxis }
211
+ },
212
+ isEditor: true
213
+ },
214
+ play: async ({ canvasElement }) => {
215
+ await waitForPresence('.alert-info', canvasElement)
216
+ expectStructuredRequiredFields(canvasElement, [{ section: 'Data Series', field: 'Add Data Series' }])
217
+ await assertAlertNavigates(canvasElement, 'Data Series', 'Add Data Series', 'data-series')
218
+ }
219
+ }
220
+
221
+ export const HeatMapCategoryAxisNavigation: Story = {
222
+ args: {
223
+ config: {
224
+ ...heatMapConfig,
225
+ series: heatMapConfig.series.map(series => ({ ...series })),
226
+ xAxis: { ...heatMapConfig.xAxis, dataKey: '' }
227
+ },
228
+ isEditor: true
229
+ },
230
+ play: async ({ canvasElement }) => {
231
+ await waitForPresence('.alert-info', canvasElement)
232
+ expectStructuredRequiredFields(canvasElement, [{ section: 'Date/Category Axis', field: 'Data Key' }])
233
+ await assertAlertNavigates(canvasElement, 'Date/Category Axis', 'Data Key', 'date-category-axis')
234
+ }
235
+ }
236
+
237
+ export const RadarCategoryColumnRequired: Story = {
238
+ args: {
239
+ config: {
240
+ ...radarConfig,
241
+ series: radarConfig.series.map(series => ({ ...series })),
242
+ xAxis: { ...radarConfig.xAxis, dataKey: '' }
243
+ },
244
+ isEditor: true
245
+ },
246
+ play: async ({ canvasElement }) => {
247
+ await waitForPresence('.alert-info', canvasElement)
248
+ const radarSettingsButton = canvasElement.querySelector('[data-required-field-section="radar-category"]')
249
+ expect(radarSettingsButton?.querySelector('.warning-icon')).toBeInTheDocument()
250
+ expectStructuredRequiredFields(canvasElement, [{ section: 'Radar Chart Settings', field: 'Category Column' }])
251
+ await assertAlertNavigates(canvasElement, 'Radar Chart Settings', 'Category Column', 'radar-category')
252
+
253
+ expect(radarConfig.series).toHaveLength(6)
254
+ const categorySelect = canvasElement.querySelector(
255
+ '[data-required-field-control="radar-category"]'
256
+ ) as HTMLSelectElement
257
+ await performAndAssert(
258
+ 'Selecting the sample category renders Radar without the existing minimum-dimensions error',
259
+ () => ({
260
+ hasRadar: Boolean(canvasElement.querySelector('svg.radar-chart')),
261
+ hasDimensionsError: canvasElement.textContent?.includes('Radar chart requires at least 3 dimensions'),
262
+ hasWarningIcon: Boolean(radarSettingsButton?.querySelector('.warning-icon'))
263
+ }),
264
+ async () => userEvent.selectOptions(categorySelect, 'Program'),
265
+ (_before, after) => after.hasRadar && !after.hasDimensionsError && !after.hasWarningIcon
266
+ )
267
+ }
268
+ }
269
+
270
+ export const RadarMinimumDataSeriesRequired: Story = {
271
+ args: {
272
+ config: {
273
+ ...radarConfig,
274
+ series: radarConfig.series.slice(0, 2).map(series => ({ ...series })),
275
+ xAxis: { ...radarConfig.xAxis }
276
+ },
277
+ isEditor: true
278
+ },
279
+ play: async ({ canvasElement }) => {
280
+ await waitForPresence('.alert-info', canvasElement)
281
+ const dataSeriesButton = canvasElement.querySelector('[data-required-field-section="data-series"]')
282
+ expect(dataSeriesButton?.querySelector('.warning-icon')).toBeInTheDocument()
283
+ expectStructuredRequiredFields(canvasElement, [{ section: 'Data Series', field: 'At Least 3 Data Series' }])
284
+ await assertAlertNavigates(canvasElement, 'Data Series', 'At Least 3 Data Series', 'data-series')
285
+ expect(canvasElement.querySelector('.radar-chart-error')).toBeNull()
286
+ }
287
+ }
288
+
289
+ export const RadarRuntimeKeepsExistingMinimumDimensionsError: Story = {
290
+ args: {
291
+ config: {
292
+ ...radarConfig,
293
+ series: radarConfig.series.slice(0, 2).map(series => ({ ...series })),
294
+ xAxis: { ...radarConfig.xAxis }
295
+ },
296
+ isEditor: false
297
+ },
298
+ play: async ({ canvasElement }) => {
299
+ await waitForPresence('.radar-chart-error', canvasElement)
300
+ expect(canvasElement).toHaveTextContent(
301
+ 'Radar chart requires at least 3 dimensions. Please add more series in the configuration.'
302
+ )
303
+ expect(canvasElement.querySelector('.alert-info')).toBeNull()
304
+ }
305
+ }
306
+
307
+ export const LollipopExactlyOneSeriesRequired: Story = {
308
+ args: {
309
+ config: {
310
+ ...lollipopConfig,
311
+ series: [
312
+ ...lollipopConfig.series.map(series => ({ ...series })),
313
+ { ...lollipopConfig.series[0], dataKey: 'Vehicle' }
314
+ ]
315
+ },
316
+ isEditor: true
317
+ },
318
+ play: async ({ canvasElement }) => {
319
+ await waitForEditor(within(canvasElement))
320
+ await waitForPresence('.alert-info', canvasElement)
321
+
322
+ const dataSeriesButton = canvasElement.querySelector('[data-required-field-section="data-series"]')
323
+ expect(dataSeriesButton?.querySelector('.warning-icon')).toBeInTheDocument()
324
+ expectStructuredRequiredFields(canvasElement, [{ section: 'Data Series', field: 'Exactly 1 Data Series' }])
325
+ await assertAlertNavigates(canvasElement, 'Data Series', 'Exactly 1 Data Series', 'data-series')
326
+ expect(canvasElement).toHaveTextContent('Select exactly one data series for a lollipop chart.')
327
+ }
328
+ }
329
+
330
+ export const PairedBarExactlyTwoSeriesRequired: Story = {
331
+ args: {
332
+ config: {
333
+ ...pairedBarConfig,
334
+ series: [
335
+ ...pairedBarConfig.series.map(series => ({ ...series })),
336
+ { ...pairedBarConfig.series[0], dataKey: 'Additional Series' }
337
+ ]
338
+ },
339
+ isEditor: true
340
+ },
341
+ play: async ({ canvasElement }) => {
342
+ await waitForEditor(within(canvasElement))
343
+ await waitForPresence('.alert-info', canvasElement)
344
+
345
+ const dataSeriesButton = canvasElement.querySelector('[data-required-field-section="data-series"]')
346
+ expect(dataSeriesButton?.querySelector('.warning-icon')).toBeInTheDocument()
347
+ expectStructuredRequiredFields(canvasElement, [{ section: 'Data Series', field: 'Exactly 2 Data Series' }])
348
+ await assertAlertNavigates(canvasElement, 'Data Series', 'Exactly 2 Data Series', 'data-series')
349
+ expect(canvasElement).toHaveTextContent('Select two data series for paired bar chart (e.g., Male and Female).')
350
+ }
351
+ }
352
+
353
+ export const DeviationBarExactlyOneSeriesRequired: Story = {
354
+ args: {
355
+ config: {
356
+ ...deviationBarConfig,
357
+ dataUrl: '',
358
+ data: [
359
+ { name: 'Mercury', Radius: 2440, Diameter: 4880 },
360
+ { name: 'Earth', Radius: 6371, Diameter: 12742 }
361
+ ],
362
+ series: [
363
+ ...deviationBarConfig.series.map(series => ({ ...series })),
364
+ { ...deviationBarConfig.series[0], dataKey: 'Diameter' }
365
+ ]
366
+ },
367
+ isEditor: true
368
+ },
369
+ play: async ({ canvasElement }) => {
370
+ await waitForEditor(within(canvasElement))
371
+ await waitForPresence('.alert-info', canvasElement)
372
+
373
+ const dataSeriesButton = canvasElement.querySelector('[data-required-field-section="data-series"]')
374
+ expect(dataSeriesButton?.querySelector('.warning-icon')).toBeInTheDocument()
375
+ expectStructuredRequiredFields(canvasElement, [{ section: 'Data Series', field: 'Exactly 1 Data Series' }])
376
+ await assertAlertNavigates(canvasElement, 'Data Series', 'Exactly 1 Data Series', 'data-series')
377
+ expect(canvasElement).toHaveTextContent('Select exactly one data series for a deviation bar chart.')
378
+ }
379
+ }
380
+
381
+ export const ForestPlotRequiredFields: Story = {
382
+ args: {
383
+ config: {
384
+ ...forestPlotConfig,
385
+ xAxis: { ...forestPlotConfig.xAxis, dataKey: '' },
386
+ forestPlot: {
387
+ ...forestPlotConfig.forestPlot,
388
+ type: '',
389
+ estimateField: '',
390
+ lower: '',
391
+ upper: ''
392
+ }
393
+ },
394
+ isEditor: true
395
+ },
396
+ play: async ({ canvasElement }) => {
397
+ await waitForEditor(within(canvasElement))
398
+ await waitForPresence('.alert-info', canvasElement)
399
+
400
+ const forestPlotButton = canvasElement.querySelector('[data-required-field-section="forest-plot-settings"]')
401
+ expect(forestPlotButton?.querySelector('.warning-icon')).toBeInTheDocument()
402
+ expectStructuredRequiredFields(canvasElement, [
403
+ { section: 'Forest Plot Settings', field: 'Study Column' },
404
+ { section: 'Forest Plot Settings', field: 'Forest Plot Type' },
405
+ { section: 'Forest Plot Settings', field: 'Point Estimate Column' },
406
+ { section: 'Forest Plot Settings', field: 'Lower CI Column' },
407
+ { section: 'Forest Plot Settings', field: 'Upper CI Column' }
408
+ ])
409
+
410
+ await assertAlertNavigates(
411
+ canvasElement,
412
+ 'Forest Plot Settings',
413
+ 'Study Column',
414
+ 'forest-study',
415
+ 'forest-plot-settings'
416
+ )
417
+ await assertAlertNavigates(
418
+ canvasElement,
419
+ 'Forest Plot Settings',
420
+ 'Forest Plot Type',
421
+ 'forest-type',
422
+ 'forest-plot-settings'
423
+ )
424
+ await assertAlertNavigates(
425
+ canvasElement,
426
+ 'Forest Plot Settings',
427
+ 'Point Estimate Column',
428
+ 'forest-estimate',
429
+ 'forest-plot-settings'
430
+ )
431
+ await assertAlertNavigates(
432
+ canvasElement,
433
+ 'Forest Plot Settings',
434
+ 'Lower CI Column',
435
+ 'forest-lower',
436
+ 'forest-plot-settings'
437
+ )
438
+ await assertAlertNavigates(
439
+ canvasElement,
440
+ 'Forest Plot Settings',
441
+ 'Upper CI Column',
442
+ 'forest-upper',
443
+ 'forest-plot-settings'
444
+ )
445
+ }
446
+ }
447
+
448
+ export const SankeyRequiredColumns: Story = {
449
+ args: {
450
+ config: {
451
+ ...sankeyConfig,
452
+ sankey: { ...sankeyConfig.sankey, columns: { source: 'source', target: 'target', value: 'value' } }
453
+ },
454
+ isEditor: true
455
+ },
456
+ play: async ({ canvasElement }) => {
457
+ await waitForEditor(within(canvasElement))
458
+ await waitForPresence('.alert-info', canvasElement)
459
+
460
+ const sankeyButton = canvasElement.querySelector('[data-required-field-section="sankey-columns"]')
461
+ expect(sankeyButton?.querySelector('.warning-icon')).toBeInTheDocument()
462
+ expectStructuredRequiredFields(canvasElement, [
463
+ { section: 'Sankey', field: 'Source Column' },
464
+ { section: 'Sankey', field: 'Target Column' },
465
+ { section: 'Sankey', field: 'Value Column' }
466
+ ])
467
+
468
+ await assertAlertNavigates(canvasElement, 'Sankey', 'Source Column', 'sankey-source', 'sankey-columns')
469
+ await assertAlertNavigates(canvasElement, 'Sankey', 'Target Column', 'sankey-target', 'sankey-columns')
470
+ await assertAlertNavigates(canvasElement, 'Sankey', 'Value Column', 'sankey-value', 'sankey-columns')
471
+ }
472
+ }
473
+
474
+ export const SankeyCompletesRequiredColumns: Story = {
475
+ args: {
476
+ config: {
477
+ ...sankeyConfig,
478
+ sankey: { ...sankeyConfig.sankey, columns: { source: 'source', target: 'target', value: 'value' } }
479
+ },
480
+ isEditor: true
481
+ },
482
+ play: async ({ canvasElement }) => {
483
+ await waitForEditor(within(canvasElement))
484
+ await waitForPresence('.alert-info', canvasElement)
485
+ const sankeyButton = canvasElement.querySelector('[data-required-field-section="sankey-columns"]')
486
+
487
+ await performAndAssert(
488
+ 'Completing the Sankey mappings clears the guidance and renders the diagram',
489
+ () => ({
490
+ alertCount: canvasElement.querySelectorAll('.chart-required-fields-alerts .alert-info').length,
491
+ hasWarningIcon: Boolean(sankeyButton?.querySelector('.warning-icon')),
492
+ hasDiagram: Boolean(canvasElement.querySelector('.sankey-chart__diagram'))
493
+ }),
494
+ async () => {
495
+ await userEvent.selectOptions(
496
+ canvasElement.querySelector('[data-required-field-control="sankey-source"]') as HTMLSelectElement,
497
+ 'example_source'
498
+ )
499
+ await userEvent.selectOptions(
500
+ canvasElement.querySelector('[data-required-field-control="sankey-target"]') as HTMLSelectElement,
501
+ 'example_target'
502
+ )
503
+ await userEvent.selectOptions(
504
+ canvasElement.querySelector('[data-required-field-control="sankey-value"]') as HTMLSelectElement,
505
+ 'example_value'
506
+ )
507
+ },
508
+ (before, after) =>
509
+ before.alertCount === 3 &&
510
+ before.hasWarningIcon &&
511
+ after.alertCount === 0 &&
512
+ !after.hasWarningIcon &&
513
+ after.hasDiagram
514
+ )
515
+ }
516
+ }
517
+
518
+ export const SankeyRuntimeKeepsExistingEmptyState: Story = {
519
+ args: {
520
+ config: {
521
+ ...sankeyConfig,
522
+ sankey: { ...sankeyConfig.sankey, columns: { source: 'source', target: 'target', value: 'value' } }
523
+ },
524
+ isEditor: false
525
+ },
526
+ play: async ({ canvasElement }) => {
527
+ await waitForPresence('.sankey-chart--empty', canvasElement)
528
+ expect(canvasElement.querySelector('.alert-info')).toBeNull()
529
+ }
530
+ }
531
+
532
+ export const NetworkRequiredFields: Story = {
533
+ args: {
534
+ config: {
535
+ ...networkConfig,
536
+ network: { ...networkConfig.network, columns: { ...networkConfig.network.columns, source: 'from', target: 'to' } }
537
+ },
538
+ isEditor: true
539
+ },
540
+ play: async ({ canvasElement }) => {
541
+ await waitForEditor(within(canvasElement))
542
+ await waitForPresence('.alert-info', canvasElement)
543
+
544
+ const networkButton = canvasElement.querySelector('[data-required-field-section="network-columns"]')
545
+ expect(networkButton?.querySelector('.warning-icon')).toBeInTheDocument()
546
+ expectStructuredRequiredFields(canvasElement, [
547
+ { section: 'Network', field: 'Source Column' },
548
+ { section: 'Network', field: 'Target Column' }
549
+ ])
550
+
551
+ await assertAlertNavigates(canvasElement, 'Network', 'Source Column', 'network-source', 'network-columns')
552
+ await assertAlertNavigates(canvasElement, 'Network', 'Target Column', 'network-target', 'network-columns')
553
+
554
+ await performAndAssert(
555
+ 'Completing the Network mappings clears the guidance and renders the graph',
556
+ () => ({
557
+ alertCount: canvasElement.querySelectorAll('.chart-required-fields-alerts .alert-info').length,
558
+ hasWarningIcon: Boolean(networkButton?.querySelector('.warning-icon')),
559
+ hasGraph: Boolean(canvasElement.querySelector('.network-chart'))
560
+ }),
561
+ async () => {
562
+ await userEvent.selectOptions(
563
+ canvasElement.querySelector('[data-required-field-control="network-source"]') as HTMLSelectElement,
564
+ 'source'
565
+ )
566
+ await userEvent.selectOptions(
567
+ canvasElement.querySelector('[data-required-field-control="network-target"]') as HTMLSelectElement,
568
+ 'target'
569
+ )
570
+ },
571
+ (before, after) =>
572
+ before.alertCount === 2 &&
573
+ before.hasWarningIcon &&
574
+ after.alertCount === 0 &&
575
+ !after.hasWarningIcon &&
576
+ after.hasGraph
577
+ )
578
+ }
579
+ }
580
+
581
+ export const DendrogramRequiredFields: Story = {
582
+ args: {
583
+ config: {
584
+ ...dendrogramConfig,
585
+ data: [
586
+ { id: 'Public Health', parentId: '' },
587
+ { id: 'Programs', parentId: 'Public Health' },
588
+ { id: 'Clinics', parentId: 'Programs' }
589
+ ],
590
+ dendrogram: {
591
+ ...dendrogramConfig.dendrogram,
592
+ columns: { node: 'node', parent: 'parent' }
593
+ }
594
+ },
595
+ isEditor: true
596
+ },
597
+ play: async ({ canvasElement }) => {
598
+ await waitForEditor(within(canvasElement))
599
+ await waitForPresence('.alert-info', canvasElement)
600
+
601
+ const dendrogramButton = canvasElement.querySelector('[data-required-field-section="dendrogram-columns"]')
602
+ expect(dendrogramButton?.querySelector('.warning-icon')).toBeInTheDocument()
603
+ expectStructuredRequiredFields(canvasElement, [
604
+ { section: 'Dendrogram', field: 'Node ID Column' },
605
+ { section: 'Dendrogram', field: 'Parent ID Column' }
606
+ ])
607
+ expect(canvasElement.querySelector('.dendrogram-chart')).toBeNull()
608
+
609
+ await assertAlertNavigates(canvasElement, 'Dendrogram', 'Node ID Column', 'dendrogram-node', 'dendrogram-columns')
610
+ await assertAlertNavigates(
611
+ canvasElement,
612
+ 'Dendrogram',
613
+ 'Parent ID Column',
614
+ 'dendrogram-parent',
615
+ 'dendrogram-columns'
616
+ )
617
+
618
+ await performAndAssert(
619
+ 'Completing the Node ID mapping leaves only the Parent ID requirement',
620
+ () => ({
621
+ alertText: getRequiredAlertText(canvasElement),
622
+ hasWarningIcon: Boolean(dendrogramButton?.querySelector('.warning-icon')),
623
+ hasDendrogram: Boolean(canvasElement.querySelector('.dendrogram-chart'))
624
+ }),
625
+ async () =>
626
+ userEvent.selectOptions(
627
+ canvasElement.querySelector('[data-required-field-control="dendrogram-node"]') as HTMLSelectElement,
628
+ 'id'
629
+ ),
630
+ (before, after) =>
631
+ before.alertText.includes('Node ID Column') &&
632
+ before.alertText.includes('Parent ID Column') &&
633
+ !after.alertText.includes('Node ID Column') &&
634
+ after.alertText.includes('Parent ID Column') &&
635
+ after.hasWarningIcon &&
636
+ !after.hasDendrogram
637
+ )
638
+
639
+ expectStructuredRequiredFields(canvasElement, [{ section: 'Dendrogram', field: 'Parent ID Column' }])
640
+
641
+ await performAndAssert(
642
+ 'Completing both Dendrogram mappings clears the guidance and renders the diagram',
643
+ () => ({
644
+ alertCount: canvasElement.querySelectorAll('.chart-required-fields-alerts .alert-info').length,
645
+ hasWarningIcon: Boolean(dendrogramButton?.querySelector('.warning-icon')),
646
+ hasDendrogram: Boolean(canvasElement.querySelector('.dendrogram-chart'))
647
+ }),
648
+ async () =>
649
+ userEvent.selectOptions(
650
+ canvasElement.querySelector('[data-required-field-control="dendrogram-parent"]') as HTMLSelectElement,
651
+ 'parentId'
652
+ ),
653
+ (before, after) =>
654
+ before.alertCount === 1 &&
655
+ before.hasWarningIcon &&
656
+ after.alertCount === 0 &&
657
+ !after.hasWarningIcon &&
658
+ after.hasDendrogram
659
+ )
660
+
661
+ await assertVisualizationRendered(canvasElement)
662
+ }
663
+ }
664
+
665
+ export const RuntimeDoesNotExposeDirections: Story = {
666
+ args: {
667
+ config: {
668
+ ...scatterPlotConfig,
669
+ series: [],
670
+ xAxis: { ...scatterPlotConfig.xAxis, dataKey: '' }
671
+ },
672
+ isEditor: false
673
+ },
674
+ play: async ({ canvasElement }) => {
675
+ await waitForAbsence('.loading', canvasElement)
676
+ expect(canvasElement.querySelector('.alert-info')).toBeNull()
677
+ expect(canvasElement.querySelector('.tooltip-boundary svg[role="img"], svg.linear')).toBeNull()
678
+ }
679
+ }
680
+
681
+ export default meta