@cdc/chart 4.25.11 → 4.26.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/CLAUDE.local.md +79 -0
- package/dist/{cdcchart-dgT_1dIT.es.js → cdcchart-DQ00cQCm.es.js} +1 -20
- package/dist/cdcchart.js +51401 -50814
- package/examples/default.json +378 -0
- package/examples/feature/__data__/horizon-chart-data.json +373 -0
- package/examples/feature/annotations/index.json +3 -6
- package/examples/feature/horizon/horizon-chart.json +395 -0
- package/examples/feature/pie/planet-pie-example-config.json +48 -2
- package/examples/line-chart-states.json +1085 -0
- package/examples/private/123.json +694 -0
- package/examples/private/DEV-12100.json +1303 -0
- package/examples/private/anchor-issue.json +4094 -0
- package/examples/private/backwards-slider.json +10430 -0
- package/examples/private/cat-y.json +1235 -0
- package/examples/private/data-points.json +228 -0
- package/examples/private/georgia.csv +160 -0
- package/examples/private/height.json +3915 -0
- package/examples/private/links.json +569 -0
- package/examples/private/quadrant.txt +30 -0
- package/examples/private/test-forecast.json +5510 -0
- package/examples/private/timeline-data.json +1 -0
- package/examples/private/timeline.json +389 -0
- package/examples/private/warming-stripe-test.json +2578 -0
- package/examples/private/warming-stripes.json +4763 -0
- package/examples/radar-chart-simple.json +133 -0
- package/examples/radar-chart.json +148 -0
- package/examples/tech-adoption-with-links.json +560 -0
- package/index.html +1 -36
- package/package.json +59 -60
- package/src/CdcChartComponent.tsx +206 -89
- package/src/_stories/Chart.Anchors.stories.tsx +10 -0
- package/src/_stories/Chart.BoxPlot.stories.tsx +7 -0
- package/src/_stories/Chart.CI.stories.tsx +13 -0
- package/src/_stories/Chart.Combo.stories.tsx +17 -0
- package/src/_stories/Chart.CustomColors.stories.tsx +4 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +19 -0
- package/src/_stories/Chart.Filters.stories.tsx +4 -0
- package/src/_stories/Chart.Forecast.stories.tsx +4 -0
- package/src/_stories/Chart.HTMLInDataTable.stories.tsx +22 -0
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +28 -0
- package/src/_stories/Chart.Patterns.stories.tsx +4 -0
- package/src/_stories/Chart.PreserveDecimals.stories.tsx +25 -0
- package/src/_stories/Chart.Regions.Categorical.stories.tsx +161 -0
- package/src/_stories/Chart.Regions.DateScale.stories.tsx +216 -0
- package/src/_stories/Chart.Regions.DateTimeScale.stories.tsx +312 -0
- package/src/_stories/Chart.ScatterPlot.stories.tsx +4 -0
- package/src/_stories/Chart.SmallMultiples.stories.tsx +16 -0
- package/src/_stories/Chart.stories.tsx +45 -0
- package/src/_stories/Chart.tooltip.stories.tsx +7 -0
- package/src/_stories/ChartAnnotation.stories.tsx +10 -0
- package/src/_stories/ChartAxisLabels.stories.tsx +4 -0
- package/src/_stories/ChartAxisTitles.stories.tsx +10 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +11 -6
- package/src/_stories/ChartBrush.Editor.stories.tsx +295 -0
- package/src/_stories/ChartBrush.Matrix.Continuous.stories.tsx +41 -0
- package/src/_stories/ChartBrush.Matrix.Date.stories.tsx +114 -0
- package/src/_stories/ChartBrush.Matrix.DateTime.stories.tsx +78 -0
- package/src/_stories/ChartBrush.stories.tsx +57 -0
- package/src/_stories/ChartEditor.Editor.stories.tsx +3 -5
- package/src/_stories/ChartEditor.stories.tsx +7 -0
- package/src/_stories/ChartLine.QuadrantAngles.stories.tsx +89 -0
- package/src/_stories/ChartLine.Suppression.stories.tsx +7 -0
- package/src/_stories/ChartLine.Symbols.stories.tsx +4 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +46 -1
- package/src/_stories/TechAdoptionWithLinks.stories.tsx +34 -0
- package/src/_stories/_mock/brush_continuous.json +86 -0
- package/src/_stories/_mock/brush_date_large.json +176 -0
- package/src/_stories/_mock/brush_enabled.json +326 -0
- package/src/_stories/_mock/brush_mock.json +2 -69
- package/src/_stories/_mock/horizontal-bars-dynamic-y-axis.json +413 -0
- package/src/_stories/_mock/line_chart_angle_near_zero_fall.json +195 -0
- package/src/_stories/_mock/line_chart_angle_near_zero_rise.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q1_steep_upward.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q2_gentle_downward.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q3_steep_downward.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q4_gentle_upward.json +195 -0
- package/src/_stories/_mock/line_chart_quadrant_angles.json +264 -0
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +11 -17
- package/src/components/Annotations/components/AnnotationDraggable.tsx +240 -116
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -2
- package/src/components/Annotations/components/AnnotationDropdown.tsx +8 -12
- package/src/components/Annotations/components/AnnotationList.styles.css +4 -10
- package/src/components/Annotations/components/AnnotationList.tsx +5 -4
- package/src/components/Annotations/components/findNearestDatum.ts +75 -85
- package/src/components/Annotations/helpers/getVisibleAnnotations.ts +38 -0
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -2
- package/src/components/Axis/BottomAxis.tsx +270 -0
- package/src/components/Axis/Categorical.Axis.tsx +6 -7
- package/src/components/Axis/LeftAxis.tsx +404 -0
- package/src/components/Axis/LeftAxisGridlines.tsx +77 -0
- package/src/components/Axis/PairedBarAxis.tsx +186 -0
- package/src/components/Axis/README.md +94 -0
- package/src/components/Axis/RightAxis.tsx +108 -0
- package/src/components/Axis/axis.constants.ts +21 -0
- package/src/components/Axis/index.ts +7 -0
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +178 -24
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +3 -1
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +1 -0
- package/src/components/BarChart/components/BarChart.Vertical.tsx +6 -8
- package/src/components/BarChart/components/BarChart.tsx +7 -1
- package/src/components/BarChart/components/context.tsx +1 -0
- package/src/components/BarChart/helpers/useBarChart.ts +14 -2
- package/src/components/Brush/BrushSelector.tsx +1390 -0
- package/src/components/Brush/MiniChartPreview.tsx +400 -0
- package/src/components/DeviationBar.jsx +9 -7
- package/src/components/EditorPanel/EditorPanel.tsx +2734 -2595
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +60 -22
- package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +56 -34
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +137 -30
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +2 -0
- package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +353 -0
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +0 -1
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +30 -25
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +42 -28
- package/src/components/EditorPanel/components/Panels/index.tsx +2 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +81 -39
- package/src/components/HorizonChart/HorizonChart.tsx +131 -0
- package/src/components/HorizonChart/components/HorizonBand.tsx +160 -0
- package/src/components/HorizonChart/helpers/calculateHorizonBands.ts +27 -0
- package/src/components/HorizonChart/helpers/getHorizonLayerColors.ts +40 -0
- package/src/components/HorizonChart/index.tsx +3 -0
- package/src/components/Legend/Legend.Component.tsx +52 -4
- package/src/components/Legend/Legend.tsx +4 -3
- package/src/components/Legend/LegendValueRange.tsx +77 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +164 -2
- package/src/components/Legend/helpers/generateValueRanges.ts +92 -0
- package/src/components/Legend/helpers/index.ts +10 -6
- package/src/components/LineChart/helpers/README.md +292 -0
- package/src/components/LineChart/helpers/labelPositioning.test.ts +245 -0
- package/src/components/LineChart/helpers/labelPositioning.ts +304 -0
- package/src/components/LineChart/index.tsx +44 -8
- package/src/components/LinearChart/README.md +109 -0
- package/src/components/LinearChart/VisualizationRenderer.tsx +267 -0
- package/src/components/LinearChart/linearChart.constants.ts +84 -0
- package/src/components/LinearChart/tests/LinearChart.test.tsx +201 -0
- package/src/components/LinearChart/tests/mockConfigContext.ts +129 -0
- package/src/components/LinearChart/utils/tickFormatting.ts +146 -0
- package/src/components/LinearChart.tsx +338 -1082
- package/src/components/PairedBarChart.jsx +20 -3
- package/src/components/PieChart/PieChart.tsx +1 -1
- package/src/components/RadarChart/RadarAxis.tsx +78 -0
- package/src/components/RadarChart/RadarChart.tsx +298 -0
- package/src/components/RadarChart/RadarGrid.tsx +64 -0
- package/src/components/RadarChart/RadarPolygon.tsx +91 -0
- package/src/components/RadarChart/helpers.ts +83 -0
- package/src/components/RadarChart/index.tsx +3 -0
- package/src/components/Regions/components/Regions.tsx +365 -122
- package/src/components/ScatterPlot/ScatterPlot.jsx +2 -2
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +5 -1
- package/src/components/WarmingStripes/WarmingStripes.tsx +230 -0
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +35 -0
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +104 -0
- package/src/components/WarmingStripes/index.tsx +3 -0
- package/src/data/initial-state.js +17 -2
- package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +57 -0
- package/src/helpers/getExcludedData.ts +4 -0
- package/src/helpers/getMinMax.ts +12 -7
- package/src/helpers/handleChartAriaLabels.ts +19 -19
- package/src/helpers/handleLineType.ts +22 -18
- package/src/helpers/sizeHelpers.ts +0 -20
- package/src/helpers/smallMultiplesHelpers.ts +1 -1
- package/src/hooks/useChartHoverAnalytics.tsx +10 -9
- package/src/hooks/useProgrammaticTooltip.ts +23 -2
- package/src/hooks/useScales.ts +18 -1
- package/src/hooks/useTooltip.tsx +34 -10
- package/src/scss/DataTable.scss +0 -4
- package/src/scss/main.scss +22 -3
- package/src/selectors/README.md +68 -0
- package/src/store/chart.reducer.ts +2 -0
- package/src/test/CdcChart.test.jsx +1 -1
- package/src/types/ChartConfig.ts +21 -0
- package/src/types/ChartContext.ts +1 -0
- package/src/types/Horizon.ts +64 -0
- package/src/types/Label.ts +1 -0
- package/src/utils/analyticsTracking.ts +19 -0
- package/LICENSE +0 -201
- package/src/components/Annotations/components/helpers/index.tsx +0 -46
- package/src/components/Brush/BrushChart.tsx +0 -128
- package/src/components/Brush/BrushController.tsx +0 -71
- package/src/components/Brush/types.tsx +0 -8
- package/src/components/BrushChart.tsx +0 -223
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Axis Components
|
|
2
|
+
|
|
3
|
+
This folder contains extracted axis components from `LinearChart.tsx` to improve maintainability and reduce complexity.
|
|
4
|
+
|
|
5
|
+
## Components
|
|
6
|
+
|
|
7
|
+
### LeftAxis
|
|
8
|
+
**File:** `LeftAxis.tsx`
|
|
9
|
+
|
|
10
|
+
Renders the left (Y) axis for vertical charts. Handles:
|
|
11
|
+
- Tick formatting and positioning
|
|
12
|
+
- Labels above gridlines option
|
|
13
|
+
- Inline label (suffix) display
|
|
14
|
+
- Forest plot special rendering
|
|
15
|
+
|
|
16
|
+
**Props:**
|
|
17
|
+
- `yScale` - D3 scale for Y axis
|
|
18
|
+
- `xScale` - D3 scale for X axis
|
|
19
|
+
- `yMax`, `xMax` - Chart dimensions
|
|
20
|
+
- `yAxisWidth` - Width allocated for Y axis
|
|
21
|
+
- `numTicks` - Number of ticks to display
|
|
22
|
+
- `handleLeftTickFormatting` - Tick format function
|
|
23
|
+
|
|
24
|
+
### LeftAxisGridlines
|
|
25
|
+
**File:** `LeftAxisGridlines.tsx`
|
|
26
|
+
|
|
27
|
+
Renders horizontal gridlines separately from the axis itself. This separation allows gridlines to be drawn behind the visualization while the axis is drawn on top.
|
|
28
|
+
|
|
29
|
+
### BottomAxis
|
|
30
|
+
**File:** `BottomAxis.tsx`
|
|
31
|
+
|
|
32
|
+
Renders the bottom (X) axis. Handles:
|
|
33
|
+
- Date/time formatting
|
|
34
|
+
- Tick rotation for responsive layouts
|
|
35
|
+
- Manual step configuration
|
|
36
|
+
- Brush integration
|
|
37
|
+
|
|
38
|
+
**Props:**
|
|
39
|
+
- `xScale` - D3 scale for X axis
|
|
40
|
+
- `yMax` - Chart height
|
|
41
|
+
- `xTickCount` - Number of ticks
|
|
42
|
+
- `handleBottomTickFormatting` - Tick format function
|
|
43
|
+
- `useDateSpanMonths` - Date range calculation flag
|
|
44
|
+
|
|
45
|
+
### PairedBarAxis
|
|
46
|
+
**File:** `PairedBarAxis.tsx`
|
|
47
|
+
|
|
48
|
+
Specialized axis for Paired Bar charts with two mirrored AxisBottom components. Handles:
|
|
49
|
+
- Dual scale rendering (g1xScale, g2xScale)
|
|
50
|
+
- Responsive tick rotation
|
|
51
|
+
- Tick overlap detection
|
|
52
|
+
|
|
53
|
+
### RightAxis
|
|
54
|
+
**File:** `RightAxis.tsx`
|
|
55
|
+
|
|
56
|
+
Renders the right (Y) axis for dual-axis charts. Handles:
|
|
57
|
+
- Secondary Y scale
|
|
58
|
+
- Configurable tick/label colors
|
|
59
|
+
- Optional gridlines from right axis
|
|
60
|
+
|
|
61
|
+
### CategoricalYAxis
|
|
62
|
+
**File:** `Categorical.Axis.tsx`
|
|
63
|
+
|
|
64
|
+
Specialized Y axis for categorical data types.
|
|
65
|
+
|
|
66
|
+
## Constants
|
|
67
|
+
**File:** `axis.constants.ts`
|
|
68
|
+
|
|
69
|
+
Shared constants used across axis components.
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
All components are exported from `index.ts`:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import {
|
|
77
|
+
LeftAxis,
|
|
78
|
+
LeftAxisGridlines,
|
|
79
|
+
BottomAxis,
|
|
80
|
+
PairedBarAxis,
|
|
81
|
+
RightAxis,
|
|
82
|
+
CategoricalYAxis
|
|
83
|
+
} from './Axis'
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Architecture Notes
|
|
87
|
+
|
|
88
|
+
These components were extracted from `LinearChart.tsx` as part of a refactoring effort to:
|
|
89
|
+
1. Reduce the main component from 1,704 to ~845 lines
|
|
90
|
+
2. Improve testability through smaller, focused components
|
|
91
|
+
3. Enable reuse across different chart types
|
|
92
|
+
4. Simplify maintenance and debugging
|
|
93
|
+
|
|
94
|
+
Each component accesses the `ConfigContext` for chart configuration rather than receiving all config as props, keeping the prop interfaces focused on rendering-specific data.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import { AxisRight as VisxAxisRight } from '@visx/axis'
|
|
3
|
+
import { Group } from '@visx/group'
|
|
4
|
+
import { Line } from '@visx/shape'
|
|
5
|
+
import { Text } from '@visx/text'
|
|
6
|
+
import { ScaleLinear } from 'd3-scale'
|
|
7
|
+
|
|
8
|
+
import ConfigContext from '../../ConfigContext'
|
|
9
|
+
|
|
10
|
+
// Constants
|
|
11
|
+
const HORIZONTAL_TICK_OFFSET_ADJUSTMENT = 5
|
|
12
|
+
|
|
13
|
+
type RightAxisProps = {
|
|
14
|
+
yScaleRight: ScaleLinear<number, number>
|
|
15
|
+
yMax: number
|
|
16
|
+
xMax: number
|
|
17
|
+
yAxisWidth: number
|
|
18
|
+
tickLabelFontSize: number
|
|
19
|
+
axisLabelFontSize: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Right Y-axis component for dual-axis charts.
|
|
24
|
+
* Renders a secondary y-axis on the right side with configurable styling.
|
|
25
|
+
* Extracted from LinearChart.tsx
|
|
26
|
+
*/
|
|
27
|
+
export const RightAxis: React.FC<RightAxisProps> = ({
|
|
28
|
+
yScaleRight,
|
|
29
|
+
yMax,
|
|
30
|
+
xMax,
|
|
31
|
+
yAxisWidth,
|
|
32
|
+
tickLabelFontSize,
|
|
33
|
+
axisLabelFontSize
|
|
34
|
+
}) => {
|
|
35
|
+
const { config, formatNumber } = useContext(ConfigContext)
|
|
36
|
+
const { runtime } = config
|
|
37
|
+
|
|
38
|
+
const horizontalTickOffset = (ticks: any[]) =>
|
|
39
|
+
yMax / ticks.length / 2 - (yMax / ticks.length) * (1 - config.barThickness) + HORIZONTAL_TICK_OFFSET_ADJUSTMENT
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<VisxAxisRight
|
|
43
|
+
scale={yScaleRight}
|
|
44
|
+
left={yAxisWidth + xMax}
|
|
45
|
+
label={config.yAxis.rightLabel}
|
|
46
|
+
tickFormat={tick => formatNumber(tick, 'right')}
|
|
47
|
+
numTicks={runtime.yAxis.rightNumTicks || undefined}
|
|
48
|
+
labelOffset={45}
|
|
49
|
+
>
|
|
50
|
+
{props => {
|
|
51
|
+
const axisCenter =
|
|
52
|
+
config.orientation === 'horizontal'
|
|
53
|
+
? (props.axisToPoint.y - props.axisFromPoint.y) / 2
|
|
54
|
+
: (props.axisFromPoint.y - props.axisToPoint.y) / 2
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Group className='right-axis'>
|
|
58
|
+
{props.ticks.map((tick, i) => (
|
|
59
|
+
<Group key={`vx-tick-${tick.value}-${i}`} className='vx-axis-tick'>
|
|
60
|
+
{!runtime.yAxis.rightHideTicks && (
|
|
61
|
+
<Line
|
|
62
|
+
from={tick.from}
|
|
63
|
+
to={tick.to}
|
|
64
|
+
display={runtime.horizontal ? 'none' : 'block'}
|
|
65
|
+
stroke={config.yAxis.rightAxisTickColor}
|
|
66
|
+
/>
|
|
67
|
+
)}
|
|
68
|
+
|
|
69
|
+
{runtime.yAxis.rightGridLines && (
|
|
70
|
+
<Line from={{ x: tick.from.x + xMax, y: tick.from.y }} to={tick.from} stroke='#d6d6d6' />
|
|
71
|
+
)}
|
|
72
|
+
|
|
73
|
+
{!config.yAxis.rightHideLabel && (
|
|
74
|
+
<Text
|
|
75
|
+
x={tick.to.x}
|
|
76
|
+
y={tick.to.y + (runtime.horizontal ? horizontalTickOffset(props.ticks) : 0)}
|
|
77
|
+
verticalAnchor={runtime.horizontal ? 'start' : 'middle'}
|
|
78
|
+
textAnchor='start'
|
|
79
|
+
fill={config.yAxis.rightAxisTickLabelColor}
|
|
80
|
+
fontSize={tickLabelFontSize}
|
|
81
|
+
>
|
|
82
|
+
{tick.formattedValue}
|
|
83
|
+
</Text>
|
|
84
|
+
)}
|
|
85
|
+
</Group>
|
|
86
|
+
))}
|
|
87
|
+
|
|
88
|
+
{!config.yAxis.rightHideAxis && <Line from={props.axisFromPoint} to={props.axisToPoint} stroke='#333' />}
|
|
89
|
+
|
|
90
|
+
<Text
|
|
91
|
+
className='y-label'
|
|
92
|
+
textAnchor='middle'
|
|
93
|
+
verticalAnchor='start'
|
|
94
|
+
transform={`translate(${config.yAxis.rightLabelOffsetSize || 0}, ${axisCenter}) rotate(-90)`}
|
|
95
|
+
fontWeight='bold'
|
|
96
|
+
fill={config.yAxis.rightAxisLabelColor}
|
|
97
|
+
fontSize={axisLabelFontSize}
|
|
98
|
+
>
|
|
99
|
+
{props.label}
|
|
100
|
+
</Text>
|
|
101
|
+
</Group>
|
|
102
|
+
)
|
|
103
|
+
}}
|
|
104
|
+
</VisxAxisRight>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export default RightAxis
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Tick length constants
|
|
2
|
+
export const DEFAULT_TICK_LENGTH = 8
|
|
3
|
+
export const LOGARITHMIC_TICK_LENGTH = 6
|
|
4
|
+
export const MAJOR_LOG_TICK_LENGTH = 7
|
|
5
|
+
|
|
6
|
+
// Tick styling constants
|
|
7
|
+
export const TICK_LABEL_MARGIN_RIGHT = 4.5
|
|
8
|
+
export const MAJOR_LOG_TICK_STROKE_WIDTH = 1.3
|
|
9
|
+
|
|
10
|
+
// Label positioning constants
|
|
11
|
+
export const VALUE_ON_LINE_PADDING_NO_AXIS = -8
|
|
12
|
+
export const VALUE_ON_LINE_PADDING_WITH_AXIS = -12
|
|
13
|
+
export const LABEL_Y_PADDING_ABOVE_GRIDLINES = 4
|
|
14
|
+
export const HORIZONTAL_TICK_OFFSET_ADJUSTMENT = 5
|
|
15
|
+
|
|
16
|
+
// Chart-specific constants
|
|
17
|
+
export const ZERO_LINE_STROKE_WIDTH = 2
|
|
18
|
+
export const BAR_MIN_HEIGHT = 15
|
|
19
|
+
|
|
20
|
+
// Lollipop chart sizes
|
|
21
|
+
export const LOLLIPOP_SIZES = { large: 7, medium: 6, small: 5 } as const
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as CategoricalYAxis } from './Categorical.Axis'
|
|
2
|
+
export { default as LeftAxis } from './LeftAxis'
|
|
3
|
+
export { default as LeftAxisGridlines } from './LeftAxisGridlines'
|
|
4
|
+
export { default as BottomAxis } from './BottomAxis'
|
|
5
|
+
export { default as PairedBarAxis } from './PairedBarAxis'
|
|
6
|
+
export { default as RightAxis } from './RightAxis'
|
|
7
|
+
export * from './axis.constants'
|
|
@@ -13,7 +13,6 @@ import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
|
|
|
13
13
|
// CDC core components and helpers
|
|
14
14
|
import { getColorContrast, getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
15
15
|
import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
|
|
16
|
-
import { isMobileFontViewport } from '@cdc/core/helpers/viewports'
|
|
17
16
|
import createBarElement from '@cdc/core/components/createBarElement'
|
|
18
17
|
import { getBarConfig, testZeroValue, getLollipopStemColor, getLollipopHeadColor } from '../helpers'
|
|
19
18
|
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
@@ -36,6 +35,7 @@ const BarChartHorizontal = () => {
|
|
|
36
35
|
isLabelBelowBar,
|
|
37
36
|
lollipopBarWidth,
|
|
38
37
|
lollipopShapeSize,
|
|
38
|
+
labelFontSize,
|
|
39
39
|
getHighlightedBarColorByValue,
|
|
40
40
|
getHighlightedBarByValue,
|
|
41
41
|
getAdditionalColumn,
|
|
@@ -59,8 +59,6 @@ const BarChartHorizontal = () => {
|
|
|
59
59
|
|
|
60
60
|
const { HighLightedBarUtils } = useHighlightedBars(config)
|
|
61
61
|
|
|
62
|
-
const LABEL_FONT_SIZE = isMobileFontViewport(vizViewport) ? 13 : 16
|
|
63
|
-
|
|
64
62
|
const hasConfidenceInterval = [config.confidenceKeys?.upper, config.confidenceKeys?.lower].every(
|
|
65
63
|
v => v != null && String(v).trim() !== ''
|
|
66
64
|
)
|
|
@@ -209,7 +207,7 @@ const BarChartHorizontal = () => {
|
|
|
209
207
|
isVertical: false,
|
|
210
208
|
yAxisValue,
|
|
211
209
|
barWidth: 0,
|
|
212
|
-
labelFontSize:
|
|
210
|
+
labelFontSize: labelFontSize
|
|
213
211
|
})
|
|
214
212
|
|
|
215
213
|
const barPosition = !isPositiveBar ? 'below' : 'above'
|
|
@@ -217,7 +215,7 @@ const BarChartHorizontal = () => {
|
|
|
217
215
|
const barDefaultLabel = !config.yAxis.displayNumbersOnBar || absentDataLabel ? '' : yAxisValue
|
|
218
216
|
|
|
219
217
|
// check if bar text/value string fits into each bars.
|
|
220
|
-
const textWidth = getTextWidth(barDefaultLabel)
|
|
218
|
+
const textWidth = getTextWidth(barDefaultLabel, `normal ${labelFontSize}px sans-serif`)
|
|
221
219
|
const textFits = Number(textWidth) < defaultBarWidth - 5
|
|
222
220
|
|
|
223
221
|
// control text position
|
|
@@ -447,7 +445,7 @@ const BarChartHorizontal = () => {
|
|
|
447
445
|
return (
|
|
448
446
|
<Text // prettier-ignore
|
|
449
447
|
key={index}
|
|
450
|
-
fontSize={
|
|
448
|
+
fontSize={labelFontSize}
|
|
451
449
|
display={displayBar ? 'block' : 'none'}
|
|
452
450
|
opacity={transparentBar ? 0.5 : 1}
|
|
453
451
|
x={barX}
|
|
@@ -473,6 +471,7 @@ const BarChartHorizontal = () => {
|
|
|
473
471
|
dx={textPadding}
|
|
474
472
|
verticalAnchor='middle'
|
|
475
473
|
textAnchor={textAnchor}
|
|
474
|
+
fontSize={labelFontSize}
|
|
476
475
|
>
|
|
477
476
|
{testZeroValue(bar.value) ? '' : barDefaultLabel}
|
|
478
477
|
</Text>
|
|
@@ -488,6 +487,7 @@ const BarChartHorizontal = () => {
|
|
|
488
487
|
dx={-textPadding}
|
|
489
488
|
verticalAnchor='middle'
|
|
490
489
|
textAnchor={bar.value < 0 ? 'end' : 'start'}
|
|
490
|
+
fontSize={labelFontSize}
|
|
491
491
|
>
|
|
492
492
|
{testZeroValue(bar.value) ? '' : barDefaultLabel}
|
|
493
493
|
</Text>
|
|
@@ -496,12 +496,17 @@ const BarChartHorizontal = () => {
|
|
|
496
496
|
display={displayBar ? 'block' : 'none'}
|
|
497
497
|
x={bar.y}
|
|
498
498
|
opacity={transparentBar ? 0.5 : 1}
|
|
499
|
-
y={
|
|
499
|
+
y={
|
|
500
|
+
config.isLollipopChart
|
|
501
|
+
? barHeight * bar.index + lollipopBarWidth / 2
|
|
502
|
+
: config.barHeight / 2 + config.barHeight * bar.index
|
|
503
|
+
}
|
|
500
504
|
fill={labelColor}
|
|
501
505
|
dx={absentDataLabel === 'N/A' ? 20 : textPadding}
|
|
502
|
-
dy={
|
|
506
|
+
dy={0}
|
|
503
507
|
verticalAnchor='middle'
|
|
504
508
|
textAnchor={absentDataLabel === 'N/A' ? 'middle' : textAnchor}
|
|
509
|
+
fontSize={labelFontSize}
|
|
505
510
|
>
|
|
506
511
|
{absentDataLabel}
|
|
507
512
|
</Text>
|
|
@@ -510,31 +515,180 @@ const BarChartHorizontal = () => {
|
|
|
510
515
|
<Text // prettier-ignore
|
|
511
516
|
display={displayBar ? 'block' : 'none'}
|
|
512
517
|
x={bar.y}
|
|
513
|
-
y={
|
|
518
|
+
y={barHeight * bar.index + lollipopBarWidth / 2}
|
|
514
519
|
fill={APP_FONT_COLOR}
|
|
515
520
|
dx={textPaddingLollipop}
|
|
516
521
|
textAnchor={textAnchorLollipop}
|
|
517
522
|
verticalAnchor='middle'
|
|
518
523
|
fontWeight={'normal'}
|
|
524
|
+
fontSize={labelFontSize}
|
|
519
525
|
>
|
|
520
526
|
{testZeroValue(bar.value) ? '' : barDefaultLabel}
|
|
521
527
|
</Text>
|
|
522
528
|
)}
|
|
523
|
-
{isLabelBelowBar &&
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
?
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
529
|
+
{isLabelBelowBar &&
|
|
530
|
+
!config.yAxis.hideLabel &&
|
|
531
|
+
(() => {
|
|
532
|
+
const label =
|
|
533
|
+
config.runtime.yAxis.type === 'date'
|
|
534
|
+
? formatDate(parseDate(dataValue))
|
|
535
|
+
: isHorizontal
|
|
536
|
+
? dataValue
|
|
537
|
+
: formatNumber(dataValue)
|
|
538
|
+
if (typeof label === 'string') {
|
|
539
|
+
// 1. Check for HTML <a> tag and extract its text and href
|
|
540
|
+
const aTagMatch = label.match(/<a [^>]*href=["']?([^"'>\s]+)["']?[^>]*>(.*?)<\/a>/i)
|
|
541
|
+
if (aTagMatch) {
|
|
542
|
+
const href = aTagMatch[1].startsWith('http') ? aTagMatch[1] : `https://${aTagMatch[1]}`
|
|
543
|
+
const linkText = aTagMatch[2]
|
|
544
|
+
return (
|
|
545
|
+
<foreignObject
|
|
546
|
+
x={config.yAxis.hideAxis ? 0 : 5}
|
|
547
|
+
y={barGroup.height}
|
|
548
|
+
width={120}
|
|
549
|
+
height={24}
|
|
550
|
+
style={{ overflow: 'visible' }}
|
|
551
|
+
>
|
|
552
|
+
<a
|
|
553
|
+
href={href}
|
|
554
|
+
target='_blank'
|
|
555
|
+
rel='noopener noreferrer'
|
|
556
|
+
style={
|
|
557
|
+
config.tooltips.singleSeries
|
|
558
|
+
? {
|
|
559
|
+
color: '#0071bc',
|
|
560
|
+
textDecoration: 'underline',
|
|
561
|
+
fontSize: 12,
|
|
562
|
+
fontFamily: 'inherit',
|
|
563
|
+
display: 'inline-block',
|
|
564
|
+
width: '100%'
|
|
565
|
+
}
|
|
566
|
+
: {
|
|
567
|
+
color: 'inherit',
|
|
568
|
+
textDecoration: 'none',
|
|
569
|
+
fontSize: 12,
|
|
570
|
+
fontFamily: 'inherit',
|
|
571
|
+
display: 'inline-block',
|
|
572
|
+
width: '100%'
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
>
|
|
576
|
+
{linkText}
|
|
577
|
+
</a>
|
|
578
|
+
</foreignObject>
|
|
579
|
+
)
|
|
580
|
+
}
|
|
581
|
+
// 2. Check for markdown link
|
|
582
|
+
const mdMatch = label.match(/\[([^\]]+)\]\((https?:\/\/[^\s]+|www\.[^\s]+)\)/i)
|
|
583
|
+
if (mdMatch) {
|
|
584
|
+
const href = mdMatch[2].startsWith('http') ? mdMatch[2] : `https://${mdMatch[2]}`
|
|
585
|
+
const linkText = mdMatch[1]
|
|
586
|
+
return (
|
|
587
|
+
<foreignObject
|
|
588
|
+
x={config.yAxis.hideAxis ? 0 : 5}
|
|
589
|
+
y={barGroup.height}
|
|
590
|
+
width={120}
|
|
591
|
+
height={24}
|
|
592
|
+
style={{ overflow: 'visible' }}
|
|
593
|
+
>
|
|
594
|
+
<a
|
|
595
|
+
href={href}
|
|
596
|
+
target='_blank'
|
|
597
|
+
rel='noopener noreferrer'
|
|
598
|
+
style={
|
|
599
|
+
config.tooltips.singleSeries
|
|
600
|
+
? {
|
|
601
|
+
color: '#0071bc',
|
|
602
|
+
textDecoration: 'underline',
|
|
603
|
+
fontSize: 12,
|
|
604
|
+
fontFamily: 'inherit',
|
|
605
|
+
display: 'inline-block',
|
|
606
|
+
width: '100%'
|
|
607
|
+
}
|
|
608
|
+
: {
|
|
609
|
+
color: 'inherit',
|
|
610
|
+
textDecoration: 'none',
|
|
611
|
+
fontSize: 12,
|
|
612
|
+
fontFamily: 'inherit',
|
|
613
|
+
display: 'inline-block',
|
|
614
|
+
width: '100%'
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
>
|
|
618
|
+
{linkText}
|
|
619
|
+
</a>
|
|
620
|
+
</foreignObject>
|
|
621
|
+
)
|
|
622
|
+
}
|
|
623
|
+
// 3. Check for plain URL
|
|
624
|
+
if (/(https?:\/\/|www\.)/i.test(label)) {
|
|
625
|
+
try {
|
|
626
|
+
const urlObj = new URL(label.startsWith('http') ? label : `https://${label}`)
|
|
627
|
+
const linkText = urlObj.hostname.replace(/^www\./, '')
|
|
628
|
+
return (
|
|
629
|
+
<foreignObject
|
|
630
|
+
x={config.yAxis.hideAxis ? 0 : 5}
|
|
631
|
+
y={barGroup.height}
|
|
632
|
+
width={120}
|
|
633
|
+
height={24}
|
|
634
|
+
style={{ overflow: 'visible' }}
|
|
635
|
+
>
|
|
636
|
+
<a
|
|
637
|
+
href={urlObj.href}
|
|
638
|
+
target='_blank'
|
|
639
|
+
rel='noopener noreferrer'
|
|
640
|
+
style={
|
|
641
|
+
config.tooltips.singleSeries
|
|
642
|
+
? {
|
|
643
|
+
color: '#0071bc',
|
|
644
|
+
textDecoration: 'underline',
|
|
645
|
+
fontSize: 12,
|
|
646
|
+
fontFamily: 'inherit',
|
|
647
|
+
display: 'inline-block',
|
|
648
|
+
width: '100%'
|
|
649
|
+
}
|
|
650
|
+
: {
|
|
651
|
+
color: 'inherit',
|
|
652
|
+
textDecoration: 'none',
|
|
653
|
+
fontSize: 12,
|
|
654
|
+
fontFamily: 'inherit',
|
|
655
|
+
display: 'inline-block',
|
|
656
|
+
width: '100%'
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
>
|
|
660
|
+
{linkText}
|
|
661
|
+
</a>
|
|
662
|
+
</foreignObject>
|
|
663
|
+
)
|
|
664
|
+
} catch {
|
|
665
|
+
return (
|
|
666
|
+
<Text
|
|
667
|
+
x={config.yAxis.hideAxis ? 0 : 5}
|
|
668
|
+
y={barGroup.height}
|
|
669
|
+
dy={4}
|
|
670
|
+
verticalAnchor={'start'}
|
|
671
|
+
textAnchor={'start'}
|
|
672
|
+
>
|
|
673
|
+
{label}
|
|
674
|
+
</Text>
|
|
675
|
+
)
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
// Not a link, render as normal
|
|
680
|
+
return (
|
|
681
|
+
<Text
|
|
682
|
+
x={config.yAxis.hideAxis ? 0 : 5}
|
|
683
|
+
y={barGroup.height}
|
|
684
|
+
dy={4}
|
|
685
|
+
verticalAnchor={'start'}
|
|
686
|
+
textAnchor={'start'}
|
|
687
|
+
>
|
|
688
|
+
{label}
|
|
689
|
+
</Text>
|
|
690
|
+
)
|
|
691
|
+
})()}
|
|
538
692
|
|
|
539
693
|
{config.isLollipopChart && config.lollipopShape === 'circle' && (
|
|
540
694
|
<circle
|
|
@@ -38,6 +38,7 @@ const BarChartStackedHorizontal = () => {
|
|
|
38
38
|
hoveredBar,
|
|
39
39
|
isHorizontal,
|
|
40
40
|
isLabelBelowBar,
|
|
41
|
+
labelFontSize,
|
|
41
42
|
onMouseLeaveBar,
|
|
42
43
|
onMouseOverBar,
|
|
43
44
|
barStackedSeriesKeys
|
|
@@ -155,7 +156,7 @@ const BarChartStackedHorizontal = () => {
|
|
|
155
156
|
const yAxisTooltip = config.runtime.yAxis.label
|
|
156
157
|
? `${config.runtime.yAxis.label}: ${yAxisValue}`
|
|
157
158
|
: yAxisValue
|
|
158
|
-
const textWidth = getTextWidth(xAxisValue)
|
|
159
|
+
const textWidth = getTextWidth(xAxisValue, `normal ${labelFontSize}px sans-serif`)
|
|
159
160
|
const additionalColTooltip = getAdditionalColumn(bar.key, hoveredBar)
|
|
160
161
|
const tooltipBody = `${config.runtime.seriesLabels[bar.key]}: ${xAxisValue}`
|
|
161
162
|
const tooltip = `<ul>
|
|
@@ -286,6 +287,7 @@ const BarChartStackedHorizontal = () => {
|
|
|
286
287
|
fill={labelColor}
|
|
287
288
|
textAnchor='middle'
|
|
288
289
|
verticalAnchor='middle'
|
|
290
|
+
fontSize={labelFontSize}
|
|
289
291
|
>
|
|
290
292
|
{xAxisValue}
|
|
291
293
|
</Text>
|
|
@@ -17,7 +17,6 @@ import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
|
17
17
|
import isNumber from '@cdc/core/helpers/isNumber'
|
|
18
18
|
import createBarElement from '@cdc/core/components/createBarElement'
|
|
19
19
|
import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
|
|
20
|
-
import { isMobileFontViewport } from '@cdc/core/helpers/viewports'
|
|
21
20
|
// Types
|
|
22
21
|
import { type ChartContext } from '../../../types/ChartContext'
|
|
23
22
|
import _ from 'lodash'
|
|
@@ -32,6 +31,7 @@ const BarChartVertical = () => {
|
|
|
32
31
|
getAdditionalColumn,
|
|
33
32
|
getHighlightedBarByValue,
|
|
34
33
|
getHighlightedBarColorByValue,
|
|
34
|
+
labelFontSize,
|
|
35
35
|
lollipopBarWidth,
|
|
36
36
|
lollipopShapeSize,
|
|
37
37
|
onMouseLeaveBar,
|
|
@@ -59,8 +59,6 @@ const BarChartVertical = () => {
|
|
|
59
59
|
|
|
60
60
|
const { HighLightedBarUtils } = useHighlightedBars(config)
|
|
61
61
|
|
|
62
|
-
const LABEL_FONT_SIZE = isMobileFontViewport(vizViewport) ? 13 : 16
|
|
63
|
-
|
|
64
62
|
const root = document.documentElement
|
|
65
63
|
|
|
66
64
|
let data = transformedData
|
|
@@ -242,7 +240,7 @@ const BarChartVertical = () => {
|
|
|
242
240
|
barWidth,
|
|
243
241
|
isVertical: true,
|
|
244
242
|
yAxisValue,
|
|
245
|
-
labelFontSize:
|
|
243
|
+
labelFontSize: labelFontSize
|
|
246
244
|
})
|
|
247
245
|
// configure colors
|
|
248
246
|
let labelColor = APP_FONT_COLOR
|
|
@@ -481,7 +479,7 @@ const BarChartVertical = () => {
|
|
|
481
479
|
verticalAnchor={verticalAnchor}
|
|
482
480
|
fill={fillColor}
|
|
483
481
|
textAnchor='middle'
|
|
484
|
-
fontSize={
|
|
482
|
+
fontSize={labelFontSize}
|
|
485
483
|
>
|
|
486
484
|
{pd.iconCode}
|
|
487
485
|
</Text>
|
|
@@ -494,7 +492,7 @@ const BarChartVertical = () => {
|
|
|
494
492
|
y={barY - BAR_LABEL_PADDING}
|
|
495
493
|
fill={labelColor}
|
|
496
494
|
textAnchor='middle'
|
|
497
|
-
fontSize={
|
|
495
|
+
fontSize={labelFontSize}
|
|
498
496
|
>
|
|
499
497
|
{testZeroValue(bar.value) ? '' : barDefaultLabel}
|
|
500
498
|
</Text>
|
|
@@ -505,7 +503,7 @@ const BarChartVertical = () => {
|
|
|
505
503
|
y={barY - BAR_LABEL_PADDING}
|
|
506
504
|
fill={labelColor}
|
|
507
505
|
textAnchor='middle'
|
|
508
|
-
fontSize={config.isLollipopChart ? null :
|
|
506
|
+
fontSize={config.isLollipopChart ? null : labelFontSize}
|
|
509
507
|
>
|
|
510
508
|
{absentDataLabel}
|
|
511
509
|
</Text>
|
|
@@ -568,7 +566,7 @@ const BarChartVertical = () => {
|
|
|
568
566
|
}}
|
|
569
567
|
</BarGroup>
|
|
570
568
|
|
|
571
|
-
<Regions xScale={xScale} yMax={yMax} barWidth={barWidth} totalBarsInGroup={totalBarsInGroup} />
|
|
569
|
+
<Regions xScale={xScale} yMax={yMax} barWidth={barWidth} totalBarsInGroup={totalBarsInGroup} xMax={xMax} />
|
|
572
570
|
</Group>
|
|
573
571
|
)
|
|
574
572
|
)
|
|
@@ -18,6 +18,7 @@ type BarChartProps = {
|
|
|
18
18
|
seriesScale: PositionScale
|
|
19
19
|
xMax: number
|
|
20
20
|
yMax: number
|
|
21
|
+
yAxisWidth?: number
|
|
21
22
|
handleTooltipMouseOver: MouseEventHandler<SVGRectElement>
|
|
22
23
|
handleTooltipMouseOff: MouseEventHandler<SVGRectElement>
|
|
23
24
|
handleTooltipClick: MouseEventHandler<SVGRectElement>
|
|
@@ -29,6 +30,7 @@ const BarChart: React.FC<BarChartProps> = ({
|
|
|
29
30
|
seriesScale,
|
|
30
31
|
xMax,
|
|
31
32
|
yMax,
|
|
33
|
+
yAxisWidth,
|
|
32
34
|
handleTooltipMouseOver,
|
|
33
35
|
handleTooltipMouseOff,
|
|
34
36
|
handleTooltipClick
|
|
@@ -47,10 +49,14 @@ const BarChart: React.FC<BarChartProps> = ({
|
|
|
47
49
|
barChart
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
// Use yAxisWidth prop if provided (for horizontal bar charts with dynamic labels)
|
|
53
|
+
// otherwise fall back to config value
|
|
54
|
+
const leftOffset = yAxisWidth ?? parseFloat(config.runtime.yAxis.size)
|
|
55
|
+
|
|
50
56
|
return (
|
|
51
57
|
<ErrorBoundary component='BarChart'>
|
|
52
58
|
<BarChartContext.Provider value={contextValues}>
|
|
53
|
-
<Group left={
|
|
59
|
+
<Group left={leftOffset}>
|
|
54
60
|
<BarChartType.StackedVertical />
|
|
55
61
|
<BarChartType.StackedHorizontal />
|
|
56
62
|
<BarChartType.Vertical />
|