@cdc/chart 4.24.5 → 4.24.9
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 +44197 -38258
- package/examples/cases-year.json +13379 -0
- package/examples/feature/annotations/index.json +542 -0
- package/examples/gallery/bar-chart-vertical/combo-line-chart.json +76 -15
- package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +5 -5
- package/examples/xaxis.json +493 -0
- package/index.html +20 -10
- package/package.json +5 -4
- package/src/CdcChart.tsx +462 -172
- package/src/_stories/Chart.Legend.Gradient.tsx +19 -0
- package/src/_stories/Chart.stories.tsx +18 -171
- package/src/_stories/ChartAnnotation.stories.tsx +32 -0
- package/src/_stories/_mock/annotation_category_mock.json +473 -0
- package/src/_stories/_mock/annotation_date-linear_mock.json +530 -0
- package/{examples/feature/line/line-chart.json → src/_stories/_mock/annotation_date-time_mock.json} +150 -69
- package/src/_stories/_mock/legend.gradient_mock.json +236 -0
- package/src/_stories/_mock/line_chart_two_points_new_chart.json +128 -0
- package/src/_stories/_mock/line_chart_two_points_regression_test.json +127 -0
- package/src/_stories/_mock/lollipop.json +171 -0
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +31 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +207 -0
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +14 -0
- package/src/components/Annotations/components/AnnotationDropdown.tsx +72 -0
- package/src/components/Annotations/components/AnnotationList.styles.css +45 -0
- package/src/components/Annotations/components/AnnotationList.tsx +42 -0
- package/src/components/Annotations/components/findNearestDatum.ts +138 -0
- package/src/components/Annotations/components/helpers/index.tsx +46 -0
- package/src/components/Annotations/index.tsx +13 -0
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
- package/src/components/AreaChart/components/AreaChart.jsx +1 -1
- package/src/components/Axis/Categorical.Axis.tsx +145 -0
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +47 -44
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +0 -1
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +11 -14
- package/src/components/BarChart/components/BarChart.Vertical.tsx +67 -30
- package/src/components/BarChart/helpers/index.ts +91 -0
- package/src/components/BrushChart.tsx +205 -0
- package/src/components/EditorPanel/EditorPanel.tsx +1794 -403
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +320 -0
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +282 -18
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +43 -8
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -4
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +4 -13
- package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
- package/src/components/EditorPanel/components/panels.scss +4 -0
- package/src/components/EditorPanel/editor-panel.scss +35 -3
- package/src/components/EditorPanel/{useEditorPermissions.js → useEditorPermissions.ts} +105 -17
- package/src/components/Legend/Legend.Component.tsx +185 -194
- package/src/components/Legend/Legend.Suppression.tsx +146 -0
- package/src/components/Legend/Legend.tsx +21 -5
- package/src/components/Legend/helpers/createFormatLabels.tsx +1 -1
- package/src/components/Legend/helpers/index.ts +35 -0
- package/src/components/LegendWrapper.tsx +26 -0
- package/src/components/LineChart/LineChartProps.ts +1 -15
- package/src/components/LineChart/components/LineChart.BumpCircle.tsx +103 -0
- package/src/components/LineChart/components/LineChart.Circle.tsx +47 -8
- package/src/components/LineChart/helpers.ts +72 -14
- package/src/components/LineChart/index.tsx +117 -42
- package/src/components/LinearChart.jsx +179 -136
- package/src/components/LinearChart.tsx +1366 -0
- package/src/components/PairedBarChart.jsx +9 -9
- package/src/components/PieChart/PieChart.tsx +75 -18
- package/src/components/Sankey/index.tsx +89 -30
- package/src/components/ScatterPlot/ScatterPlot.jsx +22 -8
- package/src/components/Sparkline/components/SparkLine.tsx +2 -2
- package/src/components/ZoomBrush.tsx +90 -44
- package/src/data/initial-state.js +25 -7
- package/src/helpers/handleChartTabbing.ts +8 -0
- package/src/helpers/isConvertLineToBarGraph.ts +4 -0
- package/src/hooks/{useBarChart.js → useBarChart.ts} +2 -40
- package/src/hooks/useColorScale.ts +1 -1
- package/src/hooks/useLegendClasses.ts +68 -0
- package/src/hooks/useMinMax.ts +12 -7
- package/src/hooks/useScales.ts +58 -26
- package/src/hooks/useTooltip.tsx +135 -25
- package/src/scss/DataTable.scss +2 -1
- package/src/scss/main.scss +128 -28
- package/src/types/ChartConfig.ts +83 -10
- package/src/types/ChartContext.ts +14 -4
- package/tests-examples/helpers/testZeroValue.test.ts +30 -0
- package/LICENSE +0 -201
- package/src/components/BrushHandle.jsx +0 -17
- package/src/components/LineChart/index.scss +0 -1
- package/src/helpers/filterData.ts +0 -18
- package/src/helpers/tests/computeMarginBottom.test.ts +0 -21
- package/src/hooks/useLegendClasses.js +0 -31
- /package/src/hooks/{useReduceData.js → useReduceData.ts} +0 -0
|
@@ -6,6 +6,7 @@ import { FC, useContext, useEffect, useRef, useState } from 'react'
|
|
|
6
6
|
import ConfigContext from '../ConfigContext'
|
|
7
7
|
import { ScaleLinear, ScaleBand } from 'd3-scale'
|
|
8
8
|
import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
9
|
+
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
9
10
|
|
|
10
11
|
interface Props {
|
|
11
12
|
xScaleBrush: ScaleLinear<number, number>
|
|
@@ -14,8 +15,11 @@ interface Props {
|
|
|
14
15
|
yMax: number
|
|
15
16
|
}
|
|
16
17
|
const ZoomBrush: FC<Props> = props => {
|
|
17
|
-
const { tableData, config, parseDate, formatDate, setBrushConfig } = useContext(ConfigContext)
|
|
18
|
+
const { tableData, config, parseDate, formatDate, setBrushConfig, getTextWidth, dashboardConfig } = useContext(ConfigContext)
|
|
19
|
+
const sharedFilters = dashboardConfig?.dashboard?.sharedFilters ?? []
|
|
20
|
+
const isDashboardFilters = sharedFilters?.length > 0
|
|
18
21
|
const { fontSize } = useBarChart()
|
|
22
|
+
const [showTooltip, setShowTooltip] = useState(false)
|
|
19
23
|
const [brushKey, setBrushKey] = useState(0)
|
|
20
24
|
const brushRef = useRef(null)
|
|
21
25
|
const radius = 15
|
|
@@ -24,16 +28,16 @@ const ZoomBrush: FC<Props> = props => {
|
|
|
24
28
|
startPosition: 0,
|
|
25
29
|
endPosition: 0,
|
|
26
30
|
startValue: '',
|
|
27
|
-
endValue: ''
|
|
31
|
+
endValue: '',
|
|
32
|
+
xMax: props.xMax
|
|
28
33
|
})
|
|
29
34
|
|
|
30
35
|
const initialPosition = {
|
|
31
36
|
start: { x: 0 },
|
|
32
37
|
end: { x: props.xMax }
|
|
33
38
|
}
|
|
34
|
-
|
|
35
39
|
const style = {
|
|
36
|
-
fill: '#
|
|
40
|
+
fill: '#474747',
|
|
37
41
|
stroke: 'blue',
|
|
38
42
|
fillOpacity: 0.8,
|
|
39
43
|
strokeOpacity: 0,
|
|
@@ -41,18 +45,19 @@ const ZoomBrush: FC<Props> = props => {
|
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
const onBrushChange = event => {
|
|
44
|
-
|
|
45
|
-
const
|
|
48
|
+
setShowTooltip(false)
|
|
49
|
+
const filteredValues = event?.xValues?.filter(val => val !== undefined)
|
|
50
|
+
if (filteredValues?.length === 0) return
|
|
46
51
|
|
|
47
52
|
const dataKey = config.xAxis?.dataKey
|
|
48
53
|
|
|
49
|
-
const brushedData = tableData.filter(item =>
|
|
54
|
+
const brushedData = tableData.filter(item => filteredValues.includes(item[dataKey]))
|
|
50
55
|
|
|
51
|
-
const endValue =
|
|
56
|
+
const endValue = filteredValues
|
|
52
57
|
.slice()
|
|
53
58
|
.reverse()
|
|
54
59
|
.find(item => item !== undefined)
|
|
55
|
-
const startValue =
|
|
60
|
+
const startValue = filteredValues.find(item => item !== undefined)
|
|
56
61
|
|
|
57
62
|
const formatIfDate = value => (isDateScale(config.runtime.xAxis) ? formatDate(parseDate(value)) : value)
|
|
58
63
|
|
|
@@ -61,7 +66,8 @@ const ZoomBrush: FC<Props> = props => {
|
|
|
61
66
|
startPosition: brushRef.current?.state.start.x,
|
|
62
67
|
endPosition: brushRef.current?.state.end.x,
|
|
63
68
|
endValue: formatIfDate(endValue),
|
|
64
|
-
startValue: formatIfDate(startValue)
|
|
69
|
+
startValue: formatIfDate(startValue),
|
|
70
|
+
xMax: props.xMax
|
|
65
71
|
}))
|
|
66
72
|
|
|
67
73
|
setBrushConfig(prev => {
|
|
@@ -72,9 +78,9 @@ const ZoomBrush: FC<Props> = props => {
|
|
|
72
78
|
}
|
|
73
79
|
})
|
|
74
80
|
}
|
|
75
|
-
|
|
81
|
+
// reset brush if brush is off.
|
|
76
82
|
useEffect(() => {
|
|
77
|
-
if (!config.brush
|
|
83
|
+
if (!config.brush?.active) {
|
|
78
84
|
setBrushKey(prevKey => prevKey + 1)
|
|
79
85
|
setBrushConfig({
|
|
80
86
|
data: [],
|
|
@@ -82,10 +88,15 @@ const ZoomBrush: FC<Props> = props => {
|
|
|
82
88
|
isBrushing: false
|
|
83
89
|
})
|
|
84
90
|
}
|
|
85
|
-
}, [config.brush
|
|
91
|
+
}, [config.brush?.active])
|
|
92
|
+
|
|
93
|
+
// reset brush if filters or exclusions are ON each time
|
|
86
94
|
|
|
87
95
|
useEffect(() => {
|
|
88
|
-
|
|
96
|
+
const isFiltersActive = config.filters?.some(filter => filter.active)
|
|
97
|
+
const isExclusionsActive = config.exclusions?.active
|
|
98
|
+
|
|
99
|
+
if ((isFiltersActive || isExclusionsActive || isDashboardFilters) && config.brush?.active) {
|
|
89
100
|
setBrushKey(prevKey => prevKey + 1)
|
|
90
101
|
setBrushConfig(prev => {
|
|
91
102
|
return {
|
|
@@ -101,12 +112,12 @@ const ZoomBrush: FC<Props> = props => {
|
|
|
101
112
|
data: []
|
|
102
113
|
}
|
|
103
114
|
})
|
|
104
|
-
}, [config.filters])
|
|
115
|
+
}, [config.filters, config.exclusions, config.brush?.active, isDashboardFilters])
|
|
105
116
|
|
|
106
117
|
const calculateTop = (): number => {
|
|
107
118
|
const tickRotation = Number(config.xAxis.tickRotation) > 0 ? Number(config.xAxis.tickRotation) : 0
|
|
108
119
|
let top = 0
|
|
109
|
-
const offSet =
|
|
120
|
+
const offSet = 30
|
|
110
121
|
if (!config.xAxis.label) {
|
|
111
122
|
if (!config.isResponsiveTicks && tickRotation) {
|
|
112
123
|
top = Number(tickRotation + config.xAxis.tickWidthMax) / 1.6
|
|
@@ -123,7 +134,7 @@ const ZoomBrush: FC<Props> = props => {
|
|
|
123
134
|
}
|
|
124
135
|
if (config.xAxis.label) {
|
|
125
136
|
if (!config.isResponsiveTicks && tickRotation) {
|
|
126
|
-
top = Number(config.xAxis.tickWidthMax + tickRotation)
|
|
137
|
+
top = Number(config.xAxis.tickWidthMax + tickRotation) + offSet
|
|
127
138
|
}
|
|
128
139
|
|
|
129
140
|
if (!config.isResponsiveTicks && !tickRotation) {
|
|
@@ -131,7 +142,7 @@ const ZoomBrush: FC<Props> = props => {
|
|
|
131
142
|
}
|
|
132
143
|
|
|
133
144
|
if (config.isResponsiveTicks && !tickRotation) {
|
|
134
|
-
top = Number(config.dynamicMarginTop ? config.dynamicMarginTop : config.xAxis.labelOffset) + offSet
|
|
145
|
+
top = Number(config.dynamicMarginTop ? config.dynamicMarginTop : config.xAxis.labelOffset) + offSet * 2
|
|
135
146
|
}
|
|
136
147
|
}
|
|
137
148
|
|
|
@@ -142,30 +153,56 @@ const ZoomBrush: FC<Props> = props => {
|
|
|
142
153
|
}
|
|
143
154
|
|
|
144
155
|
return (
|
|
145
|
-
<
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
156
|
+
<ErrorBoundary component='Brush Chart'>
|
|
157
|
+
<Group
|
|
158
|
+
onMouseMove={() => {
|
|
159
|
+
// show tooltip only once before brush started
|
|
160
|
+
if (textProps.startPosition === 0 && (textProps.endPosition === 0 || textProps.endPosition === props.xMax)) {
|
|
161
|
+
setShowTooltip(true)
|
|
162
|
+
}
|
|
163
|
+
}}
|
|
164
|
+
onMouseLeave={() => setShowTooltip(false)}
|
|
165
|
+
display={config.brush?.active ? 'block' : 'none'}
|
|
166
|
+
top={Number(props.yMax) + calculateTop()}
|
|
167
|
+
left={Number(config.runtime.yAxis.size)}
|
|
168
|
+
pointerEvents='fill'
|
|
169
|
+
>
|
|
170
|
+
<rect fill='#949494' width={props.xMax} height={config.brush.height} rx={radius} />
|
|
171
|
+
<Brush
|
|
172
|
+
key={brushKey}
|
|
173
|
+
disableDraggingOverlay={true}
|
|
174
|
+
renderBrushHandle={props => (
|
|
175
|
+
<BrushHandle
|
|
176
|
+
left={Number(config.runtime.yAxis.size)}
|
|
177
|
+
showTooltip={showTooltip}
|
|
178
|
+
getTextWidth={getTextWidth}
|
|
179
|
+
pixelDistance={textProps.endPosition - textProps.startPosition}
|
|
180
|
+
textProps={textProps}
|
|
181
|
+
fontSize={fontSize[config.fontSize]}
|
|
182
|
+
{...props}
|
|
183
|
+
isBrushing={brushRef.current?.state.isBrushing}
|
|
184
|
+
/>
|
|
185
|
+
)}
|
|
186
|
+
innerRef={brushRef}
|
|
187
|
+
useWindowMoveEvents={true}
|
|
188
|
+
selectedBoxStyle={style}
|
|
189
|
+
xScale={props.xScaleBrush}
|
|
190
|
+
yScale={props.yScale}
|
|
191
|
+
width={props.xMax}
|
|
192
|
+
resizeTriggerAreas={['left', 'right']}
|
|
193
|
+
height={config.brush.height}
|
|
194
|
+
handleSize={8}
|
|
195
|
+
brushDirection='horizontal'
|
|
196
|
+
initialBrushPosition={initialPosition}
|
|
197
|
+
onChange={onBrushChange}
|
|
198
|
+
/>
|
|
199
|
+
</Group>
|
|
200
|
+
</ErrorBoundary>
|
|
164
201
|
)
|
|
165
202
|
}
|
|
166
203
|
|
|
167
204
|
const BrushHandle = props => {
|
|
168
|
-
const { x, isBrushActive, isBrushing, className, textProps } = props
|
|
205
|
+
const { x, isBrushActive, isBrushing, className, textProps, fontSize, showTooltip, left, getTextWidth } = props
|
|
169
206
|
const pathWidth = 8
|
|
170
207
|
if (!isBrushActive) {
|
|
171
208
|
return null
|
|
@@ -174,14 +211,23 @@ const BrushHandle = props => {
|
|
|
174
211
|
const isLeft = className.includes('left')
|
|
175
212
|
const transform = isLeft ? 'scale(-1, 1)' : 'translate(0,0)'
|
|
176
213
|
const textAnchor = isLeft ? 'end' : 'start'
|
|
214
|
+
const tooltipText = isLeft ? ` Drag edges to focus on a specific segment ` : ''
|
|
215
|
+
const textWidth = getTextWidth(tooltipText, `normal ${fontSize / 1.1}px sans-serif`)
|
|
177
216
|
|
|
178
217
|
return (
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
{
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
218
|
+
<>
|
|
219
|
+
{showTooltip && (
|
|
220
|
+
<Text x={(Number(textProps.xMax) - textWidth) / 2} dy={-12} pointerEvents='visiblePainted' fontSize={fontSize / 1.1}>
|
|
221
|
+
{tooltipText}
|
|
222
|
+
</Text>
|
|
223
|
+
)}
|
|
224
|
+
<Group left={x + pathWidth / 2} top={-2}>
|
|
225
|
+
<Text pointerEvents='visiblePainted' dominantBaseline='hanging' x={isLeft ? 55 : -50} y={25} verticalAnchor='start' textAnchor={textAnchor} fontSize={fontSize / 1.4}>
|
|
226
|
+
{isLeft ? textProps.startValue : textProps.endValue}
|
|
227
|
+
</Text>
|
|
228
|
+
<path cursor='ew-resize' d='M0.5,10A6,6 0 0 1 6.5,16V14A6,6 0 0 1 0.5,20ZM2.5,18V12M4.5,18V12' fill={'#297EF1'} strokeWidth='1' transform={transform}></path>
|
|
229
|
+
</Group>
|
|
230
|
+
</>
|
|
185
231
|
)
|
|
186
232
|
}
|
|
187
233
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
|
+
annotations: [],
|
|
3
|
+
allowLineToBarGraph: undefined,
|
|
2
4
|
type: 'chart',
|
|
3
5
|
debugSvg: false,
|
|
4
6
|
chartMessage: {
|
|
@@ -22,7 +24,11 @@ export default {
|
|
|
22
24
|
tipRounding: 'top',
|
|
23
25
|
isResponsiveTicks: false,
|
|
24
26
|
general: {
|
|
25
|
-
|
|
27
|
+
annotationDropdownText: 'Annotations',
|
|
28
|
+
showDownloadButton: false,
|
|
29
|
+
showMissingDataLabel: true,
|
|
30
|
+
showSuppressedSymbol: true,
|
|
31
|
+
hideNullValue: true
|
|
26
32
|
},
|
|
27
33
|
padding: {
|
|
28
34
|
left: 5,
|
|
@@ -53,7 +59,10 @@ export default {
|
|
|
53
59
|
axisPadding: 0,
|
|
54
60
|
scalePadding: 10,
|
|
55
61
|
tickRotation: 0,
|
|
56
|
-
anchors: []
|
|
62
|
+
anchors: [],
|
|
63
|
+
shoMissingDataLabel: true,
|
|
64
|
+
showMissingDataLine: true,
|
|
65
|
+
categories: []
|
|
57
66
|
},
|
|
58
67
|
boxplot: {
|
|
59
68
|
plots: [],
|
|
@@ -116,9 +125,7 @@ export default {
|
|
|
116
125
|
labelOffset: 65,
|
|
117
126
|
axisPadding: 200,
|
|
118
127
|
target: 0,
|
|
119
|
-
maxTickRotation: 0
|
|
120
|
-
showSuppressedSymbol: true,
|
|
121
|
-
showSuppressedLine: true
|
|
128
|
+
maxTickRotation: 0
|
|
122
129
|
},
|
|
123
130
|
table: {
|
|
124
131
|
label: 'Data Table',
|
|
@@ -131,7 +138,9 @@ export default {
|
|
|
131
138
|
indexLabel: '',
|
|
132
139
|
download: false,
|
|
133
140
|
showVertical: true,
|
|
134
|
-
dateDisplayFormat: ''
|
|
141
|
+
dateDisplayFormat: '',
|
|
142
|
+
showMissingDataLabel: true,
|
|
143
|
+
showSuppressedSymbol: true
|
|
135
144
|
},
|
|
136
145
|
orientation: 'vertical',
|
|
137
146
|
color: 'pinkpurple',
|
|
@@ -151,11 +160,20 @@ export default {
|
|
|
151
160
|
dynamicLegendItemLimit: 5,
|
|
152
161
|
dynamicLegendItemLimitMessage: 'Dynamic Legend Item Limit Hit.',
|
|
153
162
|
dynamicLegendChartMessage: 'Select Options from the Legend',
|
|
163
|
+
label: '',
|
|
154
164
|
lineMode: false,
|
|
155
165
|
verticalSorted: false,
|
|
156
166
|
highlightOnHover: false,
|
|
157
167
|
hideSuppressedLabels: false,
|
|
158
|
-
|
|
168
|
+
hideSuppressionLink: false,
|
|
169
|
+
seriesHighlight: [],
|
|
170
|
+
style: 'circles',
|
|
171
|
+
subStyle: 'linear blocks',
|
|
172
|
+
tickRotation: '',
|
|
173
|
+
hideBorder: {
|
|
174
|
+
side: false,
|
|
175
|
+
topBottom: true
|
|
176
|
+
}
|
|
159
177
|
},
|
|
160
178
|
brush: {
|
|
161
179
|
height: 25,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ChartConfig } from './../types/ChartConfig'
|
|
2
|
+
|
|
3
|
+
export const handleChartTabbing = (config: ChartConfig, legendId: string) => {
|
|
4
|
+
if (!config) return `dataTableSection`
|
|
5
|
+
if (!config.legend?.hide) return legendId
|
|
6
|
+
if (config?.title) return `dataTableSection__${config.title.replace(/\s/g, '')}`
|
|
7
|
+
return `dataTableSection`
|
|
8
|
+
}
|
|
@@ -29,7 +29,7 @@ export const useBarChart = () => {
|
|
|
29
29
|
updateConfig({
|
|
30
30
|
...config,
|
|
31
31
|
yAxis: {
|
|
32
|
-
...config,
|
|
32
|
+
...config.yAxis,
|
|
33
33
|
labelPlacement: 'Below Bar'
|
|
34
34
|
}
|
|
35
35
|
})
|
|
@@ -210,43 +210,6 @@ export const useBarChart = () => {
|
|
|
210
210
|
if (config.legend.highlightOnHover && config.legend.behavior === 'highlight') setSeriesHighlight([])
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
const composeSuppressionBars = ({ bar }) => {
|
|
214
|
-
const suppresedBarHeight = config.xAxis.showSuppressedLine ? 3 : 0
|
|
215
|
-
const ASTERISK = 'Asterisk'
|
|
216
|
-
const getIconPadding = symbol => (String(symbol).includes(ASTERISK) ? -5 : -suppresedBarHeight * 3)
|
|
217
|
-
const getVerticalAnchor = symbol => {
|
|
218
|
-
return String(symbol).includes(ASTERISK) ? 'middle' : 'end'
|
|
219
|
-
}
|
|
220
|
-
const getIconSize = (symbol, barWidth) => {
|
|
221
|
-
switch (symbol) {
|
|
222
|
-
case ASTERISK:
|
|
223
|
-
return barWidth * 1.2
|
|
224
|
-
case 'Double ' + ASTERISK:
|
|
225
|
-
return barWidth
|
|
226
|
-
default:
|
|
227
|
-
return barWidth / 1.5
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function shouldSuppressBar() {
|
|
232
|
-
const isSuppressed = config.preliminaryData.some(pd => {
|
|
233
|
-
const selectedSuppressionColumn = !pd.column || pd.column === bar.key
|
|
234
|
-
const isValueMatch = String(pd.value) === String(bar.value) && pd.value !== '' && pd.type === 'suppression'
|
|
235
|
-
|
|
236
|
-
return isValueMatch && selectedSuppressionColumn
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
return isSuppressed && config.xAxis.showSuppressedSymbol
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return {
|
|
243
|
-
suppresedBarHeight,
|
|
244
|
-
getIconSize,
|
|
245
|
-
getIconPadding,
|
|
246
|
-
getVerticalAnchor,
|
|
247
|
-
isSuppressed: shouldSuppressBar()
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
213
|
return {
|
|
251
214
|
isHorizontal,
|
|
252
215
|
barBorderWidth,
|
|
@@ -273,7 +236,6 @@ export const useBarChart = () => {
|
|
|
273
236
|
hoveredBar,
|
|
274
237
|
setHoveredBar,
|
|
275
238
|
onMouseOverBar,
|
|
276
|
-
onMouseLeaveBar
|
|
277
|
-
composeSuppressionBars
|
|
239
|
+
onMouseLeaveBar
|
|
278
240
|
}
|
|
279
241
|
}
|
|
@@ -30,7 +30,7 @@ const useColorScale = () => {
|
|
|
30
30
|
})
|
|
31
31
|
}
|
|
32
32
|
if (visualizationType === 'Bar' && visualizationSubType === 'regular' && series?.length === 1 && legend?.colorCode) {
|
|
33
|
-
const set = new Set(data
|
|
33
|
+
const set = new Set(data?.map(d => d[legend.colorCode]))
|
|
34
34
|
colorScale = scaleOrdinal({
|
|
35
35
|
domain: [...set],
|
|
36
36
|
range: generatePalette([...set].length)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
type ConfigType = {
|
|
2
|
+
legend: {
|
|
3
|
+
position: 'left' | 'bottom' | 'top' | 'right'
|
|
4
|
+
singleRow?: boolean
|
|
5
|
+
reverseLabelOrder?: boolean
|
|
6
|
+
verticalSorted?: boolean
|
|
7
|
+
hideBorder: {
|
|
8
|
+
side?: boolean
|
|
9
|
+
topBottom?: boolean
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const useLegendClasses = (config: ConfigType) => {
|
|
15
|
+
const containerClasses = ['legend-container']
|
|
16
|
+
const innerClasses = ['legend-container__inner']
|
|
17
|
+
|
|
18
|
+
// Handle legend positioning
|
|
19
|
+
switch (config.legend.position) {
|
|
20
|
+
case 'left':
|
|
21
|
+
containerClasses.push('left')
|
|
22
|
+
break
|
|
23
|
+
case 'right':
|
|
24
|
+
containerClasses.push('right')
|
|
25
|
+
break
|
|
26
|
+
case 'bottom':
|
|
27
|
+
containerClasses.push('bottom')
|
|
28
|
+
innerClasses.push('double-column', 'bottom')
|
|
29
|
+
break
|
|
30
|
+
case 'top':
|
|
31
|
+
containerClasses.push('top')
|
|
32
|
+
innerClasses.push('double-column', 'top')
|
|
33
|
+
break
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Handle single row configuration for 'bottom' and 'top' positions
|
|
37
|
+
if (['bottom', 'top'].includes(config.legend.position) && config.legend.singleRow) {
|
|
38
|
+
innerClasses.push('single-row')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Reverse label order
|
|
42
|
+
if (config.legend.reverseLabelOrder) {
|
|
43
|
+
innerClasses.push('d-flex', 'flex-column-reverse')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Vertical sorting for 'bottom' and 'top' positions
|
|
47
|
+
if (['bottom', 'top'].includes(config.legend.position) && config.legend.verticalSorted) {
|
|
48
|
+
innerClasses.push('vertical-sorted')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Configure border classes
|
|
52
|
+
if (
|
|
53
|
+
config.legend.hideBorder.side &&
|
|
54
|
+
(['right', 'left'].includes(config.legend.position) || !config.legend.position)
|
|
55
|
+
) {
|
|
56
|
+
containerClasses.push('no-border')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (config.legend.hideBorder.topBottom && ['top', 'bottom'].includes(config.legend.position)) {
|
|
60
|
+
containerClasses.push('no-border')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
containerClasses,
|
|
65
|
+
innerClasses
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export default useLegendClasses
|
package/src/hooks/useMinMax.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ChartConfig } from '../types/ChartConfig'
|
|
2
2
|
import _ from 'lodash'
|
|
3
|
+
import { isConvertLineToBarGraph } from '../helpers/isConvertLineToBarGraph'
|
|
3
4
|
|
|
4
5
|
type UseMinMaxProps = {
|
|
5
6
|
/** config - standard chart config */
|
|
@@ -30,13 +31,17 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
30
31
|
return { min, max }
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
const checkLineToBarGraph = () => {
|
|
35
|
+
return isConvertLineToBarGraph(config.visualizationType, data, config.allowLineToBarGraph)
|
|
36
|
+
}
|
|
37
|
+
|
|
33
38
|
const { visualizationType, series } = config
|
|
34
39
|
const { max: enteredMaxValue, min: enteredMinValue } = config.runtime.yAxis
|
|
35
40
|
const minRequiredCIPadding = 1.15 // regardless of Editor if CI data, there must be 10% padding added
|
|
36
|
-
|
|
41
|
+
const isLogarithmicAxis = config.yAxis.type === 'logarithmic'
|
|
37
42
|
// do validation bafore applying t0 charts
|
|
38
43
|
const isMaxValid = existPositiveValue ? enteredMaxValue >= maxValue : enteredMaxValue >= 0
|
|
39
|
-
const isMinValid =
|
|
44
|
+
const isMinValid = isLogarithmicAxis ? enteredMinValue >= 0 : (enteredMinValue <= 0 && minValue >= 0) || (enteredMinValue <= minValue && minValue < 0)
|
|
40
45
|
|
|
41
46
|
min = enteredMinValue && isMinValid ? enteredMinValue : minValue
|
|
42
47
|
max = enteredMaxValue && isMaxValid ? enteredMaxValue : Number.MIN_VALUE
|
|
@@ -126,10 +131,10 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
126
131
|
}
|
|
127
132
|
|
|
128
133
|
// this should not apply to bar charts if there is negative CI data
|
|
129
|
-
if ((visualizationType === 'Bar' || (visualizationType === 'Combo' && !isAllLine)) && min > 0) {
|
|
134
|
+
if ((visualizationType === 'Bar' || checkLineToBarGraph() || (visualizationType === 'Combo' && !isAllLine)) && min > 0) {
|
|
130
135
|
min = 0
|
|
131
136
|
}
|
|
132
|
-
if ((config.visualizationType === 'Bar' || (config.visualizationType === 'Combo' && !isAllLine)) && min < 0) {
|
|
137
|
+
if ((config.visualizationType === 'Bar' || checkLineToBarGraph() || (config.visualizationType === 'Combo' && !isAllLine)) && min < 0) {
|
|
133
138
|
min = min * 1.1
|
|
134
139
|
}
|
|
135
140
|
|
|
@@ -138,7 +143,7 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
138
143
|
min = 0
|
|
139
144
|
}
|
|
140
145
|
if (enteredMinValue) {
|
|
141
|
-
const isMinValid =
|
|
146
|
+
const isMinValid = isLogarithmicAxis ? enteredMinValue >= 0 && enteredMinValue < minValue : enteredMinValue < minValue
|
|
142
147
|
min = enteredMinValue && isMinValid ? enteredMinValue : minValue
|
|
143
148
|
}
|
|
144
149
|
}
|
|
@@ -148,8 +153,8 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
148
153
|
min = enteredMinValue && isMinValid ? enteredMinValue : 0
|
|
149
154
|
}
|
|
150
155
|
|
|
151
|
-
if (config.visualizationType === 'Line') {
|
|
152
|
-
const isMinValid =
|
|
156
|
+
if (config.visualizationType === 'Line' && !checkLineToBarGraph()) {
|
|
157
|
+
const isMinValid = isLogarithmicAxis ? enteredMinValue >= 0 && enteredMinValue < minValue : enteredMinValue < minValue
|
|
153
158
|
// update minValue for (0) Suppression points
|
|
154
159
|
const suppressedMinValue = tableData?.some((dataItem, index) => {
|
|
155
160
|
return config.preliminaryData?.some(pd => {
|