@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
|
@@ -7,12 +7,14 @@ import lollipop from './_mock/lollipop.json'
|
|
|
7
7
|
import forestPlot from '../../examples/feature/forest-plot/forest-plot.json'
|
|
8
8
|
import pairedBar from './_mock/paired-bar.json'
|
|
9
9
|
import horizontalBarConfig from './_mock/horizontal_bar.json'
|
|
10
|
+
import horizontalBarsDynamicYAxis from './_mock/horizontal-bars-dynamic-y-axis.json'
|
|
10
11
|
import barChartLabels from './_mock/barchart_labels.mock.json'
|
|
11
12
|
import pieConfig from './_mock/pie_with_data.json'
|
|
12
13
|
import pieCalculatedArea from './_mock/pie_calculated_area.json'
|
|
13
14
|
import areaChartStacked from './_mock/area_chart_stacked.json'
|
|
14
15
|
import multipleLines from './_mock/short_dates.json'
|
|
15
16
|
import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
|
|
17
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
16
18
|
|
|
17
19
|
const meta: Meta<typeof Chart> = {
|
|
18
20
|
title: 'Components/Templates/Chart',
|
|
@@ -25,18 +27,27 @@ export const line_Chart_Two_Points_Regression_Test: Story = {
|
|
|
25
27
|
args: {
|
|
26
28
|
config: lineChartTwoPointsRegressionTest,
|
|
27
29
|
isEditor: false
|
|
30
|
+
},
|
|
31
|
+
play: async ({ canvasElement }) => {
|
|
32
|
+
await assertVisualizationRendered(canvasElement)
|
|
28
33
|
}
|
|
29
34
|
}
|
|
30
35
|
export const line_Chart_Two_Points_New_Chart: Story = {
|
|
31
36
|
args: {
|
|
32
37
|
config: lineChartTwoPointsNewChart,
|
|
33
38
|
isEditor: false
|
|
39
|
+
},
|
|
40
|
+
play: async ({ canvasElement }) => {
|
|
41
|
+
await assertVisualizationRendered(canvasElement)
|
|
34
42
|
}
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
export const multiple_lines: Story = {
|
|
38
46
|
args: {
|
|
39
47
|
config: editConfigKeys(multipleLines, [{ path: ['tooltips', 'dateDisplayFormat'], value: '%b. %d %Y' }])
|
|
48
|
+
},
|
|
49
|
+
play: async ({ canvasElement }) => {
|
|
50
|
+
await assertVisualizationRendered(canvasElement)
|
|
40
51
|
}
|
|
41
52
|
}
|
|
42
53
|
|
|
@@ -44,24 +55,46 @@ export const Lollipop: Story = {
|
|
|
44
55
|
args: {
|
|
45
56
|
config: lollipop,
|
|
46
57
|
isEditor: false
|
|
58
|
+
},
|
|
59
|
+
play: async ({ canvasElement }) => {
|
|
60
|
+
await assertVisualizationRendered(canvasElement)
|
|
47
61
|
}
|
|
48
62
|
}
|
|
49
63
|
|
|
50
64
|
export const Forest_Plot: Story = {
|
|
51
65
|
args: {
|
|
52
66
|
config: forestPlot
|
|
67
|
+
},
|
|
68
|
+
play: async ({ canvasElement }) => {
|
|
69
|
+
await assertVisualizationRendered(canvasElement)
|
|
53
70
|
}
|
|
54
71
|
}
|
|
55
72
|
|
|
56
73
|
export const Horizontal_Bar: Story = {
|
|
57
74
|
args: {
|
|
58
75
|
config: horizontalBarConfig
|
|
76
|
+
},
|
|
77
|
+
play: async ({ canvasElement }) => {
|
|
78
|
+
await assertVisualizationRendered(canvasElement)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const Horizontal_Bars_Dynamic_Y_Axis: Story = {
|
|
83
|
+
args: {
|
|
84
|
+
config: horizontalBarsDynamicYAxis,
|
|
85
|
+
isEditor: false
|
|
86
|
+
},
|
|
87
|
+
play: async ({ canvasElement }) => {
|
|
88
|
+
await assertVisualizationRendered(canvasElement)
|
|
59
89
|
}
|
|
60
90
|
}
|
|
61
91
|
|
|
62
92
|
export const BarChart_Labels: Story = {
|
|
63
93
|
args: {
|
|
64
94
|
config: barChartLabels
|
|
95
|
+
},
|
|
96
|
+
play: async ({ canvasElement }) => {
|
|
97
|
+
await assertVisualizationRendered(canvasElement)
|
|
65
98
|
}
|
|
66
99
|
}
|
|
67
100
|
|
|
@@ -69,12 +102,18 @@ export const Pie: Story = {
|
|
|
69
102
|
args: {
|
|
70
103
|
config: pieConfig,
|
|
71
104
|
isEditor: true
|
|
105
|
+
},
|
|
106
|
+
play: async ({ canvasElement }) => {
|
|
107
|
+
await assertVisualizationRendered(canvasElement)
|
|
72
108
|
}
|
|
73
109
|
}
|
|
74
110
|
export const Pie_Calculated_Area: Story = {
|
|
75
111
|
args: {
|
|
76
112
|
config: pieCalculatedArea,
|
|
77
113
|
isEditor: true
|
|
114
|
+
},
|
|
115
|
+
play: async ({ canvasElement }) => {
|
|
116
|
+
await assertVisualizationRendered(canvasElement)
|
|
78
117
|
}
|
|
79
118
|
}
|
|
80
119
|
|
|
@@ -82,6 +121,9 @@ export const Paired_Bar: Story = {
|
|
|
82
121
|
args: {
|
|
83
122
|
config: pairedBar,
|
|
84
123
|
isEditor: true
|
|
124
|
+
},
|
|
125
|
+
play: async ({ canvasElement }) => {
|
|
126
|
+
await assertVisualizationRendered(canvasElement)
|
|
85
127
|
}
|
|
86
128
|
}
|
|
87
129
|
|
|
@@ -89,6 +131,9 @@ export const Area_Chart_stacked: Story = {
|
|
|
89
131
|
args: {
|
|
90
132
|
config: areaChartStacked,
|
|
91
133
|
isEditor: false
|
|
134
|
+
},
|
|
135
|
+
play: async ({ canvasElement }) => {
|
|
136
|
+
await assertVisualizationRendered(canvasElement)
|
|
92
137
|
}
|
|
93
138
|
}
|
|
94
139
|
|
|
@@ -3,6 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
|
3
3
|
import Chart from '../CdcChartComponent'
|
|
4
4
|
import barChartStacked from './_mock/barchart_labels.mock.json'
|
|
5
5
|
import barChartSuppressed from './_mock/bar-chart-suppressed.json'
|
|
6
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
6
7
|
const meta: Meta<typeof Chart> = {
|
|
7
8
|
title: 'Components/Templates/Chart/Tooltip',
|
|
8
9
|
component: Chart
|
|
@@ -20,6 +21,9 @@ export const Increased_Tooltip_Range: Story = {
|
|
|
20
21
|
singleSeries: true
|
|
21
22
|
}
|
|
22
23
|
}
|
|
24
|
+
},
|
|
25
|
+
play: async ({ canvasElement }) => {
|
|
26
|
+
await assertVisualizationRendered(canvasElement)
|
|
23
27
|
}
|
|
24
28
|
}
|
|
25
29
|
|
|
@@ -58,6 +62,9 @@ export const Additional_Tooltip: Story = {
|
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
}
|
|
65
|
+
},
|
|
66
|
+
play: async ({ canvasElement }) => {
|
|
67
|
+
await assertVisualizationRendered(canvasElement)
|
|
61
68
|
}
|
|
62
69
|
}
|
|
63
70
|
|
|
@@ -3,6 +3,7 @@ import annotationConfig from './_mock/annotation_category_mock.json'
|
|
|
3
3
|
import annotationConfigDateLinear from './_mock/annotation_date-linear_mock.json'
|
|
4
4
|
import annotationConfigDateTime from './_mock/annotation_date-time_mock.json'
|
|
5
5
|
import Chart from '../CdcChartComponent'
|
|
6
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
6
7
|
|
|
7
8
|
const meta: Meta<typeof Chart> = {
|
|
8
9
|
title: 'Components/Templates/Chart/Annotation',
|
|
@@ -15,6 +16,9 @@ export const Chart_Annotation_Categorical: Story = {
|
|
|
15
16
|
args: {
|
|
16
17
|
config: annotationConfig,
|
|
17
18
|
isEditor: true
|
|
19
|
+
},
|
|
20
|
+
play: async ({ canvasElement }) => {
|
|
21
|
+
await assertVisualizationRendered(canvasElement)
|
|
18
22
|
}
|
|
19
23
|
}
|
|
20
24
|
|
|
@@ -22,6 +26,9 @@ export const Chart_Annotation_Date_Linear: Story = {
|
|
|
22
26
|
args: {
|
|
23
27
|
config: annotationConfigDateLinear,
|
|
24
28
|
isEditor: true
|
|
29
|
+
},
|
|
30
|
+
play: async ({ canvasElement }) => {
|
|
31
|
+
await assertVisualizationRendered(canvasElement)
|
|
25
32
|
}
|
|
26
33
|
}
|
|
27
34
|
|
|
@@ -29,6 +36,9 @@ export const Chart_Annotation_Date_Time: Story = {
|
|
|
29
36
|
args: {
|
|
30
37
|
config: annotationConfigDateTime,
|
|
31
38
|
isEditor: true
|
|
39
|
+
},
|
|
40
|
+
play: async ({ canvasElement }) => {
|
|
41
|
+
await assertVisualizationRendered(canvasElement)
|
|
32
42
|
}
|
|
33
43
|
}
|
|
34
44
|
|
|
@@ -3,6 +3,7 @@ import SimplifiedLineConfig from './_mock/simplified_line.json'
|
|
|
3
3
|
|
|
4
4
|
import Chart from '../CdcChartComponent'
|
|
5
5
|
import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
|
|
6
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
6
7
|
|
|
7
8
|
const meta: Meta<typeof Chart> = {
|
|
8
9
|
title: 'Components/Templates/Chart/Axis Labels',
|
|
@@ -17,6 +18,9 @@ export const Abbreviated_Dates: Story = {
|
|
|
17
18
|
{ path: ['xAxis', 'showYearsOnce'], value: true },
|
|
18
19
|
{ path: ['tooltips', 'dateDisplayFormat'], value: '%b. %d %Y' }
|
|
19
20
|
])
|
|
21
|
+
},
|
|
22
|
+
play: async ({ canvasElement }) => {
|
|
23
|
+
await assertVisualizationRendered(canvasElement)
|
|
20
24
|
}
|
|
21
25
|
}
|
|
22
26
|
|
|
@@ -4,6 +4,7 @@ import longXLabelsConfig from './_mock/large_x_axis_labels.json'
|
|
|
4
4
|
import pairedBarConfig from './_mock/paired-bar.json'
|
|
5
5
|
import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
|
|
6
6
|
import { ChartConfig } from '../types/ChartConfig'
|
|
7
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
7
8
|
|
|
8
9
|
const meta: Meta<typeof Chart> = {
|
|
9
10
|
title: 'Components/Templates/Chart/Axis Titles',
|
|
@@ -17,6 +18,9 @@ type Story = StoryObj<typeof Chart>
|
|
|
17
18
|
export const Dynamic_Labels: Story = {
|
|
18
19
|
args: {
|
|
19
20
|
config: editConfigKeys(longXLabelsConfig, [{ path: ['xAxis', 'label'], value: 'This is the title' }])
|
|
21
|
+
},
|
|
22
|
+
play: async ({ canvasElement }) => {
|
|
23
|
+
await assertVisualizationRendered(canvasElement)
|
|
20
24
|
}
|
|
21
25
|
}
|
|
22
26
|
|
|
@@ -43,11 +47,17 @@ export const Rotated_Labels: StoryObj<{ config: ChartConfig; tickRotation: numbe
|
|
|
43
47
|
])
|
|
44
48
|
|
|
45
49
|
return <Chart config={config} />
|
|
50
|
+
},
|
|
51
|
+
play: async ({ canvasElement }) => {
|
|
52
|
+
await assertVisualizationRendered(canvasElement)
|
|
46
53
|
}
|
|
47
54
|
}
|
|
48
55
|
|
|
49
56
|
export const Paired_Bar: Story = {
|
|
50
57
|
args: {
|
|
51
58
|
config: pairedBarConfig
|
|
59
|
+
},
|
|
60
|
+
play: async ({ canvasElement }) => {
|
|
61
|
+
await assertVisualizationRendered(canvasElement)
|
|
52
62
|
}
|
|
53
63
|
}
|
|
@@ -2388,10 +2388,9 @@ export const BarFiltersTests: Story = {
|
|
|
2388
2388
|
const chartContainer = canvasElement.querySelector('.cove-component__content, .chart-container, .visualization')
|
|
2389
2389
|
const svg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
2390
2390
|
const bars = svg?.querySelectorAll('rect[class*="bar"], rect[data-testid*="bar"], g[class*="bar"] rect') || []
|
|
2391
|
-
const filtersList = canvasElement.querySelector('.
|
|
2391
|
+
const filtersList = canvasElement.querySelector('.draggable-field-list')
|
|
2392
2392
|
|
|
2393
|
-
|
|
2394
|
-
const filterElements = filtersList?.querySelectorAll('div.mb-1, fieldset.edit-block.mb-1') || []
|
|
2393
|
+
const filterElements = filtersList?.querySelectorAll('.editor-field-item') || []
|
|
2395
2394
|
|
|
2396
2395
|
// Get actual data visualization state for filtering verification
|
|
2397
2396
|
// Method 1: Try X-axis tick labels (most direct)
|
|
@@ -2565,9 +2564,15 @@ export const BarFiltersTests: Story = {
|
|
|
2565
2564
|
'Apply Filter Value - Chart data visually filtered to show only Q2',
|
|
2566
2565
|
getChartDataState,
|
|
2567
2566
|
async () => {
|
|
2568
|
-
// Find
|
|
2569
|
-
const
|
|
2570
|
-
|
|
2567
|
+
// Find all "Filter Default Value (category)" dropdowns
|
|
2568
|
+
const filterDefaultValueSelects = canvas.getAllByLabelText(
|
|
2569
|
+
/filter default value \(category\)/i
|
|
2570
|
+
) as HTMLSelectElement[]
|
|
2571
|
+
// Select the dropdown that contains Q2 as an option
|
|
2572
|
+
const filterDefaultValueSelect = filterDefaultValueSelects.find(select =>
|
|
2573
|
+
Array.from(select.options).some(opt => opt.value === 'Q2')
|
|
2574
|
+
)
|
|
2575
|
+
if (!filterDefaultValueSelect) throw new Error('Could not find filter default value dropdown for Q2')
|
|
2571
2576
|
// Select Q2 to filter the chart to only show Q2 data (different from current state)
|
|
2572
2577
|
await userEvent.selectOptions(filterDefaultValueSelect, 'Q2')
|
|
2573
2578
|
},
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { within, userEvent, expect } from 'storybook/test'
|
|
3
|
+
import Chart from '../CdcChartComponent'
|
|
4
|
+
|
|
5
|
+
// Import testing helpers following best practices document
|
|
6
|
+
import { openAccordion, performAndAssert, waitForEditor, waitForPresence } from '@cdc/core/helpers/testing'
|
|
7
|
+
|
|
8
|
+
// Import working brush configuration
|
|
9
|
+
import brushEnabledConfig from './_mock/brush_enabled.json'
|
|
10
|
+
|
|
11
|
+
const meta: Meta<typeof Chart> = {
|
|
12
|
+
title: 'Components/Templates/Chart/Editor Tests/Brush',
|
|
13
|
+
component: Chart
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default meta
|
|
17
|
+
type Story = StoryObj<typeof Chart>
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// BRUSH CHART EDITOR TESTS
|
|
21
|
+
// Tests the Brush Slider features in the Date/Category Axis accordion section
|
|
22
|
+
// Following best practices:
|
|
23
|
+
// - Tests visualization output changes, not control state
|
|
24
|
+
// - Uses performAndAssert pattern for all interactions
|
|
25
|
+
// - Tests specific visual changes in the brush selection
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
export const BrushDefaultSelectionTests: Story = {
|
|
29
|
+
name: 'Default Selection Mode Tests',
|
|
30
|
+
parameters: {
|
|
31
|
+
test: {
|
|
32
|
+
timeout: 30000
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
args: {
|
|
36
|
+
config: {
|
|
37
|
+
...brushEnabledConfig,
|
|
38
|
+
xAxis: {
|
|
39
|
+
...brushEnabledConfig.xAxis,
|
|
40
|
+
brushActive: true,
|
|
41
|
+
// New property: when set, shows last X data points instead of 35%
|
|
42
|
+
brushDefaultRecentDateCount: undefined
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
isEditor: true
|
|
46
|
+
},
|
|
47
|
+
play: async ({ canvasElement }) => {
|
|
48
|
+
const canvas = within(canvasElement)
|
|
49
|
+
await waitForEditor(canvas)
|
|
50
|
+
await openAccordion(canvas, 'Date/Category Axis')
|
|
51
|
+
|
|
52
|
+
// ============================================================================
|
|
53
|
+
// TEST: Brush Slider Toggle
|
|
54
|
+
// Verifies: Brush slider appears/disappears in the visualization
|
|
55
|
+
// ============================================================================
|
|
56
|
+
|
|
57
|
+
const getBrushVisibility = () => {
|
|
58
|
+
const brushSvg = canvasElement.querySelector('svg[style*="border: 1px solid"]')
|
|
59
|
+
const brushRect = canvasElement.querySelector('.visx-brush rect')
|
|
60
|
+
return {
|
|
61
|
+
hasBrushSvg: !!brushSvg,
|
|
62
|
+
hasBrushRect: !!brushRect,
|
|
63
|
+
brushContainerVisible: !!canvasElement.querySelector('[class*="brush"]')
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Verify brush is initially visible since config has brushActive: true
|
|
68
|
+
const initialBrushState = getBrushVisibility()
|
|
69
|
+
expect(initialBrushState.hasBrushSvg || initialBrushState.brushContainerVisible).toBe(true)
|
|
70
|
+
|
|
71
|
+
// Find and toggle the brush checkbox
|
|
72
|
+
const brushCheckbox = canvas.getByLabelText(/show brush slider/i) as HTMLInputElement
|
|
73
|
+
expect(brushCheckbox).toBeTruthy()
|
|
74
|
+
expect(brushCheckbox.checked).toBe(true)
|
|
75
|
+
|
|
76
|
+
// Toggle brush off and verify it disappears from visualization
|
|
77
|
+
await performAndAssert(
|
|
78
|
+
'Brush Slider Toggle Off',
|
|
79
|
+
getBrushVisibility,
|
|
80
|
+
async () => await userEvent.click(brushCheckbox),
|
|
81
|
+
(before, after) => {
|
|
82
|
+
// Either brush SVG or brush container should disappear
|
|
83
|
+
return (
|
|
84
|
+
(before.hasBrushSvg && !after.hasBrushSvg) || (before.brushContainerVisible && !after.brushContainerVisible)
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
// Toggle brush back on
|
|
90
|
+
await performAndAssert(
|
|
91
|
+
'Brush Slider Toggle On',
|
|
92
|
+
getBrushVisibility,
|
|
93
|
+
async () => await userEvent.click(brushCheckbox),
|
|
94
|
+
(before, after) => {
|
|
95
|
+
return (
|
|
96
|
+
(!before.hasBrushSvg && after.hasBrushSvg) || (!before.brushContainerVisible && after.brushContainerVisible)
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// TEST: Default Recent Date Count Input
|
|
103
|
+
// Verifies: When "Show last X dates" is set, the brush selection changes
|
|
104
|
+
// to show exactly X data points instead of the default 35%
|
|
105
|
+
// ============================================================================
|
|
106
|
+
|
|
107
|
+
// This control should appear when brush is enabled
|
|
108
|
+
// Look for the new "Show last X dates by default" input
|
|
109
|
+
const recentDateCountInput = canvas.getByLabelText(/show last.*dates.*default/i) as HTMLInputElement
|
|
110
|
+
|
|
111
|
+
// If the control doesn't exist yet (TDD - test before implementation),
|
|
112
|
+
// the test will fail here, indicating we need to implement this feature
|
|
113
|
+
expect(recentDateCountInput).toBeTruthy()
|
|
114
|
+
expect(recentDateCountInput).toHaveAttribute('type', 'number')
|
|
115
|
+
|
|
116
|
+
// ============================================================================
|
|
117
|
+
// TEST: Setting Recent Date Count Dynamically Updates Brush Selection
|
|
118
|
+
// Verifies: Changing the value in the editor immediately updates the brush
|
|
119
|
+
// ============================================================================
|
|
120
|
+
|
|
121
|
+
const getBrushSelectionState = () => {
|
|
122
|
+
// The brush selection is represented by the visx-brush extent rect
|
|
123
|
+
const brushExtent = canvasElement.querySelector('.visx-brush rect[class*="selection"]') as SVGRectElement
|
|
124
|
+
const brushSvg = canvasElement.querySelector('svg[style*="border: 1px solid"]') as SVGSVGElement
|
|
125
|
+
|
|
126
|
+
// Get the visible data points in the main chart (lines or bars)
|
|
127
|
+
const chartSvg = canvasElement.querySelector('.linear-chart svg, .cove-chart svg') as SVGSVGElement
|
|
128
|
+
const dataPointsInChart = chartSvg?.querySelectorAll('circle, rect:not([fill="transparent"])').length || 0
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
brushWidth: brushExtent ? parseFloat(brushExtent.getAttribute('width') || '0') : 0,
|
|
132
|
+
brushX: brushExtent ? parseFloat(brushExtent.getAttribute('x') || '0') : 0,
|
|
133
|
+
totalBrushWidth: brushSvg ? brushSvg.clientWidth : 0,
|
|
134
|
+
visibleDataPoints: dataPointsInChart,
|
|
135
|
+
selectionPercentage:
|
|
136
|
+
brushExtent && brushSvg
|
|
137
|
+
? (parseFloat(brushExtent.getAttribute('width') || '0') / brushSvg.clientWidth) * 100
|
|
138
|
+
: 0
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const initialSelectionState = getBrushSelectionState()
|
|
143
|
+
|
|
144
|
+
// Default is ~35% of the width
|
|
145
|
+
// With 329 data points in brush_enabled.json, 35% would be ~115 points
|
|
146
|
+
// Setting to 30 should make the selection much narrower AND update immediately
|
|
147
|
+
await performAndAssert(
|
|
148
|
+
'Set Default Recent Date Count to 30 - Brush Updates Dynamically',
|
|
149
|
+
getBrushSelectionState,
|
|
150
|
+
async () => {
|
|
151
|
+
await userEvent.clear(recentDateCountInput)
|
|
152
|
+
await userEvent.type(recentDateCountInput, '30')
|
|
153
|
+
// Trigger change/blur to apply the value - this should update the brush immediately
|
|
154
|
+
await userEvent.tab()
|
|
155
|
+
},
|
|
156
|
+
(before, after) => {
|
|
157
|
+
// The brush selection width should decrease when we set a smaller count
|
|
158
|
+
// Since we have ~329 data points and default is 35% (~115 points),
|
|
159
|
+
// setting to 30 points should make the selection narrower
|
|
160
|
+
return after.brushWidth < before.brushWidth || after.selectionPercentage < before.selectionPercentage
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
// ============================================================================
|
|
165
|
+
// TEST: Changing Value Again Updates Selection Again
|
|
166
|
+
// Verifies: Multiple changes continue to update the brush dynamically
|
|
167
|
+
// ============================================================================
|
|
168
|
+
|
|
169
|
+
await performAndAssert(
|
|
170
|
+
'Change to 50 dates - Brush Expands Dynamically',
|
|
171
|
+
getBrushSelectionState,
|
|
172
|
+
async () => {
|
|
173
|
+
await userEvent.clear(recentDateCountInput)
|
|
174
|
+
await userEvent.type(recentDateCountInput, '50')
|
|
175
|
+
await userEvent.tab()
|
|
176
|
+
},
|
|
177
|
+
(before, after) => {
|
|
178
|
+
// Changing from 30 to 50 should expand the brush width
|
|
179
|
+
return after.brushWidth > before.brushWidth
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
// ============================================================================
|
|
184
|
+
// TEST: Verify Exact Data Point Count in Selection
|
|
185
|
+
// Verifies: The number of selected data points matches the input value
|
|
186
|
+
// ============================================================================
|
|
187
|
+
|
|
188
|
+
// The brush selection should now show exactly 30 data points
|
|
189
|
+
// We can verify this by checking the filtered data in the visualization
|
|
190
|
+
const getSelectedDataPointCount = () => {
|
|
191
|
+
// When brush is active, only the selected data points are rendered in the main chart
|
|
192
|
+
// Look for the number of data points (circles, bars, or line path points)
|
|
193
|
+
const chartContainer = canvasElement.querySelector('.linear-chart, .cove-chart')
|
|
194
|
+
const svg = chartContainer?.querySelector('svg')
|
|
195
|
+
|
|
196
|
+
// For line charts, count the line path data points
|
|
197
|
+
const linePaths = svg?.querySelectorAll('path[class*="line"], .visx-linepath')
|
|
198
|
+
let dataPointCount = 0
|
|
199
|
+
|
|
200
|
+
if (linePaths && linePaths.length > 0) {
|
|
201
|
+
// Count points in the path by looking at the rendered circles
|
|
202
|
+
const circles = svg?.querySelectorAll('circle')
|
|
203
|
+
dataPointCount = circles?.length || 0
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// For bar charts
|
|
207
|
+
const bars = svg?.querySelectorAll('rect[class*="bar"]')
|
|
208
|
+
if (bars && bars.length > 0) {
|
|
209
|
+
dataPointCount = bars.length
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
dataPointCount,
|
|
214
|
+
hasData: dataPointCount > 0
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// After setting to 30, the chart should show ~30 data points
|
|
219
|
+
// (exact count may vary based on how brush boundaries align with data points)
|
|
220
|
+
const afterSettingCount = getSelectedDataPointCount()
|
|
221
|
+
|
|
222
|
+
// The count should be close to 30 (allowing some tolerance for edge cases)
|
|
223
|
+
// Note: This assertion helps verify the feature works - if it fails,
|
|
224
|
+
// the implementation needs adjustment
|
|
225
|
+
if (afterSettingCount.hasData) {
|
|
226
|
+
expect(afterSettingCount.dataPointCount).toBeGreaterThan(0)
|
|
227
|
+
expect(afterSettingCount.dataPointCount).toBeLessThanOrEqual(35) // 30 + tolerance
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ============================================================================
|
|
231
|
+
// TEST: Clear Recent Date Count Returns to Default 35%
|
|
232
|
+
// Verifies: When the count is cleared, brush returns to percentage-based default
|
|
233
|
+
// ============================================================================
|
|
234
|
+
|
|
235
|
+
await performAndAssert(
|
|
236
|
+
'Clear Default Recent Date Count (return to 35%)',
|
|
237
|
+
getBrushSelectionState,
|
|
238
|
+
async () => {
|
|
239
|
+
await userEvent.clear(recentDateCountInput)
|
|
240
|
+
await userEvent.tab()
|
|
241
|
+
},
|
|
242
|
+
(before, after) => {
|
|
243
|
+
// The selection should return to approximately 35% of the total width
|
|
244
|
+
// Since we had set it to 30 points (~9% with 329 data points),
|
|
245
|
+
// clearing should expand it back to ~35%
|
|
246
|
+
return after.brushWidth > before.brushWidth || after.selectionPercentage > before.selectionPercentage
|
|
247
|
+
}
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ============================================================================
|
|
253
|
+
// BRUSH SECTION ACCESSIBILITY TEST
|
|
254
|
+
// Verifies the brush controls are properly labeled and accessible
|
|
255
|
+
// ============================================================================
|
|
256
|
+
|
|
257
|
+
export const BrushAccessibilityTests: Story = {
|
|
258
|
+
name: 'Accessibility Tests',
|
|
259
|
+
parameters: {
|
|
260
|
+
test: {
|
|
261
|
+
timeout: 20000
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
args: {
|
|
265
|
+
config: {
|
|
266
|
+
...brushEnabledConfig,
|
|
267
|
+
xAxis: {
|
|
268
|
+
...brushEnabledConfig.xAxis,
|
|
269
|
+
brushActive: true
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
isEditor: true
|
|
273
|
+
},
|
|
274
|
+
play: async ({ canvasElement }) => {
|
|
275
|
+
const canvas = within(canvasElement)
|
|
276
|
+
await waitForEditor(canvas)
|
|
277
|
+
await openAccordion(canvas, 'Date/Category Axis')
|
|
278
|
+
|
|
279
|
+
// ============================================================================
|
|
280
|
+
// TEST: Brush Controls Have Proper Labels
|
|
281
|
+
// ============================================================================
|
|
282
|
+
|
|
283
|
+
// The brush slider checkbox should be findable by its label
|
|
284
|
+
const brushCheckbox = canvas.getByLabelText(/show brush slider/i)
|
|
285
|
+
expect(brushCheckbox).toBeTruthy()
|
|
286
|
+
expect(brushCheckbox).toHaveAttribute('type', 'checkbox')
|
|
287
|
+
|
|
288
|
+
// The recent date count input should have a descriptive label
|
|
289
|
+
const recentDateLabel = canvas.getByLabelText(/show last.*dates.*default/i)
|
|
290
|
+
|
|
291
|
+
// If this test fails, it indicates the control needs a proper accessible label
|
|
292
|
+
expect(recentDateLabel).toBeTruthy()
|
|
293
|
+
expect(recentDateLabel).toHaveAttribute('type', 'number')
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import Chart from '../CdcChartComponent'
|
|
3
|
+
import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
|
|
4
|
+
import brushContinuousConfig from './_mock/brush_continuous.json'
|
|
5
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Chart> = {
|
|
8
|
+
title: 'Components/Templates/Chart/BrushSlider/Matrix/Continuous',
|
|
9
|
+
component: Chart
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default meta
|
|
13
|
+
|
|
14
|
+
type Story = StoryObj<typeof Chart>
|
|
15
|
+
|
|
16
|
+
export const Default: Story = {
|
|
17
|
+
args: { config: brushContinuousConfig, isEditor: false },
|
|
18
|
+
parameters: {
|
|
19
|
+
docs: {
|
|
20
|
+
description: { story: 'xAxis.type = "continuous", default ascending numeric order.' }
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
play: async ({ canvasElement }) => {
|
|
24
|
+
await assertVisualizationRendered(canvasElement)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const Reversed: Story = {
|
|
29
|
+
args: {
|
|
30
|
+
config: editConfigKeys(brushContinuousConfig, [{ path: ['xAxis', 'sortByRecentDate'], value: true }]),
|
|
31
|
+
isEditor: false
|
|
32
|
+
},
|
|
33
|
+
parameters: {
|
|
34
|
+
docs: {
|
|
35
|
+
description: { story: 'xAxis.type = "continuous", sortByRecentDate = true.' }
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
play: async ({ canvasElement }) => {
|
|
39
|
+
await assertVisualizationRendered(canvasElement)
|
|
40
|
+
}
|
|
41
|
+
}
|