@cdc/chart 4.26.4 → 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 (93) hide show
  1. package/CONFIG.md +349 -29
  2. package/LICENSE +201 -0
  3. package/dist/cdcchart.js +34247 -32805
  4. package/examples/feature/heatmap/average-age-categorical.json +313 -0
  5. package/examples/feature/heatmap/calendar-heatmap.json +134 -0
  6. package/examples/feature/heatmap/cell-values-bottom-axis.json +181 -0
  7. package/examples/feature/heatmap/sparse-aggregation.json +158 -0
  8. package/examples/private/bump-chart.json +1116 -0
  9. package/examples/private/test-123.json +624 -0
  10. package/package.json +4 -3
  11. package/src/CdcChartComponent.tsx +135 -54
  12. package/src/_stories/Chart.AltText.stories.tsx +136 -0
  13. package/src/_stories/Chart.HeatMap.stories.tsx +187 -0
  14. package/src/_stories/Chart.Sankey.smoke.stories.tsx +62 -0
  15. package/src/_stories/Chart.SmallMultiples.smoke.stories.tsx +29 -0
  16. package/src/_stories/ChartAxisTitles.smoke.stories.tsx +13 -0
  17. package/src/_stories/ChartBar.Editor.stories.tsx +63 -0
  18. package/src/_stories/ChartBrush.Interaction.stories.tsx +112 -0
  19. package/src/_stories/ChartBrush.smoke.stories.tsx +13 -4
  20. package/src/_stories/_mock/alt_text_metadata.json +38 -0
  21. package/src/_stories/_mock/heatmap-average-age-categorical.json +315 -0
  22. package/src/_stories/_mock/heatmap.json +156 -0
  23. package/src/_stories/_mock/reported_pertussis_cases.json +1643 -0
  24. package/src/_stories/_mock/sankey_mock.json +78 -0
  25. package/src/_stories/_mock/small_multiples/small_multiples_lines.json +1 -1
  26. package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -1
  27. package/src/components/Annotations/components/AnnotationDropdown.tsx +5 -2
  28. package/src/components/Axis/LeftAxis.tsx +14 -11
  29. package/src/components/Axis/LeftAxisGridlines.tsx +14 -11
  30. package/src/components/Brush/BrushSelector.tsx +88 -187
  31. package/src/components/Brush/brushInteraction.ts +22 -0
  32. package/src/components/Brush/tests/brushInteraction.test.ts +38 -0
  33. package/src/components/EditorPanel/EditorPanel.tsx +310 -49
  34. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +13 -0
  35. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +105 -13
  36. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +1 -0
  37. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +5 -0
  38. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +2 -2
  39. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +1 -1
  40. package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +57 -0
  41. package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +56 -0
  42. package/src/components/EditorPanel/useEditorPermissions.test.tsx +70 -0
  43. package/src/components/EditorPanel/useEditorPermissions.ts +42 -22
  44. package/src/components/HeatMap/components/HeatMap.tsx +762 -0
  45. package/src/components/HeatMap/components/HeatMapGradientLegend.tsx +114 -0
  46. package/src/components/HeatMap/heatmap.constants.ts +13 -0
  47. package/src/components/HeatMap/heatmap.css +71 -0
  48. package/src/components/HeatMap/helpers/index.ts +261 -0
  49. package/src/components/HeatMap/index.tsx +5 -0
  50. package/src/components/HeatMap/tests/HeatMap.test.tsx +1040 -0
  51. package/src/components/HeatMap/tests/helpers.test.ts +167 -0
  52. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +19 -22
  53. package/src/components/LineChart/helpers.test.ts +345 -0
  54. package/src/components/LineChart/helpers.ts +15 -5
  55. package/src/components/LineChart/index.tsx +7 -1
  56. package/src/components/LinearChart/tests/LinearChart.test.tsx +107 -33
  57. package/src/components/LinearChart/tests/mockConfigContext.ts +1 -0
  58. package/src/components/LinearChart.tsx +58 -37
  59. package/src/components/PieChart/PieChart.tsx +4 -1
  60. package/src/components/RadarChart/RadarChart.tsx +3 -1
  61. package/src/components/Regions/components/Regions.tsx +195 -41
  62. package/src/components/Sankey/components/Sankey.tsx +7 -3
  63. package/src/components/Sankey/helpers/getSankeyTooltip.tsx +7 -6
  64. package/src/components/SmallMultiples/SmallMultipleTile.tsx +30 -3
  65. package/src/components/SmallMultiples/SmallMultiples.css +12 -0
  66. package/src/components/SmallMultiples/SmallMultiples.tsx +15 -9
  67. package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +152 -0
  68. package/src/components/Sparkline/components/SparkLine.tsx +4 -9
  69. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +1 -1
  70. package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +2 -2
  71. package/src/data/initial-state.js +11 -0
  72. package/src/helpers/axisLabelFontSize.ts +8 -0
  73. package/src/helpers/bumpChartTooltip.ts +54 -0
  74. package/src/helpers/calculateLeftYAxisWidth.ts +4 -0
  75. package/src/helpers/ensureSpecialChartAxisTypes.ts +11 -0
  76. package/src/helpers/getMinMax.ts +2 -3
  77. package/src/helpers/getYAxisAutoPadding.ts +8 -6
  78. package/src/helpers/getYAxisAutoPaddingMode.ts +17 -0
  79. package/src/helpers/handleChartAriaLabels.ts +23 -10
  80. package/src/helpers/seriesHighlight.ts +26 -0
  81. package/src/helpers/tests/axisLabelFontSize.test.ts +15 -0
  82. package/src/helpers/tests/bumpChartTooltip.test.ts +75 -0
  83. package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +30 -0
  84. package/src/helpers/tests/getYAxisAutoPadding.test.ts +37 -0
  85. package/src/helpers/tests/getYAxisAutoPaddingMode.test.ts +90 -0
  86. package/src/helpers/tests/handleChartAriaLabels.test.ts +71 -0
  87. package/src/helpers/tests/seriesHighlight.test.ts +61 -0
  88. package/src/hooks/tests/useScales.test.tsx +105 -0
  89. package/src/hooks/useScales.ts +19 -9
  90. package/src/index.jsx +5 -2
  91. package/src/scss/main.scss +32 -49
  92. package/src/types/ChartConfig.ts +18 -0
  93. package/src/types/ChartContext.ts +2 -0
@@ -13,12 +13,15 @@ import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
13
13
  import { TextField, Select, CheckBox } from '@cdc/core/components/EditorPanel/Inputs'
14
14
  import Tooltip from '@cdc/core/components/ui/Tooltip'
15
15
  import Icon from '@cdc/core/components/ui/Icon'
16
+ import { resolveAltTextDescription } from '@cdc/core/helpers/resolveAltTextDescription'
16
17
 
17
18
  // contexts
18
19
  import { useEditorPermissions } from '../../useEditorPermissions.js'
19
20
  import { useEditorPanelContext } from '../../EditorPanelContext.js'
20
21
  import ConfigContext from '../../../../ConfigContext.js'
21
22
  import { PanelProps } from '../PanelProps'
23
+ import { getVisualizationTypeConfigUpdate } from '../../helpers/getVisualizationTypeConfigUpdate'
24
+ import { type VisualizationType } from '../../../../types/ChartConfig'
22
25
 
23
26
  const PanelGeneral: FC<PanelProps> = props => {
24
27
  const { config, updateConfig } = useContext(ConfigContext)
@@ -79,22 +82,12 @@ const PanelGeneral: FC<PanelProps> = props => {
79
82
  updateField={updateField}
80
83
  options={enabledChartTypes}
81
84
  onChange={event => {
82
- const newVisType = event.target.value
85
+ const newVisType = event.target.value as VisualizationType
83
86
 
84
87
  updateField(null, null, 'visualizationType', newVisType)
85
88
 
86
- if (newVisType === 'Forecasting' && config.xAxis.type === 'categorical') {
87
- updateConfig({
88
- ...config,
89
- visualizationType: newVisType,
90
- xAxis: {
91
- ...config.xAxis,
92
- type: 'date',
93
- dateParseFormat: config.xAxis.dateParseFormat || '%Y-%m-%d',
94
- dateDisplayFormat: config.xAxis.dateDisplayFormat || '%Y-%m-%d'
95
- }
96
- })
97
- }
89
+ const updatedConfig = getVisualizationTypeConfigUpdate(config, newVisType)
90
+ if (updatedConfig) updateConfig(updatedConfig)
98
91
  }}
99
92
  />
100
93
  )}
@@ -617,6 +610,105 @@ const PanelGeneral: FC<PanelProps> = props => {
617
610
  </Tooltip>
618
611
  }
619
612
  />
613
+
614
+ {/* Accessible Alt Text Description */}
615
+ {(() => {
616
+ const metadataKeys = Object.keys(config.dataMetadata || {})
617
+ const hasMetadata = metadataKeys.length > 0
618
+ const descType = config.altText?.type || ''
619
+ const resolvedDescription = resolveAltTextDescription(config.altText, config.dataMetadata)
620
+ return (
621
+ <>
622
+ <Select
623
+ value={descType}
624
+ fieldName='altTextType'
625
+ label='Alt Text Description'
626
+ options={[
627
+ { value: '', label: 'None' },
628
+ { value: 'static', label: 'Static (manual text)' },
629
+ { value: 'metadata', label: 'Data File Metadata' }
630
+ ]}
631
+ updateField={(_section, _subsection, _fieldName, value) => {
632
+ if (value === '') {
633
+ updateField(null, null, 'altText', undefined)
634
+ } else {
635
+ updateField(null, null, 'altText', { type: value as 'static' | 'metadata' })
636
+ }
637
+ }}
638
+ tooltip={
639
+ <Tooltip style={{ textTransform: 'none' }}>
640
+ <Tooltip.Target>
641
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
642
+ </Tooltip.Target>
643
+ <Tooltip.Content>
644
+ <p>
645
+ Add a longer description for screen readers. The chart title is always auto-generated. Use
646
+ "Static" for manually written text, or "Data File Metadata" to pull it from a key in your data
647
+ file.
648
+ </p>
649
+ </Tooltip.Content>
650
+ </Tooltip>
651
+ }
652
+ />
653
+ {descType === 'static' && (
654
+ <TextField
655
+ value={config.altText?.value || ''}
656
+ fieldName='altTextValue'
657
+ type='textarea'
658
+ label='Description Text'
659
+ placeholder='Longer interpretive description of chart insights...'
660
+ updateField={(_section, _subsection, _fieldName, value) => {
661
+ updateField(null, null, 'altText', { ...config.altText, value })
662
+ }}
663
+ />
664
+ )}
665
+ {descType === 'metadata' && (
666
+ <>
667
+ {hasMetadata ? (
668
+ <Select
669
+ value={config.altText?.metadataKey || ''}
670
+ fieldName='altTextMetadataKey'
671
+ label='Description Metadata Field'
672
+ options={[
673
+ { value: '', label: 'Select Metadata Field...' },
674
+ ...metadataKeys.map(key => ({
675
+ value: key,
676
+ label: `${key}: ${config.dataMetadata[key]}`
677
+ }))
678
+ ]}
679
+ updateField={(_section, _subsection, _fieldName, value) => {
680
+ updateField(null, null, 'altText', { ...config.altText, metadataKey: value })
681
+ }}
682
+ />
683
+ ) : (
684
+ <span className='subtext'>
685
+ No metadata fields are available. Your data file must be a JSON object with a <code>data</code>{' '}
686
+ array and sibling key-value pairs, for example:{' '}
687
+ <code>{`{ "altDescription": "...", "data": [...] }`}</code>
688
+ </span>
689
+ )}
690
+ </>
691
+ )}
692
+ {resolvedDescription && (
693
+ <div
694
+ style={{
695
+ marginTop: '1em',
696
+ padding: '0.75em',
697
+ background: '#f5f5f5',
698
+ borderRadius: '4px',
699
+ fontSize: '0.8em',
700
+ textTransform: 'none'
701
+ }}
702
+ >
703
+ <strong style={{ display: 'block', marginBottom: '0.25em' }}>Preview:</strong>
704
+ <p data-testid='alt-text-desc-preview' style={{ margin: 0, fontStyle: 'italic' }}>
705
+ {resolvedDescription}
706
+ </p>
707
+ </div>
708
+ )}
709
+ </>
710
+ )
711
+ })()}
620
712
  </AccordionItemPanel>
621
713
  </AccordionItem>
622
714
  )
@@ -22,6 +22,7 @@ import { sanitizeToSvgId } from '@cdc/core/helpers/cove/string'
22
22
 
23
23
  const PanelPatternSettings: FC<PanelProps> = props => {
24
24
  const { config, updateConfig, transformedData } = useContext<ChartContext>(ConfigContext)
25
+ if (config.visualizationType === 'HeatMap') return null
25
26
 
26
27
  type LegendPattern = {
27
28
  label?: string
@@ -14,10 +14,13 @@ import { TextField, Select } from '@cdc/core/components/EditorPanel/Inputs'
14
14
  import Tooltip from '@cdc/core/components/ui/Tooltip'
15
15
  import Icon from '@cdc/core/components/ui/Icon'
16
16
  import Button from '@cdc/core/components/elements/Button'
17
+ import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
17
18
  import { type ChartContext } from '../../../../types/ChartContext.js'
18
19
  import { type PanelProps } from '../PanelProps.js'
19
20
  import ConfigContext from '../../../../ConfigContext.js'
20
21
 
22
+ const DEFAULT_REGION_BACKGROUND = getComputedStyle(document.documentElement).getPropertyValue('--cool-gray-50').trim()
23
+
21
24
  const RegionSettings = memo(({ config, updateConfig }: { config: ChartConfig; updateConfig: Function }) => {
22
25
  let regionUpdate = (fieldName, value, i) => {
23
26
  let regions = []
@@ -116,12 +119,14 @@ const RegionSettings = memo(({ config, updateConfig }: { config: ChartConfig; up
116
119
  value={color}
117
120
  label='Text Color'
118
121
  fieldName='color'
122
+ placeholder={APP_FONT_COLOR}
119
123
  updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)}
120
124
  />
121
125
  <TextField
122
126
  value={background}
123
127
  label='Background'
124
128
  fieldName='background'
129
+ placeholder={DEFAULT_REGION_BACKGROUND}
125
130
  updateField={(section, subsection, fieldName, value) => regionUpdate(fieldName, value, i)}
126
131
  />
127
132
  </div>
@@ -660,7 +660,7 @@ const SeriesButtonRemove = props => {
660
660
 
661
661
  return (
662
662
  config.series &&
663
- config.series.length > 1 && (
663
+ (config.series.length > 1 || config.visualizationType === 'HeatMap') && (
664
664
  <Button variant='danger' size='sm' className='grouped-list__remove' onClick={e => handleRemoveSeries(e, series)}>
665
665
  Remove
666
666
  </Button>
@@ -710,7 +710,7 @@ const SeriesItem = props => {
710
710
  }
711
711
  >
712
712
  <Icon display='move' size={15} style={{ cursor: 'default' }} />
713
- {series.dataKey}
713
+ {config.visualizationType === 'HeatMap' ? series.name || series.dataKey : series.dataKey}
714
714
  </AccordionItemButton>
715
715
  </AccordionItemHeading>
716
716
  {chartsWithOptions.includes(config.visualizationType) && (
@@ -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,31 +272,41 @@ 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
  }
275
285
 
276
286
  const visSupportsRegions = () => {
277
- const disabledCharts = ['Forest Plot', 'Pie', 'Paired Bar', 'Radar', 'Spark Line', 'Sankey', 'Warming Stripes']
287
+ const disabledCharts = [
288
+ 'Forest Plot',
289
+ 'Horizon Chart',
290
+ 'HeatMap',
291
+ 'Pie',
292
+ 'Paired Bar',
293
+ 'Radar',
294
+ 'Spark Line',
295
+ 'Sankey',
296
+ 'Warming Stripes'
297
+ ]
278
298
  if (disabledCharts.includes(visualizationType)) return false
279
299
  return true
280
300
  }
281
301
 
282
302
  const visSupportsValueAxisMax = () => {
283
- const disabledCharts = ['Forest Plot']
303
+ const disabledCharts = ['Forest Plot', 'HeatMap']
284
304
  if (disabledCharts.includes(visualizationType)) return false
285
305
  return true
286
306
  }
287
307
 
288
308
  const visSupportsValueAxisMin = () => {
289
- const disabledCharts = ['Forest Plot']
309
+ const disabledCharts = ['Forest Plot', 'HeatMap']
290
310
  if (disabledCharts.includes(visualizationType)) return false
291
311
  return true
292
312
  }
@@ -298,14 +318,14 @@ export const useEditorPermissions = () => {
298
318
  }
299
319
 
300
320
  const visSupportsValueAxisGridLines = () => {
301
- const disabledCharts = ['Forest Plot']
321
+ const disabledCharts = ['Forest Plot', 'HeatMap']
302
322
  if (disabledCharts.includes(visualizationType)) return false
303
323
  return true
304
324
  }
305
325
 
306
326
  // implement later
307
327
  const visSupportsValueAxisTicks = () => {
308
- const disabledCharts = ['Forest Plot']
328
+ const disabledCharts = ['Forest Plot', 'HeatMap']
309
329
  if (disabledCharts.includes(visualizationType)) return false
310
330
  return true
311
331
  }
@@ -319,7 +339,7 @@ export const useEditorPermissions = () => {
319
339
 
320
340
  // implement later
321
341
  const visSupportsValueAxisLabels = () => {
322
- const disabledCharts = ['Forest Plot', 'Bump Chart']
342
+ const disabledCharts = ['Forest Plot', 'HeatMap', 'Bump Chart']
323
343
  if (disabledCharts.includes(visualizationType)) return false
324
344
  return true
325
345
  }
@@ -332,7 +352,7 @@ export const useEditorPermissions = () => {
332
352
  }
333
353
 
334
354
  const visSupportsBarThickness = () => {
335
- const disabledCharts = ['Forest Plot', 'Warming Stripes']
355
+ const disabledCharts = ['Forest Plot', 'HeatMap', 'Warming Stripes']
336
356
  if (disabledCharts.includes(visualizationType)) return false
337
357
  return true
338
358
  }
@@ -344,7 +364,7 @@ export const useEditorPermissions = () => {
344
364
  }
345
365
  const visSupportsMobileChartHeight = () => {
346
366
  // TODO: this is a soft release. Support should eventually match visSupportsChartHeight
347
- const enabledCharts = ['Bar', 'Line', 'Combo', 'Area Chart', 'Radar']
367
+ const enabledCharts = ['Bar', 'Line', 'Combo', 'Area Chart', 'HeatMap', 'Radar']
348
368
  if (enabledCharts.includes(visualizationType)) return true
349
369
  return false
350
370
  }
@@ -356,13 +376,13 @@ export const useEditorPermissions = () => {
356
376
  }
357
377
 
358
378
  const visSupportsRankByValue = () => {
359
- const disabledCharts = ['Spark Line']
379
+ const disabledCharts = ['HeatMap', 'Spark Line']
360
380
  if (disabledCharts.includes(visualizationType)) return false
361
381
  return true
362
382
  }
363
383
 
364
384
  const visSupportsDateCategoryHeight = () => {
365
- const disabledCharts = ['Spark Line', 'Sankey', 'Bump Chart']
385
+ const disabledCharts = ['HeatMap', 'Spark Line', 'Sankey', 'Bump Chart']
366
386
  if (disabledCharts.includes(visualizationType)) return false
367
387
  if (config.orientation !== 'horizontal') return false
368
388
  if (config.orientation === 'horizontal' && visualizationType === 'Bar' && !config.isLollipopChart) return false
@@ -407,7 +427,7 @@ export const useEditorPermissions = () => {
407
427
  }
408
428
 
409
429
  const visSupportsYPadding = () => {
410
- return !config.yAxis.inlineLabel || !config.yAxis.inlineLabel?.includes(' ')
430
+ return getYAxisAutoPaddingMode(config) === 'none'
411
431
  }
412
432
 
413
433
  const visHasSingleSeriesTooltip = () => {