@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
@@ -1,139 +1,123 @@
1
- import { useContext } from 'react'
2
- import ConfigContext from '../../../../ConfigContext'
3
- import { CheckBox, TextField } from '@cdc/core/components/EditorPanel/Inputs'
4
- import Button from '@cdc/core/components/elements/Button'
5
-
1
+ import { useContext, FC } from 'react'
6
2
  import {
7
3
  AccordionItem,
8
4
  AccordionItemHeading,
9
5
  AccordionItemPanel,
10
6
  AccordionItemButton
11
7
  } from 'react-accessible-accordion'
12
- import EditorPanelContext, { type EditorPanelContext as EPContext } from '../../EditorPanelContext'
8
+ import { CheckBox, Select, TextField } from '@cdc/core/components/EditorPanel/Inputs'
13
9
 
14
- const SankeySettings: React.FC<PanelProps> = props => {
15
- const { config, updateConfig } = useContext(ConfigContext)
16
- const data = config.data?.[0]
17
- const { updateField } = useContext<EPContext>(EditorPanelContext)
10
+ import ConfigContext from '../../../../ConfigContext'
11
+ import type { ChartContext } from '../../../../types/ChartContext'
12
+ import { useEditorPanelContext } from '../../EditorPanelContext'
13
+ import { type PanelProps } from '../PanelProps'
14
+ import WarningImage from '../../../../images/warning.svg'
18
15
 
19
- if (config.visualizationType !== 'Sankey') return
16
+ const SankeySettings: FC<PanelProps> = ({ name }) => {
17
+ const { config, updateConfig } = useContext<ChartContext>(ConfigContext)
18
+ const { getColumns } = useEditorPanelContext()
20
19
 
21
- const updateStoryNode = (fieldName, value, i) => {
22
- let storyNodes = []
20
+ if (config.visualizationType !== 'Sankey') return null
23
21
 
24
- if (data?.storyNodeText) {
25
- storyNodes = [...data?.storyNodeText]
26
- }
22
+ const columnOptions = getColumns?.(false) || []
23
+ const selectedColumns = config.sankey?.columns || {}
24
+ const isMissingColumn = (column: unknown) =>
25
+ !column || (columnOptions.length > 0 && !columnOptions.includes(String(column)))
27
26
 
28
- storyNodes[i][fieldName] = value
29
- updateConfig({
27
+ const updateSankeyColumn = (_section: string, _subsection: string, fieldName: string, value: string) => {
28
+ updateConfig?.({
30
29
  ...config,
31
30
  sankey: {
32
31
  ...config.sankey,
33
- data: {
34
- ...config.sankey.data,
35
- storyNodeText: storyNodes
32
+ columns: {
33
+ ...selectedColumns,
34
+ [fieldName]: value
36
35
  }
37
36
  }
38
37
  })
39
38
  }
40
39
 
41
- const addStoryNode = () => {
42
- const newData = data
43
-
44
- newData.storyNodeText.push({
45
- StoryNode: '',
46
- segmentTextBefore: '',
47
- segmentTextAfter: ''
48
- })
49
-
50
- updateConfig({
40
+ const updateEnableTooltips = (_section: string, _subsection: string, _fieldName: string, value: boolean) => {
41
+ updateConfig?.({
51
42
  ...config,
52
- sankey: {
53
- ...config.sankey,
54
- data: [{ ...newData }]
55
- }
43
+ enableTooltips: value
56
44
  })
57
45
  }
58
46
 
59
- const removeStoryNode = index => {
60
- const newData = data
61
- newData.storyNodeText.splice(index, 1)
47
+ const updateHorizontalScrollWidth = (_section: string, _subsection: string, _fieldName: string, value: string) => {
48
+ const nextSankey = { ...config.sankey }
49
+ const width = Number(value)
50
+
51
+ if (value.trim() === '' || !Number.isFinite(width) || width <= 0) {
52
+ delete nextSankey.horizontalScrollWidth
53
+ } else {
54
+ nextSankey.horizontalScrollWidth = width
55
+ }
62
56
 
63
- updateConfig({ ...config, sankey: { ...config.sankey, data: { ...newData } } })
57
+ updateConfig?.({ ...config, sankey: nextSankey })
64
58
  }
65
59
 
66
60
  return (
67
61
  <AccordionItem>
68
62
  <AccordionItemHeading>
69
- <AccordionItemButton>Sankey Settings</AccordionItemButton>
63
+ <AccordionItemButton data-required-field-section='sankey-columns'>
64
+ {name}
65
+ {(isMissingColumn(selectedColumns.source) ||
66
+ isMissingColumn(selectedColumns.target) ||
67
+ isMissingColumn(selectedColumns.value)) && <WarningImage width='25' className='warning-icon' />}
68
+ </AccordionItemButton>
70
69
  </AccordionItemHeading>
71
70
  <AccordionItemPanel>
72
- <p>
73
- Node stories can provide additional details to support public health messaging. COVE can display a maximum of
74
- 3 node stories.
75
- </p>
76
- {data?.storyNodeText &&
77
- data?.storyNodeText.map(({ StoryNode, segmentTextBefore, segmentTextAfter }, i) => (
78
- <div
79
- key={i}
80
- style={{ border: '1px solid black', margin: '15px auto', padding: '15px', borderRadius: '10px' }}
81
- >
82
- <label>
83
- Story Node Text
84
- <input
85
- type='text'
86
- value={StoryNode}
87
- fieldName='StoryNode'
88
- label='StoryNode'
89
- onChange={e => updateStoryNode('StoryNode', e.target.value, i)}
90
- />
91
- </label>
92
- <label>
93
- Story Text Before
94
- <input
95
- type='text'
96
- value={segmentTextBefore}
97
- fieldName='segmentTextBefore'
98
- label='Segment Text Before'
99
- onChange={e => updateStoryNode('segmentTextBefore', e.target.value, i)}
100
- />
101
- </label>
102
- <label>
103
- Story Text After
104
- <input
105
- type='text'
106
- value={segmentTextAfter}
107
- fieldName='segmentTextAfter'
108
- label='Segment Text After'
109
- onChange={e => updateStoryNode('segmentTextAfter', e.target.value, i)}
110
- />
111
- </label>
112
- <Button onClick={e => removeStoryNode(i)} className='btn btn-danger full-width'>
113
- Remove Story Node
114
- </Button>
115
- </div>
116
- ))}
117
- {data?.storyNodeText?.length < 3 && (
118
- <Button
119
- type='button'
120
- variant='editor-primary'
121
- onClick={e => {
122
- e.preventDefault()
123
- addStoryNode()
124
- }}
125
- >
126
- Add StoryNode
127
- </Button>
128
- )}
129
- {config.data?.[0]?.tooltips?.length > 0 && (
130
- <CheckBox
131
- value={config.enableTooltips}
132
- fieldName='enableTooltips'
133
- label='Enable Tooltips'
134
- updateField={updateField}
135
- />
136
- )}
71
+ <Select
72
+ value={selectedColumns.source || ''}
73
+ section='sankey'
74
+ subsection='columns'
75
+ fieldName='source'
76
+ label='Source Column'
77
+ data-required-field-control='sankey-source'
78
+ initial='Select'
79
+ required={true}
80
+ updateField={updateSankeyColumn}
81
+ options={columnOptions}
82
+ />
83
+ <Select
84
+ value={selectedColumns.target || ''}
85
+ section='sankey'
86
+ subsection='columns'
87
+ fieldName='target'
88
+ label='Target Column'
89
+ data-required-field-control='sankey-target'
90
+ initial='Select'
91
+ required={true}
92
+ updateField={updateSankeyColumn}
93
+ options={columnOptions}
94
+ />
95
+ <Select
96
+ value={selectedColumns.value || ''}
97
+ section='sankey'
98
+ subsection='columns'
99
+ fieldName='value'
100
+ label='Value Column'
101
+ data-required-field-control='sankey-value'
102
+ initial='Select'
103
+ required={true}
104
+ updateField={updateSankeyColumn}
105
+ options={columnOptions}
106
+ />
107
+ <CheckBox
108
+ value={Boolean(config.enableTooltips)}
109
+ fieldName='enableTooltips'
110
+ label='Show Tooltips'
111
+ updateField={updateEnableTooltips}
112
+ />
113
+ <TextField
114
+ value={config.sankey?.horizontalScrollWidth ?? ''}
115
+ type='number'
116
+ section='sankey'
117
+ fieldName='horizontalScrollWidth'
118
+ label='Horizontal Scroll Width'
119
+ updateField={updateHorizontalScrollWidth}
120
+ />
137
121
  </AccordionItemPanel>
138
122
  </AccordionItem>
139
123
  )
@@ -6,7 +6,7 @@ import Check from '@cdc/core/assets/icon-check.svg'
6
6
  import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
7
7
  import { colorPalettesChartV1, colorPalettesChartV2, sequentialPalettes } from '@cdc/core/data/colorPalettes'
8
8
  import { updatePaletteNames } from '@cdc/core/helpers/updatePaletteNames'
9
- import { getColorPaletteVersion } from '@cdc/core/helpers/getColorPaletteVersion'
9
+ import { getColorPaletteMajorVersion } from '@cdc/core/helpers/getColorPaletteMajorVersion'
10
10
  import Icon from '@cdc/core/components/ui/Icon'
11
11
  import { CheckBox, Select, TextField } from '@cdc/core/components/EditorPanel/Inputs'
12
12
  import Button from '@cdc/core/components/elements/Button'
@@ -257,7 +257,7 @@ const SeriesDropdownForecastColor = props => {
257
257
 
258
258
  // Determine palette version and use appropriate palette set
259
259
  // Forecasting charts use sequentialPalettes for v1, sequential-only palettes for v2
260
- const paletteVersion = getColorPaletteVersion(config)
260
+ const paletteVersion = getColorPaletteMajorVersion(config)
261
261
 
262
262
  // Get version-appropriate palettes (v1 uses sequentialPalettes, v2 uses filtered v2 palettes)
263
263
  const forecastPalettes =
@@ -54,7 +54,7 @@ const PanelSmallMultiples: FC<PanelProps> = props => {
54
54
 
55
55
  return (
56
56
  <>
57
- {visSupportsSmallMultiples() && (
57
+ {visSupportsSmallMultiples() && config.visualizationSubType !== 'racing' && (
58
58
  <AccordionItem>
59
59
  <AccordionItemHeading>
60
60
  <AccordionItemButton>Small Multiples</AccordionItemButton>
@@ -18,7 +18,7 @@ import InputToggle from '@cdc/core/components/inputs/InputToggle'
18
18
  // contexts
19
19
  import { useColorPalette } from '@cdc/core/hooks/useColorPalette'
20
20
  import { getCurrentPaletteName } from '@cdc/core/helpers/palettes/utils'
21
- import { getColorPaletteVersion } from '@cdc/core/helpers/getColorPaletteVersion'
21
+ import { getColorPaletteMajorVersion } from '@cdc/core/helpers/getColorPaletteMajorVersion'
22
22
  import { isCoveDeveloperMode } from '@cdc/core/helpers/queryStringUtils'
23
23
  import { ChartContext } from './../../../../types/ChartContext.js'
24
24
 
@@ -60,38 +60,12 @@ const PanelVisual: FC<PanelProps> = props => {
60
60
  const { twoColorPalettes, sequential, nonSequential, accessibleColors } = useColorPalette(config, updateConfig)
61
61
 
62
62
  const currentPaletteName = getCurrentPaletteName(config)
63
-
64
63
  const versionedTwoColorPalette = useMemo(() => {
65
- const version = getColorPaletteVersion(config)
64
+ const version = getColorPaletteMajorVersion(config)
66
65
  const versionKey = `v${version}`
67
66
  return twoColorPalette[versionKey] || twoColorPalette.v2
68
67
  }, [config, twoColorPalette])
69
68
 
70
- const updateColor = (property, _value) => {
71
- console.error('value', _value)
72
- if (property === 'storyNodeFontColor') {
73
- updateConfig({
74
- ...config,
75
- sankey: {
76
- ...config.sankey,
77
- storyNodeFontColor: _value
78
- }
79
- })
80
- return
81
- } else {
82
- updateConfig({
83
- ...config,
84
- sankey: {
85
- ...config.sankey,
86
- [property]: {
87
- ...config.sankey[property],
88
- default: _value
89
- }
90
- }
91
- })
92
- }
93
- }
94
-
95
69
  const handleStyleTreatmentChange = (value: string) => {
96
70
  const useTp5Treatment = value === 'tp5'
97
71
 
@@ -214,7 +188,7 @@ const PanelVisual: FC<PanelProps> = props => {
214
188
  options={['true', 'false']}
215
189
  />
216
190
  )}
217
- {visCanAnimate() && (
191
+ {visCanAnimate() && config.visualizationSubType !== 'racing' && (
218
192
  <CheckBox
219
193
  value={config.animate}
220
194
  fieldName='animate'
@@ -429,7 +403,7 @@ const PanelVisual: FC<PanelProps> = props => {
429
403
  // Set default palette if none exists
430
404
  if (!_state.general.palette.name) {
431
405
  _state.general.palette.name = 'qualitative_standard'
432
- _state.general.palette.version = '2.0'
406
+ _state.general.palette.version = '2.1'
433
407
  }
434
408
  }
435
409
  updateConfig(_state)
@@ -462,40 +436,6 @@ const PanelVisual: FC<PanelProps> = props => {
462
436
  <SeriesColorAssignments config={config} updateConfig={updateConfig} colorPalettes={colorPalettes} />
463
437
  </>
464
438
  )}
465
- {config.visualizationType === 'Sankey' && (
466
- <>
467
- <span className='sankey__color-input'>
468
- <input
469
- type='color'
470
- value={config.sankey.nodeColor.default}
471
- id='storyNodeColor'
472
- name='storyNodeColor'
473
- onChange={e => updateColor('nodeColor', e.target.value)}
474
- />
475
- <label htmlFor='storyNodeColor'>Story Node Color</label>
476
- </span>
477
- <span className='sankey__color-input'>
478
- <input
479
- type='color'
480
- value={config.sankey.storyNodeFontColor || 'red'}
481
- id='storyNodeFontColor'
482
- name='storyNodeFontColor'
483
- onChange={e => updateColor('storyNodeFontColor', e.target.value)}
484
- />
485
- <label htmlFor='storyNodeFontColor'>Story Node Font Color</label>
486
- </span>
487
- <span className='sankey__color-input'>
488
- <input
489
- type='color'
490
- value={config.sankey.linkColor.default}
491
- id='linkColor'
492
- name='linkColor'
493
- onChange={e => updateColor('linkColor', e.target.value)}
494
- />
495
- <label htmlFor='linkColor'>Link Color</label>
496
- </span>
497
- </>
498
- )}
499
439
  {(config.visualizationType === 'Paired Bar' || config.visualizationType === 'Deviation Bar') && (
500
440
  <>
501
441
  <DeveloperPaletteRollback config={config} updateConfig={updateConfig} className='mt-3' />
@@ -514,7 +454,7 @@ const PanelVisual: FC<PanelProps> = props => {
514
454
 
515
455
  if (!paletteColors || paletteColors.length < 2) {
516
456
  console.warn(
517
- `Two-color palette "${palette}" not found or incomplete in version ${getColorPaletteVersion(
457
+ `Two-color palette "${palette}" not found or incomplete in version ${getColorPaletteMajorVersion(
518
458
  config
519
459
  )}`
520
460
  )
@@ -579,7 +519,9 @@ const PanelVisual: FC<PanelProps> = props => {
579
519
  )}
580
520
 
581
521
  {(config.orientation !== 'horizontal' || config.visualizationType === 'Combo') &&
582
- config.visualizationType !== 'Warming Stripes' && (
522
+ config.visualizationType !== 'Warming Stripes' &&
523
+ !['Sankey', 'Network', 'Dendrogram'].includes(config.visualizationType) &&
524
+ config.visualizationType !== 'Pie' && (
583
525
  <TextField
584
526
  value={config.barThickness}
585
527
  type='number'
@@ -0,0 +1,131 @@
1
+ import React, { useState } from 'react'
2
+ import { fireEvent, render, screen } from '@testing-library/react'
3
+
4
+ import ConfigContext from '../../../../ConfigContext'
5
+ import type { ChartConfig } from '../../../../types/ChartConfig'
6
+ import { createMockChartContext } from '../../../LinearChart/tests/mockConfigContext'
7
+ import EditorPanelContext from '../../EditorPanelContext'
8
+ import barRaceConfig from '../../../../../examples/feature/bar/bar-chart-race-annual-change.json'
9
+ import lineRaceConfig from '../../../../../examples/feature/line/line-chart-race.json'
10
+ import RacingControls from './RacingControls'
11
+
12
+ const originalUrl = window.location.href
13
+
14
+ const renderControls = (config: ChartConfig) => {
15
+ const updateConfig = vi.fn()
16
+ const updateField = vi.fn()
17
+
18
+ render(
19
+ <ConfigContext.Provider
20
+ value={createMockChartContext(config, {
21
+ transformedData: config.data,
22
+ updateConfig
23
+ })}
24
+ >
25
+ <EditorPanelContext.Provider value={{ updateField }}>
26
+ <RacingControls />
27
+ </EditorPanelContext.Provider>
28
+ </ConfigContext.Provider>
29
+ )
30
+
31
+ return { updateConfig, updateField }
32
+ }
33
+
34
+ const StatefulControls = ({ initialConfig }: { initialConfig: ChartConfig }) => {
35
+ const [config, setConfig] = useState(initialConfig)
36
+
37
+ return (
38
+ <ConfigContext.Provider
39
+ value={createMockChartContext(config, {
40
+ transformedData: config.data,
41
+ updateConfig: setConfig
42
+ })}
43
+ >
44
+ <EditorPanelContext.Provider value={{ updateField: vi.fn() }}>
45
+ <RacingControls />
46
+ </EditorPanelContext.Provider>
47
+ <button onClick={() => setConfig(current => ({ ...current, title: 'Updated title' }))}>Update title</button>
48
+ </ConfigContext.Provider>
49
+ )
50
+ }
51
+
52
+ const getSubtypeValues = () =>
53
+ Array.from((screen.getByLabelText('Chart Subtype') as HTMLSelectElement).options).map(option => option.value)
54
+
55
+ describe('RacingControls', () => {
56
+ beforeEach(() => {
57
+ window.history.replaceState({}, '', window.location.pathname)
58
+ })
59
+
60
+ afterEach(() => {
61
+ window.history.replaceState({}, '', originalUrl)
62
+ })
63
+
64
+ it('hides the Bar racing option outside COVE developer mode', () => {
65
+ renderControls({ ...barRaceConfig, visualizationSubType: 'regular' } as ChartConfig)
66
+
67
+ expect(getSubtypeValues()).toEqual(['regular', 'stacked'])
68
+ })
69
+
70
+ it('shows the Bar racing option for an ineligible chart in COVE developer mode', () => {
71
+ window.history.replaceState({}, '', `${window.location.pathname}?isCoveDeveloper=true`)
72
+
73
+ const { updateConfig, updateField } = renderControls({
74
+ ...barRaceConfig,
75
+ visualizationSubType: 'regular',
76
+ data: []
77
+ } as ChartConfig)
78
+
79
+ expect(getSubtypeValues()).toEqual(['regular', 'stacked', 'racing'])
80
+ fireEvent.change(screen.getByLabelText('Chart Subtype'), { target: { value: 'racing' } })
81
+ expect(updateField).not.toHaveBeenCalled()
82
+ expect(updateConfig).toHaveBeenCalledTimes(1)
83
+ expect(updateConfig).toHaveBeenCalledWith(
84
+ expect.objectContaining({
85
+ visualizationSubType: 'racing',
86
+ orientation: 'horizontal',
87
+ barStyle: 'flat',
88
+ isLollipopChart: false
89
+ })
90
+ )
91
+ })
92
+
93
+ it('keeps Racing selected when the chart is ineligible and its controls rerender', () => {
94
+ window.history.replaceState({}, '', `${window.location.pathname}?isCoveDeveloper=true`)
95
+ const initialConfig = {
96
+ ...barRaceConfig,
97
+ visualizationSubType: 'regular',
98
+ xAxis: { ...barRaceConfig.xAxis, type: 'date' }
99
+ } as ChartConfig
100
+
101
+ render(<StatefulControls initialConfig={initialConfig} />)
102
+ fireEvent.change(screen.getByLabelText('Chart Subtype'), { target: { value: 'racing' } })
103
+
104
+ expect(screen.getByLabelText('Chart Subtype')).toHaveValue('racing')
105
+
106
+ fireEvent.click(screen.getByRole('button', { name: 'Update title' }))
107
+ expect(screen.getByLabelText('Chart Subtype')).toHaveValue('racing')
108
+ })
109
+
110
+ it('keeps the option visible for an ineligible existing Bar race outside COVE developer mode', () => {
111
+ renderControls({ ...barRaceConfig, data: [] } as ChartConfig)
112
+
113
+ expect(getSubtypeValues()).toEqual(['regular', 'stacked', 'racing'])
114
+ expect(screen.getByLabelText('Chart Subtype')).toHaveValue('racing')
115
+ })
116
+
117
+ it('keeps the Bar racing option available after an existing race switches to Standard', () => {
118
+ render(<StatefulControls initialConfig={barRaceConfig as ChartConfig} />)
119
+
120
+ fireEvent.change(screen.getByLabelText('Chart Subtype'), { target: { value: 'regular' } })
121
+
122
+ expect(screen.getByLabelText('Chart Subtype')).toHaveValue('regular')
123
+ expect(getSubtypeValues()).toEqual(['regular', 'stacked', 'racing'])
124
+ })
125
+
126
+ it('does not gate Line racing', () => {
127
+ renderControls({ ...lineRaceConfig, visualizationSubType: 'regular' } as ChartConfig)
128
+
129
+ expect(getSubtypeValues()).toEqual(['regular', 'racing'])
130
+ })
131
+ })