@cdc/chart 4.24.10 → 4.24.12
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 +34651 -33978
- package/examples/feature/boxplot/boxplot-data.json +88 -22
- package/examples/feature/boxplot/boxplot.json +540 -16
- package/examples/feature/boxplot/testing.csv +7 -7
- package/examples/feature/sankey/sankey-example-data.json +126 -14
- package/examples/feature/tests-date-exclusions/date-exclusions-config.json +372 -12
- package/examples/private/DEV-8850-2.json +493 -0
- package/examples/private/DEV-9822.json +574 -0
- package/examples/private/DEV-9840.json +553 -0
- package/examples/private/DEV-9850-3.json +461 -0
- package/examples/private/chart.json +1084 -0
- package/examples/private/ci_formatted.json +202 -0
- package/examples/private/ci_issue.json +3016 -0
- package/examples/private/completed.json +634 -0
- package/examples/private/dem-data-long.csv +20 -0
- package/examples/private/dem-data-long.json +36 -0
- package/examples/private/demographic_data.csv +157 -0
- package/examples/private/demographic_data.json +2654 -0
- package/examples/private/demographic_dynamic.json +443 -0
- package/examples/private/demographic_standard.json +560 -0
- package/examples/private/test.json +493 -0
- package/index.html +10 -7
- package/package.json +2 -2
- package/src/CdcChart.tsx +132 -152
- package/src/_stories/Chart.Anchors.stories.tsx +31 -0
- package/src/_stories/Chart.CustomColors.stories.tsx +19 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +34 -0
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +42 -1
- package/src/_stories/Chart.stories.tsx +37 -6
- package/src/_stories/ChartAxisLabels.stories.tsx +4 -1
- package/src/_stories/ChartEditor.stories.tsx +27 -0
- package/src/_stories/ChartLine.Suppression.stories.tsx +25 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +8 -0
- package/{examples/feature/area/area-chart-date-city-temperature.json → src/_stories/_mock/area_chart_stacked.json} +125 -27
- package/src/_stories/_mock/boxplot_multiseries.json +647 -0
- package/src/_stories/_mock/dynamic_series_bar_config.json +723 -0
- package/src/_stories/_mock/dynamic_series_config.json +979 -0
- package/src/_stories/_mock/line_chart_dynamic_ci.json +493 -0
- package/src/_stories/_mock/line_chart_non_dynamic_ci.json +522 -0
- package/{examples/feature/scatterplot/scatterplot.json → src/_stories/_mock/scatterplot_mock.json} +62 -92
- package/src/_stories/_mock/short_dates.json +288 -0
- package/src/_stories/_mock/suppression_mock.json +1549 -0
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +15 -3
- package/src/components/Axis/Categorical.Axis.tsx +2 -2
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +46 -37
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +43 -9
- package/src/components/BarChart/components/BarChart.Vertical.tsx +53 -47
- package/src/components/BarChart/helpers/getBarData.ts +28 -0
- package/src/components/BarChart/helpers/index.ts +1 -2
- package/src/components/BarChart/helpers/tests/getBarData.test.ts +74 -0
- package/src/components/BoxPlot/BoxPlot.tsx +131 -0
- package/src/components/BoxPlot/helpers/index.ts +54 -0
- package/src/components/BrushChart.tsx +23 -26
- package/src/components/EditorPanel/EditorPanel.tsx +117 -139
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +3 -3
- package/src/components/EditorPanel/components/Panels/Panel.BoxPlot.tsx +51 -6
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +40 -9
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +122 -56
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +1 -2
- package/src/components/EditorPanel/useEditorPermissions.ts +20 -2
- package/src/components/Legend/Legend.Component.tsx +11 -12
- package/src/components/Legend/Legend.tsx +16 -16
- package/src/components/Legend/helpers/getLegendClasses.ts +59 -0
- package/src/components/Legend/helpers/index.ts +2 -1
- package/src/components/Legend/tests/getLegendClasses.test.ts +115 -0
- package/src/components/LineChart/components/LineChart.Circle.tsx +1 -1
- package/src/components/LineChart/helpers.ts +49 -43
- package/src/components/LineChart/index.tsx +135 -83
- package/src/components/LinearChart.tsx +187 -181
- package/src/components/PieChart/PieChart.tsx +7 -1
- package/src/components/Sankey/components/ColumnList.tsx +19 -0
- package/src/components/Sankey/components/Sankey.tsx +479 -0
- package/src/components/Sankey/helpers/getSankeyTooltip.tsx +33 -0
- package/src/components/Sankey/index.tsx +1 -492
- package/src/components/Sankey/sankey.scss +22 -21
- package/src/components/Sankey/types/index.ts +1 -1
- package/src/components/Sankey/useSankeyAlert.tsx +60 -0
- package/src/components/ScatterPlot/ScatterPlot.jsx +20 -4
- package/src/data/initial-state.js +7 -12
- package/src/helpers/countNumOfTicks.ts +57 -0
- package/src/helpers/getQuartiles.ts +15 -18
- package/src/hooks/useMinMax.ts +44 -16
- package/src/hooks/useReduceData.ts +43 -10
- package/src/hooks/useScales.ts +90 -35
- package/src/hooks/useTooltip.tsx +59 -50
- package/src/scss/DataTable.scss +5 -0
- package/src/scss/main.scss +6 -20
- package/src/types/ChartConfig.ts +6 -19
- package/src/types/ChartContext.ts +4 -1
- package/src/types/ForestPlot.ts +8 -0
- package/src/components/BoxPlot/BoxPlot.jsx +0 -111
- package/src/hooks/useLegendClasses.ts +0 -72
|
@@ -15,7 +15,8 @@ export const getMarginTop = (isBottomOrSmallViewport, config) => {
|
|
|
15
15
|
return '0px'
|
|
16
16
|
}
|
|
17
17
|
if (isBottomOrSmallViewport && config.brush?.active) {
|
|
18
|
-
|
|
18
|
+
const BRUSH_HEIGHT_MULTIPLIER = 1.5
|
|
19
|
+
return `${config.brush.height * BRUSH_HEIGHT_MULTIPLIER}px`
|
|
19
20
|
}
|
|
20
21
|
return '20px'
|
|
21
22
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { getLegendClasses } from './../helpers/getLegendClasses'
|
|
3
|
+
import { ChartConfig } from '../../../types/ChartConfig'
|
|
4
|
+
|
|
5
|
+
describe('getLegendClasses', () => {
|
|
6
|
+
it('should return correct classes for left position', () => {
|
|
7
|
+
const config: ChartConfig = {
|
|
8
|
+
legend: {
|
|
9
|
+
position: 'left',
|
|
10
|
+
singleRow: false,
|
|
11
|
+
reverseLabelOrder: false,
|
|
12
|
+
verticalSorted: false,
|
|
13
|
+
hideBorder: { side: false, topBottom: false }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const result = getLegendClasses(config)
|
|
17
|
+
expect(result.containerClasses).toContain('left')
|
|
18
|
+
// Left Position Charts can't have single row...
|
|
19
|
+
expect(result.innerClasses).not.toContain('single-row')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('should return correct classes for right position', () => {
|
|
23
|
+
const config: ChartConfig = {
|
|
24
|
+
legend: {
|
|
25
|
+
position: 'right',
|
|
26
|
+
singleRow: false,
|
|
27
|
+
reverseLabelOrder: false,
|
|
28
|
+
verticalSorted: false,
|
|
29
|
+
hideBorder: { side: false, topBottom: false }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const result = getLegendClasses(config)
|
|
33
|
+
expect(result.containerClasses).toContain('right')
|
|
34
|
+
// Right Position Charts can't have single row...
|
|
35
|
+
expect(result.innerClasses).not.toContain('single-row')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should return correct classes for bottom position with single row', () => {
|
|
39
|
+
const config: ChartConfig = {
|
|
40
|
+
legend: {
|
|
41
|
+
position: 'bottom',
|
|
42
|
+
singleRow: true,
|
|
43
|
+
reverseLabelOrder: false,
|
|
44
|
+
verticalSorted: false,
|
|
45
|
+
hideBorder: { side: false, topBottom: false }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const result = getLegendClasses(config)
|
|
49
|
+
expect(result.containerClasses).toContain('bottom')
|
|
50
|
+
expect(result.innerClasses).toContain('double-column')
|
|
51
|
+
expect(result.innerClasses).toContain('bottom')
|
|
52
|
+
expect(result.innerClasses).toContain('single-row')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('should return correct classes for top position with vertical sorting', () => {
|
|
56
|
+
const config: ChartConfig = {
|
|
57
|
+
legend: {
|
|
58
|
+
position: 'top',
|
|
59
|
+
singleRow: false,
|
|
60
|
+
reverseLabelOrder: false,
|
|
61
|
+
verticalSorted: true,
|
|
62
|
+
hideBorder: { side: false, topBottom: false }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const result = getLegendClasses(config)
|
|
66
|
+
expect(result.containerClasses).toContain('top')
|
|
67
|
+
expect(result.innerClasses).toContain('double-column')
|
|
68
|
+
expect(result.innerClasses).toContain('top')
|
|
69
|
+
expect(result.innerClasses).toContain('vertical-sorted')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should return correct classes for reverse label order', () => {
|
|
73
|
+
const config: ChartConfig = {
|
|
74
|
+
legend: {
|
|
75
|
+
position: 'bottom',
|
|
76
|
+
singleRow: false,
|
|
77
|
+
reverseLabelOrder: true,
|
|
78
|
+
verticalSorted: false,
|
|
79
|
+
hideBorder: { side: false, topBottom: false }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const result = getLegendClasses(config)
|
|
83
|
+
expect(result.innerClasses).toContain('d-flex')
|
|
84
|
+
expect(result.innerClasses).toContain('flex-column-reverse')
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('should return correct classes for hide border side', () => {
|
|
88
|
+
const config: ChartConfig = {
|
|
89
|
+
legend: {
|
|
90
|
+
position: 'left',
|
|
91
|
+
singleRow: false,
|
|
92
|
+
reverseLabelOrder: false,
|
|
93
|
+
verticalSorted: false,
|
|
94
|
+
hideBorder: { side: true, topBottom: false }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const result = getLegendClasses(config)
|
|
98
|
+
expect(result.containerClasses).toContain('border-0')
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should return correct classes for hide border topBottom', () => {
|
|
102
|
+
const config: ChartConfig = {
|
|
103
|
+
legend: {
|
|
104
|
+
position: 'top',
|
|
105
|
+
singleRow: false,
|
|
106
|
+
reverseLabelOrder: false,
|
|
107
|
+
verticalSorted: false,
|
|
108
|
+
hideBorder: { side: false, topBottom: true }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const result = getLegendClasses(config)
|
|
112
|
+
expect(result.containerClasses).toContain('border-0')
|
|
113
|
+
expect(result.containerClasses).toContain('p-0')
|
|
114
|
+
})
|
|
115
|
+
})
|
|
@@ -82,7 +82,7 @@ const LineChartCircle = (props: LineChartCircleProps) => {
|
|
|
82
82
|
const isMatch = circleData?.some(
|
|
83
83
|
cd => cd[config.xAxis.dataKey] === d[config.xAxis.dataKey] && cd[seriesKey] === d[seriesKey]
|
|
84
84
|
)
|
|
85
|
-
if (isMatch) {
|
|
85
|
+
if (isMatch || !filtered) {
|
|
86
86
|
return <></>
|
|
87
87
|
}
|
|
88
88
|
return (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DataItem, StyleProps, Style } from './LineChartProps'
|
|
2
2
|
import { PreliminaryDataItem } from '../../types/ChartConfig'
|
|
3
|
+
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
3
4
|
import _ from 'lodash'
|
|
4
5
|
export const createStyles = (props: StyleProps): Style[] => {
|
|
5
6
|
const { preliminaryData, data, stroke, strokeWidth, handleLineType, lineType, seriesKey } = props
|
|
@@ -92,8 +93,9 @@ export const filterCircles = (
|
|
|
92
93
|
|
|
93
94
|
const isCalculable = value => !isNaN(parseFloat(value)) && isFinite(value)
|
|
94
95
|
const handleFirstIndex = (data, seriesKey, preliminaryData) => {
|
|
96
|
+
let pairCount = '0'
|
|
95
97
|
const result = {
|
|
96
|
-
data: [],
|
|
98
|
+
data: { '0': [] },
|
|
97
99
|
style: ''
|
|
98
100
|
}
|
|
99
101
|
|
|
@@ -116,7 +118,7 @@ const handleFirstIndex = (data, seriesKey, preliminaryData) => {
|
|
|
116
118
|
if (suppressionData && suppressionData.style) {
|
|
117
119
|
// Modify first item and add to result
|
|
118
120
|
const modifiedItem = { ...firstIndexDataItem, [seriesKey]: 0 }
|
|
119
|
-
result.data.push(modifiedItem)
|
|
121
|
+
result.data[pairCount].push(modifiedItem)
|
|
120
122
|
result.style = suppressionData.style
|
|
121
123
|
|
|
122
124
|
// Find the next calculable item index
|
|
@@ -125,19 +127,20 @@ const handleFirstIndex = (data, seriesKey, preliminaryData) => {
|
|
|
125
127
|
nextIndex++
|
|
126
128
|
}
|
|
127
129
|
if (nextIndex < data.length) {
|
|
128
|
-
result.data.push(data[nextIndex])
|
|
130
|
+
result.data[pairCount].push(data[nextIndex])
|
|
129
131
|
}
|
|
130
132
|
} else {
|
|
131
133
|
// If no suppression, just add the first item
|
|
132
|
-
result.data.push(firstIndexDataItem)
|
|
134
|
+
result.data[pairCount].push(firstIndexDataItem)
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
return result
|
|
136
138
|
}
|
|
137
139
|
|
|
138
140
|
const handleLastIndex = (data, seriesKey, preliminaryData) => {
|
|
141
|
+
let pairCount = '0'
|
|
139
142
|
const result = {
|
|
140
|
-
data: [],
|
|
143
|
+
data: { '0': [] },
|
|
141
144
|
style: ''
|
|
142
145
|
}
|
|
143
146
|
let lastAddedIndex = -1 // Tracks the last index added to the result
|
|
@@ -152,7 +155,7 @@ const handleLastIndex = (data, seriesKey, preliminaryData) => {
|
|
|
152
155
|
) {
|
|
153
156
|
const lastIndex = data.length - 1
|
|
154
157
|
const modifiedItem = { ...data[lastIndex], [seriesKey]: 0 }
|
|
155
|
-
result.data.push(modifiedItem)
|
|
158
|
+
result.data[pairCount].push(modifiedItem)
|
|
156
159
|
|
|
157
160
|
// Find previous calculable item
|
|
158
161
|
let prevIndex = lastIndex - 1
|
|
@@ -160,7 +163,7 @@ const handleLastIndex = (data, seriesKey, preliminaryData) => {
|
|
|
160
163
|
prevIndex--
|
|
161
164
|
}
|
|
162
165
|
if (prevIndex >= 0 && lastAddedIndex !== prevIndex) {
|
|
163
|
-
result.data.push(data[prevIndex])
|
|
166
|
+
result.data[pairCount].push(data[prevIndex])
|
|
164
167
|
lastAddedIndex = prevIndex
|
|
165
168
|
}
|
|
166
169
|
result.style = pd.style
|
|
@@ -170,47 +173,48 @@ const handleLastIndex = (data, seriesKey, preliminaryData) => {
|
|
|
170
173
|
return result
|
|
171
174
|
}
|
|
172
175
|
|
|
173
|
-
function handleMiddleIndices(data, seriesKey,
|
|
174
|
-
|
|
175
|
-
|
|
176
|
+
function handleMiddleIndices(data, seriesKey, preliminaryData) {
|
|
177
|
+
// slice data to remove first and last object these no need for handleMiddleIndices
|
|
178
|
+
|
|
179
|
+
let result = {
|
|
180
|
+
data: {},
|
|
176
181
|
style: ''
|
|
177
182
|
}
|
|
183
|
+
// Variable to count the number of sibling pairs found
|
|
184
|
+
let pairCount = 1
|
|
185
|
+
|
|
186
|
+
// Loop through the data array to find each occurrence of the target value
|
|
187
|
+
data.forEach((item, index) => {
|
|
188
|
+
preliminaryData.forEach(pd => {
|
|
189
|
+
const targetValue = pd.value
|
|
190
|
+
if (item[seriesKey] === targetValue) {
|
|
191
|
+
let siblingBefore = null
|
|
192
|
+
let siblingAfter = null
|
|
193
|
+
|
|
194
|
+
// Find the nearest numeric sibling before the current index
|
|
195
|
+
for (let i = index - 1; i >= 0; i--) {
|
|
196
|
+
if (isCalculable(data[i][seriesKey])) {
|
|
197
|
+
siblingBefore = data[i]
|
|
198
|
+
break // Stop searching once a valid sibling is found
|
|
199
|
+
}
|
|
200
|
+
}
|
|
178
201
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const matchingIndices = data.reduce((indices, item, index) => {
|
|
187
|
-
if (item[seriesKey] === targetValue && isValidMiddleIndex(index) && (!pd.column || pd.column === seriesKey)) {
|
|
188
|
-
indices.push(index)
|
|
189
|
-
}
|
|
190
|
-
return indices
|
|
191
|
-
}, [])
|
|
192
|
-
|
|
193
|
-
// Process each valid index
|
|
194
|
-
matchingIndices.forEach(i => {
|
|
195
|
-
result.style = pd.style
|
|
196
|
-
// Add previous object if calculable
|
|
197
|
-
if (isCalculable(data[i - 1][seriesKey])) {
|
|
198
|
-
result.data.push(data[i - 1])
|
|
199
|
-
}
|
|
202
|
+
// Find the nearest numeric sibling after the current index
|
|
203
|
+
for (let j = index + 1; j < data.length; j++) {
|
|
204
|
+
if (isCalculable(data[j][seriesKey])) {
|
|
205
|
+
siblingAfter = data[j]
|
|
206
|
+
break // Stop searching once a valid sibling is found
|
|
207
|
+
}
|
|
208
|
+
}
|
|
200
209
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
result.data.push(data[i + 1 + nextIndex])
|
|
210
|
+
// Only add siblings to results if both siblings are found
|
|
211
|
+
if (siblingBefore && siblingAfter) {
|
|
212
|
+
result.style = pd.style
|
|
213
|
+
result.data[pairCount++] = [siblingBefore, siblingAfter]
|
|
214
|
+
}
|
|
207
215
|
}
|
|
208
216
|
})
|
|
209
217
|
})
|
|
210
|
-
|
|
211
|
-
// Deduplicate entries
|
|
212
|
-
result.data = _.uniqWith(result.data, (a, b) => a[dataKey] === b[dataKey] && a[seriesKey] === b[seriesKey])
|
|
213
|
-
|
|
214
218
|
return result
|
|
215
219
|
}
|
|
216
220
|
|
|
@@ -221,7 +225,9 @@ export const createDataSegments = (data, seriesKey, preliminaryData, dataKey) =>
|
|
|
221
225
|
// Process the last index if necessary
|
|
222
226
|
const lastSegment = handleLastIndex(data, seriesKey, preliminaryData)
|
|
223
227
|
// Process the middle segment
|
|
224
|
-
const middleSegments = handleMiddleIndices(data, seriesKey,
|
|
228
|
+
const middleSegments = handleMiddleIndices(data, seriesKey, preliminaryData)
|
|
229
|
+
|
|
225
230
|
// Combine all segments into a single array
|
|
226
|
-
return [firstSegment, middleSegments, lastSegment]
|
|
231
|
+
return [firstSegment, middleSegments, lastSegment]
|
|
232
|
+
// return [firstSegment, middleSegments, lastSegment].filter(segment => segment.data.length > 0 && segment.style !== '')
|
|
227
233
|
}
|