@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
@@ -0,0 +1,167 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import {
3
+ buildHeatMapData,
4
+ getHeatMapBucketPalette,
5
+ getHeatMapColorBucketCount,
6
+ getHeatMapColorScale,
7
+ getHeatMapDataGroupRanges
8
+ } from '../helpers'
9
+ import { type ChartConfig } from '../../../types/ChartConfig'
10
+
11
+ describe('buildHeatMapData', () => {
12
+ it('aggregates duplicate x/series cells using numeric sum', () => {
13
+ const { columns, minValue, maxValue } = buildHeatMapData({
14
+ data: [
15
+ { month: 'Jan', Austin: 2, Boston: 4 },
16
+ { month: 'Jan', Austin: 3, Boston: 0 }
17
+ ],
18
+ xDataKey: 'month',
19
+ series: [
20
+ { dataKey: 'Austin', name: 'Austin' },
21
+ { dataKey: 'Boston', name: 'Boston' }
22
+ ],
23
+ xAxisType: 'categorical'
24
+ })
25
+
26
+ expect(columns).toHaveLength(1)
27
+ expect(columns[0].bins[0].value).toBe(5)
28
+ expect(columns[0].bins[0].sourceRows).toHaveLength(2)
29
+ expect(columns[0].bins[1].value).toBe(4)
30
+ expect(minValue).toBe(4)
31
+ expect(maxValue).toBe(5)
32
+ })
33
+
34
+ it('orders date-based x values in ascending order', () => {
35
+ const { columns } = buildHeatMapData({
36
+ data: [
37
+ { date: '2024-03-01', Austin: 1 },
38
+ { date: '2024-01-01', Austin: 2 },
39
+ { date: '2024-02-01', Austin: 3 }
40
+ ],
41
+ xDataKey: 'date',
42
+ series: [{ dataKey: 'Austin', name: 'Austin' }],
43
+ xAxisType: 'date'
44
+ })
45
+
46
+ expect(columns.map(column => column.key)).toEqual(['2024-01-01', '2024-02-01', '2024-03-01'])
47
+ })
48
+
49
+ it('creates empty cells for missing x/series combinations', () => {
50
+ const { columns, rowLabels } = buildHeatMapData({
51
+ data: [
52
+ { month: 'Jan', Austin: 5 },
53
+ { month: 'Feb', Boston: 4 }
54
+ ],
55
+ xDataKey: 'month',
56
+ series: [
57
+ { dataKey: 'Austin', name: 'Austin' },
58
+ { dataKey: 'Boston', name: 'Boston' }
59
+ ],
60
+ xAxisType: 'categorical'
61
+ })
62
+
63
+ expect(rowLabels).toEqual(['Austin', 'Boston'])
64
+ expect(columns).toHaveLength(2)
65
+ expect(columns[0].bins[1].value).toBeNull()
66
+ expect(columns[1].bins[0].value).toBeNull()
67
+ })
68
+
69
+ it('builds series-driven rows when data series are provided', () => {
70
+ const { columns, rowLabels, minValue, maxValue } = buildHeatMapData({
71
+ data: [
72
+ { month: 'Jan', Austin: 12, Boston: 30 },
73
+ { month: 'Feb', Austin: 18, Boston: 40 }
74
+ ],
75
+ xDataKey: 'month',
76
+ series: [
77
+ { dataKey: 'Austin', name: 'Austin' },
78
+ { dataKey: 'Boston', name: 'Boston' }
79
+ ],
80
+ seriesLabels: { Austin: 'Austin', Boston: 'Boston' },
81
+ xAxisType: 'categorical'
82
+ })
83
+
84
+ expect(rowLabels).toEqual(['Austin', 'Boston'])
85
+ expect(columns).toHaveLength(2)
86
+ expect(columns[0].bins[0].value).toBe(12)
87
+ expect(columns[0].bins[1].value).toBe(30)
88
+ expect(columns[1].bins[0].value).toBe(18)
89
+ expect(columns[1].bins[1].value).toBe(40)
90
+ expect(minValue).toBe(12)
91
+ expect(maxValue).toBe(40)
92
+ })
93
+
94
+ it('keeps non-calendar categories in source order for average age by city', () => {
95
+ const { columns, rowLabels, minValue, maxValue } = buildHeatMapData({
96
+ data: [
97
+ { communityType: 'Urban Core', Atlanta: 34, Chicago: 36, Phoenix: 38 },
98
+ { communityType: 'Suburban', Atlanta: 41, Chicago: 39, Phoenix: 42 },
99
+ { communityType: 'Rural', Atlanta: 46, Chicago: 44, Phoenix: 49 }
100
+ ],
101
+ xDataKey: 'communityType',
102
+ series: [
103
+ { dataKey: 'Atlanta', name: 'Atlanta' },
104
+ { dataKey: 'Chicago', name: 'Chicago' },
105
+ { dataKey: 'Phoenix', name: 'Phoenix' }
106
+ ],
107
+ xAxisType: 'categorical'
108
+ })
109
+
110
+ expect(columns.map(column => column.key)).toEqual(['Urban Core', 'Suburban', 'Rural'])
111
+ expect(rowLabels).toEqual(['Atlanta', 'Chicago', 'Phoenix'])
112
+ expect(columns[0].bins.map(bin => bin.value)).toEqual([34, 36, 38])
113
+ expect(columns[2].bins[2].value).toBe(49)
114
+ expect(minValue).toBe(34)
115
+ expect(maxValue).toBe(49)
116
+ })
117
+ })
118
+
119
+ describe('HeatMap data grouping', () => {
120
+ const buildPaletteConfig = (colorBucketCount?: unknown) =>
121
+ ({
122
+ general: {
123
+ palette: {
124
+ customColors: ['#000000', '#ffffff']
125
+ }
126
+ },
127
+ heatmap: {
128
+ colorBucketCount
129
+ }
130
+ } as ChartConfig)
131
+
132
+ it('clamps HeatMap data grouping to the supported range', () => {
133
+ expect(getHeatMapColorBucketCount(buildPaletteConfig(undefined))).toBe(9)
134
+ expect(getHeatMapColorBucketCount(buildPaletteConfig(0))).toBe(1)
135
+ expect(getHeatMapColorBucketCount(buildPaletteConfig(-4))).toBe(1)
136
+ expect(getHeatMapColorBucketCount(buildPaletteConfig(12))).toBe(9)
137
+ })
138
+
139
+ it('supports one bucket as the minimum heatmap color count', () => {
140
+ const colorScale = getHeatMapColorScale(buildPaletteConfig(1), 0, 100)
141
+
142
+ expect(getHeatMapBucketPalette(buildPaletteConfig(1))).toHaveLength(1)
143
+ expect(colorScale(1)).toBe(colorScale(99))
144
+ })
145
+
146
+ it('uses the configured number of data groups for heatmap cell colors', () => {
147
+ const colorScale = getHeatMapColorScale(buildPaletteConfig(5), 0, 100)
148
+ const bucketColors = [0, 20, 40, 60, 80].map(value => colorScale(value))
149
+
150
+ expect(getHeatMapColorBucketCount(buildPaletteConfig(5))).toBe(5)
151
+ expect(getHeatMapBucketPalette(buildPaletteConfig(5))).toHaveLength(5)
152
+ expect(new Set(bucketColors)).toHaveLength(5)
153
+ expect(colorScale(1)).toBe(colorScale(19))
154
+ })
155
+
156
+ it('builds data group legend ranges from the heatmap grouping domain', () => {
157
+ const ranges = getHeatMapDataGroupRanges(buildPaletteConfig(5), 0, 1, value => value.toFixed(1))
158
+
159
+ expect(ranges.map(range => range.label)).toEqual([
160
+ '0.0\u20130.2',
161
+ '0.2\u20130.4',
162
+ '0.4\u20130.6',
163
+ '0.6\u20130.8',
164
+ '0.8\u20131.0'
165
+ ])
166
+ })
167
+ })
@@ -23,13 +23,21 @@ type HorizonChartProps = {
23
23
  yScale: any
24
24
  xMax: number
25
25
  yMax: number
26
+ yAxisWidth: number
26
27
  handleTooltipMouseOver: (e: any, additionalData?: any) => void
27
28
  handleTooltipMouseOff: () => void
28
29
  tooltipData?: any
29
30
  showTooltip?: boolean
30
31
  }
31
32
 
32
- const HorizonChart = ({ xScale, xMax, yMax, handleTooltipMouseOver, handleTooltipMouseOff }: HorizonChartProps) => {
33
+ const HorizonChart = ({
34
+ xScale,
35
+ xMax,
36
+ yMax,
37
+ yAxisWidth,
38
+ handleTooltipMouseOver,
39
+ handleTooltipMouseOff
40
+ }: HorizonChartProps) => {
33
41
  // Get data and config from context
34
42
  const { transformedData: data, config, colorScale, rawData, parseDate } = useContext(ConfigContext)
35
43
 
@@ -94,7 +102,7 @@ const HorizonChart = ({ xScale, xMax, yMax, handleTooltipMouseOver, handleToolti
94
102
 
95
103
  return (
96
104
  <ErrorBoundary component='HorizonChart'>
97
- <Group className='horizon-chart' key='horizon-wrapper' left={Number(config.yAxis.size)} height={Number(yMax)}>
105
+ <Group className='horizon-chart' key='horizon-wrapper' left={yAxisWidth} height={Number(yMax)}>
98
106
  {seriesKeys.map((seriesKey, index) => {
99
107
  const rowY = getRowY(index)
100
108
  return (
@@ -9,9 +9,10 @@ type HoverLineProps = {
9
9
  yMax?: number
10
10
  point: { x: number; y: number }
11
11
  orientation: 'horizontal' | 'vertical'
12
+ yAxisWidth: number
12
13
  }
13
14
 
14
- const HoverLine: React.FC<HoverLineProps> = ({ tooltipData, xMax, yMax, point, orientation }) => {
15
+ const HoverLine: React.FC<HoverLineProps> = ({ tooltipData, xMax, yMax, point, orientation, yAxisWidth }) => {
15
16
  const { config } = useContext(ConfigContext)
16
17
  const { verticalHoverLine, horizontalHoverLine } = config.visual
17
18
  const { visualizationType } = config
@@ -23,8 +24,9 @@ const HoverLine: React.FC<HoverLineProps> = ({ tooltipData, xMax, yMax, point, o
23
24
  const showHorizontalHoverLine = horizontalHoverLine || (horizontalHoverLine && isScatterPlot)
24
25
 
25
26
  const getX = () => {
26
- if (point.x > xMax + Number(config.yAxis.size)) return xMax
27
- if (point.x < config.yAxis.size) return config.yAxis.size
27
+ if (point.x < yAxisWidth) return yAxisWidth
28
+ if (xMax === undefined) return point.x
29
+ if (point.x > xMax + yAxisWidth) return xMax + yAxisWidth
28
30
  return point.x
29
31
  }
30
32
 
@@ -52,11 +54,7 @@ const HoverLine: React.FC<HoverLineProps> = ({ tooltipData, xMax, yMax, point, o
52
54
  if (isHorizontal) {
53
55
  return (
54
56
  showHorizontalHoverLine && (
55
- <Group
56
- key={`tooltipLine-horizontal${point.y}${point.x}`}
57
- className='horizontal-tooltip-line'
58
- left={config.yAxis.size ? config.yAxis.size : 0}
59
- >
57
+ <Group key={`tooltipLine-horizontal${point.y}${point.x}`} className='horizontal-tooltip-line' left={yAxisWidth}>
60
58
  <Line
61
59
  from={{ x: 0, y: getY() }}
62
60
  to={{ x: xMax, y: getY() }}
@@ -117,8 +117,8 @@ const Legend: React.FC<LegendProps> = forwardRef(
117
117
  >
118
118
  {(legend.label || legend.description) && (
119
119
  <div className={legend.description ? 'mb-3' : 'mb-2'}>
120
- {legend.label && <h3 className='fw-bold'>{parse(legend.label)}</h3>}
121
- {legend.description && <p className='mt-2'>{parse(legend.description)}</p>}
120
+ {legend.label && <h3 className='fw-bold cove-prose'>{parse(legend.label)}</h3>}
121
+ {legend.description && <p className='mt-2 cove-prose'>{parse(legend.description)}</p>}
122
122
  </div>
123
123
  )}
124
124
  <LegendGradient
@@ -242,7 +242,7 @@ const Legend: React.FC<LegendProps> = forwardRef(
242
242
  </>
243
243
  )}
244
244
  </>
245
- <LegendLabel align='left' className='m-0'>
245
+ <LegendLabel align='left' className='m-0 cove-prose'>
246
246
  {parse(label.text)}
247
247
  </LegendLabel>
248
248
  </LegendItem>
@@ -361,7 +361,7 @@ const Legend: React.FC<LegendProps> = forwardRef(
361
361
  />
362
362
  </svg>
363
363
  </span>
364
- <LegendLabel align='left' className='m-0'>
364
+ <LegendLabel align='left' className='m-0 cove-prose'>
365
365
  {parse(String((pattern as any)?.label || key))}
366
366
  </LegendLabel>
367
367
  </LegendItem>
@@ -17,6 +17,11 @@
17
17
  }
18
18
  }
19
19
 
20
+ .legend-group-grid {
21
+ display: flex;
22
+ flex-wrap: wrap;
23
+ }
24
+
20
25
  .legend-group.top,
21
26
  .legend-group.bottom {
22
27
  grid-gap: 10px;
@@ -32,7 +32,7 @@ const LegendGroup = ({ formatLabels }) => {
32
32
 
33
33
  const groups: string[] = getSubGroups(data, config.legend.groupBy)
34
34
 
35
- const classNames = ['legend-group', 'container', config.legend.position, currentViewport, 'row']
35
+ const classNames = ['legend-group', 'legend-group-grid', 'container', config.legend.position, currentViewport]
36
36
 
37
37
  const gridCol =
38
38
  currentViewport === 'xs'
@@ -47,6 +47,8 @@ const LegendGroup = ({ formatLabels }) => {
47
47
 
48
48
  let classNameItem = ['legend-group group-item', isSigleCol]
49
49
 
50
+ if (groups.length === 0) return null
51
+
50
52
  return (
51
53
  <div className={classNames.join(' ')}>
52
54
  {groups.map(group => {
@@ -40,8 +40,6 @@ export const getMarginBottom = (isLegendBottom, config) => {
40
40
 
41
41
  if (isLegendTop) marginBottom = 27
42
42
 
43
- if (isLegendTop && config.yAxis?.inlineLabel) marginBottom += 9
44
-
45
43
  if (isLegendBottom) marginBottom += 9
46
44
 
47
45
  if (hasSuppression) marginBottom += 40
@@ -8,6 +8,7 @@ export type LineChartProps = {
8
8
  getYAxisData: Function
9
9
  xMax: number
10
10
  yMax: number
11
+ yAxisWidth: number
11
12
  handleTooltipMouseOver: Function
12
13
  handleTooltipMouseOff: Function
13
14
  showTooltip: boolean
@@ -1,30 +1,31 @@
1
- import { useState } from 'react'
1
+ import { useContext } from 'react'
2
2
  import { Group } from '@visx/group'
3
- import { type Column } from '@cdc/core/types/Column'
4
3
  import React from 'react'
5
4
  import { type ChartConfig } from '../../../types/ChartConfig'
6
5
  import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
6
+ import ConfigContext from '../../../ConfigContext'
7
+ import { isDateScale } from '@cdc/core/helpers/cove/date'
8
+ import { buildBumpChartTooltipHtml } from '../../../helpers/bumpChartTooltip'
7
9
 
8
10
  type LineChartBumpCircleProp = {
9
11
  config: ChartConfig
10
12
  xScale: any
11
13
  yScale: any
12
14
  parseDate: any
15
+ yAxisWidth: number
13
16
  }
14
17
 
15
18
  const LineChartBumpCircle = (props: LineChartBumpCircleProp) => {
16
- const { config, xScale, yScale, parseDate } = props
19
+ const { config, xScale, yScale, parseDate, yAxisWidth } = props
20
+ const { colorScale, formatDate, formatTooltipsDate } = useContext(ConfigContext)
17
21
 
18
22
  // get xScale and yScale...
19
23
  if (!config?.runtime?.series) return
20
24
 
21
25
  const handleX = xValue => {
22
- if (config.xAxis.type === 'date') {
26
+ if (isDateScale(config.xAxis)) {
23
27
  return parseDate(xValue).getTime()
24
28
  }
25
- if (config.xAxis.type === 'date-time') {
26
- return new Date(xValue)
27
- }
28
29
  if (config.xAxis.type === 'categorical') {
29
30
  return xValue
30
31
  }
@@ -34,20 +35,7 @@ const LineChartBumpCircle = (props: LineChartBumpCircleProp) => {
34
35
  return xScale.bandwidth ? xScale.bandwidth() / 2 + Number(xValue) : Number(xValue)
35
36
  }
36
37
 
37
- const getListItems = dataRow => {
38
- return Object.values(config.columns)
39
- ?.filter(column => column.tooltips)
40
- .map(column => {
41
- const label = column.label || column.name
42
- return `
43
- <li className='tooltip-body'>
44
- <strong>${label}</strong>: ${dataRow[column.name]}
45
- </li>`
46
- })
47
- .join(' ')
48
- }
49
-
50
- const getTooltip = dataRow => `<ul> ${getListItems(dataRow)} </ul>`
38
+ const tooltipId = `cdc-open-viz-tooltip-${config.runtime.uniqueId}-bump`
51
39
 
52
40
  const circles = config.runtime?.series.map(series => {
53
41
  return config.data.map((d, dataIndex) => {
@@ -55,13 +43,23 @@ const LineChartBumpCircle = (props: LineChartBumpCircleProp) => {
55
43
  let axis_dataKey = d[config.xAxis.dataKey]
56
44
  return (
57
45
  <React.Fragment key={`bump-circle-${series_dataKey}-${dataIndex}`}>
58
- <Group left={Number(config.runtime.yAxis.size)}>
46
+ <Group left={yAxisWidth}>
59
47
  {series_dataKey && (
60
48
  <>
61
49
  <circle
62
50
  key={`bump-circle-${series_dataKey}-${dataIndex}`}
63
- data-tooltip-html={getTooltip(d)}
64
- data-tooltip-id={`bump-chart`}
51
+ data-tooltip-html={buildBumpChartTooltipHtml({
52
+ config,
53
+ colorScale,
54
+ dataRow: d,
55
+ series,
56
+ helpers: {
57
+ formatDate,
58
+ formatTooltipsDate,
59
+ parseDate
60
+ }
61
+ })}
62
+ data-tooltip-id={tooltipId}
65
63
  r={10}
66
64
  cx={Number(checkBandScale(xScale(handleX(axis_dataKey))))}
67
65
  cy={Number(yScale(series_dataKey))}