@cdc/chart 4.24.12 → 4.25.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.js +79611 -78971
- package/examples/feature/boxplot/boxplot.json +2 -157
- package/examples/feature/boxplot/testing.csv +23 -38
- package/examples/feature/tests-non-numerics/example-combo-bar-nonnumeric.json +394 -30
- package/examples/private/ehdi.json +29939 -0
- package/examples/private/not-loading.json +360 -0
- package/index.html +7 -14
- package/package.json +2 -2
- package/src/CdcChart.tsx +92 -1512
- package/src/CdcChartComponent.tsx +1105 -0
- package/src/_stories/Chart.Anchors.stories.tsx +1 -1
- package/src/_stories/Chart.CustomColors.stories.tsx +1 -1
- package/src/_stories/Chart.DynamicSeries.stories.tsx +1 -1
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +2 -2
- package/src/_stories/Chart.ScatterPlot.stories.tsx +19 -0
- package/src/_stories/Chart.tooltip.stories.tsx +1 -2
- package/src/_stories/ChartAnnotation.stories.tsx +1 -1
- package/src/_stories/ChartAxisLabels.stories.tsx +1 -1
- package/src/_stories/ChartAxisTitles.stories.tsx +1 -1
- package/src/_stories/ChartEditor.stories.tsx +1 -1
- package/src/_stories/ChartLine.Suppression.stories.tsx +1 -1
- package/src/_stories/ChartLine.Symbols.stories.tsx +18 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +1 -1
- package/src/_stories/_mock/line_chart_symbols.json +437 -0
- package/src/_stories/_mock/scatterplot-image-download.json +1244 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +3 -11
- package/src/components/Annotations/components/AnnotationDropdown.tsx +3 -3
- package/src/components/Axis/Categorical.Axis.tsx +3 -4
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +14 -5
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +10 -4
- package/src/components/BarChart/components/BarChart.Vertical.tsx +2 -2
- package/src/components/BoxPlot/BoxPlot.tsx +34 -32
- package/src/components/BoxPlot/helpers/index.ts +108 -18
- package/src/components/DeviationBar.jsx +2 -6
- package/src/components/EditorPanel/EditorPanel.tsx +62 -6
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +4 -0
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +44 -7
- package/src/components/ForestPlot/ForestPlot.tsx +176 -26
- package/src/components/Legend/Legend.Component.tsx +29 -38
- package/src/components/Legend/Legend.Suppression.tsx +3 -5
- package/src/components/Legend/Legend.tsx +2 -2
- package/src/components/Legend/LegendLine.Shape.tsx +51 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +29 -26
- package/src/components/Legend/helpers/getLegendClasses.ts +20 -38
- package/src/components/Legend/helpers/index.ts +14 -7
- package/src/components/Legend/tests/getLegendClasses.test.ts +3 -20
- package/src/components/LineChart/components/LineChart.Circle.tsx +90 -88
- package/src/components/LineChart/index.tsx +4 -0
- package/src/components/LinearChart.tsx +65 -31
- package/src/components/PairedBarChart.jsx +2 -9
- package/src/components/ZoomBrush.tsx +5 -7
- package/src/data/initial-state.js +6 -3
- package/src/helpers/getBoxPlotConfig.ts +68 -0
- package/src/helpers/getColorScale.ts +28 -0
- package/src/helpers/getComboChartConfig.ts +42 -0
- package/src/helpers/getExcludedData.ts +37 -0
- package/src/helpers/getTopAxis.ts +7 -0
- package/src/hooks/useBarChart.ts +28 -9
- package/src/hooks/{useHighlightedBars.js → useHighlightedBars.ts} +2 -1
- package/src/hooks/useIntersectionObserver.ts +37 -0
- package/src/hooks/useMinMax.ts +4 -0
- package/src/hooks/useReduceData.ts +1 -1
- package/src/hooks/useTooltip.tsx +9 -1
- package/src/index.jsx +1 -0
- package/src/scss/DataTable.scss +0 -5
- package/src/scss/main.scss +30 -115
- package/src/types/ChartConfig.ts +6 -3
- package/src/types/ChartContext.ts +1 -3
- package/src/helpers/getQuartiles.ts +0 -27
- package/src/hooks/useColorScale.ts +0 -50
- package/src/hooks/useIntersectionObserver.jsx +0 -29
- package/src/hooks/useTopAxis.js +0 -6
package/src/hooks/useBarChart.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React, { useContext, useEffect, useState } from 'react'
|
|
2
2
|
import ConfigContext from '../ConfigContext'
|
|
3
3
|
import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
|
|
4
|
+
import { appFontSize } from '@cdc/core/helpers/cove/fontSettings'
|
|
4
5
|
export const useBarChart = () => {
|
|
5
|
-
const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate, setSeriesHighlight, seriesHighlight } =
|
|
6
|
+
const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate, setSeriesHighlight, seriesHighlight } =
|
|
7
|
+
useContext(ConfigContext)
|
|
6
8
|
const { orientation } = config
|
|
7
9
|
const [hoveredBar, setHoveredBar] = useState(null)
|
|
8
10
|
|
|
@@ -17,12 +19,22 @@ export const useBarChart = () => {
|
|
|
17
19
|
const isRounded = config.barStyle === 'rounded'
|
|
18
20
|
const isStacked = config.visualizationSubType === 'stacked'
|
|
19
21
|
const tipRounding = config.tipRounding
|
|
20
|
-
const radius =
|
|
22
|
+
const radius =
|
|
23
|
+
config.roundingStyle === 'standard'
|
|
24
|
+
? '8px'
|
|
25
|
+
: config.roundingStyle === 'shallow'
|
|
26
|
+
? '5px'
|
|
27
|
+
: config.roundingStyle === 'finger'
|
|
28
|
+
? '15px'
|
|
29
|
+
: '0px'
|
|
21
30
|
const stackCount = config.runtime.seriesKeys.length
|
|
22
|
-
const fontSize = { small: 16, medium: 18, large: 20 }
|
|
23
31
|
const hasMultipleSeries = Object.keys(config.runtime.seriesLabels).length > 1
|
|
24
|
-
const isBarAndLegendIsolate =
|
|
25
|
-
|
|
32
|
+
const isBarAndLegendIsolate =
|
|
33
|
+
config.visualizationType === 'Bar' && config.legend.behavior === 'isolate' && config.legend.axisAlign
|
|
34
|
+
const barStackedSeriesKeys =
|
|
35
|
+
isBarAndLegendIsolate && seriesHighlight?.length
|
|
36
|
+
? seriesHighlight
|
|
37
|
+
: config.runtime.barSeriesKeys || config.runtime.seriesKeys
|
|
26
38
|
|
|
27
39
|
useEffect(() => {
|
|
28
40
|
if (orientation === 'horizontal' && !config.yAxis.labelPlacement) {
|
|
@@ -68,7 +80,9 @@ export const useBarChart = () => {
|
|
|
68
80
|
style = isHorizontal ? { borderRadius: `0 ${radius} ${radius} 0` } : { borderRadius: `${radius} ${radius} 0 0` }
|
|
69
81
|
}
|
|
70
82
|
if (!isStacked && index === -1) {
|
|
71
|
-
style = isHorizontal
|
|
83
|
+
style = isHorizontal
|
|
84
|
+
? { borderRadius: `${radius} 0 0 ${radius} ` }
|
|
85
|
+
: { borderRadius: ` 0 0 ${radius} ${radius}` }
|
|
72
86
|
}
|
|
73
87
|
if (tipRounding === 'full' && isStacked && index === 0 && stackCount > 1) {
|
|
74
88
|
style = isHorizontal ? { borderRadius: `${radius} 0 0 ${radius}` } : { borderRadius: `0 0 ${radius} ${radius}` }
|
|
@@ -126,7 +140,7 @@ export const useBarChart = () => {
|
|
|
126
140
|
barHeight = heights.stacked
|
|
127
141
|
}
|
|
128
142
|
|
|
129
|
-
const labelHeight = isLabelBelowBar ?
|
|
143
|
+
const labelHeight = isLabelBelowBar ? appFontSize * 1.2 : 0
|
|
130
144
|
let barSpace = Number(config.barSpace)
|
|
131
145
|
|
|
132
146
|
// calculate height of container based height, space and fontSize of labels
|
|
@@ -191,7 +205,13 @@ export const useBarChart = () => {
|
|
|
191
205
|
addColCommas: config.columns[colKeys].commas
|
|
192
206
|
}
|
|
193
207
|
|
|
194
|
-
const formattedValue = formatColNumber(
|
|
208
|
+
const formattedValue = formatColNumber(
|
|
209
|
+
closestVal[config.columns[colKeys].name],
|
|
210
|
+
'left',
|
|
211
|
+
true,
|
|
212
|
+
config,
|
|
213
|
+
formattingParams
|
|
214
|
+
)
|
|
195
215
|
if (config.columns[colKeys].tooltips) {
|
|
196
216
|
columnsWithTooltips.push([config.columns[colKeys].label, formattedValue])
|
|
197
217
|
}
|
|
@@ -225,7 +245,6 @@ export const useBarChart = () => {
|
|
|
225
245
|
radius,
|
|
226
246
|
stackCount,
|
|
227
247
|
barStackedSeriesKeys,
|
|
228
|
-
fontSize,
|
|
229
248
|
hasMultipleSeries,
|
|
230
249
|
applyRadius,
|
|
231
250
|
updateBars,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React, { useContext } from 'react'
|
|
2
2
|
import ConfigContext from '../ConfigContext'
|
|
3
|
+
import { ChartConfig } from '../types/ChartConfig'
|
|
3
4
|
|
|
4
|
-
export const useHighlightedBars = (config, updateConfig) => {
|
|
5
|
+
export const useHighlightedBars = (config: ChartConfig, updateConfig: (config) => void) => {
|
|
5
6
|
const { formatDate, parseDate } = useContext(ConfigContext)
|
|
6
7
|
|
|
7
8
|
let highlightedSeries = [] // only allow single series for highlights
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useEffect, useState, MutableRefObject } from 'react'
|
|
2
|
+
|
|
3
|
+
interface IntersectionObserverOptions {
|
|
4
|
+
threshold?: number | number[]
|
|
5
|
+
root?: Element | null
|
|
6
|
+
rootMargin?: string
|
|
7
|
+
freezeOnceVisible?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function useIntersectionObserver(
|
|
11
|
+
elementRef: MutableRefObject<Element | null>,
|
|
12
|
+
{ threshold = 0, root = null, rootMargin = '0%', freezeOnceVisible = false }: IntersectionObserverOptions
|
|
13
|
+
) {
|
|
14
|
+
const [entry, setEntry] = useState<IntersectionObserverEntry | undefined>()
|
|
15
|
+
|
|
16
|
+
const frozen = entry?.isIntersecting && freezeOnceVisible
|
|
17
|
+
|
|
18
|
+
const updateEntry = ([entry]: IntersectionObserverEntry[]) => {
|
|
19
|
+
setEntry(entry)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const node = elementRef?.current
|
|
24
|
+
const hasIOSupport = !!window.IntersectionObserver
|
|
25
|
+
|
|
26
|
+
if (!hasIOSupport || frozen || !node) return
|
|
27
|
+
|
|
28
|
+
const observerParams = { threshold, root, rootMargin }
|
|
29
|
+
const observer = new IntersectionObserver(updateEntry, observerParams)
|
|
30
|
+
|
|
31
|
+
observer.observe(node)
|
|
32
|
+
|
|
33
|
+
return () => observer.disconnect()
|
|
34
|
+
}, [elementRef, threshold, root, rootMargin, frozen])
|
|
35
|
+
|
|
36
|
+
return entry
|
|
37
|
+
}
|
package/src/hooks/useMinMax.ts
CHANGED
|
@@ -240,6 +240,10 @@ const useMinMax = ({ config, minValue, maxValue, existPositiveValue, data, isAll
|
|
|
240
240
|
min = 0
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
if (config.visualizationType === 'Scatter Plot') {
|
|
244
|
+
max = max * 1.1
|
|
245
|
+
}
|
|
246
|
+
|
|
243
247
|
return { min, max, leftMax, rightMax }
|
|
244
248
|
}
|
|
245
249
|
export default useMinMax
|
package/src/hooks/useTooltip.tsx
CHANGED
|
@@ -143,11 +143,19 @@ export const useTooltip = props => {
|
|
|
143
143
|
})
|
|
144
144
|
|
|
145
145
|
if (visualizationType === 'Pie') {
|
|
146
|
+
const roundTo = Number(config.dataFormat.roundTo) || 0
|
|
147
|
+
|
|
148
|
+
const degrees = ((arc.endAngle - arc.startAngle) * 180) / Math.PI
|
|
149
|
+
|
|
150
|
+
// Calculate the percentage of the full circle (360 degrees)
|
|
151
|
+
const percentageOfCircle = (degrees / 360) * 100
|
|
152
|
+
const roundedPercentage = percentageOfCircle.toFixed(roundTo)
|
|
153
|
+
|
|
146
154
|
tooltipItems.push(
|
|
147
155
|
// ignore
|
|
148
156
|
[config.xAxis.dataKey, pieChartData],
|
|
149
157
|
[config.runtime.yAxis.dataKey, formatNumber(arc?.data[config.runtime.yAxis.dataKey])],
|
|
150
|
-
['Percent', `${
|
|
158
|
+
['Percent', `${roundedPercentage + '%'}`]
|
|
151
159
|
)
|
|
152
160
|
}
|
|
153
161
|
|
package/src/index.jsx
CHANGED
package/src/scss/DataTable.scss
CHANGED
package/src/scss/main.scss
CHANGED
|
@@ -50,13 +50,6 @@
|
|
|
50
50
|
overflow-y: auto;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
.d-flex {
|
|
54
|
-
display: flex;
|
|
55
|
-
}
|
|
56
|
-
.flex-column-reverse {
|
|
57
|
-
flex-direction: column-reverse;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
53
|
.cdc-open-viz-module.type-dashboard {
|
|
61
54
|
.cdc-open-viz-module.type-chart.isEditor {
|
|
62
55
|
.cdc-chart-inner-container {
|
|
@@ -153,34 +146,39 @@
|
|
|
153
146
|
.legend-container {
|
|
154
147
|
background: #fff;
|
|
155
148
|
width: 100%;
|
|
156
|
-
padding: 15px;
|
|
157
149
|
vertical-align: top;
|
|
158
150
|
text-align: left;
|
|
159
|
-
border: 1px solid var(--
|
|
151
|
+
border: 1px solid var(--cool-gray-10);
|
|
152
|
+
border-radius: 6px;
|
|
160
153
|
position: relative;
|
|
161
154
|
|
|
155
|
+
h3 {
|
|
156
|
+
font-size: var(--legend-title-font-size);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
p {
|
|
160
|
+
font-size: var(--legend-description-font-size);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
tspan,
|
|
164
|
+
div {
|
|
165
|
+
font-size: var(--legend-item-font-size);
|
|
166
|
+
}
|
|
167
|
+
|
|
162
168
|
&.border-0 {
|
|
163
169
|
border: 1px solid transparent;
|
|
164
170
|
padding: 0;
|
|
165
171
|
}
|
|
166
172
|
|
|
167
173
|
&__inner {
|
|
174
|
+
display: flex;
|
|
175
|
+
flex-direction: column;
|
|
176
|
+
row-gap: var(--space-between-legend-item-rows);
|
|
177
|
+
column-gap: var(--space-between-legend-item-columns);
|
|
168
178
|
&.double-column,
|
|
169
179
|
&.single-row {
|
|
170
180
|
display: grid;
|
|
171
181
|
grid-template-columns: 1fr 1fr;
|
|
172
|
-
grid-column-gap: 1.5em;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
&.vertical-sorted {
|
|
176
|
-
display: block;
|
|
177
|
-
|
|
178
|
-
@include breakpoint(sm) {
|
|
179
|
-
column-count: 2;
|
|
180
|
-
column-width: 100%;
|
|
181
|
-
}
|
|
182
|
-
column-gap: 1.5em;
|
|
183
|
-
column-fill: balance;
|
|
184
182
|
}
|
|
185
183
|
|
|
186
184
|
&.single-row {
|
|
@@ -192,13 +190,16 @@
|
|
|
192
190
|
flex-basis: auto;
|
|
193
191
|
}
|
|
194
192
|
}
|
|
193
|
+
|
|
194
|
+
&.double-column.reverse-items div.legend-item:last-child {
|
|
195
|
+
margin-bottom: 0.2rem !important;
|
|
196
|
+
}
|
|
195
197
|
}
|
|
196
198
|
|
|
197
199
|
.legend-item {
|
|
198
200
|
text-align: left;
|
|
199
201
|
user-select: none;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
+
line-height: var(--legend-item-font-size);
|
|
202
203
|
.visx-legend-label {
|
|
203
204
|
word-wrap: break-word;
|
|
204
205
|
white-space: pre-wrap;
|
|
@@ -212,27 +213,9 @@
|
|
|
212
213
|
|
|
213
214
|
.legend-item > .legend-item {
|
|
214
215
|
display: inline-block;
|
|
215
|
-
margin-right: 0.5rem;
|
|
216
216
|
flex: 0 0 auto;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
h3 {
|
|
220
|
-
font-size: 1.3rem;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
h3,
|
|
224
|
-
p {
|
|
225
|
-
margin-bottom: 0.4em;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
& div.legend-item {
|
|
229
|
-
margin-bottom: 0.2em !important;
|
|
230
|
-
|
|
231
|
-
&:last-child {
|
|
232
|
-
margin: 0 !important;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
219
|
.legend-item {
|
|
237
220
|
cursor: pointer;
|
|
238
221
|
transition: 0.2s all;
|
|
@@ -241,6 +224,11 @@
|
|
|
241
224
|
opacity: 0.5;
|
|
242
225
|
transition: 0.2s all;
|
|
243
226
|
}
|
|
227
|
+
&.highlighted {
|
|
228
|
+
outline: 1px solid #005ea2;
|
|
229
|
+
outline-offset: 5px;
|
|
230
|
+
border-radius: 1px;
|
|
231
|
+
}
|
|
244
232
|
}
|
|
245
233
|
|
|
246
234
|
&__outer {
|
|
@@ -261,10 +249,6 @@
|
|
|
261
249
|
}
|
|
262
250
|
}
|
|
263
251
|
|
|
264
|
-
.legend-container__inner.flex-column-reverse div.legend-item:last-child {
|
|
265
|
-
margin-bottom: 0.2rem !important;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
252
|
.dynamic-legend-list {
|
|
269
253
|
// overide traditional legend item that uses !important
|
|
270
254
|
.legend-item {
|
|
@@ -297,7 +281,6 @@
|
|
|
297
281
|
align-items: center;
|
|
298
282
|
font-size: 1em;
|
|
299
283
|
vertical-align: middle;
|
|
300
|
-
margin-bottom: 0.5em;
|
|
301
284
|
|
|
302
285
|
& > span {
|
|
303
286
|
display: flex;
|
|
@@ -305,7 +288,7 @@
|
|
|
305
288
|
align-items: center;
|
|
306
289
|
white-space: nowrap;
|
|
307
290
|
font-size: 1em;
|
|
308
|
-
margin-right:
|
|
291
|
+
margin-right: 9px;
|
|
309
292
|
max-height: 1px;
|
|
310
293
|
}
|
|
311
294
|
|
|
@@ -483,32 +466,6 @@
|
|
|
483
466
|
}
|
|
484
467
|
}
|
|
485
468
|
|
|
486
|
-
@include breakpointClass(xs) {
|
|
487
|
-
&.font-small {
|
|
488
|
-
font-size: 0.8em;
|
|
489
|
-
|
|
490
|
-
.chart-container > svg {
|
|
491
|
-
font-size: 12px;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
&.font-medium {
|
|
496
|
-
font-size: 0.9em;
|
|
497
|
-
|
|
498
|
-
.chart-container > svg {
|
|
499
|
-
font-size: 14px;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
&.font-large {
|
|
504
|
-
font-size: 1em;
|
|
505
|
-
|
|
506
|
-
.chart-container > svg {
|
|
507
|
-
font-size: 16px;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
469
|
@include breakpointClass(sm) {
|
|
513
470
|
.chart-container {
|
|
514
471
|
.no-wrap {
|
|
@@ -547,48 +504,6 @@
|
|
|
547
504
|
}
|
|
548
505
|
}
|
|
549
506
|
}
|
|
550
|
-
|
|
551
|
-
&.font-small {
|
|
552
|
-
font-size: 0.9em;
|
|
553
|
-
|
|
554
|
-
.chart-container > svg {
|
|
555
|
-
font-size: 14px;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
&.font-large {
|
|
560
|
-
font-size: 1.1em;
|
|
561
|
-
|
|
562
|
-
.chart-container > svg {
|
|
563
|
-
font-size: 18px;
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
@include breakpointClass(lg) {
|
|
569
|
-
&.font-small {
|
|
570
|
-
font-size: 1em;
|
|
571
|
-
|
|
572
|
-
.chart-container > svg {
|
|
573
|
-
font-size: 16px;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
&.font-medium {
|
|
578
|
-
font-size: 1.1em;
|
|
579
|
-
|
|
580
|
-
.chart-container > svg {
|
|
581
|
-
font-size: 18px;
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
&.font-large {
|
|
586
|
-
font-size: 1.2em;
|
|
587
|
-
|
|
588
|
-
.chart-container > svg {
|
|
589
|
-
font-size: 20px;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
507
|
}
|
|
593
508
|
|
|
594
509
|
[tabindex]:focus-visible {
|
package/src/types/ChartConfig.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { ConfidenceInterval } from '@cdc/core/types/ConfidenceInterval'
|
|
|
14
14
|
import { Region } from '@cdc/core/types/Region'
|
|
15
15
|
import { VizFilter } from '@cdc/core/types/VizFilter'
|
|
16
16
|
import { type Annotation } from '@cdc/core/types/Annotation'
|
|
17
|
+
import { Version } from '@cdc/core/types/Version'
|
|
17
18
|
|
|
18
19
|
export type ViewportSize = 'xxs' | 'xs' | 'sm' | 'md' | 'lg'
|
|
19
20
|
export type ChartColumns = Record<string, Column>
|
|
@@ -79,10 +80,11 @@ type Exclusions = {
|
|
|
79
80
|
|
|
80
81
|
export type Legend = CoreLegend & {
|
|
81
82
|
seriesHighlight: string[]
|
|
83
|
+
|
|
82
84
|
hideSuppressionLink: boolean
|
|
83
85
|
style: 'circles' | 'boxes' | 'gradient' | 'lines'
|
|
84
86
|
subStyle: 'linear blocks' | 'smooth'
|
|
85
|
-
|
|
87
|
+
hasShape: boolean
|
|
86
88
|
tickRotation: string
|
|
87
89
|
hideBorder: {
|
|
88
90
|
side: boolean
|
|
@@ -98,6 +100,8 @@ type Visual = {
|
|
|
98
100
|
hideBackgroundColor?: boolean
|
|
99
101
|
verticalHoverLine?: boolean
|
|
100
102
|
horizontalHoverLine?: boolean
|
|
103
|
+
lineDatapointSymbol: 'none' | 'standard'
|
|
104
|
+
maximumShapeAmount: 7
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
export type AllChartsConfig = {
|
|
@@ -131,7 +135,6 @@ export type AllChartsConfig = {
|
|
|
131
135
|
exclusions: Exclusions
|
|
132
136
|
filters: VizFilter[]
|
|
133
137
|
filterBehavior: FilterBehavior
|
|
134
|
-
fontSize: 'small' | 'medium' | 'large'
|
|
135
138
|
footnotes: string
|
|
136
139
|
forestPlot: ForestPlotConfigSettings
|
|
137
140
|
formattedData: Object[] & { urlFiltered: boolean }
|
|
@@ -184,7 +187,7 @@ export type AllChartsConfig = {
|
|
|
184
187
|
twoColor: { palette: string }
|
|
185
188
|
type: 'chart' | 'dashboard'
|
|
186
189
|
uid: string | number
|
|
187
|
-
version:
|
|
190
|
+
version: Version
|
|
188
191
|
visual: Visual
|
|
189
192
|
visualizationType: VisualizationType
|
|
190
193
|
visualizationSubType: string
|
|
@@ -23,9 +23,7 @@ type SharedChartContext = {
|
|
|
23
23
|
handleChartAriaLabels: (config: any) => string
|
|
24
24
|
handleDragStateChange: (isDragging: any) => void
|
|
25
25
|
highlight?: Function
|
|
26
|
-
|
|
27
|
-
// whether or not the legend is appearing below the chart
|
|
28
|
-
isLegendBottom?: boolean
|
|
26
|
+
handleShowAll?: Function
|
|
29
27
|
// whether or not the chart is viewed within the editor screen
|
|
30
28
|
isEditor?: boolean
|
|
31
29
|
// whether or not the user is dragging an annotation
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Calculates the first quartile (q1) and third quartile (q3) from an array of integers or decimals.
|
|
3
|
-
*
|
|
4
|
-
* @param {Array} arr - The array of integers or decimals.
|
|
5
|
-
* @returns {Object} An object containing the q1 and q3 values.
|
|
6
|
-
*/
|
|
7
|
-
import _ from 'lodash'
|
|
8
|
-
|
|
9
|
-
export const getQuartiles = (values: number[]): { q1: number; q3: number } => {
|
|
10
|
-
const sortedData: number[] = _.sortBy(values)
|
|
11
|
-
|
|
12
|
-
const quantile = (sortedData: number[], q: number): number => {
|
|
13
|
-
const position: number = (sortedData.length - 1) * q
|
|
14
|
-
const base: number = Math.floor(position)
|
|
15
|
-
const rest: number = position - base
|
|
16
|
-
if (sortedData[base + 1] !== undefined) {
|
|
17
|
-
return sortedData[base] + rest * (sortedData[base + 1] - sortedData[base])
|
|
18
|
-
} else {
|
|
19
|
-
return sortedData[base]
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const q1: number = quantile(sortedData, 0.25)
|
|
24
|
-
const q3: number = quantile(sortedData, 0.75)
|
|
25
|
-
|
|
26
|
-
return { q1, q3 }
|
|
27
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { colorPalettesChart as colorPalettes, twoColorPalette } from '@cdc/core/data/colorPalettes'
|
|
2
|
-
import { scaleOrdinal } from '@visx/scale'
|
|
3
|
-
import { useContext } from 'react'
|
|
4
|
-
import ConfigContext from '../ConfigContext'
|
|
5
|
-
|
|
6
|
-
const useColorScale = () => {
|
|
7
|
-
const { config, data } = useContext(ConfigContext)
|
|
8
|
-
const { visualizationSubType, visualizationType, series, legend } = config
|
|
9
|
-
|
|
10
|
-
const generatePalette = colorsCount => {
|
|
11
|
-
if (!series?.length) return []
|
|
12
|
-
const isSpecialType = ['Paired Bar', 'Deviation Bar'].includes(visualizationType)
|
|
13
|
-
const chosenPalette = isSpecialType ? config.twoColor.palette : config.palette
|
|
14
|
-
const allPalettes = { ...colorPalettes, ...twoColorPalette }
|
|
15
|
-
let palette = config.customColors || allPalettes[chosenPalette]
|
|
16
|
-
while (colorsCount > palette.length) palette = palette.concat(palette)
|
|
17
|
-
return palette.slice(0, colorsCount)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
let colorScale = scaleOrdinal({
|
|
21
|
-
domain: config?.runtime?.seriesLabelsAll,
|
|
22
|
-
range: generatePalette(series.length)
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
if (visualizationType === 'Deviation Bar') {
|
|
26
|
-
const { targetLabel } = config.xAxis
|
|
27
|
-
colorScale = scaleOrdinal({
|
|
28
|
-
domain: [`Below ${targetLabel}`, `Above ${targetLabel}`],
|
|
29
|
-
range: generatePalette(2)
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
if (visualizationType === 'Bar' && visualizationSubType === 'regular' && series?.length === 1 && legend?.colorCode) {
|
|
33
|
-
const set = new Set(data?.map(d => d[legend.colorCode]))
|
|
34
|
-
colorScale = scaleOrdinal({
|
|
35
|
-
domain: [...set],
|
|
36
|
-
range: generatePalette([...set].length)
|
|
37
|
-
})
|
|
38
|
-
}
|
|
39
|
-
if (config.series.some(s => s.name)) {
|
|
40
|
-
const set = new Set(series.map(d => d.name || d.dataKey))
|
|
41
|
-
colorScale = colorScale = scaleOrdinal({
|
|
42
|
-
domain: [...set],
|
|
43
|
-
range: generatePalette(series.length)
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return { colorScale }
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export default useColorScale
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react'
|
|
2
|
-
|
|
3
|
-
export default function useIntersectionObserver(elementRef, { threshold = 0, root = null, rootMargin = '0%', freezeOnceVisible = false }) {
|
|
4
|
-
const [entry, setEntry] = useState()
|
|
5
|
-
|
|
6
|
-
const frozen = entry?.isIntersecting && freezeOnceVisible
|
|
7
|
-
|
|
8
|
-
const updateEntry = ([entry]) => {
|
|
9
|
-
setEntry(entry)
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
setTimeout(() => {
|
|
14
|
-
const node = elementRef?.current
|
|
15
|
-
const hasIOSupport = !!window.IntersectionObserver
|
|
16
|
-
|
|
17
|
-
if (!hasIOSupport || frozen || !node) return
|
|
18
|
-
|
|
19
|
-
const observerParams = { threshold, root, rootMargin }
|
|
20
|
-
const observer = new IntersectionObserver(updateEntry, observerParams)
|
|
21
|
-
|
|
22
|
-
observer.observe(node)
|
|
23
|
-
|
|
24
|
-
return () => observer.disconnect()
|
|
25
|
-
}, 500)
|
|
26
|
-
}, [elementRef, threshold, root, rootMargin, frozen])
|
|
27
|
-
|
|
28
|
-
return entry
|
|
29
|
-
}
|
package/src/hooks/useTopAxis.js
DELETED