@cdc/chart 4.25.11 → 4.26.2

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 (181) hide show
  1. package/CLAUDE.local.md +79 -0
  2. package/dist/{cdcchart-dgT_1dIT.es.js → cdcchart-DQ00cQCm.es.js} +1 -20
  3. package/dist/cdcchart.js +51401 -50814
  4. package/examples/default.json +378 -0
  5. package/examples/feature/__data__/horizon-chart-data.json +373 -0
  6. package/examples/feature/annotations/index.json +3 -6
  7. package/examples/feature/horizon/horizon-chart.json +395 -0
  8. package/examples/feature/pie/planet-pie-example-config.json +48 -2
  9. package/examples/line-chart-states.json +1085 -0
  10. package/examples/private/123.json +694 -0
  11. package/examples/private/DEV-12100.json +1303 -0
  12. package/examples/private/anchor-issue.json +4094 -0
  13. package/examples/private/backwards-slider.json +10430 -0
  14. package/examples/private/cat-y.json +1235 -0
  15. package/examples/private/data-points.json +228 -0
  16. package/examples/private/georgia.csv +160 -0
  17. package/examples/private/height.json +3915 -0
  18. package/examples/private/links.json +569 -0
  19. package/examples/private/quadrant.txt +30 -0
  20. package/examples/private/test-forecast.json +5510 -0
  21. package/examples/private/timeline-data.json +1 -0
  22. package/examples/private/timeline.json +389 -0
  23. package/examples/private/warming-stripe-test.json +2578 -0
  24. package/examples/private/warming-stripes.json +4763 -0
  25. package/examples/radar-chart-simple.json +133 -0
  26. package/examples/radar-chart.json +148 -0
  27. package/examples/tech-adoption-with-links.json +560 -0
  28. package/index.html +1 -36
  29. package/package.json +59 -60
  30. package/src/CdcChartComponent.tsx +206 -89
  31. package/src/_stories/Chart.Anchors.stories.tsx +10 -0
  32. package/src/_stories/Chart.BoxPlot.stories.tsx +7 -0
  33. package/src/_stories/Chart.CI.stories.tsx +13 -0
  34. package/src/_stories/Chart.Combo.stories.tsx +17 -0
  35. package/src/_stories/Chart.CustomColors.stories.tsx +4 -0
  36. package/src/_stories/Chart.DynamicSeries.stories.tsx +19 -0
  37. package/src/_stories/Chart.Filters.stories.tsx +4 -0
  38. package/src/_stories/Chart.Forecast.stories.tsx +4 -0
  39. package/src/_stories/Chart.HTMLInDataTable.stories.tsx +22 -0
  40. package/src/_stories/Chart.Legend.Gradient.stories.tsx +28 -0
  41. package/src/_stories/Chart.Patterns.stories.tsx +4 -0
  42. package/src/_stories/Chart.PreserveDecimals.stories.tsx +25 -0
  43. package/src/_stories/Chart.Regions.Categorical.stories.tsx +161 -0
  44. package/src/_stories/Chart.Regions.DateScale.stories.tsx +216 -0
  45. package/src/_stories/Chart.Regions.DateTimeScale.stories.tsx +312 -0
  46. package/src/_stories/Chart.ScatterPlot.stories.tsx +4 -0
  47. package/src/_stories/Chart.SmallMultiples.stories.tsx +16 -0
  48. package/src/_stories/Chart.stories.tsx +45 -0
  49. package/src/_stories/Chart.tooltip.stories.tsx +7 -0
  50. package/src/_stories/ChartAnnotation.stories.tsx +10 -0
  51. package/src/_stories/ChartAxisLabels.stories.tsx +4 -0
  52. package/src/_stories/ChartAxisTitles.stories.tsx +10 -0
  53. package/src/_stories/ChartBar.Editor.stories.tsx +11 -6
  54. package/src/_stories/ChartBrush.Editor.stories.tsx +295 -0
  55. package/src/_stories/ChartBrush.Matrix.Continuous.stories.tsx +41 -0
  56. package/src/_stories/ChartBrush.Matrix.Date.stories.tsx +114 -0
  57. package/src/_stories/ChartBrush.Matrix.DateTime.stories.tsx +78 -0
  58. package/src/_stories/ChartBrush.stories.tsx +57 -0
  59. package/src/_stories/ChartEditor.Editor.stories.tsx +3 -5
  60. package/src/_stories/ChartEditor.stories.tsx +7 -0
  61. package/src/_stories/ChartLine.QuadrantAngles.stories.tsx +89 -0
  62. package/src/_stories/ChartLine.Suppression.stories.tsx +7 -0
  63. package/src/_stories/ChartLine.Symbols.stories.tsx +4 -0
  64. package/src/_stories/ChartPrefixSuffix.stories.tsx +46 -1
  65. package/src/_stories/TechAdoptionWithLinks.stories.tsx +34 -0
  66. package/src/_stories/_mock/brush_continuous.json +86 -0
  67. package/src/_stories/_mock/brush_date_large.json +176 -0
  68. package/src/_stories/_mock/brush_enabled.json +326 -0
  69. package/src/_stories/_mock/brush_mock.json +2 -69
  70. package/src/_stories/_mock/horizontal-bars-dynamic-y-axis.json +413 -0
  71. package/src/_stories/_mock/line_chart_angle_near_zero_fall.json +195 -0
  72. package/src/_stories/_mock/line_chart_angle_near_zero_rise.json +195 -0
  73. package/src/_stories/_mock/line_chart_angle_q1_steep_upward.json +195 -0
  74. package/src/_stories/_mock/line_chart_angle_q2_gentle_downward.json +195 -0
  75. package/src/_stories/_mock/line_chart_angle_q3_steep_downward.json +195 -0
  76. package/src/_stories/_mock/line_chart_angle_q4_gentle_upward.json +195 -0
  77. package/src/_stories/_mock/line_chart_quadrant_angles.json +264 -0
  78. package/src/components/Annotations/components/AnnotationDraggable.styles.css +11 -17
  79. package/src/components/Annotations/components/AnnotationDraggable.tsx +240 -116
  80. package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -2
  81. package/src/components/Annotations/components/AnnotationDropdown.tsx +8 -12
  82. package/src/components/Annotations/components/AnnotationList.styles.css +4 -10
  83. package/src/components/Annotations/components/AnnotationList.tsx +5 -4
  84. package/src/components/Annotations/components/findNearestDatum.ts +75 -85
  85. package/src/components/Annotations/helpers/getVisibleAnnotations.ts +38 -0
  86. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -2
  87. package/src/components/Axis/BottomAxis.tsx +270 -0
  88. package/src/components/Axis/Categorical.Axis.tsx +6 -7
  89. package/src/components/Axis/LeftAxis.tsx +404 -0
  90. package/src/components/Axis/LeftAxisGridlines.tsx +77 -0
  91. package/src/components/Axis/PairedBarAxis.tsx +186 -0
  92. package/src/components/Axis/README.md +94 -0
  93. package/src/components/Axis/RightAxis.tsx +108 -0
  94. package/src/components/Axis/axis.constants.ts +21 -0
  95. package/src/components/Axis/index.ts +7 -0
  96. package/src/components/BarChart/components/BarChart.Horizontal.tsx +178 -24
  97. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +3 -1
  98. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +1 -0
  99. package/src/components/BarChart/components/BarChart.Vertical.tsx +6 -8
  100. package/src/components/BarChart/components/BarChart.tsx +7 -1
  101. package/src/components/BarChart/components/context.tsx +1 -0
  102. package/src/components/BarChart/helpers/useBarChart.ts +14 -2
  103. package/src/components/Brush/BrushSelector.tsx +1390 -0
  104. package/src/components/Brush/MiniChartPreview.tsx +400 -0
  105. package/src/components/DeviationBar.jsx +9 -7
  106. package/src/components/EditorPanel/EditorPanel.tsx +2734 -2595
  107. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +60 -22
  108. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +56 -34
  109. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +137 -30
  110. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +2 -0
  111. package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +353 -0
  112. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +0 -1
  113. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +30 -25
  114. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +42 -28
  115. package/src/components/EditorPanel/components/Panels/index.tsx +2 -0
  116. package/src/components/EditorPanel/useEditorPermissions.ts +81 -39
  117. package/src/components/HorizonChart/HorizonChart.tsx +131 -0
  118. package/src/components/HorizonChart/components/HorizonBand.tsx +160 -0
  119. package/src/components/HorizonChart/helpers/calculateHorizonBands.ts +27 -0
  120. package/src/components/HorizonChart/helpers/getHorizonLayerColors.ts +40 -0
  121. package/src/components/HorizonChart/index.tsx +3 -0
  122. package/src/components/Legend/Legend.Component.tsx +52 -4
  123. package/src/components/Legend/Legend.tsx +4 -3
  124. package/src/components/Legend/LegendValueRange.tsx +77 -0
  125. package/src/components/Legend/helpers/createFormatLabels.tsx +164 -2
  126. package/src/components/Legend/helpers/generateValueRanges.ts +92 -0
  127. package/src/components/Legend/helpers/index.ts +10 -6
  128. package/src/components/LineChart/helpers/README.md +292 -0
  129. package/src/components/LineChart/helpers/labelPositioning.test.ts +245 -0
  130. package/src/components/LineChart/helpers/labelPositioning.ts +304 -0
  131. package/src/components/LineChart/index.tsx +44 -8
  132. package/src/components/LinearChart/README.md +109 -0
  133. package/src/components/LinearChart/VisualizationRenderer.tsx +267 -0
  134. package/src/components/LinearChart/linearChart.constants.ts +84 -0
  135. package/src/components/LinearChart/tests/LinearChart.test.tsx +201 -0
  136. package/src/components/LinearChart/tests/mockConfigContext.ts +129 -0
  137. package/src/components/LinearChart/utils/tickFormatting.ts +146 -0
  138. package/src/components/LinearChart.tsx +338 -1082
  139. package/src/components/PairedBarChart.jsx +20 -3
  140. package/src/components/PieChart/PieChart.tsx +1 -1
  141. package/src/components/RadarChart/RadarAxis.tsx +78 -0
  142. package/src/components/RadarChart/RadarChart.tsx +298 -0
  143. package/src/components/RadarChart/RadarGrid.tsx +64 -0
  144. package/src/components/RadarChart/RadarPolygon.tsx +91 -0
  145. package/src/components/RadarChart/helpers.ts +83 -0
  146. package/src/components/RadarChart/index.tsx +3 -0
  147. package/src/components/Regions/components/Regions.tsx +365 -122
  148. package/src/components/ScatterPlot/ScatterPlot.jsx +2 -2
  149. package/src/components/SmallMultiples/SmallMultipleTile.tsx +5 -1
  150. package/src/components/WarmingStripes/WarmingStripes.tsx +230 -0
  151. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +35 -0
  152. package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +104 -0
  153. package/src/components/WarmingStripes/index.tsx +3 -0
  154. package/src/data/initial-state.js +17 -2
  155. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +57 -0
  156. package/src/helpers/getExcludedData.ts +4 -0
  157. package/src/helpers/getMinMax.ts +12 -7
  158. package/src/helpers/handleChartAriaLabels.ts +19 -19
  159. package/src/helpers/handleLineType.ts +22 -18
  160. package/src/helpers/sizeHelpers.ts +0 -20
  161. package/src/helpers/smallMultiplesHelpers.ts +1 -1
  162. package/src/hooks/useChartHoverAnalytics.tsx +10 -9
  163. package/src/hooks/useProgrammaticTooltip.ts +23 -2
  164. package/src/hooks/useScales.ts +18 -1
  165. package/src/hooks/useTooltip.tsx +34 -10
  166. package/src/scss/DataTable.scss +0 -4
  167. package/src/scss/main.scss +22 -3
  168. package/src/selectors/README.md +68 -0
  169. package/src/store/chart.reducer.ts +2 -0
  170. package/src/test/CdcChart.test.jsx +1 -1
  171. package/src/types/ChartConfig.ts +21 -0
  172. package/src/types/ChartContext.ts +1 -0
  173. package/src/types/Horizon.ts +64 -0
  174. package/src/types/Label.ts +1 -0
  175. package/src/utils/analyticsTracking.ts +19 -0
  176. package/LICENSE +0 -201
  177. package/src/components/Annotations/components/helpers/index.tsx +0 -46
  178. package/src/components/Brush/BrushChart.tsx +0 -128
  179. package/src/components/Brush/BrushController.tsx +0 -71
  180. package/src/components/Brush/types.tsx +0 -8
  181. package/src/components/BrushChart.tsx +0 -223
@@ -1,5 +1,6 @@
1
1
  import React, { useContext } from 'react'
2
2
  import ConfigContext from '../../../../ConfigContext.js'
3
+ import { useEditorPermissions } from '../../useEditorPermissions'
3
4
 
4
5
  // CDC Core
5
6
  import Accordion from '@cdc/core/components/ui/Accordion'
@@ -13,7 +14,8 @@ import { type PanelProps } from './../PanelProps'
13
14
  import './../panels.scss'
14
15
 
15
16
  const PanelAnnotate: React.FC<PanelProps> = props => {
16
- const { updateConfig, config, svgRef } = useContext(ConfigContext)
17
+ const { updateConfig, config, transformedData } = useContext(ConfigContext)
18
+ const { visSupportsDataAnnotations } = useEditorPermissions()
17
19
 
18
20
  const updateField = (section, subsection, fieldName, value) => {
19
21
  if (subsection) {
@@ -39,11 +41,8 @@ const PanelAnnotate: React.FC<PanelProps> = props => {
39
41
  }
40
42
 
41
43
  const handleAnnotationUpdate = (value, property, index) => {
42
- const svgContainer = document.querySelector('.chart-container > svg')?.getBoundingClientRect()
43
- const newSvgDims = [svgContainer?.width, svgContainer?.height]
44
44
  const annotations = [...config?.annotations]
45
45
  annotations[index][property] = value
46
- annotations[index].savedDimensions = newSvgDims
47
46
 
48
47
  updateConfig({
49
48
  ...config,
@@ -52,15 +51,9 @@ const PanelAnnotate: React.FC<PanelProps> = props => {
52
51
  }
53
52
 
54
53
  const handleAddAnnotation = () => {
55
- // check if svg is animated svg or standard svg
56
- const newSvgDims = [
57
- svgRef?.current?.width?.baseVal?.value || svgRef?.current?.width,
58
- svgRef?.current?.height?.baseVal?.value || svgRef?.current?.height
59
- ]
60
-
61
54
  const newAnnotation = {
62
- text: 'New Annotation',
63
- snapToNearestPoint: false,
55
+ text: 'New annotation',
56
+ anchorMode: 'fixed',
64
57
  fontSize: 16,
65
58
  bezier: 10,
66
59
  show: {
@@ -84,20 +77,12 @@ const PanelAnnotate: React.FC<PanelProps> = props => {
84
77
  subject: true,
85
78
  label: true
86
79
  },
87
- seriesKey: '',
80
+ // seriesKey and dataX are only set when switching to data mode
88
81
  x: 50,
89
- y: Number(newSvgDims?.[1] / 2),
90
- xKey:
91
- config.xAxis.type === 'date'
92
- ? new Date(config?.data?.[0]?.[config.xAxis.dataKey]).getTime()
93
- : config.xAxis.type === 'categorical'
94
- ? '1/15/2016'
95
- : '',
96
- yKey: '',
82
+ y: 50,
97
83
  dx: 20,
98
84
  dy: -20,
99
85
  opacity: '100',
100
- savedDimensions: newSvgDims,
101
86
  connectionType: 'line'
102
87
  }
103
88
 
@@ -163,6 +148,59 @@ const PanelAnnotate: React.FC<PanelProps> = props => {
163
148
  onChange={e => handleAnnotationUpdate(e.target.value, 'text', index)}
164
149
  />
165
150
  </label>
151
+
152
+ {visSupportsDataAnnotations() && (
153
+ <Select
154
+ label='Position Mode:'
155
+ value={annotation.anchorMode || 'fixed'}
156
+ options={[
157
+ { value: 'fixed', label: 'Fixed position' },
158
+ { value: 'data', label: 'Snap to data' }
159
+ ]}
160
+ section='annotations'
161
+ subsection={null}
162
+ fieldName='anchorMode'
163
+ updateField={(section, subsection, fieldName, value) => {
164
+ const updatedAnnotations = _.cloneDeep(config?.annotations)
165
+ updatedAnnotations[index].anchorMode = value
166
+
167
+ // When switching to data mode, ensure seriesKey and dataX are initialized
168
+ if (value === 'data') {
169
+ if (!updatedAnnotations[index].seriesKey) {
170
+ updatedAnnotations[index].seriesKey = config.series?.[0]?.dataKey || ''
171
+ }
172
+ if (!updatedAnnotations[index].dataX) {
173
+ updatedAnnotations[index].dataX = transformedData?.[0]?.[config.xAxis.dataKey] || ''
174
+ }
175
+ }
176
+
177
+ updateConfig({
178
+ ...config,
179
+ annotations: updatedAnnotations
180
+ })
181
+ }}
182
+ />
183
+ )}
184
+
185
+ {visSupportsDataAnnotations() && annotation.anchorMode === 'data' && (
186
+ <Select
187
+ label='Series:'
188
+ value={annotation.seriesKey || config.series?.[0]?.dataKey || ''}
189
+ options={config.series.map(s => s.dataKey)}
190
+ section='annotations'
191
+ subsection={null}
192
+ fieldName='seriesKey'
193
+ updateField={(section, subsection, fieldName, value) => {
194
+ const updatedAnnotations = _.cloneDeep(config?.annotations)
195
+ updatedAnnotations[index].seriesKey = value || config.series?.[0]?.dataKey
196
+ updateConfig({
197
+ ...config,
198
+ annotations: updatedAnnotations
199
+ })
200
+ }}
201
+ />
202
+ )}
203
+
166
204
  <label>
167
205
  Opacity
168
206
  <br />
@@ -8,18 +8,24 @@ import WarningImage from '../../../../images/warning.svg'
8
8
 
9
9
  // contexts
10
10
  import ConfigContext from '../../../../ConfigContext'
11
+ import { useEditorPanelContext } from '../../EditorPanelContext'
11
12
 
12
13
  // types
13
14
  import { type ChartContext } from '../../../../types/ChartContext'
14
15
  import { type PanelProps } from '../PanelProps'
15
16
 
16
- import { AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
17
+ import {
18
+ AccordionItem,
19
+ AccordionItemHeading,
20
+ AccordionItemPanel,
21
+ AccordionItemButton
22
+ } from 'react-accessible-accordion'
17
23
 
18
24
  const ForestPlotSettings: FC<PanelProps> = ({ name }) => {
19
25
  const { config, rawData: unfilteredData, updateConfig } = useContext<ChartContext>(ConfigContext)
26
+ const { getColumns } = useEditorPanelContext()
20
27
  if (config.visualizationType !== 'Forest Plot') return
21
28
 
22
- // todo: get from editor context?
23
29
  const enforceRestrictions = updatedConfig => {
24
30
  if (updatedConfig.orientation === 'horizontal') {
25
31
  updatedConfig.labels = false
@@ -33,31 +39,6 @@ const ForestPlotSettings: FC<PanelProps> = ({ name }) => {
33
39
  }
34
40
  }
35
41
 
36
- // todo: get from editor context?
37
- const getColumns = (filter = true) => {
38
- let columns = {}
39
- unfilteredData.forEach(row => {
40
- Object.keys(row).forEach(columnName => (columns[columnName] = true))
41
- })
42
-
43
- if (filter) {
44
- Object.keys(columns).forEach(key => {
45
- if (
46
- (config.series && config.series.filter(series => series.dataKey === key).length > 0) ||
47
- (config.confidenceKeys && Object.keys(config.confidenceKeys).includes(key))
48
- /*
49
- TODO: Resolve errors when config keys exist, but have no value
50
- Proposal: (((confidenceUpper && confidenceLower) || confidenceUpper || confidenceLower) && Object.keys(config.confidenceKeys).includes(key))
51
- */
52
- ) {
53
- delete columns[key]
54
- }
55
- })
56
- }
57
-
58
- return Object.keys(columns)
59
- }
60
-
61
42
  // todo: editor context?
62
43
  const updateField = (section, subsection, fieldName, newValue) => {
63
44
  if (section === 'boxplot' && subsection === 'legend') {
@@ -151,7 +132,9 @@ const ForestPlotSettings: FC<PanelProps> = ({ name }) => {
151
132
  <AccordionItemHeading>
152
133
  <AccordionItemButton>
153
134
  {name}
154
- {(!config.forestPlot.estimateField || !config.forestPlot.upper || !config.forestPlot.lower) && <WarningImage width='25' className='warning-icon' />}
135
+ {(!config.forestPlot.estimateField || !config.forestPlot.upper || !config.forestPlot.lower) && (
136
+ <WarningImage width='25' className='warning-icon' />
137
+ )}
155
138
  </AccordionItemButton>
156
139
  </AccordionItemHeading>
157
140
  <AccordionItemPanel>
@@ -201,14 +184,22 @@ const ForestPlotSettings: FC<PanelProps> = ({ name }) => {
201
184
  <Tooltip.Content>
202
185
  <p>
203
186
  Linear - Typically used for continuous outcomes. Line of no effect is positioned on 0 (zero) <br />
204
- <br /> Logarithmic - Typically used for binary outcomes such as risk ratios and odds ratios. Line of no effect is positioned on 1.
187
+ <br /> Logarithmic - Typically used for binary outcomes such as risk ratios and odds ratios. Line of
188
+ no effect is positioned on 1.
205
189
  </p>
206
190
  </Tooltip.Content>
207
191
  </Tooltip>
208
192
  }
209
193
  />
210
194
 
211
- <TextField type='text' value={config.forestPlot?.title || ''} updateField={updateField} section='forestPlot' fieldName='title' label='Plot Title' />
195
+ <TextField
196
+ type='text'
197
+ value={config.forestPlot?.title || ''}
198
+ updateField={updateField}
199
+ section='forestPlot'
200
+ fieldName='title'
201
+ label='Plot Title'
202
+ />
212
203
 
213
204
  <br />
214
205
  <hr />
@@ -317,7 +308,14 @@ const ForestPlotSettings: FC<PanelProps> = ({ name }) => {
317
308
  </span>
318
309
  </label>
319
310
 
320
- <CheckBox value={config.forestPlot?.lineOfNoEffect?.show || false} section='forestPlot' subsection='lineOfNoEffect' fieldName='show' label='Show Line of No Effect' updateField={updateField} />
311
+ <CheckBox
312
+ value={config.forestPlot?.lineOfNoEffect?.show || false}
313
+ section='forestPlot'
314
+ subsection='lineOfNoEffect'
315
+ fieldName='show'
316
+ label='Show Line of No Effect'
317
+ updateField={updateField}
318
+ />
321
319
 
322
320
  <br />
323
321
  <hr />
@@ -400,13 +398,37 @@ const ForestPlotSettings: FC<PanelProps> = ({ name }) => {
400
398
  />
401
399
  </label>
402
400
 
403
- <TextField type='number' min={20} max={45} value={config.forestPlot.rowHeight ? config.forestPlot.rowHeight : 10} updateField={updateField} section='forestPlot' fieldName='rowHeight' label='Row Height' placeholder='10' />
401
+ <TextField
402
+ type='number'
403
+ min={20}
404
+ max={45}
405
+ value={config.forestPlot.rowHeight ? config.forestPlot.rowHeight : 10}
406
+ updateField={updateField}
407
+ section='forestPlot'
408
+ fieldName='rowHeight'
409
+ label='Row Height'
410
+ placeholder='10'
411
+ />
404
412
  <br />
405
413
  <hr />
406
414
  <br />
407
415
  <h4>Labels Settings</h4>
408
- <TextField type='text' value={config.forestPlot?.leftLabel || ''} updateField={updateField} section='forestPlot' fieldName='leftLabel' label='Left Label' />
409
- <TextField type='text' value={config.forestPlot?.rightLabel || ''} updateField={updateField} section='forestPlot' fieldName='rightLabel' label='Right Label' />
416
+ <TextField
417
+ type='text'
418
+ value={config.forestPlot?.leftLabel || ''}
419
+ updateField={updateField}
420
+ section='forestPlot'
421
+ fieldName='leftLabel'
422
+ label='Left Label'
423
+ />
424
+ <TextField
425
+ type='text'
426
+ value={config.forestPlot?.rightLabel || ''}
427
+ updateField={updateField}
428
+ section='forestPlot'
429
+ fieldName='rightLabel'
430
+ label='Right Label'
431
+ />
410
432
 
411
433
  <br />
412
434
  <hr />
@@ -206,6 +206,86 @@ const PanelGeneral: FC<PanelProps> = props => {
206
206
  options={['Below Bar', 'On Date/Category Axis']}
207
207
  />
208
208
  )}
209
+ {visualizationType === 'Horizon Chart' && (
210
+ <>
211
+ <TextField
212
+ type='number'
213
+ value={config.horizon?.numLayers || 4}
214
+ section='horizon'
215
+ fieldName='numLayers'
216
+ label='Number of Layers'
217
+ updateField={updateField}
218
+ min={1}
219
+ max={9}
220
+ onBlur={e => {
221
+ const parsed = Number(e.target.value)
222
+ if (!isNaN(parsed)) {
223
+ const value = Math.round(parsed)
224
+ const clamped = Math.min(9, Math.max(1, value))
225
+ if (clamped !== parsed) {
226
+ updateField('horizon', null, 'numLayers', clamped)
227
+ }
228
+ }
229
+ }}
230
+ tooltip={
231
+ <Tooltip style={{ textTransform: 'none' }}>
232
+ <Tooltip.Target>
233
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
234
+ </Tooltip.Target>
235
+ <Tooltip.Content>
236
+ <p>
237
+ The number of layers determines how many "bands" the horizon chart is divided into. The optimal
238
+ number of layers may depend on the data range and the desired level of detail.
239
+ </p>
240
+ <hr />
241
+ <p>Valid range: 1-9. Defaults to 4 layers.</p>
242
+ </Tooltip.Content>
243
+ </Tooltip>
244
+ }
245
+ />
246
+ <TextField
247
+ type='number'
248
+ value={config.horizon?.bandGap ?? 15}
249
+ section='horizon'
250
+ fieldName='bandGap'
251
+ label='Band Gap'
252
+ updateField={updateField}
253
+ tooltip={
254
+ <Tooltip style={{ textTransform: 'none' }}>
255
+ <Tooltip.Target>
256
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
257
+ </Tooltip.Target>
258
+ <Tooltip.Content>
259
+ <p>The vertical spacing (in pixels) between each series row in the horizon chart.</p>
260
+ <hr />
261
+ <p>Defaults to 15 pixels.</p>
262
+ </Tooltip.Content>
263
+ </Tooltip>
264
+ }
265
+ />
266
+ <TextField
267
+ type='number'
268
+ value={config.horizon?.bottomPadding ?? 15}
269
+ section='horizon'
270
+ fieldName='bottomPadding'
271
+ label='Bottom Padding'
272
+ updateField={updateField}
273
+ tooltip={
274
+ <Tooltip style={{ textTransform: 'none' }}>
275
+ <Tooltip.Target>
276
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
277
+ </Tooltip.Target>
278
+ <Tooltip.Content>
279
+ <p>The padding (in pixels) below the last series row in the horizon chart.</p>
280
+ <hr />
281
+ <p>Defaults to 15 pixels.</p>
282
+ </Tooltip.Content>
283
+ </Tooltip>
284
+ }
285
+ />
286
+ </>
287
+ )}
288
+
209
289
  {visHasNumbersOnBars() ? (
210
290
  <CheckBox
211
291
  value={config.yAxis.displayNumbersOnBar}
@@ -316,36 +396,37 @@ const PanelGeneral: FC<PanelProps> = props => {
316
396
  />
317
397
  </>
318
398
  )}
319
-
320
- <CheckBox
321
- tooltip={
322
- <Tooltip style={{ textTransform: 'none' }}>
323
- <Tooltip.Target>
324
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
325
- </Tooltip.Target>
326
- <Tooltip.Content>
327
- {config.visualizationSubType === 'stacked' && (
328
- <p>
329
- We do not recommend using stacked vertical/horizontal bar charts for missing data. If you choose to
330
- proceed, selecting this option will display 'N/A' in the tooltip hover and data table (e.g. nothing
331
- will display in chart).
332
- </p>
333
- )}
334
- {config.visualizationSubType !== 'stacked' && (
335
- <p>
336
- Selecting this option will display 'N/A' on the Date/Category Axis, in the tooltip hover, and in the
337
- data table to indicate missing or undefined data values.
338
- </p>
339
- )}
340
- </Tooltip.Content>
341
- </Tooltip>
342
- }
343
- value={config.general.showMissingDataLabel}
344
- section='general'
345
- fieldName='showMissingDataLabel'
346
- label='Display "Missing Data" Label'
347
- updateField={updateField}
348
- />
399
+ {config.visualizationType !== 'Warming Stripes' && config.visualizationType !== 'Radar' && (
400
+ <CheckBox
401
+ tooltip={
402
+ <Tooltip style={{ textTransform: 'none' }}>
403
+ <Tooltip.Target>
404
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
405
+ </Tooltip.Target>
406
+ <Tooltip.Content>
407
+ {config.visualizationSubType === 'stacked' && (
408
+ <p>
409
+ We do not recommend using stacked vertical/horizontal bar charts for missing data. If you choose
410
+ to proceed, selecting this option will display 'N/A' in the tooltip hover and data table (e.g.
411
+ nothing will display in chart).
412
+ </p>
413
+ )}
414
+ {config.visualizationSubType !== 'stacked' && (
415
+ <p>
416
+ Selecting this option will display 'N/A' on the Date/Category Axis, in the tooltip hover, and in
417
+ the data table to indicate missing or undefined data values.
418
+ </p>
419
+ )}
420
+ </Tooltip.Content>
421
+ </Tooltip>
422
+ }
423
+ value={config.general.showMissingDataLabel}
424
+ section='general'
425
+ fieldName='showMissingDataLabel'
426
+ label='Display "Missing Data" Label'
427
+ updateField={updateField}
428
+ />
429
+ )}
349
430
 
350
431
  {visualizationType === 'Pie' && (
351
432
  <Select fieldName='pieType' label='Pie Chart Type' updateField={updateField} options={['Regular', 'Donut']} />
@@ -389,6 +470,32 @@ const PanelGeneral: FC<PanelProps> = props => {
389
470
  </Tooltip>
390
471
  }
391
472
  />
473
+
474
+ <Select
475
+ value={config.titleStyle}
476
+ fieldName='titleStyle'
477
+ label='Title Style'
478
+ updateField={updateField}
479
+ options={[
480
+ { value: 'small', label: 'Small (h3)' },
481
+ { value: 'large', label: 'Large (h2)' },
482
+ { value: 'legacy', label: 'Legacy' }
483
+ ]}
484
+ tooltip={
485
+ <Tooltip style={{ textTransform: 'none' }}>
486
+ <Tooltip.Target>
487
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
488
+ </Tooltip.Target>
489
+ <Tooltip.Content>
490
+ <p>
491
+ Choose the visual style for the title. Consider heading order on your page when selecting the title
492
+ style. For 508 reasons, ensure your page follows a proper heading order.
493
+ </p>
494
+ </Tooltip.Content>
495
+ </Tooltip>
496
+ }
497
+ />
498
+
392
499
  <CheckBox value={config.showTitle} fieldName='showTitle' label='Show Title' updateField={updateField} />
393
500
 
394
501
  {visSupportsSuperTitle() && (
@@ -311,6 +311,8 @@ const PanelPatternSettings: FC<PanelProps> = props => {
311
311
  updateConfig(updatedConfig)
312
312
  }
313
313
 
314
+ if (config.visualizationType === 'Warming Stripes' || config.visualizationType === 'Radar') return
315
+
314
316
  return (
315
317
  <AccordionItem>
316
318
  <AccordionItemHeading>