@cdc/chart 4.25.3 → 4.25.5-1

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 (72) hide show
  1. package/dist/cdcchart.js +36051 -36995
  2. package/index.html +2 -1
  3. package/package.json +22 -27
  4. package/src/CdcChartComponent.tsx +10 -10
  5. package/src/_stories/Chart.CI.stories.tsx +10 -0
  6. package/src/_stories/Chart.DynamicSeries.stories.tsx +68 -49
  7. package/src/_stories/Chart.stories.tsx +7 -0
  8. package/{examples/private/line-issue.json → src/_stories/_mock/barchart_labels.mock.json} +150 -35
  9. package/src/_stories/_mock/dynamic_series_bar_config.json +1 -1
  10. package/src/_stories/_mock/dynamic_series_suppression_mock.json +610 -0
  11. package/src/components/Annotations/components/AnnotationDropdown.tsx +2 -2
  12. package/src/components/AreaChart/components/AreaChart.jsx +33 -5
  13. package/src/components/Axis/Categorical.Axis.tsx +2 -2
  14. package/src/components/BarChart/components/BarChart.Horizontal.tsx +50 -40
  15. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +18 -8
  16. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +19 -8
  17. package/src/components/BarChart/components/BarChart.Vertical.tsx +47 -30
  18. package/src/components/BarChart/components/{BarChart.jsx → BarChart.tsx} +23 -5
  19. package/src/components/BarChart/components/context.tsx +20 -2
  20. package/src/components/BarChart/helpers/getBarHeights.ts +47 -0
  21. package/src/components/BarChart/helpers/index.ts +5 -2
  22. package/src/components/BarChart/helpers/tests/getBarHeights.test.ts +83 -0
  23. package/src/{hooks → components/BarChart/helpers}/useBarChart.ts +9 -46
  24. package/src/components/BoxPlot/BoxPlot.tsx +2 -1
  25. package/src/components/DeviationBar.jsx +2 -1
  26. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +34 -34
  27. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +51 -25
  28. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +10 -3
  29. package/src/components/EditorPanel/useEditorPermissions.ts +1 -4
  30. package/src/components/ForestPlot/ForestPlot.tsx +2 -2
  31. package/src/components/Legend/Legend.Component.tsx +1 -1
  32. package/src/components/Legend/Legend.Suppression.tsx +12 -22
  33. package/src/components/Legend/helpers/createFormatLabels.tsx +28 -0
  34. package/src/components/LineChart/LineChartProps.ts +3 -1
  35. package/src/components/LineChart/components/LineChart.Circle.tsx +72 -119
  36. package/src/components/LineChart/helpers.ts +133 -56
  37. package/src/components/LineChart/index.tsx +107 -53
  38. package/src/components/LinearChart.tsx +40 -89
  39. package/src/components/PairedBarChart.jsx +3 -2
  40. package/src/components/PieChart/PieChart.tsx +71 -91
  41. package/src/components/ScatterPlot/ScatterPlot.jsx +5 -0
  42. package/src/components/Sparkline/components/SparkLine.tsx +80 -18
  43. package/src/components/ZoomBrush.tsx +4 -4
  44. package/src/data/initial-state.js +3 -1
  45. package/src/helpers/countNumOfTicks.ts +1 -1
  46. package/src/helpers/dataHelpers.ts +23 -2
  47. package/src/helpers/sizeHelpers.ts +1 -1
  48. package/src/hooks/useMinMax.ts +21 -28
  49. package/src/hooks/useRightAxis.ts +4 -2
  50. package/src/hooks/useScales.ts +10 -6
  51. package/src/hooks/useTooltip.tsx +204 -203
  52. package/src/index.jsx +2 -2
  53. package/src/scss/main.scss +13 -6
  54. package/src/types/ChartConfig.ts +5 -0
  55. package/LICENSE +0 -201
  56. package/examples/private/DEV-8850-2.json +0 -493
  57. package/examples/private/DEV-9822.json +0 -574
  58. package/examples/private/DEV-9840.json +0 -553
  59. package/examples/private/DEV-9850-3.json +0 -461
  60. package/examples/private/chart.json +0 -1084
  61. package/examples/private/ci_formatted.json +0 -202
  62. package/examples/private/ci_issue.json +0 -3016
  63. package/examples/private/completed.json +0 -634
  64. package/examples/private/dem-data-long.csv +0 -20
  65. package/examples/private/dem-data-long.json +0 -36
  66. package/examples/private/demographic_data.csv +0 -157
  67. package/examples/private/demographic_data.json +0 -2654
  68. package/examples/private/demographic_dynamic.json +0 -443
  69. package/examples/private/demographic_standard.json +0 -560
  70. package/examples/private/ehdi.json +0 -29939
  71. package/examples/private/not-loading.json +0 -360
  72. package/examples/private/test.json +0 -493
@@ -9,6 +9,7 @@ interface BarConfigProps {
9
9
  config: { [key: string]: any }
10
10
  barWidth: number
11
11
  isVertical: boolean
12
+ yAxisValue: number
12
13
  }
13
14
 
14
15
  // Function to create bar width based on suppression status and missing data label
@@ -18,7 +19,8 @@ export const getBarConfig = ({
18
19
  defaultBarWidth,
19
20
  config,
20
21
  barWidth,
21
- isVertical
22
+ isVertical,
23
+ yAxisValue
22
24
  }: BarConfigProps) => {
23
25
  const heightMini = 3 /// height of small bars aka suppressed/NA/Zero valued
24
26
  let barHeight = defaultBarHeight
@@ -97,7 +99,8 @@ export const getBarConfig = ({
97
99
  return labelFits && isVertical ? label : !isVertical ? label : ''
98
100
  }
99
101
  }
100
- return { barWidthHorizontal, barHeight, isSuppressed, showMissingDataLabel, getBarY, getAbsentDataLabel }
102
+ const absentDataLabel = getAbsentDataLabel(yAxisValue)
103
+ return { barWidthHorizontal, barHeight, isSuppressed, showMissingDataLabel, getBarY, absentDataLabel }
101
104
  }
102
105
 
103
106
  export const testZeroValue = value => {
@@ -0,0 +1,83 @@
1
+ import { getHorizontalBarHeights } from '../getBarHeights'
2
+
3
+ describe('getHorizontalBarHeights', () => {
4
+ it('should calculate bar heights for non-stacked horizontal bars', () => {
5
+ const config = {
6
+ orientation: 'horizontal',
7
+ visualizationSubType: 'grouped',
8
+ runtime: { seriesKeys: ['A', 'B'] },
9
+ barHeight: 10,
10
+ barSpace: 5,
11
+ yAxis: { labelPlacement: 'Above Bar' }
12
+ }
13
+
14
+ const bars = [{ index: 0 }, { index: 1 }]
15
+
16
+ const result = getHorizontalBarHeights(config, bars)
17
+
18
+ expect(result).toEqual([
19
+ { index: 0, y: 0, height: 20 },
20
+ { index: 1, y: 25, height: 20 }
21
+ ])
22
+ })
23
+
24
+ it('should calculate bar heights for stacked horizontal bars', () => {
25
+ const config = {
26
+ orientation: 'horizontal',
27
+ visualizationSubType: 'stacked',
28
+ barHeight: 15,
29
+ barSpace: 5,
30
+ yAxis: { labelPlacement: 'Above Bar' }
31
+ }
32
+
33
+ const bars = [{ index: 0 }, { index: 1 }]
34
+
35
+ const result = getHorizontalBarHeights(config, bars)
36
+
37
+ expect(result).toEqual([
38
+ { index: 0, y: 0, height: 15 },
39
+ { index: 1, y: 20, height: 15 }
40
+ ])
41
+ })
42
+
43
+ it('should include label space when labelPlacement is Below Bar', () => {
44
+ const config = {
45
+ orientation: 'horizontal',
46
+ visualizationSubType: 'grouped',
47
+ runtime: { seriesKeys: ['A'] },
48
+ barHeight: 10,
49
+ barSpace: 5,
50
+ yAxis: { labelPlacement: 'Below Bar' }
51
+ }
52
+
53
+ const bars = [{ index: 0 }, { index: 1 }]
54
+
55
+ const result = getHorizontalBarHeights(config, bars)
56
+
57
+ expect(result).toEqual([
58
+ { index: 0, y: 0, height: 10 },
59
+ { index: 1, y: 37, height: 10 }
60
+ ])
61
+ })
62
+
63
+ it('should handle lollipop chart bar heights', () => {
64
+ const config = {
65
+ orientation: 'horizontal',
66
+ visualizationSubType: 'grouped',
67
+ runtime: { seriesKeys: ['A', 'B', 'C'] },
68
+ isLollipopChart: true,
69
+ lollipopSize: 'medium',
70
+ barSpace: 5,
71
+ yAxis: { labelPlacement: 'Above Bar' }
72
+ }
73
+
74
+ const bars = [{ index: 0 }, { index: 1 }]
75
+
76
+ const result = getHorizontalBarHeights(config, bars)
77
+
78
+ expect(result).toEqual([
79
+ { index: 0, y: 0, height: 18 },
80
+ { index: 1, y: 23, height: 18 }
81
+ ])
82
+ })
83
+ })
@@ -1,12 +1,12 @@
1
1
  import React, { useContext, useEffect, useState } from 'react'
2
- import ConfigContext, { ChartDispatchContext } from '../ConfigContext'
2
+ import { ChartDispatchContext } from '../../../ConfigContext'
3
3
  import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
4
- import { appFontSize } from '@cdc/core/helpers/cove/fontSettings'
5
- export const useBarChart = () => {
6
- const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate, setSeriesHighlight, seriesHighlight } =
7
- useContext(ConfigContext)
8
- const dispatch = useContext(ChartDispatchContext)
4
+ import { APP_FONT_SIZE } from '@cdc/core/helpers/constants'
5
+
6
+ export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, configContext) => {
7
+ const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate, seriesHighlight } = configContext
9
8
  const { orientation } = config
9
+ const dispatch = useContext(ChartDispatchContext)
10
10
  const [hoveredBar, setHoveredBar] = useState(null)
11
11
 
12
12
  const isHorizontal = orientation === 'horizontal'
@@ -123,43 +123,6 @@ export const useBarChart = () => {
123
123
  const barColor = palette[barIndex]
124
124
  return barColor
125
125
  }
126
- const updateBars = defaultBars => {
127
- // function updates stacked && regular && lollipop horizontal bars
128
- if (config.visualizationType !== 'Bar' && !isHorizontal) return defaultBars
129
-
130
- const barsArr = [...defaultBars]
131
- let barHeight
132
-
133
- const heights = {
134
- stacked: config.barHeight,
135
- lollipop: lollipopBarWidth
136
- }
137
-
138
- if (!isStacked) {
139
- barHeight = heights[config.isLollipopChart ? 'lollipop' : 'stacked'] * stackCount
140
- } else {
141
- barHeight = heights.stacked
142
- }
143
-
144
- const labelHeight = isLabelBelowBar ? appFontSize * 1.2 : 0
145
- let barSpace = Number(config.barSpace)
146
-
147
- // calculate height of container based height, space and fontSize of labels
148
- let totalHeight = barsArr.length * (barHeight + labelHeight + barSpace)
149
-
150
- if (isHorizontal) {
151
- config.heights.horizontal = totalHeight
152
- }
153
-
154
- // return new updated bars/groupes
155
- return barsArr.map((bar, i) => {
156
- // set bars Y dynamically to handle space between bars
157
- let y = 0
158
- bar.index !== 0 && (y = (barHeight + barSpace + labelHeight) * i)
159
-
160
- return { ...bar, y: y, height: barHeight }
161
- })
162
- }
163
126
 
164
127
  const getHighlightedBarColorByValue = value => {
165
128
  const match = config?.highlightedBarValues.find(item => {
@@ -226,17 +189,18 @@ export const useBarChart = () => {
226
189
  return additionalTooltipItems
227
190
  }
228
191
 
229
- const onMouseOverBar = (categoryValue, barKey) => {
192
+ const onMouseOverBar = (categoryValue, barKey, event, data) => {
230
193
  if (config.legend.highlightOnHover && config.legend.behavior === 'highlight' && barKey) {
231
194
  dispatch({ type: 'SET_SERIES_HIGHLIGHT', payload: [barKey] })
232
195
  }
233
-
196
+ handleTooltipMouseOver(event, data)
234
197
  setHoveredBar(categoryValue)
235
198
  }
236
199
  const onMouseLeaveBar = () => {
237
200
  if (config.legend.highlightOnHover && config.legend.behavior === 'highlight') {
238
201
  dispatch({ type: 'SET_SERIES_HIGHLIGHT', payload: [] })
239
202
  }
203
+ handleTooltipMouseOff()
240
204
  }
241
205
 
242
206
  return {
@@ -256,7 +220,6 @@ export const useBarChart = () => {
256
220
  barStackedSeriesKeys,
257
221
  hasMultipleSeries,
258
222
  applyRadius,
259
- updateBars,
260
223
  assignColorsToValues,
261
224
  getHighlightedBarColorByValue,
262
225
  getHighlightedBarByValue,
@@ -5,6 +5,7 @@ import ConfigContext from '../../ConfigContext'
5
5
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
6
6
  import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
7
7
  import { handleTooltip, createPlots } from './helpers/index'
8
+ import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
8
9
  import _ from 'lodash'
9
10
 
10
11
  const CoveBoxPlot = ({ xScale, yScale, seriesScale }) => {
@@ -15,7 +16,7 @@ const CoveBoxPlot = ({ xScale, yScale, seriesScale }) => {
15
16
  const boxWidth = xScale.bandwidth()
16
17
 
17
18
  const bodyStyles = getComputedStyle(document.body)
18
- const defaultColor = bodyStyles.getPropertyValue('--cool-gray-90').trim()
19
+ const defaultColor = APP_FONT_COLOR
19
20
  const constrainedWidth = Math.min(40, boxWidth)
20
21
  const color_0 = _.get(colorPalettesChart, [config.palette, 0], '#000')
21
22
  const plots = createPlots(data, config)
@@ -6,6 +6,7 @@ import { Text } from '@visx/text'
6
6
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
7
7
  import useIntersectionObserver from '../hooks/useIntersectionObserver'
8
8
  import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
9
+ import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
9
10
  import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
10
11
 
11
12
  export default function DeviationBar({ height, xScale }) {
@@ -178,7 +179,7 @@ export default function DeviationBar({ height, xScale }) {
178
179
  // colors
179
180
  const [leftColor, rightColor] = twoColorPalette[twoColor.palette]
180
181
  const barColor = { left: leftColor, right: rightColor }
181
- const fill = getContrastColor('#000', barColor[barPosition])
182
+ const fill = getContrastColor(APP_FONT_COLOR, barColor[barPosition])
182
183
 
183
184
  let textProps = getTextProps(config.isLollipopChart, textFits, lollipopShapeSize, fill)
184
185
  // tooltips
@@ -113,7 +113,7 @@ const PanelGeneral: FC<PanelProps> = props => {
113
113
  )}
114
114
  </div>
115
115
  )}
116
- {(visualizationType === 'Bar' || visualizationType === 'Combo' || visualizationType === 'Area Chart') && (
116
+ {(visualizationType === 'Bar' || visualizationType === 'Combo') && (
117
117
  <Select
118
118
  value={visualizationSubType || 'Regular'}
119
119
  fieldName='visualizationSubType'
@@ -209,10 +209,8 @@ const PanelGeneral: FC<PanelProps> = props => {
209
209
  <Icon display='question' style={{ marginLeft: '0.5rem' }} />
210
210
  </Tooltip.Target>
211
211
  <Tooltip.Content>
212
- <p>
213
- Recommended set to display for Section 508 compliance.
214
- </p>
215
- <hr/>
212
+ <p>Recommended set to display for Section 508 compliance.</p>
213
+ <hr />
216
214
  <p>
217
215
  Selecting this option will <i> not </i> hide the display of "zero value", "suppressed data", or
218
216
  "missing data" indicators on the chart (if applicable).
@@ -245,35 +243,7 @@ const PanelGeneral: FC<PanelProps> = props => {
245
243
  label='Display "Zero Data" Label'
246
244
  updateField={updateField}
247
245
  />
248
- <CheckBox
249
- tooltip={
250
- <Tooltip style={{ textTransform: 'none' }}>
251
- <Tooltip.Target>
252
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
253
- </Tooltip.Target>
254
- <Tooltip.Content>
255
- {config.visualizationSubType === 'stacked' && (
256
- <p>
257
- We do not recommend using stacked vertical/horizontal bar charts for missing data. If you choose
258
- to proceed, selecting this option will display 'N/A' in the tooltip hover and data table (e.g.
259
- nothing will display in chart).
260
- </p>
261
- )}
262
- {config.visualizationSubType !== 'stacked' && (
263
- <p>
264
- Selecting this option will display 'N/A' on the Date/Category Axis, in the tooltip hover, and in
265
- the data table to indicate missing or undefined data values.
266
- </p>
267
- )}
268
- </Tooltip.Content>
269
- </Tooltip>
270
- }
271
- value={config.general.showMissingDataLabel}
272
- section='general'
273
- fieldName='showMissingDataLabel'
274
- label='Display "Missing Data" Label'
275
- updateField={updateField}
276
- />
246
+
277
247
  <CheckBox
278
248
  display={config.visualizationType === 'Bar' || config.visualizationType === 'Combo'}
279
249
  tooltip={
@@ -329,6 +299,36 @@ const PanelGeneral: FC<PanelProps> = props => {
329
299
  </>
330
300
  )}
331
301
 
302
+ <CheckBox
303
+ tooltip={
304
+ <Tooltip style={{ textTransform: 'none' }}>
305
+ <Tooltip.Target>
306
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
307
+ </Tooltip.Target>
308
+ <Tooltip.Content>
309
+ {config.visualizationSubType === 'stacked' && (
310
+ <p>
311
+ We do not recommend using stacked vertical/horizontal bar charts for missing data. If you choose to
312
+ proceed, selecting this option will display 'N/A' in the tooltip hover and data table (e.g. nothing
313
+ will display in chart).
314
+ </p>
315
+ )}
316
+ {config.visualizationSubType !== 'stacked' && (
317
+ <p>
318
+ Selecting this option will display 'N/A' on the Date/Category Axis, in the tooltip hover, and in the
319
+ data table to indicate missing or undefined data values.
320
+ </p>
321
+ )}
322
+ </Tooltip.Content>
323
+ </Tooltip>
324
+ }
325
+ value={config.general.showMissingDataLabel}
326
+ section='general'
327
+ fieldName='showMissingDataLabel'
328
+ label='Display "Missing Data" Label'
329
+ updateField={updateField}
330
+ />
331
+
332
332
  {visualizationType === 'Pie' && (
333
333
  <Select fieldName='pieType' label='Pie Chart Type' updateField={updateField} options={['Regular', 'Donut']} />
334
334
  )}
@@ -1,4 +1,4 @@
1
- import React, { useContext, useMemo } from 'react'
1
+ import React, { useContext } from 'react'
2
2
  import ConfigContext from '../../../../ConfigContext'
3
3
 
4
4
  // Core
@@ -17,7 +17,7 @@ import {
17
17
  AccordionItemPanel,
18
18
  AccordionItemButton
19
19
  } from 'react-accessible-accordion'
20
- import { Draggable } from '@hello-pangea/dnd'
20
+ import { Draggable, DragDropContext, Droppable } from '@hello-pangea/dnd'
21
21
  import Tooltip from '@cdc/core/components/ui/Tooltip'
22
22
 
23
23
  const SeriesContext = React.createContext({})
@@ -609,9 +609,20 @@ const SeriesButtonRemove = props => {
609
609
  }
610
610
 
611
611
  const SeriesItem = props => {
612
- const { config } = useContext(ConfigContext)
612
+ const { config, updateConfig } = useContext(ConfigContext)
613
613
  const { updateSeries, getColumns } = useContext(SeriesContext)
614
614
  const { series, getItemStyle, sortableItemStyles, chartsWithOptions, index: i } = props
615
+ const legendOrderOptions: { label: string; value: string }[] = [
616
+ { label: 'Order By Data Column', value: 'dataColumn' },
617
+ {
618
+ label: 'Ascending Alphanumeric',
619
+ value: 'asc'
620
+ },
621
+ {
622
+ label: 'Descending Alphanumeric',
623
+ value: 'desc'
624
+ }
625
+ ]
615
626
  const showDynamicCategory =
616
627
  ['Bar', 'Line'].includes(config.visualizationType) &&
617
628
  config.visualizationSubType !== 'Stacked' &&
@@ -646,28 +657,43 @@ const SeriesItem = props => {
646
657
  <AccordionItemPanel>
647
658
  <Series.Input.Name series={series} index={i} />
648
659
  {showDynamicCategory && (
649
- <Select
650
- label='Dynamic Category'
651
- value={series.dynamicCategory}
652
- options={['- Select - ', ...getColumns().filter(col => series.dataKey !== col)]}
653
- updateField={(_section, _subsection, _fieldName, value) => {
654
- if (value === '- Select -') value = ''
655
- updateSeries(i, value, 'dynamicCategory')
656
- }}
657
- tooltip={
658
- <Tooltip style={{ textTransform: 'none' }}>
659
- <Tooltip.Target>
660
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
661
- </Tooltip.Target>
662
- <Tooltip.Content>
663
- <p>
664
- This field is Optional. If you have a dynamic data series you can select the category
665
- field here. You can only add one dynamic category per visualization.
666
- </p>
667
- </Tooltip.Content>
668
- </Tooltip>
669
- }
670
- />
660
+ <>
661
+ <Select
662
+ label='Dynamic Category'
663
+ value={series.dynamicCategory}
664
+ options={['- Select - ', ...getColumns().filter(col => series.dataKey !== col)]}
665
+ updateField={(_section, _subsection, _fieldName, value) => {
666
+ if (value === '- Select -') value = ''
667
+ updateSeries(i, value, 'dynamicCategory')
668
+ }}
669
+ tooltip={
670
+ <Tooltip style={{ textTransform: 'none' }}>
671
+ <Tooltip.Target>
672
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
673
+ </Tooltip.Target>
674
+ <Tooltip.Content>
675
+ <p>
676
+ This field is Optional. If you have a dynamic data series you can select the category
677
+ field here. You can only add one dynamic category per visualization.
678
+ </p>
679
+ </Tooltip.Content>
680
+ </Tooltip>
681
+ }
682
+ />
683
+
684
+ <>
685
+ <Select
686
+ value={config.legend.order}
687
+ options={legendOrderOptions}
688
+ section='legend'
689
+ fieldName='order'
690
+ updateField={(_section, _subsection, _fieldName, value) => {
691
+ updateConfig({ ...config, legend: { ...config.legend, order: value } })
692
+ }}
693
+ label='Dynamic Series Order'
694
+ />
695
+ </>
696
+ </>
671
697
  )}
672
698
  <Series.Input.Weight series={series} index={i} />
673
699
  <Series.Dropdown.SeriesType series={series} index={i} />
@@ -22,6 +22,7 @@ import { useEditorPermissions } from '../../useEditorPermissions.js'
22
22
  import { useEditorPanelContext } from '../../EditorPanelContext.js'
23
23
  import ConfigContext from '../../../../ConfigContext.js'
24
24
  import { PanelProps } from '../PanelProps'
25
+ import { LineChartConfig } from '../../../../types/ChartConfig'
25
26
 
26
27
  const PanelVisual: FC<PanelProps> = props => {
27
28
  const { config, updateConfig, colorPalettes, twoColorPalette } = useContext<ChartContext>(ConfigContext)
@@ -147,9 +148,7 @@ const PanelVisual: FC<PanelProps> = props => {
147
148
  />
148
149
  </Tooltip.Target>
149
150
  <Tooltip.Content>
150
- <p>
151
- Recommended set to display for Section 508 compliance.
152
- </p>
151
+ <p>Recommended set to display for Section 508 compliance.</p>
153
152
  </Tooltip.Content>
154
153
  </Tooltip>
155
154
  }
@@ -222,6 +221,14 @@ const PanelVisual: FC<PanelProps> = props => {
222
221
  updateField={updateField}
223
222
  options={['Same as Line', 'Lighter than Line']}
224
223
  />
224
+ <CheckBox
225
+ value={!(config as LineChartConfig).isolatedDotsSameSize}
226
+ fieldName='isolatedDotsSameSize'
227
+ label='Accentuate isolated data points'
228
+ updateField={(section, subsection, fieldname, value) =>
229
+ updateField(section, subsection, fieldname, !value)
230
+ }
231
+ />
225
232
  </>
226
233
  )}
227
234
  {/* eslint-disable */}
@@ -358,10 +358,7 @@ export const useEditorPermissions = () => {
358
358
 
359
359
  const visSupportsReactTooltip = () => {
360
360
  if (config.yAxis.type === 'categorical') return true
361
- if (
362
- ['Deviation Bar', 'Box Plot', 'Scatter Plot', 'Paired Bar'].includes(visualizationType) ||
363
- (visualizationType === 'Bar' && config.tooltips.singleSeries)
364
- ) {
361
+ if (['Deviation Bar', 'Box Plot', 'Scatter Plot', 'Paired Bar'].includes(visualizationType)) {
365
362
  return true
366
363
  }
367
364
  }
@@ -14,7 +14,7 @@ import { type ChartContext } from '@cdc/chart/src/types/ChartContext'
14
14
 
15
15
  // cdc
16
16
  import ConfigContext from '../../ConfigContext'
17
- import { appFontSize } from '@cdc/core/helpers/cove/fontSettings'
17
+ import { APP_FONT_SIZE } from '@cdc/core/helpers/constants'
18
18
 
19
19
  const ForestPlot = ({
20
20
  xScale,
@@ -254,7 +254,7 @@ const ForestPlot = ({
254
254
  <LinePath
255
255
  data={regressionPoints}
256
256
  x={d => d.x}
257
- y={d => d.y - appFontSize / 2}
257
+ y={d => d.y - APP_FONT_SIZE / 2}
258
258
  stroke='black'
259
259
  strokeWidth={2}
260
260
  fill={'black'}
@@ -56,7 +56,7 @@ const Legend: React.FC<LegendProps> = forwardRef(
56
56
  const { series } = runtime
57
57
 
58
58
  const seriesWithData = getSeriesWithData(config)
59
- const dontFilterLegendItems = !series.length || legend.unified
59
+ const dontFilterLegendItems = !series.length || legend.unified || !seriesWithData.length
60
60
 
61
61
  const isLegendBottom =
62
62
  legend?.position === 'bottom' ||
@@ -1,7 +1,6 @@
1
1
  import React from 'react'
2
2
  import { ChartConfig } from '../../types/ChartConfig'
3
- import Icon from '@cdc/core/components/ui/Icon'
4
- import { Tooltip as ReactTooltip } from 'react-tooltip'
3
+ import RichTooltip from '@cdc/core/components/RichTooltip/RichTooltip'
5
4
  interface LegendProps {
6
5
  config: ChartConfig
7
6
  isLegendBottom: boolean
@@ -35,10 +34,6 @@ const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) =>
35
34
  </div>
36
35
  )
37
36
  )
38
- const handleLinkClick = event => {
39
- // prevent defintion link to change URl
40
- event.preventDefault()
41
- }
42
37
 
43
38
  const renderSuppressedItems = () => {
44
39
  const getStyle = displayGray => {
@@ -121,27 +116,22 @@ const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) =>
121
116
  </React.Fragment>
122
117
  )}
123
118
  {shouldShowSuppressedInfo() && (
124
- <div className='legend-container__outer definition-link'>
125
- <Icon alt='info-icon' display='info' />
119
+ <div className='legend-container__outer link-container'>
126
120
  <p>
127
121
  This chart contains
128
- <a // prettier-ignore
129
- onClick={handleLinkClick}
130
- data-tooltip-content='Data is suppressed to maintain statistical reliability. This occurs when the number of respondents or reported values does not meet the minimum reporting threshold.'
131
- data-tooltip-id='my-tooltip'
132
- href='no-router-link'
133
- >
134
- suppressed data
135
- </a>
122
+ <RichTooltip
123
+ tooltipContent={`Data is
124
+ suppressed to maintain statistical reliability.
125
+ This occurs when the number of respondents or
126
+ reported values does not meet the minimum
127
+ reporting threshold.`}
128
+ linkText='suppressed data'
129
+ href={null}
130
+ tooltipOpacity={config.tooltips.opacity}
131
+ />
136
132
  </p>
137
133
  </div>
138
134
  )}
139
-
140
- <ReactTooltip // prettier-ignore
141
- id='my-tooltip'
142
- variant='light'
143
- style={{ background: `rgba(255,255,255, ${config.tooltips.opacity / 100})`, color: 'black', maxWidth: '100%' }}
144
- />
145
135
  </React.Fragment>
146
136
  )
147
137
  }
@@ -17,8 +17,36 @@ export const createFormatLabels =
17
17
  })
18
18
  : labels
19
19
  const reverseLabels = labels => {
20
+ if (config.series.some(series => series.dynamicCategory)) {
21
+ return orderDynamicLabels(labels)
22
+ }
23
+
20
24
  return config.legend.reverseLabelOrder ? sortVertical(labels).reverse() : sortVertical(labels)
21
25
  }
26
+
27
+ const orderDynamicLabels = labels => {
28
+ // Handle different ordering configurations
29
+ switch (config.legend.order) {
30
+ case 'dataColumn':
31
+ return labels
32
+ case 'asc':
33
+ case 'desc':
34
+ return labels.sort((a, b) => {
35
+ const valA = a.datum || a.text
36
+ const valB = b.datum || b.text
37
+ const numA = parseFloat(valA)
38
+ const numB = parseFloat(valB)
39
+ if (!isNaN(numA) && !isNaN(numB)) {
40
+ return config.legend.order === 'asc' ? numA - numB : numB - numA
41
+ } else {
42
+ return config.legend.order === 'asc' ? valA.localeCompare(valB) : valB.localeCompare(valA)
43
+ }
44
+ })
45
+
46
+ default:
47
+ return labels // Default case to handle any unexpected config.legend.order values
48
+ }
49
+ }
22
50
  const colorCode = config.legend?.colorCode
23
51
  if (visualizationType === 'Deviation Bar') {
24
52
  const [belowColor, aboveColor] = twoColorPalette[config.twoColor.palette]
@@ -29,9 +29,11 @@ export interface StyleProps {
29
29
  handleLineType: Function
30
30
  lineType: string
31
31
  preliminaryData: PreliminaryDataItem[]
32
- seriesKey: 'string'
32
+ seriesKey: string
33
33
  stroke: string
34
34
  strokeWidth: number
35
+ dynamicCategory: string
36
+ originalSeriesKey: string
35
37
  }
36
38
  export interface Style {
37
39
  stroke: string