@cdc/chart 4.26.6 → 4.26.10
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 +504 -368
- package/dist/cdcchart-CYc-Kayd.es.js +22488 -0
- package/dist/cdcchart-DCKn8NmI.es.js +20695 -0
- package/dist/cdcchart-Dyui9nk_.es.js +45828 -0
- package/dist/cdcchart.js +4 -96498
- package/examples/__data__/vega-remote-data.json +6 -0
- package/examples/dendrogram.json +18 -0
- package/examples/feature/bar/bar-chart-race-annual-change.json +68 -0
- package/examples/feature/heatmap/average-age-categorical.json +2 -2
- package/examples/feature/heatmap/calendar-heatmap.json +2 -2
- package/examples/feature/heatmap/cell-values-bottom-axis.json +2 -2
- package/examples/feature/heatmap/sparse-aggregation.json +2 -2
- package/examples/feature/line/line-chart-race.json +50 -0
- package/examples/feature/sankey/sankey-example-data.json +40 -1441
- package/examples/feature/sankey/sankey_chart_data.csv +9 -20
- package/examples/network.json +31 -0
- package/examples/private/box-plot.json +573 -0
- package/examples/private/custom-color-issue.json +625 -0
- package/examples/private/custom-colors.json +522 -0
- package/examples/private/cutoff.json +10750 -0
- package/examples/private/flueview.json +22051 -0
- package/examples/private/overlay-legend.json +485 -0
- package/examples/private/pie-test.json +469 -0
- package/examples/vega-remote.json +22 -0
- package/package.json +11 -13
- package/src/CdcChart.tsx +2 -2
- package/src/CdcChartComponent.tsx +444 -217
- package/src/_stories/Chart.BarRace.stories.tsx +443 -0
- package/src/_stories/Chart.BoxPlotNumericSort.stories.tsx +189 -0
- package/src/_stories/Chart.BoxPlotStatsParity.stories.tsx +207 -0
- package/src/_stories/Chart.ColorDistribution.stories.tsx +299 -0
- package/src/_stories/Chart.DataCutoff.stories.tsx +147 -0
- package/src/_stories/Chart.DataTableDownloadFilename.stories.tsx +60 -0
- package/src/_stories/Chart.Dendrogram.stories.tsx +373 -0
- package/src/_stories/Chart.Forecast.CILegend.stories.tsx +236 -0
- package/src/_stories/Chart.HeatMap.stories.tsx +170 -4
- package/src/_stories/Chart.Legend.AxisAlign.stories.tsx +119 -0
- package/src/_stories/Chart.LineRace.stories.tsx +149 -0
- package/src/_stories/Chart.Network.stories.tsx +432 -0
- package/src/_stories/Chart.PaletteConversion.Editor.stories.tsx +208 -0
- package/src/_stories/Chart.Patterns.smoke.stories.tsx +31 -0
- package/src/_stories/Chart.PercentageValue.stories.tsx +311 -0
- package/src/_stories/Chart.PieLabels.stories.tsx +49 -0
- package/src/_stories/Chart.RankByValueMutation.stories.tsx +206 -0
- package/src/_stories/Chart.RequiredFields.stories.tsx +681 -0
- package/src/_stories/Chart.Sankey.stories.tsx +417 -0
- package/src/_stories/Chart.SeriesColorAssignments.stories.tsx +47 -0
- package/src/_stories/Chart.SmallMultiplesNumericTileOrder.stories.tsx +137 -0
- package/src/_stories/Chart.TooltipSeriesLabels.stories.tsx +43 -0
- package/src/_stories/Chart.VegaRemoteData.stories.tsx +31 -0
- package/src/_stories/Chart.smoke.stories.tsx +14 -1
- package/src/_stories/ChartAnnotation.Editor.stories.tsx +78 -0
- package/src/_stories/ChartAxisTitles.smoke.stories.tsx +48 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +499 -57
- package/src/_stories/ChartBrush.Editor.stories.tsx +46 -67
- package/src/_stories/ChartBrush.Regression.stories.tsx +135 -0
- package/src/_stories/ChartEditor.Editor.stories.tsx +33 -3
- package/src/_stories/ChartLine.EffectCompatibility.stories.tsx +120 -0
- package/src/_stories/ChartPrefixSuffix.smoke.stories.tsx +28 -1
- package/src/_stories/_mock/annotation_event-line_mock.json +2 -2
- package/src/_stories/_mock/editor-test-configs.json +20 -10
- package/src/_stories/_mock/editor-test-datasets.json +38 -42
- package/src/_stories/_mock/heatmap-average-age-categorical.json +2 -2
- package/src/_stories/_mock/heatmap.json +2 -2
- package/src/_stories/_mock/legend-axis-align-stacked-bar.json +125 -0
- package/src/_stories/_mock/sankey_mock.json +23 -27
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +52 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +639 -139
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +44 -4
- package/src/components/Annotations/components/AnnotationDropdown.tsx +10 -32
- package/src/components/Annotations/helpers/resolveAnnotationLayout.ts +212 -0
- package/src/components/Annotations/tests/AnnotationDraggable.test.tsx +612 -19
- package/src/components/Annotations/tests/AnnotationDropdown.test.tsx +50 -0
- package/src/components/Annotations/tests/resolveAnnotationLayout.test.ts +203 -0
- package/src/components/Axis/BottomAxis.tsx +1 -1
- package/src/components/Axis/Categorical.Axis.tsx +3 -3
- package/src/components/Axis/RightAxis.tsx +21 -13
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +39 -8
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +13 -4
- package/src/components/BarChart/components/BarChart.StackedVertical.test.ts +28 -0
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +30 -26
- package/src/components/BarChart/components/BarChart.Vertical.tsx +38 -28
- package/src/components/BarChart/components/PortionPatternOverlay.tsx +61 -0
- package/src/components/BarChart/components/context.tsx +6 -0
- package/src/components/BarChart/helpers/getBarData.ts +28 -28
- package/src/components/BarChart/helpers/getBarHeights.ts +2 -2
- package/src/components/BarChart/helpers/getPatternUrl.ts +2 -5
- package/src/components/BarChart/helpers/index.ts +8 -0
- package/src/components/BarChart/helpers/portionPattern.ts +243 -0
- package/src/components/BarChart/helpers/tests/addMinimumBarHeights.test.ts +42 -0
- package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +13 -0
- package/src/components/BarChart/helpers/tests/portionPattern.test.ts +280 -0
- package/src/components/BarChart/helpers/tests/useBarChart.test.tsx +202 -0
- package/src/components/BarChart/helpers/useBarChart.ts +60 -60
- package/src/components/BarChartRace/BarChartRace.test.tsx +199 -0
- package/src/components/BarChartRace/BarChartRace.tsx +230 -0
- package/src/components/BarChartRace/BarChartRaceFallback.test.tsx +61 -0
- package/src/components/BarChartRace/BarChartRaceFallback.tsx +48 -0
- package/src/components/BarChartRace/bar-chart-race.css +160 -0
- package/src/components/BarChartRace/helpers.test.ts +114 -0
- package/src/components/BarChartRace/helpers.ts +185 -0
- package/src/components/BarChartRace/index.ts +3 -0
- package/src/components/BoxPlot/BoxPlot.Horizontal.tsx +7 -6
- package/src/components/BoxPlot/BoxPlot.Vertical.tsx +9 -7
- package/src/components/BoxPlot/helpers/index.ts +13 -83
- package/src/components/Brush/BrushSelector.tsx +10 -24
- package/src/components/Brush/MiniChartPreview.tsx +33 -1
- package/src/components/Dendrogram/components/Dendrogram.tsx +374 -0
- package/src/components/Dendrogram/dendrogram.css +113 -0
- package/src/components/Dendrogram/helpers/layoutDendrogram.test.ts +95 -0
- package/src/components/Dendrogram/helpers/layoutDendrogram.ts +56 -0
- package/src/components/Dendrogram/helpers/prepareDendrogramData.test.ts +135 -0
- package/src/components/Dendrogram/helpers/prepareDendrogramData.ts +166 -0
- package/src/components/Dendrogram/helpers/resolveDendrogramLabels.test.ts +196 -0
- package/src/components/Dendrogram/helpers/resolveDendrogramLabels.ts +418 -0
- package/src/components/Dendrogram/index.tsx +1 -0
- package/src/components/Dendrogram/types/index.ts +66 -0
- package/src/components/EditorPanel/EditorPanel.tsx +1111 -805
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +50 -5
- package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.css +10 -0
- package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.tsx +211 -0
- package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +19 -8
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +104 -61
- package/src/components/EditorPanel/components/Panels/Panel.Network.css +9 -0
- package/src/components/EditorPanel/components/Panels/Panel.Network.tsx +202 -0
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +236 -36
- package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +6 -1
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +89 -105
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +21 -6
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +1 -1
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +8 -66
- package/src/components/EditorPanel/components/Panels/RacingControls.test.tsx +131 -0
- package/src/components/EditorPanel/components/Panels/RacingControls.tsx +185 -0
- package/src/components/EditorPanel/components/Panels/SeriesColorAssignments.tsx +2 -2
- package/src/components/EditorPanel/components/Panels/index.tsx +4 -0
- package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +20 -7
- package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +25 -2
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +5 -4
- package/src/components/EditorPanel/useEditorPermissions.test.tsx +94 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +75 -25
- package/src/components/Forecasting/Forecasting.tsx +16 -7
- package/src/components/HeatMap/components/HeatMap.tsx +511 -178
- package/src/components/HeatMap/heatmap.constants.ts +7 -3
- package/src/components/HeatMap/heatmap.css +101 -2
- package/src/components/HeatMap/helpers/layout.ts +80 -0
- package/src/components/HeatMap/tests/HeatMap.test.tsx +339 -16
- package/src/components/HeatMap/tests/helpers.test.ts +79 -0
- package/src/components/Legend/Legend.Component.tsx +163 -148
- package/src/components/Legend/Legend.Suppression.tsx +3 -3
- package/src/components/Legend/LegendGroup/LegendGroup.styles.css +7 -0
- package/src/components/Legend/LegendGroup/LegendGroup.tsx +47 -35
- package/src/components/Legend/helpers/createFormatLabels.test.ts +226 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +45 -51
- package/src/components/LineChart/components/LineChart.Circle.tsx +3 -3
- package/src/components/LineChart/helpers.test.ts +126 -38
- package/src/components/LineChart/helpers.ts +9 -6
- package/src/components/LineChart/index.tsx +71 -27
- package/src/components/LineChart/tests/LineChart.test.tsx +630 -0
- package/src/components/LineChartRace/LineChartRace.test.tsx +275 -0
- package/src/components/LineChartRace/LineChartRace.tsx +134 -0
- package/src/components/LineChartRace/LineChartRaceFallback.tsx +28 -0
- package/src/components/LineChartRace/helpers.test.ts +88 -0
- package/src/components/LineChartRace/helpers.ts +157 -0
- package/src/components/LineChartRace/index.ts +3 -0
- package/src/components/LineChartRace/line-chart-race.css +16 -0
- package/src/components/LinearChart/ValueAxisAnchors.tsx +58 -0
- package/src/components/LinearChart/VisualizationRenderer.tsx +18 -23
- package/src/components/LinearChart/tests/LinearChart.test.tsx +412 -13
- package/src/components/LinearChart/tests/VisualizationRenderer.test.tsx +141 -0
- package/src/components/LinearChart/tests/mockConfigContext.ts +2 -1
- package/src/components/LinearChart.tsx +85 -54
- package/src/components/Network/components/Network.tsx +372 -0
- package/src/components/Network/helpers/layoutNetwork.test.ts +68 -0
- package/src/components/Network/helpers/layoutNetwork.ts +87 -0
- package/src/components/Network/helpers/prepareNetworkData.test.ts +116 -0
- package/src/components/Network/helpers/prepareNetworkData.ts +101 -0
- package/src/components/Network/helpers/resolveNetworkLabels.test.ts +112 -0
- package/src/components/Network/helpers/resolveNetworkLabels.ts +206 -0
- package/src/components/Network/index.tsx +3 -0
- package/src/components/Network/network.css +107 -0
- package/src/components/Network/types/index.ts +56 -0
- package/src/components/PairedBarChart.jsx +5 -10
- package/src/components/PairedBarChart.test.tsx +71 -0
- package/src/components/PieChart/PieChart.tsx +121 -13
- package/src/components/PieChart/helpers/labelPlacement.test.ts +88 -0
- package/src/components/PieChart/helpers/labelPlacement.ts +95 -0
- package/src/components/RacingChartRenderer.tsx +44 -0
- package/src/components/Regions/components/Regions.test.tsx +179 -0
- package/src/components/Regions/components/Regions.tsx +104 -69
- package/src/components/Regions/index.tsx +2 -3
- package/src/components/Sankey/components/Sankey.tsx +264 -437
- package/src/components/Sankey/helpers/getSankeyTooltip.ts +12 -0
- package/src/components/Sankey/helpers/prepareSankeyData.test.ts +71 -0
- package/src/components/Sankey/helpers/prepareSankeyData.ts +90 -0
- package/src/components/Sankey/sankey.scss +57 -116
- package/src/components/Sankey/types/index.ts +41 -4
- package/src/components/ScatterPlot/ScatterPlot.jsx +28 -12
- package/src/components/ScatterPlot/ScatterPlot.test.tsx +60 -0
- package/src/components/SmallMultiples/SmallMultiples.tsx +12 -12
- package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +7 -5
- package/src/components/raceTiming.ts +18 -0
- package/src/data/initial-state.js +79 -31
- package/src/helpers/applyChartTypeContract.ts +71 -0
- package/src/helpers/axisLabelFontSize.ts +2 -2
- package/src/helpers/boxPlotStats.ts +60 -0
- package/src/helpers/bumpChartTooltip.ts +2 -1
- package/src/helpers/calculateHorizontalBarCategoryLabelWidth.test.ts +37 -0
- package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +3 -2
- package/src/helpers/calculateRightYAxisWidth.ts +60 -0
- package/src/helpers/categoryOrder.ts +60 -0
- package/src/helpers/colorAssignmentHelpers.ts +2 -2
- package/src/helpers/createNewChartConfig.ts +56 -0
- package/src/helpers/dataDrivenYAxisCategories.ts +18 -0
- package/src/helpers/forecastLegend.ts +88 -0
- package/src/helpers/getBoxPlotConfig.ts +26 -38
- package/src/helpers/getChartTypeDefaultPalette.ts +29 -0
- package/src/helpers/getColorCodeCategoryColorMap.ts +26 -0
- package/src/helpers/getColorScale.ts +31 -22
- package/src/helpers/getComboChartConfig.ts +0 -1
- package/src/helpers/getDynamicYAxisCategories.ts +69 -0
- package/src/helpers/getExcludedData.ts +0 -1
- package/src/helpers/getHasBoundarySuppression.ts +4 -3
- package/src/helpers/getMinMax.ts +11 -8
- package/src/helpers/getNewRuntime.ts +4 -3
- package/src/helpers/getPiePercent.ts +3 -3
- package/src/helpers/getStackedSeriesMax.ts +51 -0
- package/src/helpers/getV21ChartDistributionColors.ts +57 -0
- package/src/helpers/handleRankByValue.ts +15 -15
- package/src/helpers/isConvertLineToBarGraph.ts +2 -2
- package/src/helpers/seriesColumnSettings.ts +31 -3
- package/src/helpers/sizeHelpers.ts +4 -5
- package/src/helpers/smallMultiplesHelpers.ts +10 -13
- package/src/helpers/tests/applyChartTypeContract.test.ts +37 -0
- package/src/helpers/tests/boxPlotStats.test.ts +31 -0
- package/src/helpers/tests/bumpChartTooltip.test.ts +29 -0
- package/src/helpers/tests/calculateRightYAxisWidth.test.ts +105 -0
- package/src/helpers/tests/categoryOrder.test.ts +57 -0
- package/src/helpers/tests/countNumOfTicks.test.ts +11 -0
- package/src/helpers/tests/createNewChartConfig.test.ts +182 -0
- package/src/helpers/tests/getBoxPlotConfig.test.ts +70 -0
- package/src/helpers/tests/getColorCodeCategoryColorMap.test.ts +113 -0
- package/src/helpers/tests/getColorScale.test.ts +226 -1
- package/src/helpers/tests/getDynamicYAxisCategories.test.ts +61 -0
- package/src/helpers/tests/getMinMax.test.ts +41 -0
- package/src/helpers/tests/getStackedSeriesMax.test.ts +26 -0
- package/src/helpers/tests/handleRankByValue.test.ts +55 -37
- package/src/helpers/tests/seriesColumnSettings.test.ts +83 -0
- package/src/helpers/tests/smallMultiplesHelpers.test.ts +37 -1
- package/src/helpers/tests/tooltipHelpers.test.ts +80 -0
- package/src/helpers/tooltipHelpers.ts +16 -0
- package/src/hooks/tests/useRightAxis.test.tsx +143 -2
- package/src/hooks/tests/useScales.test.tsx +3 -6
- package/src/hooks/tests/useTooltip.test.tsx +484 -0
- package/src/hooks/usePrefersReducedMotion.ts +22 -0
- package/src/hooks/useReduceData.ts +4 -36
- package/src/hooks/useRightAxis.ts +19 -26
- package/src/hooks/useScales.ts +15 -0
- package/src/hooks/useTooltip.tsx +106 -71
- package/src/scss/main.scss +74 -2
- package/src/store/chart.reducer.ts +4 -4
- package/src/test/CdcChart.dataTable.test.tsx +582 -14
- package/src/test/CdcChart.dendrogram.test.tsx +277 -0
- package/src/test/CdcChart.finalization.test.tsx +143 -0
- package/src/test/CdcChart.network.test.tsx +271 -0
- package/src/types/ChartConfig.ts +91 -54
- package/src/types/ChartContext.ts +5 -0
- package/src/types/Horizon.ts +1 -1
- package/LICENSE +0 -201
- package/dist/cdcchart-BWVIUcAF.es.js +0 -4802
- package/src/_stories/Chart.Sankey.smoke.stories.tsx +0 -62
- package/src/components/Sankey/components/ColumnList.tsx +0 -19
- package/src/components/Sankey/helpers/getSankeyTooltip.tsx +0 -34
- package/src/components/Sankey/useSankeyAlert.tsx +0 -60
- package/src/data/legacy-defaults.ts +0 -18
- package/src/helpers/ensureSpecialChartAxisTypes.ts +0 -11
- package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +0 -30
|
@@ -3,7 +3,13 @@ import { within, userEvent, expect } from 'storybook/test'
|
|
|
3
3
|
import Chart from '../CdcChartComponent'
|
|
4
4
|
|
|
5
5
|
// Import testing helpers following best practices document
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
assertVisualizationRendered,
|
|
8
|
+
openAccordion,
|
|
9
|
+
performAndAssert,
|
|
10
|
+
waitForEditor,
|
|
11
|
+
waitForPresence
|
|
12
|
+
} from '@cdc/core/helpers/testing'
|
|
7
13
|
|
|
8
14
|
// Import working configuration (same as other successful tests)
|
|
9
15
|
import mockScatterPlot from './_mock/scatterplot_mock.json'
|
|
@@ -216,7 +222,14 @@ export const BarGeneralTests: Story = {
|
|
|
216
222
|
|
|
217
223
|
// Additional validation
|
|
218
224
|
hasHorizontalStructure: horizontalBars.length > 0,
|
|
219
|
-
hasVerticalStructure: verticalBarContainers.length > 0
|
|
225
|
+
hasVerticalStructure: verticalBarContainers.length > 0,
|
|
226
|
+
|
|
227
|
+
// Bottom numeric axis defaults
|
|
228
|
+
bottomAxisLineCount: svg?.querySelectorAll('.bottom-axis > line').length || 0,
|
|
229
|
+
bottomAxisTickCount: svg?.querySelectorAll('.bottom-axis .vx-axis-tick > line').length || 0,
|
|
230
|
+
|
|
231
|
+
// Left date/category axis default
|
|
232
|
+
leftAxisLineCount: svg?.querySelectorAll('.left-axis > line').length || 0
|
|
220
233
|
}
|
|
221
234
|
}
|
|
222
235
|
|
|
@@ -242,6 +255,9 @@ export const BarGeneralTests: Story = {
|
|
|
242
255
|
expect(after.hasHorizontalStructure).toBe(true)
|
|
243
256
|
expect(after.horizontalBarCount).toBeGreaterThan(0)
|
|
244
257
|
expect(after.isPredominantlyHorizontal).toBe(true)
|
|
258
|
+
expect(after.bottomAxisLineCount).toBe(0)
|
|
259
|
+
expect(after.bottomAxisTickCount).toBe(0)
|
|
260
|
+
expect(after.leftAxisLineCount).toBeGreaterThan(0)
|
|
245
261
|
|
|
246
262
|
return true
|
|
247
263
|
}
|
|
@@ -260,83 +276,74 @@ export const BarGeneralTests: Story = {
|
|
|
260
276
|
expect(after.hasVerticalStructure).toBe(true)
|
|
261
277
|
expect(after.verticalBarCount).toBeGreaterThan(0)
|
|
262
278
|
expect(after.isPredominantlyVertical).toBe(true)
|
|
279
|
+
expect(after.bottomAxisLineCount).toBeGreaterThan(0)
|
|
280
|
+
expect(after.bottomAxisTickCount).toBeGreaterThan(0)
|
|
281
|
+
expect(after.leftAxisLineCount).toBe(0)
|
|
263
282
|
|
|
264
283
|
return true
|
|
265
284
|
}
|
|
266
285
|
)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
267
288
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
289
|
+
export const BarLollipopStyleTests: Story = {
|
|
290
|
+
name: 'Lollipop Bar Style Tests',
|
|
291
|
+
args: {
|
|
292
|
+
config: {
|
|
293
|
+
...mockScatterPlot,
|
|
294
|
+
visualizationType: 'Bar',
|
|
295
|
+
visualizationSubType: 'regular',
|
|
296
|
+
orientation: 'vertical',
|
|
297
|
+
xAxis: {
|
|
298
|
+
...mockScatterPlot.xAxis,
|
|
299
|
+
type: 'categorical',
|
|
300
|
+
dataKey: 'category'
|
|
301
|
+
},
|
|
302
|
+
series: [{ ...mockScatterPlot.series[0], type: 'Bar' }],
|
|
303
|
+
data: [
|
|
304
|
+
{ category: 'Q1', y1: 19000 },
|
|
305
|
+
{ category: 'Q2', y1: 18000 },
|
|
306
|
+
{ category: 'Q3', y1: 7000 },
|
|
307
|
+
{ category: 'Q4', y1: 15000 }
|
|
308
|
+
]
|
|
309
|
+
},
|
|
310
|
+
isEditor: true
|
|
311
|
+
},
|
|
312
|
+
play: async ({ canvasElement }) => {
|
|
313
|
+
const canvas = within(canvasElement)
|
|
314
|
+
await waitForEditor(canvas)
|
|
315
|
+
await openAccordion(canvas, 'General')
|
|
273
316
|
|
|
274
317
|
const getBarStyleVisualization = () => {
|
|
275
|
-
// Target the chart visualization SVG specifically, not editor UI icons
|
|
276
318
|
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
277
319
|
const svg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
320
|
+
const lollipopCircles = svg?.querySelectorAll('circle[cx][cy][r]').length || 0
|
|
321
|
+
const lollipopSquares = svg?.querySelectorAll('rect[data-tooltip-html]').length || 0
|
|
278
322
|
|
|
279
323
|
return {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
// Lollipop-specific: Look for circle "heads"
|
|
284
|
-
lollipopCircles: svg?.querySelectorAll('circle[cx][cy][r]').length || 0,
|
|
285
|
-
|
|
286
|
-
// Lollipop-specific: Look for square "heads" (alternative shape)
|
|
287
|
-
lollipopSquares: svg?.querySelectorAll('rect[data-tooltip-html]').length || 0,
|
|
288
|
-
|
|
289
|
-
// Specific style indicators
|
|
290
|
-
hasLollipopElements: (svg?.querySelectorAll('circle[cx][cy][r], rect[data-tooltip-html]').length || 0) > 0,
|
|
324
|
+
lollipopCircles,
|
|
325
|
+
lollipopSquares,
|
|
326
|
+
hasLollipopElements: lollipopCircles + lollipopSquares > 0,
|
|
291
327
|
hasRegularBarElements: (svg?.querySelectorAll('path[fill]').length || 0) > 0
|
|
292
328
|
}
|
|
293
329
|
}
|
|
294
330
|
|
|
295
|
-
// First ensure we're in regular subtype so lollipop option is available
|
|
296
|
-
await userEvent.selectOptions(chartSubtypeDropdown, 'regular')
|
|
297
|
-
expect(chartSubtypeDropdown.value).toBe('regular')
|
|
298
|
-
|
|
299
|
-
// Find Bar Style dropdown
|
|
300
331
|
const barStyleDropdown = canvas.getByLabelText(/bar style/i) as HTMLSelectElement
|
|
332
|
+
const barStyleOptions = Array.from(barStyleDropdown.options).map(option => option.value)
|
|
333
|
+
expect(barStyleOptions).toEqual(expect.arrayContaining(['flat', 'rounded', 'lollipop']))
|
|
301
334
|
|
|
302
|
-
// Verify dropdown has expected options for regular Bar charts
|
|
303
|
-
const barStyleOptions = Array.from(barStyleDropdown.options).map(opt => opt.value)
|
|
304
|
-
expect(barStyleOptions).toContain('flat')
|
|
305
|
-
expect(barStyleOptions).toContain('rounded')
|
|
306
|
-
expect(barStyleOptions).toContain('lollipop')
|
|
307
|
-
|
|
308
|
-
// Test Bar Style: Flat → Lollipop (most dramatic visual change)
|
|
309
335
|
await performAndAssert(
|
|
310
336
|
'Switch Bar Style to Lollipop',
|
|
311
337
|
getBarStyleVisualization,
|
|
312
338
|
async () => await userEvent.selectOptions(barStyleDropdown, 'lollipop'),
|
|
313
|
-
(
|
|
314
|
-
// Control state changed
|
|
315
|
-
expect(barStyleDropdown.value).toBe('lollipop')
|
|
316
|
-
|
|
317
|
-
// Lollipop elements (circles or squares) should appear
|
|
318
|
-
expect(after.hasLollipopElements).toBe(true)
|
|
319
|
-
expect(after.lollipopCircles + after.lollipopSquares).toBeGreaterThan(0)
|
|
320
|
-
|
|
321
|
-
return true
|
|
322
|
-
}
|
|
339
|
+
(_before, after) => after.hasLollipopElements && after.lollipopCircles + after.lollipopSquares > 0
|
|
323
340
|
)
|
|
324
341
|
|
|
325
|
-
// Test switching back to flat
|
|
326
342
|
await performAndAssert(
|
|
327
343
|
'Switch Bar Style back to Flat',
|
|
328
344
|
getBarStyleVisualization,
|
|
329
345
|
async () => await userEvent.selectOptions(barStyleDropdown, 'flat'),
|
|
330
|
-
(
|
|
331
|
-
// Control state changed back
|
|
332
|
-
expect(barStyleDropdown.value).toBe('flat')
|
|
333
|
-
|
|
334
|
-
// Lollipop elements should disappear, regular bar elements should remain
|
|
335
|
-
expect(after.hasLollipopElements).toBe(false)
|
|
336
|
-
expect(after.hasRegularBarElements).toBe(true)
|
|
337
|
-
|
|
338
|
-
return true
|
|
339
|
-
}
|
|
346
|
+
(_before, after) => !after.hasLollipopElements && after.hasRegularBarElements
|
|
340
347
|
)
|
|
341
348
|
}
|
|
342
349
|
}
|
|
@@ -1204,6 +1211,87 @@ export const BarLeftValueAxisTests: Story = {
|
|
|
1204
1211
|
// - Use performAndAssert pattern for consistent testing
|
|
1205
1212
|
// - Focus on user-visible changes in the SVG rendering
|
|
1206
1213
|
// ============================================================================
|
|
1214
|
+
export const HorizontalDateCategoryAxisTitlePlacementTests: StoryObj<typeof Chart> = {
|
|
1215
|
+
name: 'Horizontal Date/Category Axis Title Placement Tests',
|
|
1216
|
+
args: {
|
|
1217
|
+
config: {
|
|
1218
|
+
...barChartEditorTest,
|
|
1219
|
+
title: 'Horizontal Bar Chart Date/Category Axis Title Placement Test',
|
|
1220
|
+
orientation: 'horizontal',
|
|
1221
|
+
xAxis: {
|
|
1222
|
+
...barChartEditorTest.xAxis,
|
|
1223
|
+
label: 'Year'
|
|
1224
|
+
},
|
|
1225
|
+
yAxis: {
|
|
1226
|
+
...barChartEditorTest.yAxis,
|
|
1227
|
+
titlePlacement: 'side'
|
|
1228
|
+
}
|
|
1229
|
+
},
|
|
1230
|
+
isEditor: true
|
|
1231
|
+
},
|
|
1232
|
+
play: async ({ canvasElement }) => {
|
|
1233
|
+
const canvas = within(canvasElement)
|
|
1234
|
+
await waitForEditor(canvas)
|
|
1235
|
+
await openAccordion(canvas, 'Date/Category Axis')
|
|
1236
|
+
|
|
1237
|
+
const titlePlacementSelect = canvasElement.querySelector('select[name="titlePlacement"]') as HTMLSelectElement
|
|
1238
|
+
expect(titlePlacementSelect).toBeTruthy()
|
|
1239
|
+
const accordionItem = titlePlacementSelect.closest('.accordion__item') as HTMLElement
|
|
1240
|
+
const categoryLabelInput = accordionItem.querySelector('input[name="xAxis-null-label"]') as HTMLInputElement
|
|
1241
|
+
|
|
1242
|
+
expect(within(accordionItem).getByRole('button', { name: /date\/category axis/i })).toBeVisible()
|
|
1243
|
+
expect(categoryLabelInput).toBeTruthy()
|
|
1244
|
+
expect(categoryLabelInput.getAttribute('maxlength')).toBe('35')
|
|
1245
|
+
|
|
1246
|
+
const getAxisTitlePlacementVisualization = () => {
|
|
1247
|
+
const chartContainer = canvasElement.querySelector('.cove-visualization__body, .chart-container, .visualization')
|
|
1248
|
+
const svg = chartContainer?.querySelector('svg') || canvasElement.querySelector('svg:not(.icon)')
|
|
1249
|
+
const sideLabel = Array.from(svg?.querySelectorAll('text.y-label') || []).find(
|
|
1250
|
+
element => element.textContent?.trim() === 'Year'
|
|
1251
|
+
)
|
|
1252
|
+
const topLabel = chartContainer?.querySelector('.y-axis-top-title')
|
|
1253
|
+
|
|
1254
|
+
return {
|
|
1255
|
+
hasSideLabel: Boolean(sideLabel),
|
|
1256
|
+
sideLabelTransform: sideLabel?.getAttribute('transform') || '',
|
|
1257
|
+
topLabelText: topLabel?.textContent?.trim() || ''
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
await performAndAssert(
|
|
1262
|
+
'Move horizontal category-axis title to top',
|
|
1263
|
+
getAxisTitlePlacementVisualization,
|
|
1264
|
+
async () => {
|
|
1265
|
+
await userEvent.selectOptions(titlePlacementSelect, 'top')
|
|
1266
|
+
},
|
|
1267
|
+
(before, after) => {
|
|
1268
|
+
expect(before.hasSideLabel).toBe(true)
|
|
1269
|
+
expect(before.sideLabelTransform).toContain('rotate(-90)')
|
|
1270
|
+
expect(after.hasSideLabel).toBe(false)
|
|
1271
|
+
expect(after.topLabelText).toBe('Year')
|
|
1272
|
+
expect(categoryLabelInput.getAttribute('maxlength')).toBeNull()
|
|
1273
|
+
return true
|
|
1274
|
+
}
|
|
1275
|
+
)
|
|
1276
|
+
|
|
1277
|
+
await performAndAssert(
|
|
1278
|
+
'Move horizontal category-axis title back to side',
|
|
1279
|
+
getAxisTitlePlacementVisualization,
|
|
1280
|
+
async () => {
|
|
1281
|
+
await userEvent.selectOptions(titlePlacementSelect, 'side')
|
|
1282
|
+
},
|
|
1283
|
+
(before, after) => {
|
|
1284
|
+
expect(before.topLabelText).toBe('Year')
|
|
1285
|
+
expect(after.hasSideLabel).toBe(true)
|
|
1286
|
+
expect(after.sideLabelTransform).toContain('rotate(-90)')
|
|
1287
|
+
expect(after.topLabelText).toBe('')
|
|
1288
|
+
expect(categoryLabelInput.getAttribute('maxlength')).toBe('35')
|
|
1289
|
+
return true
|
|
1290
|
+
}
|
|
1291
|
+
)
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1207
1295
|
export const DateCategoryAxisSectionTests: StoryObj<typeof Chart> = {
|
|
1208
1296
|
name: 'Date/Category Axis Section Tests',
|
|
1209
1297
|
args: {
|
|
@@ -2969,10 +3057,10 @@ export const BarPatternSettingsTests: Story = {
|
|
|
2969
3057
|
series: [{ dataKey: 'y1' }, { dataKey: 'y2' }, { dataKey: 'y3' }, { dataKey: 'y4' }],
|
|
2970
3058
|
// Override with data suitable for pattern testing
|
|
2971
3059
|
data: [
|
|
2972
|
-
{ category: 'Q1', y1: 19000, y2: 47000, y3: 59000, y4: 91000 },
|
|
2973
|
-
{ category: 'Q2', y1: 18000, y2: 32000, y3: 19000, y4: 89000 },
|
|
2974
|
-
{ category: 'Q3', y1: 7000, y2: 38000, y3: 74000, y4: 89000 },
|
|
2975
|
-
{ category: 'Q4', y1: 15000, y2: 41000, y3: 67000, y4: 95000 }
|
|
3060
|
+
{ category: 'Q1', y1: 19000, y2: 47000, y2Portion: 23500, y3: 59000, y4: 91000 },
|
|
3061
|
+
{ category: 'Q2', y1: 18000, y2: 32000, y2Portion: 16000, y3: 19000, y4: 89000 },
|
|
3062
|
+
{ category: 'Q3', y1: 7000, y2: 38000, y2Portion: 19000, y3: 74000, y4: 89000 },
|
|
3063
|
+
{ category: 'Q4', y1: 15000, y2: 41000, y2Portion: 20500, y3: 67000, y4: 95000 }
|
|
2976
3064
|
],
|
|
2977
3065
|
patterns: [] // Start with no patterns
|
|
2978
3066
|
},
|
|
@@ -3039,7 +3127,9 @@ export const BarPatternSettingsTests: Story = {
|
|
|
3039
3127
|
// Extract pattern IDs for validation
|
|
3040
3128
|
patternIds: Array.from(patternElements).map(pattern => pattern.getAttribute('id')),
|
|
3041
3129
|
// Extract overlay classes for validation
|
|
3042
|
-
overlayClasses: Array.from(patternOverlays).map(overlay => overlay.getAttribute('class'))
|
|
3130
|
+
overlayClasses: Array.from(patternOverlays).map(overlay => overlay.getAttribute('class')),
|
|
3131
|
+
patternTypeValue: (canvasElement.querySelector('select[id*="pattern-type-"]') as HTMLSelectElement)?.value,
|
|
3132
|
+
patternColorValue: (canvasElement.querySelector('input[id*="pattern-color-"]') as HTMLInputElement)?.value
|
|
3043
3133
|
}
|
|
3044
3134
|
}
|
|
3045
3135
|
|
|
@@ -3086,6 +3176,8 @@ export const BarPatternSettingsTests: Story = {
|
|
|
3086
3176
|
// Pattern configuration should still be there and chart functional
|
|
3087
3177
|
expect(after.hasPatternConfigSections).toBe(true)
|
|
3088
3178
|
expect(after.hasChartSvg).toBe(true)
|
|
3179
|
+
expect(after.patternTypeValue).toBe('diagonalLines')
|
|
3180
|
+
expect(after.patternColorValue).toBe('#1c1d1f')
|
|
3089
3181
|
|
|
3090
3182
|
return true
|
|
3091
3183
|
}
|
|
@@ -3448,6 +3540,356 @@ export const BarPatternSettingsTests: Story = {
|
|
|
3448
3540
|
return true
|
|
3449
3541
|
}
|
|
3450
3542
|
)
|
|
3543
|
+
|
|
3544
|
+
await performAndAssert(
|
|
3545
|
+
'Switch to Portion Application - Portion controls replace exact-value controls',
|
|
3546
|
+
() => ({
|
|
3547
|
+
hasTargetControl: !!canvasElement.querySelector('select[id*="pattern-target-series-Pattern2"]'),
|
|
3548
|
+
hasValueColumnControl: !!canvasElement.querySelector('select[id*="pattern-value-key-Pattern2"]'),
|
|
3549
|
+
hasValueColumnTooltip: !!canvasElement
|
|
3550
|
+
.querySelector('select[id*="pattern-value-key-Pattern2"]')
|
|
3551
|
+
?.closest('label')
|
|
3552
|
+
?.querySelector('.cove-tooltip'),
|
|
3553
|
+
hasExactValueControl: !!canvasElement.querySelector('input[id*="pattern-datavalue-Pattern2"]'),
|
|
3554
|
+
hasStandalonePortionGuidance:
|
|
3555
|
+
canvas.queryByText(
|
|
3556
|
+
'Portion values must be greater than zero and no greater than the corresponding total.'
|
|
3557
|
+
) !== null,
|
|
3558
|
+
legendBackgroundFill: canvasElement
|
|
3559
|
+
.querySelector(
|
|
3560
|
+
'.legend-item--pattern .legend-shape-svg > circle, .legend-item--pattern .legend-shape-svg > rect'
|
|
3561
|
+
)
|
|
3562
|
+
?.getAttribute('fill')
|
|
3563
|
+
}),
|
|
3564
|
+
async () => {
|
|
3565
|
+
const applicationSelect = canvasElement.querySelector(
|
|
3566
|
+
'select[id*="pattern-application-Pattern2"]'
|
|
3567
|
+
) as HTMLSelectElement
|
|
3568
|
+
expect(applicationSelect).toBeTruthy()
|
|
3569
|
+
await userEvent.selectOptions(applicationSelect, 'portion')
|
|
3570
|
+
},
|
|
3571
|
+
(before, after) =>
|
|
3572
|
+
before.legendBackgroundFill === 'white' &&
|
|
3573
|
+
after.hasTargetControl &&
|
|
3574
|
+
after.hasValueColumnControl &&
|
|
3575
|
+
after.hasValueColumnTooltip &&
|
|
3576
|
+
!after.hasStandalonePortionGuidance &&
|
|
3577
|
+
!after.hasExactValueControl
|
|
3578
|
+
)
|
|
3579
|
+
|
|
3580
|
+
await performAndAssert(
|
|
3581
|
+
'Configure Portion Value - Half-height overlays render at the bar end',
|
|
3582
|
+
getPatternVisualizationState,
|
|
3583
|
+
async () => {
|
|
3584
|
+
const valueColumnSelect = canvasElement.querySelector(
|
|
3585
|
+
'select[id*="pattern-value-key-Pattern2"]'
|
|
3586
|
+
) as HTMLSelectElement
|
|
3587
|
+
expect(valueColumnSelect).toBeTruthy()
|
|
3588
|
+
await userEvent.selectOptions(valueColumnSelect, 'y2Portion')
|
|
3589
|
+
},
|
|
3590
|
+
(_before, after) => after.patternOverlaysCount === 4,
|
|
3591
|
+
after => {
|
|
3592
|
+
expect(after.patternDefinitionsCount).toBe(1)
|
|
3593
|
+
expect(canvasElement.querySelectorAll('.legend-item--pattern')).toHaveLength(1)
|
|
3594
|
+
|
|
3595
|
+
const overlay = canvasElement.querySelector('.pattern-overlay--portion') as SVGGraphicsElement
|
|
3596
|
+
const baseBar = overlay?.parentElement?.querySelector('path[id^="barGroup"]') as SVGGraphicsElement
|
|
3597
|
+
const boundary = overlay?.parentElement?.querySelector('.pattern-overlay__boundary') as SVGLineElement
|
|
3598
|
+
const legendBackground = canvasElement.querySelector(
|
|
3599
|
+
'.legend-item--pattern .legend-shape-svg > circle, .legend-item--pattern .legend-shape-svg > rect'
|
|
3600
|
+
) as SVGGraphicsElement
|
|
3601
|
+
expect(overlay).toBeTruthy()
|
|
3602
|
+
expect(baseBar).toBeTruthy()
|
|
3603
|
+
expect(legendBackground).toBeTruthy()
|
|
3604
|
+
expect(canvasElement.querySelectorAll('.pattern-overlay__boundary')).toHaveLength(4)
|
|
3605
|
+
expect(legendBackground.getAttribute('fill')).toBe(baseBar.getAttribute('fill'))
|
|
3606
|
+
expect(overlay.getBBox().height / baseBar.getBBox().height).toBeCloseTo(0.5, 2)
|
|
3607
|
+
expect(overlay.getBBox().y).toBeCloseTo(baseBar.getBBox().y, 2)
|
|
3608
|
+
expect(Number(boundary.getAttribute('y1'))).toBeCloseTo(overlay.getBBox().y + overlay.getBBox().height, 2)
|
|
3609
|
+
expect(boundary.getAttribute('stroke-width')).toBe('0.75')
|
|
3610
|
+
expect(canvas.getByTestId('portion-pattern-column-note')).toBeVisible()
|
|
3611
|
+
}
|
|
3612
|
+
)
|
|
3613
|
+
|
|
3614
|
+
await performAndAssert(
|
|
3615
|
+
'Beginning Placement - Portion overlay moves to the bar baseline',
|
|
3616
|
+
() => {
|
|
3617
|
+
const overlay = canvasElement.querySelector('.pattern-overlay--portion') as SVGGraphicsElement
|
|
3618
|
+
const baseBar = overlay?.parentElement?.querySelector('path[id^="barGroup"]') as SVGGraphicsElement
|
|
3619
|
+
const boundary = overlay?.parentElement?.querySelector('.pattern-overlay__boundary') as SVGLineElement
|
|
3620
|
+
return overlay && baseBar && boundary
|
|
3621
|
+
? {
|
|
3622
|
+
overlayBottom: overlay.getBBox().y + overlay.getBBox().height,
|
|
3623
|
+
baseBottom: baseBar.getBBox().y + baseBar.getBBox().height,
|
|
3624
|
+
overlayTop: overlay.getBBox().y,
|
|
3625
|
+
boundaryY: Number(boundary.getAttribute('y1'))
|
|
3626
|
+
}
|
|
3627
|
+
: { overlayBottom: 0, baseBottom: 1, overlayTop: 0, boundaryY: 1 }
|
|
3628
|
+
},
|
|
3629
|
+
async () => {
|
|
3630
|
+
const placementSelect = canvasElement.querySelector(
|
|
3631
|
+
'select[id*="pattern-placement-Pattern2"]'
|
|
3632
|
+
) as HTMLSelectElement
|
|
3633
|
+
expect(placementSelect).toBeTruthy()
|
|
3634
|
+
await userEvent.selectOptions(placementSelect, 'start')
|
|
3635
|
+
},
|
|
3636
|
+
(_before, after) =>
|
|
3637
|
+
Math.abs(after.overlayBottom - after.baseBottom) < 0.01 && Math.abs(after.boundaryY - after.overlayTop) < 0.01
|
|
3638
|
+
)
|
|
3639
|
+
|
|
3640
|
+
await performAndAssert(
|
|
3641
|
+
'Duplicate Portion Target - Used series is unavailable to later patterns',
|
|
3642
|
+
() => ({ patternCount: patternSettingsPanel?.querySelectorAll('.series-item--chart').length || 0 }),
|
|
3643
|
+
async () => {
|
|
3644
|
+
await userEvent.click(canvas.getByRole('button', { name: /add pattern/i }))
|
|
3645
|
+
const applicationSelect = canvasElement.querySelector(
|
|
3646
|
+
'select[id*="pattern-application-Pattern2"]'
|
|
3647
|
+
) as HTMLSelectElement
|
|
3648
|
+
const applicationSelects = canvasElement.querySelectorAll('select[id*="pattern-application-"]')
|
|
3649
|
+
const newApplicationSelect = applicationSelects[applicationSelects.length - 1] as HTMLSelectElement
|
|
3650
|
+
expect(applicationSelect).toBeTruthy()
|
|
3651
|
+
expect(newApplicationSelect).toBeTruthy()
|
|
3652
|
+
await userEvent.selectOptions(newApplicationSelect, 'portion')
|
|
3653
|
+
|
|
3654
|
+
const targetSelects = canvasElement.querySelectorAll('select[id*="pattern-target-series-"]')
|
|
3655
|
+
const newTargetSelect = targetSelects[targetSelects.length - 1] as HTMLSelectElement
|
|
3656
|
+
expect(Array.from(newTargetSelect.options).some(option => option.value === 'y2')).toBe(false)
|
|
3657
|
+
},
|
|
3658
|
+
(before, after) => after.patternCount === before.patternCount + 1
|
|
3659
|
+
)
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3663
|
+
const portionPatternLegend = {
|
|
3664
|
+
...mockScatterPlot.legend,
|
|
3665
|
+
patterns: {
|
|
3666
|
+
Portion: {
|
|
3667
|
+
application: 'portion' as const,
|
|
3668
|
+
dataKey: 'y1',
|
|
3669
|
+
patternValueKey: 'portion',
|
|
3670
|
+
placement: 'end' as const,
|
|
3671
|
+
label: 'Portion',
|
|
3672
|
+
color: '#000000',
|
|
3673
|
+
shape: 'diagonalLines',
|
|
3674
|
+
patternSize: 10
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
|
|
3679
|
+
const portionPatternData = [
|
|
3680
|
+
{ category: 'Q1', y1: 100, y2: 80, portion: 25 },
|
|
3681
|
+
{ category: 'Q2', y1: 80, y2: 60, portion: 20 }
|
|
3682
|
+
]
|
|
3683
|
+
|
|
3684
|
+
export const StackedPortionPatternEditorWarningTests: Story = {
|
|
3685
|
+
name: 'Stacked Portion Pattern Editor Warning Tests',
|
|
3686
|
+
args: {
|
|
3687
|
+
config: {
|
|
3688
|
+
...mockScatterPlot,
|
|
3689
|
+
visualizationType: 'Bar',
|
|
3690
|
+
visualizationSubType: 'stacked',
|
|
3691
|
+
orientation: 'vertical',
|
|
3692
|
+
xAxis: { ...mockScatterPlot.xAxis, type: 'categorical', dataKey: 'category' },
|
|
3693
|
+
yAxis: { ...mockScatterPlot.yAxis, type: 'continuous', dataKey: 'y1' },
|
|
3694
|
+
series: [
|
|
3695
|
+
{ ...mockScatterPlot.series[0], dataKey: 'y1', type: 'Bar' },
|
|
3696
|
+
{ ...mockScatterPlot.series[1], dataKey: 'y2', type: 'Bar' }
|
|
3697
|
+
],
|
|
3698
|
+
data: portionPatternData,
|
|
3699
|
+
legend: {
|
|
3700
|
+
...mockScatterPlot.legend,
|
|
3701
|
+
patterns: {
|
|
3702
|
+
WarningPattern: {
|
|
3703
|
+
application: 'value',
|
|
3704
|
+
dataKey: 'category',
|
|
3705
|
+
dataValue: 'Q1',
|
|
3706
|
+
label: 'Warning Pattern',
|
|
3707
|
+
color: '#000000',
|
|
3708
|
+
shape: 'diagonalLines',
|
|
3709
|
+
patternSize: 10
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
},
|
|
3714
|
+
isEditor: true
|
|
3715
|
+
},
|
|
3716
|
+
play: async ({ canvasElement }) => {
|
|
3717
|
+
const canvas = within(canvasElement)
|
|
3718
|
+
await waitForEditor(canvas)
|
|
3719
|
+
await openAccordion(canvas, 'Pattern Settings')
|
|
3720
|
+
|
|
3721
|
+
await performAndAssert(
|
|
3722
|
+
'Select Portion Application - Unsupported stacked charts show an inline warning',
|
|
3723
|
+
() => ({
|
|
3724
|
+
application: (
|
|
3725
|
+
canvasElement.querySelector('select[id*="pattern-application-WarningPattern"]') as HTMLSelectElement
|
|
3726
|
+
)?.value,
|
|
3727
|
+
warningVisible:
|
|
3728
|
+
canvas.queryByText(
|
|
3729
|
+
/portion patterns are supported only for regular unstacked bar charts and combo charts/i
|
|
3730
|
+
) !== null,
|
|
3731
|
+
targetOptionCount:
|
|
3732
|
+
(canvasElement.querySelector('select[id*="pattern-target-series-WarningPattern"]') as HTMLSelectElement)
|
|
3733
|
+
?.options.length || 0
|
|
3734
|
+
}),
|
|
3735
|
+
async () => {
|
|
3736
|
+
const applicationSelect = canvasElement.querySelector(
|
|
3737
|
+
'select[id*="pattern-application-WarningPattern"]'
|
|
3738
|
+
) as HTMLSelectElement
|
|
3739
|
+
expect(applicationSelect).toBeTruthy()
|
|
3740
|
+
await userEvent.selectOptions(applicationSelect, 'portion')
|
|
3741
|
+
},
|
|
3742
|
+
(_before, after) => after.application === 'portion' && after.warningVisible && after.targetOptionCount === 1
|
|
3743
|
+
)
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
|
|
3747
|
+
export const HorizontalPortionPatternRenderingTests: Story = {
|
|
3748
|
+
name: 'Horizontal Portion Pattern Rendering Tests',
|
|
3749
|
+
args: {
|
|
3750
|
+
config: {
|
|
3751
|
+
...mockScatterPlot,
|
|
3752
|
+
visualizationType: 'Bar',
|
|
3753
|
+
visualizationSubType: 'regular',
|
|
3754
|
+
orientation: 'horizontal',
|
|
3755
|
+
xAxis: { ...mockScatterPlot.xAxis, type: 'categorical', dataKey: 'category' },
|
|
3756
|
+
yAxis: { ...mockScatterPlot.yAxis, type: 'continuous', dataKey: 'y1' },
|
|
3757
|
+
series: [{ ...mockScatterPlot.series[0], dataKey: 'y1', type: 'Bar' }],
|
|
3758
|
+
data: portionPatternData,
|
|
3759
|
+
legend: portionPatternLegend
|
|
3760
|
+
},
|
|
3761
|
+
isEditor: false
|
|
3762
|
+
},
|
|
3763
|
+
play: async ({ canvasElement }) => {
|
|
3764
|
+
await assertVisualizationRendered(canvasElement)
|
|
3765
|
+
|
|
3766
|
+
const overlay = canvasElement.querySelector('.pattern-overlay--portion') as SVGGraphicsElement
|
|
3767
|
+
const baseBar = overlay?.parentElement?.querySelector('path[id^="barGroup"]') as SVGGraphicsElement
|
|
3768
|
+
const boundary = overlay?.parentElement?.querySelector('.pattern-overlay__boundary') as SVGLineElement
|
|
3769
|
+
expect(overlay).toBeTruthy()
|
|
3770
|
+
expect(baseBar).toBeTruthy()
|
|
3771
|
+
expect(boundary).toBeTruthy()
|
|
3772
|
+
expect(overlay.getBBox().width / baseBar.getBBox().width).toBeCloseTo(0.25, 2)
|
|
3773
|
+
expect(overlay.getBBox().x + overlay.getBBox().width).toBeCloseTo(baseBar.getBBox().x + baseBar.getBBox().width, 2)
|
|
3774
|
+
expect(Number(boundary.getAttribute('x1'))).toBeCloseTo(overlay.getBBox().x, 2)
|
|
3775
|
+
expect(boundary.getAttribute('stroke')).toBe('#000000')
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
|
|
3779
|
+
export const ComboPortionPatternRenderingTests: Story = {
|
|
3780
|
+
name: 'Combo Portion Pattern Rendering Tests',
|
|
3781
|
+
args: {
|
|
3782
|
+
config: {
|
|
3783
|
+
...mockScatterPlot,
|
|
3784
|
+
visualizationType: 'Combo',
|
|
3785
|
+
visualizationSubType: 'regular',
|
|
3786
|
+
orientation: 'vertical',
|
|
3787
|
+
xAxis: { ...mockScatterPlot.xAxis, type: 'categorical', dataKey: 'category' },
|
|
3788
|
+
yAxis: { ...mockScatterPlot.yAxis, type: 'continuous', dataKey: 'y1' },
|
|
3789
|
+
series: [
|
|
3790
|
+
{ ...mockScatterPlot.series[0], dataKey: 'y1', type: 'Bar' },
|
|
3791
|
+
{ ...mockScatterPlot.series[1], dataKey: 'y2', type: 'Line' }
|
|
3792
|
+
],
|
|
3793
|
+
data: portionPatternData,
|
|
3794
|
+
legend: portionPatternLegend
|
|
3795
|
+
},
|
|
3796
|
+
isEditor: false
|
|
3797
|
+
},
|
|
3798
|
+
play: async ({ canvasElement }) => {
|
|
3799
|
+
await assertVisualizationRendered(canvasElement)
|
|
3800
|
+
expect(canvasElement.querySelectorAll('.pattern-overlay--portion')).toHaveLength(2)
|
|
3801
|
+
expect(canvasElement.querySelectorAll('.pattern-overlay__boundary')).toHaveLength(2)
|
|
3802
|
+
expect(canvasElement.querySelector('path[id^="barGroup"]')).toBeTruthy()
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
export const InvalidPortionPatternRenderingTests: Story = {
|
|
3807
|
+
name: 'Invalid Portion Pattern Rendering Tests',
|
|
3808
|
+
args: {
|
|
3809
|
+
config: {
|
|
3810
|
+
...mockScatterPlot,
|
|
3811
|
+
visualizationType: 'Bar',
|
|
3812
|
+
visualizationSubType: 'regular',
|
|
3813
|
+
orientation: 'vertical',
|
|
3814
|
+
xAxis: { ...mockScatterPlot.xAxis, type: 'categorical', dataKey: 'category' },
|
|
3815
|
+
yAxis: { ...mockScatterPlot.yAxis, type: 'continuous', dataKey: 'y1' },
|
|
3816
|
+
series: [{ ...mockScatterPlot.series[0], dataKey: 'y1', type: 'Bar' }],
|
|
3817
|
+
data: [
|
|
3818
|
+
{ category: 'Missing', y1: 100 },
|
|
3819
|
+
{ category: 'Nonnumeric', y1: 100, portion: 'invalid' },
|
|
3820
|
+
{ category: 'Zero', y1: 100, portion: 0 },
|
|
3821
|
+
{ category: 'Negative', y1: 100, portion: -10 },
|
|
3822
|
+
{ category: 'Greater', y1: 100, portion: 101 },
|
|
3823
|
+
{ category: 'Negative total', y1: -100, portion: 10 }
|
|
3824
|
+
],
|
|
3825
|
+
legend: portionPatternLegend
|
|
3826
|
+
},
|
|
3827
|
+
isEditor: false
|
|
3828
|
+
},
|
|
3829
|
+
play: async ({ canvasElement }) => {
|
|
3830
|
+
await assertVisualizationRendered(canvasElement)
|
|
3831
|
+
expect(canvasElement.querySelectorAll('.pattern-overlay--portion')).toHaveLength(0)
|
|
3832
|
+
expect(canvasElement.querySelectorAll('.pattern-overlay__boundary')).toHaveLength(0)
|
|
3833
|
+
expect(canvasElement.querySelectorAll('path[id^="barGroup"]')).toHaveLength(6)
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
|
|
3837
|
+
export const BrushPortionPatternRenderingTests: Story = {
|
|
3838
|
+
name: 'Brush Portion Pattern Rendering Tests',
|
|
3839
|
+
args: {
|
|
3840
|
+
config: {
|
|
3841
|
+
...mockScatterPlot,
|
|
3842
|
+
visualizationType: 'Bar',
|
|
3843
|
+
visualizationSubType: 'regular',
|
|
3844
|
+
orientation: 'vertical',
|
|
3845
|
+
xAxis: {
|
|
3846
|
+
...mockScatterPlot.xAxis,
|
|
3847
|
+
type: 'date-time',
|
|
3848
|
+
dataKey: 'date',
|
|
3849
|
+
dateParseFormat: '%m/%d/%Y',
|
|
3850
|
+
dateDisplayFormat: '%m/%d/%Y',
|
|
3851
|
+
brushActive: true
|
|
3852
|
+
},
|
|
3853
|
+
yAxis: { ...mockScatterPlot.yAxis, type: 'continuous', dataKey: 'y1' },
|
|
3854
|
+
series: [{ ...mockScatterPlot.series[0], dataKey: 'y1', type: 'Bar' }],
|
|
3855
|
+
data: [
|
|
3856
|
+
{ date: '01/01/2025', y1: 100, portion: 50 },
|
|
3857
|
+
{ date: '02/01/2025', y1: 80, portion: 40 },
|
|
3858
|
+
{ date: '03/01/2025', y1: 60, portion: 30 }
|
|
3859
|
+
],
|
|
3860
|
+
legend: portionPatternLegend
|
|
3861
|
+
},
|
|
3862
|
+
isEditor: false
|
|
3863
|
+
},
|
|
3864
|
+
play: async ({ canvasElement }) => {
|
|
3865
|
+
await assertVisualizationRendered(canvasElement)
|
|
3866
|
+
|
|
3867
|
+
await Promise.all([
|
|
3868
|
+
waitForPresence('.pattern-overlay--portion:not(.pattern-overlay--brush)', canvasElement),
|
|
3869
|
+
waitForPresence('.pattern-overlay--brush', canvasElement),
|
|
3870
|
+
waitForPresence('.pattern-overlay__boundary:not(.pattern-overlay__boundary--brush)', canvasElement),
|
|
3871
|
+
waitForPresence('.pattern-overlay__boundary--brush', canvasElement)
|
|
3872
|
+
])
|
|
3873
|
+
|
|
3874
|
+
const mainOverlay = canvasElement.querySelector(
|
|
3875
|
+
'.pattern-overlay--portion:not(.pattern-overlay--brush)'
|
|
3876
|
+
) as SVGGraphicsElement
|
|
3877
|
+
const brushOverlay = canvasElement.querySelector('.pattern-overlay--brush') as SVGGraphicsElement
|
|
3878
|
+
const mainBoundary = canvasElement.querySelector(
|
|
3879
|
+
'.pattern-overlay__boundary:not(.pattern-overlay__boundary--brush)'
|
|
3880
|
+
) as SVGLineElement
|
|
3881
|
+
const brushBoundary = canvasElement.querySelector('.pattern-overlay__boundary--brush') as SVGLineElement
|
|
3882
|
+
expect(mainOverlay).toBeTruthy()
|
|
3883
|
+
expect(brushOverlay).toBeTruthy()
|
|
3884
|
+
expect(mainBoundary).toBeTruthy()
|
|
3885
|
+
expect(brushBoundary).toBeTruthy()
|
|
3886
|
+
|
|
3887
|
+
const mainBase = mainOverlay.parentElement?.querySelector('path[id^="barGroup"]') as SVGGraphicsElement
|
|
3888
|
+
const brushBase = brushOverlay.parentElement?.querySelector('rect:not(.pattern-overlay)') as SVGGraphicsElement
|
|
3889
|
+
expect(mainOverlay.getBBox().height / mainBase.getBBox().height).toBeCloseTo(0.5, 2)
|
|
3890
|
+
expect(brushOverlay.getBBox().height / brushBase.getBBox().height).toBeCloseTo(0.5, 2)
|
|
3891
|
+
expect(mainBoundary.getAttribute('stroke-width')).toBe('0.75')
|
|
3892
|
+
expect(brushBoundary.getAttribute('stroke-width')).toBe('0.75')
|
|
3451
3893
|
}
|
|
3452
3894
|
}
|
|
3453
3895
|
|