@cdc/chart 4.25.3 → 4.25.6

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 (86) hide show
  1. package/dist/cdcchart.js +46641 -42561
  2. package/index.html +130 -129
  3. package/package.json +22 -27
  4. package/src/CdcChartComponent.tsx +75 -35
  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 +99 -86
  8. package/src/_stories/ChartPrefixSuffix.stories.tsx +29 -32
  9. package/{examples/private/line-issue.json → src/_stories/_mock/barchart_labels.mock.json} +150 -35
  10. package/src/_stories/_mock/dynamic_series_bar_config.json +1 -1
  11. package/src/_stories/_mock/dynamic_series_suppression_mock.json +610 -0
  12. package/{examples/private/not-loading.json → src/_stories/_mock/pie_calculated_area.json} +152 -95
  13. package/src/components/Annotations/components/AnnotationDropdown.tsx +2 -2
  14. package/src/components/AreaChart/components/AreaChart.jsx +33 -5
  15. package/src/components/Axis/Categorical.Axis.tsx +2 -2
  16. package/src/components/BarChart/components/BarChart.Horizontal.tsx +38 -37
  17. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +18 -8
  18. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +8 -8
  19. package/src/components/BarChart/components/BarChart.Vertical.tsx +47 -36
  20. package/src/components/BarChart/components/{BarChart.jsx → BarChart.tsx} +23 -5
  21. package/src/components/BarChart/components/context.tsx +20 -2
  22. package/src/components/BarChart/helpers/getBarHeights.ts +47 -0
  23. package/src/components/BarChart/helpers/index.ts +5 -2
  24. package/src/components/BarChart/helpers/tests/getBarHeights.test.ts +83 -0
  25. package/src/{hooks → components/BarChart/helpers}/useBarChart.ts +9 -46
  26. package/src/components/BoxPlot/BoxPlot.tsx +2 -1
  27. package/src/components/Brush/BrushChart.tsx +73 -0
  28. package/src/components/Brush/BrushController..tsx +39 -0
  29. package/src/components/DeviationBar.jsx +2 -2
  30. package/src/components/EditorPanel/EditorPanel.tsx +232 -147
  31. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +36 -36
  32. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +52 -25
  33. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +12 -4
  34. package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +8 -0
  35. package/src/components/EditorPanel/useEditorPermissions.ts +5 -5
  36. package/src/components/ForestPlot/ForestPlot.tsx +2 -2
  37. package/src/components/HoverLine/HoverLine.tsx +74 -0
  38. package/src/components/Legend/Legend.Component.tsx +1 -1
  39. package/src/components/Legend/Legend.Suppression.tsx +59 -25
  40. package/src/components/Legend/helpers/createFormatLabels.tsx +28 -0
  41. package/src/components/Legend/helpers/index.ts +1 -1
  42. package/src/components/LineChart/LineChartProps.ts +3 -1
  43. package/src/components/LineChart/components/LineChart.Circle.tsx +72 -119
  44. package/src/components/LineChart/helpers.ts +133 -56
  45. package/src/components/LineChart/index.tsx +106 -55
  46. package/src/components/LinearChart.tsx +178 -198
  47. package/src/components/PairedBarChart.jsx +3 -2
  48. package/src/components/PieChart/PieChart.tsx +127 -102
  49. package/src/components/ScatterPlot/ScatterPlot.jsx +5 -0
  50. package/src/components/Sparkline/components/SparkLine.tsx +80 -18
  51. package/src/data/initial-state.js +11 -6
  52. package/src/helpers/countNumOfTicks.ts +1 -1
  53. package/src/helpers/dataHelpers.ts +23 -2
  54. package/src/helpers/getNewRuntime.ts +35 -0
  55. package/src/helpers/getPiePercent.ts +22 -0
  56. package/src/helpers/getTransformedData.ts +22 -0
  57. package/src/helpers/sizeHelpers.ts +1 -1
  58. package/src/helpers/tests/getNewRuntime.test.ts +82 -0
  59. package/src/helpers/tests/getPiePercent.test.ts +38 -0
  60. package/src/hooks/useMinMax.ts +21 -28
  61. package/src/hooks/useRightAxis.ts +5 -3
  62. package/src/hooks/useScales.ts +15 -6
  63. package/src/hooks/useTooltip.tsx +218 -203
  64. package/src/index.jsx +2 -2
  65. package/src/scss/main.scss +13 -6
  66. package/src/store/chart.actions.ts +2 -6
  67. package/src/store/chart.reducer.ts +23 -23
  68. package/src/types/ChartConfig.ts +11 -3
  69. package/src/types/ChartContext.ts +0 -2
  70. package/examples/private/DEV-8850-2.json +0 -493
  71. package/examples/private/DEV-9822.json +0 -574
  72. package/examples/private/DEV-9840.json +0 -553
  73. package/examples/private/DEV-9850-3.json +0 -461
  74. package/examples/private/chart.json +0 -1084
  75. package/examples/private/ci_formatted.json +0 -202
  76. package/examples/private/ci_issue.json +0 -3016
  77. package/examples/private/completed.json +0 -634
  78. package/examples/private/dem-data-long.csv +0 -20
  79. package/examples/private/dem-data-long.json +0 -36
  80. package/examples/private/demographic_data.csv +0 -157
  81. package/examples/private/demographic_data.json +0 -2654
  82. package/examples/private/demographic_dynamic.json +0 -443
  83. package/examples/private/demographic_standard.json +0 -560
  84. package/examples/private/ehdi.json +0 -29939
  85. package/examples/private/test.json +0 -493
  86. package/src/components/ZoomBrush.tsx +0 -251
@@ -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
  )}
@@ -435,9 +435,9 @@ const PanelGeneral: FC<PanelProps> = props => {
435
435
  {visSupportsFootnotes() && (
436
436
  <TextField
437
437
  type='textarea'
438
- value={config.footnotes}
438
+ value={config.legacyFootnotes}
439
439
  updateField={updateField}
440
- fieldName='footnotes'
440
+ fieldName='legacyFootnotes'
441
441
  label='Footnotes'
442
442
  tooltip={
443
443
  <Tooltip style={{ textTransform: 'none' }}>
@@ -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,13 +609,25 @@ 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' &&
618
629
  !config.series.find(s => s.dynamicCategory && s.dataKey !== series.dataKey)
630
+ const SELECT = '- Select -'
619
631
  return (
620
632
  <Draggable key={series.dataKey} draggableId={`draggableFilter-${series.dataKey}`} index={i}>
621
633
  {(provided, snapshot) => (
@@ -646,28 +658,43 @@ const SeriesItem = props => {
646
658
  <AccordionItemPanel>
647
659
  <Series.Input.Name series={series} index={i} />
648
660
  {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
- />
661
+ <>
662
+ <Select
663
+ label='Dynamic Category'
664
+ value={series.dynamicCategory}
665
+ options={[SELECT, ...getColumns().filter(col => series.dataKey !== col)]}
666
+ updateField={(_section, _subsection, _fieldName, value) => {
667
+ if (value === SELECT) value = ''
668
+ updateSeries(i, value, 'dynamicCategory')
669
+ }}
670
+ tooltip={
671
+ <Tooltip style={{ textTransform: 'none' }}>
672
+ <Tooltip.Target>
673
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
674
+ </Tooltip.Target>
675
+ <Tooltip.Content>
676
+ <p>
677
+ This field is Optional. If you have a dynamic data series you can select the category
678
+ field here. You can only add one dynamic category per visualization.
679
+ </p>
680
+ </Tooltip.Content>
681
+ </Tooltip>
682
+ }
683
+ />
684
+
685
+ <>
686
+ <Select
687
+ value={config.legend.order}
688
+ options={legendOrderOptions}
689
+ section='legend'
690
+ fieldName='order'
691
+ updateField={(_section, _subsection, _fieldName, value) => {
692
+ updateConfig({ ...config, legend: { ...config.legend, order: value } })
693
+ }}
694
+ label='Dynamic Series Order'
695
+ />
696
+ </>
697
+ </>
671
698
  )}
672
699
  <Series.Input.Weight series={series} index={i} />
673
700
  <Series.Dropdown.SeriesType series={series} index={i} />
@@ -22,6 +22,8 @@ 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'
26
+ import './panelVisual.styles.css'
25
27
 
26
28
  const PanelVisual: FC<PanelProps> = props => {
27
29
  const { config, updateConfig, colorPalettes, twoColorPalette } = useContext<ChartContext>(ConfigContext)
@@ -44,7 +46,7 @@ const PanelVisual: FC<PanelProps> = props => {
44
46
  const { twoColorPalettes, sequential, nonSequential, accessibleColors } = useColorPalette(config, updateConfig)
45
47
 
46
48
  const updateColor = (property, _value) => {
47
- console.log('value', _value)
49
+ console.error('value', _value)
48
50
  if (property === 'storyNodeFontColor') {
49
51
  updateConfig({
50
52
  ...config,
@@ -147,9 +149,7 @@ const PanelVisual: FC<PanelProps> = props => {
147
149
  />
148
150
  </Tooltip.Target>
149
151
  <Tooltip.Content>
150
- <p>
151
- Recommended set to display for Section 508 compliance.
152
- </p>
152
+ <p>Recommended set to display for Section 508 compliance.</p>
153
153
  </Tooltip.Content>
154
154
  </Tooltip>
155
155
  }
@@ -222,6 +222,14 @@ const PanelVisual: FC<PanelProps> = props => {
222
222
  updateField={updateField}
223
223
  options={['Same as Line', 'Lighter than Line']}
224
224
  />
225
+ <CheckBox
226
+ value={!(config as LineChartConfig).isolatedDotsSameSize}
227
+ fieldName='isolatedDotsSameSize'
228
+ label='Accentuate isolated data points'
229
+ updateField={(section, subsection, fieldname, value) =>
230
+ updateField(section, subsection, fieldname, !value)
231
+ }
232
+ />
225
233
  </>
226
234
  )}
227
235
  {/* eslint-disable */}
@@ -0,0 +1,8 @@
1
+ .type-chart .sidebar .color-palette button:not(.selected) {
2
+ border: var(--cool-gray-30) 2px solid !important;
3
+
4
+ }
5
+
6
+ .type-chart .sidebar .color-palette button.selected {
7
+ border: black 2px solid !important;
8
+ }
@@ -197,7 +197,7 @@ export const useEditorPermissions = () => {
197
197
  }
198
198
 
199
199
  const visSupportsTooltipLines = () => {
200
- const enabledCharts = ['Combo', 'Forecasting', 'Area Chart', 'Line', 'Bar']
200
+ const enabledCharts = ['Combo', 'Forecasting', 'Area Chart', 'Line', 'Bar', 'Scatter Plot']
201
201
  if (enabledCharts.includes(visualizationType)) return true
202
202
  return false
203
203
  }
@@ -357,10 +357,10 @@ export const useEditorPermissions = () => {
357
357
  }
358
358
 
359
359
  const visSupportsReactTooltip = () => {
360
- if (config.yAxis.type === 'categorical') return true
360
+ if (config.yAxis.type === 'categorical' && config.tooltips.singleSeries) return true
361
361
  if (
362
- ['Deviation Bar', 'Box Plot', 'Scatter Plot', 'Paired Bar'].includes(visualizationType) ||
363
- (visualizationType === 'Bar' && config.tooltips.singleSeries)
362
+ ['Deviation Bar', 'Box Plot', 'Scatter Plot', 'Paired Bar', 'Bar'].includes(visualizationType) &&
363
+ config.tooltips.singleSeries
364
364
  ) {
365
365
  return true
366
366
  }
@@ -384,7 +384,7 @@ export const useEditorPermissions = () => {
384
384
  }
385
385
 
386
386
  const visSupportsYPadding = () => {
387
- return !config.dataFormat.onlyShowTopPrefixSuffix || !config.dataFormat.suffix?.includes(' ')
387
+ return !config.yAxis.inlineLabel || !config.yAxis.inlineLabel?.includes(' ')
388
388
  }
389
389
 
390
390
  const visHasSingleSeriesTooltip = () => {
@@ -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'}
@@ -0,0 +1,74 @@
1
+ import React, { useContext } from 'react'
2
+ import ConfigContext from '../../ConfigContext'
3
+ import { Group } from '@visx/group'
4
+ import { Line } from '@visx/shape'
5
+
6
+ type HoverLineProps = {
7
+ tooltipData?: any // same as @visx/tooltip TooltipData
8
+ xMax?: number
9
+ yMax?: number
10
+ point: { x: number; y: number }
11
+ orientation: 'horizontal' | 'vertical'
12
+ }
13
+
14
+ const HoverLine: React.FC<HoverLineProps> = ({ tooltipData, xMax, yMax, point, orientation }) => {
15
+ const { config } = useContext(ConfigContext)
16
+ const { verticalHoverLine, horizontalHoverLine } = config.visual
17
+ const { visualizationType } = config
18
+ const isScatterPlot = visualizationType === 'Scatter Plot'
19
+
20
+ const isVertical = orientation === 'vertical'
21
+ const isHorizontal = orientation === 'horizontal'
22
+ const showVerticalHoverLine = verticalHoverLine || (verticalHoverLine && isScatterPlot)
23
+ const showHorizontalHoverLine = horizontalHoverLine || (horizontalHoverLine && isScatterPlot)
24
+
25
+ const getX = () => {
26
+ if (point.x > xMax + Number(config.yAxis.size)) return xMax
27
+ if (point.x < config.yAxis.size) return config.yAxis.size
28
+ return point.x
29
+ }
30
+
31
+ const getY = () => {
32
+ if (point.y > yMax) return yMax
33
+ return point.y
34
+ }
35
+ if (isVertical) {
36
+ return (
37
+ showVerticalHoverLine && (
38
+ <Group key={`tooltipLine-vertical${point.y}${point.x}`} className='vertical-tooltip-line'>
39
+ <Line
40
+ from={{ x: getX(), y: 0 }}
41
+ to={{ x: getX(), y: yMax }}
42
+ stroke={'black'}
43
+ strokeWidth={1}
44
+ pointerEvents='none'
45
+ strokeDasharray='5,5'
46
+ className='vertical-tooltip-line'
47
+ />
48
+ </Group>
49
+ )
50
+ )
51
+ }
52
+ if (isHorizontal) {
53
+ return (
54
+ showHorizontalHoverLine && (
55
+ <Group
56
+ key={`tooltipLine-horizontal${point.y}${point.x}`}
57
+ className='horizontal-tooltip-line'
58
+ left={config.yAxis.size ? config.yAxis.size : 0}
59
+ >
60
+ <Line
61
+ from={{ x: 0, y: getY() }}
62
+ to={{ x: xMax, y: getY() }}
63
+ stroke={'black'}
64
+ strokeWidth={1}
65
+ pointerEvents='none'
66
+ strokeDasharray='5,5'
67
+ className='horizontal-tooltip-line'
68
+ />
69
+ </Group>
70
+ )
71
+ )
72
+ }
73
+ }
74
+ export default HoverLine
@@ -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,15 +1,14 @@
1
- import React from 'react'
1
+ import React, { useMemo } 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
8
7
  }
9
8
 
10
9
  const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) => {
11
- const { preliminaryData, visualizationType, visualizationSubType, legend } = config
12
-
10
+ const { preliminaryData, visualizationType, visualizationSubType, legend, data } = config
11
+ const showPiePercent = config.dataFormat.showPiePercent && config.visualizationType === 'Pie'
13
12
  const hasOpenCircleEffects = () =>
14
13
  preliminaryData?.some(pd => pd.label && pd.type === 'effect' && pd.style !== 'Filled Circles') &&
15
14
  ['Line', 'Combo'].includes(visualizationType)
@@ -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 => {
@@ -100,10 +95,31 @@ const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) =>
100
95
  const getLegendContainerClass = () =>
101
96
  legend.singleRow && isLegendBottom ? 'legend-container__inner bottom single-row' : ''
102
97
 
98
+ const sumSeries = (data, seriesKey) => {
99
+ if (!Array.isArray(data)) return 0
100
+
101
+ return data.reduce((total, row) => {
102
+ const raw = row[seriesKey]
103
+ const num = parseFloat(raw)
104
+ return total + (Number.isFinite(num) ? num : 0)
105
+ }, 0)
106
+ }
107
+ const total = sumSeries(data, config.runtime.yAxis.dataKey)
108
+
103
109
  const shouldShowSuppressedInfo = () =>
104
110
  !config.legend.hideSuppressionLink &&
105
111
  config.visualizationSubType !== 'stacked' &&
106
112
  preliminaryData?.some(pd => pd.label && pd.type === 'suppression' && pd.value && (pd?.style || pd.symbol))
113
+ // controls Pie chart Legend for calculated Area
114
+
115
+ const renderCalculatedAreaItems = () => {
116
+ return (
117
+ <div key={'pie-asterisk'} className='legend-preliminary'>
118
+ <span className={'Asterisk'}>{'**'}</span>
119
+ <p>{'Calculated Area'}</p>
120
+ </div>
121
+ )
122
+ }
107
123
 
108
124
  return (
109
125
  <React.Fragment>
@@ -113,6 +129,12 @@ const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) =>
113
129
  <div className={getLegendContainerClass()}>{renderEffectItems()}</div>
114
130
  </React.Fragment>
115
131
  )}
132
+ {showPiePercent && total < 100 && (
133
+ <React.Fragment>
134
+ <hr />
135
+ <div className={getLegendContainerClass()}>{renderCalculatedAreaItems()}</div>
136
+ </React.Fragment>
137
+ )}
116
138
 
117
139
  {shouldShowSuppressedLabels() && (
118
140
  <React.Fragment>
@@ -121,27 +143,39 @@ const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) =>
121
143
  </React.Fragment>
122
144
  )}
123
145
  {shouldShowSuppressedInfo() && (
124
- <div className='legend-container__outer definition-link'>
125
- <Icon alt='info-icon' display='info' />
146
+ <div className='legend-container__outer link-container'>
126
147
  <p>
127
148
  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>
149
+ <RichTooltip
150
+ tooltipContent={`Data is
151
+ suppressed to maintain statistical reliability.
152
+ This occurs when the number of respondents or
153
+ reported values does not meet the minimum
154
+ reporting threshold.`}
155
+ linkText='suppressed data'
156
+ href={null}
157
+ tooltipOpacity={config.tooltips.opacity}
158
+ />
159
+ </p>
160
+ </div>
161
+ )}
162
+ {showPiePercent && (total < 100 || total > 100) && (
163
+ <div className='legend-container__outer link-container'>
164
+ <p>
165
+ {total < 100 ? '** This graph contains a' : 'The sum of percentages in this graph is larger than 100 '}
166
+ <RichTooltip
167
+ tooltipContent={
168
+ total < 100
169
+ ? 'Calculated Areas are used to supplement the pie chart when the sum of the values in the data is less than 100%.'
170
+ : 'Calculated Areas are disabled when the total exceeds 100%.'
171
+ }
172
+ linkText={total < 100 ? 'calculated area' : ''}
173
+ href={null}
174
+ tooltipOpacity={config.tooltips.opacity}
175
+ />
136
176
  </p>
137
177
  </div>
138
178
  )}
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
179
  </React.Fragment>
146
180
  )
147
181
  }
@@ -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]
@@ -36,7 +36,7 @@ export const getMarginBottom = (isLegendBottom, config) => {
36
36
 
37
37
  if (isLegendTop) marginBottom = 27
38
38
 
39
- if (isLegendTop && config.dataFormat?.onlyShowTopPrefixSuffix) marginBottom += 9
39
+ if (isLegendTop && config.yAxis?.inlineLabel) marginBottom += 9
40
40
 
41
41
  if (isLegendBottom) marginBottom += 9
42
42
 
@@ -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