@cdc/chart 4.24.12 → 4.25.2-25
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 +79900 -78999
- package/examples/feature/boxplot/boxplot.json +2 -157
- package/examples/feature/boxplot/testing.csv +23 -38
- package/examples/feature/tests-non-numerics/example-combo-bar-nonnumeric.json +579 -49
- package/examples/private/ehdi.json +29939 -0
- package/examples/private/line-issue.json +497 -0
- package/examples/private/not-loading.json +360 -0
- package/index.html +11 -15
- package/package.json +2 -2
- package/src/CdcChart.tsx +92 -1512
- package/src/CdcChartComponent.tsx +1113 -0
- package/src/ConfigContext.tsx +6 -1
- package/src/_stories/Chart.Anchors.stories.tsx +1 -1
- package/src/_stories/Chart.CustomColors.stories.tsx +1 -1
- package/src/_stories/Chart.DynamicSeries.stories.tsx +17 -2
- package/src/_stories/Chart.Filters.stories.tsx +19 -0
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +2 -2
- package/src/_stories/Chart.ScatterPlot.stories.tsx +19 -0
- package/src/_stories/Chart.tooltip.stories.tsx +1 -2
- package/src/_stories/ChartAnnotation.stories.tsx +1 -1
- package/src/_stories/ChartAxisLabels.stories.tsx +1 -1
- package/src/_stories/ChartAxisTitles.stories.tsx +1 -1
- package/src/_stories/ChartEditor.stories.tsx +1 -1
- package/src/_stories/ChartLine.Suppression.stories.tsx +1 -1
- package/src/_stories/ChartLine.Symbols.stories.tsx +18 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +1 -1
- package/src/_stories/_mock/line_chart_symbols.json +437 -0
- package/src/_stories/_mock/scatterplot-image-download.json +1244 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +3 -11
- package/src/components/Annotations/components/AnnotationDropdown.tsx +3 -3
- package/src/components/Axis/Categorical.Axis.tsx +3 -4
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +14 -5
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +10 -4
- package/src/components/BarChart/components/BarChart.Vertical.tsx +5 -7
- package/src/components/BarChart/components/BarChart.jsx +24 -4
- package/src/components/BarChart/components/context.tsx +1 -0
- package/src/components/BoxPlot/BoxPlot.tsx +34 -32
- package/src/components/BoxPlot/helpers/index.ts +108 -18
- package/src/components/BrushChart.tsx +44 -24
- package/src/components/DeviationBar.jsx +2 -6
- package/src/components/EditorPanel/EditorPanel.tsx +64 -8
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +4 -0
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +3 -1
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +44 -7
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +6 -1
- package/src/components/ForestPlot/ForestPlot.tsx +176 -26
- package/src/components/Legend/Legend.Component.tsx +29 -38
- package/src/components/Legend/Legend.Suppression.tsx +3 -5
- package/src/components/Legend/Legend.tsx +2 -2
- package/src/components/Legend/LegendLine.Shape.tsx +51 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +29 -26
- package/src/components/Legend/helpers/getLegendClasses.ts +20 -38
- package/src/components/Legend/helpers/index.ts +22 -9
- package/src/components/Legend/tests/getLegendClasses.test.ts +3 -20
- package/src/components/LineChart/components/LineChart.Circle.tsx +104 -94
- package/src/components/LineChart/index.tsx +6 -2
- package/src/components/LinearChart.tsx +77 -43
- package/src/components/PairedBarChart.jsx +2 -9
- package/src/components/ZoomBrush.tsx +5 -7
- package/src/data/initial-state.js +6 -3
- package/src/helpers/getBoxPlotConfig.ts +68 -0
- package/src/helpers/getColorScale.ts +24 -0
- package/src/helpers/getComboChartConfig.ts +42 -0
- package/src/helpers/getExcludedData.ts +37 -0
- package/src/helpers/getTopAxis.ts +7 -0
- package/src/helpers/isConvertLineToBarGraph.ts +10 -3
- package/src/hooks/useBarChart.ts +40 -13
- package/src/hooks/{useHighlightedBars.js → useHighlightedBars.ts} +2 -1
- package/src/hooks/useIntersectionObserver.ts +37 -0
- package/src/hooks/useMinMax.ts +11 -8
- package/src/hooks/useReduceData.ts +1 -1
- package/src/hooks/useScales.ts +10 -0
- package/src/hooks/useTooltip.tsx +21 -2
- package/src/index.jsx +1 -0
- package/src/scss/DataTable.scss +0 -5
- package/src/scss/main.scss +31 -116
- package/src/store/chart.actions.ts +40 -0
- package/src/store/chart.reducer.ts +83 -0
- package/src/types/ChartConfig.ts +6 -3
- package/src/types/ChartContext.ts +1 -3
- package/src/helpers/getQuartiles.ts +0 -27
- package/src/hooks/useColorScale.ts +0 -50
- package/src/hooks/useIntersectionObserver.jsx +0 -29
- package/src/hooks/useTopAxis.js +0 -6
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import _ from 'lodash'
|
|
2
|
+
import { ChartConfig } from '../types/ChartConfig'
|
|
3
|
+
import * as d3 from 'd3-array'
|
|
4
|
+
|
|
5
|
+
export const getComboChartConfig = (newConfig: ChartConfig) => {
|
|
6
|
+
if (newConfig.visualizationType !== 'Combo' || !newConfig.series) return
|
|
7
|
+
|
|
8
|
+
const runtimeKeys = {
|
|
9
|
+
barSeriesKeys: [],
|
|
10
|
+
lineSeriesKeys: [],
|
|
11
|
+
areaSeriesKeys: [],
|
|
12
|
+
forecastingSeriesKeys: []
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Define a mapping of series types to runtime keys
|
|
16
|
+
const seriesTypeMap = new Map([
|
|
17
|
+
['Area Chart', 'areaSeriesKeys'],
|
|
18
|
+
['Forecasting', 'forecastingSeriesKeys'],
|
|
19
|
+
['Bar', 'barSeriesKeys'],
|
|
20
|
+
['Combo', 'barSeriesKeys'],
|
|
21
|
+
['Line', 'lineSeriesKeys'],
|
|
22
|
+
['dashed-sm', 'lineSeriesKeys'],
|
|
23
|
+
['dashed-md', 'lineSeriesKeys'],
|
|
24
|
+
['dashed-lg', 'lineSeriesKeys']
|
|
25
|
+
])
|
|
26
|
+
|
|
27
|
+
newConfig.series.forEach(series => {
|
|
28
|
+
const runtimeKey = seriesTypeMap.get(series.type)
|
|
29
|
+
if (runtimeKey) {
|
|
30
|
+
const valueToPush = runtimeKey === 'barSeriesKeys' || runtimeKey === 'lineSeriesKeys' ? series.dataKey : series
|
|
31
|
+
runtimeKeys[runtimeKey].push(valueToPush)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Change Combo series type to Bar
|
|
35
|
+
if (series.type === 'Combo') {
|
|
36
|
+
series.type = 'Bar'
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// Assign the processed runtime keys to the configuration
|
|
41
|
+
return { ...newConfig.runtime, ...runtimeKeys }
|
|
42
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
2
|
+
import _ from 'lodash'
|
|
3
|
+
import { ChartConfig } from '../types/ChartConfig'
|
|
4
|
+
export const getExcludedData = (newConfig: ChartConfig, data: object[]) => {
|
|
5
|
+
let newExcludedData = data
|
|
6
|
+
if (newConfig.exclusions && newConfig.exclusions.active) {
|
|
7
|
+
if (newConfig.xAxis.type === 'categorical' && newConfig.exclusions.keys?.length > 0) {
|
|
8
|
+
newExcludedData = data.filter(e => !newConfig.exclusions.keys.includes(e[newConfig.xAxis.dataKey]))
|
|
9
|
+
} else if (
|
|
10
|
+
isDateScale(newConfig.xAxis) &&
|
|
11
|
+
(newConfig.exclusions.dateStart || newConfig.exclusions.dateEnd) &&
|
|
12
|
+
newConfig.xAxis.dateParseFormat
|
|
13
|
+
) {
|
|
14
|
+
// Filter dates
|
|
15
|
+
const timestamp = e => new Date(e).getTime()
|
|
16
|
+
|
|
17
|
+
let startDate = timestamp(newConfig.exclusions.dateStart)
|
|
18
|
+
let endDate = timestamp(newConfig.exclusions.dateEnd) + 86399999 //Increase by 24h in ms (86400000ms - 1ms) to include selected end date for .getTime() comparative
|
|
19
|
+
|
|
20
|
+
let startDateValid = undefined !== typeof startDate && false === isNaN(startDate)
|
|
21
|
+
let endDateValid = undefined !== typeof endDate && false === isNaN(endDate)
|
|
22
|
+
|
|
23
|
+
if (startDateValid && endDateValid) {
|
|
24
|
+
newExcludedData = data.filter(
|
|
25
|
+
e => timestamp(e[newConfig.xAxis.dataKey]) >= startDate && timestamp(e[newConfig.xAxis.dataKey]) <= endDate
|
|
26
|
+
)
|
|
27
|
+
} else if (startDateValid) {
|
|
28
|
+
newExcludedData = data.filter(e => timestamp(e[newConfig.xAxis.dataKey]) >= startDate)
|
|
29
|
+
} else if (endDateValid) {
|
|
30
|
+
newExcludedData = data.filter(e => timestamp(e[newConfig.xAxis.dataKey]) <= endDate)
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
newExcludedData = data
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return newExcludedData
|
|
37
|
+
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import _ from 'lodash'
|
|
2
|
+
|
|
3
|
+
export const isConvertLineToBarGraph = (configObj, filteredData) => {
|
|
4
|
+
const { allowLineToBarGraph, formattedData, series, visualizationType, xAxis } = configObj
|
|
5
|
+
if (!allowLineToBarGraph) return false
|
|
6
|
+
const lineWithLessThanThreePoints = visualizationType === 'Line' && filteredData?.length < 3
|
|
7
|
+
const isDynamicSeries = series?.some(series => series.dynamicCategory)
|
|
8
|
+
const isDynamicWithLessThanThreePoints =
|
|
9
|
+
isDynamicSeries && _.uniq(formattedData?.map(data => data[xAxis.dataKey])).length <= 2
|
|
10
|
+
return lineWithLessThanThreePoints || isDynamicWithLessThanThreePoints
|
|
4
11
|
}
|
package/src/hooks/useBarChart.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import React, { useContext, useEffect, useState } from 'react'
|
|
2
|
-
import ConfigContext from '../ConfigContext'
|
|
2
|
+
import ConfigContext, { ChartDispatchContext } from '../ConfigContext'
|
|
3
3
|
import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
|
|
4
|
+
import { appFontSize } from '@cdc/core/helpers/cove/fontSettings'
|
|
4
5
|
export const useBarChart = () => {
|
|
5
|
-
const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate, setSeriesHighlight, seriesHighlight } =
|
|
6
|
+
const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate, setSeriesHighlight, seriesHighlight } =
|
|
7
|
+
useContext(ConfigContext)
|
|
8
|
+
const dispatch = useContext(ChartDispatchContext)
|
|
6
9
|
const { orientation } = config
|
|
7
10
|
const [hoveredBar, setHoveredBar] = useState(null)
|
|
8
11
|
|
|
@@ -17,12 +20,22 @@ export const useBarChart = () => {
|
|
|
17
20
|
const isRounded = config.barStyle === 'rounded'
|
|
18
21
|
const isStacked = config.visualizationSubType === 'stacked'
|
|
19
22
|
const tipRounding = config.tipRounding
|
|
20
|
-
const radius =
|
|
23
|
+
const radius =
|
|
24
|
+
config.roundingStyle === 'standard'
|
|
25
|
+
? '8px'
|
|
26
|
+
: config.roundingStyle === 'shallow'
|
|
27
|
+
? '5px'
|
|
28
|
+
: config.roundingStyle === 'finger'
|
|
29
|
+
? '15px'
|
|
30
|
+
: '0px'
|
|
21
31
|
const stackCount = config.runtime.seriesKeys.length
|
|
22
|
-
const fontSize = { small: 16, medium: 18, large: 20 }
|
|
23
32
|
const hasMultipleSeries = Object.keys(config.runtime.seriesLabels).length > 1
|
|
24
|
-
const isBarAndLegendIsolate =
|
|
25
|
-
|
|
33
|
+
const isBarAndLegendIsolate =
|
|
34
|
+
config.visualizationType === 'Bar' && config.legend.behavior === 'isolate' && config.legend.axisAlign
|
|
35
|
+
const barStackedSeriesKeys =
|
|
36
|
+
isBarAndLegendIsolate && seriesHighlight?.length
|
|
37
|
+
? seriesHighlight
|
|
38
|
+
: config.runtime.barSeriesKeys || config.runtime.seriesKeys
|
|
26
39
|
|
|
27
40
|
useEffect(() => {
|
|
28
41
|
if (orientation === 'horizontal' && !config.yAxis.labelPlacement) {
|
|
@@ -68,7 +81,9 @@ export const useBarChart = () => {
|
|
|
68
81
|
style = isHorizontal ? { borderRadius: `0 ${radius} ${radius} 0` } : { borderRadius: `${radius} ${radius} 0 0` }
|
|
69
82
|
}
|
|
70
83
|
if (!isStacked && index === -1) {
|
|
71
|
-
style = isHorizontal
|
|
84
|
+
style = isHorizontal
|
|
85
|
+
? { borderRadius: `${radius} 0 0 ${radius} ` }
|
|
86
|
+
: { borderRadius: ` 0 0 ${radius} ${radius}` }
|
|
72
87
|
}
|
|
73
88
|
if (tipRounding === 'full' && isStacked && index === 0 && stackCount > 1) {
|
|
74
89
|
style = isHorizontal ? { borderRadius: `${radius} 0 0 ${radius}` } : { borderRadius: `0 0 ${radius} ${radius}` }
|
|
@@ -126,7 +141,7 @@ export const useBarChart = () => {
|
|
|
126
141
|
barHeight = heights.stacked
|
|
127
142
|
}
|
|
128
143
|
|
|
129
|
-
const labelHeight = isLabelBelowBar ?
|
|
144
|
+
const labelHeight = isLabelBelowBar ? appFontSize * 1.2 : 0
|
|
130
145
|
let barSpace = Number(config.barSpace)
|
|
131
146
|
|
|
132
147
|
// calculate height of container based height, space and fontSize of labels
|
|
@@ -178,9 +193,11 @@ export const useBarChart = () => {
|
|
|
178
193
|
const columns = config.columns
|
|
179
194
|
const columnsWithTooltips = []
|
|
180
195
|
let additionalTooltipItems = ''
|
|
196
|
+
const dynamicCategorySeries = config.runtime?.series?.find(series => series?.dynamicCategory)
|
|
181
197
|
const closestVal =
|
|
182
198
|
tableData.find(d => {
|
|
183
|
-
|
|
199
|
+
const dynamicCategoryMatch = dynamicCategorySeries ? d[dynamicCategorySeries.dynamicCategory] === series : true
|
|
200
|
+
return d[config.xAxis.dataKey] === xAxisDataValue && dynamicCategoryMatch
|
|
184
201
|
}) || {}
|
|
185
202
|
Object.keys(columns).forEach(colKeys => {
|
|
186
203
|
if (series && config.columns[colKeys].series && config.columns[colKeys].series !== series) return
|
|
@@ -191,7 +208,13 @@ export const useBarChart = () => {
|
|
|
191
208
|
addColCommas: config.columns[colKeys].commas
|
|
192
209
|
}
|
|
193
210
|
|
|
194
|
-
const formattedValue = formatColNumber(
|
|
211
|
+
const formattedValue = formatColNumber(
|
|
212
|
+
closestVal[config.columns[colKeys].name],
|
|
213
|
+
'left',
|
|
214
|
+
true,
|
|
215
|
+
config,
|
|
216
|
+
formattingParams
|
|
217
|
+
)
|
|
195
218
|
if (config.columns[colKeys].tooltips) {
|
|
196
219
|
columnsWithTooltips.push([config.columns[colKeys].label, formattedValue])
|
|
197
220
|
}
|
|
@@ -203,11 +226,16 @@ export const useBarChart = () => {
|
|
|
203
226
|
}
|
|
204
227
|
|
|
205
228
|
const onMouseOverBar = (categoryValue, barKey) => {
|
|
206
|
-
if (config.legend.highlightOnHover && config.legend.behavior === 'highlight' && barKey)
|
|
229
|
+
if (config.legend.highlightOnHover && config.legend.behavior === 'highlight' && barKey) {
|
|
230
|
+
dispatch({ type: 'SET_SERIES_HIGHLIGHT', payload: [barKey] })
|
|
231
|
+
}
|
|
232
|
+
|
|
207
233
|
setHoveredBar(categoryValue)
|
|
208
234
|
}
|
|
209
235
|
const onMouseLeaveBar = () => {
|
|
210
|
-
if (config.legend.highlightOnHover && config.legend.behavior === 'highlight')
|
|
236
|
+
if (config.legend.highlightOnHover && config.legend.behavior === 'highlight') {
|
|
237
|
+
dispatch({ type: 'SET_SERIES_HIGHLIGHT', payload: [] })
|
|
238
|
+
}
|
|
211
239
|
}
|
|
212
240
|
|
|
213
241
|
return {
|
|
@@ -225,7 +253,6 @@ export const useBarChart = () => {
|
|
|
225
253
|
radius,
|
|
226
254
|
stackCount,
|
|
227
255
|
barStackedSeriesKeys,
|
|
228
|
-
fontSize,
|
|
229
256
|
hasMultipleSeries,
|
|
230
257
|
applyRadius,
|
|
231
258
|
updateBars,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React, { useContext } from 'react'
|
|
2
2
|
import ConfigContext from '../ConfigContext'
|
|
3
|
+
import { ChartConfig } from '../types/ChartConfig'
|
|
3
4
|
|
|
4
|
-
export const useHighlightedBars = (config, updateConfig) => {
|
|
5
|
+
export const useHighlightedBars = (config: ChartConfig, updateConfig: (config) => void) => {
|
|
5
6
|
const { formatDate, parseDate } = useContext(ConfigContext)
|
|
6
7
|
|
|
7
8
|
let highlightedSeries = [] // only allow single series for highlights
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useEffect, useState, MutableRefObject } from 'react'
|
|
2
|
+
|
|
3
|
+
interface IntersectionObserverOptions {
|
|
4
|
+
threshold?: number | number[]
|
|
5
|
+
root?: Element | null
|
|
6
|
+
rootMargin?: string
|
|
7
|
+
freezeOnceVisible?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function useIntersectionObserver(
|
|
11
|
+
elementRef: MutableRefObject<Element | null>,
|
|
12
|
+
{ threshold = 0, root = null, rootMargin = '0%', freezeOnceVisible = false }: IntersectionObserverOptions
|
|
13
|
+
) {
|
|
14
|
+
const [entry, setEntry] = useState<IntersectionObserverEntry | undefined>()
|
|
15
|
+
|
|
16
|
+
const frozen = entry?.isIntersecting && freezeOnceVisible
|
|
17
|
+
|
|
18
|
+
const updateEntry = ([entry]: IntersectionObserverEntry[]) => {
|
|
19
|
+
setEntry(entry)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const node = elementRef?.current
|
|
24
|
+
const hasIOSupport = !!window.IntersectionObserver
|
|
25
|
+
|
|
26
|
+
if (!hasIOSupport || frozen || !node) return
|
|
27
|
+
|
|
28
|
+
const observerParams = { threshold, root, rootMargin }
|
|
29
|
+
const observer = new IntersectionObserver(updateEntry, observerParams)
|
|
30
|
+
|
|
31
|
+
observer.observe(node)
|
|
32
|
+
|
|
33
|
+
return () => observer.disconnect()
|
|
34
|
+
}, [elementRef, threshold, root, rootMargin, frozen])
|
|
35
|
+
|
|
36
|
+
return entry
|
|
37
|
+
}
|
package/src/hooks/useMinMax.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ChartConfig } from '../types/ChartConfig'
|
|
2
2
|
import _ from 'lodash'
|
|
3
|
-
import
|
|
3
|
+
import ConfigContext from '../ConfigContext'
|
|
4
|
+
import { useContext } from 'react'
|
|
4
5
|
|
|
5
6
|
type UseMinMaxProps = {
|
|
6
7
|
/** config - standard chart config */
|
|
@@ -27,14 +28,12 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
27
28
|
let leftMax = 0
|
|
28
29
|
let rightMax = 0
|
|
29
30
|
|
|
31
|
+
const { convertLineToBarGraph } = useContext(ConfigContext)
|
|
32
|
+
|
|
30
33
|
if (!data) {
|
|
31
34
|
return { min, max }
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
const checkLineToBarGraph = () => {
|
|
35
|
-
return isConvertLineToBarGraph(config.visualizationType, data, config.allowLineToBarGraph)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
37
|
const { visualizationType, series } = config
|
|
39
38
|
const { max: enteredMaxValue, min: enteredMinValue } = config.runtime.yAxis
|
|
40
39
|
const paddingAddedToAxis = config.yAxis.enablePadding ? 1 + config.yAxis.scalePadding / 100 : 1
|
|
@@ -136,14 +135,14 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
136
135
|
|
|
137
136
|
// this should not apply to bar charts if there is negative CI data
|
|
138
137
|
if (
|
|
139
|
-
(visualizationType === 'Bar' ||
|
|
138
|
+
(visualizationType === 'Bar' || convertLineToBarGraph || (visualizationType === 'Combo' && !isAllLine)) &&
|
|
140
139
|
min > 0
|
|
141
140
|
) {
|
|
142
141
|
min = 0
|
|
143
142
|
}
|
|
144
143
|
if (
|
|
145
144
|
(config.visualizationType === 'Bar' ||
|
|
146
|
-
|
|
145
|
+
convertLineToBarGraph ||
|
|
147
146
|
(config.visualizationType === 'Combo' && !isAllLine)) &&
|
|
148
147
|
min < 0
|
|
149
148
|
) {
|
|
@@ -167,7 +166,7 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
167
166
|
min = Number(enteredMinValue) && isMinValid ? Number(enteredMinValue) : 0
|
|
168
167
|
}
|
|
169
168
|
|
|
170
|
-
if (config.visualizationType === 'Line' && !
|
|
169
|
+
if (config.visualizationType === 'Line' && !convertLineToBarGraph) {
|
|
171
170
|
const isMinValid = isLogarithmicAxis
|
|
172
171
|
? Number(enteredMinValue) >= 0 && Number(enteredMinValue) < minValue
|
|
173
172
|
: Number(enteredMinValue) < minValue
|
|
@@ -240,6 +239,10 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
240
239
|
min = 0
|
|
241
240
|
}
|
|
242
241
|
|
|
242
|
+
if (config.visualizationType === 'Scatter Plot') {
|
|
243
|
+
max = max * 1.1
|
|
244
|
+
}
|
|
245
|
+
|
|
243
246
|
return { min, max, leftMax, rightMax }
|
|
244
247
|
}
|
|
245
248
|
export default useMinMax
|
package/src/hooks/useScales.ts
CHANGED
|
@@ -136,6 +136,16 @@ const useScales = (properties: useScaleProps) => {
|
|
|
136
136
|
})
|
|
137
137
|
xScale.type = scaleTypes.LINEAR
|
|
138
138
|
}
|
|
139
|
+
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)
|
|
148
|
+
}
|
|
139
149
|
}
|
|
140
150
|
|
|
141
151
|
// handle Box plot
|
package/src/hooks/useTooltip.tsx
CHANGED
|
@@ -143,11 +143,19 @@ export const useTooltip = props => {
|
|
|
143
143
|
})
|
|
144
144
|
|
|
145
145
|
if (visualizationType === 'Pie') {
|
|
146
|
+
const roundTo = Number(config.dataFormat.roundTo) || 0
|
|
147
|
+
|
|
148
|
+
const degrees = ((arc.endAngle - arc.startAngle) * 180) / Math.PI
|
|
149
|
+
|
|
150
|
+
// Calculate the percentage of the full circle (360 degrees)
|
|
151
|
+
const percentageOfCircle = (degrees / 360) * 100
|
|
152
|
+
const roundedPercentage = percentageOfCircle.toFixed(roundTo)
|
|
153
|
+
|
|
146
154
|
tooltipItems.push(
|
|
147
155
|
// ignore
|
|
148
156
|
[config.xAxis.dataKey, pieChartData],
|
|
149
157
|
[config.runtime.yAxis.dataKey, formatNumber(arc?.data[config.runtime.yAxis.dataKey])],
|
|
150
|
-
['Percent', `${
|
|
158
|
+
['Percent', `${roundedPercentage + '%'}`]
|
|
151
159
|
)
|
|
152
160
|
}
|
|
153
161
|
|
|
@@ -167,11 +175,16 @@ export const useTooltip = props => {
|
|
|
167
175
|
?.flatMap(seriesKey => {
|
|
168
176
|
const value = resolvedScaleValues[0]?.[seriesKey]
|
|
169
177
|
const formattedValue = getFormattedValue(seriesKey, value, config, getAxisPosition)
|
|
178
|
+
const seriesObjWithName = config.runtime.series.find(
|
|
179
|
+
series => series.dataKey === seriesKey && series.name !== undefined
|
|
180
|
+
)
|
|
170
181
|
if (
|
|
171
182
|
(value === null || value === undefined || value === '' || formattedValue === 'N/A') &&
|
|
172
183
|
config.general.hideNullValue
|
|
173
184
|
) {
|
|
174
185
|
return []
|
|
186
|
+
} else if (seriesObjWithName && seriesObjWithName.name === '') {
|
|
187
|
+
return [['', formattedValue, getAxisPosition(seriesKey)]]
|
|
175
188
|
} else {
|
|
176
189
|
return [[seriesKey, formattedValue, getAxisPosition(seriesKey)]]
|
|
177
190
|
}
|
|
@@ -559,8 +572,14 @@ export const useTooltip = props => {
|
|
|
559
572
|
if (index == 1 && config.dataFormat.onlyShowTopPrefixSuffix) {
|
|
560
573
|
newValue = `${config.dataFormat.prefix}${newValue}${config.dataFormat.suffix}`
|
|
561
574
|
}
|
|
575
|
+
const activeLabel = getSeriesNameFromLabel(key)
|
|
576
|
+
const displayText = activeLabel ? `${activeLabel}: ${newValue}` : newValue
|
|
562
577
|
|
|
563
|
-
return
|
|
578
|
+
return (
|
|
579
|
+
<li style={style} className='tooltip-body'>
|
|
580
|
+
{displayText}
|
|
581
|
+
</li>
|
|
582
|
+
)
|
|
564
583
|
}
|
|
565
584
|
|
|
566
585
|
return {
|
package/src/index.jsx
CHANGED
package/src/scss/DataTable.scss
CHANGED
package/src/scss/main.scss
CHANGED
|
@@ -50,13 +50,6 @@
|
|
|
50
50
|
overflow-y: auto;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
.d-flex {
|
|
54
|
-
display: flex;
|
|
55
|
-
}
|
|
56
|
-
.flex-column-reverse {
|
|
57
|
-
flex-direction: column-reverse;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
53
|
.cdc-open-viz-module.type-dashboard {
|
|
61
54
|
.cdc-open-viz-module.type-chart.isEditor {
|
|
62
55
|
.cdc-chart-inner-container {
|
|
@@ -140,7 +133,7 @@
|
|
|
140
133
|
.subtext--responsive-ticks,
|
|
141
134
|
.section-subtext {
|
|
142
135
|
&--brush-active {
|
|
143
|
-
margin-top:
|
|
136
|
+
margin-top: 3rem !important;
|
|
144
137
|
}
|
|
145
138
|
}
|
|
146
139
|
|
|
@@ -153,34 +146,39 @@
|
|
|
153
146
|
.legend-container {
|
|
154
147
|
background: #fff;
|
|
155
148
|
width: 100%;
|
|
156
|
-
padding: 15px;
|
|
157
149
|
vertical-align: top;
|
|
158
150
|
text-align: left;
|
|
159
|
-
border: 1px solid var(--
|
|
151
|
+
border: 1px solid var(--cool-gray-10);
|
|
152
|
+
border-radius: 6px;
|
|
160
153
|
position: relative;
|
|
161
154
|
|
|
155
|
+
h3 {
|
|
156
|
+
font-size: var(--legend-title-font-size);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
p {
|
|
160
|
+
font-size: var(--legend-description-font-size);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
tspan,
|
|
164
|
+
div {
|
|
165
|
+
font-size: var(--legend-item-font-size);
|
|
166
|
+
}
|
|
167
|
+
|
|
162
168
|
&.border-0 {
|
|
163
169
|
border: 1px solid transparent;
|
|
164
170
|
padding: 0;
|
|
165
171
|
}
|
|
166
172
|
|
|
167
173
|
&__inner {
|
|
174
|
+
display: flex;
|
|
175
|
+
flex-direction: column;
|
|
176
|
+
row-gap: var(--space-between-legend-item-rows);
|
|
177
|
+
column-gap: var(--space-between-legend-item-columns);
|
|
168
178
|
&.double-column,
|
|
169
179
|
&.single-row {
|
|
170
180
|
display: grid;
|
|
171
181
|
grid-template-columns: 1fr 1fr;
|
|
172
|
-
grid-column-gap: 1.5em;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
&.vertical-sorted {
|
|
176
|
-
display: block;
|
|
177
|
-
|
|
178
|
-
@include breakpoint(sm) {
|
|
179
|
-
column-count: 2;
|
|
180
|
-
column-width: 100%;
|
|
181
|
-
}
|
|
182
|
-
column-gap: 1.5em;
|
|
183
|
-
column-fill: balance;
|
|
184
182
|
}
|
|
185
183
|
|
|
186
184
|
&.single-row {
|
|
@@ -192,13 +190,16 @@
|
|
|
192
190
|
flex-basis: auto;
|
|
193
191
|
}
|
|
194
192
|
}
|
|
193
|
+
|
|
194
|
+
&.double-column.reverse-items div.legend-item:last-child {
|
|
195
|
+
margin-bottom: 0.2rem !important;
|
|
196
|
+
}
|
|
195
197
|
}
|
|
196
198
|
|
|
197
199
|
.legend-item {
|
|
198
200
|
text-align: left;
|
|
199
201
|
user-select: none;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
+
line-height: var(--legend-item-font-size);
|
|
202
203
|
.visx-legend-label {
|
|
203
204
|
word-wrap: break-word;
|
|
204
205
|
white-space: pre-wrap;
|
|
@@ -212,27 +213,9 @@
|
|
|
212
213
|
|
|
213
214
|
.legend-item > .legend-item {
|
|
214
215
|
display: inline-block;
|
|
215
|
-
margin-right: 0.5rem;
|
|
216
216
|
flex: 0 0 auto;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
h3 {
|
|
220
|
-
font-size: 1.3rem;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
h3,
|
|
224
|
-
p {
|
|
225
|
-
margin-bottom: 0.4em;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
& div.legend-item {
|
|
229
|
-
margin-bottom: 0.2em !important;
|
|
230
|
-
|
|
231
|
-
&:last-child {
|
|
232
|
-
margin: 0 !important;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
219
|
.legend-item {
|
|
237
220
|
cursor: pointer;
|
|
238
221
|
transition: 0.2s all;
|
|
@@ -241,6 +224,11 @@
|
|
|
241
224
|
opacity: 0.5;
|
|
242
225
|
transition: 0.2s all;
|
|
243
226
|
}
|
|
227
|
+
&.highlighted {
|
|
228
|
+
outline: 1px solid #005ea2;
|
|
229
|
+
outline-offset: 5px;
|
|
230
|
+
border-radius: 1px;
|
|
231
|
+
}
|
|
244
232
|
}
|
|
245
233
|
|
|
246
234
|
&__outer {
|
|
@@ -261,10 +249,6 @@
|
|
|
261
249
|
}
|
|
262
250
|
}
|
|
263
251
|
|
|
264
|
-
.legend-container__inner.flex-column-reverse div.legend-item:last-child {
|
|
265
|
-
margin-bottom: 0.2rem !important;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
252
|
.dynamic-legend-list {
|
|
269
253
|
// overide traditional legend item that uses !important
|
|
270
254
|
.legend-item {
|
|
@@ -297,7 +281,6 @@
|
|
|
297
281
|
align-items: center;
|
|
298
282
|
font-size: 1em;
|
|
299
283
|
vertical-align: middle;
|
|
300
|
-
margin-bottom: 0.5em;
|
|
301
284
|
|
|
302
285
|
& > span {
|
|
303
286
|
display: flex;
|
|
@@ -305,7 +288,7 @@
|
|
|
305
288
|
align-items: center;
|
|
306
289
|
white-space: nowrap;
|
|
307
290
|
font-size: 1em;
|
|
308
|
-
margin-right:
|
|
291
|
+
margin-right: 9px;
|
|
309
292
|
max-height: 1px;
|
|
310
293
|
}
|
|
311
294
|
|
|
@@ -483,32 +466,6 @@
|
|
|
483
466
|
}
|
|
484
467
|
}
|
|
485
468
|
|
|
486
|
-
@include breakpointClass(xs) {
|
|
487
|
-
&.font-small {
|
|
488
|
-
font-size: 0.8em;
|
|
489
|
-
|
|
490
|
-
.chart-container > svg {
|
|
491
|
-
font-size: 12px;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
&.font-medium {
|
|
496
|
-
font-size: 0.9em;
|
|
497
|
-
|
|
498
|
-
.chart-container > svg {
|
|
499
|
-
font-size: 14px;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
&.font-large {
|
|
504
|
-
font-size: 1em;
|
|
505
|
-
|
|
506
|
-
.chart-container > svg {
|
|
507
|
-
font-size: 16px;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
469
|
@include breakpointClass(sm) {
|
|
513
470
|
.chart-container {
|
|
514
471
|
.no-wrap {
|
|
@@ -547,48 +504,6 @@
|
|
|
547
504
|
}
|
|
548
505
|
}
|
|
549
506
|
}
|
|
550
|
-
|
|
551
|
-
&.font-small {
|
|
552
|
-
font-size: 0.9em;
|
|
553
|
-
|
|
554
|
-
.chart-container > svg {
|
|
555
|
-
font-size: 14px;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
&.font-large {
|
|
560
|
-
font-size: 1.1em;
|
|
561
|
-
|
|
562
|
-
.chart-container > svg {
|
|
563
|
-
font-size: 18px;
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
@include breakpointClass(lg) {
|
|
569
|
-
&.font-small {
|
|
570
|
-
font-size: 1em;
|
|
571
|
-
|
|
572
|
-
.chart-container > svg {
|
|
573
|
-
font-size: 16px;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
&.font-medium {
|
|
578
|
-
font-size: 1.1em;
|
|
579
|
-
|
|
580
|
-
.chart-container > svg {
|
|
581
|
-
font-size: 18px;
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
&.font-large {
|
|
586
|
-
font-size: 1.2em;
|
|
587
|
-
|
|
588
|
-
.chart-container > svg {
|
|
589
|
-
font-size: 20px;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
507
|
}
|
|
593
508
|
|
|
594
509
|
[tabindex]:focus-visible {
|