@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.
- package/dist/cdcchart.js +34606 -32218
- package/examples/feature/bar/additional-column-tooltip.json +446 -0
- package/examples/feature/bar/example-bar-chart.json +1 -46
- package/examples/feature/bar/lollipop.json +156 -0
- package/examples/feature/bar/tall-data.json +98 -0
- package/examples/feature/combo/planet-combo-example-config.json +99 -9
- package/examples/feature/dev-4261.json +399 -0
- package/examples/feature/forest-plot/forest-plot.json +63 -19
- package/examples/feature/forest-plot/{broken.json → linear.json} +77 -23
- package/examples/feature/forest-plot/log.json +26 -0
- package/examples/feature/forest-plot/logarithmic.json +271 -0
- package/examples/feature/line/line-chart-preliminary.json +346 -0
- package/examples/feature/line/line-points.json +340 -0
- package/examples/feature/regions/index.json +462 -0
- package/examples/feature/scatterplot/scatterplot.json +272 -33
- package/examples/gallery/bar-chart-vertical/combo-line-chart.json +181 -48
- package/examples/private/chart-t.json +3740 -0
- package/examples/private/combo.json +369 -0
- package/examples/private/epi-data.csv +13 -0
- package/examples/private/epi-data.json +62 -0
- package/examples/private/epi.json +403 -0
- package/examples/private/occupancy.json +109283 -0
- package/examples/private/prod-line-config.json +401 -0
- package/examples/private/region-data.json +822 -0
- package/examples/private/region-testing.json +312 -0
- package/examples/private/scaling.json +45325 -0
- package/examples/private/testing-data.json +1739 -0
- package/examples/private/testing.json +816 -0
- package/examples/sparkline-multilple.json +846 -0
- package/index.html +12 -8
- package/package.json +3 -3
- package/src/CdcChart.tsx +42 -211
- package/src/ConfigContext.tsx +6 -0
- package/src/_stories/Chart.stories.tsx +188 -0
- package/src/_stories/Chart.tooltip.stories.tsx +305 -0
- package/src/_stories/ChartBrush.stories.tsx +19 -0
- package/src/_stories/ChartEditor.stories.tsx +22 -0
- package/src/_stories/ChartLine.preliminary.tsx +19 -0
- package/src/_stories/ChartSuppress.stories.tsx +19 -0
- package/src/_stories/_mock/brush_mock.json +393 -0
- package/src/_stories/_mock/pie_config.json +191 -0
- package/src/_stories/_mock/pie_data.json +218 -0
- package/src/_stories/_mock/preliminary_mock.json +346 -0
- package/src/_stories/_mock/suppress_mock.json +911 -0
- package/src/components/{AreaChart.Stacked.jsx → AreaChart/components/AreaChart.Stacked.jsx} +6 -7
- package/src/components/{AreaChart.jsx → AreaChart/components/AreaChart.jsx} +7 -36
- package/src/components/AreaChart/index.tsx +4 -0
- package/src/components/{BarChart.Horizontal.jsx → BarChart/components/BarChart.Horizontal.tsx} +111 -34
- package/src/components/{BarChart.StackedHorizontal.jsx → BarChart/components/BarChart.StackedHorizontal.tsx} +55 -20
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +106 -0
- package/src/components/{BarChart.Vertical.jsx → BarChart/components/BarChart.Vertical.tsx} +162 -34
- package/src/components/BarChart/components/BarChart.jsx +39 -0
- package/src/components/{BarChartType.jsx → BarChart/components/BarChartType.jsx} +0 -2
- package/src/components/BarChart/components/context.tsx +13 -0
- package/src/components/BarChart/index.tsx +3 -0
- package/src/components/{BoxPlot.jsx → BoxPlot/BoxPlot.jsx} +1 -1
- package/src/components/BoxPlot/index.tsx +3 -0
- package/src/components/DeviationBar.jsx +4 -3
- package/src/components/{EditorPanel.jsx → EditorPanel/EditorPanel.tsx} +807 -865
- package/src/components/EditorPanel/components/Panel.DateHighlighting.tsx +109 -0
- package/src/components/{ForestPlotSettings.jsx → EditorPanel/components/Panel.ForestPlotSettings.tsx} +190 -220
- package/src/components/EditorPanel/components/Panel.Regions.tsx +168 -0
- package/src/components/{Series.jsx → EditorPanel/components/Panel.Series.tsx} +23 -4
- package/src/components/EditorPanel/components/PanelProps.ts +3 -0
- package/src/components/EditorPanel/components/Panels.tsx +13 -0
- package/src/components/EditorPanel/components/panels.scss +72 -0
- package/src/components/EditorPanel/editor-panel.scss +751 -0
- package/src/components/EditorPanel/index.tsx +3 -0
- package/src/{hooks → components/EditorPanel}/useEditorPermissions.js +50 -5
- package/src/components/{Forecasting.jsx → Forecasting/Forecasting.jsx} +1 -1
- package/src/components/Forecasting/index.tsx +3 -0
- package/src/components/ForestPlot/ForestPlot.tsx +254 -0
- package/src/components/ForestPlot/ForestPlotProps.ts +18 -0
- package/src/components/ForestPlot/index.scss +1 -0
- package/src/components/ForestPlot/index.tsx +3 -0
- package/src/components/Legend/Legend.tsx +347 -0
- package/src/components/Legend/index.tsx +3 -0
- package/src/components/LineChart/LineChartProps.ts +46 -0
- package/src/components/{LineChart.Circle.tsx → LineChart/components/LineChart.Circle.tsx} +36 -30
- package/src/components/LineChart/helpers.ts +45 -0
- package/src/components/LineChart/index.scss +1 -0
- package/src/components/{LineChart.tsx → LineChart/index.tsx} +83 -42
- package/src/components/LinearChart.jsx +125 -82
- package/src/components/PairedBarChart.jsx +2 -2
- package/src/components/{PieChart.jsx → PieChart/PieChart.tsx} +16 -7
- package/src/components/PieChart/index.tsx +3 -0
- package/src/components/Regions/components/Regions.tsx +135 -0
- package/src/components/Regions/index.tsx +3 -0
- package/src/components/{ScatterPlot.jsx → ScatterPlot/ScatterPlot.jsx} +3 -3
- package/src/components/ScatterPlot/index.tsx +3 -0
- package/src/components/{SparkLine.jsx → Sparkline/SparkLine.jsx} +2 -2
- package/src/components/Sparkline/index.tsx +3 -0
- package/src/components/ZoomBrush.tsx +168 -0
- package/src/data/initial-state.js +30 -16
- package/src/helpers/abbreviateNumber.ts +17 -0
- package/src/helpers/computeMarginBottom.ts +55 -0
- package/src/helpers/filterData.ts +18 -0
- package/src/helpers/generateColorsArray.ts +8 -0
- package/src/helpers/getQuartiles.ts +30 -0
- package/src/helpers/handleChartAriaLabels.ts +19 -0
- package/src/helpers/handleLineType.ts +18 -0
- package/src/helpers/lineOptions.ts +18 -0
- package/src/helpers/sort.ts +7 -0
- package/src/helpers/tests/computeMarginBottom.test.ts +20 -0
- package/src/hooks/useBarChart.js +72 -7
- package/src/hooks/useColorScale.ts +50 -0
- package/src/hooks/{useMinMax.js → useMinMax.ts} +75 -23
- package/src/hooks/{useRightAxis.js → useRightAxis.ts} +10 -2
- package/src/hooks/{useScales.js → useScales.ts} +64 -17
- package/src/hooks/{useTooltip.jsx → useTooltip.tsx} +84 -55
- package/src/scss/main.scss +70 -38
- package/src/types/ChartConfig.ts +178 -0
- package/src/types/ChartContext.ts +54 -0
- package/src/types/ForestPlot.ts +53 -0
- package/examples/feature/scatterplot/scatterplot-continuous.csv +0 -17
- package/src/ConfigContext.jsx +0 -5
- package/src/components/BarChart.StackedVertical.jsx +0 -95
- package/src/components/BarChart.jsx +0 -30
- package/src/components/ForestPlot.jsx +0 -191
- package/src/components/Legend.jsx +0 -277
- package/src/scss/LinearChart.scss +0 -0
- package/src/scss/editor-panel.scss +0 -745
- package/src/scss/legend.scss +0 -206
- package/src/scss/mixins.scss +0 -0
- 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,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
|
+
})
|
package/src/hooks/useBarChart.js
CHANGED
|
@@ -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.
|
|
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
|
-
})
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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.
|
|
49
|
+
if (config.series.filter(s => s?.type === 'Forecasting')) {
|
|
40
50
|
const {
|
|
41
51
|
runtime: { forecastingSeriesKeys }
|
|
42
52
|
} = config
|
|
43
|
-
if (forecastingSeriesKeys
|
|
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 ((
|
|
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 '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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],
|