@cdc/chart 4.24.7 → 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 (51) hide show
  1. package/dist/cdcchart.js +40313 -37543
  2. package/examples/cases-year.json +13379 -0
  3. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +76 -15
  4. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +5 -5
  5. package/index.html +17 -8
  6. package/package.json +2 -2
  7. package/src/CdcChart.tsx +383 -133
  8. package/src/_stories/Chart.Legend.Gradient.tsx +19 -0
  9. package/src/_stories/_mock/legend.gradient_mock.json +236 -0
  10. package/src/components/Annotations/components/AnnotationDraggable.tsx +64 -11
  11. package/src/components/Axis/Categorical.Axis.tsx +145 -0
  12. package/src/components/BarChart/components/BarChart.Horizontal.tsx +4 -3
  13. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -1
  14. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +2 -5
  15. package/src/components/BarChart/components/BarChart.Vertical.tsx +17 -8
  16. package/src/components/BarChart/helpers/index.ts +5 -16
  17. package/src/components/BrushChart.tsx +205 -0
  18. package/src/components/EditorPanel/EditorPanel.tsx +1766 -509
  19. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +19 -5
  20. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +190 -37
  21. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +43 -7
  22. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -4
  23. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +1 -11
  24. package/src/components/EditorPanel/editor-panel.scss +16 -3
  25. package/src/components/EditorPanel/{useEditorPermissions.js → useEditorPermissions.ts} +90 -19
  26. package/src/components/Legend/Legend.Component.tsx +185 -193
  27. package/src/components/Legend/Legend.Suppression.tsx +146 -0
  28. package/src/components/Legend/Legend.tsx +21 -5
  29. package/src/components/Legend/helpers/index.ts +33 -3
  30. package/src/components/LegendWrapper.tsx +26 -0
  31. package/src/components/LineChart/LineChartProps.ts +1 -18
  32. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +103 -0
  33. package/src/components/LineChart/components/LineChart.Circle.tsx +47 -8
  34. package/src/components/LineChart/helpers.ts +55 -11
  35. package/src/components/LineChart/index.tsx +113 -38
  36. package/src/components/LinearChart.tsx +1366 -0
  37. package/src/components/PieChart/PieChart.tsx +74 -17
  38. package/src/components/Sankey/index.tsx +22 -16
  39. package/src/components/Sparkline/components/SparkLine.tsx +2 -2
  40. package/src/data/initial-state.js +13 -3
  41. package/src/hooks/useLegendClasses.ts +52 -15
  42. package/src/hooks/useMinMax.ts +4 -4
  43. package/src/hooks/useScales.ts +34 -24
  44. package/src/hooks/useTooltip.tsx +85 -22
  45. package/src/scss/DataTable.scss +2 -1
  46. package/src/scss/main.scss +107 -14
  47. package/src/types/ChartConfig.ts +34 -8
  48. package/src/types/ChartContext.ts +5 -4
  49. package/examples/feature/line/line-chart.json +0 -449
  50. package/src/components/BrushHandle.jsx +0 -17
  51. package/src/components/LineChart/index.scss +0 -1
@@ -33,7 +33,17 @@ type TooltipData = {
33
33
  }
34
34
 
35
35
  const PieChart = props => {
36
- const { transformedData: data, config, colorScale, currentViewport, dimensions, highlight, highlightReset, seriesHighlight, isDraggingAnnotation } = useContext(ConfigContext)
36
+ const {
37
+ transformedData: data,
38
+ config,
39
+ colorScale,
40
+ currentViewport,
41
+ dimensions,
42
+ highlight,
43
+ highlightReset,
44
+ seriesHighlight,
45
+ isDraggingAnnotation
46
+ } = useContext(ConfigContext)
37
47
  const { tooltipData, showTooltip, hideTooltip, tooltipOpen, tooltipLeft, tooltipTop } = useTooltip<TooltipData>()
38
48
  const { handleTooltipMouseOver, handleTooltipMouseOff, TooltipListItem } = useCoveTooltip({
39
49
  xScale: false,
@@ -135,7 +145,18 @@ const PieChart = props => {
135
145
  <>
136
146
  {transitions.map(({ item: arc, props, key }, animatedPieIndex) => {
137
147
  return (
138
- <Group className={arc.data[config.xAxis.dataKey]} key={`${key}-${animatedPieIndex}`} style={{ opacity: config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(arc.data[config.runtime.xAxis.dataKey]) === -1 ? 0.5 : 1 }}>
148
+ <Group
149
+ className={arc.data[config.xAxis.dataKey]}
150
+ key={`${key}-${animatedPieIndex}`}
151
+ style={{
152
+ opacity:
153
+ config.legend.behavior === 'highlight' &&
154
+ seriesHighlight.length > 0 &&
155
+ seriesHighlight.indexOf(arc.data[config.runtime.xAxis.dataKey]) === -1
156
+ ? 0.5
157
+ : 1
158
+ }}
159
+ >
139
160
  <animated.path
140
161
  d={interpolate([props.startAngle, props.endAngle], (startAngle, endAngle) =>
141
162
  path({
@@ -163,7 +184,14 @@ const PieChart = props => {
163
184
  return (
164
185
  <animated.g key={`${key}${i}`}>
165
186
  {hasSpaceForLabel && (
166
- <Text style={{ fill: textColor }} x={centroidX} y={centroidY} dy='.33em' textAnchor='middle' pointerEvents='none'>
187
+ <Text
188
+ style={{ fill: textColor }}
189
+ x={centroidX}
190
+ y={centroidY}
191
+ dy='.33em'
192
+ textAnchor='middle'
193
+ pointerEvents='none'
194
+ >
167
195
  {Math.round((((arc.endAngle - arc.startAngle) * 180) / Math.PI / 360) * 100) + '%'}
168
196
  </Text>
169
197
  )}
@@ -174,17 +202,18 @@ const PieChart = props => {
174
202
  )
175
203
  }
176
204
 
177
- let [width] = dimensions
205
+ let chartWidth = props.parentWidth
206
+ let width = props.parentWidth
178
207
 
179
208
  if (config && config.legend && !config.legend.hide && currentViewport === 'lg') {
180
- width = width * 0.73
209
+ width = Number(chartWidth) * 0.73
181
210
  }
182
211
 
183
212
  const height = config.heights.vertical
184
213
 
185
214
  const radius = Math.min(width, height) / 2
186
215
  const centerY = height / 2
187
- const centerX = width / 2
216
+ const centerX = props.parentWidth / 2
188
217
  const donutThickness = config.pieType === 'Donut' ? 75 : radius
189
218
 
190
219
  useEffect(() => {
@@ -205,10 +234,24 @@ const PieChart = props => {
205
234
 
206
235
  const createLegendLabels = createFormatLabels(config, [], _data, _colorScale)
207
236
 
237
+ const getSvgClasses = () => {
238
+ let classes = ['animated-pie', 'group']
239
+ if (config.animate === false || animatedPie) {
240
+ classes.push('animated')
241
+ }
242
+ return classes.join(' ')
243
+ }
244
+
208
245
  return (
209
246
  <>
210
247
  <ErrorBoundary component='PieChart'>
211
- <svg width={radius * 2} height={height} className={`animated-pie group ${config.animate === false || animatedPie ? 'animated' : ''}`} role='img' aria-label={handleChartAriaLabels(config)}>
248
+ <svg
249
+ width={radius * 2}
250
+ height={height}
251
+ className={getSvgClasses()}
252
+ role='img'
253
+ aria-label={handleChartAriaLabels(config)}
254
+ >
212
255
  <Group top={centerY} left={radius}>
213
256
  {/* prettier-ignore */}
214
257
  <Pie
@@ -223,17 +266,31 @@ const PieChart = props => {
223
266
  </Group>
224
267
  </svg>
225
268
  <div ref={triggerRef} />
226
- {!isDraggingAnnotation && tooltipData && Object.entries(tooltipData.data).length > 0 && tooltipOpen && showTooltip && tooltipData.dataYPosition && tooltipData.dataXPosition && (
227
- <>
228
- <style>{`.tooltip {background-color: rgba(255,255,255, ${config.tooltips.opacity / 100}) !important`}</style>
229
- <TooltipWithBounds key={Math.random()} className={'tooltip cdc-open-viz-module'} left={tooltipLeft} top={tooltipTop}>
230
- <ul>{typeof tooltipData === 'object' && Object.entries(tooltipData.data).map((item, index) => <TooltipListItem item={item} key={index} />)}</ul>
231
- </TooltipWithBounds>
232
- </>
233
- )}
234
- {/* <ReactTooltip id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`} variant='light' arrowColor='rgba(0,0,0,0)' className='tooltip' /> */}
269
+ {!isDraggingAnnotation &&
270
+ tooltipData &&
271
+ Object.entries(tooltipData.data).length > 0 &&
272
+ tooltipOpen &&
273
+ showTooltip &&
274
+ tooltipData.dataYPosition &&
275
+ tooltipData.dataXPosition && (
276
+ <>
277
+ <style>{`.tooltip {background-color: rgba(255,255,255, ${
278
+ config.tooltips.opacity / 100
279
+ }) !important`}</style>
280
+ <TooltipWithBounds
281
+ key={Math.random()}
282
+ className={'tooltip cdc-open-viz-module'}
283
+ left={tooltipLeft}
284
+ top={tooltipTop}
285
+ >
286
+ <ul>
287
+ {typeof tooltipData === 'object' &&
288
+ Object.entries(tooltipData.data).map((item, index) => <TooltipListItem item={item} key={index} />)}
289
+ </ul>
290
+ </TooltipWithBounds>
291
+ </>
292
+ )}
235
293
  </ErrorBoundary>
236
- <LegendComponent config={config} colorScale={_colorScale} seriesHighlight={seriesHighlight} highlight={highlight} highlightReset={highlightReset} currentViewport={currentViewport} formatLabels={createLegendLabels} />
237
294
  </>
238
295
  )
239
296
  }
@@ -28,17 +28,23 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
28
28
 
29
29
  //Tooltip
30
30
  const [tooltipID, setTooltipID] = useState<string>('')
31
+ //Mobile Pop Up
32
+ const [showPopup, setShowPopup] = useState(false)
31
33
 
32
34
  const handleNodeClick = (nodeId: string) => {
33
- setTooltipID(nodeId)
34
- }
35
+ // Store the previous tooltipID
36
+ const previousTooltipID = tooltipID
35
37
 
36
- const clearNodeClick = () => {
37
- setTooltipID('')
38
- }
38
+ // If the previous tooltipID exists, clear it
39
+ if (previousTooltipID) {
40
+ setTooltipID('')
41
+ }
39
42
 
40
- //Mobile Pop Up
41
- const [showPopup, setShowPopup] = useState(false)
43
+ // Update the tooltipID with the new nodeId if it's different from the previous one
44
+ if (previousTooltipID !== nodeId) {
45
+ setTooltipID(nodeId)
46
+ }
47
+ }
42
48
 
43
49
  useEffect(() => {
44
50
  if (window.innerWidth < 768 && window.innerHeight > window.innerWidth) {
@@ -209,7 +215,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
209
215
  fillOpacity={opacityValue}
210
216
  rx={sankeyConfig.rxValue}
211
217
  // todo: move enable tooltips to sankey
212
- data-tooltip-html={data.tooltips && config.enableTooltips ? sankeyToolTip : null}
218
+ data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
213
219
  data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
214
220
  onClick={() => handleNodeClick(node.id)}
215
221
  style={{ pointerEvents: 'visible', cursor: 'pointer' }}
@@ -240,7 +246,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
240
246
  className='node-text'
241
247
  style={{ pointerEvents: 'auto', cursor: 'pointer' }} // Enable pointer events
242
248
  onClick={() => handleNodeClick(node.id)}
243
- data-tooltip-html={data.tooltips && config.enableTooltips ? sankeyToolTip : null}
249
+ data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
244
250
  data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
245
251
  >
246
252
  {(data?.storyNodeText?.find(storyNode => storyNode.StoryNode === node.id) || {}).segmentTextBefore}
@@ -255,7 +261,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
255
261
  textAnchor='start'
256
262
  style={{ pointerEvents: 'auto', cursor: 'pointer' }} // Enable pointer events
257
263
  onClick={() => handleNodeClick(node.id)}
258
- data-tooltip-html={data.tooltips && config.enableTooltips ? sankeyToolTip : null}
264
+ data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
259
265
  data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
260
266
  >
261
267
  {typeof node.value === 'number' ? node.value.toLocaleString() : node.value}
@@ -271,7 +277,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
271
277
  verticalAnchor='end'
272
278
  style={{ pointerEvents: 'auto', cursor: 'pointer' }} // Enable pointer events
273
279
  onClick={() => handleNodeClick(node.id)}
274
- data-tooltip-html={data.tooltips && config.enableTooltips ? sankeyToolTip : null}
280
+ data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
275
281
  data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
276
282
  >
277
283
  {(data?.storyNodeText?.find(storyNode => storyNode.StoryNode === node.id) || {}).segmentTextAfter}
@@ -282,7 +288,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
282
288
  <Text
283
289
  style={{ pointerEvents: 'auto', cursor: 'pointer' }} // Enable pointer events
284
290
  onClick={() => handleNodeClick(node.id)}
285
- data-tooltip-html={data.tooltips && config.enableTooltips ? sankeyToolTip : null}
291
+ data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
286
292
  data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
287
293
  x={node.x0! + textPositionHorizontal}
288
294
  y={(node.y1! + node.y0!) / 2 + textPositionVertical}
@@ -304,7 +310,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
304
310
  textAnchor='start'
305
311
  style={{ pointerEvents: 'auto', cursor: 'pointer' }} // Enable pointer events
306
312
  onClick={() => handleNodeClick(node.id)}
307
- data-tooltip-html={data.tooltips && config.enableTooltips ? sankeyToolTip : null}
313
+ data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
308
314
  data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
309
315
  >
310
316
  <tspan className={classStyle}>{sankeyConfig.nodeValueStyle.textBefore + (typeof node.value === 'number' ? node.value.toLocaleString() : node.value) + sankeyConfig.nodeValueStyle.textAfter}</tspan>
@@ -339,7 +345,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
339
345
  strokeWidth={link.width! + 2}
340
346
  style={{ pointerEvents: 'auto', cursor: 'pointer' }} // Enable pointer events
341
347
  onClick={() => handleNodeClick(link.target.id || null)}
342
- data-tooltip-html={data.tooltips && config.enableTooltips ? sankeyToolTip : null}
348
+ data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
343
349
  data-tooltip-id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`}
344
350
  />
345
351
  )
@@ -376,7 +382,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
376
382
  fillOpacity={opacityValue}
377
383
  rx={sankeyConfig.rxValue}
378
384
  // todo: move enable tooltips to sankey
379
- data-tooltip-html={data.tooltips && config.enableTooltips ? sankeyToolTip : null}
385
+ data-tooltip-html={data.tooltips && config.enableTooltips && tooltipID !== '' ? sankeyToolTip : null}
380
386
  data-tooltip-id={`tooltip`}
381
387
  onClick={() => handleNodeClick(node.id)}
382
388
  style={{ pointerEvents: 'visible', cursor: 'pointer' }}
@@ -467,7 +473,7 @@ const Sankey = ({ width, height, runtime }: SankeyProps) => {
467
473
 
468
474
  {/* ReactTooltip needs to remain even if tooltips are disabled -- it handles when a user clicks off of the node and resets
469
475
  the sankey diagram. When tooltips are disabled this will nothing */}
470
- <ReactTooltip id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`} afterHide={() => clearNodeClick()} events={['click']} place={'bottom'} style={{ backgroundColor: `rgba(238, 238, 238, 1)`, color: 'black', boxShadow: `0 3px 10px rgb(0 0 0 / 0.2)` }} />
476
+ <ReactTooltip id={`cdc-open-viz-tooltip-${runtime.uniqueId}-sankey`} afterHide={() => setTooltipID('')} events={['click']} place={'bottom'} style={{ backgroundColor: `rgba(238, 238, 238, 1)`, color: 'black', boxShadow: `0 3px 10px rgb(0 0 0 / 0.2)` }} />
471
477
  {showPopup && (
472
478
  <div className='popup'>
473
479
  <div className='popup-content'>
@@ -107,10 +107,10 @@ const SparkLine: React.FC<SparkLineProps> = props => {
107
107
  opacity={config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
108
108
  display={config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
109
109
  >
110
- {data.map((d, dataIndex) => {
110
+ {config.labels && data.map((d, dataIndex) => {
111
111
  return (
112
112
  <Group key={`series-${seriesKey}-point-${dataIndex}`}>
113
- <Text display={config.labels ? 'block' : 'none'} x={xScale(getXAxisData(d))} y={yScale(getYAxisData(d, seriesKey))} fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'} textAnchor='middle'>
113
+ <Text x={xScale(getXAxisData(d))} y={yScale(getYAxisData(d, seriesKey))} fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'} textAnchor='middle'>
114
114
  {formatNumber(d[seriesKey])}
115
115
  </Text>
116
116
  </Group>
@@ -28,7 +28,7 @@ export default {
28
28
  showDownloadButton: false,
29
29
  showMissingDataLabel: true,
30
30
  showSuppressedSymbol: true,
31
- showZeroValueDataLabel: true
31
+ hideNullValue: true
32
32
  },
33
33
  padding: {
34
34
  left: 5,
@@ -61,7 +61,8 @@ export default {
61
61
  tickRotation: 0,
62
62
  anchors: [],
63
63
  shoMissingDataLabel: true,
64
- showMissingDataLine: true
64
+ showMissingDataLine: true,
65
+ categories: []
65
66
  },
66
67
  boxplot: {
67
68
  plots: [],
@@ -159,11 +160,20 @@ export default {
159
160
  dynamicLegendItemLimit: 5,
160
161
  dynamicLegendItemLimitMessage: 'Dynamic Legend Item Limit Hit.',
161
162
  dynamicLegendChartMessage: 'Select Options from the Legend',
163
+ label: '',
162
164
  lineMode: false,
163
165
  verticalSorted: false,
164
166
  highlightOnHover: false,
165
167
  hideSuppressedLabels: false,
166
- seriesHighlight: []
168
+ hideSuppressionLink: false,
169
+ seriesHighlight: [],
170
+ style: 'circles',
171
+ subStyle: 'linear blocks',
172
+ tickRotation: '',
173
+ hideBorder: {
174
+ side: false,
175
+ topBottom: true
176
+ }
167
177
  },
168
178
  brush: {
169
179
  height: 25,
@@ -1,31 +1,68 @@
1
- export default function useLegendClasses(config) {
2
- let containerClasses = ['legend-container']
3
- let innerClasses = ['legend-container__inner']
4
-
5
- // Legend Positioning
6
- if (config.legend.position === 'left') {
7
- containerClasses.push('left')
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
+ }
8
11
  }
9
- if (config.legend.position === 'bottom') {
10
- containerClasses.push('bottom')
11
- innerClasses.push('bottom')
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
12
34
  }
13
35
 
14
- if (config.legend.position === 'bottom' && config.legend.singleRow) {
36
+ // Handle single row configuration for 'bottom' and 'top' positions
37
+ if (['bottom', 'top'].includes(config.legend.position) && config.legend.singleRow) {
15
38
  innerClasses.push('single-row')
16
39
  }
17
40
 
18
- // Legend > Item Ordering
41
+ // Reverse label order
19
42
  if (config.legend.reverseLabelOrder) {
20
- innerClasses.push('d-flex')
21
- innerClasses.push('flex-column-reverse')
43
+ innerClasses.push('d-flex', 'flex-column-reverse')
22
44
  }
23
- if (config.legend.position === 'bottom' && config.legend.verticalSorted) {
45
+
46
+ // Vertical sorting for 'bottom' and 'top' positions
47
+ if (['bottom', 'top'].includes(config.legend.position) && config.legend.verticalSorted) {
24
48
  innerClasses.push('vertical-sorted')
25
49
  }
26
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
+
27
63
  return {
28
64
  containerClasses,
29
65
  innerClasses
30
66
  }
31
67
  }
68
+ export default useLegendClasses
@@ -38,10 +38,10 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
38
38
  const { visualizationType, series } = config
39
39
  const { max: enteredMaxValue, min: enteredMinValue } = config.runtime.yAxis
40
40
  const minRequiredCIPadding = 1.15 // regardless of Editor if CI data, there must be 10% padding added
41
-
41
+ const isLogarithmicAxis = config.yAxis.type === 'logarithmic'
42
42
  // do validation bafore applying t0 charts
43
43
  const isMaxValid = existPositiveValue ? enteredMaxValue >= maxValue : enteredMaxValue >= 0
44
- 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)
45
45
 
46
46
  min = enteredMinValue && isMinValid ? enteredMinValue : minValue
47
47
  max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
@@ -143,7 +143,7 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
143
143
  min = 0
144
144
  }
145
145
  if (enteredMinValue) {
146
- const isMinValid = config.useLogScale ? enteredMinValue >= 0 && enteredMinValue < minValue : enteredMinValue < minValue
146
+ const isMinValid = isLogarithmicAxis ? enteredMinValue >= 0 && enteredMinValue < minValue : enteredMinValue < minValue
147
147
  min = enteredMinValue && isMinValid ? enteredMinValue : minValue
148
148
  }
149
149
  }
@@ -154,7 +154,7 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
154
154
  }
155
155
 
156
156
  if (config.visualizationType === 'Line' && !checkLineToBarGraph()) {
157
- const isMinValid = config.useLogScale ? enteredMinValue >= 0 && enteredMinValue < minValue : enteredMinValue < minValue
157
+ const isMinValid = isLogarithmicAxis ? enteredMinValue >= 0 && enteredMinValue < minValue : enteredMinValue < minValue
158
158
  // update minValue for (0) Suppression points
159
159
  const suppressedMinValue = tableData?.some((dataItem, index) => {
160
160
  return config.preliminaryData?.some(pd => {
@@ -3,7 +3,7 @@ import { useContext } from 'react'
3
3
  import ConfigContext from '../ConfigContext'
4
4
  import { ChartConfig } from '../types/ChartConfig'
5
5
  import { ChartContext } from '../types/ChartContext'
6
-
6
+ import * as d3 from 'd3'
7
7
  const scaleTypes = {
8
8
  TIME: 'time',
9
9
  LOG: 'log',
@@ -27,12 +27,10 @@ const useScales = (properties: useScaleProps) => {
27
27
 
28
28
  const { rawData, dimensions } = useContext<ChartContext>(ConfigContext)
29
29
 
30
- const [screenWidth, screenHeight] = dimensions
30
+ const [screenWidth] = dimensions
31
31
  const seriesDomain = config.runtime.barSeriesKeys || config.runtime.seriesKeys
32
32
  const xAxisType = config.runtime.xAxis.type
33
33
  const isHorizontal = config.orientation === 'horizontal'
34
- const getXAxisDataKeys = d => d[config.runtime.originalXAxis.dataKey]
35
- const xAxisDataKeysMapped = data.map(d => getXAxisDataKeys(d))
36
34
 
37
35
  const { visualizationType } = config
38
36
 
@@ -43,7 +41,6 @@ const useScales = (properties: useScaleProps) => {
43
41
  let g1xScale = null
44
42
  let seriesScale = null
45
43
  let xScaleNoPadding = null
46
- let xScaleBrush = null
47
44
  let xScaleAnnotation = scaleLinear({
48
45
  domain: [0, 100],
49
46
  range: [0, xMax]
@@ -52,7 +49,7 @@ const useScales = (properties: useScaleProps) => {
52
49
  // handle Horizontal bars
53
50
  if (isHorizontal) {
54
51
  xScale = composeXScale({ min: min * 1.03, ...properties })
55
- xScale.type = config.useLogScale ? scaleTypes.LOG : scaleTypes.LINEAR
52
+ xScale.type = config.yAxis.type === 'logarithmic' ? scaleTypes.LOG : scaleTypes.LINEAR
56
53
  yScale = getYScaleFunction(xAxisType, xAxisDataMapped)
57
54
  yScale.rangeRound([0, yMax])
58
55
  seriesScale = composeScalePoint(seriesDomain, [0, yMax])
@@ -60,7 +57,6 @@ const useScales = (properties: useScaleProps) => {
60
57
 
61
58
  // handle Vertical bars
62
59
  if (!isHorizontal) {
63
- xScaleBrush = composeScalePoint(xAxisDataKeysMapped, [0, xMax], 0.5)
64
60
  xScale = composeScaleBand(xAxisDataMapped, [0, xMax], 1 - config.barThickness)
65
61
  yScale = composeYScale(properties)
66
62
  seriesScale = composeScaleBand(seriesDomain, [0, xScale.bandwidth()], 0)
@@ -73,8 +69,8 @@ const useScales = (properties: useScaleProps) => {
73
69
  }
74
70
 
75
71
  if (config.xAxis.type === 'date-time') {
76
- let xAxisMin = Math.min(...xAxisDataMapped)
77
- let xAxisMax = Math.max(...xAxisDataMapped)
72
+ let xAxisMin = Math.min(...xAxisDataMapped.map(Number))
73
+ let xAxisMax = Math.max(...xAxisDataMapped.map(Number))
78
74
  xAxisMin -= (config.xAxis.padding ? config.xAxis.padding * 0.01 : 0) * (xAxisMax - xAxisMin)
79
75
  xAxisMax += (config.xAxis.padding ? config.xAxis.padding * 0.01 : 0) * (xAxisMax - xAxisMin)
80
76
  xScale = scaleTime({
@@ -129,7 +125,9 @@ const useScales = (properties: useScaleProps) => {
129
125
  // handle Box plot
130
126
  if (visualizationType === 'Box Plot') {
131
127
  const allOutliers = []
132
- const hasOutliers = config.boxplot.plots.map(b => b.columnOutliers.map(outlier => allOutliers.push(outlier))) && !config.boxplot.hideOutliers
128
+ const hasOutliers =
129
+ config.boxplot.plots.map(b => b.columnOutliers.map(outlier => allOutliers.push(outlier))) &&
130
+ !config.boxplot.hideOutliers
133
131
 
134
132
  // check if outliers are lower
135
133
  if (hasOutliers) {
@@ -215,8 +213,11 @@ const useScales = (properties: useScaleProps) => {
215
213
  if (screenWidth > 480) {
216
214
  if (config.forestPlot.type === 'Linear') {
217
215
  xScale = scaleLinear({
218
- domain: [Math.min(...data.map(d => parseFloat(d[config.forestPlot.lower]))) - xAxisPadding, Math.max(...data.map(d => parseFloat(d[config.forestPlot.upper]))) + xAxisPadding],
219
- range: [leftWidthOffset, dimensions[0] - rightWidthOffset]
216
+ domain: [
217
+ Math.min(...data.map(d => parseFloat(d[config.forestPlot.lower]))) - xAxisPadding,
218
+ Math.max(...data.map(d => parseFloat(d[config.forestPlot.upper]))) + xAxisPadding
219
+ ],
220
+ range: [leftWidthOffset, Number(screenWidth) - rightWidthOffset]
220
221
  })
221
222
  xScale.type = scaleTypes.LINEAR
222
223
  }
@@ -234,7 +235,10 @@ const useScales = (properties: useScaleProps) => {
234
235
  } else {
235
236
  if (config.forestPlot.type === 'Linear') {
236
237
  xScale = scaleLinear({
237
- domain: [Math.min(...data.map(d => parseFloat(d[config.forestPlot.lower]))) - xAxisPadding, Math.max(...data.map(d => parseFloat(d[config.forestPlot.upper]))) + xAxisPadding],
238
+ domain: [
239
+ Math.min(...data.map(d => parseFloat(d[config.forestPlot.lower]))) - xAxisPadding,
240
+ Math.max(...data.map(d => parseFloat(d[config.forestPlot.upper]))) + xAxisPadding
241
+ ],
238
242
  range: [leftWidthOffsetMobile, xMax - rightWidthOffsetMobile],
239
243
  type: scaleTypes.LINEAR
240
244
  })
@@ -255,7 +259,7 @@ const useScales = (properties: useScaleProps) => {
255
259
  }
256
260
  }
257
261
  }
258
- return { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding, xScaleBrush, xScaleAnnotation }
262
+ return { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding, xScaleAnnotation }
259
263
  }
260
264
 
261
265
  export default useScales
@@ -295,31 +299,37 @@ export const getTickValues = (xAxisDataMapped, xScale, num) => {
295
299
  /// helper functions
296
300
  const composeXScale = ({ min, max, xMax, config }) => {
297
301
  // Adjust min value if using logarithmic scale
298
- min = config.useLogScale && min >= 0 && min < 1 ? min + 0.1 : min
302
+ const isLogarithmicAxis = config.yAxis.type === 'logarithmic'
303
+ min = isLogarithmicAxis && min >= 0 && min < 1 ? min + 0.1 : min
299
304
  // Select the appropriate scale function
300
- const scaleFunc = config.useLogScale ? scaleLog : scaleLinear
305
+ const scaleFunc = isLogarithmicAxis ? scaleLog : scaleLinear
301
306
  // Return the configured scale function
302
307
  return scaleFunc({
303
308
  domain: [min, max],
304
309
  range: [0, xMax],
305
- nice: config.useLogScale,
306
- zero: config.useLogScale
310
+ nice: isLogarithmicAxis,
311
+ zero: isLogarithmicAxis
307
312
  })
308
313
  }
309
314
 
310
315
  const composeYScale = ({ min, max, yMax, config, leftMax }) => {
311
316
  // Adjust min value if using logarithmic scale
312
- min = config.useLogScale && min >= 0 && min < 1 ? min + 0.1 : min
317
+ const isLogarithmicAxis = config.yAxis.type === 'logarithmic'
318
+ min = isLogarithmicAxis && min >= 0 && min < 1 ? min + 0.1 : min
313
319
  // Select the appropriate scale function
314
- const scaleFunc = config.useLogScale ? scaleLog : scaleLinear
320
+ const scaleFunc = isLogarithmicAxis ? scaleLog : scaleLinear
315
321
 
316
322
  if (config.visualizationType === 'Combo') max = leftMax
323
+
324
+ // If the visualization type is a bump chart then the domain and range need different values
325
+ const domainSet = config.visualizationType === 'Bump Chart' ? [1, max] : [min, max]
326
+ const yRange = config.visualizationType === 'Bump Chart' ? [30, yMax] : [yMax, 0]
317
327
  // Return the configured scale function
318
328
  return scaleFunc({
319
- domain: [min, max],
320
- range: [yMax, 0],
321
- nice: config.useLogScale,
322
- zero: config.useLogScale
329
+ domain: domainSet,
330
+ range: yRange,
331
+ nice: isLogarithmicAxis,
332
+ zero: isLogarithmicAxis
323
333
  })
324
334
  }
325
335