@cdc/chart 4.24.10 → 4.24.12-2
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 +35019 -34301
- 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/ehdi.json +29939 -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 +196 -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
package/src/hooks/useTooltip.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
|
7
7
|
// Third-party library imports
|
|
8
8
|
import { localPoint } from '@visx/event'
|
|
9
9
|
import { bisector } from 'd3-array'
|
|
10
|
+
import _ from 'lodash'
|
|
10
11
|
|
|
11
12
|
export const useTooltip = props => {
|
|
12
13
|
const {
|
|
@@ -56,11 +57,9 @@ export const useTooltip = props => {
|
|
|
56
57
|
const getFormattedValue = (seriesKey, value, config, getAxisPosition) => {
|
|
57
58
|
// handle case where data is missing
|
|
58
59
|
const showMissingDataValue = config.general.showMissingDataLabel && (!value || value === 'null')
|
|
59
|
-
|
|
60
|
+
const formattedValue = seriesKey === config.xAxis.dataKey ? value : formatNumber(value, getAxisPosition(seriesKey))
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return formattedValue
|
|
62
|
+
return showMissingDataValue ? 'N/A' : formattedValue
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
const getTooltipInformation = (tooltipDataArray, eventSvgCoords) => {
|
|
@@ -101,37 +100,9 @@ export const useTooltip = props => {
|
|
|
101
100
|
|
|
102
101
|
const closestXScaleValue = getXValueFromCoordinate(x - Number(config.yAxis.size || 0))
|
|
103
102
|
|
|
104
|
-
const includedSeries =
|
|
105
|
-
visualizationType !== 'Pie'
|
|
106
|
-
? config.runtime.series.filter(series => series.tooltip === true).map(item => item.dataKey)
|
|
107
|
-
: config.runtime.series.map(item => item.dataKey)
|
|
108
|
-
includedSeries.push(config.xAxis.dataKey)
|
|
109
|
-
if (config.visualizationType === 'Forecasting') {
|
|
110
|
-
config.runtime.series.map(s => {
|
|
111
|
-
s.confidenceIntervals.map(c => {
|
|
112
|
-
if (c.showInTooltip) {
|
|
113
|
-
includedSeries.push(c.high)
|
|
114
|
-
includedSeries.push(c.low)
|
|
115
|
-
}
|
|
116
|
-
})
|
|
117
|
-
})
|
|
118
|
-
}
|
|
119
|
-
function getColumnNames(columns) {
|
|
120
|
-
let names = []
|
|
121
|
-
for (let key in columns) {
|
|
122
|
-
if (columns.hasOwnProperty(key)) {
|
|
123
|
-
names.push(columns[key].name)
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return names
|
|
127
|
-
}
|
|
128
|
-
includedSeries.push(...getColumnNames(config.columns))
|
|
129
|
-
includedSeries.push(...getColumnNames(config.columns))
|
|
130
|
-
|
|
131
|
-
const yScaleValues = getYScaleValues(closestXScaleValue, includedSeries)
|
|
132
103
|
const xScaleValues = data.filter(d => d[xAxis.dataKey] === getClosestYValue(y))
|
|
133
104
|
|
|
134
|
-
const resolvedScaleValues = orientation === 'vertical' ?
|
|
105
|
+
const resolvedScaleValues = orientation === 'vertical' ? getYScaleValues(closestXScaleValue) : xScaleValues
|
|
135
106
|
|
|
136
107
|
const getAxisPosition = seriesKey => {
|
|
137
108
|
const seriesObj = config.runtime.series.filter(s => s.dataKey === seriesKey)[0]
|
|
@@ -187,12 +158,12 @@ export const useTooltip = props => {
|
|
|
187
158
|
if (visualizationType !== 'Pie' && visualizationType !== 'Forest Plot' && !config.tooltips.singleSeries) {
|
|
188
159
|
tooltipItems.push(
|
|
189
160
|
...getIncludedTooltipSeries()
|
|
190
|
-
?.filter(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
)
|
|
161
|
+
?.filter(seriesKey => {
|
|
162
|
+
const series = config.runtime.series?.find(
|
|
163
|
+
s => s.dataKey === seriesKey && s?.tooltip && !s.dynamicCategory
|
|
164
|
+
)
|
|
165
|
+
return series || config.xAxis?.dataKey == seriesKey || visualizationType === 'Forecasting'
|
|
166
|
+
})
|
|
196
167
|
?.flatMap(seriesKey => {
|
|
197
168
|
const value = resolvedScaleValues[0]?.[seriesKey]
|
|
198
169
|
const formattedValue = getFormattedValue(seriesKey, value, config, getAxisPosition)
|
|
@@ -206,6 +177,17 @@ export const useTooltip = props => {
|
|
|
206
177
|
}
|
|
207
178
|
})
|
|
208
179
|
)
|
|
180
|
+
|
|
181
|
+
config.runtime.series?.forEach(series => {
|
|
182
|
+
if (series?.dynamicCategory) {
|
|
183
|
+
const seriesKey = series.dataKey
|
|
184
|
+
const resolvedScaleValue = resolvedScaleValues.find(v => v[series.dynamicCategory] === seriesKey)
|
|
185
|
+
if (!resolvedScaleValue) return
|
|
186
|
+
const value = resolvedScaleValue[series.originalDataKey]
|
|
187
|
+
const formattedValue = getFormattedValue(seriesKey, value, config, getAxisPosition)
|
|
188
|
+
tooltipItems.push([seriesKey, formattedValue, getAxisPosition(seriesKey)])
|
|
189
|
+
}
|
|
190
|
+
})
|
|
209
191
|
}
|
|
210
192
|
|
|
211
193
|
// handle tooltip for single hovered series
|
|
@@ -376,25 +358,48 @@ export const useTooltip = props => {
|
|
|
376
358
|
/**
|
|
377
359
|
* Provides an array of objects with the closest y series data items
|
|
378
360
|
* @param {String} closestXScaleValue
|
|
379
|
-
* @param {Array} includedSeries
|
|
380
361
|
* @returns an array of objects with the closest y series data items
|
|
381
362
|
*/
|
|
382
|
-
const getYScaleValues =
|
|
383
|
-
|
|
384
|
-
|
|
363
|
+
const getYScaleValues = closestXScaleValue => {
|
|
364
|
+
const runtimeSeries = config.runtime.series.filter(
|
|
365
|
+
series => visualizationType === 'Pie' || (series.tooltip === true && !series.dynamicCategory)
|
|
366
|
+
)
|
|
367
|
+
const includedSeries = runtimeSeries.map(item => item.dataKey)
|
|
368
|
+
includedSeries.push(config.xAxis.dataKey)
|
|
369
|
+
// get dynamic category series
|
|
370
|
+
const dynamicDataCategories = _.uniq(
|
|
371
|
+
config.runtime.series.flatMap(series => {
|
|
372
|
+
if (series.dynamicCategory) {
|
|
373
|
+
return [series.dynamicCategory, series.originalDataKey]
|
|
374
|
+
}
|
|
375
|
+
})
|
|
376
|
+
)
|
|
377
|
+
includedSeries.push(...dynamicDataCategories)
|
|
385
378
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
379
|
+
if (config.visualizationType === 'Forecasting') {
|
|
380
|
+
config.runtime.series.map(s => {
|
|
381
|
+
s.confidenceIntervals.map(c => {
|
|
382
|
+
if (c.showInTooltip) {
|
|
383
|
+
includedSeries.push(c.high)
|
|
384
|
+
includedSeries.push(c.low)
|
|
385
|
+
}
|
|
386
|
+
})
|
|
387
|
+
})
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const colNames = Object.values(config.columns).map(column => column.name)
|
|
391
|
+
// @ Murad why are we adding them twice?
|
|
392
|
+
includedSeries.push(...colNames, ...colNames)
|
|
393
|
+
|
|
394
|
+
try {
|
|
395
|
+
const dataToSearch = data.filter(d => d[xAxis.dataKey] === closestXScaleValue)
|
|
391
396
|
// Return an empty array if no matching data is found.
|
|
392
397
|
if (!dataToSearch || dataToSearch.length === 0) {
|
|
393
398
|
return []
|
|
394
399
|
}
|
|
395
400
|
|
|
396
401
|
const yScaleValues = dataToSearch.map(object => {
|
|
397
|
-
return
|
|
402
|
+
return _.pick(object, includedSeries)
|
|
398
403
|
})
|
|
399
404
|
return yScaleValues
|
|
400
405
|
} catch (error) {
|
|
@@ -548,9 +553,13 @@ export const useTooltip = props => {
|
|
|
548
553
|
(!pd.column || key === pd.column)
|
|
549
554
|
)) ||
|
|
550
555
|
{}
|
|
551
|
-
|
|
556
|
+
let newValue = label || value
|
|
552
557
|
const style = displayGray ? { color: '#8b8b8a' } : {}
|
|
553
558
|
|
|
559
|
+
if (index == 1 && config.dataFormat.onlyShowTopPrefixSuffix) {
|
|
560
|
+
newValue = `${config.dataFormat.prefix}${newValue}${config.dataFormat.suffix}`
|
|
561
|
+
}
|
|
562
|
+
|
|
554
563
|
return <li style={style} className='tooltip-body'>{`${getSeriesNameFromLabel(key)}: ${newValue}`}</li>
|
|
555
564
|
}
|
|
556
565
|
|
package/src/scss/DataTable.scss
CHANGED
package/src/scss/main.scss
CHANGED
|
@@ -120,23 +120,6 @@
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
.btn {
|
|
124
|
-
background: #005eaa;
|
|
125
|
-
color: #fff !important;
|
|
126
|
-
border: 0;
|
|
127
|
-
padding: 0.4em 0.8em;
|
|
128
|
-
display: block;
|
|
129
|
-
border-radius: 5px;
|
|
130
|
-
transition: 0.1s all;
|
|
131
|
-
cursor: pointer;
|
|
132
|
-
|
|
133
|
-
&[disabled] {
|
|
134
|
-
opacity: 0.5;
|
|
135
|
-
z-index: -1;
|
|
136
|
-
position: relative;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
123
|
.warning-icon {
|
|
141
124
|
position: relative;
|
|
142
125
|
top: 2px;
|
|
@@ -157,7 +140,7 @@
|
|
|
157
140
|
.subtext--responsive-ticks,
|
|
158
141
|
.section-subtext {
|
|
159
142
|
&--brush-active {
|
|
160
|
-
margin-top:
|
|
143
|
+
margin-top: 3em !important;
|
|
161
144
|
}
|
|
162
145
|
}
|
|
163
146
|
|
|
@@ -176,7 +159,7 @@
|
|
|
176
159
|
border: 1px solid var(--lightGray);
|
|
177
160
|
position: relative;
|
|
178
161
|
|
|
179
|
-
&.
|
|
162
|
+
&.border-0 {
|
|
180
163
|
border: 1px solid transparent;
|
|
181
164
|
padding: 0;
|
|
182
165
|
}
|
|
@@ -213,7 +196,6 @@
|
|
|
213
196
|
|
|
214
197
|
.legend-item {
|
|
215
198
|
text-align: left;
|
|
216
|
-
align-items: flex-start !important;
|
|
217
199
|
user-select: none;
|
|
218
200
|
white-space: nowrap;
|
|
219
201
|
|
|
@@ -279,6 +261,10 @@
|
|
|
279
261
|
}
|
|
280
262
|
}
|
|
281
263
|
|
|
264
|
+
.legend-container__inner.flex-column-reverse div.legend-item:last-child {
|
|
265
|
+
margin-bottom: 0.2rem !important;
|
|
266
|
+
}
|
|
267
|
+
|
|
282
268
|
.dynamic-legend-list {
|
|
283
269
|
// overide traditional legend item that uses !important
|
|
284
270
|
.legend-item {
|
package/src/types/ChartConfig.ts
CHANGED
|
@@ -8,6 +8,8 @@ import { Table } from '@cdc/core/types/Table'
|
|
|
8
8
|
import { BoxPlot } from '@cdc/core/types/BoxPlot'
|
|
9
9
|
import { General } from '@cdc/core/types/General'
|
|
10
10
|
import { type Link } from './../components/Sankey/types'
|
|
11
|
+
import { type DataDescription } from '@cdc/core/types/DataDescription'
|
|
12
|
+
import { type Legend as CoreLegend } from '@cdc/core/types/Legend'
|
|
11
13
|
import { ConfidenceInterval } from '@cdc/core/types/ConfidenceInterval'
|
|
12
14
|
import { Region } from '@cdc/core/types/Region'
|
|
13
15
|
import { VizFilter } from '@cdc/core/types/VizFilter'
|
|
@@ -75,27 +77,12 @@ type Exclusions = {
|
|
|
75
77
|
dateEnd: string
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
export type Legend = {
|
|
80
|
+
export type Legend = CoreLegend & {
|
|
79
81
|
seriesHighlight: string[]
|
|
80
|
-
additionalCategories: string[]
|
|
81
|
-
// general legend onClick behavior
|
|
82
|
-
behavior: 'highlight' | 'isolate' | string
|
|
83
|
-
axisAlign: boolean
|
|
84
|
-
colorCode: string
|
|
85
|
-
description: string
|
|
86
|
-
// show or hide the legend
|
|
87
|
-
hide: boolean
|
|
88
|
-
highlightOnHover: boolean
|
|
89
|
-
label: string
|
|
90
|
-
position: 'left' | 'bottom' | 'top' | 'right'
|
|
91
|
-
reverseLabelOrder: boolean
|
|
92
|
-
singleRow: boolean
|
|
93
|
-
type: string
|
|
94
|
-
verticalSorted: boolean
|
|
95
82
|
hideSuppressionLink: boolean
|
|
96
83
|
style: 'circles' | 'boxes' | 'gradient' | 'lines'
|
|
97
84
|
subStyle: 'linear blocks' | 'smooth'
|
|
98
|
-
|
|
85
|
+
|
|
99
86
|
tickRotation: string
|
|
100
87
|
hideBorder: {
|
|
101
88
|
side: boolean
|
|
@@ -136,7 +123,7 @@ export type AllChartsConfig = {
|
|
|
136
123
|
data: Object[]
|
|
137
124
|
dataUrl: string
|
|
138
125
|
dataCutoff: number
|
|
139
|
-
dataDescription:
|
|
126
|
+
dataDescription: Partial<DataDescription>
|
|
140
127
|
dataFormat: DataFormat
|
|
141
128
|
dataKey: string
|
|
142
129
|
description: string
|
|
@@ -166,7 +153,7 @@ export type AllChartsConfig = {
|
|
|
166
153
|
lollipopColorStyle: 'regular' | 'two-tone'
|
|
167
154
|
lollipopShape: string
|
|
168
155
|
lollipopSize: 'small' | 'medium' | 'large'
|
|
169
|
-
newViz:
|
|
156
|
+
newViz: boolean
|
|
170
157
|
orientation: ChartOrientation
|
|
171
158
|
palette: string
|
|
172
159
|
pieType?: string
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type ChartConfig } from './ChartConfig'
|
|
2
2
|
import { PickD3Scale } from '@visx/scale'
|
|
3
|
-
import { type SharedFilter } from '@cdc/dashboard/src/types/SharedFilter'
|
|
4
3
|
import { type Annotation } from '@cdc/core/types/Annotation'
|
|
5
4
|
import { DimensionsType } from '@cdc/core/types/Dimensions'
|
|
6
5
|
import { type DashboardConfig } from '@cdc/dashboard/src/types/DashboardConfig'
|
|
@@ -70,7 +69,11 @@ export type ChartContext =
|
|
|
70
69
|
formatTooltipsDate: Function
|
|
71
70
|
formatNumber?: Function
|
|
72
71
|
handleLineType?: Function
|
|
72
|
+
// 508 compliance: tabbing handler for charts
|
|
73
|
+
handleChartTabbing?: (chartConfig: ChartConfig, legendId: string) => string
|
|
73
74
|
isNumber?: boolean
|
|
75
|
+
// 508 compliance: tabbing id for legends
|
|
76
|
+
legendId: string
|
|
74
77
|
// url param added to allow various console logs and chart helpers
|
|
75
78
|
isDebug?: boolean
|
|
76
79
|
parseDate?: Function
|
package/src/types/ForestPlot.ts
CHANGED
|
@@ -50,4 +50,12 @@ export type ForestPlotConfigSettings = {
|
|
|
50
50
|
// labels under chart
|
|
51
51
|
leftLabel: string
|
|
52
52
|
rightLabel: string
|
|
53
|
+
/** rightWidthOffsetMobile */
|
|
54
|
+
rightWidthOffsetMobile: number
|
|
55
|
+
/** leftWidthOffsetMobile */
|
|
56
|
+
leftWidthOffsetMobile: number
|
|
57
|
+
regression: {
|
|
58
|
+
showDiamond: boolean
|
|
59
|
+
description: boolean
|
|
60
|
+
}
|
|
53
61
|
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import React, { useContext, useEffect } from 'react'
|
|
2
|
-
import { BoxPlot } from '@visx/stats'
|
|
3
|
-
import { Group } from '@visx/group'
|
|
4
|
-
import ConfigContext from '../../ConfigContext'
|
|
5
|
-
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
6
|
-
import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
|
|
7
|
-
|
|
8
|
-
const CoveBoxPlot = ({ xScale, yScale }) => {
|
|
9
|
-
const { config, setConfig } = useContext(ConfigContext)
|
|
10
|
-
const { boxplot } = config
|
|
11
|
-
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
if (config.legend.hide === false) {
|
|
14
|
-
setConfig({
|
|
15
|
-
...config,
|
|
16
|
-
legend: {
|
|
17
|
-
...config.legend,
|
|
18
|
-
hide: true
|
|
19
|
-
}
|
|
20
|
-
})
|
|
21
|
-
}
|
|
22
|
-
}, []) // eslint-disable-line
|
|
23
|
-
|
|
24
|
-
// tooltips
|
|
25
|
-
const tooltip_id = `cdc-open-viz-tooltip-${config.runtime.uniqueId}`
|
|
26
|
-
const handleTooltip = d => {
|
|
27
|
-
return `
|
|
28
|
-
<strong>${d.columnCategory}</strong></br>
|
|
29
|
-
${boxplot.labels.q1}: ${d.columnFirstQuartile}<br/>
|
|
30
|
-
${boxplot.labels.q3}: ${d.columnThirdQuartile}<br/>
|
|
31
|
-
${boxplot.labels.iqr}: ${d.columnIqr}<br/>
|
|
32
|
-
${boxplot.labels.median}: ${d.columnMedian}
|
|
33
|
-
`
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// accessors & constants
|
|
37
|
-
const max = d => Number(d.columnMax)
|
|
38
|
-
const min = d => Number(d.columnMin)
|
|
39
|
-
const median = d => Number(d.columnMedian)
|
|
40
|
-
const thirdQuartile = d => Number(d.columnThirdQuartile)
|
|
41
|
-
const firstQuartile = d => Number(d.columnFirstQuartile)
|
|
42
|
-
const fillOpacity = 0.5
|
|
43
|
-
const boxWidth = xScale.bandwidth()
|
|
44
|
-
const constrainedWidth = Math.min(40, boxWidth)
|
|
45
|
-
const color_0 = colorPalettesChart[config?.palette][0] ? colorPalettesChart[config?.palette][0] : '#000'
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<ErrorBoundary component='BoxPlot'>
|
|
49
|
-
<Group className='boxplot' key={`boxplot-group`}>
|
|
50
|
-
{boxplot.plots.map((d, i) => {
|
|
51
|
-
const offset = boxWidth - constrainedWidth
|
|
52
|
-
const radius = 4
|
|
53
|
-
return (
|
|
54
|
-
<Group key={`boxplotplot-${i}`}>
|
|
55
|
-
{boxplot.plotNonOutlierValues &&
|
|
56
|
-
d.nonOutlierValues.map((value, index) => {
|
|
57
|
-
return <circle cx={xScale(d.columnCategory) + Number(config.yAxis.size) + boxWidth / 2} cy={yScale(value)} r={radius} fill={'#ccc'} style={{ opacity: 1, fillOpacity: 1, stroke: 'black' }} key={`boxplot-${i}--circle-${index}`} />
|
|
58
|
-
})}
|
|
59
|
-
<BoxPlot
|
|
60
|
-
data-left={xScale(d.columnCategory) + config.yAxis.size + offset / 2 + 0.5}
|
|
61
|
-
key={`box-plot-${i}`}
|
|
62
|
-
min={min(d)}
|
|
63
|
-
max={max(d)}
|
|
64
|
-
left={Number(xScale(d.columnCategory)) + Number(config.yAxis.size) + offset / 2 + 0.5}
|
|
65
|
-
firstQuartile={firstQuartile(d)}
|
|
66
|
-
thirdQuartile={thirdQuartile(d)}
|
|
67
|
-
median={median(d)}
|
|
68
|
-
boxWidth={constrainedWidth}
|
|
69
|
-
fill={color_0}
|
|
70
|
-
fillOpacity={fillOpacity}
|
|
71
|
-
stroke='black'
|
|
72
|
-
valueScale={yScale}
|
|
73
|
-
outliers={boxplot.plotOutlierValues ? d.columnOutliers : []}
|
|
74
|
-
outlierProps={{
|
|
75
|
-
style: {
|
|
76
|
-
fill: `${color_0}`,
|
|
77
|
-
opacity: 1
|
|
78
|
-
}
|
|
79
|
-
}}
|
|
80
|
-
medianProps={{
|
|
81
|
-
style: {
|
|
82
|
-
stroke: 'black'
|
|
83
|
-
}
|
|
84
|
-
}}
|
|
85
|
-
boxProps={{
|
|
86
|
-
style: {
|
|
87
|
-
stroke: 'black',
|
|
88
|
-
strokeWidth: boxplot.borders === 'true' ? 1 : 0
|
|
89
|
-
}
|
|
90
|
-
}}
|
|
91
|
-
maxProps={{
|
|
92
|
-
style: {
|
|
93
|
-
stroke: 'black'
|
|
94
|
-
}
|
|
95
|
-
}}
|
|
96
|
-
container
|
|
97
|
-
containerProps={{
|
|
98
|
-
'data-tooltip-html': handleTooltip(d),
|
|
99
|
-
'data-tooltip-id': tooltip_id,
|
|
100
|
-
tabIndex: -1
|
|
101
|
-
}}
|
|
102
|
-
/>
|
|
103
|
-
</Group>
|
|
104
|
-
)
|
|
105
|
-
})}
|
|
106
|
-
</Group>
|
|
107
|
-
</ErrorBoundary>
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export default CoveBoxPlot
|
|
@@ -1,72 +0,0 @@
|
|
|
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
|
-
if (config.legend.hideBorder.topBottom && ['top'].includes(config.legend.position)) {
|
|
64
|
-
containerClasses.push('p-0')
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
containerClasses,
|
|
69
|
-
innerClasses
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
export default useLegendClasses
|