@cdc/chart 4.26.7 → 4.26.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/CONFIG.md +502 -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 -97406
  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/overlay-legend.json +485 -0
  22. package/examples/private/pie-test.json +469 -0
  23. package/examples/vega-remote.json +22 -0
  24. package/package.json +11 -5
  25. package/src/CdcChartComponent.tsx +405 -208
  26. package/src/_stories/Chart.BarRace.stories.tsx +443 -0
  27. package/src/_stories/Chart.BoxPlotNumericSort.stories.tsx +189 -0
  28. package/src/_stories/Chart.BoxPlotStatsParity.stories.tsx +207 -0
  29. package/src/_stories/Chart.ColorDistribution.stories.tsx +299 -0
  30. package/src/_stories/Chart.DataCutoff.stories.tsx +147 -0
  31. package/src/_stories/Chart.DataTableDownloadFilename.stories.tsx +60 -0
  32. package/src/_stories/Chart.Dendrogram.stories.tsx +373 -0
  33. package/src/_stories/Chart.Forecast.CILegend.stories.tsx +236 -0
  34. package/src/_stories/Chart.HeatMap.stories.tsx +170 -4
  35. package/src/_stories/Chart.Legend.AxisAlign.stories.tsx +119 -0
  36. package/src/_stories/Chart.LineRace.stories.tsx +149 -0
  37. package/src/_stories/Chart.Network.stories.tsx +432 -0
  38. package/src/_stories/Chart.PaletteConversion.Editor.stories.tsx +208 -0
  39. package/src/_stories/Chart.Patterns.smoke.stories.tsx +31 -0
  40. package/src/_stories/Chart.PercentageValue.stories.tsx +311 -0
  41. package/src/_stories/Chart.PieLabels.stories.tsx +4 -2
  42. package/src/_stories/Chart.RankByValueMutation.stories.tsx +206 -0
  43. package/src/_stories/Chart.RequiredFields.stories.tsx +681 -0
  44. package/src/_stories/Chart.Sankey.stories.tsx +417 -0
  45. package/src/_stories/Chart.SeriesColorAssignments.stories.tsx +47 -0
  46. package/src/_stories/Chart.SmallMultiplesNumericTileOrder.stories.tsx +137 -0
  47. package/src/_stories/Chart.TooltipSeriesLabels.stories.tsx +43 -0
  48. package/src/_stories/Chart.VegaRemoteData.stories.tsx +31 -0
  49. package/src/_stories/ChartAnnotation.Editor.stories.tsx +78 -0
  50. package/src/_stories/ChartAxisTitles.smoke.stories.tsx +48 -0
  51. package/src/_stories/ChartBar.Editor.stories.tsx +499 -57
  52. package/src/_stories/ChartBrush.Editor.stories.tsx +46 -67
  53. package/src/_stories/ChartBrush.Regression.stories.tsx +135 -0
  54. package/src/_stories/ChartEditor.Editor.stories.tsx +33 -3
  55. package/src/_stories/ChartLine.EffectCompatibility.stories.tsx +120 -0
  56. package/src/_stories/_mock/annotation_event-line_mock.json +2 -2
  57. package/src/_stories/_mock/editor-test-configs.json +20 -10
  58. package/src/_stories/_mock/editor-test-datasets.json +38 -42
  59. package/src/_stories/_mock/heatmap-average-age-categorical.json +2 -2
  60. package/src/_stories/_mock/heatmap.json +2 -2
  61. package/src/_stories/_mock/legend-axis-align-stacked-bar.json +125 -0
  62. package/src/_stories/_mock/sankey_mock.json +23 -27
  63. package/src/components/Annotations/components/AnnotationDraggable.styles.css +52 -0
  64. package/src/components/Annotations/components/AnnotationDraggable.tsx +639 -139
  65. package/src/components/Annotations/components/AnnotationDropdown.styles.css +44 -4
  66. package/src/components/Annotations/components/AnnotationDropdown.tsx +10 -32
  67. package/src/components/Annotations/helpers/resolveAnnotationLayout.ts +212 -0
  68. package/src/components/Annotations/tests/AnnotationDraggable.test.tsx +612 -19
  69. package/src/components/Annotations/tests/AnnotationDropdown.test.tsx +50 -0
  70. package/src/components/Annotations/tests/resolveAnnotationLayout.test.ts +203 -0
  71. package/src/components/Axis/BottomAxis.tsx +1 -1
  72. package/src/components/Axis/Categorical.Axis.tsx +3 -3
  73. package/src/components/Axis/RightAxis.tsx +21 -13
  74. package/src/components/BarChart/components/BarChart.Horizontal.tsx +38 -6
  75. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +12 -3
  76. package/src/components/BarChart/components/BarChart.StackedVertical.test.ts +28 -0
  77. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +29 -25
  78. package/src/components/BarChart/components/BarChart.Vertical.tsx +37 -26
  79. package/src/components/BarChart/components/PortionPatternOverlay.tsx +61 -0
  80. package/src/components/BarChart/components/context.tsx +6 -0
  81. package/src/components/BarChart/helpers/getPatternUrl.ts +2 -5
  82. package/src/components/BarChart/helpers/index.ts +8 -0
  83. package/src/components/BarChart/helpers/portionPattern.ts +243 -0
  84. package/src/components/BarChart/helpers/tests/addMinimumBarHeights.test.ts +42 -0
  85. package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +13 -0
  86. package/src/components/BarChart/helpers/tests/portionPattern.test.ts +280 -0
  87. package/src/components/BarChart/helpers/tests/useBarChart.test.tsx +104 -0
  88. package/src/components/BarChart/helpers/useBarChart.ts +60 -59
  89. package/src/components/BarChartRace/BarChartRace.test.tsx +199 -0
  90. package/src/components/BarChartRace/BarChartRace.tsx +230 -0
  91. package/src/components/BarChartRace/BarChartRaceFallback.test.tsx +61 -0
  92. package/src/components/BarChartRace/BarChartRaceFallback.tsx +48 -0
  93. package/src/components/BarChartRace/bar-chart-race.css +160 -0
  94. package/src/components/BarChartRace/helpers.test.ts +114 -0
  95. package/src/components/BarChartRace/helpers.ts +185 -0
  96. package/src/components/BarChartRace/index.ts +3 -0
  97. package/src/components/BoxPlot/helpers/index.ts +11 -82
  98. package/src/components/Brush/BrushSelector.tsx +9 -23
  99. package/src/components/Brush/MiniChartPreview.tsx +33 -1
  100. package/src/components/Dendrogram/components/Dendrogram.tsx +374 -0
  101. package/src/components/Dendrogram/dendrogram.css +113 -0
  102. package/src/components/Dendrogram/helpers/layoutDendrogram.test.ts +95 -0
  103. package/src/components/Dendrogram/helpers/layoutDendrogram.ts +56 -0
  104. package/src/components/Dendrogram/helpers/prepareDendrogramData.test.ts +135 -0
  105. package/src/components/Dendrogram/helpers/prepareDendrogramData.ts +166 -0
  106. package/src/components/Dendrogram/helpers/resolveDendrogramLabels.test.ts +196 -0
  107. package/src/components/Dendrogram/helpers/resolveDendrogramLabels.ts +418 -0
  108. package/src/components/Dendrogram/index.tsx +1 -0
  109. package/src/components/Dendrogram/types/index.ts +66 -0
  110. package/src/components/EditorPanel/EditorPanel.tsx +1005 -760
  111. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +50 -5
  112. package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.css +10 -0
  113. package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.tsx +211 -0
  114. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +19 -8
  115. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +88 -54
  116. package/src/components/EditorPanel/components/Panels/Panel.Network.css +9 -0
  117. package/src/components/EditorPanel/components/Panels/Panel.Network.tsx +202 -0
  118. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +233 -33
  119. package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +6 -1
  120. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +89 -105
  121. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +2 -2
  122. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +1 -1
  123. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +8 -66
  124. package/src/components/EditorPanel/components/Panels/RacingControls.test.tsx +131 -0
  125. package/src/components/EditorPanel/components/Panels/RacingControls.tsx +185 -0
  126. package/src/components/EditorPanel/components/Panels/SeriesColorAssignments.tsx +2 -2
  127. package/src/components/EditorPanel/components/Panels/index.tsx +4 -0
  128. package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +20 -7
  129. package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +25 -2
  130. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +2 -1
  131. package/src/components/EditorPanel/useEditorPermissions.test.tsx +62 -0
  132. package/src/components/EditorPanel/useEditorPermissions.ts +70 -24
  133. package/src/components/Forecasting/Forecasting.tsx +15 -6
  134. package/src/components/HeatMap/components/HeatMap.tsx +511 -178
  135. package/src/components/HeatMap/heatmap.constants.ts +7 -3
  136. package/src/components/HeatMap/heatmap.css +101 -2
  137. package/src/components/HeatMap/helpers/layout.ts +80 -0
  138. package/src/components/HeatMap/tests/HeatMap.test.tsx +339 -16
  139. package/src/components/HeatMap/tests/helpers.test.ts +79 -0
  140. package/src/components/Legend/Legend.Component.tsx +110 -90
  141. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +7 -0
  142. package/src/components/Legend/LegendGroup/LegendGroup.tsx +47 -34
  143. package/src/components/Legend/helpers/createFormatLabels.test.ts +226 -0
  144. package/src/components/Legend/helpers/createFormatLabels.tsx +45 -51
  145. package/src/components/LineChart/components/LineChart.Circle.tsx +3 -3
  146. package/src/components/LineChart/helpers.test.ts +126 -38
  147. package/src/components/LineChart/helpers.ts +9 -4
  148. package/src/components/LineChart/index.tsx +69 -24
  149. package/src/components/LineChart/tests/LineChart.test.tsx +630 -0
  150. package/src/components/LineChartRace/LineChartRace.test.tsx +275 -0
  151. package/src/components/LineChartRace/LineChartRace.tsx +134 -0
  152. package/src/components/LineChartRace/LineChartRaceFallback.tsx +28 -0
  153. package/src/components/LineChartRace/helpers.test.ts +88 -0
  154. package/src/components/LineChartRace/helpers.ts +157 -0
  155. package/src/components/LineChartRace/index.ts +3 -0
  156. package/src/components/LineChartRace/line-chart-race.css +16 -0
  157. package/src/components/LinearChart/ValueAxisAnchors.tsx +58 -0
  158. package/src/components/LinearChart/VisualizationRenderer.tsx +18 -23
  159. package/src/components/LinearChart/tests/LinearChart.test.tsx +412 -13
  160. package/src/components/LinearChart/tests/VisualizationRenderer.test.tsx +141 -0
  161. package/src/components/LinearChart/tests/mockConfigContext.ts +2 -1
  162. package/src/components/LinearChart.tsx +81 -51
  163. package/src/components/Network/components/Network.tsx +372 -0
  164. package/src/components/Network/helpers/layoutNetwork.test.ts +68 -0
  165. package/src/components/Network/helpers/layoutNetwork.ts +87 -0
  166. package/src/components/Network/helpers/prepareNetworkData.test.ts +116 -0
  167. package/src/components/Network/helpers/prepareNetworkData.ts +101 -0
  168. package/src/components/Network/helpers/resolveNetworkLabels.test.ts +112 -0
  169. package/src/components/Network/helpers/resolveNetworkLabels.ts +206 -0
  170. package/src/components/Network/index.tsx +3 -0
  171. package/src/components/Network/network.css +107 -0
  172. package/src/components/Network/types/index.ts +56 -0
  173. package/src/components/PairedBarChart.jsx +5 -10
  174. package/src/components/PairedBarChart.test.tsx +71 -0
  175. package/src/components/PieChart/PieChart.tsx +9 -8
  176. package/src/components/RacingChartRenderer.tsx +44 -0
  177. package/src/components/Regions/components/Regions.test.tsx +179 -0
  178. package/src/components/Regions/components/Regions.tsx +104 -69
  179. package/src/components/Regions/index.tsx +2 -3
  180. package/src/components/Sankey/components/Sankey.tsx +264 -437
  181. package/src/components/Sankey/helpers/getSankeyTooltip.ts +12 -0
  182. package/src/components/Sankey/helpers/prepareSankeyData.test.ts +71 -0
  183. package/src/components/Sankey/helpers/prepareSankeyData.ts +90 -0
  184. package/src/components/Sankey/sankey.scss +57 -116
  185. package/src/components/Sankey/types/index.ts +41 -4
  186. package/src/components/ScatterPlot/ScatterPlot.jsx +28 -4
  187. package/src/components/ScatterPlot/ScatterPlot.test.tsx +60 -0
  188. package/src/components/SmallMultiples/SmallMultiples.tsx +12 -12
  189. package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +7 -5
  190. package/src/components/raceTiming.ts +18 -0
  191. package/src/data/initial-state.js +77 -31
  192. package/src/helpers/applyChartTypeContract.ts +71 -0
  193. package/src/helpers/axisLabelFontSize.ts +2 -2
  194. package/src/helpers/boxPlotStats.ts +60 -0
  195. package/src/helpers/bumpChartTooltip.ts +2 -1
  196. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.test.ts +37 -0
  197. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +3 -2
  198. package/src/helpers/calculateRightYAxisWidth.ts +60 -0
  199. package/src/helpers/colorAssignmentHelpers.ts +2 -2
  200. package/src/helpers/createNewChartConfig.ts +56 -0
  201. package/src/helpers/dataDrivenYAxisCategories.ts +18 -0
  202. package/src/helpers/forecastLegend.ts +88 -0
  203. package/src/helpers/getBoxPlotConfig.ts +18 -37
  204. package/src/helpers/getChartTypeDefaultPalette.ts +29 -0
  205. package/src/helpers/getColorCodeCategoryColorMap.ts +26 -0
  206. package/src/helpers/getColorScale.ts +31 -22
  207. package/src/helpers/getDynamicYAxisCategories.ts +69 -0
  208. package/src/helpers/getMinMax.ts +9 -7
  209. package/src/helpers/getNewRuntime.ts +5 -2
  210. package/src/helpers/getStackedSeriesMax.ts +51 -0
  211. package/src/helpers/getV21ChartDistributionColors.ts +57 -0
  212. package/src/helpers/handleRankByValue.ts +15 -15
  213. package/src/helpers/seriesColumnSettings.ts +10 -0
  214. package/src/helpers/sizeHelpers.ts +4 -3
  215. package/src/helpers/smallMultiplesHelpers.ts +10 -13
  216. package/src/helpers/tests/applyChartTypeContract.test.ts +37 -0
  217. package/src/helpers/tests/boxPlotStats.test.ts +31 -0
  218. package/src/helpers/tests/bumpChartTooltip.test.ts +29 -0
  219. package/src/helpers/tests/calculateRightYAxisWidth.test.ts +105 -0
  220. package/src/helpers/tests/countNumOfTicks.test.ts +11 -0
  221. package/src/helpers/tests/createNewChartConfig.test.ts +182 -0
  222. package/src/helpers/tests/getBoxPlotConfig.test.ts +70 -0
  223. package/src/helpers/tests/getColorCodeCategoryColorMap.test.ts +113 -0
  224. package/src/helpers/tests/getColorScale.test.ts +226 -1
  225. package/src/helpers/tests/getDynamicYAxisCategories.test.ts +61 -0
  226. package/src/helpers/tests/getMinMax.test.ts +41 -0
  227. package/src/helpers/tests/getStackedSeriesMax.test.ts +26 -0
  228. package/src/helpers/tests/handleRankByValue.test.ts +55 -37
  229. package/src/helpers/tests/seriesColumnSettings.test.ts +23 -0
  230. package/src/helpers/tests/smallMultiplesHelpers.test.ts +37 -1
  231. package/src/helpers/tests/tooltipHelpers.test.ts +80 -0
  232. package/src/helpers/tooltipHelpers.ts +16 -0
  233. package/src/hooks/tests/useTooltip.test.tsx +353 -1
  234. package/src/hooks/usePrefersReducedMotion.ts +22 -0
  235. package/src/hooks/useReduceData.ts +4 -36
  236. package/src/hooks/useScales.ts +15 -0
  237. package/src/hooks/useTooltip.tsx +95 -64
  238. package/src/scss/main.scss +74 -2
  239. package/src/store/chart.reducer.ts +4 -3
  240. package/src/test/CdcChart.dataTable.test.tsx +490 -16
  241. package/src/test/CdcChart.dendrogram.test.tsx +277 -0
  242. package/src/test/CdcChart.finalization.test.tsx +143 -0
  243. package/src/test/CdcChart.network.test.tsx +271 -0
  244. package/src/types/ChartConfig.ts +91 -54
  245. package/src/types/ChartContext.ts +5 -0
  246. package/src/types/Horizon.ts +1 -1
  247. package/LICENSE +0 -201
  248. package/dist/cdcchart-BWVIUcAF.es.js +0 -4802
  249. package/src/_stories/Chart.Sankey.smoke.stories.tsx +0 -62
  250. package/src/components/Sankey/components/ColumnList.tsx +0 -19
  251. package/src/components/Sankey/helpers/getSankeyTooltip.tsx +0 -34
  252. package/src/components/Sankey/useSankeyAlert.tsx +0 -60
  253. package/src/data/legacy-defaults.ts +0 -18
  254. package/src/helpers/ensureSpecialChartAxisTypes.ts +0 -11
  255. package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +0 -30
@@ -0,0 +1,271 @@
1
+ import { fireEvent, render, waitFor } from '@testing-library/react'
2
+ import { afterAll, describe, expect, it, vi } from 'vitest'
3
+ import CdcChart from '../CdcChartComponent'
4
+
5
+ const dataTableProps = vi.hoisted(() => {
6
+ const originalGetContext = Object.getOwnPropertyDescriptor(
7
+ (globalThis as any).HTMLCanvasElement.prototype,
8
+ 'getContext'
9
+ )
10
+ Object.defineProperty((globalThis as any).HTMLCanvasElement.prototype, 'getContext', {
11
+ configurable: true,
12
+ value: () => ({ measureText: (text = '') => ({ width: String(text).length * 8 }) })
13
+ })
14
+
15
+ return { calls: [] as any[], originalGetContext }
16
+ })
17
+
18
+ afterAll(() => {
19
+ if (dataTableProps.originalGetContext) {
20
+ Object.defineProperty(HTMLCanvasElement.prototype, 'getContext', dataTableProps.originalGetContext)
21
+ }
22
+ })
23
+
24
+ vi.mock('@visx/responsive/lib/components/ParentSize', () => ({
25
+ default: ({ children }) => children({ width: 640, height: 420 })
26
+ }))
27
+
28
+ vi.mock('react-tooltip', async () => {
29
+ const React = await vi.importActual<typeof import('react')>('react')
30
+ return {
31
+ Tooltip: ({ className, variant }) =>
32
+ React.createElement('div', { className, 'data-testid': 'react-tooltip', 'data-variant': variant })
33
+ }
34
+ })
35
+
36
+ vi.mock('@cdc/core/components/DataTable', async () => {
37
+ const React = await vi.importActual<typeof import('react')>('react')
38
+ return {
39
+ default: props => {
40
+ dataTableProps.calls.push(props)
41
+ return React.createElement('div', { 'data-testid': 'network-data-table' })
42
+ }
43
+ }
44
+ })
45
+
46
+ const getConfig = (overrides = {}) =>
47
+ ({
48
+ type: 'chart',
49
+ visualizationType: 'Network',
50
+ title: 'Partner Network',
51
+ data: [
52
+ { source: 'A', target: 'B', weight: 2, style: 'solid', nodeColor: '#005eaa' },
53
+ { source: 'B', target: 'A', weight: 3, style: 'dashed', nodeColor: '#4b830d' },
54
+ { source: 'B', target: 'C', weight: 1, style: 'solid', nodeColor: '#4b830d' }
55
+ ],
56
+ enableTooltips: true,
57
+ network: {
58
+ columns: {
59
+ source: 'source',
60
+ target: 'target',
61
+ weight: 'weight',
62
+ style: 'style',
63
+ nodeColor: 'nodeColor'
64
+ },
65
+ directed: false,
66
+ height: 420,
67
+ nodeRadius: 8,
68
+ nodeColor: '#712177',
69
+ linkColor: '#333333',
70
+ showLabels: true,
71
+ linkDistance: 90,
72
+ chargeStrength: -240,
73
+ rotation: 0,
74
+ scale: 1,
75
+ offsetX: 0,
76
+ offsetY: 0
77
+ },
78
+ table: { show: true, expanded: true, label: 'Data Table' },
79
+ ...overrides
80
+ } as any)
81
+
82
+ describe('CdcChart Network', () => {
83
+ it('renders aggregated accessible nodes and links and preserves raw rows for the table', async () => {
84
+ dataTableProps.calls.length = 0
85
+ const { container } = render(<CdcChart config={getConfig()} interactionLabel='network-test' />)
86
+
87
+ await waitFor(() => expect(container.querySelectorAll('.network-chart__node')).toHaveLength(3))
88
+ expect(container.querySelectorAll('.network-chart__viewport .network-chart__link')).toHaveLength(2)
89
+ expect(container.querySelector('.network-chart__node')).toHaveAttribute('tabindex', '0')
90
+ expect(container.querySelector('.network-chart__node')).toHaveAttribute('aria-label')
91
+ const link = container.querySelector('.network-chart__link-group')
92
+ expect(link).toHaveAttribute('data-tooltip-content')
93
+ expect(container.querySelector('.network-chart__tooltip')).toHaveAttribute('data-variant', 'light')
94
+ expect(container.querySelector('[aria-label^="A,"] circle')).toHaveAttribute('fill', '#005eaa')
95
+ expect(container.querySelector('[aria-label^="B,"] circle')).toHaveAttribute('fill', '#4b830d')
96
+ expect(container.querySelector('[aria-label^="C,"] circle')).toHaveAttribute('fill', '#712177')
97
+ expect(container.querySelectorAll('.network-chart__label')).toHaveLength(3)
98
+ const linkKey = container.querySelector('[aria-label="Link styles"]')!
99
+ expect(linkKey).toHaveTextContent('Solid')
100
+ expect(linkKey).toHaveTextContent('Dashed')
101
+ expect(linkKey.querySelectorAll('line')).toHaveLength(2)
102
+ expect(linkKey.querySelectorAll('line')[1]).toHaveClass('network-chart__link--dashed')
103
+ expect(container.querySelector('.network-chart')).toHaveAttribute('viewBox', '0 0 640 388')
104
+ await waitFor(() => expect(dataTableProps.calls.at(-1)?.runtimeData).toHaveLength(3))
105
+ })
106
+
107
+ it('shows an actionable state when every row is invalid', async () => {
108
+ const { container } = render(
109
+ <CdcChart
110
+ config={getConfig({ data: [{ source: 'A', target: 'A', weight: 0 }], table: { show: false } })}
111
+ interactionLabel='network-invalid-test'
112
+ />
113
+ )
114
+
115
+ await waitFor(() => expect(container).toHaveTextContent('No valid connections are available'))
116
+ expect(container).toHaveTextContent('1 row(s) were rejected')
117
+ })
118
+
119
+ it('keeps config-authored disabled tooltips working without an editor control', async () => {
120
+ const { container } = render(<CdcChart config={getConfig({ enableTooltips: false })} />)
121
+
122
+ await waitFor(() => expect(container.querySelectorAll('.network-chart__node')).toHaveLength(3))
123
+ expect(container.querySelector('.network-chart__node')).not.toHaveAttribute('data-tooltip-content')
124
+ expect(container.querySelector('.network-chart__link-group')).not.toHaveAttribute('data-tooltip-content')
125
+ expect(container.querySelector('.network-chart__tooltip')).not.toBeInTheDocument()
126
+ })
127
+
128
+ it('renders link styles from the selected data column', async () => {
129
+ const { container } = render(<CdcChart config={getConfig()} />)
130
+
131
+ await waitFor(() =>
132
+ expect(container.querySelectorAll('.network-chart__viewport .network-chart__link')).toHaveLength(2)
133
+ )
134
+ const dashedLink = container.querySelector('[aria-label*="A and B, dashed link"] .network-chart__link')
135
+ const solidLink = container.querySelector('[aria-label*="B and C, solid link"] .network-chart__link')
136
+ expect(dashedLink).toHaveClass('network-chart__link--dashed')
137
+ expect(solidLink).not.toHaveClass('network-chart__link--dashed')
138
+ })
139
+
140
+ it('always renders node labels and ignores the legacy visibility field', async () => {
141
+ const { container } = render(
142
+ <CdcChart config={getConfig({ network: { ...getConfig().network, showLabels: false } })} />
143
+ )
144
+
145
+ await waitFor(() => expect(container.querySelectorAll('.network-chart__label')).toHaveLength(3))
146
+ })
147
+
148
+ it('omits the link-style key for blank or stale mappings', async () => {
149
+ const blank = render(
150
+ <CdcChart
151
+ config={getConfig({
152
+ network: { ...getConfig().network, columns: { ...getConfig().network.columns, style: '' } }
153
+ })}
154
+ />
155
+ )
156
+ await waitFor(() => expect(blank.container.querySelector('.network-chart')).toBeInTheDocument())
157
+ expect(blank.container.querySelector('[aria-label="Link styles"]')).not.toBeInTheDocument()
158
+
159
+ const stale = render(
160
+ <CdcChart
161
+ config={getConfig({
162
+ network: { ...getConfig().network, columns: { ...getConfig().network.columns, style: 'missing' } }
163
+ })}
164
+ />
165
+ )
166
+ await waitFor(() => expect(stale.container.querySelector('.network-chart')).toBeInTheDocument())
167
+ expect(stale.container.querySelector('[aria-label="Link styles"]')).not.toBeInTheDocument()
168
+ })
169
+
170
+ it('uses custom link captions with blank fallbacks and honors the shared legend visibility setting', async () => {
171
+ const visible = render(
172
+ <CdcChart
173
+ config={getConfig({
174
+ network: {
175
+ ...getConfig().network,
176
+ linkStyleLabels: { solid: ' Established ', dashed: ' ' }
177
+ }
178
+ })}
179
+ />
180
+ )
181
+ await waitFor(() => expect(visible.container.querySelector('[aria-label="Link styles"]')).toBeInTheDocument())
182
+ expect(visible.container.querySelector('[aria-label="Link styles"]')).toHaveTextContent('Established')
183
+ expect(visible.container.querySelector('[aria-label="Link styles"]')).toHaveTextContent('Dashed')
184
+
185
+ const hidden = render(<CdcChart config={getConfig({ legend: { hide: true } })} />)
186
+ await waitFor(() => expect(hidden.container.querySelector('.network-chart')).toBeInTheDocument())
187
+ expect(hidden.container.querySelector('[aria-label="Link styles"]')).not.toBeInTheDocument()
188
+ })
189
+
190
+ it('clamps invalid authored heights and creates unique ids per chart instance', async () => {
191
+ const { container } = render(
192
+ <>
193
+ <CdcChart config={getConfig({ network: { ...getConfig().network, height: -10 } })} />
194
+ <CdcChart config={getConfig({ network: { ...getConfig().network, height: -10 } })} />
195
+ </>
196
+ )
197
+
198
+ await waitFor(() => expect(container.querySelectorAll('.network-chart')).toHaveLength(2))
199
+ expect(Array.from(container.querySelectorAll('.network-chart__container'))).toEqual([
200
+ expect.objectContaining({ style: expect.objectContaining({ height: '160px' }) }),
201
+ expect.objectContaining({ style: expect.objectContaining({ height: '160px' }) })
202
+ ])
203
+ const markerIds = Array.from(container.querySelectorAll('marker')).map(marker => marker.id)
204
+ expect(new Set(markerIds).size).toBe(2)
205
+ })
206
+
207
+ it('starts from the authored view and keeps end-user view changes ephemeral', async () => {
208
+ const config = getConfig({
209
+ network: { ...getConfig().network, rotation: 30, scale: 1.5, offsetX: 20, offsetY: -10 }
210
+ })
211
+ const { container } = render(<CdcChart config={config} />)
212
+
213
+ const viewport = await waitFor(() => {
214
+ const element = container.querySelector('.network-chart__viewport')
215
+ expect(element).toBeInTheDocument()
216
+ return element
217
+ })
218
+ const authoredTransform = viewport.getAttribute('transform')
219
+ expect(authoredTransform).toContain('rotate(30) scale(1.5)')
220
+
221
+ fireEvent.wheel(container.querySelector('.network-chart')!, { deltaY: -100 })
222
+ expect(viewport.getAttribute('transform')).toBe(authoredTransform)
223
+
224
+ fireEvent.click(container.querySelector('[aria-label="Zoom in"]')!)
225
+ expect(viewport.getAttribute('transform')).not.toBe(authoredTransform)
226
+ expect(config.network.scale).toBe(1.5)
227
+
228
+ expect(container.querySelector('[aria-label="Rotate left"]')).not.toBeInTheDocument()
229
+ expect(container.querySelector('[aria-label="Rotate right"]')).not.toBeInTheDocument()
230
+ expect(config.network.rotation).toBe(30)
231
+
232
+ fireEvent.click(container.querySelector('[aria-label="Reset view"]')!)
233
+ expect(viewport.getAttribute('transform')).toBe(authoredTransform)
234
+ })
235
+
236
+ it('normalizes extreme authored rotations before calculating view bounds', async () => {
237
+ const config = getConfig({
238
+ network: { ...getConfig().network, rotation: Number.MAX_VALUE }
239
+ })
240
+ const { container } = render(<CdcChart config={config} />)
241
+
242
+ const viewport = await waitFor(() => {
243
+ const element = container.querySelector('.network-chart__viewport')
244
+ expect(element).toBeInTheDocument()
245
+ return element
246
+ })
247
+ const transform = viewport.getAttribute('transform') || ''
248
+ expect(transform).not.toContain('NaN')
249
+ expect(transform).not.toContain('Infinity')
250
+ expect(config.network.rotation).toBe(Number.MAX_VALUE)
251
+ })
252
+
253
+ it('constrains extreme view offsets to the graph bounds', async () => {
254
+ const config = getConfig({
255
+ network: { ...getConfig().network, offsetX: 10_000, offsetY: 10_000 }
256
+ })
257
+ const { container } = render(<CdcChart config={config} />)
258
+ const viewport = await waitFor(() => {
259
+ const element = container.querySelector('.network-chart__viewport')
260
+ expect(element).toBeInTheDocument()
261
+ return element
262
+ })
263
+ const transform = viewport.getAttribute('transform') || ''
264
+ const [, translatedX, translatedY] = transform.match(/^translate\(([^ ]+) ([^)]+)\)/) || []
265
+ expect(Number(translatedX)).toBeGreaterThanOrEqual(0)
266
+ expect(Number(translatedX)).toBeLessThanOrEqual(640)
267
+ expect(Number(translatedY)).toBeGreaterThanOrEqual(0)
268
+ expect(Number(translatedY)).toBeLessThanOrEqual(420)
269
+ expect(transform).not.toContain('10000')
270
+ })
271
+ })
@@ -23,7 +23,6 @@ type General = CoreGeneral & {
23
23
  }
24
24
  useIntelligentLineChartLabels?: boolean
25
25
  }
26
- import { type Link } from './../components/Sankey/types'
27
26
  import { type DataDescription } from '@cdc/core/types/DataDescription'
28
27
  import { type Legend as CoreLegend } from '@cdc/core/types/Legend'
29
28
  import { Label } from './Label'
@@ -33,8 +32,8 @@ import { VizFilter } from '@cdc/core/types/VizFilter'
33
32
  import { type Annotation } from '@cdc/core/types/Annotation'
34
33
  import { Version } from '@cdc/core/types/Version'
35
34
  import Footnotes from '@cdc/core/types/Footnotes'
35
+ import { ConfigTracking } from '@cdc/core/types/ConfigTracking'
36
36
 
37
- export type ViewportSize = 'xxs' | 'xs' | 'sm' | 'md' | 'lg'
38
37
  type ChartColumns = Record<string, Column>
39
38
  export type ChartOrientation = 'vertical' | 'horizontal'
40
39
  export type VisualizationType =
@@ -54,6 +53,8 @@ export type VisualizationType =
54
53
  | 'Combo'
55
54
  | 'Forecasting'
56
55
  | 'Sankey'
56
+ | 'Network'
57
+ | 'Dendrogram'
57
58
  | 'Bump Chart'
58
59
  | 'Warming Stripes'
59
60
 
@@ -66,6 +67,8 @@ export type HeatMapConfig = {
66
67
  colorBucketCount?: number
67
68
  xAxisPosition?: HeatMapXAxisPosition
68
69
  showCellValues?: boolean
70
+ horizontalScroll?: boolean
71
+ minColumnWidth?: number
69
72
  }
70
73
 
71
74
  export interface PreliminaryDataItem {
@@ -86,6 +89,8 @@ export interface PreliminaryDataItem {
86
89
  circleSize: number
87
90
  displayGray: boolean
88
91
  weight?: number
92
+ /** Styles the segment after a matching point; omitted to style both adjacent segments. */
93
+ lineEffectScope?: 'outgoing'
89
94
  }
90
95
 
91
96
  type DataFormat = {
@@ -114,7 +119,21 @@ type Exclusions = {
114
119
  dateEnd: string
115
120
  }
116
121
 
122
+ export type LegendPattern = {
123
+ label?: string
124
+ color?: string
125
+ shape?: string
126
+ dataKey?: string
127
+ dataValue?: string | number
128
+ contrastCheck?: boolean
129
+ patternSize?: number
130
+ application?: 'value' | 'portion'
131
+ patternValueKey?: string
132
+ placement?: 'start' | 'end'
133
+ }
134
+
117
135
  type Legend = CoreLegend & {
136
+ expandForecastConfidenceIntervals?: boolean
118
137
  seriesHighlight: string[]
119
138
  unified: boolean
120
139
  hideSuppressionLink: boolean
@@ -132,17 +151,7 @@ type Legend = CoreLegend & {
132
151
  }
133
152
  groupBy: string
134
153
  separators?: string
135
- patterns?: {
136
- [key: string]: {
137
- label?: string
138
- color?: string
139
- shape?: string
140
- dataKey?: string
141
- dataValue?: string
142
- contrastCheck?: boolean
143
- patternSize?: number
144
- }
145
- }
154
+ patterns?: Record<string, LegendPattern>
146
155
  }
147
156
 
148
157
  type Visual = {
@@ -166,9 +175,17 @@ export type AllChartsConfig = {
166
175
  altText?: AltTextConfig
167
176
  annotations: Annotation[]
168
177
  animate: boolean
178
+ tracking?: ConfigTracking
169
179
  general: General
170
180
  barHasBorder: 'true' | 'false'
171
181
  barHeight: number
182
+ barRace?: {
183
+ maxBars?: number
184
+ secondsPerFrame?: number
185
+ }
186
+ lineRace?: {
187
+ secondsPerFrame?: number
188
+ }
172
189
  barSpace: number
173
190
  barStyle: 'lollipop' | 'rounded' | 'flat'
174
191
  barThickness: number
@@ -275,19 +292,24 @@ export type AllChartsConfig = {
275
292
  yScale: Function
276
293
  regions: Region[]
277
294
  sankey: {
278
- data: { links: Link[]; storyNodeText: Object[]; tooltips: Object[] }[]
279
295
  nodePadding: number
280
296
  iterations: number
297
+ horizontalScrollWidth?: number | string
281
298
  nodeSize: {
282
299
  nodeWidth: number
283
300
  }
284
301
  margin: { margin_x: number; margin_y: number }
285
- nodeColor: { default: boolean; inactive: boolean }
302
+ columns: {
303
+ source: string
304
+ target: string
305
+ value: string
306
+ }
307
+ nodeColor: { default: string; inactive: string }
286
308
  opacity: {
287
- LinkOpacityInactive: string
288
- LinkOpacityDefault: string
289
- nodeOpacityInactive: boolean
290
- nodeOpacityDefault: boolean
309
+ LinkOpacityInactive: number
310
+ LinkOpacityDefault: number
311
+ nodeOpacityInactive: number
312
+ nodeOpacityDefault: number
291
313
  }
292
314
  rxValue: number
293
315
  nodeFontColor: string
@@ -296,10 +318,54 @@ export type AllChartsConfig = {
296
318
  textAfter: string
297
319
  }
298
320
  linkColor: {
299
- inactive: string
300
321
  default: string
322
+ inactive: string
301
323
  }
302
324
  }
325
+ network?: {
326
+ columns: {
327
+ source: string
328
+ target: string
329
+ weight?: string
330
+ style?: string
331
+ nodeColor?: string
332
+ }
333
+ directed: boolean
334
+ height: number
335
+ nodeRadius: number
336
+ nodeColor: string
337
+ linkColor: string
338
+ linkStyleLabels?: {
339
+ solid?: string
340
+ dashed?: string
341
+ }
342
+ linkDistance: number
343
+ chargeStrength: number
344
+ rotation: number
345
+ scale: number
346
+ offsetX: number
347
+ offsetY: number
348
+ }
349
+ dendrogram?: {
350
+ columns: {
351
+ node: string
352
+ parent: string
353
+ style?: string
354
+ nodeColor?: string
355
+ }
356
+ connectionType: 'curve' | 'line' | 'elbow'
357
+ orientation: 'horizontal' | 'vertical'
358
+ height: number
359
+ nodeRadius: number
360
+ nodeColor: string
361
+ linkColor: string
362
+ linkStyleLabels?: {
363
+ solid?: string
364
+ dashed?: string
365
+ }
366
+ leafSpacing: number
367
+ depthSpacing: number
368
+ }
303
369
  radar?: {
304
370
  gridRings: number
305
371
  showGridRings: boolean
@@ -330,44 +396,15 @@ export type LineChartConfig = {
330
396
  } & AllChartsConfig &
331
397
  MarkupConfig
332
398
 
333
- type SankeyLink = {
334
- depth: number
335
- height: number
336
- id: string
337
- index: number
338
- layer: number
339
- sourceLinks: SankeyLink[]
340
- targetLinks: SankeyLink[]
341
- value: number
342
- x0: number
343
- x1: number
344
- y0: number
345
- y1: number
346
- }
347
-
348
- type StoryNode = {
349
- StoryNode: string
350
- segmentTextAfter: string
351
- segmentTextBefore: string
399
+ type SankeyLinkRow = {
400
+ source: string
401
+ target: string
402
+ value: number | string
352
403
  }
353
404
 
354
405
  type SankeyChartConfig = {
355
406
  enableTooltips: boolean
356
- data: [
357
- {
358
- tooltips: Object[]
359
- // data to display in the sankey chart tooltips
360
- tooltipData: Object[]
361
- // data to display in the data table, bypasses the default data table output
362
- tableData: Object[]
363
- links: {
364
- source: SankeyLink
365
- target: SankeyLink
366
- value: number
367
- }[]
368
- storyNodeText: StoryNode[]
369
- }
370
- ]
407
+ data: SankeyLinkRow[]
371
408
  visualizationType: 'Sankey'
372
409
  } & AllChartsConfig &
373
410
  MarkupConfig
@@ -3,6 +3,7 @@ import { PickD3Scale } from '@visx/scale'
3
3
  import { type Annotation } from '@cdc/core/types/Annotation'
4
4
  import { DimensionsType } from '@cdc/core/types/Dimensions'
5
5
  import { type DashboardConfig } from '@cdc/dashboard/src/types/DashboardConfig'
6
+ import { type RaceTiming } from '../components/raceTiming'
6
7
  export type ColorScale = PickD3Scale<'ordinal', any, any>
7
8
 
8
9
  export type TransformedData = {
@@ -12,6 +13,7 @@ export type TransformedData = {
12
13
 
13
14
  type SharedChartContext = {
14
15
  animatedChart?: boolean
16
+ brushData?: Object[]
15
17
  capitalize: (value: string) => string
16
18
  clean: Function
17
19
  colorScale?: ColorScale
@@ -34,8 +36,11 @@ type SharedChartContext = {
34
36
  isDraggingAnnotation?: boolean
35
37
  legendIsolateValues?: string[]
36
38
  legendRef?: React.RefObject<HTMLDivElement>
39
+ lineRaceProgress?: number
40
+ raceTiming?: RaceTiming
37
41
  parentRef?: React.RefObject<HTMLDivElement>
38
42
  setLegendIsolateValues?: Function
43
+ setRaceTiming?: React.Dispatch<React.SetStateAction<RaceTiming | null>>
39
44
  svgRef?: React.RefObject<SVGSVGElement>
40
45
  handleSmallMultipleHover?: (xAxisValue: any, yCoordinate: number) => void
41
46
  visibleAnnotations?: Annotation[]
@@ -10,7 +10,7 @@
10
10
  */
11
11
  export type HorizonPalette = {
12
12
  name?: string
13
- version?: '1.0' | '2.0'
13
+ version?: '1.0' | '2.0' | '2.1'
14
14
  isReversed?: boolean
15
15
  customColors?: string[]
16
16
  customColorsOrdered?: string[]