@cdc/chart 4.23.5 → 4.23.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.
@@ -0,0 +1,518 @@
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
+ <h3>Confidence Interval Groups</h3>
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 SeriesButtonRemove = props => {
404
+ const { config, updateConfig } = useContext(ConfigContext)
405
+ const { series, index } = props
406
+
407
+ const removeSeries = seriesKey => {
408
+ let series = [...config.series]
409
+ let seriesIndex = -1
410
+
411
+ for (let i = 0; i < series.length; i++) {
412
+ if (series[i].dataKey === seriesKey) {
413
+ seriesIndex = i
414
+ break
415
+ }
416
+ }
417
+
418
+ if (seriesIndex !== -1) {
419
+ series.splice(seriesIndex, 1)
420
+
421
+ let newConfig = { ...config, series }
422
+
423
+ if (series.length === 0) {
424
+ delete newConfig.series
425
+ }
426
+
427
+ updateConfig(newConfig)
428
+ }
429
+
430
+ if (config.visualizationType === 'Paired Bar') {
431
+ updateConfig({
432
+ ...config,
433
+ series: []
434
+ })
435
+ }
436
+ }
437
+
438
+ const handleRemoveSeries = (e, series, index) => {
439
+ e.preventDefault()
440
+ removeSeries(series.dataKey)
441
+ }
442
+
443
+ return (
444
+ config.series &&
445
+ config.series.length > 1 && (
446
+ <button className='series-list__remove' onClick={e => handleRemoveSeries(e, series, index)}>
447
+ Remove
448
+ </button>
449
+ )
450
+ )
451
+ }
452
+
453
+ const SeriesItem = props => {
454
+ const { config } = useContext(ConfigContext)
455
+
456
+ const { series, getItemStyle, sortableItemStyles, chartsWithOptions, index: i } = props
457
+
458
+ return (
459
+ <Draggable key={series.dataKey} draggableId={`draggableFilter-${series.dataKey}`} index={i}>
460
+ {(provided, snapshot) => (
461
+ <div key={i} className={snapshot.isDragging ? 'currently-dragging' : ''} style={getItemStyle(snapshot.isDragging, provided.draggableProps.style, sortableItemStyles)} ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
462
+ <Accordion allowZeroExpanded>
463
+ <AccordionItem className='series-item series-item--chart'>
464
+ <AccordionItemHeading className='series-item__title'>
465
+ <AccordionItemButton className={chartsWithOptions.includes(config.visualizationType) ? 'accordion__button' : 'accordion__button hide-arrow'}>
466
+ <Icon display='move' size={15} style={{ cursor: 'default' }} />
467
+ {series.dataKey}
468
+ <Series.Button.Remove series={series} index={i} />
469
+ </AccordionItemButton>
470
+ </AccordionItemHeading>
471
+ {chartsWithOptions.includes(config.visualizationType) && (
472
+ <AccordionItemPanel>
473
+ <Series.Input.Name series={series} index={i} />
474
+ <Series.Dropdown.SeriesType series={series} index={i} />
475
+ <Series.Dropdown.AxisPosition series={series} index={i} />
476
+ <Series.Dropdown.LineType series={series} index={i} />
477
+ <Series.Dropdown.ForecastingStage series={series} index={i} />
478
+ <Series.Dropdown.ForecastingColor series={series} index={i} />
479
+ <Series.Dropdown.ConfidenceInterval series={series} index={i} />
480
+ </AccordionItemPanel>
481
+ )}
482
+ </AccordionItem>
483
+ </Accordion>
484
+ </div>
485
+ )}
486
+ </Draggable>
487
+ )
488
+ }
489
+
490
+ const SeriesList = props => {
491
+ const { series, getItemStyle, sortableItemStyles, chartsWithOptions } = props
492
+ return series.map((series, i) => {
493
+ return <SeriesItem getItemStyle={getItemStyle} sortableItemStyles={sortableItemStyles} chartsWithOptions={chartsWithOptions} series={series} index={i} key={`series-list-${i}`} />
494
+ })
495
+ }
496
+
497
+ const Series = {
498
+ Wrapper: SeriesWrapper,
499
+ Dropdown: {
500
+ SeriesType: SeriesDropdownSeriesType,
501
+ AxisPosition: SeriesDropdownAxisPosition,
502
+ ConfidenceInterval: SeriesDropdownConfidenceInterval,
503
+ LineType: SeriesDropdownLineType,
504
+ ForecastingStage: SeriesDropdownForecastingStage,
505
+ ForecastingColumn: SeriesDropdownForecastingColumn,
506
+ ForecastingColor: SeriesDropdownForecastColor
507
+ },
508
+ Input: {
509
+ Name: SeriesInputName
510
+ },
511
+ Button: {
512
+ Remove: SeriesButtonRemove
513
+ },
514
+ Item: SeriesItem,
515
+ List: SeriesList
516
+ }
517
+
518
+ 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
  })}
@@ -106,7 +106,8 @@ export default {
106
106
  numTicks: '',
107
107
  labelOffset: 65,
108
108
  axisPadding: 0,
109
- target: 0
109
+ target: 0,
110
+ anchors: []
110
111
  },
111
112
  table: {
112
113
  label: 'Data Table',
@@ -122,7 +123,8 @@ export default {
122
123
  },
123
124
  orientation: 'vertical',
124
125
  color: 'pinkpurple',
125
- columns: { // start with a blank list
126
+ columns: {
127
+ // start with a blank list
126
128
  },
127
129
  legend: {
128
130
  behavior: 'isolate',
@@ -160,7 +162,9 @@ export default {
160
162
  visual: {
161
163
  border: true,
162
164
  accent: true,
163
- background: true
165
+ background: true,
166
+ verticalHoverLine: false,
167
+ horizontalHoverLine: false
164
168
  },
165
169
  useLogScale: false,
166
170
  filterBehavior: 'Filter Change',
@@ -26,6 +26,42 @@ 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
+ ) // Extract ages from result
51
+ const lowCIGroup = Math.min.apply(
52
+ null,
53
+ result.map(item => item[1])
54
+ ) // Extract ages from result
55
+
56
+ if (highCIGroup > max) {
57
+ max = highCIGroup
58
+ }
59
+ if (lowCIGroup < min) {
60
+ min = lowCIGroup
61
+ }
62
+ }
63
+ }
64
+
29
65
  if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && min > 0) {
30
66
  min = 0
31
67
  }
@@ -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,15 @@ 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
+
23
+ if (hasBarSeries && minValue > 0) {
24
+ minValue = 0
25
+ }
26
+
21
27
  const yScaleRight = scaleLinear({
22
28
  domain: [minValue, max],
23
29
  range: [yMax, 0]
@@ -6,12 +6,15 @@ const useScales = properties => {
6
6
  const xAxisType = config.runtime.xAxis.type
7
7
  const isHorizontal = config.orientation === 'horizontal'
8
8
 
9
- // define scxales
9
+ const { visualizationType } = config
10
+
11
+ // define scales
10
12
  let xScale = null
11
13
  let yScale = null
12
14
  let g2xScale = null
13
15
  let g1xScale = null
14
16
  let seriesScale = null
17
+ let xScaleNoPadding = null
15
18
 
16
19
  const scaleTypes = {
17
20
  TIME: 'time',
@@ -34,15 +37,6 @@ const useScales = properties => {
34
37
  if (!isHorizontal) {
35
38
  xScale = composeScalePoint(xAxisDataMapped, [0, xMax], 0.5)
36
39
  xScale.type = scaleTypes.POINT
37
-
38
- // if (config.visualizationType === 'Bar') {
39
- // xScale = scaleBand({
40
- // domain: xAxisDataMapped,
41
- // range: [0, xMax],
42
- // padding: 0.4
43
- // })
44
- // xScale.type = scaleTypes.BAND
45
- // }
46
40
  yScale = composeYScale(properties)
47
41
  seriesScale = composeScalePoint(seriesDomain, [0, xMax])
48
42
  }
@@ -84,7 +78,7 @@ const useScales = properties => {
84
78
  }
85
79
 
86
80
  // handle Box plot
87
- if (config.visualizationType === 'Box Plot') {
81
+ if (visualizationType === 'Box Plot') {
88
82
  const allOutliers = []
89
83
  const hasOutliers = config.boxplot.plots.map(b => b.columnOutliers.map(outlier => allOutliers.push(outlier))) && !config.boxplot.hideOutliers
90
84
 
@@ -122,7 +116,7 @@ const useScales = properties => {
122
116
  }
123
117
 
124
118
  // handle Paired bar
125
- if (config.visualizationType === 'Paired Bar') {
119
+ if (visualizationType === 'Paired Bar') {
126
120
  const offset = 1.02 // Offset of the ticks/values from the Axis
127
121
  let groupOneMax = Math.max.apply(
128
122
  Math,
@@ -147,7 +141,7 @@ const useScales = properties => {
147
141
  })
148
142
  }
149
143
 
150
- return { xScale, yScale, seriesScale, g1xScale, g2xScale }
144
+ return { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding }
151
145
  }
152
146
 
153
147
  export default useScales