@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.
- package/dist/cdcchart-1a1724a1.es.js +4886 -0
- package/dist/cdcchart.js +50347 -75521
- package/index.html +1 -0
- package/package.json +22 -27
- package/src/CdcChart.tsx +1 -22
- package/src/CdcChartComponent.tsx +35 -21
- package/src/_stories/Chart.CI.stories.tsx +43 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +68 -49
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +6 -0
- package/src/_stories/Chart.stories.tsx +7 -16
- package/src/_stories/_mock/bar_chart_ci_labels.json +620 -0
- package/src/_stories/_mock/barchart_labels.mock.json +612 -0
- package/src/_stories/_mock/dynamic_series_bar_config.json +1 -1
- package/src/_stories/_mock/dynamic_series_suppression_mock.json +610 -0
- package/{examples/private/line-issue.json → src/_stories/_mock/legend_groupBy_mock.json} +46 -69
- package/src/components/Annotations/components/AnnotationDropdown.tsx +2 -2
- package/src/components/AreaChart/components/AreaChart.jsx +33 -5
- package/src/components/Axis/Categorical.Axis.tsx +2 -2
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +51 -41
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +19 -9
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +20 -9
- package/src/components/BarChart/components/BarChart.Vertical.tsx +48 -31
- package/src/components/BarChart/components/{BarChart.jsx → BarChart.tsx} +23 -5
- package/src/components/BarChart/components/context.tsx +20 -2
- package/src/components/BarChart/helpers/getBarHeights.ts +47 -0
- package/src/components/BarChart/helpers/index.ts +5 -2
- package/src/components/BarChart/helpers/tests/getBarHeights.test.ts +83 -0
- package/src/{hooks → components/BarChart/helpers}/useBarChart.ts +11 -47
- package/src/components/BoxPlot/BoxPlot.tsx +2 -1
- package/src/components/DeviationBar.jsx +2 -1
- package/src/components/EditorPanel/EditorPanel.tsx +60 -24
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +34 -34
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +51 -25
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +10 -3
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +4 -3
- package/src/components/EditorPanel/useEditorPermissions.ts +1 -4
- package/src/components/ForestPlot/ForestPlot.tsx +2 -2
- package/src/components/Legend/Legend.Component.tsx +69 -58
- package/src/components/Legend/Legend.Suppression.tsx +12 -22
- package/src/components/Legend/Legend.tsx +3 -1
- package/src/components/Legend/LegendGroup/LegendGroup.styles.css +40 -0
- package/src/components/Legend/LegendGroup/LegendGroup.tsx +103 -0
- package/src/components/Legend/LegendGroup/index.tsx +3 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +28 -0
- package/src/components/LineChart/LineChartProps.ts +3 -1
- package/src/components/LineChart/components/LineChart.Circle.tsx +77 -119
- package/src/components/LineChart/helpers.ts +133 -56
- package/src/components/LineChart/index.tsx +125 -60
- package/src/components/LinearChart.tsx +74 -115
- package/src/components/PairedBarChart.jsx +3 -2
- package/src/components/PieChart/PieChart.tsx +71 -91
- package/src/components/ScatterPlot/ScatterPlot.jsx +5 -0
- package/src/components/Sparkline/components/SparkLine.tsx +80 -18
- package/src/components/ZoomBrush.tsx +4 -4
- package/src/data/initial-state.js +4 -2
- package/src/helpers/countNumOfTicks.ts +1 -1
- package/src/helpers/dataHelpers.ts +31 -0
- package/src/helpers/sizeHelpers.ts +23 -0
- package/src/hooks/useMinMax.ts +21 -28
- package/src/hooks/useRightAxis.ts +4 -2
- package/src/hooks/useScales.ts +12 -14
- package/src/hooks/useTooltip.tsx +204 -203
- package/src/index.jsx +2 -2
- package/src/scss/main.scss +13 -6
- package/src/store/chart.actions.ts +1 -1
- package/src/types/ChartConfig.ts +7 -1
- package/LICENSE +0 -201
- package/examples/private/DEV-8850-2.json +0 -493
- package/examples/private/DEV-9822.json +0 -574
- package/examples/private/DEV-9840.json +0 -553
- package/examples/private/DEV-9850-3.json +0 -461
- package/examples/private/chart.json +0 -1084
- package/examples/private/ci_formatted.json +0 -202
- package/examples/private/ci_issue.json +0 -3016
- package/examples/private/completed.json +0 -634
- package/examples/private/dem-data-long.csv +0 -20
- package/examples/private/dem-data-long.json +0 -36
- package/examples/private/demographic_data.csv +0 -157
- package/examples/private/demographic_data.json +0 -2654
- package/examples/private/demographic_dynamic.json +0 -443
- package/examples/private/demographic_standard.json +0 -560
- package/examples/private/ehdi.json +0 -29939
- package/examples/private/not-loading.json +0 -360
- package/examples/private/test.json +0 -493
|
@@ -1,23 +1,46 @@
|
|
|
1
1
|
import { DataItem, StyleProps, Style } from './LineChartProps'
|
|
2
2
|
import { PreliminaryDataItem } from '../../types/ChartConfig'
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
import _ from 'lodash'
|
|
5
5
|
export const createStyles = (props: StyleProps): Style[] => {
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
preliminaryData,
|
|
8
|
+
data,
|
|
9
|
+
stroke,
|
|
10
|
+
strokeWidth,
|
|
11
|
+
handleLineType,
|
|
12
|
+
lineType,
|
|
13
|
+
seriesKey,
|
|
14
|
+
dynamicCategory,
|
|
15
|
+
originalSeriesKey
|
|
16
|
+
} = props
|
|
7
17
|
|
|
18
|
+
const dynamicSeriesKey = dynamicCategory ? originalSeriesKey : seriesKey
|
|
8
19
|
const validPreliminaryData: PreliminaryDataItem[] = preliminaryData.filter(
|
|
9
20
|
pd => pd.seriesKey && pd.column && pd.value && pd.type && pd.style && pd.type === 'effect'
|
|
10
21
|
)
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
const isEffectLine = (pd, dataPoint) => {
|
|
23
|
+
if (dynamicCategory) {
|
|
24
|
+
return (
|
|
25
|
+
pd.type === 'effect' &&
|
|
26
|
+
pd.style !== 'Open Circles' &&
|
|
14
27
|
pd.seriesKey === seriesKey &&
|
|
15
|
-
|
|
28
|
+
String(dataPoint[dynamicSeriesKey]) === String(pd.value)
|
|
29
|
+
)
|
|
30
|
+
} else {
|
|
31
|
+
return (
|
|
32
|
+
pd.seriesKey === seriesKey &&
|
|
33
|
+
dataPoint[pd.column] === pd.value &&
|
|
16
34
|
pd.type === 'effect' &&
|
|
17
35
|
pd.style !== 'Open Circles'
|
|
18
|
-
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const getMatchingPd = (point: DataItem): PreliminaryDataItem =>
|
|
41
|
+
validPreliminaryData.find(pd => isEffectLine(pd, point))
|
|
19
42
|
|
|
20
|
-
|
|
43
|
+
const styles: Style[] = []
|
|
21
44
|
const createStyle = (lineStyle): Style => ({
|
|
22
45
|
stroke: stroke,
|
|
23
46
|
strokeWidth: strokeWidth,
|
|
@@ -25,7 +48,8 @@ export const createStyles = (props: StyleProps): Style[] => {
|
|
|
25
48
|
})
|
|
26
49
|
|
|
27
50
|
data.forEach((d, index) => {
|
|
28
|
-
|
|
51
|
+
const matchingPd: PreliminaryDataItem = getMatchingPd(d)
|
|
52
|
+
|
|
29
53
|
let style: Style = matchingPd
|
|
30
54
|
? createStyle(handleLineType(matchingPd.style))
|
|
31
55
|
: createStyle(handleLineType(lineType))
|
|
@@ -92,11 +116,20 @@ export const filterCircles = (
|
|
|
92
116
|
}
|
|
93
117
|
|
|
94
118
|
const isCalculable = value => !isNaN(parseFloat(value)) && isFinite(value)
|
|
95
|
-
const handleFirstIndex = (
|
|
119
|
+
const handleFirstIndex = ({
|
|
120
|
+
data,
|
|
121
|
+
seriesKey,
|
|
122
|
+
preliminaryData,
|
|
123
|
+
dynamicCategory,
|
|
124
|
+
originalSeriesKey,
|
|
125
|
+
colorScale,
|
|
126
|
+
isSuppressed
|
|
127
|
+
}) => {
|
|
96
128
|
let pairCount = '0'
|
|
97
129
|
const result = {
|
|
98
130
|
data: { '0': [] },
|
|
99
|
-
style: ''
|
|
131
|
+
style: '',
|
|
132
|
+
color: ''
|
|
100
133
|
}
|
|
101
134
|
|
|
102
135
|
// If data is empty, return the empty result
|
|
@@ -104,26 +137,24 @@ const handleFirstIndex = (data, seriesKey, preliminaryData) => {
|
|
|
104
137
|
|
|
105
138
|
const firstIndexDataItem = data[0]
|
|
106
139
|
|
|
107
|
-
// Function to check if a data item matches the suppression criteria
|
|
108
|
-
const isSuppressed = pd => {
|
|
109
|
-
if (pd.type === 'effect' || pd.hideLineStyle) return
|
|
110
|
-
return (
|
|
111
|
-
pd.type == 'suppression' && pd.value === firstIndexDataItem[seriesKey] && (!pd.column || pd.column === seriesKey)
|
|
112
|
-
)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
140
|
// Find applicable suppression data for the first item
|
|
116
|
-
const suppressionData = preliminaryData.find(
|
|
141
|
+
const suppressionData = (preliminaryData ?? []).find(
|
|
142
|
+
item => item && firstIndexDataItem && isSuppressed(item, firstIndexDataItem)
|
|
143
|
+
)
|
|
117
144
|
|
|
118
145
|
if (suppressionData && suppressionData.style) {
|
|
119
146
|
// Modify first item and add to result
|
|
120
|
-
const
|
|
147
|
+
const dynamicSeriesKey = dynamicCategory ? originalSeriesKey : seriesKey
|
|
148
|
+
|
|
149
|
+
const modifiedItem = { ...firstIndexDataItem, [dynamicSeriesKey]: 0 }
|
|
150
|
+
|
|
121
151
|
result.data[pairCount].push(modifiedItem)
|
|
122
152
|
result.style = suppressionData.style
|
|
153
|
+
result.color = dynamicCategory && modifiedItem ? colorScale(modifiedItem[dynamicCategory]) : ''
|
|
123
154
|
|
|
124
155
|
// Find the next calculable item index
|
|
125
156
|
let nextIndex = 1
|
|
126
|
-
while (nextIndex < data.length && !isCalculable(data[nextIndex][
|
|
157
|
+
while (nextIndex < data.length && !isCalculable(data[nextIndex][dynamicSeriesKey])) {
|
|
127
158
|
nextIndex++
|
|
128
159
|
}
|
|
129
160
|
if (nextIndex < data.length) {
|
|
@@ -133,33 +164,38 @@ const handleFirstIndex = (data, seriesKey, preliminaryData) => {
|
|
|
133
164
|
// If no suppression, just add the first item
|
|
134
165
|
result.data[pairCount].push(firstIndexDataItem)
|
|
135
166
|
}
|
|
136
|
-
|
|
137
167
|
return result
|
|
138
168
|
}
|
|
139
169
|
|
|
140
|
-
const handleLastIndex = (
|
|
170
|
+
const handleLastIndex = ({
|
|
171
|
+
data,
|
|
172
|
+
seriesKey,
|
|
173
|
+
preliminaryData,
|
|
174
|
+
dynamicCategory,
|
|
175
|
+
originalSeriesKey,
|
|
176
|
+
colorScale,
|
|
177
|
+
isSuppressed
|
|
178
|
+
}) => {
|
|
141
179
|
let pairCount = '0'
|
|
142
180
|
const result = {
|
|
143
181
|
data: { '0': [] },
|
|
144
|
-
style: ''
|
|
182
|
+
style: '',
|
|
183
|
+
color: ''
|
|
145
184
|
}
|
|
146
|
-
|
|
185
|
+
const lastIndexDataItem = data[data.length - 1]
|
|
186
|
+
|
|
187
|
+
const dynamicSeriesKey = dynamicCategory ? originalSeriesKey : seriesKey
|
|
188
|
+
let lastAddedIndex = -1
|
|
147
189
|
preliminaryData?.forEach(pd => {
|
|
148
|
-
if (pd.type === 'effect') return
|
|
149
|
-
if (
|
|
150
|
-
data[data.length - 1][seriesKey] === pd.value &&
|
|
151
|
-
pd.style &&
|
|
152
|
-
(!pd.column || pd.column === seriesKey) &&
|
|
153
|
-
pd.type == 'suppression' &&
|
|
154
|
-
!pd.hideLineStyle
|
|
155
|
-
) {
|
|
190
|
+
if (pd.type === 'effect') return []
|
|
191
|
+
if (isSuppressed(pd, lastIndexDataItem)) {
|
|
156
192
|
const lastIndex = data.length - 1
|
|
157
|
-
const modifiedItem = { ...data[lastIndex], [
|
|
193
|
+
const modifiedItem = { ...data[lastIndex], [dynamicSeriesKey]: 0 }
|
|
158
194
|
result.data[pairCount].push(modifiedItem)
|
|
159
195
|
|
|
160
196
|
// Find previous calculable item
|
|
161
197
|
let prevIndex = lastIndex - 1
|
|
162
|
-
while (prevIndex >= 0 && !isCalculable(data[prevIndex][
|
|
198
|
+
while (prevIndex >= 0 && !isCalculable(data[prevIndex][dynamicSeriesKey])) {
|
|
163
199
|
prevIndex--
|
|
164
200
|
}
|
|
165
201
|
if (prevIndex >= 0 && lastAddedIndex !== prevIndex) {
|
|
@@ -167,33 +203,47 @@ const handleLastIndex = (data, seriesKey, preliminaryData) => {
|
|
|
167
203
|
lastAddedIndex = prevIndex
|
|
168
204
|
}
|
|
169
205
|
result.style = pd.style
|
|
206
|
+
result.color = colorScale(modifiedItem[dynamicCategory])
|
|
170
207
|
}
|
|
171
208
|
})
|
|
172
209
|
|
|
173
210
|
return result
|
|
174
211
|
}
|
|
175
212
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
213
|
+
export const handleMiddleIndices = ({
|
|
214
|
+
data,
|
|
215
|
+
seriesKey,
|
|
216
|
+
preliminaryData,
|
|
217
|
+
dynamicCategory,
|
|
218
|
+
originalSeriesKey,
|
|
219
|
+
colorScale,
|
|
220
|
+
isSuppressed
|
|
221
|
+
}) => {
|
|
179
222
|
let result = {
|
|
180
223
|
data: {},
|
|
181
|
-
style: ''
|
|
224
|
+
style: '',
|
|
225
|
+
color: 'red'
|
|
182
226
|
}
|
|
227
|
+
|
|
228
|
+
//skip processing if data or preliminaryData is not an array
|
|
229
|
+
if (!Array.isArray(data) || !Array.isArray(preliminaryData)) {
|
|
230
|
+
return result
|
|
231
|
+
}
|
|
232
|
+
|
|
183
233
|
// Variable to count the number of sibling pairs found
|
|
184
234
|
let pairCount = 1
|
|
235
|
+
const dynamicSeriesKey = dynamicCategory ? originalSeriesKey : seriesKey
|
|
185
236
|
|
|
186
237
|
// Loop through the data array to find each occurrence of the target value
|
|
187
238
|
data.forEach((item, index) => {
|
|
188
239
|
preliminaryData.forEach(pd => {
|
|
189
|
-
|
|
190
|
-
if (item[seriesKey] === targetValue) {
|
|
240
|
+
if (isSuppressed(pd, item)) {
|
|
191
241
|
let siblingBefore = null
|
|
192
242
|
let siblingAfter = null
|
|
193
243
|
|
|
194
244
|
// Find the nearest numeric sibling before the current index
|
|
195
245
|
for (let i = index - 1; i >= 0; i--) {
|
|
196
|
-
if (isCalculable(data[i][
|
|
246
|
+
if (isCalculable(data[i][dynamicSeriesKey])) {
|
|
197
247
|
siblingBefore = data[i]
|
|
198
248
|
break // Stop searching once a valid sibling is found
|
|
199
249
|
}
|
|
@@ -201,7 +251,7 @@ function handleMiddleIndices(data, seriesKey, preliminaryData) {
|
|
|
201
251
|
|
|
202
252
|
// Find the nearest numeric sibling after the current index
|
|
203
253
|
for (let j = index + 1; j < data.length; j++) {
|
|
204
|
-
if (isCalculable(data[j][
|
|
254
|
+
if (isCalculable(data[j][dynamicSeriesKey])) {
|
|
205
255
|
siblingAfter = data[j]
|
|
206
256
|
break // Stop searching once a valid sibling is found
|
|
207
257
|
}
|
|
@@ -210,24 +260,51 @@ function handleMiddleIndices(data, seriesKey, preliminaryData) {
|
|
|
210
260
|
// Only add siblings to results if both siblings are found
|
|
211
261
|
if (siblingBefore && siblingAfter) {
|
|
212
262
|
result.style = pd.style
|
|
263
|
+
result.color = colorScale(item[dynamicCategory])
|
|
213
264
|
result.data[pairCount++] = [siblingBefore, siblingAfter]
|
|
214
265
|
}
|
|
215
266
|
}
|
|
216
267
|
})
|
|
217
268
|
})
|
|
269
|
+
|
|
218
270
|
return result
|
|
219
271
|
}
|
|
220
272
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
273
|
+
export const createDataSegments = props => {
|
|
274
|
+
const dynamicData = (props.data ?? []).filter(d => {
|
|
275
|
+
if (!props?.dynamicCategory) return true
|
|
276
|
+
|
|
277
|
+
return (props.preliminaryData ?? []).some(pd => d?.[props.dynamicCategory] === props?.seriesKey)
|
|
278
|
+
})
|
|
279
|
+
const isSuppressed = (pd, dataItem) => {
|
|
280
|
+
if (pd.type === 'effect' || pd.hideLineStyle) return false
|
|
281
|
+
|
|
282
|
+
if (props.dynamicCategory) {
|
|
283
|
+
return (
|
|
284
|
+
pd.type === 'suppression' &&
|
|
285
|
+
(!pd.column || pd.column === dataItem[props.dynamicCategory]) &&
|
|
286
|
+
pd.value === dataItem[props.originalSeriesKey]
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
return (
|
|
290
|
+
pd.type === 'suppression' &&
|
|
291
|
+
pd.value === dataItem[props.seriesKey] &&
|
|
292
|
+
(!pd.column || pd.column === props.seriesKey)
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
const firstSegment = handleFirstIndex({ ...props, data: dynamicData, isSuppressed })
|
|
296
|
+
const lastSegment = handleLastIndex({ ...props, data: dynamicData, isSuppressed })
|
|
297
|
+
const middleSegments = handleMiddleIndices({ ...props, data: dynamicData, isSuppressed })
|
|
298
|
+
|
|
299
|
+
const segments = [firstSegment, middleSegments, lastSegment]
|
|
300
|
+
|
|
301
|
+
// ✅ Filter: keep only segments with real data
|
|
302
|
+
return segments.filter(segment => {
|
|
303
|
+
if (!segment || !segment.data) return false
|
|
304
|
+
|
|
305
|
+
// Check if at least one non-empty array exists in `data`
|
|
306
|
+
const hasData = Object.values(segment.data).some(arr => Array.isArray(arr) && arr.length > 0)
|
|
307
|
+
|
|
308
|
+
return hasData
|
|
309
|
+
})
|
|
233
310
|
}
|
|
@@ -28,7 +28,6 @@ const LineChart = (props: LineChartProps) => {
|
|
|
28
28
|
const {
|
|
29
29
|
getXAxisData,
|
|
30
30
|
getYAxisData,
|
|
31
|
-
handleTooltipClick,
|
|
32
31
|
handleTooltipMouseOff,
|
|
33
32
|
handleTooltipMouseOver,
|
|
34
33
|
tooltipData,
|
|
@@ -36,53 +35,53 @@ const LineChart = (props: LineChartProps) => {
|
|
|
36
35
|
xScale,
|
|
37
36
|
yMax,
|
|
38
37
|
yScale,
|
|
39
|
-
|
|
38
|
+
} = props
|
|
40
39
|
|
|
41
40
|
// prettier-ignore
|
|
42
41
|
const { colorScale, config, formatNumber, handleLineType, parseDate, seriesHighlight, tableData, transformedData, updateConfig, brushConfig,clean } = useContext<ChartContext>(ConfigContext)
|
|
43
42
|
const { yScaleRight } = useRightAxis({ config, yMax, data: transformedData, updateConfig })
|
|
44
|
-
|
|
43
|
+
const showSingleSeries = config.tooltips.singleSeries
|
|
45
44
|
|
|
46
45
|
const DEBUG = false
|
|
47
46
|
const { lineDatapointStyle, showLineSeriesLabels, legend } = config
|
|
48
|
-
|
|
49
|
-
let tableD = tableData
|
|
47
|
+
const isBrushOn = brushConfig.data.length > 0 && config.brush?.active
|
|
50
48
|
// if brush on use brush data and clean
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
tableD = clean(brushConfig.data)
|
|
54
|
-
}
|
|
49
|
+
const data = isBrushOn ? clean(brushConfig.data) : transformedData
|
|
50
|
+
const _tableData = isBrushOn ? clean(brushConfig.data) : tableData
|
|
55
51
|
|
|
56
52
|
const xPos = d => {
|
|
57
53
|
return xScale(getXAxisData(d)) + (xScale.bandwidth ? xScale.bandwidth() / 2 : 0)
|
|
58
54
|
}
|
|
59
55
|
|
|
56
|
+
const tooltipPoints = []
|
|
57
|
+
|
|
60
58
|
return (
|
|
61
59
|
<ErrorBoundary component='LineChart'>
|
|
62
|
-
<Group left={Number(config.runtime.yAxis.size)}>
|
|
63
|
-
{' '}
|
|
60
|
+
<Group left={Number(config.runtime.yAxis.size)} className='line-chart-group'>
|
|
64
61
|
{/* left - expects a number not a string */}
|
|
65
62
|
{(config.runtime.lineSeriesKeys || config.runtime.seriesKeys).map((seriesKey, index) => {
|
|
66
63
|
const seriesData = config.runtime.series.find(item => item.dataKey === seriesKey)
|
|
64
|
+
const _seriesKey = seriesData.dynamicCategory ? seriesData.originalDataKey : seriesKey
|
|
67
65
|
const lineType = seriesData.type
|
|
68
66
|
const seriesAxis = seriesData.axis || 'left'
|
|
69
67
|
const displayArea =
|
|
70
68
|
legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1
|
|
71
69
|
|
|
72
|
-
const suppressedSegments = createDataSegments(
|
|
73
|
-
tableData,
|
|
70
|
+
const suppressedSegments = createDataSegments({
|
|
71
|
+
data: tableData,
|
|
74
72
|
seriesKey,
|
|
75
|
-
config.preliminaryData,
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
preliminaryData: config.preliminaryData,
|
|
74
|
+
dynamicCategory: seriesData.dynamicCategory,
|
|
75
|
+
originalSeriesKey: _seriesKey,
|
|
76
|
+
colorScale
|
|
77
|
+
})
|
|
78
78
|
const isSplitLine =
|
|
79
79
|
config?.preliminaryData?.filter(pd => pd.style && !pd.style.includes('Circles')).length > 0
|
|
80
80
|
|
|
81
81
|
const _data = seriesData.dynamicCategory
|
|
82
82
|
? data.filter(d => d[seriesData.dynamicCategory] === seriesKey)
|
|
83
83
|
: data
|
|
84
|
-
const
|
|
85
|
-
const circleData = filterCircles(config?.preliminaryData, tableD, _seriesKey)
|
|
84
|
+
const circleData = filterCircles(config?.preliminaryData, tableData, _seriesKey)
|
|
86
85
|
return (
|
|
87
86
|
<Group
|
|
88
87
|
key={`series-${seriesKey}-${index}`}
|
|
@@ -108,11 +107,15 @@ const LineChart = (props: LineChartProps) => {
|
|
|
108
107
|
height={Number(yMax)}
|
|
109
108
|
fill={DEBUG ? 'red' : 'transparent'}
|
|
110
109
|
fillOpacity={0.05}
|
|
111
|
-
onMouseMove={e => handleTooltipMouseOver(e, tableData)}
|
|
112
|
-
onMouseOut={handleTooltipMouseOff}
|
|
113
|
-
onClick={e => handleTooltipClick(e, data)}
|
|
114
110
|
/>
|
|
115
111
|
{_data.map((d, dataIndex) => {
|
|
112
|
+
tooltipPoints.push({
|
|
113
|
+
color: colorScale(config.runtime.seriesLabels[seriesKey]),
|
|
114
|
+
seriesKey: _seriesKey,
|
|
115
|
+
seriesIndex: index,
|
|
116
|
+
xValue: d[config.xAxis.dataKey],
|
|
117
|
+
yValue: d[_seriesKey]
|
|
118
|
+
})
|
|
116
119
|
return (
|
|
117
120
|
isNumber(d[_seriesKey]) && (
|
|
118
121
|
<React.Fragment key={`series-${seriesKey}-point-${dataIndex}`}>
|
|
@@ -136,7 +139,6 @@ const LineChart = (props: LineChartProps) => {
|
|
|
136
139
|
<LineChartCircle
|
|
137
140
|
mode='ALWAYS_SHOW_POINTS'
|
|
138
141
|
dataIndex={dataIndex}
|
|
139
|
-
circleData={circleData}
|
|
140
142
|
tableData={tableData}
|
|
141
143
|
data={_data}
|
|
142
144
|
d={d}
|
|
@@ -155,18 +157,37 @@ const LineChart = (props: LineChartProps) => {
|
|
|
155
157
|
/>
|
|
156
158
|
)}
|
|
157
159
|
|
|
160
|
+
{(lineDatapointStyle === 'hover' || lineDatapointStyle === 'hidden') && (
|
|
161
|
+
<LineChartCircle
|
|
162
|
+
mode='HOVER_POINTS'
|
|
163
|
+
dataIndex={dataIndex}
|
|
164
|
+
tableData={tableData}
|
|
165
|
+
data={_data}
|
|
166
|
+
d={d}
|
|
167
|
+
config={config}
|
|
168
|
+
seriesKey={_seriesKey}
|
|
169
|
+
displayArea={displayArea}
|
|
170
|
+
xScale={xScale}
|
|
171
|
+
yScale={yScale}
|
|
172
|
+
colorScale={colorScale}
|
|
173
|
+
parseDate={parseDate}
|
|
174
|
+
yScaleRight={yScaleRight}
|
|
175
|
+
seriesAxis={seriesAxis}
|
|
176
|
+
seriesIndex={index}
|
|
177
|
+
key={`line-hover-circle--${dataIndex}`}
|
|
178
|
+
/>
|
|
179
|
+
)}
|
|
180
|
+
|
|
158
181
|
<LineChartCircle
|
|
159
182
|
mode='ISOLATED_POINTS'
|
|
160
183
|
seriesIndex={index}
|
|
161
184
|
dataIndex={dataIndex}
|
|
162
185
|
tableData={tableData}
|
|
163
|
-
circleData={circleData}
|
|
164
186
|
data={_data}
|
|
165
187
|
d={d}
|
|
166
188
|
config={config}
|
|
167
189
|
seriesKey={_seriesKey}
|
|
168
190
|
displayArea={displayArea}
|
|
169
|
-
tooltipData={tooltipData}
|
|
170
191
|
xScale={xScale}
|
|
171
192
|
yScale={yScale}
|
|
172
193
|
colorScale={colorScale}
|
|
@@ -179,28 +200,6 @@ const LineChart = (props: LineChartProps) => {
|
|
|
179
200
|
)
|
|
180
201
|
)
|
|
181
202
|
})}
|
|
182
|
-
<>
|
|
183
|
-
{lineDatapointStyle === 'hover' && (
|
|
184
|
-
<LineChartCircle
|
|
185
|
-
seriesIndex={index}
|
|
186
|
-
tableData={tableData}
|
|
187
|
-
dataIndex={0}
|
|
188
|
-
mode='HOVER_POINTS'
|
|
189
|
-
circleData={circleData}
|
|
190
|
-
data={_data}
|
|
191
|
-
config={config}
|
|
192
|
-
seriesKey={seriesKey}
|
|
193
|
-
displayArea={displayArea}
|
|
194
|
-
tooltipData={tooltipData}
|
|
195
|
-
xScale={xScale}
|
|
196
|
-
yScale={yScale}
|
|
197
|
-
colorScale={colorScale}
|
|
198
|
-
parseDate={parseDate}
|
|
199
|
-
yScaleRight={yScaleRight}
|
|
200
|
-
seriesAxis={seriesAxis}
|
|
201
|
-
/>
|
|
202
|
-
)}
|
|
203
|
-
</>
|
|
204
203
|
|
|
205
204
|
{/* SPLIT LINE */}
|
|
206
205
|
{isSplitLine ? (
|
|
@@ -217,15 +216,17 @@ const LineChart = (props: LineChartProps) => {
|
|
|
217
216
|
}
|
|
218
217
|
styles={createStyles({
|
|
219
218
|
preliminaryData: config.preliminaryData,
|
|
220
|
-
data:
|
|
219
|
+
data: tableData,
|
|
221
220
|
stroke: colorScale(config.runtime.seriesLabels[seriesKey]),
|
|
222
221
|
strokeWidth: seriesData.weight || 2,
|
|
223
222
|
handleLineType,
|
|
224
223
|
lineType,
|
|
225
|
-
seriesKey
|
|
224
|
+
seriesKey,
|
|
225
|
+
dynamicCategory: seriesData.dynamicCategory,
|
|
226
|
+
originalSeriesKey: _seriesKey
|
|
226
227
|
})}
|
|
227
228
|
defined={(item, i) => {
|
|
228
|
-
return item[
|
|
229
|
+
return item[_seriesKey] !== '' && item[_seriesKey] !== null && item[_seriesKey] !== undefined
|
|
229
230
|
}}
|
|
230
231
|
/>
|
|
231
232
|
|
|
@@ -238,16 +239,22 @@ const LineChart = (props: LineChartProps) => {
|
|
|
238
239
|
x={d => xPos(d)}
|
|
239
240
|
y={d =>
|
|
240
241
|
seriesAxis === 'Right'
|
|
241
|
-
? yScaleRight(getYAxisData(d,
|
|
242
|
-
: yScale(Number(getYAxisData(d,
|
|
242
|
+
? yScaleRight(getYAxisData(d, _seriesKey))
|
|
243
|
+
: yScale(Number(getYAxisData(d, _seriesKey)))
|
|
244
|
+
}
|
|
245
|
+
stroke={
|
|
246
|
+
seriesData.dynamicCategory
|
|
247
|
+
? segment.color
|
|
248
|
+
: colorScale(config.runtime.seriesLabels[seriesKey])
|
|
243
249
|
}
|
|
244
|
-
stroke={colorScale(config.runtime.seriesLabels[seriesKey])}
|
|
245
250
|
strokeWidth={seriesData[0]?.weight || 2}
|
|
246
251
|
strokeOpacity={1}
|
|
247
252
|
shapeRendering='geometricPrecision'
|
|
248
253
|
strokeDasharray={handleLineType(segment.style)}
|
|
249
254
|
defined={(item, i) => {
|
|
250
|
-
return
|
|
255
|
+
return (
|
|
256
|
+
item[_seriesKey] !== '' && item[_seriesKey] !== null && item[_seriesKey] !== undefined
|
|
257
|
+
)
|
|
251
258
|
}}
|
|
252
259
|
/>
|
|
253
260
|
)
|
|
@@ -269,6 +276,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
269
276
|
return (
|
|
270
277
|
<AreaClosed
|
|
271
278
|
key={`area-closed-${seriesKey}-${categoryIndex}`}
|
|
279
|
+
className='confidence-interval'
|
|
272
280
|
data={categoryData}
|
|
273
281
|
x={d => xPos(d)}
|
|
274
282
|
y0={d => yScale(d[config.confidenceKeys.lower])} // Lower bound of the confidence interval
|
|
@@ -393,21 +401,32 @@ const LineChart = (props: LineChartProps) => {
|
|
|
393
401
|
break
|
|
394
402
|
}
|
|
395
403
|
}
|
|
396
|
-
if (!lastDatum) {
|
|
404
|
+
if (!lastDatum || legend.position === 'right') {
|
|
397
405
|
return <></>
|
|
398
406
|
}
|
|
407
|
+
|
|
408
|
+
let labelText = config.runtime.seriesLabels[seriesKey] || seriesKey
|
|
409
|
+
// truncate labels longer that 10 chars
|
|
410
|
+
const ellipsis = '...'
|
|
411
|
+
if (labelText.length > 10) {
|
|
412
|
+
labelText = labelText.substring(0, 10) + ellipsis
|
|
413
|
+
}
|
|
414
|
+
|
|
399
415
|
return (
|
|
400
416
|
<Text
|
|
417
|
+
display={
|
|
418
|
+
legend.behavior === 'highlight' ||
|
|
419
|
+
(seriesHighlight.length === 0 && !legend.dynamicLegend) ||
|
|
420
|
+
seriesHighlight.indexOf(seriesKey) !== -1
|
|
421
|
+
? 'block'
|
|
422
|
+
: 'none'
|
|
423
|
+
}
|
|
401
424
|
x={xPos(lastDatum) + 5}
|
|
402
425
|
y={yScale(getYAxisData(lastDatum, seriesKey))}
|
|
403
426
|
alignmentBaseline='middle'
|
|
404
|
-
fill={
|
|
405
|
-
config.colorMatchLineSeriesLabels && colorScale
|
|
406
|
-
? colorScale(config.runtime.seriesLabels[seriesKey] || seriesKey)
|
|
407
|
-
: 'black'
|
|
408
|
-
}
|
|
427
|
+
fill={colorScale(config.runtime.seriesLabels[seriesKey] || seriesKey)}
|
|
409
428
|
>
|
|
410
|
-
{
|
|
429
|
+
{labelText}
|
|
411
430
|
</Text>
|
|
412
431
|
)
|
|
413
432
|
})}
|
|
@@ -421,6 +440,52 @@ const LineChart = (props: LineChartProps) => {
|
|
|
421
440
|
</Text>
|
|
422
441
|
)}
|
|
423
442
|
</Group>
|
|
443
|
+
<Group left={Number(config.runtime.yAxis.size)} className='glyph-tooltip-group'>
|
|
444
|
+
<Group key={`tooltip-group`} display={'block'}>
|
|
445
|
+
{/* tooltips */}
|
|
446
|
+
<Bar
|
|
447
|
+
key={'tooltip bars'}
|
|
448
|
+
width={Number(xMax)}
|
|
449
|
+
height={Number(yMax)}
|
|
450
|
+
fill={DEBUG ? 'red' : 'transparent'}
|
|
451
|
+
fillOpacity={0.05}
|
|
452
|
+
onMouseMove={e => {
|
|
453
|
+
if (showSingleSeries) return
|
|
454
|
+
handleTooltipMouseOver(e, tableData)
|
|
455
|
+
}}
|
|
456
|
+
onMouseOut={handleTooltipMouseOff}
|
|
457
|
+
/>
|
|
458
|
+
{tooltipPoints.map((d, dataIndex) => {
|
|
459
|
+
const { _data, seriesKey, seriesIndex, color } = d
|
|
460
|
+
return (
|
|
461
|
+
<React.Fragment key={`series-${seriesKey}-point-${dataIndex}`}>
|
|
462
|
+
<LineChartCircle
|
|
463
|
+
mode='TOOLTIP_POINTS'
|
|
464
|
+
dataIndex={dataIndex}
|
|
465
|
+
tooltipPoint={d}
|
|
466
|
+
tableData={tableData}
|
|
467
|
+
data={_data}
|
|
468
|
+
d={d}
|
|
469
|
+
config={config}
|
|
470
|
+
seriesKey={seriesKey}
|
|
471
|
+
displayArea={true}
|
|
472
|
+
tooltipData={tooltipData}
|
|
473
|
+
xScale={xScale}
|
|
474
|
+
yScale={yScale}
|
|
475
|
+
colorScale={colorScale}
|
|
476
|
+
parseDate={parseDate}
|
|
477
|
+
yScaleRight={yScaleRight}
|
|
478
|
+
seriesAxis={'[circle]'}
|
|
479
|
+
seriesIndex={seriesIndex}
|
|
480
|
+
key={`line-circle--${dataIndex}`}
|
|
481
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
482
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
483
|
+
/>
|
|
484
|
+
</React.Fragment>
|
|
485
|
+
)
|
|
486
|
+
})}
|
|
487
|
+
</Group>
|
|
488
|
+
</Group>
|
|
424
489
|
{config.visualizationType === 'Bump Chart' && (
|
|
425
490
|
<LineChartBumpCircle config={config} xScale={xScale} yScale={yScale} />
|
|
426
491
|
)}
|