@cdc/chart 4.26.3 → 4.26.5

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 (192) hide show
  1. package/CONFIG.md +489 -0
  2. package/README.md +42 -17
  3. package/dist/cdcchart.js +39024 -36587
  4. package/examples/feature/bar/additional-column-tooltip.json +3 -3
  5. package/examples/feature/deviation/planet-deviation-config.json +2 -2
  6. package/examples/feature/heatmap/average-age-categorical.json +313 -0
  7. package/examples/feature/heatmap/calendar-heatmap.json +134 -0
  8. package/examples/feature/heatmap/cell-values-bottom-axis.json +181 -0
  9. package/examples/feature/heatmap/sparse-aggregation.json +158 -0
  10. package/examples/feature/paired-bar/paired-bar-example.json +2 -2
  11. package/examples/feature/tests-case-rate/case-rate-example-config.json +2 -2
  12. package/examples/feature/tests-covid/covid-confidence-example-config.json +2 -2
  13. package/examples/feature/tests-covid/covid-example-config.json +2 -2
  14. package/examples/feature/tests-cutoff/cutoff-example-config.json +2 -2
  15. package/examples/feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json +2 -2
  16. package/examples/metadata-variables.json +1 -1
  17. package/examples/minimal-example.json +24 -0
  18. package/examples/private/bump-chart.json +1116 -0
  19. package/examples/private/test-123.json +624 -0
  20. package/examples/private/test2.json +35012 -0
  21. package/package.json +5 -4
  22. package/src/CdcChartComponent.tsx +144 -60
  23. package/src/_stories/Chart.AltText.stories.tsx +136 -0
  24. package/src/_stories/{Chart.HTMLInDataTable.stories.tsx → Chart.HTMLInDataTable.smoke.stories.tsx} +10 -0
  25. package/src/_stories/Chart.HeatMap.stories.tsx +187 -0
  26. package/src/_stories/Chart.Minimal.smoke.stories.tsx +25 -0
  27. package/src/_stories/Chart.Sankey.smoke.stories.tsx +62 -0
  28. package/src/_stories/Chart.SmallMultiples.smoke.stories.tsx +108 -0
  29. package/src/_stories/{Chart.stories.tsx → Chart.smoke.stories.tsx} +12 -0
  30. package/src/_stories/{Chart.tooltip.stories.tsx → Chart.tooltip.smoke.stories.tsx} +68 -0
  31. package/src/_stories/{ChartAxisTitles.stories.tsx → ChartAxisTitles.smoke.stories.tsx} +13 -0
  32. package/src/_stories/ChartBar.Editor.stories.tsx +151 -85
  33. package/src/_stories/ChartBrush.Interaction.stories.tsx +112 -0
  34. package/src/_stories/{ChartBrush.stories.tsx → ChartBrush.smoke.stories.tsx} +13 -4
  35. package/src/_stories/ChartEditor.Editor.stories.tsx +23 -0
  36. package/src/_stories/ChartLine.DashedDates.smoke.stories.tsx +23 -0
  37. package/src/_stories/_mock/alt_text_metadata.json +38 -0
  38. package/src/_stories/_mock/heatmap-average-age-categorical.json +315 -0
  39. package/src/_stories/_mock/heatmap.json +156 -0
  40. package/src/_stories/_mock/line_chart_dashed_dates.json +276 -0
  41. package/src/_stories/_mock/paired-bar-abbr.json +1 -1
  42. package/src/_stories/_mock/paired-bar.json +1 -1
  43. package/src/_stories/_mock/reported_pertussis_cases.json +1643 -0
  44. package/src/_stories/_mock/sankey_mock.json +78 -0
  45. package/src/_stories/_mock/small_multiples/small_multiples_lines.json +1 -1
  46. package/src/components/Annotations/components/AnnotationDraggable.tsx +2 -2
  47. package/src/components/Annotations/components/AnnotationDropdown.tsx +5 -2
  48. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -2
  49. package/src/components/Axis/BottomAxis.tsx +1 -1
  50. package/src/components/Axis/LeftAxis.tsx +15 -14
  51. package/src/components/Axis/LeftAxisGridlines.tsx +14 -13
  52. package/src/components/BarChart/components/BarChart.Horizontal.tsx +9 -5
  53. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +9 -5
  54. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +9 -5
  55. package/src/components/BarChart/components/BarChart.Vertical.tsx +9 -6
  56. package/src/components/BarChart/components/BarChart.tsx +2 -6
  57. package/src/components/BarChart/helpers/useBarChart.ts +1 -1
  58. package/src/components/BoxPlot/BoxPlot.Vertical.tsx +3 -3
  59. package/src/components/Brush/BrushSelector.tsx +88 -187
  60. package/src/components/Brush/brushInteraction.ts +22 -0
  61. package/src/components/Brush/tests/brushInteraction.test.ts +38 -0
  62. package/src/components/EditorPanel/EditorPanel.tsx +946 -960
  63. package/src/components/EditorPanel/EditorPanelContext.ts +1 -1
  64. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +229 -165
  65. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +105 -13
  66. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +169 -116
  67. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +159 -109
  68. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
  69. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +14 -28
  70. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +1 -1
  71. package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +57 -0
  72. package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +56 -0
  73. package/src/components/EditorPanel/useEditorPermissions.test.tsx +70 -0
  74. package/src/components/EditorPanel/useEditorPermissions.ts +32 -21
  75. package/src/components/Forecasting/Forecasting.tsx +2 -2
  76. package/src/components/HeatMap/components/HeatMap.tsx +762 -0
  77. package/src/components/HeatMap/components/HeatMapGradientLegend.tsx +114 -0
  78. package/src/components/HeatMap/heatmap.constants.ts +13 -0
  79. package/src/components/HeatMap/heatmap.css +71 -0
  80. package/src/components/HeatMap/helpers/index.ts +261 -0
  81. package/src/components/HeatMap/index.tsx +5 -0
  82. package/src/components/HeatMap/tests/HeatMap.test.tsx +1040 -0
  83. package/src/components/HeatMap/tests/helpers.test.ts +167 -0
  84. package/src/components/HorizonChart/HorizonChart.tsx +10 -2
  85. package/src/components/HoverLine/HoverLine.tsx +6 -8
  86. package/src/components/Legend/Legend.Component.tsx +4 -4
  87. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +5 -0
  88. package/src/components/Legend/LegendGroup/LegendGroup.tsx +3 -1
  89. package/src/components/Legend/helpers/index.ts +0 -2
  90. package/src/components/LineChart/LineChartProps.ts +1 -0
  91. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +22 -24
  92. package/src/components/LineChart/helpers.test.ts +345 -0
  93. package/src/components/LineChart/helpers.ts +15 -5
  94. package/src/components/LineChart/index.tsx +18 -5
  95. package/src/components/LinearChart/VisualizationRenderer.tsx +11 -1
  96. package/src/components/LinearChart/tests/LinearChart.test.tsx +228 -7
  97. package/src/components/LinearChart/tests/mockConfigContext.ts +1 -0
  98. package/src/components/LinearChart.tsx +155 -77
  99. package/src/components/PairedBarChart.jsx +2 -2
  100. package/src/components/PieChart/PieChart.tsx +15 -10
  101. package/src/components/RadarChart/RadarChart.tsx +3 -1
  102. package/src/components/Regions/components/Regions.tsx +199 -35
  103. package/src/components/Sankey/components/Sankey.tsx +7 -3
  104. package/src/components/Sankey/helpers/getSankeyTooltip.tsx +7 -6
  105. package/src/components/ScatterPlot/ScatterPlot.jsx +19 -17
  106. package/src/components/SmallMultiples/SmallMultipleTile.tsx +31 -3
  107. package/src/components/SmallMultiples/SmallMultiples.css +12 -0
  108. package/src/components/SmallMultiples/SmallMultiples.tsx +15 -9
  109. package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +152 -0
  110. package/src/components/Sparkline/components/SparkLine.tsx +5 -10
  111. package/src/components/WarmingStripes/WarmingStripes.tsx +5 -3
  112. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +1 -1
  113. package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +2 -2
  114. package/src/data/initial-state.js +12 -0
  115. package/src/helpers/axisLabelFontSize.ts +8 -0
  116. package/src/helpers/bumpChartTooltip.ts +54 -0
  117. package/src/helpers/calculateLeftYAxisWidth.ts +197 -0
  118. package/src/helpers/ensureSpecialChartAxisTypes.ts +11 -0
  119. package/src/helpers/getMinMax.ts +2 -3
  120. package/src/helpers/getYAxisAutoPadding.ts +8 -6
  121. package/src/helpers/getYAxisAutoPaddingMode.ts +17 -0
  122. package/src/helpers/handleChartAriaLabels.ts +23 -10
  123. package/src/helpers/seriesHighlight.ts +26 -0
  124. package/src/helpers/tests/axisLabelFontSize.test.ts +15 -0
  125. package/src/helpers/tests/bumpChartTooltip.test.ts +75 -0
  126. package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +30 -0
  127. package/src/helpers/tests/getYAxisAutoPadding.test.ts +37 -0
  128. package/src/helpers/tests/getYAxisAutoPaddingMode.test.ts +90 -0
  129. package/src/helpers/tests/handleChartAriaLabels.test.ts +71 -0
  130. package/src/helpers/tests/seriesHighlight.test.ts +61 -0
  131. package/src/helpers/tests/tooltipHelpers.test.ts +151 -0
  132. package/src/helpers/tooltipHelpers.ts +176 -0
  133. package/src/hooks/tests/useScales.test.tsx +105 -0
  134. package/src/hooks/useHighlightedBars.ts +9 -16
  135. package/src/hooks/useProgrammaticTooltip.ts +16 -2
  136. package/src/hooks/useScales.ts +19 -9
  137. package/src/hooks/useSmallMultipleSynchronization.ts +3 -4
  138. package/src/hooks/useTooltip.tsx +167 -38
  139. package/src/index.jsx +5 -2
  140. package/src/scss/main.scss +32 -49
  141. package/src/test/CdcChart.test.jsx +24 -0
  142. package/src/types/ChartConfig.ts +19 -0
  143. package/src/types/ChartContext.ts +2 -0
  144. package/examples/chart-regression-2.json +0 -2360
  145. package/examples/feature/__data__/age-adjusted-rates.json +0 -1486
  146. package/examples/feature/__data__/city-temperature.json +0 -2198
  147. package/examples/feature/bar/Barchart_with_negative.json +0 -34
  148. package/examples/feature/boxplot/box-plot-data.json +0 -71
  149. package/examples/feature/boxplot/boxplot-data.json +0 -156
  150. package/examples/feature/combo/combochart-categories_are_numbers .json +0 -18
  151. package/examples/feature/forest-plot/log.json +0 -26
  152. package/examples/feature/tests-date-exclusions/date-exclusions-data.json +0 -162
  153. package/examples/region-issue.json +0 -2065
  154. package/examples/xaxis.json +0 -493
  155. package/src/_stories/Chart.SmallMultiples.stories.tsx +0 -63
  156. /package/examples/{data → __data__}/data-with-metadata.json +0 -0
  157. /package/examples/feature/bar/{tall-data.json → __data__/tall-data.json} +0 -0
  158. /package/examples/feature/deviation/{planet-deviation-data.json → __data__/planet-deviation-data.json} +0 -0
  159. /package/examples/feature/paired-bar/{paired-bar-data.json → __data__/paired-bar-data.json} +0 -0
  160. /package/examples/feature/paired-bar/{paired-bar-formatted.json → __data__/paired-bar-formatted.json} +0 -0
  161. /package/examples/feature/tests-case-rate/{case-rate-example-data.json → __data__/case-rate-example-data.json} +0 -0
  162. /package/examples/feature/tests-covid/{covid-example-data-confidence.json → __data__/covid-example-data-confidence.json} +0 -0
  163. /package/examples/feature/tests-covid/{covid-example-data.json → __data__/covid-example-data.json} +0 -0
  164. /package/examples/feature/tests-cutoff/{cutoff-example-data.json → __data__/cutoff-example-data.json} +0 -0
  165. /package/examples/feature/tests-non-numerics/{planet-example-data-nonnumeric.json → __data__/planet-example-data-nonnumeric.json} +0 -0
  166. /package/src/_stories/{Chart.Anchors.stories.tsx → Chart.Anchors.smoke.stories.tsx} +0 -0
  167. /package/src/_stories/{Chart.BoxPlot.stories.tsx → Chart.BoxPlot.smoke.stories.tsx} +0 -0
  168. /package/src/_stories/{Chart.CI.stories.tsx → Chart.CI.smoke.stories.tsx} +0 -0
  169. /package/src/_stories/{Chart.Combo.stories.tsx → Chart.Combo.smoke.stories.tsx} +0 -0
  170. /package/src/_stories/{Chart.CustomColors.stories.tsx → Chart.CustomColors.smoke.stories.tsx} +0 -0
  171. /package/src/_stories/{Chart.Defaults.stories.tsx → Chart.Defaults.smoke.stories.tsx} +0 -0
  172. /package/src/_stories/{Chart.DynamicSeries.stories.tsx → Chart.DynamicSeries.smoke.stories.tsx} +0 -0
  173. /package/src/_stories/{Chart.Filters.stories.tsx → Chart.Filters.smoke.stories.tsx} +0 -0
  174. /package/src/_stories/{Chart.Forecast.stories.tsx → Chart.Forecast.smoke.stories.tsx} +0 -0
  175. /package/src/_stories/{Chart.Legend.Gradient.stories.tsx → Chart.Legend.Gradient.smoke.stories.tsx} +0 -0
  176. /package/src/_stories/{Chart.Patterns.stories.tsx → Chart.Patterns.smoke.stories.tsx} +0 -0
  177. /package/src/_stories/{Chart.PreserveDecimals.stories.tsx → Chart.PreserveDecimals.smoke.stories.tsx} +0 -0
  178. /package/src/_stories/{Chart.Regions.Categorical.stories.tsx → Chart.Regions.Categorical.smoke.stories.tsx} +0 -0
  179. /package/src/_stories/{Chart.Regions.DateScale.stories.tsx → Chart.Regions.DateScale.smoke.stories.tsx} +0 -0
  180. /package/src/_stories/{Chart.Regions.DateTimeScale.stories.tsx → Chart.Regions.DateTimeScale.smoke.stories.tsx} +0 -0
  181. /package/src/_stories/{Chart.ScatterPlot.stories.tsx → Chart.ScatterPlot.smoke.stories.tsx} +0 -0
  182. /package/src/_stories/{Chart.SmallestLeftAxisMax.stories.tsx → Chart.SmallestLeftAxisMax.smoke.stories.tsx} +0 -0
  183. /package/src/_stories/{ChartAnnotation.stories.tsx → ChartAnnotation.smoke.stories.tsx} +0 -0
  184. /package/src/_stories/{ChartAxisLabels.stories.tsx → ChartAxisLabels.smoke.stories.tsx} +0 -0
  185. /package/src/_stories/{ChartBrush.Matrix.Continuous.stories.tsx → ChartBrush.Matrix.Continuous.smoke.stories.tsx} +0 -0
  186. /package/src/_stories/{ChartBrush.Matrix.Date.stories.tsx → ChartBrush.Matrix.Date.smoke.stories.tsx} +0 -0
  187. /package/src/_stories/{ChartBrush.Matrix.DateTime.stories.tsx → ChartBrush.Matrix.DateTime.smoke.stories.tsx} +0 -0
  188. /package/src/_stories/{ChartLine.QuadrantAngles.stories.tsx → ChartLine.QuadrantAngles.smoke.stories.tsx} +0 -0
  189. /package/src/_stories/{ChartLine.Suppression.stories.tsx → ChartLine.Suppression.smoke.stories.tsx} +0 -0
  190. /package/src/_stories/{ChartLine.Symbols.stories.tsx → ChartLine.Symbols.smoke.stories.tsx} +0 -0
  191. /package/src/_stories/{ChartPrefixSuffix.stories.tsx → ChartPrefixSuffix.smoke.stories.tsx} +0 -0
  192. /package/src/_stories/{TechAdoptionWithLinks.stories.tsx → TechAdoptionWithLinks.smoke.stories.tsx} +0 -0
@@ -115,16 +115,16 @@ const SankeySettings: React.FC<PanelProps> = props => {
115
115
  </div>
116
116
  ))}
117
117
  {data?.storyNodeText?.length < 3 && (
118
- <button
118
+ <Button
119
119
  type='button'
120
- className='btn btn-primary full-width'
120
+ variant='editor-primary'
121
121
  onClick={e => {
122
122
  e.preventDefault()
123
123
  addStoryNode()
124
124
  }}
125
125
  >
126
126
  Add StoryNode
127
- </button>
127
+ </Button>
128
128
  )}
129
129
  {config.data?.[0]?.tooltips?.length > 0 && (
130
130
  <CheckBox
@@ -9,6 +9,7 @@ import { updatePaletteNames } from '@cdc/core/helpers/updatePaletteNames'
9
9
  import { getColorPaletteVersion } from '@cdc/core/helpers/getColorPaletteVersion'
10
10
  import Icon from '@cdc/core/components/ui/Icon'
11
11
  import { CheckBox, Select, TextField } from '@cdc/core/components/EditorPanel/Inputs'
12
+ import Button from '@cdc/core/components/elements/Button'
12
13
  import { buildForecastPaletteOptions } from '../../../../helpers/buildForecastPaletteOptions'
13
14
  import { getSeriesColumnConfig, upsertSeriesColumnConfig } from '../../../../helpers/seriesColumnSettings'
14
15
 
@@ -20,7 +21,7 @@ import {
20
21
  AccordionItemPanel,
21
22
  AccordionItemButton
22
23
  } from 'react-accessible-accordion'
23
- import { Draggable, DragDropContext, Droppable } from '@hello-pangea/dnd'
24
+ import { Draggable } from '@hello-pangea/dnd'
24
25
  import Tooltip from '@cdc/core/components/ui/Tooltip'
25
26
 
26
27
  const SeriesContext = React.createContext({})
@@ -375,8 +376,8 @@ const SeriesDropdownConfidenceInterval = props => {
375
376
  )
376
377
  })}
377
378
  </Accordion>
378
- <button
379
- className='btn btn-primary full-width'
379
+ <Button
380
+ variant='editor-primary'
380
381
  onClick={e => {
381
382
  e.preventDefault()
382
383
  let copiedIndex = null
@@ -397,7 +398,7 @@ const SeriesDropdownConfidenceInterval = props => {
397
398
  }}
398
399
  >
399
400
  Add Confidence Interval Group
400
- </button>
401
+ </Button>
401
402
  </fieldset>
402
403
  </div>
403
404
  )
@@ -652,17 +653,17 @@ const SeriesButtonRemove = props => {
652
653
  }
653
654
  }
654
655
 
655
- const handleRemoveSeries = (e, series, index) => {
656
+ const handleRemoveSeries = (e, series) => {
656
657
  e.preventDefault()
657
658
  removeSeries(series.dataKey)
658
659
  }
659
660
 
660
661
  return (
661
662
  config.series &&
662
- config.series.length > 1 && (
663
- <button className='series-list__remove' onClick={e => handleRemoveSeries(e, series, index)}>
663
+ (config.series.length > 1 || config.visualizationType === 'HeatMap') && (
664
+ <Button variant='danger' size='sm' className='grouped-list__remove' onClick={e => handleRemoveSeries(e, series)}>
664
665
  Remove
665
- </button>
666
+ </Button>
666
667
  )
667
668
  )
668
669
  }
@@ -709,12 +710,14 @@ const SeriesItem = props => {
709
710
  }
710
711
  >
711
712
  <Icon display='move' size={15} style={{ cursor: 'default' }} />
712
- {series.dataKey}
713
- <Series.Button.Remove series={series} index={i} />
713
+ {config.visualizationType === 'HeatMap' ? series.name || series.dataKey : series.dataKey}
714
714
  </AccordionItemButton>
715
715
  </AccordionItemHeading>
716
716
  {chartsWithOptions.includes(config.visualizationType) && (
717
717
  <AccordionItemPanel>
718
+ <div className='series-item__panel-actions'>
719
+ <Series.Button.Remove series={series} index={i} />
720
+ </div>
718
721
  <Series.Input.Name series={series} index={i} />
719
722
  <Series.Input.ColumnSettings series={series} index={i} />
720
723
  {showDynamicCategory && (
@@ -773,22 +776,6 @@ const SeriesItem = props => {
773
776
  )
774
777
  }
775
778
 
776
- const SeriesList = props => {
777
- const { series, getItemStyle, sortableItemStyles, chartsWithOptions } = props
778
- return series.map((series, i) => {
779
- return (
780
- <SeriesItem
781
- getItemStyle={getItemStyle}
782
- sortableItemStyles={sortableItemStyles}
783
- chartsWithOptions={chartsWithOptions}
784
- series={series}
785
- index={i}
786
- key={`series-list-${i}`}
787
- />
788
- )
789
- })
790
- }
791
-
792
779
  const Series = {
793
780
  Wrapper: SeriesWrapper,
794
781
  Dropdown: {
@@ -811,8 +798,7 @@ const Series = {
811
798
  Button: {
812
799
  Remove: SeriesButtonRemove
813
800
  },
814
- Item: SeriesItem,
815
- List: SeriesList
801
+ Item: SeriesItem
816
802
  }
817
803
 
818
804
  export default Series
@@ -306,7 +306,7 @@ const PanelVisual: FC<PanelProps> = props => {
306
306
  />
307
307
  </>
308
308
  )}
309
- {(visSupportsNonSequentialPallete() || visSupportsNonSequentialPallete()) && (
309
+ {(visSupportsSequentialPallete() || visSupportsNonSequentialPallete()) && (
310
310
  <>
311
311
  <label>
312
312
  <span className='edit-label'>Chart Color Palette</span>
@@ -0,0 +1,57 @@
1
+ import { type ChartConfig, type VisualizationType } from '../../../types/ChartConfig'
2
+ import { HEATMAP_CONFIG_DEFAULTS } from '../../HeatMap/heatmap.constants'
3
+
4
+ export const getVisualizationTypeConfigUpdate = (
5
+ config: ChartConfig,
6
+ visualizationType: VisualizationType
7
+ ): ChartConfig | undefined => {
8
+ if (visualizationType === 'Forecasting' && config.xAxis.type === 'categorical') {
9
+ return {
10
+ ...config,
11
+ visualizationType,
12
+ xAxis: {
13
+ ...config.xAxis,
14
+ type: 'date',
15
+ dateParseFormat: config.xAxis.dateParseFormat || '%Y-%m-%d',
16
+ dateDisplayFormat: config.xAxis.dateDisplayFormat || '%Y-%m-%d'
17
+ }
18
+ }
19
+ }
20
+
21
+ if (visualizationType === 'HeatMap') {
22
+ return {
23
+ ...config,
24
+ visualizationType,
25
+ series: [],
26
+ yAxis: {
27
+ ...config.yAxis,
28
+ type: 'categorical',
29
+ titlePlacement: 'side'
30
+ },
31
+ heatmap: {
32
+ cellPadding: config.heatmap?.cellPadding ?? HEATMAP_CONFIG_DEFAULTS.cellPadding,
33
+ rowLabelGap: config.heatmap?.rowLabelGap ?? HEATMAP_CONFIG_DEFAULTS.rowLabelGap,
34
+ columnLabelGap: config.heatmap?.columnLabelGap ?? HEATMAP_CONFIG_DEFAULTS.columnLabelGap,
35
+ colorBucketCount: config.heatmap?.colorBucketCount ?? HEATMAP_CONFIG_DEFAULTS.colorBucketCount,
36
+ xAxisPosition: config.heatmap?.xAxisPosition ?? HEATMAP_CONFIG_DEFAULTS.xAxisPosition,
37
+ showCellValues: config.heatmap?.showCellValues ?? HEATMAP_CONFIG_DEFAULTS.showCellValues
38
+ },
39
+ legend: {
40
+ ...config.legend,
41
+ position: 'top',
42
+ style: 'gradient',
43
+ subStyle: 'smooth'
44
+ },
45
+ general: {
46
+ ...config.general,
47
+ palette: config.general?.palette || {
48
+ isReversed: false,
49
+ version: '2.0',
50
+ name: 'sequential_blue'
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ return undefined
57
+ }
@@ -0,0 +1,56 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { type ChartConfig } from '../../../../types/ChartConfig'
3
+ import { HEATMAP_CONFIG_DEFAULTS } from '../../../HeatMap/heatmap.constants'
4
+ import { getVisualizationTypeConfigUpdate } from '../getVisualizationTypeConfigUpdate'
5
+
6
+ const buildConfig = (overrides: Partial<ChartConfig> = {}) =>
7
+ ({
8
+ visualizationType: 'Bar',
9
+ xAxis: {
10
+ type: 'categorical'
11
+ },
12
+ yAxis: {
13
+ type: 'linear'
14
+ },
15
+ series: [{ dataKey: 'Value' }],
16
+ legend: {
17
+ position: 'bottom',
18
+ style: 'circles',
19
+ subStyle: 'linear blocks'
20
+ },
21
+ general: {},
22
+ ...overrides
23
+ } as ChartConfig)
24
+
25
+ describe('getVisualizationTypeConfigUpdate', () => {
26
+ it('does not build a special config update for chart types without transition rules', () => {
27
+ expect(getVisualizationTypeConfigUpdate(buildConfig(), 'Line')).toBeUndefined()
28
+ })
29
+
30
+ it('normalizes categorical x-axis settings when switching to Forecasting', () => {
31
+ const updatedConfig = getVisualizationTypeConfigUpdate(buildConfig(), 'Forecasting')
32
+
33
+ expect(updatedConfig?.visualizationType).toBe('Forecasting')
34
+ expect(updatedConfig?.xAxis.type).toBe('date')
35
+ expect(updatedConfig?.xAxis.dateParseFormat).toBe('%Y-%m-%d')
36
+ expect(updatedConfig?.xAxis.dateDisplayFormat).toBe('%Y-%m-%d')
37
+ })
38
+
39
+ it('applies HeatMap defaults and required editor config when switching to HeatMap', () => {
40
+ const updatedConfig = getVisualizationTypeConfigUpdate(buildConfig(), 'HeatMap')
41
+
42
+ expect(updatedConfig?.visualizationType).toBe('HeatMap')
43
+ expect(updatedConfig?.series).toEqual([])
44
+ expect(updatedConfig?.yAxis.type).toBe('categorical')
45
+ expect(updatedConfig?.yAxis.titlePlacement).toBe('side')
46
+ expect(updatedConfig?.heatmap).toEqual(HEATMAP_CONFIG_DEFAULTS)
47
+ expect(updatedConfig?.legend.position).toBe('top')
48
+ expect(updatedConfig?.legend.style).toBe('gradient')
49
+ expect(updatedConfig?.legend.subStyle).toBe('smooth')
50
+ expect(updatedConfig?.general.palette).toEqual({
51
+ isReversed: false,
52
+ version: '2.0',
53
+ name: 'sequential_blue'
54
+ })
55
+ })
56
+ })
@@ -0,0 +1,70 @@
1
+ import React from 'react'
2
+ import { renderHook } from '@testing-library/react'
3
+ import { describe, expect, it } from 'vitest'
4
+ import ConfigContext from '../../ConfigContext'
5
+ import { useEditorPermissions } from './useEditorPermissions'
6
+ import { createMockChartContext, createMockConfig } from '../LinearChart/tests/mockConfigContext'
7
+ import { ChartConfig } from '../../types/ChartConfig'
8
+
9
+ const renderUseEditorPermissions = (configOverrides: Partial<ChartConfig>) => {
10
+ const config = createMockConfig(configOverrides)
11
+ const context = createMockChartContext(config)
12
+ const wrapper = ({ children }: React.PropsWithChildren) => (
13
+ <ConfigContext.Provider value={context}>{children}</ConfigContext.Provider>
14
+ )
15
+
16
+ return renderHook(() => useEditorPermissions(), { wrapper })
17
+ }
18
+
19
+ describe('useEditorPermissions', () => {
20
+ it('shows manual y-axis padding controls when auto-padding mode is none', () => {
21
+ const { result } = renderUseEditorPermissions({
22
+ yAxis: {
23
+ ...createMockConfig().yAxis,
24
+ titlePlacement: 'side',
25
+ inlineLabel: 'Rate'
26
+ }
27
+ })
28
+
29
+ expect(result.current.visSupportsYPadding()).toBe(true)
30
+ })
31
+
32
+ it('hides manual y-axis padding controls for inline-label auto-padding', () => {
33
+ const { result } = renderUseEditorPermissions({
34
+ yAxis: {
35
+ ...createMockConfig().yAxis,
36
+ titlePlacement: 'side',
37
+ inlineLabel: 'per 100k'
38
+ }
39
+ })
40
+
41
+ expect(result.current.visSupportsYPadding()).toBe(false)
42
+ })
43
+
44
+ it('hides manual y-axis padding controls for top-title auto-padding', () => {
45
+ const { result } = renderUseEditorPermissions({
46
+ yAxis: {
47
+ ...createMockConfig().yAxis,
48
+ titlePlacement: 'top',
49
+ label: 'Y-Axis',
50
+ inlineLabel: ''
51
+ }
52
+ })
53
+
54
+ expect(result.current.visSupportsYPadding()).toBe(false)
55
+ })
56
+
57
+ it('shows manual y-axis padding controls when a top y-axis title is hidden', () => {
58
+ const { result } = renderUseEditorPermissions({
59
+ hideYAxisLabel: true,
60
+ yAxis: {
61
+ ...createMockConfig().yAxis,
62
+ titlePlacement: 'top',
63
+ label: 'Y-Axis',
64
+ inlineLabel: ''
65
+ }
66
+ })
67
+
68
+ expect(result.current.visSupportsYPadding()).toBe(true)
69
+ })
70
+ })
@@ -1,5 +1,6 @@
1
1
  import React, { useContext } from 'react'
2
2
  import ConfigContext from '../../ConfigContext'
3
+ import { getYAxisAutoPaddingMode } from '../../helpers/getYAxisAutoPaddingMode'
3
4
 
4
5
  export const useEditorPermissions = () => {
5
6
  const { config } = useContext(ConfigContext)
@@ -16,6 +17,7 @@ export const useEditorPermissions = () => {
16
17
  'Deviation Bar',
17
18
  'Forecasting',
18
19
  // 'Forest Plot',
20
+ 'HeatMap',
19
21
  'Horizon Chart',
20
22
  'Line',
21
23
  'Paired Bar',
@@ -58,7 +60,7 @@ export const useEditorPermissions = () => {
58
60
  }
59
61
 
60
62
  const visSupportsClickingLegend = () => {
61
- const disabledCharts = ['Horizon Chart']
63
+ const disabledCharts = ['HeatMap', 'Horizon Chart']
62
64
  if (disabledCharts.includes(visualizationType)) return false
63
65
  return true
64
66
  }
@@ -75,6 +77,7 @@ export const useEditorPermissions = () => {
75
77
  'Box Plot',
76
78
  'Bump Chart',
77
79
  'Forest Plot',
80
+ 'HeatMap',
78
81
  'Horizon Chart',
79
82
  'Pie',
80
83
  'Radar',
@@ -93,6 +96,7 @@ export const useEditorPermissions = () => {
93
96
  'Box Plot',
94
97
  'Bump Chart',
95
98
  'Forest Plot',
99
+ 'HeatMap',
96
100
  'Radar',
97
101
  'Sankey',
98
102
  'Scatter Plot',
@@ -109,6 +113,8 @@ export const useEditorPermissions = () => {
109
113
  return true
110
114
  case 'Forest Plot':
111
115
  return false
116
+ case 'HeatMap':
117
+ return true
112
118
  case 'Spark Line':
113
119
  return false
114
120
  case 'Sankey':
@@ -197,9 +203,13 @@ export const useEditorPermissions = () => {
197
203
  }
198
204
  }
199
205
 
200
- const visHasSelectableLegendValues = !['Box Plot', 'Forest Plot', 'Spark Line', 'Warming Stripes'].includes(
201
- visualizationType
202
- )
206
+ const visHasSelectableLegendValues = ![
207
+ 'Box Plot',
208
+ 'Forest Plot',
209
+ 'HeatMap',
210
+ 'Spark Line',
211
+ 'Warming Stripes'
212
+ ].includes(visualizationType)
203
213
  const visHasLegendAxisAlign = () => {
204
214
  return visualizationType === 'Bar' && visualizationSubType === 'stacked' && config.legend.behavior === 'isolate'
205
215
  }
@@ -208,7 +218,7 @@ export const useEditorPermissions = () => {
208
218
  }
209
219
 
210
220
  const visSupportsTooltipOpacity = () => {
211
- const disabledCharts = ['Spark Line', 'Sankey', 'Warming Stripes']
221
+ const disabledCharts = ['HeatMap', 'Spark Line', 'Sankey', 'Warming Stripes']
212
222
  if (disabledCharts.includes(visualizationType)) return false
213
223
  return true
214
224
  }
@@ -226,7 +236,7 @@ export const useEditorPermissions = () => {
226
236
  }
227
237
 
228
238
  const visSupportsNonSequentialPallete = () => {
229
- const disabledCharts = ['Paired Bar', 'Deviation Bar', 'Forest Plot', 'Forecasting', 'Sankey']
239
+ const disabledCharts = ['HeatMap', 'Paired Bar', 'Deviation Bar', 'Forest Plot', 'Forecasting', 'Sankey']
230
240
  if (disabledCharts.includes(visualizationType)) return false
231
241
  return true
232
242
  }
@@ -238,19 +248,19 @@ export const useEditorPermissions = () => {
238
248
  }
239
249
 
240
250
  const visSupportsDateCategoryAxisLabel = () => {
241
- const disabledCharts = ['Forest Plot', 'Spark Line', 'Bump Chart', 'Warming Stripes']
251
+ const disabledCharts = ['Forest Plot', 'HeatMap', 'Spark Line', 'Bump Chart', 'Warming Stripes']
242
252
  if (disabledCharts.includes(visualizationType)) return false
243
253
  return true
244
254
  }
245
255
 
246
256
  const visSupportsDateCategoryAxisLine = () => {
247
- const disabledCharts = ['Forest Plot', 'Spark Line', 'Warming Stripes']
257
+ const disabledCharts = ['Forest Plot', 'HeatMap', 'Spark Line', 'Warming Stripes']
248
258
  if (disabledCharts.includes(visualizationType)) return false
249
259
  return true
250
260
  }
251
261
 
252
262
  const visSupportsDateCategoryAxisTicks = () => {
253
- const disabledCharts = ['Forest Plot', 'Spark Line', 'Warming Stripes']
263
+ const disabledCharts = ['Forest Plot', 'HeatMap', 'Spark Line', 'Warming Stripes']
254
264
  if (disabledCharts.includes(visualizationType)) return false
255
265
  return true
256
266
  }
@@ -262,13 +272,13 @@ export const useEditorPermissions = () => {
262
272
  }
263
273
 
264
274
  const visSupportsDateCategoryNumTicks = () => {
265
- const disabledCharts = ['Spark Line']
275
+ const disabledCharts = ['HeatMap', 'Spark Line']
266
276
  if (disabledCharts.includes(visualizationType)) return false
267
277
  return true
268
278
  }
269
279
 
270
280
  const visSupportsResponsiveTicks = () => {
271
- const disabledCharts = ['Spark Line']
281
+ const disabledCharts = ['HeatMap', 'Spark Line']
272
282
  if (disabledCharts.includes(visualizationType)) return false
273
283
  return true
274
284
  }
@@ -277,6 +287,7 @@ export const useEditorPermissions = () => {
277
287
  const disabledCharts = [
278
288
  'Forest Plot',
279
289
  'Horizon Chart',
290
+ 'HeatMap',
280
291
  'Pie',
281
292
  'Paired Bar',
282
293
  'Radar',
@@ -289,13 +300,13 @@ export const useEditorPermissions = () => {
289
300
  }
290
301
 
291
302
  const visSupportsValueAxisMax = () => {
292
- const disabledCharts = ['Forest Plot']
303
+ const disabledCharts = ['Forest Plot', 'HeatMap']
293
304
  if (disabledCharts.includes(visualizationType)) return false
294
305
  return true
295
306
  }
296
307
 
297
308
  const visSupportsValueAxisMin = () => {
298
- const disabledCharts = ['Forest Plot']
309
+ const disabledCharts = ['Forest Plot', 'HeatMap']
299
310
  if (disabledCharts.includes(visualizationType)) return false
300
311
  return true
301
312
  }
@@ -307,14 +318,14 @@ export const useEditorPermissions = () => {
307
318
  }
308
319
 
309
320
  const visSupportsValueAxisGridLines = () => {
310
- const disabledCharts = ['Forest Plot']
321
+ const disabledCharts = ['Forest Plot', 'HeatMap']
311
322
  if (disabledCharts.includes(visualizationType)) return false
312
323
  return true
313
324
  }
314
325
 
315
326
  // implement later
316
327
  const visSupportsValueAxisTicks = () => {
317
- const disabledCharts = ['Forest Plot']
328
+ const disabledCharts = ['Forest Plot', 'HeatMap']
318
329
  if (disabledCharts.includes(visualizationType)) return false
319
330
  return true
320
331
  }
@@ -328,7 +339,7 @@ export const useEditorPermissions = () => {
328
339
 
329
340
  // implement later
330
341
  const visSupportsValueAxisLabels = () => {
331
- const disabledCharts = ['Forest Plot', 'Bump Chart']
342
+ const disabledCharts = ['Forest Plot', 'HeatMap', 'Bump Chart']
332
343
  if (disabledCharts.includes(visualizationType)) return false
333
344
  return true
334
345
  }
@@ -341,7 +352,7 @@ export const useEditorPermissions = () => {
341
352
  }
342
353
 
343
354
  const visSupportsBarThickness = () => {
344
- const disabledCharts = ['Forest Plot', 'Warming Stripes']
355
+ const disabledCharts = ['Forest Plot', 'HeatMap', 'Warming Stripes']
345
356
  if (disabledCharts.includes(visualizationType)) return false
346
357
  return true
347
358
  }
@@ -353,7 +364,7 @@ export const useEditorPermissions = () => {
353
364
  }
354
365
  const visSupportsMobileChartHeight = () => {
355
366
  // TODO: this is a soft release. Support should eventually match visSupportsChartHeight
356
- const enabledCharts = ['Bar', 'Line', 'Combo', 'Area Chart', 'Radar']
367
+ const enabledCharts = ['Bar', 'Line', 'Combo', 'Area Chart', 'HeatMap', 'Radar']
357
368
  if (enabledCharts.includes(visualizationType)) return true
358
369
  return false
359
370
  }
@@ -365,13 +376,13 @@ export const useEditorPermissions = () => {
365
376
  }
366
377
 
367
378
  const visSupportsRankByValue = () => {
368
- const disabledCharts = ['Spark Line']
379
+ const disabledCharts = ['HeatMap', 'Spark Line']
369
380
  if (disabledCharts.includes(visualizationType)) return false
370
381
  return true
371
382
  }
372
383
 
373
384
  const visSupportsDateCategoryHeight = () => {
374
- const disabledCharts = ['Spark Line', 'Sankey', 'Bump Chart']
385
+ const disabledCharts = ['HeatMap', 'Spark Line', 'Sankey', 'Bump Chart']
375
386
  if (disabledCharts.includes(visualizationType)) return false
376
387
  if (config.orientation !== 'horizontal') return false
377
388
  if (config.orientation === 'horizontal' && visualizationType === 'Bar' && !config.isLollipopChart) return false
@@ -416,7 +427,7 @@ export const useEditorPermissions = () => {
416
427
  }
417
428
 
418
429
  const visSupportsYPadding = () => {
419
- return !config.yAxis.inlineLabel || !config.yAxis.inlineLabel?.includes(' ')
430
+ return getYAxisAutoPaddingMode(config) === 'none'
420
431
  }
421
432
 
422
433
  const visHasSingleSeriesTooltip = () => {
@@ -110,7 +110,7 @@ const prepareForecastData = (
110
110
  return segments.length > 0 ? segments : [sortedData]
111
111
  }
112
112
 
113
- const Forecasting = ({ xScale, yScale, height, width, handleTooltipMouseOver, handleTooltipMouseOff }) => {
113
+ const Forecasting = ({ xScale, yScale, height, width, yAxisWidth, handleTooltipMouseOver, handleTooltipMouseOff }) => {
114
114
  const { transformedData: data, rawData, config, seriesHighlight, parseDate } = useContext(ConfigContext)
115
115
  const { xAxis, yAxis, legend, runtime } = config
116
116
  const DEBUG = false
@@ -143,7 +143,7 @@ const Forecasting = ({ xScale, yScale, height, width, handleTooltipMouseOver, ha
143
143
  return (
144
144
  data && (
145
145
  <ErrorBoundary component='ForecastingChart'>
146
- <Group className='forecasting-items' key='forecasting-items-wrapper' left={Number(yAxis.size)}>
146
+ <Group className='forecasting-items' key='forecasting-items-wrapper' left={yAxisWidth}>
147
147
  {runtime.forecastingSeriesKeys?.map((group, index) => {
148
148
  if (!group || !group.stages) return false
149
149
  return group.stages.map((stage, stageIndex) => {