@cdc/chart 4.23.5 → 4.23.7

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.
@@ -0,0 +1,554 @@
1
+ import React, { useContext } from 'react'
2
+ import ConfigContext from '../ConfigContext'
3
+
4
+ // Core
5
+ import InputSelect from '@cdc/core/components/inputs/InputSelect'
6
+ import Check from '@cdc/core/assets/icon-check.svg'
7
+
8
+ import Icon from '@cdc/core/components/ui/Icon'
9
+
10
+ // Third Party
11
+ import { Accordion, AccordionItem, AccordionItemHeading, AccordionItemPanel, AccordionItemButton } from 'react-accessible-accordion'
12
+ import { Draggable } from '@hello-pangea/dnd'
13
+ import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
14
+
15
+ const SeriesContext = React.createContext()
16
+
17
+ const SeriesWrapper = props => {
18
+ const { updateConfig, config } = useContext(ConfigContext)
19
+
20
+ const { getColumns, selectComponent } = props
21
+
22
+ const supportedRightAxisTypes = ['Line', 'dashed-sm', 'dashed-md', 'dashed-lg']
23
+
24
+ const updateSeries = (index, value, property) => {
25
+ let series = [...config.series]
26
+ series[index][property] = value
27
+
28
+ updateConfig({ ...config, series })
29
+ }
30
+
31
+ return <SeriesContext.Provider value={{ updateSeries, supportedRightAxisTypes, getColumns, selectComponent }}>{props.children}</SeriesContext.Provider>
32
+ }
33
+
34
+ const SeriesDropdownLineType = props => {
35
+ const { config, updateConfig } = useContext(ConfigContext)
36
+ const { series, index } = props
37
+
38
+ // Run a quick test to determine if we should even show this.
39
+ const supportsLineType = ['Line', 'dashed-sm', 'dashed-md', 'dashed-lg', 'Area Chart'].some(item => item.includes(series.type))
40
+
41
+ if (!supportsLineType) return
42
+
43
+ const changeLineType = (i, value) => {
44
+ let series = [...config.series]
45
+ series[i].lineType = value
46
+ updateConfig({ ...config, series })
47
+ }
48
+
49
+ const approvedCurveTypes = {
50
+ Linear: 'curveLinear',
51
+ Cardinal: 'curveCardinal',
52
+ Natural: 'curveNatural',
53
+ 'Monotone X': 'curveMonotoneX',
54
+ Step: 'curveStep'
55
+ }
56
+
57
+ let options = []
58
+
59
+ Object.keys(approvedCurveTypes).map(curveName => {
60
+ return options.push(approvedCurveTypes[curveName])
61
+ })
62
+
63
+ return (
64
+ <InputSelect
65
+ initial='Select an option'
66
+ value={series.lineType ? series.lineType : 'curveLinear'}
67
+ label='Series Line Type'
68
+ onChange={event => {
69
+ changeLineType(index, event.target.value)
70
+ }}
71
+ options={options}
72
+ />
73
+ )
74
+ }
75
+
76
+ const SeriesDropdownSeriesType = props => {
77
+ const { config } = useContext(ConfigContext)
78
+ const { updateSeries } = useContext(SeriesContext)
79
+
80
+ const { index, series } = props
81
+
82
+ const getOptions = () => {
83
+ if (config.visualizationType === 'Combo') {
84
+ return {
85
+ Bar: 'Bar',
86
+ Line: 'Line',
87
+ 'dashed-sm': 'Small Dashed',
88
+ 'dashed-md': 'Medium Dashed',
89
+ 'dashed-lg': 'Large Dashed',
90
+ 'Area Chart': 'Area Chart',
91
+ Forecasting: 'Forecasting'
92
+ }
93
+ }
94
+ if (config.visualizationType === 'Line') {
95
+ return {
96
+ Line: 'Line',
97
+ 'dashed-sm': 'Small Dashed',
98
+ 'dashed-md': 'Medium Dashed',
99
+ 'dashed-lg': 'Large Dashed'
100
+ }
101
+ }
102
+ }
103
+
104
+ // Allowable changes
105
+ if (!['Line', 'Combo'].includes(config.visualizationType)) return
106
+ return (
107
+ <InputSelect
108
+ initial='Select an option'
109
+ value={series.type}
110
+ label='Series Type'
111
+ onChange={event => {
112
+ updateSeries(index, event.target.value, 'type')
113
+ }}
114
+ options={getOptions()}
115
+ />
116
+ )
117
+ }
118
+
119
+ const SeriesDropdownForecastingStage = props => {
120
+ const { config, updateConfig, rawData } = useContext(ConfigContext)
121
+ const { updateSeries, getColumns } = useContext(SeriesContext)
122
+
123
+ const { index, series } = props
124
+
125
+ // Only combo charts are allowed to have different options
126
+ if (series.type !== 'Forecasting') return
127
+
128
+ return (
129
+ <InputSelect
130
+ initial='Select an option'
131
+ value={series.stageColumn}
132
+ label='Add Forecasting Stages'
133
+ onChange={e => {
134
+ let stageObjects = []
135
+ let tempGroups = new Set(rawData?.map(item => item[e.target.value])) // [estimate, forecast, etc.]
136
+ tempGroups = Array.from(tempGroups) // convert set to array
137
+
138
+ tempGroups = tempGroups.filter(group => group !== undefined) // removes undefined
139
+
140
+ tempGroups.forEach(group => stageObjects.push({ key: group }))
141
+
142
+ const copyOfSeries = [...config.series] // copy the entire series array
143
+ copyOfSeries[index] = { ...copyOfSeries[index], stages: stageObjects, stageColumn: e.target.value }
144
+
145
+ updateConfig({
146
+ ...config,
147
+ series: copyOfSeries
148
+ })
149
+ }}
150
+ options={getColumns(false)}
151
+ />
152
+ )
153
+ }
154
+
155
+ const SeriesDropdownForecastingColumn = props => {
156
+ const { config, rawData } = useContext(ConfigContext)
157
+ const { updateSeries } = useContext(SeriesContext)
158
+
159
+ const { index, series } = props
160
+
161
+ // Only combo charts are allowed to have different options
162
+ if (series.type !== 'Forecasting') return
163
+ if (!rawData) return
164
+ if (!series.stageColumn) return
165
+
166
+ let tempGroups = new Set(rawData.map(item => item[series.stageColumn])) // [estimate, forecast, etc.]
167
+ tempGroups = Array.from(tempGroups) // convert set to array
168
+
169
+ tempGroups = tempGroups.filter(group => group !== undefined) // removes undefined
170
+
171
+ return (
172
+ <InputSelect
173
+ initial='Select an option'
174
+ value={series.stageItem}
175
+ label='Forecasting Item Column'
176
+ onChange={event => {
177
+ updateSeries(index, event.target.value, 'stageItem')
178
+ }}
179
+ options={tempGroups}
180
+ />
181
+ )
182
+ }
183
+
184
+ const SeriesDropdownAxisPosition = props => {
185
+ const { config } = useContext(ConfigContext)
186
+ const { updateSeries, supportedRightAxisTypes } = useContext(SeriesContext)
187
+
188
+ const { index, series } = props
189
+
190
+ // Hide AxisPositionDropdown in certain cases.
191
+ if (config.visualizationType !== 'Combo' || !series) return
192
+ if (!supportedRightAxisTypes.includes(series.type)) {
193
+ return
194
+ }
195
+ return (
196
+ <InputSelect
197
+ initial='Select an option'
198
+ value={series.axis ? series.axis : 'Left'}
199
+ label='Series Axis'
200
+ onChange={event => {
201
+ updateSeries(index, event.target.value, 'axis')
202
+ }}
203
+ options={{
204
+ ['Left']: 'Left',
205
+ ['Right']: 'Right'
206
+ }}
207
+ />
208
+ )
209
+ }
210
+
211
+ const SeriesDropdownForecastColor = props => {
212
+ const { config, updateConfig } = useContext(ConfigContext)
213
+
214
+ const { index, series } = props
215
+
216
+ if (series.type !== 'Forecasting') return
217
+
218
+ // Hide AxisPositionDropdown in certain cases.
219
+ if (!series) return
220
+
221
+ return series?.stages?.map((stage, stageIndex) => (
222
+ <InputSelect
223
+ key={`${stage}--${stageIndex}`}
224
+ initial='Select an option'
225
+ value={config.series?.[index].stages?.[stageIndex].color ? config.series?.[index].stages?.[stageIndex].color : 'Select'}
226
+ label={`${stage.key} Series Color`}
227
+ onChange={event => {
228
+ const copyOfSeries = [...config.series] // copy the entire series array
229
+ const copyOfStages = copyOfSeries[index].stages
230
+ copyOfStages[stageIndex].color = event.target.value
231
+ copyOfSeries[index] = { ...copyOfSeries[index], stages: copyOfStages }
232
+
233
+ updateConfig({
234
+ ...config,
235
+ series: copyOfSeries
236
+ })
237
+ }}
238
+ options={Object.keys(colorPalettesChart)}
239
+ />
240
+ ))
241
+ }
242
+
243
+ const SeriesDropdownConfidenceInterval = props => {
244
+ const { config, updateConfig } = useContext(ConfigContext)
245
+ const { series, index } = props
246
+ const { getColumns } = useContext(SeriesContext)
247
+ if (series.type !== 'Forecasting') return
248
+
249
+ return (
250
+ <div className='edit-block'>
251
+ <span className='edit-label column-heading'>Confidence Interval Groups</span>
252
+ <fieldset>
253
+ <Accordion allowZeroExpanded>
254
+ {series?.confidenceIntervals?.map((ciGroup, ciIndex) => {
255
+ const showInTooltip = ciGroup.showInTooltip ? ciGroup.showInTooltip : false
256
+
257
+ const updateShowInTooltip = (e, seriesIndex, ciIndex) => {
258
+ e.preventDefault()
259
+ let copiedSeries = [...config.series]
260
+ copiedSeries[seriesIndex].confidenceIntervals[ciIndex].showInTooltip = !showInTooltip
261
+ updateConfig({
262
+ ...config,
263
+ series: copiedSeries
264
+ })
265
+ }
266
+
267
+ return (
268
+ <AccordionItem className='series-item series-item--chart' key={`${ciIndex}`}>
269
+ <AccordionItemHeading className='series-item__title'>
270
+ <>
271
+ <AccordionItemButton className={'accordion__button accordion__button'}>
272
+ Group {ciIndex + 1}
273
+ <button
274
+ className='series-list__remove'
275
+ onClick={e => {
276
+ e.preventDefault()
277
+ const copiedIndex = [...config.series[index].confidenceIntervals]
278
+ copiedIndex.splice(ciIndex, 1)
279
+ const copyOfSeries = [...config.series] // copy the entire series array
280
+ copyOfSeries[index] = { ...copyOfSeries[index], confidenceIntervals: [...copiedIndex] }
281
+ updateConfig({
282
+ ...config,
283
+ series: copyOfSeries
284
+ })
285
+ }}
286
+ >
287
+ Remove
288
+ </button>
289
+ </AccordionItemButton>
290
+ </>
291
+ </AccordionItemHeading>
292
+ <AccordionItemPanel>
293
+ {/* <div className='input-group'>
294
+ <label htmlFor='showInTooltip'>Show In Tooltip</label>
295
+ <div className={'cove-input__checkbox--small'} onClick={e => updateShowInTooltip(e, index, ciIndex)}>
296
+ <div className={`cove-input__checkbox-box${'blue' ? ' custom-color' : ''}`} style={{ backgroundColor: '' }}>
297
+ {showInTooltip && <Check className='' style={{ fill: '#025eaa' }} />}
298
+ </div>
299
+ <input className='cove-input--hidden' type='checkbox' name={'showInTooltip'} checked={showInTooltip ? showInTooltip : false} readOnly />
300
+ </div>
301
+ </div> */}
302
+
303
+ <InputSelect
304
+ initial='Select an option'
305
+ value={config.series[index].confidenceIntervals[ciIndex].low ? config.series[index].confidenceIntervals[ciIndex].low : 'Select'}
306
+ label='Low Confidence Interval'
307
+ onChange={e => {
308
+ const copiedConfidenceArray = [...config.series[index].confidenceIntervals]
309
+ copiedConfidenceArray[ciIndex].low = e.target.value
310
+ const copyOfSeries = [...config.series] // copy the entire series array
311
+ copyOfSeries[index] = { ...copyOfSeries[index], confidenceIntervals: copiedConfidenceArray }
312
+ updateConfig({
313
+ ...config,
314
+ series: copyOfSeries
315
+ })
316
+ }}
317
+ options={getColumns()}
318
+ />
319
+ <InputSelect
320
+ initial='Select an option'
321
+ value={config.series[index].confidenceIntervals[ciIndex].high ? config.series[index].confidenceIntervals[ciIndex].high : 'Select'}
322
+ label='High Confidence Interval'
323
+ onChange={e => {
324
+ const copiedConfidenceArray = [...config.series[index].confidenceIntervals]
325
+ copiedConfidenceArray[ciIndex].high = e.target.value
326
+ const copyOfSeries = [...config.series] // copy the entire series array
327
+ copyOfSeries[index] = { ...copyOfSeries[index], confidenceIntervals: copiedConfidenceArray }
328
+ updateConfig({
329
+ ...config,
330
+ series: copyOfSeries
331
+ })
332
+ }}
333
+ options={getColumns()}
334
+ />
335
+ </AccordionItemPanel>
336
+ </AccordionItem>
337
+ )
338
+ })}
339
+ </Accordion>
340
+ <button
341
+ className='btn full-width'
342
+ onClick={e => {
343
+ e.preventDefault()
344
+ let copiedIndex = null
345
+ if (config.series[index].confidenceIntervals) {
346
+ copiedIndex = [...config.series[index].confidenceIntervals]
347
+ } else {
348
+ copiedIndex = []
349
+ }
350
+ const copyOfSeries = [...config.series] // copy the entire series array
351
+ copyOfSeries[index] = { ...copyOfSeries[index], confidenceIntervals: [...copiedIndex, { high: '', low: '' }] } // update the nested array
352
+ updateConfig({
353
+ ...config,
354
+ series: copyOfSeries
355
+ })
356
+ }}
357
+ >
358
+ Add Confidence Interval Group
359
+ </button>
360
+ </fieldset>
361
+ </div>
362
+ )
363
+ }
364
+
365
+ const SeriesInputName = props => {
366
+ const { series, index: i } = props
367
+ const { config, updateConfig } = useContext(ConfigContext)
368
+ const adjustableNameSeriesTypes = ['Bar', 'Line', 'Area Chart']
369
+
370
+ if (config.visualizationType === 'Combo') return
371
+
372
+ if (!adjustableNameSeriesTypes.includes(series.type)) return
373
+
374
+ let changeSeriesName = (i, value) => {
375
+ let series = [...config.series]
376
+ let seriesLabelsCopy = { ...config.runtime.seriesLabels }
377
+ series[i].name = value
378
+ seriesLabelsCopy[series[i].dataKey] = series[i].name
379
+
380
+ let newConfig = {
381
+ ...config,
382
+ series
383
+ }
384
+
385
+ updateConfig(newConfig)
386
+ }
387
+
388
+ return (
389
+ <>
390
+ <label htmlFor='series-name'>Series Name</label>
391
+ <input
392
+ type='text'
393
+ key={`series-name-${i}`}
394
+ value={series.name ? series.name : ''}
395
+ onChange={event => {
396
+ changeSeriesName(i, event.target.value)
397
+ }}
398
+ />
399
+ </>
400
+ )
401
+ }
402
+
403
+ const SeriesDisplayInTooltip = props => {
404
+ const { series, index } = props
405
+ const { config, updateConfig } = useContext(ConfigContext)
406
+
407
+ const toggleTooltip = seriesIndex => {
408
+ let copiedSeries = [...config.series]
409
+
410
+ const showInTooltip = copiedSeries[seriesIndex].tooltip ? copiedSeries[seriesIndex].tooltip : false
411
+
412
+ copiedSeries[seriesIndex].tooltip = !copiedSeries[seriesIndex].tooltip
413
+
414
+ updateConfig({
415
+ ...config,
416
+ series: copiedSeries
417
+ })
418
+ }
419
+
420
+ return (
421
+ <>
422
+ <div className='input-group'>
423
+ <label htmlFor={`series-tooltip--${index}`}>Show In Tooltip</label>
424
+ <div className={'cove-input__checkbox--small'} onClick={e => toggleTooltip(index)}>
425
+ <div className={`cove-input__checkbox-box${'blue' ? ' custom-color' : ''}`} style={{ backgroundColor: '' }}>
426
+ {series.tooltip && <Check className='' style={{ fill: '#025eaa' }} />}
427
+ </div>
428
+ <input className='cove-input--hidden' type='checkbox' name={`series-tooltip--${index}`} checked={series.tooltip ? series.tooltip : false} readOnly />
429
+ </div>
430
+ </div>
431
+ </>
432
+ )
433
+ }
434
+
435
+ const SeriesButtonRemove = props => {
436
+ const { config, updateConfig } = useContext(ConfigContext)
437
+ const { series, index } = props
438
+
439
+ const removeSeries = seriesKey => {
440
+ let series = [...config.series]
441
+ let seriesIndex = -1
442
+
443
+ for (let i = 0; i < series.length; i++) {
444
+ if (series[i].dataKey === seriesKey) {
445
+ seriesIndex = i
446
+ break
447
+ }
448
+ }
449
+
450
+ if (seriesIndex !== -1) {
451
+ series.splice(seriesIndex, 1)
452
+
453
+ let newConfig = { ...config, series }
454
+
455
+ if (series.length === 0) {
456
+ delete newConfig.series
457
+ }
458
+
459
+ updateConfig(newConfig)
460
+ }
461
+
462
+ if (config.visualizationType === 'Paired Bar') {
463
+ updateConfig({
464
+ ...config,
465
+ series: []
466
+ })
467
+ }
468
+ }
469
+
470
+ const handleRemoveSeries = (e, series, index) => {
471
+ e.preventDefault()
472
+ removeSeries(series.dataKey)
473
+ }
474
+
475
+ return (
476
+ config.series &&
477
+ config.series.length > 1 && (
478
+ <button className='series-list__remove' onClick={e => handleRemoveSeries(e, series, index)}>
479
+ Remove
480
+ </button>
481
+ )
482
+ )
483
+ }
484
+
485
+ const SeriesItem = props => {
486
+ const { config } = useContext(ConfigContext)
487
+
488
+ const { series, getItemStyle, sortableItemStyles, chartsWithOptions, index: i } = props
489
+
490
+ return (
491
+ <Draggable key={series.dataKey} draggableId={`draggableFilter-${series.dataKey}`} index={i}>
492
+ {(provided, snapshot) => (
493
+ <div key={i} className={snapshot.isDragging ? 'currently-dragging' : ''} style={getItemStyle(snapshot.isDragging, provided.draggableProps.style, sortableItemStyles)} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
494
+ <Accordion allowZeroExpanded>
495
+ <AccordionItem className='series-item series-item--chart'>
496
+ <AccordionItemHeading className='series-item__title'>
497
+ <AccordionItemButton className={chartsWithOptions.includes(config.visualizationType) ? 'accordion__button' : 'accordion__button hide-arrow'}>
498
+ <Icon display='move' size={15} style={{ cursor: 'default' }} />
499
+ {series.dataKey}
500
+ <Series.Button.Remove series={series} index={i} />
501
+ </AccordionItemButton>
502
+ </AccordionItemHeading>
503
+ {chartsWithOptions.includes(config.visualizationType) && (
504
+ <AccordionItemPanel>
505
+ <Series.Input.Name series={series} index={i} />
506
+ <Series.Dropdown.SeriesType series={series} index={i} />
507
+ <Series.Dropdown.AxisPosition series={series} index={i} />
508
+ <Series.Dropdown.LineType series={series} index={i} />
509
+ {/* <Series.Dropdown.ForecastingStage series={series} index={i} /> */}
510
+ <Series.Dropdown.ForecastingColor series={series} index={i} />
511
+ <Series.Dropdown.ConfidenceInterval series={series} index={i} />
512
+ <Series.Checkbox.DisplayInTooltip series={series} index={i} />
513
+ </AccordionItemPanel>
514
+ )}
515
+ </AccordionItem>
516
+ </Accordion>
517
+ </div>
518
+ )}
519
+ </Draggable>
520
+ )
521
+ }
522
+
523
+ const SeriesList = props => {
524
+ const { series, getItemStyle, sortableItemStyles, chartsWithOptions } = props
525
+ return series.map((series, i) => {
526
+ return <SeriesItem getItemStyle={getItemStyle} sortableItemStyles={sortableItemStyles} chartsWithOptions={chartsWithOptions} series={series} index={i} key={`series-list-${i}`} />
527
+ })
528
+ }
529
+
530
+ const Series = {
531
+ Wrapper: SeriesWrapper,
532
+ Dropdown: {
533
+ SeriesType: SeriesDropdownSeriesType,
534
+ AxisPosition: SeriesDropdownAxisPosition,
535
+ ConfidenceInterval: SeriesDropdownConfidenceInterval,
536
+ LineType: SeriesDropdownLineType,
537
+ ForecastingStage: SeriesDropdownForecastingStage,
538
+ ForecastingColumn: SeriesDropdownForecastingColumn,
539
+ ForecastingColor: SeriesDropdownForecastColor
540
+ },
541
+ Input: {
542
+ Name: SeriesInputName
543
+ },
544
+ Checkbox: {
545
+ DisplayInTooltip: SeriesDisplayInTooltip
546
+ },
547
+ Button: {
548
+ Remove: SeriesButtonRemove
549
+ },
550
+ Item: SeriesItem,
551
+ List: SeriesList
552
+ }
553
+
554
+ export default Series
@@ -121,8 +121,8 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
121
121
  <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'>
122
122
  {formatNumber(d[seriesKey])}
123
123
  </Text>
124
-
125
- {dataIndex + 1 !== data.length && (config.lineDatapointStyle === 'always show' || config.lineDatapointStyle === 'hover') && (
124
+ {/* hide data point circles */}
125
+ {/* dataIndex + 1 !== data.length && (config.lineDatapointStyle === 'always show' || config.lineDatapointStyle === 'hover') && (
126
126
  <circle
127
127
  key={`${seriesKey}-${dataIndex}`}
128
128
  r={circleRadii}
@@ -133,7 +133,7 @@ export default function SparkLine({ width: parentWidth, height: parentHeight })
133
133
  data-tooltip-html={tooltip}
134
134
  data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
135
135
  />
136
- )}
136
+ )*/}
137
137
  </Group>
138
138
  )
139
139
  })}
@@ -15,6 +15,7 @@ export default {
15
15
  barStyle: '',
16
16
  roundingStyle: 'standard',
17
17
  tipRounding: 'top',
18
+ isResponsiveTicks: false,
18
19
  general: {
19
20
  showDownloadButton: false
20
21
  },
@@ -36,7 +37,7 @@ export default {
36
37
  tickLabelColor: '#333',
37
38
  tickColor: '#333',
38
39
  rightHideAxis: true,
39
- rightAxisSize: 50,
40
+ rightAxisSize: 0,
40
41
  rightLabel: '',
41
42
  rightLabelOffsetSize: 0,
42
43
  rightAxisLabelColor: '#333',
@@ -106,7 +107,8 @@ export default {
106
107
  numTicks: '',
107
108
  labelOffset: 65,
108
109
  axisPadding: 0,
109
- target: 0
110
+ target: 0,
111
+ maxTickRotation: 0
110
112
  },
111
113
  table: {
112
114
  label: 'Data Table',
@@ -122,7 +124,8 @@ export default {
122
124
  },
123
125
  orientation: 'vertical',
124
126
  color: 'pinkpurple',
125
- columns: { // start with a blank list
127
+ columns: {
128
+ // start with a blank list
126
129
  },
127
130
  legend: {
128
131
  behavior: 'isolate',
@@ -160,10 +163,15 @@ export default {
160
163
  visual: {
161
164
  border: true,
162
165
  accent: true,
163
- background: true
166
+ background: true,
167
+ verticalHoverLine: false,
168
+ horizontalHoverLine: false
164
169
  },
165
170
  useLogScale: false,
166
171
  filterBehavior: 'Filter Change',
167
172
  highlightedBarValues: [],
168
- series: []
173
+ series: [],
174
+ tooltips: {
175
+ opacity: 90
176
+ }
169
177
  }
@@ -26,6 +26,43 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
26
26
  }
27
27
  }
28
28
 
29
+ if (config.visualizationType === 'Forecasting') {
30
+ const {
31
+ runtime: { forecastingSeriesKeys }
32
+ } = config
33
+ if (forecastingSeriesKeys.length > 0) {
34
+ // push all keys into an array
35
+ let columnNames = []
36
+
37
+ forecastingSeriesKeys.forEach(f => {
38
+ f.confidenceIntervals?.map(ciGroup => {
39
+ columnNames.push(ciGroup.high)
40
+ columnNames.push(ciGroup.low)
41
+ })
42
+ })
43
+
44
+ // Using the columnNames or "keys" get the returned result
45
+ const result = data.map(obj => columnNames.map(key => obj[key]))
46
+
47
+ const highCIGroup = Math.max.apply(
48
+ null,
49
+ result.map(item => item[0])
50
+ )
51
+
52
+ const lowCIGroup = Math.min.apply(
53
+ null,
54
+ result.map(item => item[1])
55
+ )
56
+
57
+ if (highCIGroup > max) {
58
+ max = highCIGroup
59
+ }
60
+ if (lowCIGroup < min) {
61
+ min = lowCIGroup
62
+ }
63
+ }
64
+ }
65
+
29
66
  if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && min > 0) {
30
67
  min = 0
31
68
  }
@@ -4,7 +4,7 @@ import useReduceData from '../hooks/useReduceData'
4
4
  export default function useRightAxis({ config, yMax = 0, data = [], updateConfig }) {
5
5
  const hasRightAxis = config.visualizationType === 'Combo' && config.orientation === 'vertical'
6
6
  const rightSeriesKeys = config.series && config.series.filter(series => series.axis === 'Right').map(key => key.dataKey)
7
- const { minValue } = useReduceData(config, data)
7
+ let { minValue } = useReduceData(config, data)
8
8
 
9
9
  const allRightAxisData = rightSeriesKeys => {
10
10
  if (!rightSeriesKeys) return [0]
@@ -15,9 +15,16 @@ export default function useRightAxis({ config, yMax = 0, data = [], updateConfig
15
15
  return rightAxisData
16
16
  }
17
17
 
18
- const min = Math.min.apply(null, allRightAxisData(rightSeriesKeys))
19
18
  const max = Math.max.apply(null, allRightAxisData(rightSeriesKeys))
20
19
 
20
+ // if there is a bar series & the right axis doesn't include a negative number, default to zero
21
+ const hasBarSeries = config.runtime?.barSeriesKeys?.length > 0
22
+ const hasLineSeries = config.runtime?.lineSeriesKeys?.length > 0
23
+
24
+ if ((hasBarSeries || hasLineSeries) && minValue > 0) {
25
+ minValue = 0
26
+ }
27
+
21
28
  const yScaleRight = scaleLinear({
22
29
  domain: [minValue, max],
23
30
  range: [yMax, 0]