@cdc/chart 4.25.3-6 → 4.25.5-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 (84) hide show
  1. package/dist/cdcchart-1a1724a1.es.js +4886 -0
  2. package/dist/cdcchart.js +50347 -75521
  3. package/index.html +1 -0
  4. package/package.json +22 -27
  5. package/src/CdcChart.tsx +1 -22
  6. package/src/CdcChartComponent.tsx +35 -21
  7. package/src/_stories/Chart.CI.stories.tsx +43 -0
  8. package/src/_stories/Chart.DynamicSeries.stories.tsx +68 -49
  9. package/src/_stories/Chart.Legend.Gradient.stories.tsx +6 -0
  10. package/src/_stories/Chart.stories.tsx +7 -16
  11. package/src/_stories/_mock/bar_chart_ci_labels.json +620 -0
  12. package/src/_stories/_mock/barchart_labels.mock.json +612 -0
  13. package/src/_stories/_mock/dynamic_series_bar_config.json +1 -1
  14. package/src/_stories/_mock/dynamic_series_suppression_mock.json +610 -0
  15. package/{examples/private/line-issue.json → src/_stories/_mock/legend_groupBy_mock.json} +46 -69
  16. package/src/components/Annotations/components/AnnotationDropdown.tsx +2 -2
  17. package/src/components/AreaChart/components/AreaChart.jsx +33 -5
  18. package/src/components/Axis/Categorical.Axis.tsx +2 -2
  19. package/src/components/BarChart/components/BarChart.Horizontal.tsx +51 -41
  20. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +19 -9
  21. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +20 -9
  22. package/src/components/BarChart/components/BarChart.Vertical.tsx +48 -31
  23. package/src/components/BarChart/components/{BarChart.jsx → BarChart.tsx} +23 -5
  24. package/src/components/BarChart/components/context.tsx +20 -2
  25. package/src/components/BarChart/helpers/getBarHeights.ts +47 -0
  26. package/src/components/BarChart/helpers/index.ts +5 -2
  27. package/src/components/BarChart/helpers/tests/getBarHeights.test.ts +83 -0
  28. package/src/{hooks → components/BarChart/helpers}/useBarChart.ts +11 -47
  29. package/src/components/BoxPlot/BoxPlot.tsx +2 -1
  30. package/src/components/DeviationBar.jsx +2 -1
  31. package/src/components/EditorPanel/EditorPanel.tsx +60 -24
  32. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +34 -34
  33. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +51 -25
  34. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +10 -3
  35. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +4 -3
  36. package/src/components/EditorPanel/useEditorPermissions.ts +1 -4
  37. package/src/components/ForestPlot/ForestPlot.tsx +2 -2
  38. package/src/components/Legend/Legend.Component.tsx +69 -58
  39. package/src/components/Legend/Legend.Suppression.tsx +12 -22
  40. package/src/components/Legend/Legend.tsx +3 -1
  41. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +40 -0
  42. package/src/components/Legend/LegendGroup/LegendGroup.tsx +103 -0
  43. package/src/components/Legend/LegendGroup/index.tsx +3 -0
  44. package/src/components/Legend/helpers/createFormatLabels.tsx +28 -0
  45. package/src/components/LineChart/LineChartProps.ts +3 -1
  46. package/src/components/LineChart/components/LineChart.Circle.tsx +77 -119
  47. package/src/components/LineChart/helpers.ts +133 -56
  48. package/src/components/LineChart/index.tsx +125 -60
  49. package/src/components/LinearChart.tsx +74 -115
  50. package/src/components/PairedBarChart.jsx +3 -2
  51. package/src/components/PieChart/PieChart.tsx +71 -91
  52. package/src/components/ScatterPlot/ScatterPlot.jsx +5 -0
  53. package/src/components/Sparkline/components/SparkLine.tsx +80 -18
  54. package/src/components/ZoomBrush.tsx +4 -4
  55. package/src/data/initial-state.js +4 -2
  56. package/src/helpers/countNumOfTicks.ts +1 -1
  57. package/src/helpers/dataHelpers.ts +31 -0
  58. package/src/helpers/sizeHelpers.ts +23 -0
  59. package/src/hooks/useMinMax.ts +21 -28
  60. package/src/hooks/useRightAxis.ts +4 -2
  61. package/src/hooks/useScales.ts +12 -14
  62. package/src/hooks/useTooltip.tsx +204 -203
  63. package/src/index.jsx +2 -2
  64. package/src/scss/main.scss +13 -6
  65. package/src/store/chart.actions.ts +1 -1
  66. package/src/types/ChartConfig.ts +7 -1
  67. package/LICENSE +0 -201
  68. package/examples/private/DEV-8850-2.json +0 -493
  69. package/examples/private/DEV-9822.json +0 -574
  70. package/examples/private/DEV-9840.json +0 -553
  71. package/examples/private/DEV-9850-3.json +0 -461
  72. package/examples/private/chart.json +0 -1084
  73. package/examples/private/ci_formatted.json +0 -202
  74. package/examples/private/ci_issue.json +0 -3016
  75. package/examples/private/completed.json +0 -634
  76. package/examples/private/dem-data-long.csv +0 -20
  77. package/examples/private/dem-data-long.json +0 -36
  78. package/examples/private/demographic_data.csv +0 -157
  79. package/examples/private/demographic_data.json +0 -2654
  80. package/examples/private/demographic_dynamic.json +0 -443
  81. package/examples/private/demographic_standard.json +0 -560
  82. package/examples/private/ehdi.json +0 -29939
  83. package/examples/private/not-loading.json +0 -360
  84. package/examples/private/test.json +0 -493
@@ -18,17 +18,29 @@ type SparkLineProps = {
18
18
 
19
19
  const SparkLine: React.FC<SparkLineProps> = props => {
20
20
  const { width: parentWidth, height: parentHeight } = props
21
- const { transformedData: data, config, parseDate, formatDate, seriesHighlight, formatNumber, colorScale, handleChartAriaLabels } = useContext(ConfigContext)
21
+ const {
22
+ transformedData: data,
23
+ config,
24
+ parseDate,
25
+ formatDate,
26
+ seriesHighlight,
27
+ formatNumber,
28
+ colorScale,
29
+ handleChartAriaLabels
30
+ } = useContext(ConfigContext)
22
31
  let width = Number(parentWidth)
23
32
  const { minValue, maxValue } = useReduceData(config, data, ConfigContext)
24
33
 
25
- const margin = { top: 5, right: 10, bottom: 10, left: 10 }
34
+ const margin = { top: 5, right: 20, bottom: 10, left: 10 }
26
35
  const height = Number(parentHeight)
27
36
 
28
37
  const xMax = width - config.runtime.yAxis.size
29
38
  const yMax = height - margin.top - 20
30
39
 
31
- const getXAxisData = d => (config.runtime.xAxis.type === 'date' ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime() : d[config.runtime.originalXAxis.dataKey])
40
+ const getXAxisData = d =>
41
+ config.runtime.xAxis.type === 'date'
42
+ ? parseDate(d[config.runtime.originalXAxis.dataKey]).getTime()
43
+ : d[config.runtime.originalXAxis.dataKey]
32
44
  const getYAxisData = (d, seriesKey) => d[seriesKey]
33
45
 
34
46
  let xScale
@@ -61,7 +73,10 @@ const SparkLine: React.FC<SparkLineProps> = props => {
61
73
  range: [0, xMax]
62
74
  })
63
75
 
64
- yScale = config.runtime.xAxis.type === 'date' ? scaleLinear({ domain: [Math.min(...xAxisDataMapped), Math.max(...xAxisDataMapped)] }) : scalePoint({ domain: xAxisDataMapped, padding: 0.5 })
76
+ yScale =
77
+ config.runtime.xAxis.type === 'date'
78
+ ? scaleLinear({ domain: [Math.min(...xAxisDataMapped), Math.max(...xAxisDataMapped)] })
79
+ : scalePoint({ domain: xAxisDataMapped, padding: 0.5 })
65
80
 
66
81
  seriesScale = scalePoint({
67
82
  domain: config.runtime.barSeriesKeys || config.runtime.seriesKeys,
@@ -94,7 +109,14 @@ const SparkLine: React.FC<SparkLineProps> = props => {
94
109
 
95
110
  return (
96
111
  <ErrorBoundary component='SparkLine'>
97
- <svg role='img' aria-label={handleChartAriaLabels(config)} width={parentWidth} height={100} className={'sparkline'} tabIndex={0}>
112
+ <svg
113
+ role='img'
114
+ aria-label={handleChartAriaLabels(config)}
115
+ width={parentWidth}
116
+ height={100}
117
+ className={'sparkline'}
118
+ tabIndex={0}
119
+ >
98
120
  <title>{`Spark line graphic with the title ${config.title ? config.title : 'No Title Found'}`}</title>
99
121
  {config.runtime.lineSeriesKeys?.length > 0
100
122
  ? config.runtime.lineSeriesKeys
@@ -104,30 +126,70 @@ const SparkLine: React.FC<SparkLineProps> = props => {
104
126
  style={{ width }}
105
127
  className='sparkline-group'
106
128
  key={`series-${seriesKey}`}
107
- opacity={config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
108
- display={config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
129
+ opacity={
130
+ config.legend.behavior === 'highlight' &&
131
+ seriesHighlight.length > 0 &&
132
+ seriesHighlight.indexOf(seriesKey) === -1
133
+ ? 0.5
134
+ : 1
135
+ }
136
+ display={
137
+ config.legend.behavior === 'highlight' ||
138
+ seriesHighlight.length === 0 ||
139
+ seriesHighlight.indexOf(seriesKey) !== -1
140
+ ? 'block'
141
+ : 'none'
142
+ }
109
143
  >
110
- {config.labels && data.map((d, dataIndex) => {
111
- return (
112
- <Group key={`series-${seriesKey}-point-${dataIndex}`}>
113
- <Text x={xScale(getXAxisData(d))} y={yScale(getYAxisData(d, seriesKey))} fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'} textAnchor='middle'>
114
- {formatNumber(d[seriesKey])}
115
- </Text>
116
- </Group>
117
- )
118
- })}
144
+ {config.labels &&
145
+ data.map((d, dataIndex) => {
146
+ return (
147
+ <Group key={`series-${seriesKey}-point-${dataIndex}`}>
148
+ <Text
149
+ x={xScale(getXAxisData(d))}
150
+ y={yScale(getYAxisData(d, seriesKey))}
151
+ fill={
152
+ colorScale
153
+ ? colorScale(
154
+ config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey
155
+ )
156
+ : '#000'
157
+ }
158
+ textAnchor='middle'
159
+ >
160
+ {formatNumber(d[seriesKey])}
161
+ </Text>
162
+ </Group>
163
+ )
164
+ })}
119
165
  <LinePath
120
166
  curve={allCurves.curveLinear}
121
167
  data={data}
122
168
  x={d => xScale(getXAxisData(d))}
123
169
  y={d => yScale(getYAxisData(d, seriesKey))}
124
- stroke={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
170
+ stroke={
171
+ colorScale
172
+ ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey)
173
+ : '#000'
174
+ }
125
175
  strokeWidth={2}
126
176
  strokeOpacity={1}
127
177
  shapeRendering='geometricPrecision'
128
178
  markerEnd={`url(#${'arrow'}--${index})`}
129
179
  />
130
- <MarkerArrow id={`arrow--${index}`} refX={2} size={6} markerEnd={`url(#${'arrow'}--${index})`} strokeOpacity={1} fillOpacity={1} fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'} />
180
+ <MarkerArrow
181
+ id={`arrow--${index}`}
182
+ refX={2}
183
+ size={6}
184
+ markerEnd={`url(#${'arrow'}--${index})`}
185
+ strokeOpacity={1}
186
+ fillOpacity={1}
187
+ fill={
188
+ colorScale
189
+ ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey)
190
+ : '#000'
191
+ }
192
+ />
131
193
  </Group>
132
194
  <AxisBottom
133
195
  top={yMax + margin.top}
@@ -7,7 +7,7 @@ import { ScaleLinear, ScaleBand } from 'd3-scale'
7
7
  import { isDateScale } from '@cdc/core/helpers/cove/date'
8
8
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
9
9
  import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
10
- import { appFontSize } from '@cdc/core/helpers/cove/fontSettings'
10
+ import { APP_FONT_SIZE } from '@cdc/core/helpers/constants'
11
11
 
12
12
  interface Props {
13
13
  xScaleBrush: ScaleLinear<number, number>
@@ -210,7 +210,7 @@ const BrushHandle = props => {
210
210
  const transform = isLeft ? 'scale(-1, 1)' : 'translate(0,0)'
211
211
  const textAnchor = isLeft ? 'end' : 'start'
212
212
  const tooltipText = isLeft ? ` Drag edges to focus on a specific segment ` : ''
213
- const textWidth = getTextWidth(tooltipText, `${appFontSize / 1.1}px`)
213
+ const textWidth = getTextWidth(tooltipText, `${APP_FONT_SIZE / 1.1}px`)
214
214
 
215
215
  return (
216
216
  <>
@@ -219,7 +219,7 @@ const BrushHandle = props => {
219
219
  x={(Number(textProps.xMax) - textWidth) / 2}
220
220
  dy={-12}
221
221
  pointerEvents='visiblePainted'
222
- fontSize={appFontSize / 1.1}
222
+ fontSize={APP_FONT_SIZE / 1.1}
223
223
  >
224
224
  {tooltipText}
225
225
  </Text>
@@ -232,7 +232,7 @@ const BrushHandle = props => {
232
232
  y={25}
233
233
  verticalAnchor='start'
234
234
  textAnchor={textAnchor}
235
- fontSize={appFontSize / 1.4}
235
+ fontSize={APP_FONT_SIZE / 1.4}
236
236
  >
237
237
  {isLeft ? textProps.startValue : textProps.endValue}
238
238
  </Text>
@@ -24,7 +24,6 @@ export default {
24
24
  isResponsiveTicks: false,
25
25
  general: {
26
26
  annotationDropdownText: 'Annotations',
27
- showDownloadButton: false,
28
27
  showMissingDataLabel: true,
29
28
  showSuppressedSymbol: true,
30
29
  showZeroValueData: true,
@@ -166,13 +165,16 @@ export default {
166
165
  seriesHighlight: [],
167
166
  style: 'circles',
168
167
  subStyle: 'linear blocks',
168
+ groupBy: '',
169
169
  shape: 'circle',
170
170
  tickRotation: '',
171
+ order: 'dataColumn',
171
172
  hideBorder: {
172
173
  side: false,
173
174
  topBottom: true
174
175
  },
175
- position: 'right'
176
+ position: 'right',
177
+ orderedValues: []
176
178
  },
177
179
  brush: {
178
180
  height: 45,
@@ -25,7 +25,7 @@ export const countNumOfTicks = ({ axis, max, runtime, currentViewport, isHorizon
25
25
  }
26
26
  if (Number(tickCount) > Number(max) && !isHorizontal) {
27
27
  // cap it and round it so its an integer
28
- tickCount = Number(min) < 0 ? Math.round(max) * 2 : Math.round(max)
28
+ tickCount = Math.max(2, Number(min) < 0 ? Math.round(max) * 2 : Math.round(max))
29
29
  }
30
30
  }
31
31
 
@@ -0,0 +1,31 @@
1
+ import { Series } from '@cdc/core/types/Series'
2
+ import { ChartConfig } from '../types/ChartConfig'
3
+
4
+ export const getSeriesWithData = (config: ChartConfig) => {
5
+ const { filters, data, runtime, legend } = config
6
+ const { colorCode } = legend
7
+ const { series } = runtime
8
+
9
+ const filteredData = data.filter(d => filters.every(f => d[f.columnName] === f.active))
10
+ const colorCodeSeries = colorCode && Array.from(new Set(filteredData.map(d => d[colorCode])))
11
+
12
+ const result = series
13
+ .flatMap(s => {
14
+ if (!colorCode || s.type !== 'Bar') return s
15
+ return colorCodeSeries.map(c => ({ ...s, colorCodeSeries: c }))
16
+ })
17
+ .map(s => ({
18
+ ...s,
19
+ data: filteredData
20
+ .filter(d => !s.dynamicCategory || d[s.dynamicCategory] === s.dataKey)
21
+ .filter(d => !s.colorCodeSeries || d[colorCode] === s.colorCodeSeries)
22
+ .filter(d => {
23
+ const key = s.dynamicCategory ? s.originalDataKey : s.dataKey
24
+ return d[key] || d[key] === 0
25
+ })
26
+ }))
27
+ .filter(s => s.data.length)
28
+ .map(s => s.colorCodeSeries || s.name || s.dataKey)
29
+
30
+ return result
31
+ }
@@ -1,5 +1,8 @@
1
+ import { clamp } from 'lodash'
2
+
1
3
  import { isMobileHeightViewport } from '@cdc/core/helpers/viewports'
2
4
  import { ChartConfig, ViewportSize } from '../types/ChartConfig'
5
+ import { EDITOR_WIDTH } from '@cdc/core/helpers/constants'
3
6
 
4
7
  export function getOrientation(
5
8
  { orientation, heights, visualizationType }: Pick<ChartConfig, 'orientation' | 'heights' | 'visualizationType'>,
@@ -23,3 +26,23 @@ export function calcInitialHeight(
23
26
  const height = Number(heights?.[renderedOrientation])
24
27
  return isNaN(height) ? 0 : height
25
28
  }
29
+
30
+ export function handleAutoPaddingRight(parentRef, xAxisLabelRefs, parentWidth): [boolean, number] {
31
+ const parentX = parentRef.current.getBoundingClientRect().x
32
+ const editorIsOpen = !!document.querySelector('.editor-panel:not(.hidden)')
33
+ const lastTickRect = xAxisLabelRefs.current?.[xAxisLabelRefs.current.length - 1]?.getBoundingClientRect()
34
+ const lastBottomTickEnd = lastTickRect ? lastTickRect.x + lastTickRect.width : 0
35
+ const editorWidth = editorIsOpen ? EDITOR_WIDTH : 0
36
+ const calculatedOverhang = lastBottomTickEnd - parentX - editorWidth - parentWidth
37
+
38
+ const paddingToAdd = clamp(calculatedOverhang, 0, 20)
39
+ const currentPadding = Number(parentRef.current.style.paddingRight.replace('px', ''))
40
+ const paddingDiff = Math.abs(currentPadding - paddingToAdd)
41
+ const DIFF_THRESHOLD = 5
42
+
43
+ const noChange = currentPadding === calculatedOverhang
44
+ const insufficientDiff = (paddingDiff < DIFF_THRESHOLD && calculatedOverhang > 0) || Math.abs(calculatedOverhang) < 1
45
+ const updatePadding = !noChange && !insufficientDiff
46
+
47
+ return [updatePadding, paddingToAdd]
48
+ }
@@ -46,7 +46,6 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
46
46
 
47
47
  min = enteredMinValue && isMinValid ? Number(enteredMinValue) : minValue
48
48
  max = enteredMaxValue && isMaxValid ? Number(enteredMaxValue) : Number.MIN_VALUE
49
-
50
49
  const { lower, upper } = config?.confidenceKeys || {}
51
50
 
52
51
  if (lower && upper && config.visualizationType === 'Bar') {
@@ -167,35 +166,28 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
167
166
  }
168
167
 
169
168
  if (config.visualizationType === 'Line' && !convertLineToBarGraph) {
170
- const isMinValid = isLogarithmicAxis
171
- ? Number(enteredMinValue) >= 0 && Number(enteredMinValue) < minValue
172
- : Number(enteredMinValue) < minValue
173
- // update minValue for (0) Suppression points
174
- const suppressedMinValue = tableData?.some((dataItem, index) => {
175
- return config.preliminaryData?.some(pd => {
176
- if (pd.type !== 'suppression' || !pd.style) return false
177
-
178
- // Filter data item based on current series keys and check if pd.value is present
179
- const relevantData = _.pick(dataItem, config.runtime?.seriesKeys)
180
- const isValuePresent = _.values(relevantData).includes(pd.value)
181
-
182
- // Check for value match condition
183
- const valueMatch = pd.column ? dataItem[pd.column] === pd.value : isValuePresent
184
-
185
- // Return true if the value matches and it's either the first or the last item
186
- return valueMatch && (index === 0 || index === tableData.length - 1)
169
+ const numEnteredMin = Number(enteredMinValue)
170
+ const isMinValid = isLogarithmicAxis ? numEnteredMin >= 0 && numEnteredMin < minValue : numEnteredMin < minValue
171
+
172
+ const suppressedMinValue = tableData?.some((item, i, arr) =>
173
+ config.preliminaryData?.some(({ type, style, column, value }) => {
174
+ if (type !== 'suppression' || !style) return false
175
+
176
+ const values = _.values(_.pick(item, config.runtime?.seriesKeys))
177
+ const dynamicCategory = config.series[0].dynamicCategory
178
+
179
+ const match = column ? item[column] === value : values.includes(value)
180
+ const dynamic = dynamicCategory && (item[dynamicCategory] === column || !column)
181
+
182
+ return (match || dynamic) && (i === 0 || i === arr.length - 1)
187
183
  })
188
- })
189
- let isCategoricalAxis = config.yAxis.type === 'categorical'
190
- min =
191
- enteredMinValue !== '' && isMinValid
192
- ? Number(enteredMinValue)
193
- : suppressedMinValue
194
- ? 0
195
- : isCategoricalAxis
196
- ? 0
197
- : minValue
184
+ )
185
+
186
+ const isCategorical = config.yAxis.type === 'categorical'
187
+
188
+ min = enteredMinValue !== '' && isMinValid ? numEnteredMin : suppressedMinValue ? 0 : isCategorical ? 0 : minValue
198
189
  }
190
+
199
191
  //If data value max wasn't provided, calculate it
200
192
  if (max === Number.MIN_VALUE) {
201
193
  // if all values in data are negative set max = 0
@@ -241,6 +233,7 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
241
233
 
242
234
  if (config.visualizationType === 'Scatter Plot') {
243
235
  max = max * 1.1
236
+ min = min / 1.1
244
237
  }
245
238
 
246
239
  return { min, max, leftMax, rightMax }
@@ -1,9 +1,11 @@
1
1
  import { scaleLinear } from '@visx/scale'
2
2
  import useReduceData from './useReduceData'
3
+ import { TOP_PADDING } from './useScales'
3
4
 
4
5
  export default function useRightAxis({ config, yMax = 0, data = [], updateConfig }) {
5
6
  const hasRightAxis = config.visualizationType === 'Combo' && config.orientation === 'vertical'
6
- const rightSeriesKeys = config.series && config.series.filter(series => series.axis === 'Right').map(key => key.dataKey)
7
+ const rightSeriesKeys =
8
+ config.series && config.series.filter(series => series.axis === 'Right').map(key => key.dataKey)
7
9
  let { minValue } = useReduceData(config, data)
8
10
 
9
11
  const allRightAxisData = rightSeriesKeys => {
@@ -35,7 +37,7 @@ export default function useRightAxis({ config, yMax = 0, data = [], updateConfig
35
37
 
36
38
  const yScaleRight = scaleLinear({
37
39
  domain: [minValue, max],
38
- range: [yMax, 0]
40
+ range: [yMax, TOP_PADDING]
39
41
  })
40
42
 
41
43
  return { yScaleRight, hasRightAxis }
@@ -22,6 +22,8 @@ const scaleTypes = {
22
22
  BAND: 'band'
23
23
  }
24
24
 
25
+ export const TOP_PADDING = 10
26
+
25
27
  type useScaleProps = {
26
28
  config: ChartConfig // standard chart config
27
29
  data: Object[] // standard data array
@@ -80,11 +82,13 @@ const useScales = (properties: useScaleProps) => {
80
82
  if (xAxis.type === 'date-time' || xAxis.type === 'continuous') {
81
83
  let xAxisMin = Math.min(...xAxisDataMapped.map(Number))
82
84
  let xAxisMax = Math.max(...xAxisDataMapped.map(Number))
83
- xAxisMin -= (config.xAxis.padding ? config.xAxis.padding * 0.01 : 0) * (xAxisMax - xAxisMin)
84
- xAxisMax +=
85
- visualizationType === 'Line'
86
- ? 0
87
- : (config.xAxis.padding ? config.xAxis.padding * 0.01 : 0) * (xAxisMax - xAxisMin)
85
+ let paddingRatio = config.xAxis.padding ? config.xAxis.padding * 0.01 : 0
86
+ if (config.brush.active) {
87
+ paddingRatio = config.barThickness * 0.2
88
+ }
89
+
90
+ xAxisMin -= paddingRatio * (xAxisMax - xAxisMin)
91
+ xAxisMax += visualizationType === 'Line' ? 0 : paddingRatio * (xAxisMax - xAxisMin)
88
92
  const range = config.xAxis.sortByRecentDate ? [xMax, 0] : [0, xMax]
89
93
  xScale = scaleTime({
90
94
  domain: [xAxisMin, xAxisMax],
@@ -137,14 +141,8 @@ const useScales = (properties: useScaleProps) => {
137
141
  xScale.type = scaleTypes.LINEAR
138
142
  }
139
143
  if (xAxis.type === 'categorical') {
140
- // Map items to rounded numbers if numeric, skip formatting non-numeric strings.
141
- const xAxisDataMappedRoundedItems = xAxisDataMapped.map(item => {
142
- const strItem = String(item)
143
- const parsed = parseFloat(strItem)
144
- return !isNaN(parsed) ? Math.round(parsed).toString() : strItem
145
- })
146
-
147
- xScale = composeScaleBand(xAxisDataMappedRoundedItems, [0, xMax], 1 - config.barThickness)
144
+ xScale = composeScaleBand(xAxisDataMapped, [0, xMax], 1)
145
+ xScale.type = scaleTypes.BAND
148
146
  }
149
147
  }
150
148
 
@@ -398,7 +396,7 @@ const composeYScale = ({ min, max, yMax, config, leftMax }) => {
398
396
 
399
397
  // If the visualization type is a bump chart then the domain and range need different values
400
398
  const domainSet = config.visualizationType === 'Bump Chart' ? [1, max] : [min, max]
401
- const yRange = config.visualizationType === 'Bump Chart' ? [30, yMax] : [yMax, 0]
399
+ const yRange = config.visualizationType === 'Bump Chart' ? [30, yMax] : [yMax, TOP_PADDING]
402
400
  // Return the configured scale function
403
401
  return scaleFunc({
404
402
  domain: domainSet,