@cdc/chart 4.26.6 → 4.26.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/CONFIG.md +504 -368
  2. package/dist/cdcchart-CYc-Kayd.es.js +22488 -0
  3. package/dist/cdcchart-DCKn8NmI.es.js +20695 -0
  4. package/dist/cdcchart-Dyui9nk_.es.js +45828 -0
  5. package/dist/cdcchart.js +4 -96498
  6. package/examples/__data__/vega-remote-data.json +6 -0
  7. package/examples/dendrogram.json +18 -0
  8. package/examples/feature/bar/bar-chart-race-annual-change.json +68 -0
  9. package/examples/feature/heatmap/average-age-categorical.json +2 -2
  10. package/examples/feature/heatmap/calendar-heatmap.json +2 -2
  11. package/examples/feature/heatmap/cell-values-bottom-axis.json +2 -2
  12. package/examples/feature/heatmap/sparse-aggregation.json +2 -2
  13. package/examples/feature/line/line-chart-race.json +50 -0
  14. package/examples/feature/sankey/sankey-example-data.json +40 -1441
  15. package/examples/feature/sankey/sankey_chart_data.csv +9 -20
  16. package/examples/network.json +31 -0
  17. package/examples/private/box-plot.json +573 -0
  18. package/examples/private/custom-color-issue.json +625 -0
  19. package/examples/private/custom-colors.json +522 -0
  20. package/examples/private/cutoff.json +10750 -0
  21. package/examples/private/flueview.json +22051 -0
  22. package/examples/private/overlay-legend.json +485 -0
  23. package/examples/private/pie-test.json +469 -0
  24. package/examples/vega-remote.json +22 -0
  25. package/package.json +11 -13
  26. package/src/CdcChart.tsx +2 -2
  27. package/src/CdcChartComponent.tsx +444 -217
  28. package/src/_stories/Chart.BarRace.stories.tsx +443 -0
  29. package/src/_stories/Chart.BoxPlotNumericSort.stories.tsx +189 -0
  30. package/src/_stories/Chart.BoxPlotStatsParity.stories.tsx +207 -0
  31. package/src/_stories/Chart.ColorDistribution.stories.tsx +299 -0
  32. package/src/_stories/Chart.DataCutoff.stories.tsx +147 -0
  33. package/src/_stories/Chart.DataTableDownloadFilename.stories.tsx +60 -0
  34. package/src/_stories/Chart.Dendrogram.stories.tsx +373 -0
  35. package/src/_stories/Chart.Forecast.CILegend.stories.tsx +236 -0
  36. package/src/_stories/Chart.HeatMap.stories.tsx +170 -4
  37. package/src/_stories/Chart.Legend.AxisAlign.stories.tsx +119 -0
  38. package/src/_stories/Chart.LineRace.stories.tsx +149 -0
  39. package/src/_stories/Chart.Network.stories.tsx +432 -0
  40. package/src/_stories/Chart.PaletteConversion.Editor.stories.tsx +208 -0
  41. package/src/_stories/Chart.Patterns.smoke.stories.tsx +31 -0
  42. package/src/_stories/Chart.PercentageValue.stories.tsx +311 -0
  43. package/src/_stories/Chart.PieLabels.stories.tsx +49 -0
  44. package/src/_stories/Chart.RankByValueMutation.stories.tsx +206 -0
  45. package/src/_stories/Chart.RequiredFields.stories.tsx +681 -0
  46. package/src/_stories/Chart.Sankey.stories.tsx +417 -0
  47. package/src/_stories/Chart.SeriesColorAssignments.stories.tsx +47 -0
  48. package/src/_stories/Chart.SmallMultiplesNumericTileOrder.stories.tsx +137 -0
  49. package/src/_stories/Chart.TooltipSeriesLabels.stories.tsx +43 -0
  50. package/src/_stories/Chart.VegaRemoteData.stories.tsx +31 -0
  51. package/src/_stories/Chart.smoke.stories.tsx +14 -1
  52. package/src/_stories/ChartAnnotation.Editor.stories.tsx +78 -0
  53. package/src/_stories/ChartAxisTitles.smoke.stories.tsx +48 -0
  54. package/src/_stories/ChartBar.Editor.stories.tsx +499 -57
  55. package/src/_stories/ChartBrush.Editor.stories.tsx +46 -67
  56. package/src/_stories/ChartBrush.Regression.stories.tsx +135 -0
  57. package/src/_stories/ChartEditor.Editor.stories.tsx +33 -3
  58. package/src/_stories/ChartLine.EffectCompatibility.stories.tsx +120 -0
  59. package/src/_stories/ChartPrefixSuffix.smoke.stories.tsx +28 -1
  60. package/src/_stories/_mock/annotation_event-line_mock.json +2 -2
  61. package/src/_stories/_mock/editor-test-configs.json +20 -10
  62. package/src/_stories/_mock/editor-test-datasets.json +38 -42
  63. package/src/_stories/_mock/heatmap-average-age-categorical.json +2 -2
  64. package/src/_stories/_mock/heatmap.json +2 -2
  65. package/src/_stories/_mock/legend-axis-align-stacked-bar.json +125 -0
  66. package/src/_stories/_mock/sankey_mock.json +23 -27
  67. package/src/components/Annotations/components/AnnotationDraggable.styles.css +52 -0
  68. package/src/components/Annotations/components/AnnotationDraggable.tsx +639 -139
  69. package/src/components/Annotations/components/AnnotationDropdown.styles.css +44 -4
  70. package/src/components/Annotations/components/AnnotationDropdown.tsx +10 -32
  71. package/src/components/Annotations/helpers/resolveAnnotationLayout.ts +212 -0
  72. package/src/components/Annotations/tests/AnnotationDraggable.test.tsx +612 -19
  73. package/src/components/Annotations/tests/AnnotationDropdown.test.tsx +50 -0
  74. package/src/components/Annotations/tests/resolveAnnotationLayout.test.ts +203 -0
  75. package/src/components/Axis/BottomAxis.tsx +1 -1
  76. package/src/components/Axis/Categorical.Axis.tsx +3 -3
  77. package/src/components/Axis/RightAxis.tsx +21 -13
  78. package/src/components/BarChart/components/BarChart.Horizontal.tsx +39 -8
  79. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +13 -4
  80. package/src/components/BarChart/components/BarChart.StackedVertical.test.ts +28 -0
  81. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +30 -26
  82. package/src/components/BarChart/components/BarChart.Vertical.tsx +38 -28
  83. package/src/components/BarChart/components/PortionPatternOverlay.tsx +61 -0
  84. package/src/components/BarChart/components/context.tsx +6 -0
  85. package/src/components/BarChart/helpers/getBarData.ts +28 -28
  86. package/src/components/BarChart/helpers/getBarHeights.ts +2 -2
  87. package/src/components/BarChart/helpers/getPatternUrl.ts +2 -5
  88. package/src/components/BarChart/helpers/index.ts +8 -0
  89. package/src/components/BarChart/helpers/portionPattern.ts +243 -0
  90. package/src/components/BarChart/helpers/tests/addMinimumBarHeights.test.ts +42 -0
  91. package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +13 -0
  92. package/src/components/BarChart/helpers/tests/portionPattern.test.ts +280 -0
  93. package/src/components/BarChart/helpers/tests/useBarChart.test.tsx +202 -0
  94. package/src/components/BarChart/helpers/useBarChart.ts +60 -60
  95. package/src/components/BarChartRace/BarChartRace.test.tsx +199 -0
  96. package/src/components/BarChartRace/BarChartRace.tsx +230 -0
  97. package/src/components/BarChartRace/BarChartRaceFallback.test.tsx +61 -0
  98. package/src/components/BarChartRace/BarChartRaceFallback.tsx +48 -0
  99. package/src/components/BarChartRace/bar-chart-race.css +160 -0
  100. package/src/components/BarChartRace/helpers.test.ts +114 -0
  101. package/src/components/BarChartRace/helpers.ts +185 -0
  102. package/src/components/BarChartRace/index.ts +3 -0
  103. package/src/components/BoxPlot/BoxPlot.Horizontal.tsx +7 -6
  104. package/src/components/BoxPlot/BoxPlot.Vertical.tsx +9 -7
  105. package/src/components/BoxPlot/helpers/index.ts +13 -83
  106. package/src/components/Brush/BrushSelector.tsx +10 -24
  107. package/src/components/Brush/MiniChartPreview.tsx +33 -1
  108. package/src/components/Dendrogram/components/Dendrogram.tsx +374 -0
  109. package/src/components/Dendrogram/dendrogram.css +113 -0
  110. package/src/components/Dendrogram/helpers/layoutDendrogram.test.ts +95 -0
  111. package/src/components/Dendrogram/helpers/layoutDendrogram.ts +56 -0
  112. package/src/components/Dendrogram/helpers/prepareDendrogramData.test.ts +135 -0
  113. package/src/components/Dendrogram/helpers/prepareDendrogramData.ts +166 -0
  114. package/src/components/Dendrogram/helpers/resolveDendrogramLabels.test.ts +196 -0
  115. package/src/components/Dendrogram/helpers/resolveDendrogramLabels.ts +418 -0
  116. package/src/components/Dendrogram/index.tsx +1 -0
  117. package/src/components/Dendrogram/types/index.ts +66 -0
  118. package/src/components/EditorPanel/EditorPanel.tsx +1111 -805
  119. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +50 -5
  120. package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.css +10 -0
  121. package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.tsx +211 -0
  122. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +19 -8
  123. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +104 -61
  124. package/src/components/EditorPanel/components/Panels/Panel.Network.css +9 -0
  125. package/src/components/EditorPanel/components/Panels/Panel.Network.tsx +202 -0
  126. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +236 -36
  127. package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +6 -1
  128. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +89 -105
  129. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +21 -6
  130. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +1 -1
  131. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +8 -66
  132. package/src/components/EditorPanel/components/Panels/RacingControls.test.tsx +131 -0
  133. package/src/components/EditorPanel/components/Panels/RacingControls.tsx +185 -0
  134. package/src/components/EditorPanel/components/Panels/SeriesColorAssignments.tsx +2 -2
  135. package/src/components/EditorPanel/components/Panels/index.tsx +4 -0
  136. package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +20 -7
  137. package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +25 -2
  138. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +5 -4
  139. package/src/components/EditorPanel/useEditorPermissions.test.tsx +94 -0
  140. package/src/components/EditorPanel/useEditorPermissions.ts +75 -25
  141. package/src/components/Forecasting/Forecasting.tsx +16 -7
  142. package/src/components/HeatMap/components/HeatMap.tsx +511 -178
  143. package/src/components/HeatMap/heatmap.constants.ts +7 -3
  144. package/src/components/HeatMap/heatmap.css +101 -2
  145. package/src/components/HeatMap/helpers/layout.ts +80 -0
  146. package/src/components/HeatMap/tests/HeatMap.test.tsx +339 -16
  147. package/src/components/HeatMap/tests/helpers.test.ts +79 -0
  148. package/src/components/Legend/Legend.Component.tsx +163 -148
  149. package/src/components/Legend/Legend.Suppression.tsx +3 -3
  150. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +7 -0
  151. package/src/components/Legend/LegendGroup/LegendGroup.tsx +47 -35
  152. package/src/components/Legend/helpers/createFormatLabels.test.ts +226 -0
  153. package/src/components/Legend/helpers/createFormatLabels.tsx +45 -51
  154. package/src/components/LineChart/components/LineChart.Circle.tsx +3 -3
  155. package/src/components/LineChart/helpers.test.ts +126 -38
  156. package/src/components/LineChart/helpers.ts +9 -6
  157. package/src/components/LineChart/index.tsx +71 -27
  158. package/src/components/LineChart/tests/LineChart.test.tsx +630 -0
  159. package/src/components/LineChartRace/LineChartRace.test.tsx +275 -0
  160. package/src/components/LineChartRace/LineChartRace.tsx +134 -0
  161. package/src/components/LineChartRace/LineChartRaceFallback.tsx +28 -0
  162. package/src/components/LineChartRace/helpers.test.ts +88 -0
  163. package/src/components/LineChartRace/helpers.ts +157 -0
  164. package/src/components/LineChartRace/index.ts +3 -0
  165. package/src/components/LineChartRace/line-chart-race.css +16 -0
  166. package/src/components/LinearChart/ValueAxisAnchors.tsx +58 -0
  167. package/src/components/LinearChart/VisualizationRenderer.tsx +18 -23
  168. package/src/components/LinearChart/tests/LinearChart.test.tsx +412 -13
  169. package/src/components/LinearChart/tests/VisualizationRenderer.test.tsx +141 -0
  170. package/src/components/LinearChart/tests/mockConfigContext.ts +2 -1
  171. package/src/components/LinearChart.tsx +85 -54
  172. package/src/components/Network/components/Network.tsx +372 -0
  173. package/src/components/Network/helpers/layoutNetwork.test.ts +68 -0
  174. package/src/components/Network/helpers/layoutNetwork.ts +87 -0
  175. package/src/components/Network/helpers/prepareNetworkData.test.ts +116 -0
  176. package/src/components/Network/helpers/prepareNetworkData.ts +101 -0
  177. package/src/components/Network/helpers/resolveNetworkLabels.test.ts +112 -0
  178. package/src/components/Network/helpers/resolveNetworkLabels.ts +206 -0
  179. package/src/components/Network/index.tsx +3 -0
  180. package/src/components/Network/network.css +107 -0
  181. package/src/components/Network/types/index.ts +56 -0
  182. package/src/components/PairedBarChart.jsx +5 -10
  183. package/src/components/PairedBarChart.test.tsx +71 -0
  184. package/src/components/PieChart/PieChart.tsx +121 -13
  185. package/src/components/PieChart/helpers/labelPlacement.test.ts +88 -0
  186. package/src/components/PieChart/helpers/labelPlacement.ts +95 -0
  187. package/src/components/RacingChartRenderer.tsx +44 -0
  188. package/src/components/Regions/components/Regions.test.tsx +179 -0
  189. package/src/components/Regions/components/Regions.tsx +104 -69
  190. package/src/components/Regions/index.tsx +2 -3
  191. package/src/components/Sankey/components/Sankey.tsx +264 -437
  192. package/src/components/Sankey/helpers/getSankeyTooltip.ts +12 -0
  193. package/src/components/Sankey/helpers/prepareSankeyData.test.ts +71 -0
  194. package/src/components/Sankey/helpers/prepareSankeyData.ts +90 -0
  195. package/src/components/Sankey/sankey.scss +57 -116
  196. package/src/components/Sankey/types/index.ts +41 -4
  197. package/src/components/ScatterPlot/ScatterPlot.jsx +28 -12
  198. package/src/components/ScatterPlot/ScatterPlot.test.tsx +60 -0
  199. package/src/components/SmallMultiples/SmallMultiples.tsx +12 -12
  200. package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +7 -5
  201. package/src/components/raceTiming.ts +18 -0
  202. package/src/data/initial-state.js +79 -31
  203. package/src/helpers/applyChartTypeContract.ts +71 -0
  204. package/src/helpers/axisLabelFontSize.ts +2 -2
  205. package/src/helpers/boxPlotStats.ts +60 -0
  206. package/src/helpers/bumpChartTooltip.ts +2 -1
  207. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.test.ts +37 -0
  208. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +3 -2
  209. package/src/helpers/calculateRightYAxisWidth.ts +60 -0
  210. package/src/helpers/categoryOrder.ts +60 -0
  211. package/src/helpers/colorAssignmentHelpers.ts +2 -2
  212. package/src/helpers/createNewChartConfig.ts +56 -0
  213. package/src/helpers/dataDrivenYAxisCategories.ts +18 -0
  214. package/src/helpers/forecastLegend.ts +88 -0
  215. package/src/helpers/getBoxPlotConfig.ts +26 -38
  216. package/src/helpers/getChartTypeDefaultPalette.ts +29 -0
  217. package/src/helpers/getColorCodeCategoryColorMap.ts +26 -0
  218. package/src/helpers/getColorScale.ts +31 -22
  219. package/src/helpers/getComboChartConfig.ts +0 -1
  220. package/src/helpers/getDynamicYAxisCategories.ts +69 -0
  221. package/src/helpers/getExcludedData.ts +0 -1
  222. package/src/helpers/getHasBoundarySuppression.ts +4 -3
  223. package/src/helpers/getMinMax.ts +11 -8
  224. package/src/helpers/getNewRuntime.ts +4 -3
  225. package/src/helpers/getPiePercent.ts +3 -3
  226. package/src/helpers/getStackedSeriesMax.ts +51 -0
  227. package/src/helpers/getV21ChartDistributionColors.ts +57 -0
  228. package/src/helpers/handleRankByValue.ts +15 -15
  229. package/src/helpers/isConvertLineToBarGraph.ts +2 -2
  230. package/src/helpers/seriesColumnSettings.ts +31 -3
  231. package/src/helpers/sizeHelpers.ts +4 -5
  232. package/src/helpers/smallMultiplesHelpers.ts +10 -13
  233. package/src/helpers/tests/applyChartTypeContract.test.ts +37 -0
  234. package/src/helpers/tests/boxPlotStats.test.ts +31 -0
  235. package/src/helpers/tests/bumpChartTooltip.test.ts +29 -0
  236. package/src/helpers/tests/calculateRightYAxisWidth.test.ts +105 -0
  237. package/src/helpers/tests/categoryOrder.test.ts +57 -0
  238. package/src/helpers/tests/countNumOfTicks.test.ts +11 -0
  239. package/src/helpers/tests/createNewChartConfig.test.ts +182 -0
  240. package/src/helpers/tests/getBoxPlotConfig.test.ts +70 -0
  241. package/src/helpers/tests/getColorCodeCategoryColorMap.test.ts +113 -0
  242. package/src/helpers/tests/getColorScale.test.ts +226 -1
  243. package/src/helpers/tests/getDynamicYAxisCategories.test.ts +61 -0
  244. package/src/helpers/tests/getMinMax.test.ts +41 -0
  245. package/src/helpers/tests/getStackedSeriesMax.test.ts +26 -0
  246. package/src/helpers/tests/handleRankByValue.test.ts +55 -37
  247. package/src/helpers/tests/seriesColumnSettings.test.ts +83 -0
  248. package/src/helpers/tests/smallMultiplesHelpers.test.ts +37 -1
  249. package/src/helpers/tests/tooltipHelpers.test.ts +80 -0
  250. package/src/helpers/tooltipHelpers.ts +16 -0
  251. package/src/hooks/tests/useRightAxis.test.tsx +143 -2
  252. package/src/hooks/tests/useScales.test.tsx +3 -6
  253. package/src/hooks/tests/useTooltip.test.tsx +484 -0
  254. package/src/hooks/usePrefersReducedMotion.ts +22 -0
  255. package/src/hooks/useReduceData.ts +4 -36
  256. package/src/hooks/useRightAxis.ts +19 -26
  257. package/src/hooks/useScales.ts +15 -0
  258. package/src/hooks/useTooltip.tsx +106 -71
  259. package/src/scss/main.scss +74 -2
  260. package/src/store/chart.reducer.ts +4 -4
  261. package/src/test/CdcChart.dataTable.test.tsx +582 -14
  262. package/src/test/CdcChart.dendrogram.test.tsx +277 -0
  263. package/src/test/CdcChart.finalization.test.tsx +143 -0
  264. package/src/test/CdcChart.network.test.tsx +271 -0
  265. package/src/types/ChartConfig.ts +91 -54
  266. package/src/types/ChartContext.ts +5 -0
  267. package/src/types/Horizon.ts +1 -1
  268. package/LICENSE +0 -201
  269. package/dist/cdcchart-BWVIUcAF.es.js +0 -4802
  270. package/src/_stories/Chart.Sankey.smoke.stories.tsx +0 -62
  271. package/src/components/Sankey/components/ColumnList.tsx +0 -19
  272. package/src/components/Sankey/helpers/getSankeyTooltip.tsx +0 -34
  273. package/src/components/Sankey/useSankeyAlert.tsx +0 -60
  274. package/src/data/legacy-defaults.ts +0 -18
  275. package/src/helpers/ensureSpecialChartAxisTypes.ts +0 -11
  276. package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +0 -30
@@ -1,19 +1,19 @@
1
1
  import parse from 'html-react-parser'
2
2
  import React, { useMemo } from 'react'
3
3
  import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend'
4
- import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
5
4
  import LegendShape from '@cdc/core/components/LegendShape'
6
5
  import Button from '@cdc/core/components/elements/Button'
7
6
  import { getLegendClasses } from './helpers/getLegendClasses'
8
7
  import { useHighlightedBars } from '../../hooks/useHighlightedBars'
9
8
  import { getMarginTop, getGradientConfig, getMarginBottom } from './helpers/index'
10
9
  import { Label } from '../../types/Label'
11
- import { ChartConfig, ViewportSize } from '../../types/ChartConfig'
10
+ import { ChartConfig } from '../../types/ChartConfig'
12
11
  import { ColorScale } from '../../types/ChartContext'
13
12
  import { forwardRef } from 'react'
14
13
  import LegendSuppression from './Legend.Suppression'
15
14
  import LegendGradient from '@cdc/core/components/Legend/Legend.Gradient'
16
15
  import { DimensionsType } from '@cdc/core/types/Dimensions'
16
+ import type { ViewPort } from '@cdc/core/types/ViewPort'
17
17
  import { isLegendWrapViewport } from '@cdc/core/helpers/viewports'
18
18
  import LegendLineShape from './LegendLine.Shape'
19
19
  import LegendGroup from './LegendGroup'
@@ -22,13 +22,14 @@ import { getHorizonLayerColors, getHorizonMaxValue } from '../../components/Hori
22
22
  import { getSeriesWithData } from '../../helpers/dataHelpers'
23
23
  import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
24
24
  import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
25
+ import { processMarkupVariables } from '@cdc/core/helpers/markupProcessor'
25
26
 
26
27
  const LEGEND_PADDING = 36
27
28
 
28
29
  interface LegendProps {
29
30
  colorScale: ColorScale
30
31
  config: ChartConfig
31
- currentViewport: ViewportSize
32
+ currentViewport: ViewPort
32
33
  formatLabels: (labels: Label[]) => Label[]
33
34
  formatNumber?: (value: number, axis?: string) => string
34
35
  highlight: Function
@@ -62,6 +63,42 @@ const Legend: React.FC<LegendProps> = forwardRef(
62
63
  const { innerClasses, containerClasses } = getLegendClasses(config)
63
64
  const { runtime, legend } = config
64
65
  const { series } = runtime
66
+ const processedLegendText = useMemo(() => {
67
+ if (!config.enableMarkupVariables || !config.markupVariables?.length) {
68
+ return {
69
+ label: legend?.label,
70
+ description: legend?.description
71
+ }
72
+ }
73
+
74
+ const markupOptions = {
75
+ isEditor: false,
76
+ filters: config.filters || [],
77
+ locale: config.locale,
78
+ dataMetadata: config.dataMetadata
79
+ }
80
+
81
+ return {
82
+ label: legend?.label
83
+ ? processMarkupVariables(legend.label, data || config.data || [], config.markupVariables, markupOptions)
84
+ .processedContent
85
+ : legend?.label,
86
+ description: legend?.description
87
+ ? processMarkupVariables(legend.description, data || config.data || [], config.markupVariables, markupOptions)
88
+ .processedContent
89
+ : legend?.description
90
+ }
91
+ }, [
92
+ config.enableMarkupVariables,
93
+ config.markupVariables,
94
+ config.filters,
95
+ config.locale,
96
+ config.dataMetadata,
97
+ config.data,
98
+ data,
99
+ legend?.label,
100
+ legend?.description
101
+ ])
65
102
 
66
103
  const seriesWithData = getSeriesWithData(config)
67
104
  // For Radar charts, seriesWithData contains dimension keys but legend shows entity names
@@ -115,10 +152,12 @@ const Legend: React.FC<LegendProps> = forwardRef(
115
152
  aria-label='legend'
116
153
  tabIndex={0}
117
154
  >
118
- {(legend.label || legend.description) && (
119
- <div className={legend.description ? 'mb-3' : 'mb-2'}>
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>}
155
+ {(processedLegendText.label || processedLegendText.description) && (
156
+ <div className={processedLegendText.description ? 'mb-3' : 'mb-2'}>
157
+ {processedLegendText.label && <h3 className='fw-bold cove-prose'>{parse(processedLegendText.label)}</h3>}
158
+ {processedLegendText.description && (
159
+ <p className='mt-2 cove-prose'>{parse(processedLegendText.description)}</p>
160
+ )}
122
161
  </div>
123
162
  )}
124
163
  <LegendGradient
@@ -145,109 +184,122 @@ const Legend: React.FC<LegendProps> = forwardRef(
145
184
 
146
185
  <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
147
186
  {labels => {
187
+ const formattedLabels = formatLabels(labels as Label[]).filter(
188
+ label => dontFilterLegendItems || seriesWithData.includes(label.datum)
189
+ )
190
+ const visibleForecastLegendKeys = [
191
+ ...formattedLabels
192
+ .filter(label => !config.exclusions.active || !config.exclusions.keys?.includes(label.datum))
193
+ .map(label => label.datum),
194
+ ...highLightedLegendItems.map(item => item.legendLabel).filter(Boolean)
195
+ ]
196
+ const allForecastLegendItemsSelected =
197
+ config.visualizationType === 'Forecasting' &&
198
+ visibleForecastLegendKeys.length > 0 &&
199
+ visibleForecastLegendKeys.every(key => seriesHighlight.includes(key))
200
+ const showAllButton = seriesHighlight.length > 0 && !allForecastLegendItemsSelected
201
+
148
202
  return (
149
203
  <>
150
204
  <div className={innerClasses.join(' ')}>
151
- {formatLabels(labels as Label[])
152
- .filter(label => dontFilterLegendItems || seriesWithData.includes(label.datum))
153
- .map((label, i) => {
154
- let className = ['legend-item', `legend-text--${label.text.replace(' ', '').toLowerCase()}`]
155
- let itemName = label.datum
205
+ {formattedLabels.map((label, i) => {
206
+ let className = [
207
+ 'legend-item',
208
+ 'legend-item--interactive',
209
+ `legend-text--${label.text.replace(' ', '').toLowerCase()}`
210
+ ]
211
+ let itemName = label.datum
156
212
 
157
- // Filter excluded data keys from legend
158
- if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
159
- return null
160
- }
161
-
162
- if (runtime.seriesLabels) {
163
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
164
- itemName = config.runtime.seriesKeys[index]
213
+ // Filter excluded data keys from legend
214
+ if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
215
+ return null
216
+ }
165
217
 
166
- if (runtime?.forecastingSeriesKeys?.length > 0) {
167
- itemName = label.text
168
- }
169
- }
218
+ if (runtime.seriesLabels && !runtime?.forecastingSeriesKeys?.length) {
219
+ let index = config.runtime.seriesLabelsAll.indexOf(itemName)
220
+ itemName = config.runtime.seriesKeys[index]
221
+ }
170
222
 
171
- if (seriesHighlight.length) {
172
- if (!seriesHighlight.includes(itemName)) {
173
- className.push('inactive')
174
- } else className.push('highlighted')
175
- }
223
+ if (seriesHighlight.length) {
224
+ if (!seriesHighlight.includes(itemName)) {
225
+ className.push('inactive')
226
+ } else className.push('highlighted')
227
+ }
176
228
 
177
- if (
178
- config.legend.style === 'gradient' ||
179
- config.legend.groupBy ||
180
- config.visualizationType === 'Horizon Chart'
181
- ) {
182
- return null
183
- }
229
+ if (
230
+ config.legend.style === 'gradient' ||
231
+ config.legend.groupBy ||
232
+ config.visualizationType === 'Horizon Chart'
233
+ ) {
234
+ return null
235
+ }
184
236
 
185
- return (
186
- <LegendItem
187
- className={className.join(' ')}
188
- tabIndex={0}
189
- key={`legend-quantile-${i}`}
190
- onKeyDown={e => {
191
- if (e.key === 'Enter') {
192
- e.preventDefault()
193
- publishAnalyticsEvent({
194
- vizType: config?.type,
195
- vizSubType: getVizSubType(config),
196
- vizTitle: getVizTitle(config),
197
- eventType: `chart_legend_item_toggled` as any,
198
- eventAction: 'keydown',
199
- eventLabel: interactionLabel,
200
- specifics:
201
- config.visualizationType === 'Bar'
202
- ? `label: ${label.text}, orientation: ${
203
- config.orientation === 'horizontal' ? 'horizontal' : 'vertical'
204
- }, mode: ${legend.behavior}`
205
- : `label: ${label.text}, mode: ${legend.behavior}`
206
- })
207
- highlight(label)
208
- }
209
- }}
210
- onClick={e => {
237
+ return (
238
+ <LegendItem
239
+ className={className.join(' ')}
240
+ tabIndex={0}
241
+ key={`legend-quantile-${i}`}
242
+ onKeyDown={e => {
243
+ if (e.key === 'Enter') {
211
244
  e.preventDefault()
212
245
  publishAnalyticsEvent({
213
246
  vizType: config?.type,
214
247
  vizSubType: getVizSubType(config),
248
+ vizTitle: getVizTitle(config),
215
249
  eventType: `chart_legend_item_toggled` as any,
216
- eventAction: 'click',
250
+ eventAction: 'keydown',
217
251
  eventLabel: interactionLabel,
218
252
  specifics:
219
253
  config.visualizationType === 'Bar'
220
254
  ? `label: ${label.text}, orientation: ${
221
255
  config.orientation === 'horizontal' ? 'horizontal' : 'vertical'
222
256
  }, mode: ${legend.behavior}`
223
- : `label: ${label.text}, mode: ${legend.behavior}`,
224
-
225
- vizTitle: getVizTitle(config)
257
+ : `label: ${label.text}, mode: ${legend.behavior}`
226
258
  })
227
259
  highlight(label)
228
- }}
229
- role='button'
230
- >
231
- <>
232
- {config.visualizationType === 'Line' && config.legend.style === 'lines' ? (
233
- <React.Fragment>
234
- <LegendLineShape index={i} label={label} config={config} />
235
- </React.Fragment>
236
- ) : (
237
- <>
238
- <LegendShape
239
- shape={config.legend.style === 'boxes' ? 'square' : 'circle'}
240
- fill={label.value}
241
- />
242
- </>
243
- )}
244
- </>
245
- <LegendLabel align='left' className='m-0'>
246
- {parse(label.text)}
247
- </LegendLabel>
248
- </LegendItem>
249
- )
250
- })}
260
+ }
261
+ }}
262
+ onClick={e => {
263
+ e.preventDefault()
264
+ publishAnalyticsEvent({
265
+ vizType: config?.type,
266
+ vizSubType: getVizSubType(config),
267
+ eventType: `chart_legend_item_toggled` as any,
268
+ eventAction: 'click',
269
+ eventLabel: interactionLabel,
270
+ specifics:
271
+ config.visualizationType === 'Bar'
272
+ ? `label: ${label.text}, orientation: ${
273
+ config.orientation === 'horizontal' ? 'horizontal' : 'vertical'
274
+ }, mode: ${legend.behavior}`
275
+ : `label: ${label.text}, mode: ${legend.behavior}`,
276
+
277
+ vizTitle: getVizTitle(config)
278
+ })
279
+ highlight(label)
280
+ }}
281
+ role='button'
282
+ >
283
+ <>
284
+ {config.visualizationType === 'Line' && config.legend.style === 'lines' ? (
285
+ <React.Fragment>
286
+ <LegendLineShape index={i} label={label} config={config} />
287
+ </React.Fragment>
288
+ ) : (
289
+ <>
290
+ <LegendShape
291
+ shape={config.legend.style === 'boxes' ? 'square' : 'circle'}
292
+ fill={label.value}
293
+ />
294
+ </>
295
+ )}
296
+ </>
297
+ <LegendLabel align='left' className='m-0'>
298
+ {parse(label.text)}
299
+ </LegendLabel>
300
+ </LegendItem>
301
+ )
302
+ })}
251
303
 
252
304
  {highLightedLegendItems.map((bar, i) => {
253
305
  // if duplicates only return first item
@@ -287,6 +339,12 @@ const Legend: React.FC<LegendProps> = forwardRef(
287
339
  })}
288
340
  </div>
289
341
 
342
+ {showAllButton && (
343
+ <Button size='sm' onClick={labels => handleShowAll(labels)} style={{ marginTop: '1rem' }}>
344
+ Show All
345
+ </Button>
346
+ )}
347
+
290
348
  <LegendSuppression config={config} isLegendBottom={isLegendBottom} />
291
349
 
292
350
  {/* Pattern Legend Items */}
@@ -298,9 +356,13 @@ const Legend: React.FC<LegendProps> = forwardRef(
298
356
  >
299
357
  {Object.entries(config.legend.patterns).map(([key, pattern]) => {
300
358
  const patternId = `legend-pattern-${key}`
301
- const size = config.legend.patternSize || 8
302
- const legendSize = 16
303
359
  const pColor = (pattern as any)?.color || '#666666'
360
+ const patternSize = (pattern as any)?.patternSize ?? 10
361
+ const targetSeriesLabel =
362
+ pattern.application === 'portion' && pattern.dataKey
363
+ ? config.runtime?.seriesLabels?.[pattern.dataKey]
364
+ : undefined
365
+ const patternBackground = targetSeriesLabel ? colorScale(targetSeriesLabel) : 'white'
304
366
 
305
367
  return (
306
368
  <LegendItem
@@ -309,58 +371,16 @@ const Legend: React.FC<LegendProps> = forwardRef(
309
371
  tabIndex={0}
310
372
  role='button'
311
373
  >
312
- <span className='me-2'>
313
- <svg width={legendSize} height={legendSize}>
314
- <defs>
315
- {pattern.shape === 'circles' && (
316
- <PatternCircles
317
- id={patternId}
318
- height={size}
319
- width={size}
320
- fill={pColor}
321
- radius={1.25}
322
- />
323
- )}
324
- {pattern.shape === 'lines' && (
325
- <PatternLines
326
- id={patternId}
327
- height={size}
328
- width={size}
329
- stroke={pColor}
330
- strokeWidth={0.75}
331
- orientation={['horizontal']}
332
- />
333
- )}
334
- {pattern.shape === 'diagonalLines' && (
335
- <PatternLines
336
- id={patternId}
337
- height={size}
338
- width={size}
339
- stroke={pColor}
340
- strokeWidth={0.75}
341
- orientation={['diagonalRightToLeft']}
342
- />
343
- )}
344
- {pattern.shape === 'waves' && (
345
- <PatternWaves
346
- id={patternId}
347
- height={size}
348
- width={size}
349
- fill={pColor}
350
- strokeWidth={0.25}
351
- />
352
- )}
353
- </defs>
354
- <circle
355
- fill={`url(#${patternId})`}
356
- r={legendSize / 2}
357
- cx={legendSize / 2}
358
- cy={legendSize / 2}
359
- stroke='#0000004d'
360
- strokeWidth={1}
361
- />
362
- </svg>
363
- </span>
374
+ <LegendShape
375
+ shape={config.legend.style === 'boxes' ? 'square' : 'circle'}
376
+ fill={patternBackground}
377
+ patternInfo={{
378
+ pattern: pattern.shape || 'circles',
379
+ patternId,
380
+ size: patternSize,
381
+ color: pColor
382
+ }}
383
+ />
364
384
  <LegendLabel align='left' className='m-0'>
365
385
  {parse(String((pattern as any)?.label || key))}
366
386
  </LegendLabel>
@@ -373,11 +393,6 @@ const Legend: React.FC<LegendProps> = forwardRef(
373
393
  )
374
394
  }}
375
395
  </LegendOrdinal>
376
- {seriesHighlight.length > 0 && (
377
- <Button onClick={labels => handleShowAll(labels)} style={{ marginTop: '1rem' }}>
378
- Show All
379
- </Button>
380
- )}
381
396
  </aside>
382
397
  )
383
398
  }
@@ -70,16 +70,16 @@ const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) =>
70
70
 
71
71
  if (visualizationType === 'Combo') {
72
72
  return (
73
- <React.Fragment>
73
+ <React.Fragment key={itemKey}>
74
74
  {pd.symbol && pd.iconCode && (
75
- <div style={getStyle(pd.displayGray)} key={itemKey} className={`${baseClass} ${pd.symbol}`}>
75
+ <div style={getStyle(pd.displayGray)} className={`${baseClass} ${pd.symbol}`}>
76
76
  <span className={pd.symbol}>{pd.iconCode}</span>
77
77
  <p className={pd.type}>{pd.label}</p>
78
78
  </div>
79
79
  )}
80
80
 
81
81
  {pd.style && pd.lineCode && (
82
- <div style={getStyle(pd.displayGray)} key={itemKey} className={baseClass}>
82
+ <div style={getStyle(pd.displayGray)} className={baseClass}>
83
83
  <span>{pd.lineCode}</span>
84
84
  <p>{pd.label}</p>
85
85
  </div>
@@ -10,6 +10,7 @@
10
10
  font-weight: 400;
11
11
  margin-bottom: 0.5rem;
12
12
  }
13
+
13
14
  .group-label {
14
15
  font-family: Nunito, sans-serif;
15
16
  font-size: 1rem;
@@ -17,6 +18,12 @@
17
18
  }
18
19
  }
19
20
 
21
+ .legend-group__items {
22
+ display: flex;
23
+ flex-direction: column;
24
+ row-gap: 0.25rem;
25
+ }
26
+
20
27
  .legend-group-grid {
21
28
  display: flex;
22
29
  flex-wrap: wrap;
@@ -2,7 +2,6 @@ import React, { useContext } from 'react'
2
2
  import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend'
3
3
  import LegendShape from '@cdc/core/components/LegendShape'
4
4
  import ConfigContext from '../../../ConfigContext'
5
- import _ from 'lodash'
6
5
  import './LegendGroup.styles.css'
7
6
 
8
7
  interface LegendGroup {
@@ -58,43 +57,56 @@ const LegendGroup = ({ formatLabels }) => {
58
57
  <p className='legend-group group-label'>{group}</p>
59
58
  </div>
60
59
  <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
61
- {labels =>
62
- formatLabels(labels)
63
- .filter(label => {
64
- const groupBy = config.legend.groupBy || ''
65
- return data.some(d => d[groupBy] === group && d[label.text] !== undefined && d[label.text] !== 'NA')
66
- })
60
+ {labels => (
61
+ <div className='legend-group__items'>
62
+ {formatLabels(labels)
63
+ .filter(label => {
64
+ const groupBy = config.legend.groupBy || ''
65
+ return data.some(
66
+ d => d[groupBy] === group && d[label.text] !== undefined && d[label.text] !== 'NA'
67
+ )
68
+ })
67
69
 
68
- .map((label, i) => {
69
- let className = ['legend-group', 'group-item']
70
- if (seriesHighlight.length) {
71
- if (!seriesHighlight.includes(label.datum)) {
72
- className.push('inactive')
73
- } else {
74
- className.push('highlighted')
70
+ .map((label, i) => {
71
+ let className = ['legend-group', 'group-item', 'legend-item--interactive']
72
+ if (seriesHighlight.length) {
73
+ if (!seriesHighlight.includes(label.datum)) {
74
+ className.push('inactive')
75
+ } else {
76
+ className.push('highlighted')
77
+ }
75
78
  }
76
- }
77
79
 
78
- return (
79
- <LegendItem
80
- alignItems='start'
81
- className={className.join(' ')}
82
- onClick={e => {
83
- e.preventDefault()
84
- highlight(label)
85
- }}
86
- key={`legend-item-${i}`}
87
- tabIndex={0}
88
- role='button'
89
- >
90
- <LegendShape shape={config.legend.style === 'boxes' ? 'square' : 'circle'} fill={label.value} />
91
- <LegendLabel align='left' margin='0'>
92
- {label.text}
93
- </LegendLabel>
94
- </LegendItem>
95
- )
96
- })
97
- }
80
+ return (
81
+ <LegendItem
82
+ alignItems='start'
83
+ className={className.join(' ')}
84
+ onClick={e => {
85
+ e.preventDefault()
86
+ highlight(label)
87
+ }}
88
+ onKeyDown={e => {
89
+ if (e.key === 'Enter') {
90
+ e.preventDefault()
91
+ highlight(label)
92
+ }
93
+ }}
94
+ key={`legend-item-${i}`}
95
+ tabIndex={0}
96
+ role='button'
97
+ >
98
+ <LegendShape
99
+ shape={config.legend.style === 'boxes' ? 'square' : 'circle'}
100
+ fill={label.value}
101
+ />
102
+ <LegendLabel align='left' margin='0'>
103
+ {label.text}
104
+ </LegendLabel>
105
+ </LegendItem>
106
+ )
107
+ })}
108
+ </div>
109
+ )}
98
110
  </LegendOrdinal>
99
111
  </div>
100
112
  )