@cdc/chart 4.24.5 → 4.24.9

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 (87) hide show
  1. package/dist/cdcchart.js +44197 -38258
  2. package/examples/cases-year.json +13379 -0
  3. package/examples/feature/annotations/index.json +542 -0
  4. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +76 -15
  5. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +5 -5
  6. package/examples/xaxis.json +493 -0
  7. package/index.html +20 -10
  8. package/package.json +5 -4
  9. package/src/CdcChart.tsx +462 -172
  10. package/src/_stories/Chart.Legend.Gradient.tsx +19 -0
  11. package/src/_stories/Chart.stories.tsx +18 -171
  12. package/src/_stories/ChartAnnotation.stories.tsx +32 -0
  13. package/src/_stories/_mock/annotation_category_mock.json +473 -0
  14. package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
  15. package/{examples/feature/line/line-chart.json → src/_stories/_mock/annotation_date-time_mock.json} +150 -69
  16. package/src/_stories/_mock/legend.gradient_mock.json +236 -0
  17. package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
  18. package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
  19. package/src/_stories/_mock/lollipop.json +171 -0
  20. package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
  21. package/src/components/Annotations/components/AnnotationDraggable.tsx +207 -0
  22. package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
  23. package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
  24. package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
  25. package/src/components/Annotations/components/AnnotationList.tsx +42 -0
  26. package/src/components/Annotations/components/findNearestDatum.ts +138 -0
  27. package/src/components/Annotations/components/helpers/index.tsx +46 -0
  28. package/src/components/Annotations/index.tsx +13 -0
  29. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
  30. package/src/components/AreaChart/components/AreaChart.jsx +1 -1
  31. package/src/components/Axis/Categorical.Axis.tsx +145 -0
  32. package/src/components/BarChart/components/BarChart.Horizontal.tsx +47 -44
  33. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +0 -1
  34. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -14
  35. package/src/components/BarChart/components/BarChart.Vertical.tsx +67 -30
  36. package/src/components/BarChart/helpers/index.ts +91 -0
  37. package/src/components/BrushChart.tsx +205 -0
  38. package/src/components/EditorPanel/EditorPanel.tsx +1794 -403
  39. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +320 -0
  40. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +282 -18
  41. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +43 -8
  42. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -4
  43. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +4 -13
  44. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  45. package/src/components/EditorPanel/components/panels.scss +4 -0
  46. package/src/components/EditorPanel/editor-panel.scss +35 -3
  47. package/src/components/EditorPanel/{useEditorPermissions.js → useEditorPermissions.ts} +105 -17
  48. package/src/components/Legend/Legend.Component.tsx +185 -194
  49. package/src/components/Legend/Legend.Suppression.tsx +146 -0
  50. package/src/components/Legend/Legend.tsx +21 -5
  51. package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
  52. package/src/components/Legend/helpers/index.ts +35 -0
  53. package/src/components/LegendWrapper.tsx +26 -0
  54. package/src/components/LineChart/LineChartProps.ts +1 -15
  55. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +103 -0
  56. package/src/components/LineChart/components/LineChart.Circle.tsx +47 -8
  57. package/src/components/LineChart/helpers.ts +72 -14
  58. package/src/components/LineChart/index.tsx +117 -42
  59. package/src/components/LinearChart.jsx +179 -136
  60. package/src/components/LinearChart.tsx +1366 -0
  61. package/src/components/PairedBarChart.jsx +9 -9
  62. package/src/components/PieChart/PieChart.tsx +75 -18
  63. package/src/components/Sankey/index.tsx +89 -30
  64. package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
  65. package/src/components/Sparkline/components/SparkLine.tsx +2 -2
  66. package/src/components/ZoomBrush.tsx +90 -44
  67. package/src/data/initial-state.js +25 -7
  68. package/src/helpers/handleChartTabbing.ts +8 -0
  69. package/src/helpers/isConvertLineToBarGraph.ts +4 -0
  70. package/src/hooks/{useBarChart.js → useBarChart.ts} +2 -40
  71. package/src/hooks/useColorScale.ts +1 -1
  72. package/src/hooks/useLegendClasses.ts +68 -0
  73. package/src/hooks/useMinMax.ts +12 -7
  74. package/src/hooks/useScales.ts +58 -26
  75. package/src/hooks/useTooltip.tsx +135 -25
  76. package/src/scss/DataTable.scss +2 -1
  77. package/src/scss/main.scss +128 -28
  78. package/src/types/ChartConfig.ts +83 -10
  79. package/src/types/ChartContext.ts +14 -4
  80. package/tests-examples/helpers/testZeroValue.test.ts +30 -0
  81. package/LICENSE +0 -201
  82. package/src/components/BrushHandle.jsx +0 -17
  83. package/src/components/LineChart/index.scss +0 -1
  84. package/src/helpers/filterData.ts +0 -18
  85. package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
  86. package/src/hooks/useLegendClasses.js +0 -31
  87. /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
@@ -6,6 +6,7 @@ import { FC, useContext, useEffect, useRef, useState } from 'react'
6
6
  import ConfigContext from '../ConfigContext'
7
7
  import { ScaleLinear, ScaleBand } from 'd3-scale'
8
8
  import { isDateScale } from '@cdc/core/helpers/cove/date'
9
+ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
9
10
 
10
11
  interface Props {
11
12
  xScaleBrush: ScaleLinear<number, number>
@@ -14,8 +15,11 @@ interface Props {
14
15
  yMax: number
15
16
  }
16
17
  const ZoomBrush: FC<Props> = props => {
17
- const { tableData, config, parseDate, formatDate, setBrushConfig } = useContext(ConfigContext)
18
+ const { tableData, config, parseDate, formatDate, setBrushConfig, getTextWidth, dashboardConfig } = useContext(ConfigContext)
19
+ const sharedFilters = dashboardConfig?.dashboard?.sharedFilters ?? []
20
+ const isDashboardFilters = sharedFilters?.length > 0
18
21
  const { fontSize } = useBarChart()
22
+ const [showTooltip, setShowTooltip] = useState(false)
19
23
  const [brushKey, setBrushKey] = useState(0)
20
24
  const brushRef = useRef(null)
21
25
  const radius = 15
@@ -24,16 +28,16 @@ const ZoomBrush: FC<Props> = props => {
24
28
  startPosition: 0,
25
29
  endPosition: 0,
26
30
  startValue: '',
27
- endValue: ''
31
+ endValue: '',
32
+ xMax: props.xMax
28
33
  })
29
34
 
30
35
  const initialPosition = {
31
36
  start: { x: 0 },
32
37
  end: { x: props.xMax }
33
38
  }
34
-
35
39
  const style = {
36
- fill: '#AFA6A5 ',
40
+ fill: '#474747',
37
41
  stroke: 'blue',
38
42
  fillOpacity: 0.8,
39
43
  strokeOpacity: 0,
@@ -41,18 +45,19 @@ const ZoomBrush: FC<Props> = props => {
41
45
  }
42
46
 
43
47
  const onBrushChange = event => {
44
- if (!event || !event.xValues) return
45
- const { xValues } = event
48
+ setShowTooltip(false)
49
+ const filteredValues = event?.xValues?.filter(val => val !== undefined)
50
+ if (filteredValues?.length === 0) return
46
51
 
47
52
  const dataKey = config.xAxis?.dataKey
48
53
 
49
- const brushedData = tableData.filter(item => xValues.includes(item[dataKey]))
54
+ const brushedData = tableData.filter(item => filteredValues.includes(item[dataKey]))
50
55
 
51
- const endValue = xValues
56
+ const endValue = filteredValues
52
57
  .slice()
53
58
  .reverse()
54
59
  .find(item => item !== undefined)
55
- const startValue = xValues.find(item => item !== undefined)
60
+ const startValue = filteredValues.find(item => item !== undefined)
56
61
 
57
62
  const formatIfDate = value => (isDateScale(config.runtime.xAxis) ? formatDate(parseDate(value)) : value)
58
63
 
@@ -61,7 +66,8 @@ const ZoomBrush: FC<Props> = props => {
61
66
  startPosition: brushRef.current?.state.start.x,
62
67
  endPosition: brushRef.current?.state.end.x,
63
68
  endValue: formatIfDate(endValue),
64
- startValue: formatIfDate(startValue)
69
+ startValue: formatIfDate(startValue),
70
+ xMax: props.xMax
65
71
  }))
66
72
 
67
73
  setBrushConfig(prev => {
@@ -72,9 +78,9 @@ const ZoomBrush: FC<Props> = props => {
72
78
  }
73
79
  })
74
80
  }
75
-
81
+ // reset brush if brush is off.
76
82
  useEffect(() => {
77
- if (!config.brush.active) {
83
+ if (!config.brush?.active) {
78
84
  setBrushKey(prevKey => prevKey + 1)
79
85
  setBrushConfig({
80
86
  data: [],
@@ -82,10 +88,15 @@ const ZoomBrush: FC<Props> = props => {
82
88
  isBrushing: false
83
89
  })
84
90
  }
85
- }, [config.brush.active])
91
+ }, [config.brush?.active])
92
+
93
+ // reset brush if filters or exclusions are ON each time
86
94
 
87
95
  useEffect(() => {
88
- if (config.filters?.some(filter => filter.active)) {
96
+ const isFiltersActive = config.filters?.some(filter => filter.active)
97
+ const isExclusionsActive = config.exclusions?.active
98
+
99
+ if ((isFiltersActive || isExclusionsActive || isDashboardFilters) && config.brush?.active) {
89
100
  setBrushKey(prevKey => prevKey + 1)
90
101
  setBrushConfig(prev => {
91
102
  return {
@@ -101,12 +112,12 @@ const ZoomBrush: FC<Props> = props => {
101
112
  data: []
102
113
  }
103
114
  })
104
- }, [config.filters])
115
+ }, [config.filters, config.exclusions, config.brush?.active, isDashboardFilters])
105
116
 
106
117
  const calculateTop = (): number => {
107
118
  const tickRotation = Number(config.xAxis.tickRotation) > 0 ? Number(config.xAxis.tickRotation) : 0
108
119
  let top = 0
109
- const offSet = 20
120
+ const offSet = 30
110
121
  if (!config.xAxis.label) {
111
122
  if (!config.isResponsiveTicks && tickRotation) {
112
123
  top = Number(tickRotation + config.xAxis.tickWidthMax) / 1.6
@@ -123,7 +134,7 @@ const ZoomBrush: FC<Props> = props => {
123
134
  }
124
135
  if (config.xAxis.label) {
125
136
  if (!config.isResponsiveTicks && tickRotation) {
126
- top = Number(config.xAxis.tickWidthMax + tickRotation)
137
+ top = Number(config.xAxis.tickWidthMax + tickRotation) + offSet
127
138
  }
128
139
 
129
140
  if (!config.isResponsiveTicks && !tickRotation) {
@@ -131,7 +142,7 @@ const ZoomBrush: FC<Props> = props => {
131
142
  }
132
143
 
133
144
  if (config.isResponsiveTicks && !tickRotation) {
134
- top = Number(config.dynamicMarginTop ? config.dynamicMarginTop : config.xAxis.labelOffset) + offSet
145
+ top = Number(config.dynamicMarginTop ? config.dynamicMarginTop : config.xAxis.labelOffset) + offSet * 2
135
146
  }
136
147
  }
137
148
 
@@ -142,30 +153,56 @@ const ZoomBrush: FC<Props> = props => {
142
153
  }
143
154
 
144
155
  return (
145
- <Group display={config.brush.active ? 'block' : 'none'} top={Number(props.yMax) + calculateTop()} left={Number(config.runtime.yAxis.size)} pointerEvents='fill'>
146
- <rect fill='#F7F7F7 ' width={props.xMax} height={config.brush.height} rx={radius} />
147
- <Brush
148
- key={brushKey}
149
- renderBrushHandle={props => <BrushHandle textProps={textProps} fontSize={fontSize[config.fontSize]} {...props} isBrushing={brushRef.current?.state.isBrushing} />}
150
- innerRef={brushRef}
151
- useWindowMoveEvents={true}
152
- selectedBoxStyle={style}
153
- xScale={props.xScaleBrush}
154
- yScale={props.yScale}
155
- width={props.xMax}
156
- resizeTriggerAreas={['left', 'right']}
157
- height={config.brush.height}
158
- handleSize={8}
159
- brushDirection='horizontal'
160
- initialBrushPosition={initialPosition}
161
- onChange={onBrushChange}
162
- />
163
- </Group>
156
+ <ErrorBoundary component='Brush Chart'>
157
+ <Group
158
+ onMouseMove={() => {
159
+ // show tooltip only once before brush started
160
+ if (textProps.startPosition === 0 && (textProps.endPosition === 0 || textProps.endPosition === props.xMax)) {
161
+ setShowTooltip(true)
162
+ }
163
+ }}
164
+ onMouseLeave={() => setShowTooltip(false)}
165
+ display={config.brush?.active ? 'block' : 'none'}
166
+ top={Number(props.yMax) + calculateTop()}
167
+ left={Number(config.runtime.yAxis.size)}
168
+ pointerEvents='fill'
169
+ >
170
+ <rect fill='#949494' width={props.xMax} height={config.brush.height} rx={radius} />
171
+ <Brush
172
+ key={brushKey}
173
+ disableDraggingOverlay={true}
174
+ renderBrushHandle={props => (
175
+ <BrushHandle
176
+ left={Number(config.runtime.yAxis.size)}
177
+ showTooltip={showTooltip}
178
+ getTextWidth={getTextWidth}
179
+ pixelDistance={textProps.endPosition - textProps.startPosition}
180
+ textProps={textProps}
181
+ fontSize={fontSize[config.fontSize]}
182
+ {...props}
183
+ isBrushing={brushRef.current?.state.isBrushing}
184
+ />
185
+ )}
186
+ innerRef={brushRef}
187
+ useWindowMoveEvents={true}
188
+ selectedBoxStyle={style}
189
+ xScale={props.xScaleBrush}
190
+ yScale={props.yScale}
191
+ width={props.xMax}
192
+ resizeTriggerAreas={['left', 'right']}
193
+ height={config.brush.height}
194
+ handleSize={8}
195
+ brushDirection='horizontal'
196
+ initialBrushPosition={initialPosition}
197
+ onChange={onBrushChange}
198
+ />
199
+ </Group>
200
+ </ErrorBoundary>
164
201
  )
165
202
  }
166
203
 
167
204
  const BrushHandle = props => {
168
- const { x, isBrushActive, isBrushing, className, textProps } = props
205
+ const { x, isBrushActive, isBrushing, className, textProps, fontSize, showTooltip, left, getTextWidth } = props
169
206
  const pathWidth = 8
170
207
  if (!isBrushActive) {
171
208
  return null
@@ -174,14 +211,23 @@ const BrushHandle = props => {
174
211
  const isLeft = className.includes('left')
175
212
  const transform = isLeft ? 'scale(-1, 1)' : 'translate(0,0)'
176
213
  const textAnchor = isLeft ? 'end' : 'start'
214
+ const tooltipText = isLeft ? ` Drag edges to focus on a specific segment ` : ''
215
+ const textWidth = getTextWidth(tooltipText, `normal ${fontSize / 1.1}px sans-serif`)
177
216
 
178
217
  return (
179
- <Group left={x + pathWidth / 2} top={-2}>
180
- <Text pointerEvents='visiblePainted' dominantBaseline='hanging' x={0} verticalAnchor='start' textAnchor={textAnchor} fontSize={props.fontSize / 1.4} dy={10} y={15}>
181
- {isLeft ? textProps.startValue : textProps.endValue}
182
- </Text>
183
- <path cursor='ew-resize' d='M0.5,10A6,6 0 0 1 6.5,16V14A6,6 0 0 1 0.5,20ZM2.5,18V12M4.5,18V12' fill={!isBrushing ? '#000' : '#297EF1'} strokeWidth='1' transform={transform}></path>
184
- </Group>
218
+ <>
219
+ {showTooltip && (
220
+ <Text x={(Number(textProps.xMax) - textWidth) / 2} dy={-12} pointerEvents='visiblePainted' fontSize={fontSize / 1.1}>
221
+ {tooltipText}
222
+ </Text>
223
+ )}
224
+ <Group left={x + pathWidth / 2} top={-2}>
225
+ <Text pointerEvents='visiblePainted' dominantBaseline='hanging' x={isLeft ? 55 : -50} y={25} verticalAnchor='start' textAnchor={textAnchor} fontSize={fontSize / 1.4}>
226
+ {isLeft ? textProps.startValue : textProps.endValue}
227
+ </Text>
228
+ <path cursor='ew-resize' d='M0.5,10A6,6 0 0 1 6.5,16V14A6,6 0 0 1 0.5,20ZM2.5,18V12M4.5,18V12' fill={'#297EF1'} strokeWidth='1' transform={transform}></path>
229
+ </Group>
230
+ </>
185
231
  )
186
232
  }
187
233
 
@@ -1,4 +1,6 @@
1
1
  export default {
2
+ annotations: [],
3
+ allowLineToBarGraph: undefined,
2
4
  type: 'chart',
3
5
  debugSvg: false,
4
6
  chartMessage: {
@@ -22,7 +24,11 @@ export default {
22
24
  tipRounding: 'top',
23
25
  isResponsiveTicks: false,
24
26
  general: {
25
- showDownloadButton: false
27
+ annotationDropdownText: 'Annotations',
28
+ showDownloadButton: false,
29
+ showMissingDataLabel: true,
30
+ showSuppressedSymbol: true,
31
+ hideNullValue: true
26
32
  },
27
33
  padding: {
28
34
  left: 5,
@@ -53,7 +59,10 @@ export default {
53
59
  axisPadding: 0,
54
60
  scalePadding: 10,
55
61
  tickRotation: 0,
56
- anchors: []
62
+ anchors: [],
63
+ shoMissingDataLabel: true,
64
+ showMissingDataLine: true,
65
+ categories: []
57
66
  },
58
67
  boxplot: {
59
68
  plots: [],
@@ -116,9 +125,7 @@ export default {
116
125
  labelOffset: 65,
117
126
  axisPadding: 200,
118
127
  target: 0,
119
- maxTickRotation: 0,
120
- showSuppressedSymbol: true,
121
- showSuppressedLine: true
128
+ maxTickRotation: 0
122
129
  },
123
130
  table: {
124
131
  label: 'Data Table',
@@ -131,7 +138,9 @@ export default {
131
138
  indexLabel: '',
132
139
  download: false,
133
140
  showVertical: true,
134
- dateDisplayFormat: ''
141
+ dateDisplayFormat: '',
142
+ showMissingDataLabel: true,
143
+ showSuppressedSymbol: true
135
144
  },
136
145
  orientation: 'vertical',
137
146
  color: 'pinkpurple',
@@ -151,11 +160,20 @@ export default {
151
160
  dynamicLegendItemLimit: 5,
152
161
  dynamicLegendItemLimitMessage: 'Dynamic Legend Item Limit Hit.',
153
162
  dynamicLegendChartMessage: 'Select Options from the Legend',
163
+ label: '',
154
164
  lineMode: false,
155
165
  verticalSorted: false,
156
166
  highlightOnHover: false,
157
167
  hideSuppressedLabels: false,
158
- seriesHighlight: []
168
+ hideSuppressionLink: false,
169
+ seriesHighlight: [],
170
+ style: 'circles',
171
+ subStyle: 'linear blocks',
172
+ tickRotation: '',
173
+ hideBorder: {
174
+ side: false,
175
+ topBottom: true
176
+ }
159
177
  },
160
178
  brush: {
161
179
  height: 25,
@@ -0,0 +1,8 @@
1
+ import { ChartConfig } from './../types/ChartConfig'
2
+
3
+ export const handleChartTabbing = (config: ChartConfig, legendId: string) => {
4
+ if (!config) return `dataTableSection`
5
+ if (!config.legend?.hide) return legendId
6
+ if (config?.title) return `dataTableSection__${config.title.replace(/\s/g, '')}`
7
+ return `dataTableSection`
8
+ }
@@ -0,0 +1,4 @@
1
+ export const isConvertLineToBarGraph = (visualizationType, filteredData, allowLineToBarGraph) => {
2
+ const convertLineToBarGraph = visualizationType === 'Line' && filteredData?.length < 3 && allowLineToBarGraph ? true : false
3
+ return convertLineToBarGraph
4
+ }
@@ -29,7 +29,7 @@ export const useBarChart = () => {
29
29
  updateConfig({
30
30
  ...config,
31
31
  yAxis: {
32
- ...config,
32
+ ...config.yAxis,
33
33
  labelPlacement: 'Below Bar'
34
34
  }
35
35
  })
@@ -210,43 +210,6 @@ export const useBarChart = () => {
210
210
  if (config.legend.highlightOnHover && config.legend.behavior === 'highlight') setSeriesHighlight([])
211
211
  }
212
212
 
213
- const composeSuppressionBars = ({ bar }) => {
214
- const suppresedBarHeight = config.xAxis.showSuppressedLine ? 3 : 0
215
- const ASTERISK = 'Asterisk'
216
- const getIconPadding = symbol => (String(symbol).includes(ASTERISK) ? -5 : -suppresedBarHeight * 3)
217
- const getVerticalAnchor = symbol => {
218
- return String(symbol).includes(ASTERISK) ? 'middle' : 'end'
219
- }
220
- const getIconSize = (symbol, barWidth) => {
221
- switch (symbol) {
222
- case ASTERISK:
223
- return barWidth * 1.2
224
- case 'Double ' + ASTERISK:
225
- return barWidth
226
- default:
227
- return barWidth / 1.5
228
- }
229
- }
230
-
231
- function shouldSuppressBar() {
232
- const isSuppressed = config.preliminaryData.some(pd => {
233
- const selectedSuppressionColumn = !pd.column || pd.column === bar.key
234
- const isValueMatch = String(pd.value) === String(bar.value) && pd.value !== '' && pd.type === 'suppression'
235
-
236
- return isValueMatch && selectedSuppressionColumn
237
- })
238
-
239
- return isSuppressed && config.xAxis.showSuppressedSymbol
240
- }
241
-
242
- return {
243
- suppresedBarHeight,
244
- getIconSize,
245
- getIconPadding,
246
- getVerticalAnchor,
247
- isSuppressed: shouldSuppressBar()
248
- }
249
- }
250
213
  return {
251
214
  isHorizontal,
252
215
  barBorderWidth,
@@ -273,7 +236,6 @@ export const useBarChart = () => {
273
236
  hoveredBar,
274
237
  setHoveredBar,
275
238
  onMouseOverBar,
276
- onMouseLeaveBar,
277
- composeSuppressionBars
239
+ onMouseLeaveBar
278
240
  }
279
241
  }
@@ -30,7 +30,7 @@ const useColorScale = () => {
30
30
  })
31
31
  }
32
32
  if (visualizationType === 'Bar' && visualizationSubType === 'regular' && series?.length === 1 && legend?.colorCode) {
33
- const set = new Set(data.map(d => d[legend.colorCode]))
33
+ const set = new Set(data?.map(d => d[legend.colorCode]))
34
34
  colorScale = scaleOrdinal({
35
35
  domain: [...set],
36
36
  range: generatePalette([...set].length)
@@ -0,0 +1,68 @@
1
+ type ConfigType = {
2
+ legend: {
3
+ position: 'left' | 'bottom' | 'top' | 'right'
4
+ singleRow?: boolean
5
+ reverseLabelOrder?: boolean
6
+ verticalSorted?: boolean
7
+ hideBorder: {
8
+ side?: boolean
9
+ topBottom?: boolean
10
+ }
11
+ }
12
+ }
13
+
14
+ const useLegendClasses = (config: ConfigType) => {
15
+ const containerClasses = ['legend-container']
16
+ const innerClasses = ['legend-container__inner']
17
+
18
+ // Handle legend positioning
19
+ switch (config.legend.position) {
20
+ case 'left':
21
+ containerClasses.push('left')
22
+ break
23
+ case 'right':
24
+ containerClasses.push('right')
25
+ break
26
+ case 'bottom':
27
+ containerClasses.push('bottom')
28
+ innerClasses.push('double-column', 'bottom')
29
+ break
30
+ case 'top':
31
+ containerClasses.push('top')
32
+ innerClasses.push('double-column', 'top')
33
+ break
34
+ }
35
+
36
+ // Handle single row configuration for 'bottom' and 'top' positions
37
+ if (['bottom', 'top'].includes(config.legend.position) && config.legend.singleRow) {
38
+ innerClasses.push('single-row')
39
+ }
40
+
41
+ // Reverse label order
42
+ if (config.legend.reverseLabelOrder) {
43
+ innerClasses.push('d-flex', 'flex-column-reverse')
44
+ }
45
+
46
+ // Vertical sorting for 'bottom' and 'top' positions
47
+ if (['bottom', 'top'].includes(config.legend.position) && config.legend.verticalSorted) {
48
+ innerClasses.push('vertical-sorted')
49
+ }
50
+
51
+ // Configure border classes
52
+ if (
53
+ config.legend.hideBorder.side &&
54
+ (['right', 'left'].includes(config.legend.position) || !config.legend.position)
55
+ ) {
56
+ containerClasses.push('no-border')
57
+ }
58
+
59
+ if (config.legend.hideBorder.topBottom && ['top', 'bottom'].includes(config.legend.position)) {
60
+ containerClasses.push('no-border')
61
+ }
62
+
63
+ return {
64
+ containerClasses,
65
+ innerClasses
66
+ }
67
+ }
68
+ export default useLegendClasses
@@ -1,5 +1,6 @@
1
1
  import { ChartConfig } from '../types/ChartConfig'
2
2
  import _ from 'lodash'
3
+ import { isConvertLineToBarGraph } from '../helpers/isConvertLineToBarGraph'
3
4
 
4
5
  type UseMinMaxProps = {
5
6
  /** config - standard chart config */
@@ -30,13 +31,17 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
30
31
  return { min, max }
31
32
  }
32
33
 
34
+ const checkLineToBarGraph = () => {
35
+ return isConvertLineToBarGraph(config.visualizationType, data, config.allowLineToBarGraph)
36
+ }
37
+
33
38
  const { visualizationType, series } = config
34
39
  const { max: enteredMaxValue, min: enteredMinValue } = config.runtime.yAxis
35
40
  const minRequiredCIPadding = 1.15 // regardless of Editor if CI data, there must be 10% padding added
36
-
41
+ const isLogarithmicAxis = config.yAxis.type === 'logarithmic'
37
42
  // do validation bafore applying t0 charts
38
43
  const isMaxValid = existPositiveValue ? enteredMaxValue >= maxValue : enteredMaxValue >= 0
39
- const isMinValid = config.useLogScale ? enteredMinValue >= 0 : (enteredMinValue <= 0 && minValue >= 0) || (enteredMinValue <= minValue && minValue < 0)
44
+ const isMinValid = isLogarithmicAxis ? enteredMinValue >= 0 : (enteredMinValue <= 0 && minValue >= 0) || (enteredMinValue <= minValue && minValue < 0)
40
45
 
41
46
  min = enteredMinValue && isMinValid ? enteredMinValue : minValue
42
47
  max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
@@ -126,10 +131,10 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
126
131
  }
127
132
 
128
133
  // this should not apply to bar charts if there is negative CI data
129
- if ((visualizationType === 'Bar' || (visualizationType === 'Combo' && !isAllLine)) && min > 0) {
134
+ if ((visualizationType === 'Bar' || checkLineToBarGraph() || (visualizationType === 'Combo' && !isAllLine)) && min > 0) {
130
135
  min = 0
131
136
  }
132
- if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && min < 0) {
137
+ if ((config.visualizationType === 'Bar' || checkLineToBarGraph() || (config.visualizationType === 'Combo' && !isAllLine)) && min < 0) {
133
138
  min = min * 1.1
134
139
  }
135
140
 
@@ -138,7 +143,7 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
138
143
  min = 0
139
144
  }
140
145
  if (enteredMinValue) {
141
- const isMinValid = config.useLogScale ? enteredMinValue >= 0 && enteredMinValue < minValue : enteredMinValue < minValue
146
+ const isMinValid = isLogarithmicAxis ? enteredMinValue >= 0 && enteredMinValue < minValue : enteredMinValue < minValue
142
147
  min = enteredMinValue && isMinValid ? enteredMinValue : minValue
143
148
  }
144
149
  }
@@ -148,8 +153,8 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
148
153
  min = enteredMinValue && isMinValid ? enteredMinValue : 0
149
154
  }
150
155
 
151
- if (config.visualizationType === 'Line') {
152
- const isMinValid = config.useLogScale ? enteredMinValue >= 0 && enteredMinValue < minValue : enteredMinValue < minValue
156
+ if (config.visualizationType === 'Line' && !checkLineToBarGraph()) {
157
+ const isMinValid = isLogarithmicAxis ? enteredMinValue >= 0 && enteredMinValue < minValue : enteredMinValue < minValue
153
158
  // update minValue for (0) Suppression points
154
159
  const suppressedMinValue = tableData?.some((dataItem, index) => {
155
160
  return config.preliminaryData?.some(pd => {