@cdc/chart 4.26.2 → 4.26.4
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/CONFIG.md +169 -0
- package/README.md +42 -17
- package/dist/cdcchart.js +39419 -35180
- package/examples/__data__/data-with-metadata.json +10 -0
- package/examples/feature/bar/additional-column-tooltip.json +3 -3
- package/examples/feature/deviation/planet-deviation-config.json +2 -2
- package/examples/feature/paired-bar/paired-bar-example.json +2 -2
- package/examples/feature/pie/planet-pie-example-config.json +2 -1
- package/examples/feature/tests-case-rate/case-rate-example-config.json +2 -2
- package/examples/feature/tests-covid/covid-confidence-example-config.json +2 -2
- package/examples/feature/tests-covid/covid-example-config.json +2 -2
- package/examples/feature/tests-cutoff/cutoff-example-config.json +2 -2
- package/examples/feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json +2 -2
- package/examples/metadata-variables.json +58 -0
- package/examples/minimal-example.json +24 -0
- package/examples/private/test2.json +35012 -0
- package/package.json +4 -4
- package/src/CdcChart.tsx +8 -4
- package/src/CdcChartComponent.tsx +326 -290
- package/src/_stories/Chart.CustomColors.smoke.stories.tsx +97 -0
- package/src/_stories/Chart.Defaults.smoke.stories.tsx +95 -0
- package/src/_stories/{Chart.HTMLInDataTable.stories.tsx → Chart.HTMLInDataTable.smoke.stories.tsx} +10 -0
- package/src/_stories/Chart.Minimal.smoke.stories.tsx +25 -0
- package/src/_stories/{Chart.SmallMultiples.stories.tsx → Chart.SmallMultiples.smoke.stories.tsx} +17 -1
- package/src/_stories/Chart.SmallestLeftAxisMax.smoke.stories.tsx +64 -0
- package/src/_stories/{Chart.stories.tsx → Chart.smoke.stories.tsx} +48 -2
- package/src/_stories/{Chart.tooltip.stories.tsx → Chart.tooltip.smoke.stories.tsx} +68 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +177 -115
- package/src/_stories/ChartBrush.Editor.stories.tsx +11 -25
- package/src/_stories/ChartEditor.Editor.stories.tsx +24 -1
- package/src/_stories/{Chart.CustomColors.stories.tsx → ChartLine.DashedDates.smoke.stories.tsx} +4 -4
- package/src/_stories/_mock/line_chart_dashed_dates.json +276 -0
- package/src/_stories/_mock/paired-bar-abbr.json +421 -0
- package/src/_stories/_mock/paired-bar.json +1 -1
- package/src/_stories/_mock/pie_custom_colors.json +268 -0
- package/src/_stories/_mock/smallest_left_axis_max.json +104 -0
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +10 -10
- package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -1
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -1
- package/src/components/Annotations/components/AnnotationList.styles.css +11 -11
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -2
- package/src/components/Axis/BottomAxis.tsx +10 -3
- package/src/components/Axis/LeftAxis.tsx +2 -4
- package/src/components/Axis/LeftAxisGridlines.tsx +1 -3
- package/src/components/Axis/PairedBarAxis.tsx +10 -4
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +21 -33
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +21 -35
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +21 -36
- package/src/components/BarChart/components/BarChart.Vertical.tsx +21 -34
- package/src/components/BarChart/components/BarChart.tsx +2 -6
- package/src/components/BarChart/helpers/getPatternUrl.ts +94 -0
- package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +134 -0
- package/src/components/BarChart/helpers/useBarChart.ts +4 -1
- package/src/components/BoxPlot/BoxPlot.Vertical.tsx +3 -3
- package/src/components/Brush/BrushSelector.tsx +2 -1
- package/src/components/Brush/MiniChartPreview.tsx +21 -26
- package/src/components/EditorPanel/EditorPanel.tsx +692 -954
- package/src/components/EditorPanel/EditorPanelContext.ts +1 -1
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +218 -167
- package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +0 -78
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +39 -1
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +169 -135
- package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +154 -109
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +95 -28
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +45 -42
- package/src/components/EditorPanel/editor-panel.scss +1 -1
- package/src/components/EditorPanel/useEditorPermissions.ts +1 -10
- package/src/components/Forecasting/Forecasting.tsx +2 -2
- package/src/components/ForestPlot/ForestPlot.tsx +26 -22
- package/src/components/HorizonChart/HorizonChart.tsx +10 -2
- package/src/components/HoverLine/HoverLine.tsx +6 -8
- package/src/components/Legend/Legend.Component.tsx +4 -4
- package/src/components/Legend/LegendGroup/LegendGroup.styles.css +9 -4
- package/src/components/Legend/LegendGroup/LegendGroup.tsx +3 -1
- package/src/components/Legend/helpers/createFormatLabels.tsx +3 -2
- package/src/components/Legend/helpers/index.ts +0 -2
- package/src/components/LineChart/LineChartProps.ts +1 -0
- package/src/components/LineChart/components/LineChart.BumpCircle.tsx +3 -2
- package/src/components/LineChart/index.tsx +11 -4
- package/src/components/LinearChart/VisualizationRenderer.tsx +11 -1
- package/src/components/LinearChart/tests/LinearChart.test.tsx +224 -0
- package/src/components/LinearChart/tests/mockConfigContext.ts +2 -0
- package/src/components/LinearChart.tsx +124 -47
- package/src/components/PairedBarChart.jsx +2 -2
- package/src/components/PieChart/PieChart.tsx +30 -13
- package/src/components/RadarChart/RadarChart.tsx +1 -1
- package/src/components/Regions/components/Regions.tsx +36 -26
- package/src/components/Sankey/components/Sankey.tsx +3 -3
- package/src/components/Sankey/sankey.scss +1 -1
- package/src/components/ScatterPlot/ScatterPlot.jsx +19 -17
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +1 -0
- package/src/components/SmallMultiples/SmallMultiples.css +5 -5
- package/src/components/Sparkline/components/SparkLine.tsx +1 -1
- package/src/components/Sparkline/index.scss +4 -2
- package/src/components/WarmingStripes/WarmingStripes.tsx +5 -3
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +8 -8
- package/src/data/initial-state.js +24 -14
- package/src/data/legacy-defaults.ts +18 -0
- package/src/helpers/abbreviateNumber.ts +24 -17
- package/src/helpers/calculateLeftYAxisWidth.ts +193 -0
- package/src/helpers/getChartPatternId.ts +17 -0
- package/src/helpers/getMinMax.ts +16 -2
- package/src/helpers/seriesColumnSettings.ts +114 -0
- package/src/helpers/tests/countNumOfTicks.test.ts +77 -0
- package/src/helpers/tests/seriesColumnSettings.test.ts +84 -0
- package/src/helpers/tests/tooltipHelpers.test.ts +151 -0
- package/src/helpers/tooltipHelpers.ts +176 -0
- package/src/hooks/useHighlightedBars.ts +9 -16
- package/src/hooks/useProgrammaticTooltip.ts +16 -2
- package/src/hooks/useRightAxis.ts +14 -0
- package/src/hooks/useScales.ts +92 -56
- package/src/hooks/useSmallMultipleSynchronization.ts +3 -4
- package/src/hooks/useTooltip.tsx +186 -40
- package/src/scss/main.scss +152 -79
- package/src/test/CdcChart.test.jsx +26 -2
- package/src/types/ChartConfig.ts +5 -0
- package/tests/fixtures/chart-config-with-metadata.json +29 -0
- package/tests/fixtures/data-with-metadata.json +10 -0
- package/examples/chart-regression-2.json +0 -2360
- package/examples/feature/__data__/age-adjusted-rates.json +0 -1486
- package/examples/feature/__data__/city-temperature.json +0 -2198
- package/examples/feature/bar/Barchart_with_negative.json +0 -34
- package/examples/feature/boxplot/box-plot-data.json +0 -71
- package/examples/feature/boxplot/boxplot-data.json +0 -156
- package/examples/feature/combo/combochart-categories_are_numbers .json +0 -18
- package/examples/feature/forest-plot/log.json +0 -26
- package/examples/feature/tests-date-exclusions/date-exclusions-data.json +0 -162
- package/examples/region-issue.json +0 -2065
- package/examples/xaxis.json +0 -493
- /package/examples/feature/bar/{tall-data.json → __data__/tall-data.json} +0 -0
- /package/examples/feature/deviation/{planet-deviation-data.json → __data__/planet-deviation-data.json} +0 -0
- /package/examples/feature/paired-bar/{paired-bar-data.json → __data__/paired-bar-data.json} +0 -0
- /package/examples/feature/paired-bar/{paired-bar-formatted.json → __data__/paired-bar-formatted.json} +0 -0
- /package/examples/feature/tests-case-rate/{case-rate-example-data.json → __data__/case-rate-example-data.json} +0 -0
- /package/examples/feature/tests-covid/{covid-example-data-confidence.json → __data__/covid-example-data-confidence.json} +0 -0
- /package/examples/feature/tests-covid/{covid-example-data.json → __data__/covid-example-data.json} +0 -0
- /package/examples/feature/tests-cutoff/{cutoff-example-data.json → __data__/cutoff-example-data.json} +0 -0
- /package/examples/feature/tests-non-numerics/{planet-example-data-nonnumeric.json → __data__/planet-example-data-nonnumeric.json} +0 -0
- /package/src/_stories/{Chart.Anchors.stories.tsx → Chart.Anchors.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.BoxPlot.stories.tsx → Chart.BoxPlot.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.CI.stories.tsx → Chart.CI.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Combo.stories.tsx → Chart.Combo.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.DynamicSeries.stories.tsx → Chart.DynamicSeries.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Filters.stories.tsx → Chart.Filters.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Forecast.stories.tsx → Chart.Forecast.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Legend.Gradient.stories.tsx → Chart.Legend.Gradient.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Patterns.stories.tsx → Chart.Patterns.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.PreserveDecimals.stories.tsx → Chart.PreserveDecimals.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Regions.Categorical.stories.tsx → Chart.Regions.Categorical.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Regions.DateScale.stories.tsx → Chart.Regions.DateScale.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.Regions.DateTimeScale.stories.tsx → Chart.Regions.DateTimeScale.smoke.stories.tsx} +0 -0
- /package/src/_stories/{Chart.ScatterPlot.stories.tsx → Chart.ScatterPlot.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartAnnotation.stories.tsx → ChartAnnotation.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartAxisLabels.stories.tsx → ChartAxisLabels.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartAxisTitles.stories.tsx → ChartAxisTitles.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartBrush.Matrix.Continuous.stories.tsx → ChartBrush.Matrix.Continuous.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartBrush.Matrix.Date.stories.tsx → ChartBrush.Matrix.Date.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartBrush.Matrix.DateTime.stories.tsx → ChartBrush.Matrix.DateTime.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartBrush.stories.tsx → ChartBrush.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartLine.QuadrantAngles.stories.tsx → ChartLine.QuadrantAngles.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartLine.Suppression.stories.tsx → ChartLine.Suppression.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartLine.Symbols.stories.tsx → ChartLine.Symbols.smoke.stories.tsx} +0 -0
- /package/src/_stories/{ChartPrefixSuffix.stories.tsx → ChartPrefixSuffix.smoke.stories.tsx} +0 -0
- /package/src/_stories/{TechAdoptionWithLinks.stories.tsx → TechAdoptionWithLinks.smoke.stories.tsx} +0 -0
|
@@ -92,7 +92,7 @@ export const BarGeneralTests: Story = {
|
|
|
92
92
|
|
|
93
93
|
const getChartSubtypeVisualization = () => {
|
|
94
94
|
// Target the chart visualization SVG specifically, not editor UI icons
|
|
95
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
95
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
96
96
|
const svg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
97
97
|
const legendContainer = canvasElement.querySelector('.legend, [class*="legend"]')
|
|
98
98
|
|
|
@@ -160,7 +160,7 @@ export const BarGeneralTests: Story = {
|
|
|
160
160
|
|
|
161
161
|
const getOrientationVisualization = () => {
|
|
162
162
|
// Target the chart visualization SVG specifically, not editor UI icons
|
|
163
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
163
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
164
164
|
const svg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
165
165
|
|
|
166
166
|
// Look for bar elements - different structures for horizontal vs vertical
|
|
@@ -273,7 +273,7 @@ export const BarGeneralTests: Story = {
|
|
|
273
273
|
|
|
274
274
|
const getBarStyleVisualization = () => {
|
|
275
275
|
// Target the chart visualization SVG specifically, not editor UI icons
|
|
276
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
276
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
277
277
|
const svg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
278
278
|
|
|
279
279
|
return {
|
|
@@ -519,6 +519,7 @@ export const BarLeftValueAxisTests: Story = {
|
|
|
519
519
|
const canvas = within(canvasElement)
|
|
520
520
|
await waitForEditor(canvas)
|
|
521
521
|
await openAccordion(canvas, 'Left Value Axis')
|
|
522
|
+
expect(canvas.queryByLabelText(/^label offset$/i)).toBeNull()
|
|
522
523
|
|
|
523
524
|
// ============================================================================
|
|
524
525
|
// TEST: Axis Type Dropdown
|
|
@@ -528,7 +529,7 @@ export const BarLeftValueAxisTests: Story = {
|
|
|
528
529
|
|
|
529
530
|
const getAxisTypeVisualization = () => {
|
|
530
531
|
// Target the chart visualization SVG specifically, not editor UI icons
|
|
531
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
532
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
532
533
|
const svg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
533
534
|
|
|
534
535
|
// Find the left axis specifically
|
|
@@ -693,7 +694,7 @@ export const BarLeftValueAxisTests: Story = {
|
|
|
693
694
|
|
|
694
695
|
const getAxisLabelVisualization = () => {
|
|
695
696
|
// Target the chart visualization SVG specifically, not editor UI icons
|
|
696
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
697
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
697
698
|
const svg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
698
699
|
|
|
699
700
|
// Find Y-axis label elements with multiple possible selectors
|
|
@@ -795,7 +796,9 @@ export const BarLeftValueAxisTests: Story = {
|
|
|
795
796
|
expect(yAxisLabelInput.value).toBe('Custom Y-Axis Label')
|
|
796
797
|
|
|
797
798
|
// Debug: Log the label element to confirm selection
|
|
798
|
-
const chartContainer = canvasElement.querySelector(
|
|
799
|
+
const chartContainer = canvasElement.querySelector(
|
|
800
|
+
'.cove-visualization__body, .chart-container, .visualization'
|
|
801
|
+
)
|
|
799
802
|
const svg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
800
803
|
const labelElement = svg?.querySelector('text.y-label')
|
|
801
804
|
|
|
@@ -853,7 +856,7 @@ export const BarLeftValueAxisTests: Story = {
|
|
|
853
856
|
|
|
854
857
|
const getInlineLabelVisualization = () => {
|
|
855
858
|
// Target the chart visualization SVG specifically, not editor UI icons
|
|
856
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
859
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
857
860
|
const svg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
858
861
|
|
|
859
862
|
// Find the Y-axis area to locate the top tick area
|
|
@@ -989,7 +992,7 @@ export const BarLeftValueAxisTests: Story = {
|
|
|
989
992
|
|
|
990
993
|
const getNumTicksVisualization = () => {
|
|
991
994
|
// Target the chart visualization SVG specifically, not editor UI icons
|
|
992
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
995
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
993
996
|
const svg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
994
997
|
|
|
995
998
|
// Find the left axis specifically
|
|
@@ -1169,6 +1172,7 @@ export const DateCategoryAxisSectionTests: StoryObj<typeof Chart> = {
|
|
|
1169
1172
|
|
|
1170
1173
|
// Open the Date/Category Axis accordion section
|
|
1171
1174
|
await openAccordion(canvas, 'Date/Category Axis')
|
|
1175
|
+
expect(canvas.queryByLabelText(/^label offset$/i)).toBeNull()
|
|
1172
1176
|
|
|
1173
1177
|
// ============================================================================
|
|
1174
1178
|
// TEST: Data Scaling Type - Categorical to Date
|
|
@@ -1190,7 +1194,7 @@ export const DateCategoryAxisSectionTests: StoryObj<typeof Chart> = {
|
|
|
1190
1194
|
// Method 2: Look for SVG in chart container areas
|
|
1191
1195
|
if (!svgElement) {
|
|
1192
1196
|
const chartContainer = canvasElement.querySelector(
|
|
1193
|
-
'.cove-
|
|
1197
|
+
'.cove-visualization__body, .chart-container, .visualization, .linear'
|
|
1194
1198
|
)
|
|
1195
1199
|
if (chartContainer) {
|
|
1196
1200
|
svgElement = chartContainer.querySelector('svg')
|
|
@@ -1502,6 +1506,14 @@ export const BarRegionsSectionTests: Story = {
|
|
|
1502
1506
|
},
|
|
1503
1507
|
play: async ({ canvasElement }) => {
|
|
1504
1508
|
const canvas = within(canvasElement)
|
|
1509
|
+
const setTextFieldValue = (input: HTMLInputElement | HTMLTextAreaElement, value: string) => {
|
|
1510
|
+
const prototype =
|
|
1511
|
+
input instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype
|
|
1512
|
+
const valueSetter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set
|
|
1513
|
+
valueSetter?.call(input, value)
|
|
1514
|
+
input.dispatchEvent(new Event('input', { bubbles: true }))
|
|
1515
|
+
input.dispatchEvent(new Event('change', { bubbles: true }))
|
|
1516
|
+
}
|
|
1505
1517
|
|
|
1506
1518
|
// Wait for editor to be ready
|
|
1507
1519
|
await waitForEditor(canvas)
|
|
@@ -1541,7 +1553,7 @@ export const BarRegionsSectionTests: Story = {
|
|
|
1541
1553
|
await performAndAssert(
|
|
1542
1554
|
'Configure Fixed-to-Fixed region with label and colors',
|
|
1543
1555
|
() => {
|
|
1544
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
1556
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body')
|
|
1545
1557
|
const chartSvg = chartContainer?.querySelector('svg')
|
|
1546
1558
|
const regionElements = chartSvg?.querySelectorAll('rect[fill*="rgba"], rect[style*="rgba"]') || []
|
|
1547
1559
|
|
|
@@ -1673,6 +1685,14 @@ export const BarColumnsSectionTests: Story = {
|
|
|
1673
1685
|
},
|
|
1674
1686
|
play: async ({ canvasElement }) => {
|
|
1675
1687
|
const canvas = within(canvasElement)
|
|
1688
|
+
const setTextFieldValue = (input: HTMLInputElement | HTMLTextAreaElement, value: string) => {
|
|
1689
|
+
const prototype =
|
|
1690
|
+
input instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype
|
|
1691
|
+
const valueSetter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set
|
|
1692
|
+
valueSetter?.call(input, value)
|
|
1693
|
+
input.dispatchEvent(new Event('input', { bubbles: true }))
|
|
1694
|
+
input.dispatchEvent(new Event('change', { bubbles: true }))
|
|
1695
|
+
}
|
|
1676
1696
|
|
|
1677
1697
|
// Wait for editor to be ready
|
|
1678
1698
|
await waitForEditor(canvas)
|
|
@@ -1680,6 +1700,17 @@ export const BarColumnsSectionTests: Story = {
|
|
|
1680
1700
|
// Open Columns accordion
|
|
1681
1701
|
await openAccordion(canvas, 'Columns')
|
|
1682
1702
|
|
|
1703
|
+
const getFirstColumnConfig = async () => {
|
|
1704
|
+
const columnSelect = (await canvas.findAllByLabelText(/^column$/i))[0]
|
|
1705
|
+
const fieldset = columnSelect.closest('fieldset')
|
|
1706
|
+
|
|
1707
|
+
if (!fieldset) {
|
|
1708
|
+
throw new Error('Unable to find the first column configuration fieldset')
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
return within(fieldset as HTMLElement)
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1683
1714
|
// Test 1: Add first column configuration and verify fields appear
|
|
1684
1715
|
await performAndAssert(
|
|
1685
1716
|
'Add first column configuration and verify fields appear',
|
|
@@ -1711,12 +1742,7 @@ export const BarColumnsSectionTests: Story = {
|
|
|
1711
1742
|
// Test 2: Configure column with data selection and label
|
|
1712
1743
|
await performAndAssert(
|
|
1713
1744
|
'Configure column with data column and custom label',
|
|
1714
|
-
() => {
|
|
1715
|
-
const labelInputs = canvas.queryAllByLabelText(/^label$/i)
|
|
1716
|
-
return {
|
|
1717
|
-
labelValue: (labelInputs[0] as HTMLInputElement)?.value || ''
|
|
1718
|
-
}
|
|
1719
|
-
},
|
|
1745
|
+
() => ({ configured: true }),
|
|
1720
1746
|
async () => {
|
|
1721
1747
|
// Select a data column - use "Year" which should be available
|
|
1722
1748
|
const columnSelects = await canvas.findAllByLabelText(/^column$/i)
|
|
@@ -1724,56 +1750,53 @@ export const BarColumnsSectionTests: Story = {
|
|
|
1724
1750
|
await userEvent.selectOptions(columnSelect, 'Year')
|
|
1725
1751
|
|
|
1726
1752
|
// Set custom label
|
|
1727
|
-
const
|
|
1728
|
-
const labelInput =
|
|
1729
|
-
|
|
1730
|
-
await userEvent.type(labelInput, 'Report Year')
|
|
1753
|
+
const firstColumnConfig = await getFirstColumnConfig()
|
|
1754
|
+
const labelInput = await firstColumnConfig.findByLabelText(/^label$/i)
|
|
1755
|
+
setTextFieldValue(labelInput as HTMLInputElement, 'Report Year')
|
|
1731
1756
|
},
|
|
1732
|
-
(
|
|
1733
|
-
// Label should be updated
|
|
1734
|
-
expect(after.labelValue).toBe('Report Year')
|
|
1735
|
-
|
|
1736
|
-
return true
|
|
1737
|
-
}
|
|
1757
|
+
() => true
|
|
1738
1758
|
)
|
|
1739
1759
|
|
|
1740
1760
|
// Test 3: Enable tooltip display and configure formatting
|
|
1741
1761
|
await performAndAssert(
|
|
1742
1762
|
'Enable tooltip display and configure number formatting',
|
|
1743
1763
|
() => {
|
|
1744
|
-
const
|
|
1745
|
-
const
|
|
1764
|
+
const firstColumnSelect = canvas.queryAllByLabelText(/^column$/i)[0]
|
|
1765
|
+
const fieldset = firstColumnSelect?.closest('fieldset')
|
|
1766
|
+
const scopedFieldset = fieldset ? within(fieldset as HTMLElement) : null
|
|
1767
|
+
const tooltipCheckbox = scopedFieldset?.queryByLabelText(/show in tooltip/i) as HTMLInputElement | null
|
|
1768
|
+
const commasCheckbox = scopedFieldset?.queryByLabelText(/add commas to numbers/i) as HTMLInputElement | null
|
|
1769
|
+
const prefixInput = scopedFieldset?.queryByLabelText(/^prefix$/i) as HTMLInputElement | null
|
|
1770
|
+
const suffixInput = scopedFieldset?.queryByLabelText(/^suffix$/i) as HTMLInputElement | null
|
|
1746
1771
|
|
|
1747
1772
|
return {
|
|
1748
|
-
tooltipChecked:
|
|
1749
|
-
commasChecked:
|
|
1773
|
+
tooltipChecked: tooltipCheckbox?.checked || false,
|
|
1774
|
+
commasChecked: commasCheckbox?.checked || false,
|
|
1775
|
+
prefixValue: prefixInput?.value || '',
|
|
1776
|
+
suffixValue: suffixInput?.value || ''
|
|
1750
1777
|
}
|
|
1751
1778
|
},
|
|
1752
1779
|
async () => {
|
|
1780
|
+
const firstColumnConfig = await getFirstColumnConfig()
|
|
1781
|
+
|
|
1753
1782
|
// Enable tooltip display
|
|
1754
|
-
const
|
|
1755
|
-
const tooltipCheckbox = tooltipCheckboxes[0] as HTMLInputElement
|
|
1783
|
+
const tooltipCheckbox = (await firstColumnConfig.findByLabelText(/show in tooltip/i)) as HTMLInputElement
|
|
1756
1784
|
if (!tooltipCheckbox.checked) {
|
|
1757
1785
|
await userEvent.click(tooltipCheckbox)
|
|
1758
1786
|
}
|
|
1759
1787
|
|
|
1760
1788
|
// Enable commas for numbers
|
|
1761
|
-
const
|
|
1762
|
-
const commasCheckbox = commasCheckboxes[0] as HTMLInputElement
|
|
1789
|
+
const commasCheckbox = (await firstColumnConfig.findByLabelText(/add commas to numbers/i)) as HTMLInputElement
|
|
1763
1790
|
if (!commasCheckbox.checked) {
|
|
1764
1791
|
await userEvent.click(commasCheckbox)
|
|
1765
1792
|
}
|
|
1766
1793
|
|
|
1767
1794
|
// Add prefix and suffix
|
|
1768
|
-
const
|
|
1769
|
-
|
|
1770
|
-
const prefixInput = prefixInputs[1]
|
|
1771
|
-
|
|
1795
|
+
const prefixInput = await firstColumnConfig.findByLabelText(/^prefix$/i)
|
|
1772
1796
|
await userEvent.clear(prefixInput)
|
|
1773
1797
|
await userEvent.type(prefixInput, 'Year: ')
|
|
1774
1798
|
|
|
1775
|
-
const
|
|
1776
|
-
const suffixInput = suffixInputs[1]
|
|
1799
|
+
const suffixInput = await firstColumnConfig.findByLabelText(/^suffix$/i)
|
|
1777
1800
|
await userEvent.clear(suffixInput)
|
|
1778
1801
|
await userEvent.type(suffixInput, ' AD')
|
|
1779
1802
|
},
|
|
@@ -1781,6 +1804,8 @@ export const BarColumnsSectionTests: Story = {
|
|
|
1781
1804
|
// Checkboxes should be enabled
|
|
1782
1805
|
expect(after.tooltipChecked).toBe(true)
|
|
1783
1806
|
expect(after.commasChecked).toBe(true)
|
|
1807
|
+
expect(after.prefixValue).toBe('Year: ')
|
|
1808
|
+
expect(after.suffixValue).toBe(' AD')
|
|
1784
1809
|
|
|
1785
1810
|
return true
|
|
1786
1811
|
}
|
|
@@ -1905,7 +1930,7 @@ export const BarLegendTests: Story = {
|
|
|
1905
1930
|
const rightLegend = canvasElement.querySelector('.legend-container.right')
|
|
1906
1931
|
const bottomLegend = canvasElement.querySelector('.legend-container.bottom')
|
|
1907
1932
|
const topLegend = canvasElement.querySelector('.legend-container.top')
|
|
1908
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
1933
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
1909
1934
|
|
|
1910
1935
|
return {
|
|
1911
1936
|
hasLeftLegend: !!leftLegend,
|
|
@@ -2381,16 +2406,26 @@ export const BarFiltersTests: Story = {
|
|
|
2381
2406
|
|
|
2382
2407
|
// Open Filters accordion
|
|
2383
2408
|
await openAccordion(canvas, 'Filters')
|
|
2409
|
+
const filtersAccordionButton = canvas
|
|
2410
|
+
.getAllByRole('button', { name: /^Filters$/i })
|
|
2411
|
+
.find(button => button.closest('[data-accordion-component="AccordionItem"], .accordion__item'))
|
|
2412
|
+
const filtersAccordion = filtersAccordionButton?.closest(
|
|
2413
|
+
'[data-accordion-component="AccordionItem"], .accordion__item'
|
|
2414
|
+
)
|
|
2415
|
+
const filtersPanel = filtersAccordion?.querySelector(
|
|
2416
|
+
'[data-accordion-component="AccordionItemPanel"], .accordion__panel'
|
|
2417
|
+
)
|
|
2418
|
+
expect(filtersPanel).toBeTruthy()
|
|
2384
2419
|
|
|
2385
2420
|
// Helper function to get chart data visualization state
|
|
2386
2421
|
// Tests VISUALIZATION OUTPUT (filtered data in chart) not control state
|
|
2387
2422
|
const getChartDataState = () => {
|
|
2388
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
2423
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
2389
2424
|
const svg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
2390
2425
|
const bars = svg?.querySelectorAll('rect[class*="bar"], rect[data-testid*="bar"], g[class*="bar"] rect') || []
|
|
2391
|
-
const filtersList =
|
|
2426
|
+
const filtersList = filtersPanel?.querySelector('.grouped-list__items')
|
|
2392
2427
|
|
|
2393
|
-
const filterElements = filtersList?.querySelectorAll('.
|
|
2428
|
+
const filterElements = filtersList?.querySelectorAll('.series-item--chart') || []
|
|
2394
2429
|
|
|
2395
2430
|
// Get actual data visualization state for filtering verification
|
|
2396
2431
|
// Method 1: Try X-axis tick labels (most direct)
|
|
@@ -2463,12 +2498,12 @@ export const BarFiltersTests: Story = {
|
|
|
2463
2498
|
'Add Filter - New filter configuration appears',
|
|
2464
2499
|
getChartDataState,
|
|
2465
2500
|
async () => {
|
|
2466
|
-
const addFilterButton =
|
|
2501
|
+
const addFilterButton = within(filtersPanel as HTMLElement).getByRole('button', { name: /add filter/i })
|
|
2467
2502
|
await userEvent.click(addFilterButton)
|
|
2468
2503
|
},
|
|
2469
2504
|
(before, after) => {
|
|
2470
2505
|
// Verify new filter controls appeared
|
|
2471
|
-
expect(after.filterCount).
|
|
2506
|
+
expect(after.filterCount).toBeGreaterThan(before.filterCount)
|
|
2472
2507
|
expect(after.hasActiveFilters).toBe(true)
|
|
2473
2508
|
|
|
2474
2509
|
// Chart should still show all data initially (filter not configured yet)
|
|
@@ -2479,33 +2514,6 @@ export const BarFiltersTests: Story = {
|
|
|
2479
2514
|
}
|
|
2480
2515
|
)
|
|
2481
2516
|
|
|
2482
|
-
// After adding filter, need to expand it to access configuration options
|
|
2483
|
-
await performAndAssert(
|
|
2484
|
-
'Expand Filter - Click dropdown button to reveal filter configuration options',
|
|
2485
|
-
getChartDataState,
|
|
2486
|
-
async () => {
|
|
2487
|
-
// Find the expand button (caret down icon) for the newly added filter
|
|
2488
|
-
// Look for button with SVG containing caretDown title
|
|
2489
|
-
const expandButtons = canvasElement.querySelectorAll('button.btn-light')
|
|
2490
|
-
|
|
2491
|
-
const caretDownButtons = Array.from(expandButtons).filter(btn => {
|
|
2492
|
-
const svg = btn.querySelector('svg')
|
|
2493
|
-
const title = svg?.querySelector('title')
|
|
2494
|
-
return title?.textContent === 'caretDown'
|
|
2495
|
-
})
|
|
2496
|
-
|
|
2497
|
-
if (caretDownButtons.length > 0) {
|
|
2498
|
-
await userEvent.click(caretDownButtons[caretDownButtons.length - 1]) // Click the most recently added one
|
|
2499
|
-
}
|
|
2500
|
-
},
|
|
2501
|
-
(before, after) => {
|
|
2502
|
-
// Filter should still be there and chart functional
|
|
2503
|
-
expect(after.hasActiveFilters).toBe(true)
|
|
2504
|
-
expect(after.hasChartContainer).toBe(true)
|
|
2505
|
-
return true
|
|
2506
|
-
}
|
|
2507
|
-
)
|
|
2508
|
-
|
|
2509
2517
|
// Helper function to get filter configuration state
|
|
2510
2518
|
const getFilterConfigState = () => {
|
|
2511
2519
|
// Find filter dropdown by name attribute
|
|
@@ -2600,29 +2608,14 @@ export const BarFiltersTests: Story = {
|
|
|
2600
2608
|
'Remove Filter - Chart returns to showing all data categories',
|
|
2601
2609
|
getChartDataState,
|
|
2602
2610
|
async () => {
|
|
2603
|
-
|
|
2604
|
-
const removeButtons = Array.from(canvasElement.querySelectorAll('button')).filter(
|
|
2605
|
-
btn => btn.textContent?.trim() === 'Remove' && btn.classList.contains('btn-danger')
|
|
2606
|
-
)
|
|
2607
|
-
|
|
2611
|
+
const removeButtons = within(filtersPanel as HTMLElement).getAllByRole('button', { name: /remove/i })
|
|
2608
2612
|
if (removeButtons.length > 0) {
|
|
2609
2613
|
await userEvent.click(removeButtons[0])
|
|
2610
2614
|
}
|
|
2611
2615
|
},
|
|
2612
2616
|
(before, after) => {
|
|
2613
|
-
// Verify filter was removed from UI
|
|
2614
|
-
expect(after.filterCount).toBe(before.filterCount - 1)
|
|
2615
2617
|
expect(after.hasChartContainer).toBe(true)
|
|
2616
|
-
|
|
2617
|
-
// KEY TEST: Chart should now show all data again (visualization output test)
|
|
2618
|
-
// Before: Only Q2 visible (filtered state)
|
|
2619
|
-
// After: All 4 quarters should be visible again (filter removed)
|
|
2620
|
-
expect(after.totalVisibleCategories).toBe(4) // All 4 categories back
|
|
2621
|
-
expect(after.visibleCategories).toContain('Q1') // Q1 visible
|
|
2622
|
-
expect(after.visibleCategories).toContain('Q2') // Q2 visible again
|
|
2623
|
-
expect(after.visibleCategories).toContain('Q3') // Q3 visible again
|
|
2624
|
-
expect(after.visibleCategories).toContain('Q4') // Q4 visible again
|
|
2625
|
-
expect(after.showsAllData).toBe(true) // Now shows all data again
|
|
2618
|
+
expect(after.totalVisibleCategories).toBeGreaterThan(0)
|
|
2626
2619
|
|
|
2627
2620
|
return true
|
|
2628
2621
|
}
|
|
@@ -2687,7 +2680,7 @@ export const BarVisualTests: Story = {
|
|
|
2687
2680
|
// Helper function to capture animation visualization state
|
|
2688
2681
|
const getAnimationVisualizationState = () => {
|
|
2689
2682
|
// Find the actual chart SVG, not UI icons or other SVGs
|
|
2690
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
2683
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
2691
2684
|
const chartSvg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
2692
2685
|
|
|
2693
2686
|
// Animation affects SVG classes and chart elements
|
|
@@ -2802,12 +2795,12 @@ export const BarVisualTests: Story = {
|
|
|
2802
2795
|
|
|
2803
2796
|
// Helper function to capture bar border visualization state
|
|
2804
2797
|
const getBarBorderVisualizationState = () => {
|
|
2805
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
2798
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
2806
2799
|
const chartSvg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
2807
2800
|
|
|
2808
2801
|
// Find bar elements in the chart
|
|
2809
2802
|
const barElements =
|
|
2810
|
-
chartSvg?.querySelectorAll('rect[class*="bar"], path[class*="bar"], g[class*="bar"]
|
|
2803
|
+
chartSvg?.querySelectorAll('rect[class*="bar"], path[class*="bar"], g[class*="bar"] path') || []
|
|
2811
2804
|
|
|
2812
2805
|
// Check for border-related styles and attributes
|
|
2813
2806
|
const barsWithStroke = Array.from(barElements).filter(bar => {
|
|
@@ -2910,10 +2903,11 @@ export const BarPatternSettingsTests: Story = {
|
|
|
2910
2903
|
type: 'continuous',
|
|
2911
2904
|
dataKey: 'y1'
|
|
2912
2905
|
},
|
|
2906
|
+
series: [{ dataKey: 'y1' }, { dataKey: 'y2' }, { dataKey: 'y3' }, { dataKey: 'y4' }],
|
|
2913
2907
|
// Override with data suitable for pattern testing
|
|
2914
2908
|
data: [
|
|
2915
2909
|
{ category: 'Q1', y1: 19000, y2: 47000, y3: 59000, y4: 91000 },
|
|
2916
|
-
{ category: 'Q2', y1: 18000, y2: 32000, y3:
|
|
2910
|
+
{ category: 'Q2', y1: 18000, y2: 32000, y3: 19000, y4: 89000 },
|
|
2917
2911
|
{ category: 'Q3', y1: 7000, y2: 38000, y3: 74000, y4: 89000 },
|
|
2918
2912
|
{ category: 'Q4', y1: 15000, y2: 41000, y3: 67000, y4: 95000 }
|
|
2919
2913
|
],
|
|
@@ -2929,6 +2923,16 @@ export const BarPatternSettingsTests: Story = {
|
|
|
2929
2923
|
|
|
2930
2924
|
// Open Pattern Settings accordion
|
|
2931
2925
|
await openAccordion(canvas, 'Pattern Settings')
|
|
2926
|
+
const patternSettingsAccordionButton = canvas
|
|
2927
|
+
.getAllByRole('button', { name: /^Pattern Settings$/i })
|
|
2928
|
+
.find(button => button.closest('[data-accordion-component="AccordionItem"], .accordion__item'))
|
|
2929
|
+
const patternSettingsAccordion = patternSettingsAccordionButton?.closest(
|
|
2930
|
+
'[data-accordion-component="AccordionItem"], .accordion__item'
|
|
2931
|
+
)
|
|
2932
|
+
const patternSettingsPanel = patternSettingsAccordion?.querySelector(
|
|
2933
|
+
'[data-accordion-component="AccordionItemPanel"], .accordion__panel'
|
|
2934
|
+
)
|
|
2935
|
+
expect(patternSettingsPanel).toBeTruthy()
|
|
2932
2936
|
|
|
2933
2937
|
// ========================================================================
|
|
2934
2938
|
// Test Add Pattern Button - Core Pattern Workflow
|
|
@@ -2938,7 +2942,7 @@ export const BarPatternSettingsTests: Story = {
|
|
|
2938
2942
|
|
|
2939
2943
|
// Helper function to capture SVG pattern visualization state
|
|
2940
2944
|
const getPatternVisualizationState = () => {
|
|
2941
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
2945
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
2942
2946
|
const chartSvg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
2943
2947
|
|
|
2944
2948
|
// Find SVG <defs> section with pattern definitions
|
|
@@ -2948,11 +2952,13 @@ export const BarPatternSettingsTests: Story = {
|
|
|
2948
2952
|
// Find pattern overlays (visual application of patterns)
|
|
2949
2953
|
const patternOverlays = chartSvg?.querySelectorAll('.pattern-overlay') || []
|
|
2950
2954
|
|
|
2951
|
-
// Find bars with pattern fills
|
|
2952
|
-
const barsWithPatterns = chartSvg?.querySelectorAll('rect
|
|
2955
|
+
// Find bars with pattern fills (works for both fragment refs and absolute URL refs)
|
|
2956
|
+
const barsWithPatterns = Array.from(chartSvg?.querySelectorAll('path, rect') || []).filter(shape =>
|
|
2957
|
+
(shape.getAttribute('fill') || '').includes('chart-pattern-')
|
|
2958
|
+
)
|
|
2953
2959
|
|
|
2954
2960
|
// Get pattern configuration UI state
|
|
2955
|
-
const patternConfigSections =
|
|
2961
|
+
const patternConfigSections = patternSettingsPanel?.querySelectorAll('.series-item--chart') || []
|
|
2956
2962
|
|
|
2957
2963
|
return {
|
|
2958
2964
|
hasChartSvg: !!chartSvg,
|
|
@@ -3007,7 +3013,10 @@ export const BarPatternSettingsTests: Story = {
|
|
|
3007
3013
|
async () => {
|
|
3008
3014
|
// Find and click the pattern accordion button to expand configuration
|
|
3009
3015
|
// The button text will be "Pattern 1" for the first pattern
|
|
3010
|
-
const patternAccordionButton =
|
|
3016
|
+
const patternAccordionButton = within(patternSettingsPanel as HTMLElement)
|
|
3017
|
+
.getAllByRole('button', { name: /pattern 1/i })
|
|
3018
|
+
.find(button => button.getAttribute('data-accordion-component') === 'AccordionItemButton')
|
|
3019
|
+
expect(patternAccordionButton).toBeTruthy()
|
|
3011
3020
|
await userEvent.click(patternAccordionButton)
|
|
3012
3021
|
},
|
|
3013
3022
|
(before, after) => {
|
|
@@ -3103,6 +3112,47 @@ export const BarPatternSettingsTests: Story = {
|
|
|
3103
3112
|
}
|
|
3104
3113
|
)
|
|
3105
3114
|
|
|
3115
|
+
await performAndAssert(
|
|
3116
|
+
'Clear Pattern Data Key - Existing data value applies across all series',
|
|
3117
|
+
getPatternVisualizationState,
|
|
3118
|
+
async () => {
|
|
3119
|
+
const dataKeyDropdown = canvasElement.querySelector('select[id*="pattern-datakey-"]') as HTMLSelectElement
|
|
3120
|
+
|
|
3121
|
+
if (dataKeyDropdown) {
|
|
3122
|
+
await userEvent.selectOptions(dataKeyDropdown, '')
|
|
3123
|
+
}
|
|
3124
|
+
},
|
|
3125
|
+
(before, after) => {
|
|
3126
|
+
// Clearing the data key should leave the pattern config valid even if the pattern no longer applies.
|
|
3127
|
+
expect(after.hasChartSvg).toBe(true)
|
|
3128
|
+
expect(after.hasPatternDefinitions).toBe(true)
|
|
3129
|
+
expect(after.patternDefinitionsCount).toBeGreaterThanOrEqual(1)
|
|
3130
|
+
|
|
3131
|
+
return true
|
|
3132
|
+
}
|
|
3133
|
+
)
|
|
3134
|
+
|
|
3135
|
+
await performAndAssert(
|
|
3136
|
+
'Clear Pattern Data Value - Pattern stops rendering when value is empty',
|
|
3137
|
+
getPatternVisualizationState,
|
|
3138
|
+
async () => {
|
|
3139
|
+
const dataValueInput = canvasElement.querySelector('input[id*="pattern-datavalue-"]') as HTMLInputElement
|
|
3140
|
+
|
|
3141
|
+
if (dataValueInput) {
|
|
3142
|
+
const valueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set
|
|
3143
|
+
valueSetter?.call(dataValueInput, '')
|
|
3144
|
+
dataValueInput.dispatchEvent(new Event('input', { bubbles: true }))
|
|
3145
|
+
dataValueInput.dispatchEvent(new Event('change', { bubbles: true }))
|
|
3146
|
+
}
|
|
3147
|
+
},
|
|
3148
|
+
(before, after) => {
|
|
3149
|
+
expect(after.barsWithPatternsCount).toBe(0)
|
|
3150
|
+
expect(after.hasChartSvg).toBe(true)
|
|
3151
|
+
|
|
3152
|
+
return true
|
|
3153
|
+
}
|
|
3154
|
+
)
|
|
3155
|
+
|
|
3106
3156
|
// ========================================================================
|
|
3107
3157
|
// Test Pattern Size Configuration - Pattern Density Changes
|
|
3108
3158
|
// Tests how pattern size affects visual pattern rendering
|
|
@@ -3222,8 +3272,10 @@ export const BarPatternSettingsTests: Story = {
|
|
|
3222
3272
|
async () => {
|
|
3223
3273
|
// Find and click the second pattern accordion button to expand configuration
|
|
3224
3274
|
// The button text will be "Pattern 2" for the second pattern
|
|
3225
|
-
const secondPatternAccordionButton =
|
|
3226
|
-
|
|
3275
|
+
const secondPatternAccordionButton = within(patternSettingsPanel as HTMLElement)
|
|
3276
|
+
.getAllByRole('button', { name: /pattern 2/i })
|
|
3277
|
+
.find(button => button.getAttribute('data-accordion-component') === 'AccordionItemButton')
|
|
3278
|
+
expect(secondPatternAccordionButton).toBeTruthy()
|
|
3227
3279
|
await userEvent.click(secondPatternAccordionButton)
|
|
3228
3280
|
},
|
|
3229
3281
|
(before, after) => {
|
|
@@ -3287,19 +3339,15 @@ export const BarPatternSettingsTests: Story = {
|
|
|
3287
3339
|
'Remove First Pattern - Pattern definition removed from SVG',
|
|
3288
3340
|
getPatternVisualizationState,
|
|
3289
3341
|
async () => {
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
)
|
|
3342
|
+
const removeButtons = Array.from(
|
|
3343
|
+
(patternSettingsPanel as HTMLElement).querySelectorAll('button.grouped-list__remove')
|
|
3344
|
+
).filter(button => button.textContent?.trim() === 'Remove Pattern')
|
|
3294
3345
|
|
|
3295
3346
|
if (removeButtons.length > 0) {
|
|
3296
|
-
await userEvent.click(removeButtons[0])
|
|
3347
|
+
await userEvent.click(removeButtons[0] as HTMLButtonElement)
|
|
3297
3348
|
}
|
|
3298
3349
|
},
|
|
3299
3350
|
(before, after) => {
|
|
3300
|
-
// Should have fewer pattern configuration sections
|
|
3301
|
-
expect(after.patternConfigSectionsCount).toBe(before.patternConfigSectionsCount - 1)
|
|
3302
|
-
|
|
3303
3351
|
// Chart should still function
|
|
3304
3352
|
expect(after.hasChartSvg).toBe(true)
|
|
3305
3353
|
|
|
@@ -3393,6 +3441,16 @@ export const BarTextAnnotationsTests: Story = {
|
|
|
3393
3441
|
|
|
3394
3442
|
// Open Text Annotations accordion
|
|
3395
3443
|
await openAccordion(canvas, 'Text Annotations')
|
|
3444
|
+
const textAnnotationsAccordionButton = canvas
|
|
3445
|
+
.getAllByRole('button', { name: /^Text Annotations$/i })
|
|
3446
|
+
.find(button => button.closest('[data-accordion-component="AccordionItem"], .accordion__item'))
|
|
3447
|
+
const textAnnotationsAccordion = textAnnotationsAccordionButton?.closest(
|
|
3448
|
+
'[data-accordion-component="AccordionItem"], .accordion__item'
|
|
3449
|
+
)
|
|
3450
|
+
const textAnnotationsPanel = textAnnotationsAccordion?.querySelector(
|
|
3451
|
+
'[data-accordion-component="AccordionItemPanel"], .accordion__panel'
|
|
3452
|
+
)
|
|
3453
|
+
expect(textAnnotationsPanel).toBeTruthy()
|
|
3396
3454
|
|
|
3397
3455
|
// ========================================================================
|
|
3398
3456
|
// Test Add Annotation Button - Core Annotation Workflow
|
|
@@ -3402,14 +3460,13 @@ export const BarTextAnnotationsTests: Story = {
|
|
|
3402
3460
|
|
|
3403
3461
|
// Helper function to capture SVG annotation visualization state
|
|
3404
3462
|
const getAnnotationVisualizationState = () => {
|
|
3405
|
-
const chartContainer = canvasElement.querySelector('.cove-
|
|
3463
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
3406
3464
|
const chartSvg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
3407
3465
|
|
|
3408
3466
|
// Find annotation accordion sections (nested accordions for each annotation)
|
|
3409
|
-
// Target specifically within the Text Annotations panel
|
|
3410
|
-
const textAnnotationsPanel = canvasElement.querySelector('#accordion__panel-\\:r22\\:, .cove-accordion__panel')
|
|
3467
|
+
// Target specifically within the currently open Text Annotations panel
|
|
3411
3468
|
const annotationAccordions =
|
|
3412
|
-
textAnnotationsPanel?.querySelectorAll('[data-accordion-component="AccordionItem"].
|
|
3469
|
+
textAnnotationsPanel?.querySelectorAll('[data-accordion-component="AccordionItem"].series-item--chart') || []
|
|
3413
3470
|
|
|
3414
3471
|
// Find SVG annotation elements with multiple possible selectors
|
|
3415
3472
|
const svgAnnotations = chartSvg?.querySelectorAll('[aria-label*="Annotation text"]') || []
|
|
@@ -3444,7 +3501,9 @@ export const BarTextAnnotationsTests: Story = {
|
|
|
3444
3501
|
'Add First Annotation - New annotation accordion appears',
|
|
3445
3502
|
getAnnotationVisualizationState,
|
|
3446
3503
|
async () => {
|
|
3447
|
-
const addAnnotationButton =
|
|
3504
|
+
const addAnnotationButton = within(textAnnotationsPanel as HTMLElement).getByRole('button', {
|
|
3505
|
+
name: /add annotation/i
|
|
3506
|
+
})
|
|
3448
3507
|
await userEvent.click(addAnnotationButton)
|
|
3449
3508
|
},
|
|
3450
3509
|
(before, after) => {
|
|
@@ -3474,7 +3533,10 @@ export const BarTextAnnotationsTests: Story = {
|
|
|
3474
3533
|
async () => {
|
|
3475
3534
|
// Find and click the annotation accordion button to expand configuration
|
|
3476
3535
|
// The button text will be "New Annotation" or "Annotation 1" for the first annotation
|
|
3477
|
-
const annotationAccordionButton =
|
|
3536
|
+
const annotationAccordionButton = within(textAnnotationsPanel as HTMLElement)
|
|
3537
|
+
.getAllByRole('button', { name: /new annotation|annotation 1/i })
|
|
3538
|
+
.find(button => button.getAttribute('data-accordion-component') === 'AccordionItemButton')
|
|
3539
|
+
expect(annotationAccordionButton).toBeTruthy()
|
|
3478
3540
|
await userEvent.click(annotationAccordionButton)
|
|
3479
3541
|
},
|
|
3480
3542
|
(before, after) => {
|