@cdc/chart 4.23.10 → 4.24.1

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 (125) hide show
  1. package/dist/cdcchart.js +34606 -32218
  2. package/examples/feature/bar/additional-column-tooltip.json +446 -0
  3. package/examples/feature/bar/example-bar-chart.json +1 -46
  4. package/examples/feature/bar/lollipop.json +156 -0
  5. package/examples/feature/bar/tall-data.json +98 -0
  6. package/examples/feature/combo/planet-combo-example-config.json +99 -9
  7. package/examples/feature/dev-4261.json +399 -0
  8. package/examples/feature/forest-plot/forest-plot.json +63 -19
  9. package/examples/feature/forest-plot/{broken.json → linear.json} +77 -23
  10. package/examples/feature/forest-plot/log.json +26 -0
  11. package/examples/feature/forest-plot/logarithmic.json +271 -0
  12. package/examples/feature/line/line-chart-preliminary.json +346 -0
  13. package/examples/feature/line/line-points.json +340 -0
  14. package/examples/feature/regions/index.json +462 -0
  15. package/examples/feature/scatterplot/scatterplot.json +272 -33
  16. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +181 -48
  17. package/examples/private/chart-t.json +3740 -0
  18. package/examples/private/combo.json +369 -0
  19. package/examples/private/epi-data.csv +13 -0
  20. package/examples/private/epi-data.json +62 -0
  21. package/examples/private/epi.json +403 -0
  22. package/examples/private/occupancy.json +109283 -0
  23. package/examples/private/prod-line-config.json +401 -0
  24. package/examples/private/region-data.json +822 -0
  25. package/examples/private/region-testing.json +312 -0
  26. package/examples/private/scaling.json +45325 -0
  27. package/examples/private/testing-data.json +1739 -0
  28. package/examples/private/testing.json +816 -0
  29. package/examples/sparkline-multilple.json +846 -0
  30. package/index.html +12 -8
  31. package/package.json +3 -3
  32. package/src/CdcChart.tsx +42 -211
  33. package/src/ConfigContext.tsx +6 -0
  34. package/src/_stories/Chart.stories.tsx +188 -0
  35. package/src/_stories/Chart.tooltip.stories.tsx +305 -0
  36. package/src/_stories/ChartBrush.stories.tsx +19 -0
  37. package/src/_stories/ChartEditor.stories.tsx +22 -0
  38. package/src/_stories/ChartLine.preliminary.tsx +19 -0
  39. package/src/_stories/ChartSuppress.stories.tsx +19 -0
  40. package/src/_stories/_mock/brush_mock.json +393 -0
  41. package/src/_stories/_mock/pie_config.json +191 -0
  42. package/src/_stories/_mock/pie_data.json +218 -0
  43. package/src/_stories/_mock/preliminary_mock.json +346 -0
  44. package/src/_stories/_mock/suppress_mock.json +911 -0
  45. package/src/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +6 -7
  46. package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +7 -36
  47. package/src/components/AreaChart/index.tsx +4 -0
  48. package/src/components/{BarChart.Horizontal.jsx → BarChart/components/BarChart.Horizontal.tsx} +111 -34
  49. package/src/components/{BarChart.StackedHorizontal.jsx → BarChart/components/BarChart.StackedHorizontal.tsx} +55 -20
  50. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +106 -0
  51. package/src/components/{BarChart.Vertical.jsx → BarChart/components/BarChart.Vertical.tsx} +162 -34
  52. package/src/components/BarChart/components/BarChart.jsx +39 -0
  53. package/src/components/{BarChartType.jsx → BarChart/components/BarChartType.jsx} +0 -2
  54. package/src/components/BarChart/components/context.tsx +13 -0
  55. package/src/components/BarChart/index.tsx +3 -0
  56. package/src/components/{BoxPlot.jsx → BoxPlot/BoxPlot.jsx} +1 -1
  57. package/src/components/BoxPlot/index.tsx +3 -0
  58. package/src/components/DeviationBar.jsx +4 -3
  59. package/src/components/{EditorPanel.jsx → EditorPanel/EditorPanel.tsx} +807 -865
  60. package/src/components/EditorPanel/components/Panel.DateHighlighting.tsx +109 -0
  61. package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panel.ForestPlotSettings.tsx} +190 -220
  62. package/src/components/EditorPanel/components/Panel.Regions.tsx +168 -0
  63. package/src/components/{Series.jsx → EditorPanel/components/Panel.Series.tsx} +23 -4
  64. package/src/components/EditorPanel/components/PanelProps.ts +3 -0
  65. package/src/components/EditorPanel/components/Panels.tsx +13 -0
  66. package/src/components/EditorPanel/components/panels.scss +72 -0
  67. package/src/components/EditorPanel/editor-panel.scss +751 -0
  68. package/src/components/EditorPanel/index.tsx +3 -0
  69. package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +50 -5
  70. package/src/components/{Forecasting.jsx → Forecasting/Forecasting.jsx} +1 -1
  71. package/src/components/Forecasting/index.tsx +3 -0
  72. package/src/components/ForestPlot/ForestPlot.tsx +254 -0
  73. package/src/components/ForestPlot/ForestPlotProps.ts +18 -0
  74. package/src/components/ForestPlot/index.scss +1 -0
  75. package/src/components/ForestPlot/index.tsx +3 -0
  76. package/src/components/Legend/Legend.tsx +347 -0
  77. package/src/components/Legend/index.tsx +3 -0
  78. package/src/components/LineChart/LineChartProps.ts +46 -0
  79. package/src/components/{LineChart.Circle.tsx → LineChart/components/LineChart.Circle.tsx} +36 -30
  80. package/src/components/LineChart/helpers.ts +45 -0
  81. package/src/components/LineChart/index.scss +1 -0
  82. package/src/components/{LineChart.tsx → LineChart/index.tsx} +83 -42
  83. package/src/components/LinearChart.jsx +125 -82
  84. package/src/components/PairedBarChart.jsx +2 -2
  85. package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +16 -7
  86. package/src/components/PieChart/index.tsx +3 -0
  87. package/src/components/Regions/components/Regions.tsx +135 -0
  88. package/src/components/Regions/index.tsx +3 -0
  89. package/src/components/{ScatterPlot.jsx → ScatterPlot/ScatterPlot.jsx} +3 -3
  90. package/src/components/ScatterPlot/index.tsx +3 -0
  91. package/src/components/{SparkLine.jsx → Sparkline/SparkLine.jsx} +2 -2
  92. package/src/components/Sparkline/index.tsx +3 -0
  93. package/src/components/ZoomBrush.tsx +168 -0
  94. package/src/data/initial-state.js +30 -16
  95. package/src/helpers/abbreviateNumber.ts +17 -0
  96. package/src/helpers/computeMarginBottom.ts +55 -0
  97. package/src/helpers/filterData.ts +18 -0
  98. package/src/helpers/generateColorsArray.ts +8 -0
  99. package/src/helpers/getQuartiles.ts +30 -0
  100. package/src/helpers/handleChartAriaLabels.ts +19 -0
  101. package/src/helpers/handleLineType.ts +18 -0
  102. package/src/helpers/lineOptions.ts +18 -0
  103. package/src/helpers/sort.ts +7 -0
  104. package/src/helpers/tests/computeMarginBottom.test.ts +20 -0
  105. package/src/hooks/useBarChart.js +72 -7
  106. package/src/hooks/useColorScale.ts +50 -0
  107. package/src/hooks/{useMinMax.js → useMinMax.ts} +75 -23
  108. package/src/hooks/{useRightAxis.js → useRightAxis.ts} +10 -2
  109. package/src/hooks/{useScales.js → useScales.ts} +64 -17
  110. package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +84 -55
  111. package/src/scss/main.scss +70 -38
  112. package/src/types/ChartConfig.ts +178 -0
  113. package/src/types/ChartContext.ts +54 -0
  114. package/src/types/ForestPlot.ts +53 -0
  115. package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
  116. package/src/ConfigContext.jsx +0 -5
  117. package/src/components/BarChart.StackedVertical.jsx +0 -95
  118. package/src/components/BarChart.jsx +0 -30
  119. package/src/components/ForestPlot.jsx +0 -191
  120. package/src/components/Legend.jsx +0 -277
  121. package/src/scss/LinearChart.scss +0 -0
  122. package/src/scss/editor-panel.scss +0 -745
  123. package/src/scss/legend.scss +0 -206
  124. package/src/scss/mixins.scss +0 -0
  125. package/src/scss/variables.scss +0 -1
@@ -0,0 +1,18 @@
1
+ export const handleLineType = lineType => {
2
+ switch (lineType) {
3
+ case 'dashed-sm':
4
+ return '5 5'
5
+ case 'Dashed Small':
6
+ return '5 5'
7
+ case 'dashed-md':
8
+ return '10 5'
9
+ case 'Dashed Medium':
10
+ return '10 5'
11
+ case 'dashed-lg':
12
+ return '15 5'
13
+ case 'Dashed Large':
14
+ return '15 5'
15
+ default:
16
+ return 0
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ export const lineOptions = [
2
+ {
3
+ value: 'Dashed Small',
4
+ key: 'dashed-sm'
5
+ },
6
+ {
7
+ value: 'Dashed Medium',
8
+ key: 'dashed-md'
9
+ },
10
+ {
11
+ value: 'Dashed Large',
12
+ key: 'dashed-lg'
13
+ },
14
+ {
15
+ value: 'Solid Line',
16
+ key: 'solid-line'
17
+ }
18
+ ]
@@ -0,0 +1,7 @@
1
+ export const sortAsc = (a, b) => {
2
+ return a.toString().localeCompare(b.toString(), 'en', { numeric: true })
3
+ }
4
+
5
+ export const sortDesc = (a, b) => {
6
+ return b.toString().localeCompare(a.toString(), 'en', { numeric: true })
7
+ }
@@ -0,0 +1,20 @@
1
+ import { ChartConfig, Legend } from '../../types/ChartConfig'
2
+ import { computeMarginBottom } from '../computeMarginBottom'
3
+
4
+ describe('computeMarginBottom', () => {
5
+ it('should return correct value', () => {
6
+ const config = {
7
+ orientation: 'horizontal',
8
+ xAxis: { labelOffset: '10' },
9
+ yAxis: { label: null },
10
+ brush: { active: false },
11
+ isResponsiveTicks: true,
12
+ dynamicMarginTop: 20
13
+ }
14
+ const legend = { position: 'top' }
15
+ const currentViewport = 'md'
16
+ expect(computeMarginBottom(config as unknown as ChartConfig, legend as Legend, currentViewport)).toBe('0px')
17
+ config.yAxis.label = 'label'
18
+ expect(computeMarginBottom(config as unknown as ChartConfig, legend as Legend, currentViewport)).toBe('40px')
19
+ })
20
+ })
@@ -1,9 +1,10 @@
1
- import React, { useContext, useEffect } from 'react'
1
+ import React, { useContext, useEffect, useState } from 'react'
2
2
  import ConfigContext from '../ConfigContext'
3
-
3
+ import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
4
4
  export const useBarChart = () => {
5
- const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate } = useContext(ConfigContext)
5
+ const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate, setSeriesHighlight } = useContext(ConfigContext)
6
6
  const { orientation } = config
7
+ const [hoveredBar, setHoveredBar] = useState(null)
7
8
 
8
9
  const isHorizontal = orientation === 'horizontal'
9
10
  const barBorderWidth = 1
@@ -80,7 +81,7 @@ export const useBarChart = () => {
80
81
  if (!config.legend.colorCode && config.series.length > 1) {
81
82
  return currentBarColor
82
83
  }
83
- const palettesArr = colorPalettes[config.palette]
84
+ const palettesArr = config.customColors ?? colorPalettes[config.palette]
84
85
  const values = tableData.map(d => {
85
86
  return d[config.legend.colorCode]
86
87
  })
@@ -144,10 +145,10 @@ export const useBarChart = () => {
144
145
  }
145
146
 
146
147
  const getHighlightedBarColorByValue = value => {
147
- const match = config?.highlightedBarValues.filter(item => {
148
+ const match = config?.highlightedBarValues.find(item => {
148
149
  if (!item.value) return
149
150
  return config.xAxis.type === 'date' ? formatDate(parseDate(item.value)) === value : item.value === value
150
- })[0]
151
+ })
151
152
 
152
153
  if (!match?.color) return `rgba(255, 102, 1)`
153
154
  return match.color
@@ -161,8 +162,67 @@ export const useBarChart = () => {
161
162
  if (!match?.color) return false
162
163
  return match
163
164
  }
165
+ const generateIconSize = barWidth => {
166
+ if (barWidth < 4) {
167
+ return 1
168
+ }
169
+ if (barWidth < 5) {
170
+ return 4
171
+ }
172
+ if (barWidth < 10) {
173
+ return 6
174
+ }
175
+ if (barWidth < 15) {
176
+ return 7
177
+ }
178
+ if (barWidth < 20) {
179
+ return 8
180
+ }
181
+ if (barWidth < 90) {
182
+ return 8
183
+ }
184
+ return 0
185
+ }
186
+
187
+ const getAdditionalColumn = (series, xAxisDataValue) => {
188
+ if (!xAxisDataValue) return ''
189
+ const columns = config.columns
190
+ const columnsWithTooltips = []
191
+ let additionalTooltipItems = ''
192
+ const closestVal =
193
+ tableData.find(d => {
194
+ return d[config.xAxis.dataKey] === xAxisDataValue
195
+ }) || {}
196
+ Object.keys(columns).forEach(colKeys => {
197
+ if(series && config.columns[colKeys].series && config.columns[colKeys].series !== series) return
198
+ const formattingParams = {
199
+ addColPrefix: config.columns[colKeys].prefix,
200
+ addColSuffix: config.columns[colKeys].suffix,
201
+ addColRoundTo: config.columns[colKeys].roundToPlace ? config.columns[colKeys].roundToPlace : '',
202
+ addColCommas: config.columns[colKeys].commas
203
+ }
204
+
205
+ const formattedValue = formatColNumber(closestVal[config.columns[colKeys].name], 'left', true, config, formattingParams)
206
+ if (config.columns[colKeys].tooltips) {
207
+ columnsWithTooltips.push([config.columns[colKeys].label, formattedValue])
208
+ }
209
+ })
210
+ columnsWithTooltips.forEach(columnData => {
211
+ additionalTooltipItems += `${columnData[0]} : ${columnData[1]} <br/>`
212
+ })
213
+ return additionalTooltipItems
214
+ }
215
+
216
+ const onMouseOverBar = (categoryValue, barKey) => {
217
+ if (config.legend.highlightOnHover && config.legend.behavior === 'highlight' && barKey) setSeriesHighlight([barKey])
218
+ setHoveredBar(categoryValue)
219
+ }
220
+ const onMouseLeaveBar = () => {
221
+ if (config.legend.highlightOnHover && config.legend.behavior === 'highlight') setSeriesHighlight([])
222
+ }
164
223
 
165
224
  return {
225
+ generateIconSize,
166
226
  isHorizontal,
167
227
  barBorderWidth,
168
228
  lollipopBarWidth,
@@ -181,6 +241,11 @@ export const useBarChart = () => {
181
241
  updateBars,
182
242
  assignColorsToValues,
183
243
  getHighlightedBarColorByValue,
184
- getHighlightedBarByValue
244
+ getHighlightedBarByValue,
245
+ getAdditionalColumn,
246
+ hoveredBar,
247
+ setHoveredBar,
248
+ onMouseOverBar,
249
+ onMouseLeaveBar
185
250
  }
186
251
  }
@@ -0,0 +1,50 @@
1
+ import { colorPalettesChart as colorPalettes, twoColorPalette } from '@cdc/core/data/colorPalettes'
2
+ import { scaleOrdinal } from '@visx/scale'
3
+ import { useContext } from 'react'
4
+ import ConfigContext from '../ConfigContext'
5
+
6
+ const useColorScale = () => {
7
+ const { config, data } = useContext(ConfigContext)
8
+ const { visualizationSubType, visualizationType, series, legend } = config
9
+
10
+ const generatePalette = colorsCount => {
11
+ if (!series?.length) return []
12
+ const isSpecialType = ['Paired Bar', 'Deviation Bar'].includes(visualizationType)
13
+ const chosenPalette = isSpecialType ? config.twoColor.palette : config.palette
14
+ const allPalettes = { ...colorPalettes, ...twoColorPalette }
15
+ let palette = config.customColors || allPalettes[chosenPalette]
16
+ while (colorsCount > palette.length) palette = palette.concat(palette)
17
+ return palette.slice(0, colorsCount)
18
+ }
19
+
20
+ let colorScale = scaleOrdinal({
21
+ domain: config?.runtime?.seriesLabelsAll,
22
+ range: generatePalette(series.length)
23
+ })
24
+
25
+ if (visualizationType === 'Deviation Bar') {
26
+ const { targetLabel } = config.xAxis
27
+ colorScale = scaleOrdinal({
28
+ domain: [`Below ${targetLabel}`, `Above ${targetLabel}`],
29
+ range: generatePalette(2)
30
+ })
31
+ }
32
+ if (visualizationType === 'Bar' && visualizationSubType === 'regular' && series?.length === 1 && legend?.colorCode) {
33
+ const set = new Set(data.map(d => d[legend.colorCode]))
34
+ colorScale = scaleOrdinal({
35
+ domain: [...set],
36
+ range: generatePalette([...set].length)
37
+ })
38
+ }
39
+ if (config.series.some(s => s.name)) {
40
+ const set = new Set(series.map(d => d.name || d.dataKey))
41
+ colorScale = colorScale = scaleOrdinal({
42
+ domain: [...set],
43
+ range: generatePalette(series.length)
44
+ })
45
+ }
46
+
47
+ return { colorScale }
48
+ }
49
+
50
+ export default useColorScale
@@ -1,14 +1,36 @@
1
- const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAllLine }) => {
1
+ import { ChartConfig } from '../types/ChartConfig'
2
+
3
+ type UseMinMaxProps = {
4
+ /** config - standard chart config */
5
+ config: ChartConfig
6
+ /** minValue - starting minimum value */
7
+ minValue: number
8
+ /** maxValue - starting maximum value before transformations */
9
+ maxValue: number
10
+ /** existsPositiveValue - determines if axis should show values above/below 0 */
11
+ existPositiveValue: boolean
12
+ /** data - standard data array */
13
+ data: Object[]
14
+ /** isAllLine: if all series are line type including dashed lines */
15
+ isAllLine: boolean
16
+ }
17
+
18
+ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAllLine }: UseMinMaxProps) => {
2
19
  let min = 0
3
20
  let max = 0
4
21
 
22
+ // Implementation for left and right axis
23
+ let leftMax = 0
24
+ let rightMax = 0
25
+
5
26
  if (!data) {
6
27
  return { min, max }
7
28
  }
8
29
 
30
+ const { visualizationType, series } = config
9
31
  const { max: enteredMaxValue, min: enteredMinValue } = config.runtime.yAxis
10
32
  const minRequiredCIPadding = 1.15 // regardless of Editor if CI data, there must be 10% padding added
11
-
33
+
12
34
  // do validation bafore applying t0 charts
13
35
  const isMaxValid = existPositiveValue ? enteredMaxValue >= maxValue : enteredMaxValue >= 0
14
36
  const isMinValid = config.useLogScale ? enteredMinValue >= 0 : (enteredMinValue <= 0 && minValue >= 0) || (enteredMinValue <= minValue && minValue < 0)
@@ -16,31 +38,19 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
16
38
  min = enteredMinValue && isMinValid ? enteredMinValue : minValue
17
39
  max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
18
40
 
19
- let ciYMin = 0
20
- if (config.visualizationType === 'Bar' || config.visualizationType === 'Combo' || config.visualizationType === 'Deviation Bar') {
21
- let ciYMax = 0
22
- if (config.hasOwnProperty('confidenceKeys')) {
23
- let upperCIValues = data.map(function (d) {
24
- return d[config.confidenceKeys.upper]
25
- })
26
- ciYMax = Math.max.apply(Math, upperCIValues)
27
- if (ciYMax > max) max = ciYMax * minRequiredCIPadding // bump up the max plus some padding always
41
+ const { lower, upper } = config?.confidenceKeys || {}
28
42
 
29
- // check the min if lower confidence
30
- let lowerCIValues = data.map(function (d) {
31
- // if no lower CI then we need lowerCIValues to have nothing in it
32
- return d[config.confidenceKeys.lower] !== undefined ? d[config.confidenceKeys.lower] : ''
33
- })
34
- ciYMin = Math.min.apply(Math, lowerCIValues)
35
- if (ciYMin < min) min = ciYMin * minRequiredCIPadding // adjust the min + 10% padding for negative numbers to separate from axis
36
- }
43
+ if (lower && upper && config.visualizationType === 'Bar') {
44
+ const buffer = min < 0 ? 1.1 : 0
45
+ max = Math.max(maxValue, Math.max(...data.flatMap(d => [d[upper], d[lower]])) * 1.15)
46
+ min = Math.min(minValue, Math.min(...data.flatMap(d => [d[upper], d[lower]])) * 1.15) * buffer
37
47
  }
38
48
 
39
- if (config.visualizationType === 'Forecasting') {
49
+ if (config.series.filter(s => s?.type === 'Forecasting')) {
40
50
  const {
41
51
  runtime: { forecastingSeriesKeys }
42
52
  } = config
43
- if (forecastingSeriesKeys.length > 0) {
53
+ if (forecastingSeriesKeys?.length > 0) {
44
54
  // push all keys into an array
45
55
  let columnNames = []
46
56
 
@@ -73,10 +83,52 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
73
83
  }
74
84
  }
75
85
 
86
+ if (visualizationType === 'Combo') {
87
+ try {
88
+ if (!data) throw new Error('COVE: missing data while getting min/max for combo chart.')
89
+ // seperate the left and right axis items & get each sides series keys
90
+ let leftAxisSeriesItems = series.filter(s => s.axis === 'Left')
91
+ let rightAxisSeriesItems = series.filter(s => s.axis === 'Right')
92
+
93
+ const findMaxFromSeriesKeys = (data, seriesData, max, axis = 'left') => {
94
+ let stackedBarMax = 0
95
+ let axisSeriesKeys = seriesData.map(i => i.dataKey) || []
96
+
97
+ axisSeriesKeys.forEach(key => {
98
+ let _seriesData = seriesData.find(s => s.dataKey === key)
99
+ let _data = data.map(d => d[key])
100
+ let seriesMax = Math.max.apply(null, _data)
101
+ if (config.visualizationSubType === 'stacked' && axis === 'left' && _seriesData.type === 'Bar') {
102
+ stackedBarMax += seriesMax
103
+ }
104
+ if (seriesMax > max) {
105
+ max = seriesMax
106
+ }
107
+
108
+ if (max < stackedBarMax) {
109
+ max = stackedBarMax
110
+ }
111
+ })
112
+ return max
113
+ }
114
+ leftMax = findMaxFromSeriesKeys(data, leftAxisSeriesItems, leftMax, 'left')
115
+ rightMax = findMaxFromSeriesKeys(data, rightAxisSeriesItems, rightMax, 'right')
116
+
117
+ if (leftMax < enteredMaxValue) {
118
+ leftMax = enteredMaxValue
119
+ }
120
+ } catch (e) {
121
+ console.error(e.message)
122
+ }
123
+ }
124
+
76
125
  // this should not apply to bar charts if there is negative CI data
77
- if (((config.visualizationType === 'Bar' && ciYMin >= 0) || (config.visualizationType === 'Combo' && !isAllLine)) && min > 0) {
126
+ if ((visualizationType === 'Bar' || (visualizationType === 'Combo' && !isAllLine)) && min > 0) {
78
127
  min = 0
79
128
  }
129
+ if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && min < 0) {
130
+ min = min * 1.1
131
+ }
80
132
 
81
133
  if (config.visualizationType === 'Combo' && isAllLine) {
82
134
  if ((enteredMinValue === undefined || enteredMinValue === null || enteredMinValue === '') && min > 0) {
@@ -136,6 +188,6 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
136
188
  }
137
189
  }
138
190
 
139
- return { min, max }
191
+ return { min, max, leftMax, rightMax }
140
192
  }
141
193
  export default useMinMax
@@ -1,5 +1,5 @@
1
1
  import { scaleLinear } from '@visx/scale'
2
- import useReduceData from '../hooks/useReduceData'
2
+ import useReduceData from './useReduceData'
3
3
 
4
4
  export default function useRightAxis({ config, yMax = 0, data = [], updateConfig }) {
5
5
  const hasRightAxis = config.visualizationType === 'Combo' && config.orientation === 'vertical'
@@ -15,7 +15,15 @@ export default function useRightAxis({ config, yMax = 0, data = [], updateConfig
15
15
  return rightAxisData
16
16
  }
17
17
 
18
- const max = Math.max.apply(null, allRightAxisData(rightSeriesKeys))
18
+ let max = Math.max.apply(null, allRightAxisData(rightSeriesKeys))
19
+
20
+ if (config.yAxis.rightMax > max) {
21
+ max = config.yAxis.rightMax
22
+ }
23
+
24
+ if (config.yAxis.rightMin < minValue) {
25
+ minValue = config.yAxis.rightMin
26
+ }
19
27
 
20
28
  // if there is a bar series & the right axis doesn't include a negative number, default to zero
21
29
  const hasBarSeries = config.runtime?.barSeriesKeys?.length > 0
@@ -1,16 +1,30 @@
1
- import { scaleBand, scaleLinear, scaleLog, scalePoint, scaleTime } from '@visx/scale'
1
+ import { LogScaleConfig, scaleBand, scaleLinear, scaleLog, scalePoint, scaleTime } from '@visx/scale'
2
2
  import { useContext } from 'react'
3
3
  import ConfigContext from '../ConfigContext'
4
- // TODO move props in
4
+ import { ChartConfig } from '../types/ChartConfig'
5
+ import { ChartContext } from '../types/ChartContext'
6
+
7
+ type useScaleProps = {
8
+ config: ChartConfig // standard chart config
9
+ data: Object[] // standard data array
10
+ max: number // maximum value from useMinMax hook
11
+ min: number // minimum value from useMinMax hook
12
+ xAxisDataMapped: Object[] // array of x axis date/category items
13
+ xMax: number // chart svg width
14
+ yMax: number // chart svg height
15
+ }
5
16
 
6
- const useScales = properties => {
17
+ const useScales = (properties: useScaleProps) => {
7
18
  let { xAxisDataMapped, xMax, yMax, min, max, config, data } = properties
8
- const { rawData, dimensions } = useContext(ConfigContext)
19
+
20
+ const { rawData, dimensions } = useContext<ChartContext>(ConfigContext)
9
21
 
10
22
  const [screenWidth, screenHeight] = dimensions
11
23
  const seriesDomain = config.runtime.barSeriesKeys || config.runtime.seriesKeys
12
24
  const xAxisType = config.runtime.xAxis.type
13
25
  const isHorizontal = config.orientation === 'horizontal'
26
+ const getXAxisDataKeys = d => d[config.runtime.originalXAxis.dataKey]
27
+ const xAxisDataKeysMapped = data.map(d => getXAxisDataKeys(d))
14
28
 
15
29
  const { visualizationType } = config
16
30
 
@@ -21,6 +35,7 @@ const useScales = properties => {
21
35
  let g1xScale = null
22
36
  let seriesScale = null
23
37
  let xScaleNoPadding = null
38
+ let xScaleBrush = null
24
39
 
25
40
  const scaleTypes = {
26
41
  TIME: 'time',
@@ -41,6 +56,7 @@ const useScales = properties => {
41
56
 
42
57
  // handle Vertical bars
43
58
  if (!isHorizontal) {
59
+ xScaleBrush = composeScalePoint(xAxisDataKeysMapped, [0, xMax], 0.5)
44
60
  xScale = composeScalePoint(xAxisDataMapped, [0, xMax], 0.5)
45
61
  xScale.type = scaleTypes.POINT
46
62
  yScale = composeYScale(properties)
@@ -53,6 +69,7 @@ const useScales = properties => {
53
69
  domain: [Math.min(...xAxisDataMapped), Math.max(...xAxisDataMapped)],
54
70
  range: [0, xMax]
55
71
  })
72
+ xScaleBrush = xScale
56
73
  xScale.type = scaleTypes.LINEAR
57
74
  }
58
75
 
@@ -170,21 +187,49 @@ const useScales = properties => {
170
187
  const leftWidthOffsetMobile = (Number(config.forestPlot.leftWidthOffsetMobile) / 100) * xMax
171
188
 
172
189
  if (screenWidth > 480) {
173
- xScale = scaleLinear({
174
- domain: [Math.min(...data.map(d => parseFloat(d.Lower))) - xAxisPadding, Math.max(...data.map(d => parseFloat(d.Upper))) + xAxisPadding],
175
- range: [leftWidthOffset, xMax - rightWidthOffset],
176
- type: 'linear'
177
- })
190
+ if (config.forestPlot.type === 'Linear') {
191
+ xScale = scaleLinear({
192
+ domain: [Math.min(...data.map(d => parseFloat(d[config.forestPlot.lower]))) - xAxisPadding, Math.max(...data.map(d => parseFloat(d[config.forestPlot.upper]))) + xAxisPadding],
193
+ range: [leftWidthOffset, dimensions[0] - rightWidthOffset]
194
+ })
195
+ xScale.type = scaleTypes.LINEAR
196
+ }
197
+ if (config.forestPlot.type === 'Logarithmic') {
198
+ let max = Math.max(...data.map(d => parseFloat(d[config.forestPlot.upper])))
199
+ let fp_min = Math.min(...data.map(d => parseFloat(d[config.forestPlot.lower])))
200
+
201
+ xScale = scaleLog<LogScaleConfig>({
202
+ domain: [fp_min, max],
203
+ range: [leftWidthOffset, xMax - rightWidthOffset],
204
+ nice: true
205
+ })
206
+ xScale.type = scaleTypes.LOG
207
+ }
178
208
  } else {
179
- xScale = scaleLinear({
180
- domain: [Math.min(...data.map(d => parseFloat(d.Lower))) - xAxisPadding, Math.max(...data.map(d => parseFloat(d.Upper))) + xAxisPadding],
181
- range: [leftWidthOffsetMobile, xMax - rightWidthOffsetMobile],
182
- type: 'linear'
183
- })
209
+ if (config.forestPlot.type === 'Linear') {
210
+ xScale = scaleLinear({
211
+ domain: [Math.min(...data.map(d => parseFloat(d[config.forestPlot.lower]))) - xAxisPadding, Math.max(...data.map(d => parseFloat(d[config.forestPlot.upper]))) + xAxisPadding],
212
+ range: [leftWidthOffsetMobile, xMax - rightWidthOffsetMobile],
213
+ type: scaleTypes.LINEAR
214
+ })
215
+ }
216
+
217
+ if (config.forestPlot.type === 'Logarithmic') {
218
+ let max = Math.max(...data.map(d => parseFloat(d[config.forestPlot.upper])))
219
+ let fp_min = Math.min(...data.map(d => parseFloat(d[config.forestPlot.lower])))
220
+
221
+ xScale = scaleLog<LogScaleConfig>({
222
+ domain: [fp_min, max],
223
+ range: [leftWidthOffset, xMax - rightWidthOffset],
224
+ nice: true,
225
+ base: max > 1 ? 10 : 2,
226
+ round: false,
227
+ type: scaleTypes.LOG
228
+ })
229
+ }
184
230
  }
185
231
  }
186
-
187
- return { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding }
232
+ return { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding, xScaleBrush }
188
233
  }
189
234
 
190
235
  export default useScales
@@ -205,11 +250,13 @@ const composeXScale = ({ min, max, xMax, config }) => {
205
250
  })
206
251
  }
207
252
 
208
- const composeYScale = ({ min, max, yMax, config }) => {
253
+ const composeYScale = ({ min, max, yMax, config, leftMax }) => {
209
254
  // Adjust min value if using logarithmic scale
210
255
  min = config.useLogScale && min >= 0 && min < 1 ? min + 0.1 : min
211
256
  // Select the appropriate scale function
212
257
  const scaleFunc = config.useLogScale ? scaleLog : scaleLinear
258
+
259
+ if (config.visualizationType === 'Combo') max = leftMax
213
260
  // Return the configured scale function
214
261
  return scaleFunc({
215
262
  domain: [min, max],