@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
|
@@ -4,12 +4,14 @@ import React, { forwardRef, useContext, useEffect, useMemo, useRef, useState } f
|
|
|
4
4
|
import { AxisLeft, AxisBottom, AxisRight, AxisTop } from '@visx/axis'
|
|
5
5
|
import { Group } from '@visx/group'
|
|
6
6
|
import { Line, Bar } from '@visx/shape'
|
|
7
|
-
import { Text } from '@visx/text'
|
|
8
7
|
import { Tooltip as ReactTooltip } from 'react-tooltip'
|
|
8
|
+
import { Text } from '@visx/text'
|
|
9
9
|
import { useTooltip, TooltipWithBounds } from '@visx/tooltip'
|
|
10
|
+
import _ from 'lodash'
|
|
11
|
+
|
|
12
|
+
// CDC Components
|
|
10
13
|
import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
11
14
|
import BrushChart from './BrushChart'
|
|
12
|
-
// CDC Components
|
|
13
15
|
import { AreaChart, AreaChartStacked } from './AreaChart'
|
|
14
16
|
import BarChart from './BarChart'
|
|
15
17
|
import ConfigContext from '../ConfigContext'
|
|
@@ -28,7 +30,7 @@ import CategoricalYAxis from './Axis/Categorical.Axis'
|
|
|
28
30
|
// Helpers
|
|
29
31
|
import { isLegendWrapViewport, isMobileHeightViewport } from '@cdc/core/helpers/viewports'
|
|
30
32
|
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
31
|
-
import { calcInitialHeight } from '../helpers/sizeHelpers'
|
|
33
|
+
import { calcInitialHeight, handleAutoPaddingRight } from '../helpers/sizeHelpers'
|
|
32
34
|
|
|
33
35
|
// Hooks
|
|
34
36
|
import useMinMax from '../hooks/useMinMax'
|
|
@@ -41,7 +43,6 @@ import { useEditorPermissions } from './EditorPanel/useEditorPermissions'
|
|
|
41
43
|
import Annotation from './Annotations'
|
|
42
44
|
import { BlurStrokeText } from '@cdc/core/components/BlurStrokeText'
|
|
43
45
|
import { countNumOfTicks } from '../helpers/countNumOfTicks'
|
|
44
|
-
import _ from 'lodash'
|
|
45
46
|
|
|
46
47
|
type LinearChartProps = {
|
|
47
48
|
parentWidth: number
|
|
@@ -119,6 +120,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
119
120
|
const xAxisLabelRefs = useRef([])
|
|
120
121
|
const xAxisTitleRef = useRef(null)
|
|
121
122
|
const lastMaxValue = useRef(maxValue)
|
|
123
|
+
const gridLineRefs = useRef([])
|
|
122
124
|
|
|
123
125
|
const dataRef = useIntersectionObserver(triggerRef, {
|
|
124
126
|
freezeOnceVisible: false
|
|
@@ -224,7 +226,10 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
224
226
|
leftMax,
|
|
225
227
|
rightMax,
|
|
226
228
|
dimensions,
|
|
227
|
-
xMax:
|
|
229
|
+
xMax:
|
|
230
|
+
parentWidth -
|
|
231
|
+
Number(config.orientation === 'horizontal' ? config.xAxis.size : config.yAxis.size) -
|
|
232
|
+
(hasRightAxis ? config.yAxis.rightAxisSize : 0)
|
|
228
233
|
})
|
|
229
234
|
|
|
230
235
|
const [yTickCount, xTickCount] = ['yAxis', 'xAxis'].map(axis =>
|
|
@@ -241,10 +246,10 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
241
246
|
handleTooltipClick,
|
|
242
247
|
handleTooltipMouseOff,
|
|
243
248
|
TooltipListItem,
|
|
244
|
-
getXValueFromCoordinate
|
|
245
249
|
} = useCoveTooltip({
|
|
246
250
|
xScale,
|
|
247
251
|
yScale,
|
|
252
|
+
seriesScale,
|
|
248
253
|
showTooltip,
|
|
249
254
|
hideTooltip
|
|
250
255
|
})
|
|
@@ -254,7 +259,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
254
259
|
data.length && isDateTime
|
|
255
260
|
? [0, data.length - 1].map(i => parseDate(data[i][dataKey])).reduce((a, b) => Math.abs(a - b)) / MONTH_AS_MS
|
|
256
261
|
: 0
|
|
257
|
-
const useDateSpanMonths = isDateTime && dateSpanMonths > xTickCount
|
|
262
|
+
const useDateSpanMonths = isDateTime && dateSpanMonths > xTickCount && !config.runtime.xAxis.manual
|
|
258
263
|
|
|
259
264
|
// GETTERS & FUNCTIONS
|
|
260
265
|
const handleLeftTickFormatting = (tick, index, ticks) => {
|
|
@@ -329,6 +334,21 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
329
334
|
}
|
|
330
335
|
|
|
331
336
|
// EFFECTS
|
|
337
|
+
// Adjust padding on the right side of the chart to accommodate for overflow
|
|
338
|
+
useEffect(() => {
|
|
339
|
+
if (!parentRef.current || !parentWidth || !gridLineRefs.current.length) return
|
|
340
|
+
|
|
341
|
+
const [updatePadding, paddingToAdd] = handleAutoPaddingRight(parentRef, xAxisLabelRefs, parentWidth)
|
|
342
|
+
|
|
343
|
+
if (!updatePadding) return
|
|
344
|
+
|
|
345
|
+
parentRef.current.style.paddingRight = `${paddingToAdd}px`
|
|
346
|
+
// subtract padding from grid line's x1 value
|
|
347
|
+
gridLineRefs.current.forEach(gridLine => {
|
|
348
|
+
if (!gridLine) return
|
|
349
|
+
gridLine.setAttribute('x1', xMax - paddingToAdd)
|
|
350
|
+
})
|
|
351
|
+
}, [parentWidth, parentHeight, data])
|
|
332
352
|
|
|
333
353
|
// Make sure the chart is visible if in the editor
|
|
334
354
|
/* eslint-disable react-hooks/exhaustive-deps */
|
|
@@ -421,19 +441,22 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
421
441
|
}, [maxValue])
|
|
422
442
|
|
|
423
443
|
useEffect(() => {
|
|
424
|
-
if (
|
|
425
|
-
|
|
444
|
+
if (!yScale?.ticks) return
|
|
445
|
+
const ticks = yScale.ticks(handleNumTicks)
|
|
446
|
+
if (orientation === 'horizontal' || !labelsOverflow || config.yAxis?.max || ticks.length === 0) {
|
|
447
|
+
setYAxisAutoPadding(0)
|
|
448
|
+
return
|
|
449
|
+
}
|
|
426
450
|
|
|
427
451
|
// minimum percentage of the max value that the distance should be from the top grid line
|
|
428
452
|
const MINIMUM_DISTANCE_PERCENTAGE = 0.025
|
|
429
453
|
|
|
430
|
-
const topGridLine = Math.max(...
|
|
454
|
+
const topGridLine = Math.max(...ticks)
|
|
431
455
|
const needsPaddingThreshold = topGridLine - maxValue * MINIMUM_DISTANCE_PERCENTAGE
|
|
432
456
|
const maxValueIsGreaterThanThreshold = maxValue > needsPaddingThreshold
|
|
433
457
|
|
|
434
458
|
if (!maxValueIsGreaterThanThreshold) return
|
|
435
459
|
|
|
436
|
-
const ticks = yScale.ticks(handleNumTicks)
|
|
437
460
|
const tickGap = ticks.length === 1 ? ticks[0] : ticks[1] - ticks[0]
|
|
438
461
|
const nextTick = Math.max(...yScale.ticks(handleNumTicks)) + tickGap
|
|
439
462
|
const divideBy = minValue < 0 ? maxValue / 2 : maxValue
|
|
@@ -592,7 +615,6 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
592
615
|
</>
|
|
593
616
|
)
|
|
594
617
|
}
|
|
595
|
-
|
|
596
618
|
return isNaN(width) ? (
|
|
597
619
|
<React.Fragment></React.Fragment>
|
|
598
620
|
) : (
|
|
@@ -605,7 +627,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
605
627
|
<svg
|
|
606
628
|
ref={svgRef}
|
|
607
629
|
onMouseMove={onMouseMove}
|
|
608
|
-
width={parentWidth}
|
|
630
|
+
width={parentWidth + config.yAxis.rightAxisSize}
|
|
609
631
|
height={isNoDataAvailable ? 1 : parentHeight}
|
|
610
632
|
className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${
|
|
611
633
|
debugSvg && 'debug'
|
|
@@ -638,6 +660,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
638
660
|
<Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
|
|
639
661
|
{runtime.yAxis.gridLines && !hideFirstGridLine ? (
|
|
640
662
|
<Line
|
|
663
|
+
innerRef={el => (gridLineRefs.current[i] = el)}
|
|
641
664
|
key={`${tick.value}--hide-hideGridLines`}
|
|
642
665
|
display={(isLogarithmicAxis && showTicks).toString()}
|
|
643
666
|
from={{ x: tick.from.x + xMax, y: tick.from.y }}
|
|
@@ -697,22 +720,6 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
697
720
|
yScale={yScale}
|
|
698
721
|
/>
|
|
699
722
|
)}
|
|
700
|
-
{((visualizationType === 'Area Chart' && config.visualizationSubType === 'regular') ||
|
|
701
|
-
visualizationType === 'Combo') && (
|
|
702
|
-
<AreaChart
|
|
703
|
-
xScale={xScale}
|
|
704
|
-
yScale={yScale}
|
|
705
|
-
yMax={yMax}
|
|
706
|
-
xMax={xMax}
|
|
707
|
-
chartRef={svgRef}
|
|
708
|
-
width={xMax}
|
|
709
|
-
height={yMax}
|
|
710
|
-
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
711
|
-
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
712
|
-
tooltipData={tooltipData}
|
|
713
|
-
showTooltip={showTooltip}
|
|
714
|
-
/>
|
|
715
|
-
)}
|
|
716
723
|
{((visualizationType === 'Area Chart' && config.visualizationSubType === 'stacked') ||
|
|
717
724
|
visualizationType === 'Combo') && (
|
|
718
725
|
<AreaChartStacked
|
|
@@ -748,9 +755,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
748
755
|
chartRef={svgRef}
|
|
749
756
|
/>
|
|
750
757
|
)}
|
|
751
|
-
{(
|
|
752
|
-
visualizationType === 'Combo' ||
|
|
753
|
-
visualizationType === 'Bump Chart') && (
|
|
758
|
+
{(visualizationType === 'Combo' || visualizationType === 'Bump Chart') && (
|
|
754
759
|
<LineChart
|
|
755
760
|
xScale={xScale}
|
|
756
761
|
yScale={yScale}
|
|
@@ -764,7 +769,6 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
764
769
|
handleTooltipClick={handleTooltipClick}
|
|
765
770
|
tooltipData={tooltipData}
|
|
766
771
|
showTooltip={showTooltip}
|
|
767
|
-
chartRef={svgRef}
|
|
768
772
|
/>
|
|
769
773
|
)}
|
|
770
774
|
{(visualizationType === 'Forecasting' || visualizationType === 'Combo') && (
|
|
@@ -778,7 +782,6 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
778
782
|
height={yMax}
|
|
779
783
|
xScaleNoPadding={xScaleNoPadding}
|
|
780
784
|
chartRef={svgRef}
|
|
781
|
-
getXValueFromCoordinate={getXValueFromCoordinate}
|
|
782
785
|
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
783
786
|
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
784
787
|
isBrush={false}
|
|
@@ -822,6 +825,9 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
822
825
|
xMax={xMax}
|
|
823
826
|
yMax={yMax}
|
|
824
827
|
seriesStyle={config.runtime.series}
|
|
828
|
+
tooltipData={tooltipData}
|
|
829
|
+
handleTooltipMouseOver={handleTooltipMouseOver}
|
|
830
|
+
handleTooltipMouseOff={handleTooltipMouseOff}
|
|
825
831
|
/>
|
|
826
832
|
</>
|
|
827
833
|
)}
|
|
@@ -906,79 +912,45 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
906
912
|
width={width}
|
|
907
913
|
/>
|
|
908
914
|
)}
|
|
909
|
-
{
|
|
910
|
-
<
|
|
915
|
+
{isNoDataAvailable && (
|
|
916
|
+
<Text
|
|
917
|
+
x={Number(config.yAxis.size) + Number(xMax / 2)}
|
|
918
|
+
y={initialHeight / 2 - (config.xAxis.padding || 0) / 2}
|
|
919
|
+
textAnchor='middle'
|
|
920
|
+
>
|
|
921
|
+
{config.chartMessage.noData}
|
|
922
|
+
</Text>
|
|
923
|
+
)}
|
|
924
|
+
{config.visual.horizontalHoverLine && tooltipData && (
|
|
925
|
+
<Group
|
|
926
|
+
key={`tooltipLine-horizontal${point.y}${point.x}`}
|
|
927
|
+
className='horizontal-tooltip-line'
|
|
928
|
+
left={config.yAxis.size ? config.yAxis.size : 0}
|
|
929
|
+
>
|
|
911
930
|
<Line
|
|
912
|
-
from={{ x:
|
|
913
|
-
to={{ x:
|
|
931
|
+
from={{ x: 0, y: point.y }}
|
|
932
|
+
to={{ x: xMax, y: point.y }}
|
|
914
933
|
stroke={'black'}
|
|
915
934
|
strokeWidth={1}
|
|
916
935
|
pointerEvents='none'
|
|
917
936
|
strokeDasharray='5,5'
|
|
918
|
-
className='
|
|
937
|
+
className='horizontal-tooltip-line'
|
|
919
938
|
/>
|
|
920
939
|
</Group>
|
|
921
940
|
)}
|
|
922
|
-
{
|
|
923
|
-
<Group
|
|
924
|
-
key='tooltipLine-horizontal'
|
|
925
|
-
className='horizontal-tooltip-line'
|
|
926
|
-
left={config.yAxis.size ? config.yAxis.size : 0}
|
|
927
|
-
>
|
|
941
|
+
{config.visual.verticalHoverLine && tooltipData && (
|
|
942
|
+
<Group key={`tooltipLine-vertical${point.y}${point.x}`} className='vertical-tooltip-line'>
|
|
928
943
|
<Line
|
|
929
|
-
from={{ x:
|
|
930
|
-
to={{ x:
|
|
944
|
+
from={{ x: point.x, y: 0 }}
|
|
945
|
+
to={{ x: point.x, y: yMax }}
|
|
931
946
|
stroke={'black'}
|
|
932
947
|
strokeWidth={1}
|
|
933
948
|
pointerEvents='none'
|
|
934
949
|
strokeDasharray='5,5'
|
|
935
|
-
className='
|
|
950
|
+
className='vertical-tooltip-line'
|
|
936
951
|
/>
|
|
937
952
|
</Group>
|
|
938
953
|
)}
|
|
939
|
-
{isNoDataAvailable && (
|
|
940
|
-
<Text
|
|
941
|
-
x={Number(config.yAxis.size) + Number(xMax / 2)}
|
|
942
|
-
y={initialHeight / 2 - (config.xAxis.padding || 0) / 2}
|
|
943
|
-
textAnchor='middle'
|
|
944
|
-
>
|
|
945
|
-
{config.chartMessage.noData}
|
|
946
|
-
</Text>
|
|
947
|
-
)}
|
|
948
|
-
{(config.visualizationType === 'Bar' || convertLineToBarGraph) &&
|
|
949
|
-
config.tooltips.singleSeries &&
|
|
950
|
-
config.visual.horizontalHoverLine && (
|
|
951
|
-
<Group
|
|
952
|
-
key='tooltipLine-horizontal'
|
|
953
|
-
className='horizontal-tooltip-line'
|
|
954
|
-
left={config.yAxis.size ? config.yAxis.size : 0}
|
|
955
|
-
>
|
|
956
|
-
<Line
|
|
957
|
-
from={{ x: 0, y: point.y }}
|
|
958
|
-
to={{ x: xMax, y: point.y }}
|
|
959
|
-
stroke={'black'}
|
|
960
|
-
strokeWidth={1}
|
|
961
|
-
pointerEvents='none'
|
|
962
|
-
strokeDasharray='5,5'
|
|
963
|
-
className='horizontal-tooltip-line'
|
|
964
|
-
/>
|
|
965
|
-
</Group>
|
|
966
|
-
)}
|
|
967
|
-
{(config.visualizationType === 'Bar' || convertLineToBarGraph) &&
|
|
968
|
-
config.tooltips.singleSeries &&
|
|
969
|
-
config.visual.verticalHoverLine && (
|
|
970
|
-
<Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
|
|
971
|
-
<Line
|
|
972
|
-
from={{ x: point.x, y: 0 }}
|
|
973
|
-
to={{ x: point.x, y: yMax }}
|
|
974
|
-
stroke={'black'}
|
|
975
|
-
strokeWidth={1}
|
|
976
|
-
pointerEvents='none'
|
|
977
|
-
strokeDasharray='5,5'
|
|
978
|
-
className='vertical-tooltip-line'
|
|
979
|
-
/>
|
|
980
|
-
</Group>
|
|
981
|
-
)}
|
|
982
954
|
<Group left={Number(config.runtime.yAxis.size)}>
|
|
983
955
|
<Annotation.Draggable
|
|
984
956
|
xScale={xScale}
|
|
@@ -1399,20 +1371,21 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
1399
1371
|
const axisMaxHeight = bottomLabelStart + BOTTOM_LABEL_PADDING
|
|
1400
1372
|
|
|
1401
1373
|
const containsMultipleWords = inputString => /\s/.test(inputString)
|
|
1402
|
-
const
|
|
1374
|
+
const isMultiLabel = filteredTicks.some(tick => containsMultipleWords(tick.value))
|
|
1403
1375
|
|
|
1404
1376
|
// Calculate sumOfTickWidth here, before map function
|
|
1405
|
-
const
|
|
1377
|
+
const longestTickLength = Math.max(
|
|
1406
1378
|
...filteredTicks.map(tick => getTextWidth(tick.formattedValue, GET_TEXT_WIDTH_FONT))
|
|
1407
1379
|
)
|
|
1408
1380
|
// const marginTop = 20 // moved to top bc need for yMax calcs
|
|
1409
|
-
const accumulator =
|
|
1381
|
+
const accumulator = isMultiLabel ? 180 : 100
|
|
1410
1382
|
|
|
1411
1383
|
const textWidths = filteredTicks.map(tick => getTextWidth(tick.formattedValue, GET_TEXT_WIDTH_FONT))
|
|
1412
1384
|
const sumOfTickWidth = textWidths.reduce((a, b) => a + b, accumulator)
|
|
1413
1385
|
const spaceBetweenEachTick = (xMax - sumOfTickWidth) / (filteredTicks.length - 1)
|
|
1386
|
+
const bufferBetweenTicks = 40
|
|
1387
|
+
const maxLengthOfTick = width / filteredTicks.length - X_TICK_LABEL_PADDING * 2 - bufferBetweenTicks
|
|
1414
1388
|
|
|
1415
|
-
// Check if ticks are overlapping
|
|
1416
1389
|
// Determine the position of each tick
|
|
1417
1390
|
let positions = [0] // The first tick is at position 0
|
|
1418
1391
|
for (let i = 1; i < textWidths.length; i++) {
|
|
@@ -1424,35 +1397,22 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
1424
1397
|
const axisBBox = axisBottomRef?.current?.getBBox().height
|
|
1425
1398
|
config.xAxis.axisBBox = axisBBox
|
|
1426
1399
|
|
|
1427
|
-
// Check if ticks are overlapping
|
|
1428
|
-
let areTicksTouching = false
|
|
1429
|
-
textWidths.forEach((_, i) => {
|
|
1430
|
-
if (positions[i] + textWidths[i] > positions[i + 1]) {
|
|
1431
|
-
areTicksTouching = true
|
|
1432
|
-
return
|
|
1433
|
-
}
|
|
1434
|
-
})
|
|
1435
|
-
|
|
1436
|
-
// Force wrap when showing years once so it's easier to read
|
|
1437
|
-
if (config.xAxis.showYearsOnce) {
|
|
1438
|
-
areTicksTouching = true
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
1400
|
// force wrap it last tick is close to the end of the axis
|
|
1442
1401
|
const lastTickWidth = textWidths[textWidths.length - 1]
|
|
1443
1402
|
const lastTickPosition = positions[positions.length - 1] + lastTickWidth
|
|
1444
1403
|
const lastTickEnd = lastTickPosition + lastTickWidth / 2
|
|
1445
1404
|
const lastTickEndThreshold = xMax - lastTickWidth
|
|
1446
1405
|
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1406
|
+
const areTicksTouching =
|
|
1407
|
+
textWidths.some(textWidth => textWidth > maxLengthOfTick) || // Force wrap if any tick is too long
|
|
1408
|
+
config.xAxis.showYearsOnce || // Force wrap when showing years once so it's easier to read
|
|
1409
|
+
lastTickEnd > lastTickEndThreshold // Force wrap it last tick is close to the end of the axis
|
|
1450
1410
|
|
|
1451
1411
|
const dynamicMarginTop =
|
|
1452
|
-
areTicksTouching && config.isResponsiveTicks ?
|
|
1412
|
+
areTicksTouching && config.isResponsiveTicks ? longestTickLength + DEFAULT_TICK_LENGTH + 20 : 0
|
|
1453
1413
|
|
|
1454
1414
|
config.dynamicMarginTop = dynamicMarginTop
|
|
1455
|
-
config.xAxis.tickWidthMax =
|
|
1415
|
+
config.xAxis.tickWidthMax = longestTickLength
|
|
1456
1416
|
|
|
1457
1417
|
return (
|
|
1458
1418
|
<Group className='bottom-axis' width={dimensions[0]}>
|
|
@@ -1556,7 +1516,6 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
|
|
|
1556
1516
|
</TooltipWithBounds>
|
|
1557
1517
|
</>
|
|
1558
1518
|
)}
|
|
1559
|
-
|
|
1560
1519
|
{config.visualizationType === 'Bump Chart' && (
|
|
1561
1520
|
<ReactTooltip
|
|
1562
1521
|
id={`bump-chart`}
|
|
@@ -6,6 +6,7 @@ import { Text } from '@visx/text'
|
|
|
6
6
|
|
|
7
7
|
import ConfigContext from '../ConfigContext'
|
|
8
8
|
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
9
|
+
import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
|
|
9
10
|
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
10
11
|
|
|
11
12
|
const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
@@ -47,8 +48,8 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
|
|
|
47
48
|
})
|
|
48
49
|
|
|
49
50
|
// Set label color
|
|
50
|
-
groupOne.labelColor = groupOne.color ? getContrastColor(
|
|
51
|
-
groupTwo.labelColor = groupTwo.color ? getContrastColor(
|
|
51
|
+
groupOne.labelColor = groupOne.color ? getContrastColor(APP_FONT_COLOR, groupOne.color) : APP_FONT_COLOR
|
|
52
|
+
groupTwo.labelColor = groupTwo.color ? getContrastColor(APP_FONT_COLOR, groupTwo.color) : APP_FONT_COLOR
|
|
52
53
|
|
|
53
54
|
const label = config.yAxis.label ? `${config.yAxis.label}: ` : ''
|
|
54
55
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useContext, useState, useEffect, useRef, useMemo } from 'react'
|
|
2
|
-
import { animated, useTransition,
|
|
2
|
+
import { animated, useTransition, to } from '@react-spring/web'
|
|
3
3
|
|
|
4
4
|
// visx
|
|
5
5
|
import { Pie } from '@visx/shape'
|
|
@@ -14,16 +14,9 @@ import { useTooltip as useCoveTooltip } from '../../hooks/useTooltip'
|
|
|
14
14
|
import useIntersectionObserver from '../../hooks/useIntersectionObserver'
|
|
15
15
|
import { handleChartAriaLabels } from '../../helpers/handleChartAriaLabels'
|
|
16
16
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
17
|
-
import LegendComponent from '../Legend/Legend.Component'
|
|
18
|
-
import { createFormatLabels } from '../Legend/helpers/createFormatLabels'
|
|
19
17
|
import { scaleOrdinal } from '@visx/scale'
|
|
20
18
|
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
21
19
|
|
|
22
|
-
const enterUpdateTransition = ({ startAngle, endAngle }) => ({
|
|
23
|
-
startAngle,
|
|
24
|
-
endAngle
|
|
25
|
-
})
|
|
26
|
-
|
|
27
20
|
type TooltipData = {
|
|
28
21
|
data: {
|
|
29
22
|
[key: string]: string | number
|
|
@@ -117,92 +110,71 @@ const PieChart = props => {
|
|
|
117
110
|
}
|
|
118
111
|
}, [dataRef?.isIntersecting, config.animate]) // eslint-disable-line
|
|
119
112
|
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
113
|
+
function AnimatedPie({ arcs, path, getKey, colorScale, onHover, onLeave }) {
|
|
114
|
+
const enterExit = ({ startAngle, endAngle }) => ({ startAngle, endAngle })
|
|
115
|
+
const transitions = useTransition(arcs, {
|
|
116
|
+
keys: getKey,
|
|
117
|
+
from: enterExit,
|
|
118
|
+
enter: enterExit,
|
|
119
|
+
update: enterExit,
|
|
120
|
+
leave: enterExit
|
|
126
121
|
})
|
|
127
122
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
// and use this useEffect to hide the tooltip so it doesn't persist when users scroll.
|
|
132
|
-
useEffect(() => {
|
|
133
|
-
const timeout = setTimeout(() => {
|
|
134
|
-
hideTooltip()
|
|
135
|
-
}, 500)
|
|
136
|
-
return () => {
|
|
137
|
-
clearTimeout(timeout)
|
|
138
|
-
}
|
|
139
|
-
}, [tooltipData])
|
|
140
|
-
|
|
141
|
-
return (
|
|
142
|
-
<>
|
|
143
|
-
{transitions.map(({ item: arc, props, key }, animatedPieIndex) => {
|
|
144
|
-
return (
|
|
145
|
-
<Group
|
|
146
|
-
className={arc.data[config.xAxis.dataKey]}
|
|
147
|
-
key={`${key}-${animatedPieIndex}`}
|
|
148
|
-
style={{
|
|
149
|
-
opacity:
|
|
150
|
-
config.legend.behavior === 'highlight' &&
|
|
151
|
-
seriesHighlight.length > 0 &&
|
|
152
|
-
seriesHighlight.indexOf(arc.data[config.runtime.xAxis.dataKey]) === -1
|
|
153
|
-
? 0.5
|
|
154
|
-
: 1
|
|
155
|
-
}}
|
|
156
|
-
>
|
|
157
|
-
<animated.path
|
|
158
|
-
d={interpolate([props.startAngle, props.endAngle], (startAngle, endAngle) =>
|
|
159
|
-
path({
|
|
160
|
-
...arc,
|
|
161
|
-
startAngle,
|
|
162
|
-
endAngle
|
|
163
|
-
})
|
|
164
|
-
)}
|
|
165
|
-
fill={_colorScale(arc.data[config.runtime.xAxis.dataKey])}
|
|
166
|
-
onMouseEnter={e => handleTooltipMouseOver(e, { data: arc.data[config.runtime.xAxis.dataKey], arc })}
|
|
167
|
-
onMouseLeave={e => handleTooltipMouseOff()}
|
|
168
|
-
/>
|
|
169
|
-
</Group>
|
|
170
|
-
)
|
|
171
|
-
})}
|
|
172
|
-
{transitions.map(({ item: arc, key }, i) => {
|
|
173
|
-
const roundTo = Number(config.dataFormat.roundTo) || 0
|
|
174
|
-
const [centroidX, centroidY] = path.centroid(arc)
|
|
175
|
-
const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.1
|
|
123
|
+
return transitions((styles, arc) => {
|
|
124
|
+
const key = getKey(arc)
|
|
125
|
+
let textColor = '#FFF'
|
|
176
126
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
127
|
+
if (key && _colorScale(key)) {
|
|
128
|
+
textColor = getContrastColor(textColor, _colorScale(arc.data[config.runtime.xAxis.dataKey]))
|
|
129
|
+
}
|
|
130
|
+
const roundTo = Number(config.dataFormat.roundTo) || 0
|
|
131
|
+
// Calculate the percentage of the full circle (360 degrees)
|
|
132
|
+
const degrees = ((arc.endAngle - arc.startAngle) * 180) / Math.PI
|
|
182
133
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
134
|
+
const percentageOfCircle = (degrees / 360) * 100
|
|
135
|
+
const roundedPercentage = percentageOfCircle.toFixed(roundTo) + '%'
|
|
136
|
+
return (
|
|
137
|
+
<Group key={key} className={`slice-${key}`}>
|
|
138
|
+
{/* ── the slice */}
|
|
139
|
+
<animated.path
|
|
140
|
+
d={to([styles.startAngle, styles.endAngle], (start, end) =>
|
|
141
|
+
path({ ...arc, startAngle: start, endAngle: end })
|
|
142
|
+
)}
|
|
143
|
+
fill={colorScale(key)}
|
|
144
|
+
onMouseEnter={e =>
|
|
145
|
+
onHover(e, {
|
|
146
|
+
data: arc.data,
|
|
147
|
+
dataXPosition: e.clientX,
|
|
148
|
+
dataYPosition: e.clientY,
|
|
149
|
+
startAngle: arc.startAngle,
|
|
150
|
+
endAngle: arc.endAngle
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
onMouseLeave={onLeave}
|
|
154
|
+
/>
|
|
186
155
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
156
|
+
{/* ── the percentage label */}
|
|
157
|
+
{arc.endAngle - arc.startAngle > 0.1 && (
|
|
158
|
+
<animated.text
|
|
159
|
+
transform={to([styles.startAngle, styles.endAngle], (start, end) => {
|
|
160
|
+
const [x, y] = path.centroid({
|
|
161
|
+
...arc,
|
|
162
|
+
startAngle: start,
|
|
163
|
+
endAngle: end
|
|
164
|
+
})
|
|
165
|
+
return `translate(${x},${y})`
|
|
166
|
+
})}
|
|
167
|
+
textAnchor='middle'
|
|
168
|
+
pointerEvents='none'
|
|
169
|
+
fill={textColor}
|
|
170
|
+
>
|
|
171
|
+
{/** compute text inside the spring callback */}
|
|
172
|
+
{roundedPercentage}
|
|
173
|
+
</animated.text>
|
|
174
|
+
)}
|
|
175
|
+
</Group>
|
|
176
|
+
)
|
|
177
|
+
})
|
|
206
178
|
}
|
|
207
179
|
|
|
208
180
|
let chartWidth = props.parentWidth
|
|
@@ -257,12 +229,20 @@ const PieChart = props => {
|
|
|
257
229
|
{/* prettier-ignore */}
|
|
258
230
|
<Pie
|
|
259
231
|
data={filteredData || _data}
|
|
260
|
-
pieValue={d => d[pivotKey || config.runtime.yAxis.dataKey]}
|
|
232
|
+
pieValue={d => parseFloat(d[pivotKey || config.runtime.yAxis.dataKey])}
|
|
261
233
|
pieSortValues={() => -1}
|
|
262
234
|
innerRadius={radius - donutThickness}
|
|
263
235
|
outerRadius={radius}
|
|
264
236
|
>
|
|
265
|
-
|
|
237
|
+
{pie => (
|
|
238
|
+
<AnimatedPie
|
|
239
|
+
{...pie}
|
|
240
|
+
getKey={d => d.data[config.runtime.xAxis.dataKey]}
|
|
241
|
+
colorScale={_colorScale}
|
|
242
|
+
onHover={handleTooltipMouseOver}
|
|
243
|
+
onLeave={handleTooltipMouseOff}
|
|
244
|
+
/>
|
|
245
|
+
)}
|
|
266
246
|
</Pie>
|
|
267
247
|
</Group>
|
|
268
248
|
</svg>
|