@cdc/chart 4.23.7 → 4.23.8
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/LICENSE +201 -0
- package/dist/cdcchart.js +27964 -26942
- package/examples/feature/__data__/area-chart-date-apple.json +5122 -0
- package/examples/feature/__data__/city-temperature.json +2198 -0
- package/examples/feature/area/area-chart-category.json +45 -45
- package/examples/feature/area/area-chart-date-apple.json +10376 -0
- package/examples/feature/area/area-chart-date-city-temperature.json +4528 -0
- package/examples/feature/area/area-chart-date.json +111 -3
- package/examples/feature/forest-plot/broken.json +700 -0
- package/examples/feature/forest-plot/data.csv +24 -0
- package/examples/feature/forest-plot/forest-plot.json +717 -0
- package/examples/feature/pie/planet-pie-example-config.json +1 -1
- package/examples/private/confidence_interval_test.json +248 -0
- package/examples/private/tooltip-issue.json +45275 -0
- package/index.html +13 -11
- package/package.json +4 -3
- package/src/CdcChart.jsx +24 -14
- package/src/components/AreaChart.jsx +84 -59
- package/src/components/BarChart.Horizontal.jsx +251 -0
- package/src/components/BarChart.StackedHorizontal.jsx +118 -0
- package/src/components/BarChart.StackedVertical.jsx +93 -0
- package/src/components/BarChart.Vertical.jsx +204 -0
- package/src/components/BarChart.jsx +14 -674
- package/src/components/BarChartType.jsx +15 -0
- package/src/components/BrushHandle.jsx +17 -0
- package/src/components/DataTable.jsx +63 -21
- package/src/components/EditorPanel.jsx +351 -303
- package/src/components/ForestPlot.jsx +191 -0
- package/src/components/ForestPlotSettings.jsx +508 -0
- package/src/components/LineChart.jsx +2 -2
- package/src/components/LinearChart.jsx +115 -310
- package/src/data/initial-state.js +43 -0
- package/src/hooks/useBarChart.js +186 -0
- package/src/hooks/useEditorPermissions.js +218 -0
- package/src/hooks/useMinMax.js +15 -3
- package/src/hooks/useScales.js +45 -2
- package/src/hooks/useTooltip.jsx +407 -0
- package/src/scss/main.scss +7 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
type: 'chart',
|
|
3
|
+
debugSvg: false,
|
|
3
4
|
title: '',
|
|
4
5
|
showTitle: true,
|
|
5
6
|
showDownloadMediaButton: false,
|
|
7
|
+
showChartBrush: false,
|
|
6
8
|
theme: 'theme-blue',
|
|
7
9
|
animate: false,
|
|
8
10
|
fontSize: 'medium',
|
|
@@ -173,5 +175,46 @@ export default {
|
|
|
173
175
|
series: [],
|
|
174
176
|
tooltips: {
|
|
175
177
|
opacity: 90
|
|
178
|
+
},
|
|
179
|
+
forestPlot: {
|
|
180
|
+
startAt: 0,
|
|
181
|
+
width: 'auto',
|
|
182
|
+
colors: {
|
|
183
|
+
line: '',
|
|
184
|
+
shape: ''
|
|
185
|
+
},
|
|
186
|
+
estimateField: '',
|
|
187
|
+
estimateRadius: '',
|
|
188
|
+
lowerCiField: '',
|
|
189
|
+
upperCiField: '',
|
|
190
|
+
shape: '',
|
|
191
|
+
rowHeight: 20,
|
|
192
|
+
showZeroLine: false,
|
|
193
|
+
description: {
|
|
194
|
+
show: true,
|
|
195
|
+
text: 'description',
|
|
196
|
+
location: 0
|
|
197
|
+
},
|
|
198
|
+
result: {
|
|
199
|
+
show: true,
|
|
200
|
+
text: 'result',
|
|
201
|
+
location: 100
|
|
202
|
+
},
|
|
203
|
+
radius: {
|
|
204
|
+
min: 1,
|
|
205
|
+
max: 8,
|
|
206
|
+
scalingColumn: ''
|
|
207
|
+
},
|
|
208
|
+
regression: {
|
|
209
|
+
lower: 0,
|
|
210
|
+
upper: 0,
|
|
211
|
+
estimateField: 0
|
|
212
|
+
},
|
|
213
|
+
leftWidthOffset: 0,
|
|
214
|
+
rightWidthOffset: 0
|
|
215
|
+
},
|
|
216
|
+
brush: {
|
|
217
|
+
pattern_id: 'brush_pattern',
|
|
218
|
+
accent_color: '#ddd'
|
|
176
219
|
}
|
|
177
220
|
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import React, { useContext, useEffect } from 'react'
|
|
2
|
+
import ConfigContext from '../ConfigContext'
|
|
3
|
+
|
|
4
|
+
export const useBarChart = () => {
|
|
5
|
+
const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate } = useContext(ConfigContext)
|
|
6
|
+
const { orientation } = config
|
|
7
|
+
|
|
8
|
+
const isHorizontal = orientation === 'horizontal'
|
|
9
|
+
const barBorderWidth = 1
|
|
10
|
+
const lollipopBarWidth = config.lollipopSize === 'large' ? 7 : config.lollipopSize === 'medium' ? 6 : 5
|
|
11
|
+
const lollipopShapeSize = config.lollipopSize === 'large' ? 14 : config.lollipopSize === 'medium' ? 12 : 10
|
|
12
|
+
const isLabelBelowBar = config.yAxis.labelPlacement === 'Below Bar'
|
|
13
|
+
const displayNumbersOnBar = config.yAxis.displayNumbersOnBar
|
|
14
|
+
const section = config.orientation === 'horizontal' ? 'yAxis' : 'xAxis'
|
|
15
|
+
|
|
16
|
+
const isRounded = config.barStyle === 'rounded'
|
|
17
|
+
const isStacked = config.visualizationSubType === 'stacked'
|
|
18
|
+
const tipRounding = config.tipRounding
|
|
19
|
+
const radius = config.roundingStyle === 'standard' ? '8px' : config.roundingStyle === 'shallow' ? '5px' : config.roundingStyle === 'finger' ? '15px' : '0px'
|
|
20
|
+
const stackCount = config.runtime.seriesKeys.length
|
|
21
|
+
const fontSize = { small: 16, medium: 18, large: 20 }
|
|
22
|
+
const hasMultipleSeries = Object.keys(config.runtime.seriesLabels).length > 1
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (orientation === 'horizontal' && !config.yAxis.labelPlacement) {
|
|
26
|
+
updateConfig({
|
|
27
|
+
...config,
|
|
28
|
+
yAxis: {
|
|
29
|
+
...config,
|
|
30
|
+
labelPlacement: 'Below Bar'
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
}, [config, updateConfig]) // eslint-disable-line
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (config.isLollipopChart === false && config.barHeight < 25) {
|
|
38
|
+
updateConfig({ ...config, barHeight: 25 })
|
|
39
|
+
}
|
|
40
|
+
}, [config.isLollipopChart]) // eslint-disable-line
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (config.visualizationSubType === 'horizontal') {
|
|
44
|
+
updateConfig({
|
|
45
|
+
...config,
|
|
46
|
+
orientation: 'horizontal'
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
}, []) // eslint-disable-line
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (config.barStyle === 'lollipop' && !config.isLollipopChart) {
|
|
53
|
+
updateConfig({ ...config, isLollipopChart: true })
|
|
54
|
+
}
|
|
55
|
+
if (isRounded || config.barStyle === 'flat') {
|
|
56
|
+
updateConfig({ ...config, isLollipopChart: false })
|
|
57
|
+
}
|
|
58
|
+
}, [config.barStyle]) // eslint-disable-line
|
|
59
|
+
|
|
60
|
+
const applyRadius = index => {
|
|
61
|
+
if (index === undefined || index === null || !isRounded) return {}
|
|
62
|
+
let style = {}
|
|
63
|
+
|
|
64
|
+
if ((isStacked && index + 1 === stackCount) || !isStacked) {
|
|
65
|
+
style = isHorizontal ? { borderRadius: `0 ${radius} ${radius} 0` } : { borderRadius: `${radius} ${radius} 0 0` }
|
|
66
|
+
}
|
|
67
|
+
if (!isStacked && index === -1) {
|
|
68
|
+
style = isHorizontal ? { borderRadius: `${radius} 0 0 ${radius} ` } : { borderRadius: ` 0 0 ${radius} ${radius}` }
|
|
69
|
+
}
|
|
70
|
+
if (tipRounding === 'full' && isStacked && index === 0 && stackCount > 1) {
|
|
71
|
+
style = isHorizontal ? { borderRadius: `${radius} 0 0 ${radius}` } : { borderRadius: `0 0 ${radius} ${radius}` }
|
|
72
|
+
}
|
|
73
|
+
if (tipRounding === 'full' && ((isStacked && index === 0 && stackCount === 1) || !isStacked)) {
|
|
74
|
+
style = { borderRadius: radius }
|
|
75
|
+
}
|
|
76
|
+
return style
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const assignColorsToValues = (barsCount, barIndex, currentBarColor) => {
|
|
80
|
+
if (!config.legend.colorCode && config.series.length > 1) {
|
|
81
|
+
return currentBarColor
|
|
82
|
+
}
|
|
83
|
+
const palettesArr = colorPalettes[config.palette]
|
|
84
|
+
const values = tableData.map(d => {
|
|
85
|
+
return d[config.legend.colorCode]
|
|
86
|
+
})
|
|
87
|
+
// Map to hold unique values and their colors
|
|
88
|
+
let colorMap = new Map()
|
|
89
|
+
// Resultant array to hold colors to the values
|
|
90
|
+
let palette = []
|
|
91
|
+
|
|
92
|
+
for (let i = 0; i < values.length; i++) {
|
|
93
|
+
// If value not in map, add it and assign a color
|
|
94
|
+
if (!colorMap.has(values[i])) {
|
|
95
|
+
colorMap.set(values[i], palettesArr[colorMap.size % palettesArr.length])
|
|
96
|
+
}
|
|
97
|
+
// push the color to the result array
|
|
98
|
+
palette.push(colorMap.get(values[i]))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// loop throghy existing colors and extend if needed
|
|
102
|
+
while (palette.length < barsCount) {
|
|
103
|
+
palette = palette.concat(palette)
|
|
104
|
+
}
|
|
105
|
+
const barColor = palette[barIndex]
|
|
106
|
+
return barColor
|
|
107
|
+
}
|
|
108
|
+
const updateBars = defaultBars => {
|
|
109
|
+
// function updates stacked && regular && lollipop horizontal bars
|
|
110
|
+
if (config.visualizationType !== 'Bar' && !isHorizontal) return defaultBars
|
|
111
|
+
|
|
112
|
+
const barsArr = [...defaultBars]
|
|
113
|
+
let barHeight
|
|
114
|
+
|
|
115
|
+
const heights = {
|
|
116
|
+
stacked: config.barHeight,
|
|
117
|
+
lollipop: lollipopBarWidth
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!isStacked) {
|
|
121
|
+
barHeight = heights[config.isLollipopChart ? 'lollipop' : 'stacked'] * stackCount
|
|
122
|
+
} else {
|
|
123
|
+
barHeight = heights.stacked
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const labelHeight = isLabelBelowBar ? fontSize[config.fontSize] * 1.2 : 0
|
|
127
|
+
let barSpace = Number(config.barSpace)
|
|
128
|
+
|
|
129
|
+
// calculate height of container based height, space and fontSize of labels
|
|
130
|
+
let totalHeight = barsArr.length * (barHeight + labelHeight + barSpace)
|
|
131
|
+
|
|
132
|
+
if (isHorizontal) {
|
|
133
|
+
config.heights.horizontal = totalHeight
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// return new updated bars/groupes
|
|
137
|
+
return barsArr.map((bar, i) => {
|
|
138
|
+
// set bars Y dynamically to handle space between bars
|
|
139
|
+
let y = 0
|
|
140
|
+
bar.index !== 0 && (y = (barHeight + barSpace + labelHeight) * i)
|
|
141
|
+
|
|
142
|
+
return { ...bar, y: y, height: barHeight }
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const getHighlightedBarColorByValue = value => {
|
|
147
|
+
const match = config?.highlightedBarValues.filter(item => {
|
|
148
|
+
if (!item.value) return
|
|
149
|
+
return config.xAxis.type === 'date' ? formatDate(parseDate(item.value)) === value : item.value === value
|
|
150
|
+
})[0]
|
|
151
|
+
|
|
152
|
+
if (!match?.color) return `rgba(255, 102, 1)`
|
|
153
|
+
return match.color
|
|
154
|
+
}
|
|
155
|
+
const getHighlightedBarByValue = value => {
|
|
156
|
+
const match = config?.highlightedBarValues.filter(item => {
|
|
157
|
+
if (!item.value) return
|
|
158
|
+
return config.xAxis.type === 'date' ? formatDate(parseDate(item.value)) === value : item.value === value
|
|
159
|
+
})[0]
|
|
160
|
+
|
|
161
|
+
if (!match?.color) return false
|
|
162
|
+
return match
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
isHorizontal,
|
|
167
|
+
barBorderWidth,
|
|
168
|
+
lollipopBarWidth,
|
|
169
|
+
lollipopShapeSize,
|
|
170
|
+
isLabelBelowBar,
|
|
171
|
+
displayNumbersOnBar,
|
|
172
|
+
section,
|
|
173
|
+
isRounded,
|
|
174
|
+
isStacked,
|
|
175
|
+
tipRounding,
|
|
176
|
+
radius,
|
|
177
|
+
stackCount,
|
|
178
|
+
fontSize,
|
|
179
|
+
hasMultipleSeries,
|
|
180
|
+
applyRadius,
|
|
181
|
+
updateBars,
|
|
182
|
+
assignColorsToValues,
|
|
183
|
+
getHighlightedBarColorByValue,
|
|
184
|
+
getHighlightedBarByValue
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import ConfigContext from '../ConfigContext'
|
|
3
|
+
|
|
4
|
+
export const useEditorPermissions = () => {
|
|
5
|
+
const { config } = useContext(ConfigContext)
|
|
6
|
+
const { visualizationType, series, orientation } = config
|
|
7
|
+
|
|
8
|
+
// Overall support for the chart types
|
|
9
|
+
// prettier-ignore
|
|
10
|
+
const enabledChartTypes = [
|
|
11
|
+
'Area Chart',
|
|
12
|
+
'Bar',
|
|
13
|
+
'Box Plot',
|
|
14
|
+
'Combo',
|
|
15
|
+
'Deviation Bar',
|
|
16
|
+
'Forecasting',
|
|
17
|
+
// 'Forest Plot',
|
|
18
|
+
'Line',
|
|
19
|
+
'Paired Bar',
|
|
20
|
+
'Pie',
|
|
21
|
+
'Scatter Plot',
|
|
22
|
+
'Spark Line'
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
const visHasLabelOnData = () => {
|
|
26
|
+
const disabledCharts = ['Area Chart', 'Box Plot', 'Pie', 'Scatter Plot', 'Forest Plot']
|
|
27
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
28
|
+
return true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const visCanAnimate = () => {
|
|
32
|
+
const disabledCharts = ['Area Chart', 'Scatter Plot', 'Box Plot', 'Forest Plot']
|
|
33
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const visHasLegend = () => {
|
|
38
|
+
switch (visualizationType) {
|
|
39
|
+
case 'Box Plot':
|
|
40
|
+
return false
|
|
41
|
+
case 'Forest Plot':
|
|
42
|
+
return false
|
|
43
|
+
default:
|
|
44
|
+
return true
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const visHasNumbersOnBars = () => {
|
|
49
|
+
if (visualizationType === 'Forest Plot') return false
|
|
50
|
+
if (config.orientation === 'horizontal' && (config.yAxis.labelPlacement === 'Below Bar' || config.yAxis.labelPlacement === 'On Date/Category Axis' || config.visualizationType === 'Paired Bar' || config.visualizationType === 'Deviation Bar')) return true
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const visHasAnchors = () => {
|
|
55
|
+
switch (visualizationType) {
|
|
56
|
+
case 'Area Chart':
|
|
57
|
+
return true
|
|
58
|
+
case 'Combo':
|
|
59
|
+
return true
|
|
60
|
+
case 'Line':
|
|
61
|
+
return true
|
|
62
|
+
case 'Bar':
|
|
63
|
+
return true
|
|
64
|
+
case 'Scatter Plot':
|
|
65
|
+
return true
|
|
66
|
+
default:
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const visHasBarBorders = () => {
|
|
72
|
+
const disabledCharts = ['Box Plot', 'Scatter Plot', 'Pie']
|
|
73
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
74
|
+
return series?.some(series => series.type === 'Bar' || series.type === 'Paired Bar' || series.type === 'Deviation Bar')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const visHasDataCutoff = () => {
|
|
78
|
+
switch (visualizationType) {
|
|
79
|
+
case 'Forest Plot':
|
|
80
|
+
return false
|
|
81
|
+
case 'Box Plot':
|
|
82
|
+
return false
|
|
83
|
+
case 'Pie':
|
|
84
|
+
return false
|
|
85
|
+
default:
|
|
86
|
+
return true
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const visSupportsTooltipLines = () => {
|
|
91
|
+
const enabledCharts = ['Combo', 'Forecasting', 'Area Chart', 'Line', 'Bar']
|
|
92
|
+
if (enabledCharts.includes(visualizationType)) return true
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const visSupportsSequentialPallete = () => {
|
|
97
|
+
const disabledCharts = ['Paired Bar', 'Deviation Bar', 'Forest Plot']
|
|
98
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
99
|
+
return true
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const visSupportsNonSequentialPallete = () => {
|
|
103
|
+
const disabledCharts = ['Paired Bar', 'Deviation Bar', 'Forest Plot']
|
|
104
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
105
|
+
return true
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const visSupportsReverseColorPalette = () => {
|
|
109
|
+
const disabledCharts = ['Forest Plot', 'Paired Bar', 'Deviation Bar']
|
|
110
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
111
|
+
return true
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const visSupportsDateCategoryAxisLabel = () => {
|
|
115
|
+
const disabledCharts = ['Forest Plot']
|
|
116
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
117
|
+
return true
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const visSupportsDateCategoryAxisLine = () => {
|
|
121
|
+
const disabledCharts = ['Forest Plot']
|
|
122
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
123
|
+
return true
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const visSupportsDateCategoryAxisTicks = () => {
|
|
127
|
+
const disabledCharts = ['Forest Plot']
|
|
128
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
129
|
+
return true
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const visSupportsDateCategoryTickRotation = () => {
|
|
133
|
+
const disabledCharts = ['Forest Plot']
|
|
134
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
135
|
+
return true
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const visSupportsDateCategoryNumTicks = () => {
|
|
139
|
+
const disabledCharts = ['Forest Plot']
|
|
140
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
141
|
+
return true
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const visSupportsRegions = () => {
|
|
145
|
+
const disabledCharts = ['Forest Plot', 'Pie', 'Paired Bar']
|
|
146
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
147
|
+
return true
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const visSupportsFilters = () => {
|
|
151
|
+
const disabledCharts = ['Forest Plot']
|
|
152
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
153
|
+
return true
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const visSupportsValueAxisGridLines = () => {
|
|
157
|
+
const disabledCharts = ['Forest Plot']
|
|
158
|
+
if (orientation === 'horizontal') return false
|
|
159
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
160
|
+
return true
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// implement later
|
|
164
|
+
const visSupportsValueAxisTicks = () => {
|
|
165
|
+
return true
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// implement later
|
|
169
|
+
const visSupportsValueAxisLine = () => {
|
|
170
|
+
return true
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// implement later
|
|
174
|
+
const visSupportsValueAxisLabels = () => {
|
|
175
|
+
return true
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const visSupportsBarSpace = () => {
|
|
179
|
+
const disabledCharts = ['Forest Plot']
|
|
180
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
181
|
+
if (orientation === 'horizontal' || visualizationType === 'Paired Bar') return true
|
|
182
|
+
return false
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const visSupportsBarThickness = () => {
|
|
186
|
+
const disabledCharts = ['Forest Plot']
|
|
187
|
+
if (disabledCharts.includes(visualizationType)) return false
|
|
188
|
+
return true
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
enabledChartTypes,
|
|
193
|
+
visHasLabelOnData,
|
|
194
|
+
visHasNumbersOnBars,
|
|
195
|
+
visHasAnchors,
|
|
196
|
+
visHasBarBorders,
|
|
197
|
+
visHasDataCutoff,
|
|
198
|
+
visCanAnimate,
|
|
199
|
+
visHasLegend,
|
|
200
|
+
visSupportsTooltipLines,
|
|
201
|
+
visSupportsNonSequentialPallete,
|
|
202
|
+
visSupportsSequentialPallete,
|
|
203
|
+
visSupportsReverseColorPalette,
|
|
204
|
+
visSupportsDateCategoryAxisLabel,
|
|
205
|
+
visSupportsDateCategoryAxisLine,
|
|
206
|
+
visSupportsDateCategoryAxisTicks,
|
|
207
|
+
visSupportsDateCategoryTickRotation,
|
|
208
|
+
visSupportsDateCategoryNumTicks,
|
|
209
|
+
visSupportsRegions,
|
|
210
|
+
visSupportsFilters,
|
|
211
|
+
visSupportsValueAxisGridLines,
|
|
212
|
+
visSupportsValueAxisTicks,
|
|
213
|
+
visSupportsValueAxisLine,
|
|
214
|
+
visSupportsValueAxisLabels,
|
|
215
|
+
visSupportsBarSpace,
|
|
216
|
+
visSupportsBarThickness
|
|
217
|
+
}
|
|
218
|
+
}
|
package/src/hooks/useMinMax.js
CHANGED
|
@@ -7,7 +7,8 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const { max: enteredMaxValue, min: enteredMinValue } = config.runtime.yAxis
|
|
10
|
-
|
|
10
|
+
const minRequiredCIPadding = 1.15 // regardless of Editor if CI data, there must be 10% padding added
|
|
11
|
+
|
|
11
12
|
// do validation bafore applying t0 charts
|
|
12
13
|
const isMaxValid = existPositiveValue ? enteredMaxValue >= maxValue : enteredMaxValue >= 0
|
|
13
14
|
const isMinValid = config.useLogScale ? enteredMinValue >= 0 : (enteredMinValue <= 0 && minValue >= 0) || (enteredMinValue <= minValue && minValue < 0)
|
|
@@ -15,6 +16,7 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
15
16
|
min = enteredMinValue && isMinValid ? enteredMinValue : minValue
|
|
16
17
|
max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
|
|
17
18
|
|
|
19
|
+
let ciYMin = 0
|
|
18
20
|
if (config.visualizationType === 'Bar' || config.visualizationType === 'Combo' || config.visualizationType === 'Deviation Bar') {
|
|
19
21
|
let ciYMax = 0
|
|
20
22
|
if (config.hasOwnProperty('confidenceKeys')) {
|
|
@@ -22,7 +24,15 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
22
24
|
return d[config.confidenceKeys.upper]
|
|
23
25
|
})
|
|
24
26
|
ciYMax = Math.max.apply(Math, upperCIValues)
|
|
25
|
-
if (ciYMax > max) max = ciYMax // bump up the max
|
|
27
|
+
if (ciYMax > max) max = ciYMax * minRequiredCIPadding // bump up the max plus some padding always
|
|
28
|
+
|
|
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
|
|
26
36
|
}
|
|
27
37
|
}
|
|
28
38
|
|
|
@@ -63,9 +73,11 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
63
73
|
}
|
|
64
74
|
}
|
|
65
75
|
|
|
66
|
-
|
|
76
|
+
// 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) {
|
|
67
78
|
min = 0
|
|
68
79
|
}
|
|
80
|
+
|
|
69
81
|
if (config.visualizationType === 'Combo' && isAllLine) {
|
|
70
82
|
if ((enteredMinValue === undefined || enteredMinValue === null || enteredMinValue === '') && min > 0) {
|
|
71
83
|
min = 0
|
package/src/hooks/useScales.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { scaleBand, scaleLinear, scaleLog, scalePoint, scaleTime } from '@visx/scale'
|
|
2
|
+
import { useContext } from 'react'
|
|
3
|
+
import ConfigContext from '../ConfigContext'
|
|
4
|
+
// TODO move props in
|
|
2
5
|
|
|
3
6
|
const useScales = properties => {
|
|
4
7
|
let { xAxisDataMapped, xMax, yMax, min, max, config, data } = properties
|
|
8
|
+
const { rawData, dimensions } = useContext(ConfigContext)
|
|
9
|
+
|
|
10
|
+
const [screenWidth, screenHeight] = dimensions
|
|
5
11
|
const seriesDomain = config.runtime.barSeriesKeys || config.runtime.seriesKeys
|
|
6
12
|
const xAxisType = config.runtime.xAxis.type
|
|
7
13
|
const isHorizontal = config.orientation === 'horizontal'
|
|
@@ -120,11 +126,11 @@ const useScales = properties => {
|
|
|
120
126
|
const offset = 1.02 // Offset of the ticks/values from the Axis
|
|
121
127
|
let groupOneMax = Math.max.apply(
|
|
122
128
|
Math,
|
|
123
|
-
data.map(d => d[config.series[0]
|
|
129
|
+
data.map(d => d[config.series[0]?.dataKey])
|
|
124
130
|
)
|
|
125
131
|
let groupTwoMax = Math.max.apply(
|
|
126
132
|
Math,
|
|
127
|
-
data.map(d => d[config.series[1]
|
|
133
|
+
data.map(d => d[config.series[1]?.dataKey])
|
|
128
134
|
)
|
|
129
135
|
|
|
130
136
|
// group one
|
|
@@ -141,6 +147,43 @@ const useScales = properties => {
|
|
|
141
147
|
})
|
|
142
148
|
}
|
|
143
149
|
|
|
150
|
+
if (visualizationType === 'Forest Plot') {
|
|
151
|
+
const resolvedYRange = () => {
|
|
152
|
+
if (config.forestPlot.regression.showDiamond || config.forestPlot.regression.description) {
|
|
153
|
+
return [0 + config.forestPlot.rowHeight * 2, yMax - config.forestPlot.rowHeight]
|
|
154
|
+
} else {
|
|
155
|
+
return [0 + config.forestPlot.rowHeight * 2, yMax]
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
yScale = scaleLinear({
|
|
160
|
+
domain: [0, rawData.length],
|
|
161
|
+
range: resolvedYRange()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
const xAxisPadding = 5
|
|
165
|
+
|
|
166
|
+
const leftWidthOffset = (Number(config.forestPlot.leftWidthOffset) / 100) * xMax
|
|
167
|
+
const rightWidthOffset = (Number(config.forestPlot.rightWidthOffset) / 100) * xMax
|
|
168
|
+
|
|
169
|
+
const rightWidthOffsetMobile = (Number(config.forestPlot.rightWidthOffsetMobile) / 100) * xMax
|
|
170
|
+
const leftWidthOffsetMobile = (Number(config.forestPlot.leftWidthOffsetMobile) / 100) * xMax
|
|
171
|
+
|
|
172
|
+
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
|
+
})
|
|
178
|
+
} 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
|
+
})
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
144
187
|
return { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding }
|
|
145
188
|
}
|
|
146
189
|
|