@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
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { expect, fireEvent, userEvent, within } from 'storybook/test'
|
|
3
|
+
import {
|
|
4
|
+
assertVisualizationRendered,
|
|
5
|
+
openAccordion,
|
|
6
|
+
performAndAssert,
|
|
7
|
+
waitForEditor,
|
|
8
|
+
waitForPresence
|
|
9
|
+
} from '@cdc/core/helpers/testing'
|
|
10
|
+
import Chart from '../CdcChartComponent'
|
|
11
|
+
import annualChangeConfig from '../../examples/feature/bar/bar-chart-race-annual-change.json'
|
|
12
|
+
|
|
13
|
+
const meta: Meta<typeof Chart> = {
|
|
14
|
+
title: 'Components/Templates/Chart/Bar Race',
|
|
15
|
+
component: Chart,
|
|
16
|
+
parameters: { layout: 'fullscreen' }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default meta
|
|
20
|
+
type Story = StoryObj<typeof Chart>
|
|
21
|
+
|
|
22
|
+
export const AnnualChangesOverTime: Story = {
|
|
23
|
+
name: 'Annual Changes Over Time',
|
|
24
|
+
args: {
|
|
25
|
+
config: annualChangeConfig as any,
|
|
26
|
+
isEditor: false,
|
|
27
|
+
interactionLabel: 'Annual program reach bar race'
|
|
28
|
+
},
|
|
29
|
+
play: async ({ canvasElement }) => {
|
|
30
|
+
const canvas = within(canvasElement)
|
|
31
|
+
await assertVisualizationRendered(canvasElement)
|
|
32
|
+
|
|
33
|
+
expect(canvasElement.querySelector('.bar-chart-race__frame')).toHaveTextContent('2018')
|
|
34
|
+
expect(canvasElement.querySelectorAll('.bar-chart-race__row')).toHaveLength(8)
|
|
35
|
+
const frameAxis = canvas.getByRole('group', { name: 'Year axis' })
|
|
36
|
+
expect(frameAxis).toHaveTextContent('2018')
|
|
37
|
+
expect(frameAxis).toHaveTextContent('2024')
|
|
38
|
+
expect(frameAxis.querySelectorAll('.bar-chart-race__frame-axis-tick')).toHaveLength(7)
|
|
39
|
+
expect(frameAxis.querySelector('[aria-current="step"]')).toHaveTextContent('2018')
|
|
40
|
+
expect(canvasElement.querySelector('.component--has-accent')).not.toBeInTheDocument()
|
|
41
|
+
expect(canvas.getByRole('button', { name: 'Play' })).toBeInTheDocument()
|
|
42
|
+
expect(canvasElement.querySelector('[data-category="Northeast"] .bar-chart-race__value')).toHaveTextContent('74')
|
|
43
|
+
expect(canvasElement.querySelector('[data-category="Mountain"] .bar-chart-race__value')).toHaveTextContent('61')
|
|
44
|
+
|
|
45
|
+
await performAndAssert(
|
|
46
|
+
'Advance the annual time series',
|
|
47
|
+
() => canvasElement.querySelector('.bar-chart-race__frame')?.textContent?.trim(),
|
|
48
|
+
async () => userEvent.click(canvas.getByRole('button', { name: 'Play' })),
|
|
49
|
+
(before, after) => before === '2018' && after === '2019'
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
expect(frameAxis.querySelector('[aria-current="step"]')).toHaveTextContent('2019')
|
|
53
|
+
expect(canvasElement.querySelector('[data-category="Northeast"] .bar-chart-race__value')).toHaveTextContent('73')
|
|
54
|
+
expect(canvasElement.querySelector('[data-category="Mountain"] .bar-chart-race__value')).toHaveTextContent('67')
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const GeneralSectionTests: Story = {
|
|
59
|
+
name: 'Editor: General Section',
|
|
60
|
+
parameters: { test: { timeout: 30000 } },
|
|
61
|
+
beforeEach: () => {
|
|
62
|
+
const originalUrl = window.location.href
|
|
63
|
+
const originalState = window.history.state
|
|
64
|
+
const developerUrl = new URL(originalUrl)
|
|
65
|
+
developerUrl.searchParams.set('isCoveDeveloper', 'true')
|
|
66
|
+
window.history.replaceState(originalState, '', developerUrl)
|
|
67
|
+
|
|
68
|
+
return () => window.history.replaceState(originalState, '', originalUrl)
|
|
69
|
+
},
|
|
70
|
+
args: {
|
|
71
|
+
config: {
|
|
72
|
+
...annualChangeConfig,
|
|
73
|
+
visualizationSubType: 'regular',
|
|
74
|
+
orientation: 'vertical'
|
|
75
|
+
} as any,
|
|
76
|
+
isEditor: true,
|
|
77
|
+
interactionLabel: 'Bar race editor story'
|
|
78
|
+
},
|
|
79
|
+
play: async ({ canvasElement }) => {
|
|
80
|
+
const canvas = within(canvasElement)
|
|
81
|
+
await waitForEditor(canvas)
|
|
82
|
+
await openAccordion(canvas, 'General')
|
|
83
|
+
|
|
84
|
+
expect(canvasElement.querySelector('.bar-chart-race')).not.toBeInTheDocument()
|
|
85
|
+
const subtype = canvas.getByLabelText(/chart subtype/i) as HTMLSelectElement
|
|
86
|
+
expect(Array.from(subtype.options).map(option => option.value)).toEqual(['regular', 'stacked', 'racing'])
|
|
87
|
+
await performAndAssert(
|
|
88
|
+
'Enable racing mode',
|
|
89
|
+
() => Boolean(canvasElement.querySelector('.bar-chart-race')),
|
|
90
|
+
async () => userEvent.selectOptions(subtype, 'racing'),
|
|
91
|
+
(_before, after) => after
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
await waitForPresence('.bar-chart-race', canvasElement)
|
|
95
|
+
const labelPlacement = canvasElement.querySelector('select[name="labelPlacement"]') as HTMLSelectElement
|
|
96
|
+
expect(labelPlacement).toBeInTheDocument()
|
|
97
|
+
expect(Array.from(labelPlacement.options).map(option => option.value)).toEqual([
|
|
98
|
+
'Below Bar',
|
|
99
|
+
'On Date/Category Axis'
|
|
100
|
+
])
|
|
101
|
+
await performAndAssert(
|
|
102
|
+
'Move race labels below their bars',
|
|
103
|
+
() => {
|
|
104
|
+
const race = canvasElement.querySelector('.bar-chart-race')
|
|
105
|
+
return {
|
|
106
|
+
labelsBelowBar: race?.classList.contains('bar-chart-race--labels-below-bar') ?? false,
|
|
107
|
+
labelsOnAxis: race?.classList.contains('bar-chart-race--labels-on-axis') ?? false,
|
|
108
|
+
plotHeight: Number.parseFloat(
|
|
109
|
+
(canvasElement.querySelector('.bar-chart-race__plot') as HTMLElement | null)?.style.height || '0'
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
async () => userEvent.selectOptions(labelPlacement, 'Below Bar'),
|
|
114
|
+
(before, after) =>
|
|
115
|
+
before.labelsOnAxis &&
|
|
116
|
+
!before.labelsBelowBar &&
|
|
117
|
+
after.labelsBelowBar &&
|
|
118
|
+
!after.labelsOnAxis &&
|
|
119
|
+
after.plotHeight > before.plotHeight
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
const displayNumbersOnBar = canvas.getByLabelText(/display numbers on bar/i) as HTMLInputElement
|
|
123
|
+
await performAndAssert(
|
|
124
|
+
'Move race values after their bars',
|
|
125
|
+
() => ({
|
|
126
|
+
inside: canvasElement.querySelectorAll('.bar-chart-race__value--inside').length,
|
|
127
|
+
after: canvasElement.querySelectorAll('.bar-chart-race__value--after').length
|
|
128
|
+
}),
|
|
129
|
+
async () => userEvent.click(displayNumbersOnBar),
|
|
130
|
+
(before, after) => before.inside > 0 && before.after === 0 && after.inside === 0 && after.after > 0
|
|
131
|
+
)
|
|
132
|
+
await performAndAssert(
|
|
133
|
+
'Show race value labels on fitting bars',
|
|
134
|
+
() => ({
|
|
135
|
+
inside: canvasElement.querySelectorAll('.bar-chart-race__value--inside').length,
|
|
136
|
+
after: canvasElement.querySelectorAll('.bar-chart-race__value--after').length
|
|
137
|
+
}),
|
|
138
|
+
async () => userEvent.click(displayNumbersOnBar),
|
|
139
|
+
(_before, after) => after.inside > 0
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
const timing = canvas.getByRole('slider', { name: /seconds per time step/i }) as HTMLInputElement
|
|
143
|
+
expect(timing.valueAsNumber).toBe(0.5)
|
|
144
|
+
expect(timing).toHaveAttribute('min', '0')
|
|
145
|
+
expect(timing).toHaveAttribute('max', '1.5')
|
|
146
|
+
expect(timing).toHaveAttribute('step', '0.5')
|
|
147
|
+
|
|
148
|
+
const maximumBars = canvas.getByLabelText(/maximum bars/i) as HTMLInputElement
|
|
149
|
+
await performAndAssert(
|
|
150
|
+
'Limit visible bars',
|
|
151
|
+
() => canvasElement.querySelectorAll('.bar-chart-race__row').length,
|
|
152
|
+
async () => {
|
|
153
|
+
await userEvent.clear(maximumBars)
|
|
154
|
+
await userEvent.type(maximumBars, '2')
|
|
155
|
+
await userEvent.tab()
|
|
156
|
+
},
|
|
157
|
+
(_before, after) => after === 2
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
await fireEvent.change(timing, { target: { value: '0' } })
|
|
161
|
+
await performAndAssert(
|
|
162
|
+
'Apply instant playback timing',
|
|
163
|
+
() => canvasElement.querySelector('.bar-chart-race__frame')?.textContent?.trim(),
|
|
164
|
+
async () => userEvent.click(canvas.getByRole('button', { name: 'Play' })),
|
|
165
|
+
(before, after) => before === '2018' && after === '2024'
|
|
166
|
+
)
|
|
167
|
+
expect(canvas.getByRole('button', { name: 'Replay' })).toBeInTheDocument()
|
|
168
|
+
|
|
169
|
+
await performAndAssert(
|
|
170
|
+
'Switch to stacked bars without playback',
|
|
171
|
+
() => Boolean(canvasElement.querySelector('.bar-chart-race')),
|
|
172
|
+
async () => userEvent.selectOptions(subtype, 'stacked'),
|
|
173
|
+
(before, after) => before && !after
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export const ExistingRaceSubtypeTests: Story = {
|
|
179
|
+
name: 'Editor: Existing Race Subtype',
|
|
180
|
+
parameters: { test: { timeout: 30000 } },
|
|
181
|
+
args: {
|
|
182
|
+
config: annualChangeConfig as any,
|
|
183
|
+
isEditor: true,
|
|
184
|
+
interactionLabel: 'Existing bar race subtype story'
|
|
185
|
+
},
|
|
186
|
+
play: async ({ canvasElement }) => {
|
|
187
|
+
const canvas = within(canvasElement)
|
|
188
|
+
await waitForEditor(canvas)
|
|
189
|
+
await openAccordion(canvas, 'General')
|
|
190
|
+
|
|
191
|
+
const subtype = canvas.getByLabelText(/chart subtype/i) as HTMLSelectElement
|
|
192
|
+
expect(Array.from(subtype.options).map(option => option.value)).toContain('racing')
|
|
193
|
+
await performAndAssert(
|
|
194
|
+
'Switch an existing race to Standard without losing Racing',
|
|
195
|
+
() => ({
|
|
196
|
+
options: Array.from((canvas.getByLabelText(/chart subtype/i) as HTMLSelectElement).options).map(
|
|
197
|
+
option => option.value
|
|
198
|
+
),
|
|
199
|
+
raceRendered: Boolean(canvasElement.querySelector('.bar-chart-race')),
|
|
200
|
+
subtype: (canvas.getByLabelText(/chart subtype/i) as HTMLSelectElement).value
|
|
201
|
+
}),
|
|
202
|
+
async () => userEvent.selectOptions(subtype, 'regular'),
|
|
203
|
+
(before, after) =>
|
|
204
|
+
before.subtype === 'racing' &&
|
|
205
|
+
before.raceRendered &&
|
|
206
|
+
after.subtype === 'regular' &&
|
|
207
|
+
!after.raceRendered &&
|
|
208
|
+
after.options.includes('racing')
|
|
209
|
+
)
|
|
210
|
+
await performAndAssert(
|
|
211
|
+
'Switch the same chart back to Racing',
|
|
212
|
+
() => Boolean(canvasElement.querySelector('.bar-chart-race')),
|
|
213
|
+
async () => userEvent.selectOptions(canvas.getByLabelText(/chart subtype/i), 'racing'),
|
|
214
|
+
(before, after) => !before && after
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export const ValueAxisTests: Story = {
|
|
220
|
+
name: 'Editor: Race Value Axis',
|
|
221
|
+
parameters: { test: { timeout: 30000 } },
|
|
222
|
+
args: {
|
|
223
|
+
config: annualChangeConfig as any,
|
|
224
|
+
isEditor: true,
|
|
225
|
+
interactionLabel: 'Bar race value axis story'
|
|
226
|
+
},
|
|
227
|
+
play: async ({ canvasElement }) => {
|
|
228
|
+
const canvas = within(canvasElement)
|
|
229
|
+
await waitForEditor(canvas)
|
|
230
|
+
const valueAxisButton = canvas.getByRole('button', { name: 'Value Axis' })
|
|
231
|
+
await performAndAssert(
|
|
232
|
+
'Open the race Value Axis panel',
|
|
233
|
+
() => valueAxisButton.getAttribute('aria-expanded'),
|
|
234
|
+
async () => userEvent.click(valueAxisButton),
|
|
235
|
+
(before, after) => before === 'false' && after === 'true'
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
expect(canvas.queryByLabelText('Axis Type')).not.toBeInTheDocument()
|
|
239
|
+
expect(canvas.queryByLabelText('Hide Axis')).not.toBeInTheDocument()
|
|
240
|
+
expect(canvas.queryByLabelText('Hide Tick Labels')).not.toBeInTheDocument()
|
|
241
|
+
expect(canvasElement.querySelector('#checkbox-dataFormat-none-commas')).toBeInTheDocument()
|
|
242
|
+
expect(canvasElement.querySelector('#input-dataFormat-none-roundTo')).toBeInTheDocument()
|
|
243
|
+
const prefix = canvasElement.querySelector('#input-dataFormat-none-prefix') as HTMLInputElement
|
|
244
|
+
await performAndAssert(
|
|
245
|
+
'Apply value-axis number formatting to race labels',
|
|
246
|
+
() => canvasElement.querySelector('[data-category="Northeast"] .bar-chart-race__value')?.textContent,
|
|
247
|
+
async () => {
|
|
248
|
+
await userEvent.type(prefix, '$')
|
|
249
|
+
await userEvent.tab()
|
|
250
|
+
},
|
|
251
|
+
(before, after) => before === '74K' && after === '$74K'
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export const DateCategoryAxisTests: Story = {
|
|
257
|
+
name: 'Editor: Race Date Category Axis',
|
|
258
|
+
parameters: { test: { timeout: 30000 } },
|
|
259
|
+
args: {
|
|
260
|
+
config: {
|
|
261
|
+
...annualChangeConfig,
|
|
262
|
+
exclusions: { active: false, keys: [] }
|
|
263
|
+
} as any,
|
|
264
|
+
isEditor: true,
|
|
265
|
+
interactionLabel: 'Bar race date category axis story'
|
|
266
|
+
},
|
|
267
|
+
play: async ({ canvasElement }) => {
|
|
268
|
+
const canvas = within(canvasElement)
|
|
269
|
+
await waitForEditor(canvas)
|
|
270
|
+
const dateCategoryAxisButton = canvas.getByRole('button', { name: 'Date/Category Axis' })
|
|
271
|
+
await performAndAssert(
|
|
272
|
+
'Open the race Date/Category Axis panel',
|
|
273
|
+
() => dateCategoryAxisButton.getAttribute('aria-expanded'),
|
|
274
|
+
async () => userEvent.click(dateCategoryAxisButton),
|
|
275
|
+
(before, after) => before === 'false' && after === 'true'
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
expect(canvas.queryByLabelText('Data Scaling Type')).not.toBeInTheDocument()
|
|
279
|
+
expect(canvas.queryByLabelText('Manual Ticks')).not.toBeInTheDocument()
|
|
280
|
+
expect(canvas.queryByLabelText('Number of Ticks')).not.toBeInTheDocument()
|
|
281
|
+
expect(canvas.queryByLabelText(/tick rotation/i)).not.toBeInTheDocument()
|
|
282
|
+
expect(canvas.queryByLabelText('Hide Axis')).not.toBeInTheDocument()
|
|
283
|
+
expect(canvas.queryByLabelText('Hide Tick Labels')).not.toBeInTheDocument()
|
|
284
|
+
expect(canvas.queryByLabelText(/show years once/i)).not.toBeInTheDocument()
|
|
285
|
+
expect(canvas.getByLabelText(/data key/i)).toBeInTheDocument()
|
|
286
|
+
|
|
287
|
+
const exclusionsToggle = canvas.getByLabelText(/exclude one or more values/i)
|
|
288
|
+
await userEvent.click(exclusionsToggle)
|
|
289
|
+
const getAddExclusion = () =>
|
|
290
|
+
canvasElement.querySelector('select option[value="2018"]')?.parentElement as HTMLSelectElement | null
|
|
291
|
+
const addExclusion = getAddExclusion() as HTMLSelectElement
|
|
292
|
+
await performAndAssert(
|
|
293
|
+
'Exclude the first race frame',
|
|
294
|
+
() => canvasElement.querySelector('.bar-chart-race__frame')?.textContent?.trim(),
|
|
295
|
+
async () => userEvent.selectOptions(addExclusion, '2018'),
|
|
296
|
+
(before, after) => before === '2018' && after === '2019'
|
|
297
|
+
)
|
|
298
|
+
await performAndAssert(
|
|
299
|
+
'Remove the final exclusion without corrupting its config',
|
|
300
|
+
() => ({
|
|
301
|
+
addExclusionVisible: Boolean(getAddExclusion()),
|
|
302
|
+
frame: canvasElement.querySelector('.bar-chart-race__frame')?.textContent?.trim(),
|
|
303
|
+
removeButtonVisible: Boolean(canvasElement.querySelector('.series-list__remove'))
|
|
304
|
+
}),
|
|
305
|
+
async () => userEvent.click(canvasElement.querySelector('.series-list__remove') as HTMLButtonElement),
|
|
306
|
+
(before, after) =>
|
|
307
|
+
before.frame === '2019' &&
|
|
308
|
+
before.removeButtonVisible &&
|
|
309
|
+
after.frame === '2018' &&
|
|
310
|
+
!after.removeButtonVisible &&
|
|
311
|
+
after.addExclusionVisible
|
|
312
|
+
)
|
|
313
|
+
expect(exclusionsToggle).toBeChecked()
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export const UnsupportedPanelsTests: Story = {
|
|
318
|
+
name: 'Editor: Race Unsupported Panels',
|
|
319
|
+
args: {
|
|
320
|
+
config: {
|
|
321
|
+
...annualChangeConfig,
|
|
322
|
+
filters: [
|
|
323
|
+
{
|
|
324
|
+
active: '2018',
|
|
325
|
+
columnName: 'Year',
|
|
326
|
+
filterStyle: 'dropdown',
|
|
327
|
+
values: ['2018', '2019', '2020', '2021', '2022', '2023', '2024']
|
|
328
|
+
}
|
|
329
|
+
]
|
|
330
|
+
} as any,
|
|
331
|
+
isEditor: true,
|
|
332
|
+
interactionLabel: 'Bar race unsupported panels story'
|
|
333
|
+
},
|
|
334
|
+
play: async ({ canvasElement }) => {
|
|
335
|
+
const canvas = within(canvasElement)
|
|
336
|
+
await waitForEditor(canvas)
|
|
337
|
+
|
|
338
|
+
expect(canvasElement.querySelector('.bar-chart-race')).toBeInTheDocument()
|
|
339
|
+
expect(canvasElement.querySelectorAll('.bar-chart-race__frame-axis-tick')).toHaveLength(7)
|
|
340
|
+
for (const name of ['Regions', 'Legend', 'Filters', 'PatternSettings', 'Text Annotations']) {
|
|
341
|
+
expect(canvas.queryByRole('button', { name })).not.toBeInTheDocument()
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export const VisualSectionTests: Story = {
|
|
347
|
+
name: 'Editor: Race Visual Section',
|
|
348
|
+
args: {
|
|
349
|
+
config: annualChangeConfig as any,
|
|
350
|
+
isEditor: true,
|
|
351
|
+
interactionLabel: 'Bar race visual section story'
|
|
352
|
+
},
|
|
353
|
+
play: async ({ canvasElement }) => {
|
|
354
|
+
const canvas = within(canvasElement)
|
|
355
|
+
await waitForEditor(canvas)
|
|
356
|
+
await openAccordion(canvas, '^Visual$')
|
|
357
|
+
|
|
358
|
+
expect(canvas.queryByLabelText('Bar Borders')).not.toBeInTheDocument()
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export const IneligibleGeneralSectionTests: Story = {
|
|
363
|
+
name: 'Editor: Ineligible General Section',
|
|
364
|
+
parameters: { test: { timeout: 30000 } },
|
|
365
|
+
beforeEach: () => {
|
|
366
|
+
const originalUrl = window.location.href
|
|
367
|
+
const originalState = window.history.state
|
|
368
|
+
const developerUrl = new URL(originalUrl)
|
|
369
|
+
developerUrl.searchParams.set('isCoveDeveloper', 'true')
|
|
370
|
+
window.history.replaceState(originalState, '', developerUrl)
|
|
371
|
+
|
|
372
|
+
return () => window.history.replaceState(originalState, '', originalUrl)
|
|
373
|
+
},
|
|
374
|
+
args: {
|
|
375
|
+
config: {
|
|
376
|
+
...annualChangeConfig,
|
|
377
|
+
visualizationSubType: 'regular',
|
|
378
|
+
orientation: 'horizontal',
|
|
379
|
+
barStyle: 'rounded',
|
|
380
|
+
series: annualChangeConfig.series.slice(0, 1)
|
|
381
|
+
} as any,
|
|
382
|
+
isEditor: true,
|
|
383
|
+
interactionLabel: 'Ineligible bar race editor story'
|
|
384
|
+
},
|
|
385
|
+
play: async ({ canvasElement }) => {
|
|
386
|
+
const canvas = within(canvasElement)
|
|
387
|
+
await waitForEditor(canvas)
|
|
388
|
+
await openAccordion(canvas, 'General')
|
|
389
|
+
|
|
390
|
+
const subtype = canvas.getByLabelText(/chart subtype/i) as HTMLSelectElement
|
|
391
|
+
await performAndAssert(
|
|
392
|
+
'Keep ineligible racing mode selected with the regular Bar fallback',
|
|
393
|
+
() => {
|
|
394
|
+
const fallbackBars = Array.from(canvasElement.querySelectorAll('svg .horizontal path[id^="barGroup"]'))
|
|
395
|
+
return {
|
|
396
|
+
fallbackRendered: Boolean(canvasElement.querySelector('svg .horizontal')),
|
|
397
|
+
flatBarsRendered:
|
|
398
|
+
fallbackBars.length > 0 && fallbackBars.every(path => !path.getAttribute('d')?.includes('Q')),
|
|
399
|
+
guidanceInPreviewAlerts: Boolean(
|
|
400
|
+
Array.from(canvasElement.querySelectorAll('.chart-required-fields-alerts .alert-info')).find(alert =>
|
|
401
|
+
/requires at least two ordinary data series/i.test(alert.textContent || '')
|
|
402
|
+
)
|
|
403
|
+
),
|
|
404
|
+
guidanceInEditorPanel: /requires at least two ordinary data series/i.test(
|
|
405
|
+
canvasElement.querySelector('.editor-panel')?.textContent || ''
|
|
406
|
+
),
|
|
407
|
+
lollipopRendered: Boolean(canvasElement.querySelector('svg .horizontal circle[data-tooltip-html]')),
|
|
408
|
+
raceRendered: Boolean(canvasElement.querySelector('.bar-chart-race')),
|
|
409
|
+
subtype: (canvas.getByLabelText(/chart subtype/i) as HTMLSelectElement).value
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
async () => userEvent.selectOptions(subtype, 'racing'),
|
|
413
|
+
(_before, after) =>
|
|
414
|
+
after.subtype === 'racing' &&
|
|
415
|
+
after.guidanceInPreviewAlerts &&
|
|
416
|
+
!after.guidanceInEditorPanel &&
|
|
417
|
+
after.fallbackRendered &&
|
|
418
|
+
after.flatBarsRendered &&
|
|
419
|
+
!after.lollipopRendered &&
|
|
420
|
+
!after.raceRendered
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
await performAndAssert(
|
|
424
|
+
'Exit an ineligible race through the same subtype control',
|
|
425
|
+
() => ({
|
|
426
|
+
fallbackRendered: Boolean(canvasElement.querySelector('svg .horizontal')),
|
|
427
|
+
guidanceInPreviewAlerts: Boolean(
|
|
428
|
+
Array.from(canvasElement.querySelectorAll('.chart-required-fields-alerts .alert-info')).find(alert =>
|
|
429
|
+
/requires at least two ordinary data series/i.test(alert.textContent || '')
|
|
430
|
+
)
|
|
431
|
+
),
|
|
432
|
+
subtype: (canvas.getByLabelText(/chart subtype/i) as HTMLSelectElement).value
|
|
433
|
+
}),
|
|
434
|
+
async () => userEvent.selectOptions(canvas.getByLabelText(/chart subtype/i), 'regular'),
|
|
435
|
+
(before, after) =>
|
|
436
|
+
before.subtype === 'racing' &&
|
|
437
|
+
before.guidanceInPreviewAlerts &&
|
|
438
|
+
after.subtype === 'regular' &&
|
|
439
|
+
!after.guidanceInPreviewAlerts &&
|
|
440
|
+
after.fallbackRendered
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
3
|
+
import { expect, waitFor } from 'storybook/test'
|
|
4
|
+
|
|
5
|
+
import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
|
|
6
|
+
import Chart from '../CdcChartComponent'
|
|
7
|
+
import { getBoxPlotConfig } from '../helpers/getBoxPlotConfig'
|
|
8
|
+
import { ChartConfig } from '../types/ChartConfig'
|
|
9
|
+
|
|
10
|
+
const expectedNumericValueOrder = '1,2,3,10,20,100'
|
|
11
|
+
const expectedNumericNonOutlierOrder = '1,2,3,10,20'
|
|
12
|
+
|
|
13
|
+
const boxPlotRows = [
|
|
14
|
+
{ Group: 'Group A', Score: '1' },
|
|
15
|
+
{ Group: 'Group A', Score: '2' },
|
|
16
|
+
{ Group: 'Group A', Score: '3' },
|
|
17
|
+
{ Group: 'Group A', Score: '10' },
|
|
18
|
+
{ Group: 'Group A', Score: '20' },
|
|
19
|
+
{ Group: 'Group A', Score: '100' }
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
const boxPlotLabels = {
|
|
23
|
+
maximum: 'Maximum',
|
|
24
|
+
q3: 'Upper Quartile',
|
|
25
|
+
median: 'Median',
|
|
26
|
+
q1: 'Lower Quartile',
|
|
27
|
+
minimum: 'Minimum',
|
|
28
|
+
count: 'Count',
|
|
29
|
+
mean: 'Mean',
|
|
30
|
+
iqr: 'Interquartile Range',
|
|
31
|
+
outliers: 'Outliers',
|
|
32
|
+
values: 'Values',
|
|
33
|
+
lowerBounds: 'Lower Bounds',
|
|
34
|
+
upperBounds: 'Upper Bounds'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const getConfig = (): ChartConfig =>
|
|
38
|
+
({
|
|
39
|
+
version: '4.26.8',
|
|
40
|
+
type: 'chart',
|
|
41
|
+
visualizationType: 'Box Plot',
|
|
42
|
+
title: 'Box Plot Numeric Sort Regression',
|
|
43
|
+
orientation: 'vertical',
|
|
44
|
+
animate: false,
|
|
45
|
+
data: boxPlotRows,
|
|
46
|
+
table: {
|
|
47
|
+
show: true,
|
|
48
|
+
expanded: true,
|
|
49
|
+
download: false,
|
|
50
|
+
label: 'Data Table',
|
|
51
|
+
indexLabel: ''
|
|
52
|
+
},
|
|
53
|
+
xAxis: {
|
|
54
|
+
dataKey: 'Group',
|
|
55
|
+
type: 'categorical',
|
|
56
|
+
label: 'Group'
|
|
57
|
+
},
|
|
58
|
+
yAxis: {
|
|
59
|
+
dataKey: 'Score',
|
|
60
|
+
label: 'Score',
|
|
61
|
+
hideAxis: false,
|
|
62
|
+
hideTicks: false,
|
|
63
|
+
gridLines: true,
|
|
64
|
+
numTicks: 5
|
|
65
|
+
},
|
|
66
|
+
series: [
|
|
67
|
+
{
|
|
68
|
+
dataKey: 'Score',
|
|
69
|
+
name: 'Score'
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
boxplot: {
|
|
73
|
+
plots: [],
|
|
74
|
+
categories: [],
|
|
75
|
+
borders: 'true',
|
|
76
|
+
plotOutlierValues: false,
|
|
77
|
+
plotNonOutlierValues: false,
|
|
78
|
+
labels: boxPlotLabels
|
|
79
|
+
},
|
|
80
|
+
legend: {
|
|
81
|
+
hide: true
|
|
82
|
+
},
|
|
83
|
+
dataFormat: {
|
|
84
|
+
roundTo: 0,
|
|
85
|
+
commas: false
|
|
86
|
+
}
|
|
87
|
+
} as ChartConfig)
|
|
88
|
+
|
|
89
|
+
function BoxPlotNumericSortRegression() {
|
|
90
|
+
const config = useMemo(() => getConfig(), [])
|
|
91
|
+
const [plots] = getBoxPlotConfig(config, config.data)
|
|
92
|
+
const currentValues = plots[0]?.values?.join(',') || ''
|
|
93
|
+
const currentNonOutliers = plots[0]?.columnNonOutliers?.join(',') || ''
|
|
94
|
+
const preservesNumericOrder =
|
|
95
|
+
currentValues === expectedNumericValueOrder && currentNonOutliers === expectedNumericNonOutlierOrder
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<div
|
|
99
|
+
style={{
|
|
100
|
+
background: '#eef2f7',
|
|
101
|
+
display: 'grid',
|
|
102
|
+
gap: 16,
|
|
103
|
+
padding: 16
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
<section
|
|
107
|
+
style={{
|
|
108
|
+
background: '#fff',
|
|
109
|
+
border: '1px solid #d9dfe7',
|
|
110
|
+
borderRadius: 6,
|
|
111
|
+
padding: 16
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
114
|
+
<Chart config={config} isEditor={false} interactionLabel='boxplot-numeric-sort-regression' />
|
|
115
|
+
</section>
|
|
116
|
+
|
|
117
|
+
<section
|
|
118
|
+
aria-label='Box plot numeric sort diagnostic'
|
|
119
|
+
style={{
|
|
120
|
+
background: '#f7f8fa',
|
|
121
|
+
border: '1px solid #d9dfe7',
|
|
122
|
+
borderRadius: 6,
|
|
123
|
+
display: 'grid',
|
|
124
|
+
gap: 8,
|
|
125
|
+
padding: 12
|
|
126
|
+
}}
|
|
127
|
+
>
|
|
128
|
+
<div>
|
|
129
|
+
<strong>Expected numeric values:</strong> <span>{expectedNumericValueOrder}</span>
|
|
130
|
+
</div>
|
|
131
|
+
<div>
|
|
132
|
+
<strong>Generated boxplot values:</strong> <span data-testid='boxplot-current-values'>{currentValues}</span>
|
|
133
|
+
</div>
|
|
134
|
+
<div>
|
|
135
|
+
<strong>Generated non-outliers:</strong>{' '}
|
|
136
|
+
<span data-testid='boxplot-current-non-outliers'>{currentNonOutliers}</span>
|
|
137
|
+
</div>
|
|
138
|
+
<div data-testid='boxplot-numeric-sort-status'>
|
|
139
|
+
<strong>Status:</strong> {preservesNumericOrder ? 'Numeric order preserved' : 'Issue reproduced'}
|
|
140
|
+
</div>
|
|
141
|
+
</section>
|
|
142
|
+
</div>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const getBoxPlotTableValue = (canvasElement: HTMLElement, rowLabel: string) => {
|
|
147
|
+
const row = Array.from(canvasElement.querySelectorAll('tr')).find(tableRow => {
|
|
148
|
+
const firstCell = tableRow.querySelector('th, td')
|
|
149
|
+
return firstCell?.textContent?.trim() === rowLabel
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
return Array.from(row?.querySelectorAll('th, td') || [])
|
|
153
|
+
.slice(1)
|
|
154
|
+
.map(cell => cell.textContent?.trim())
|
|
155
|
+
.join('')
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const meta: Meta<typeof BoxPlotNumericSortRegression> = {
|
|
159
|
+
title: 'Components/Templates/Chart/Box Plot Numeric Sort Regression',
|
|
160
|
+
component: BoxPlotNumericSortRegression,
|
|
161
|
+
parameters: {
|
|
162
|
+
layout: 'fullscreen'
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export default meta
|
|
167
|
+
type Story = StoryObj<typeof BoxPlotNumericSortRegression>
|
|
168
|
+
|
|
169
|
+
export const Generated_Values_Use_Numeric_Order: Story = {
|
|
170
|
+
play: async ({ canvasElement }) => {
|
|
171
|
+
await assertVisualizationRendered(canvasElement)
|
|
172
|
+
|
|
173
|
+
await waitFor(() => {
|
|
174
|
+
expect(canvasElement.querySelector('[data-testid="boxplot-current-values"]')?.textContent).toBe(
|
|
175
|
+
expectedNumericValueOrder
|
|
176
|
+
)
|
|
177
|
+
expect(canvasElement.querySelector('[data-testid="boxplot-current-non-outliers"]')?.textContent).toBe(
|
|
178
|
+
expectedNumericNonOutlierOrder
|
|
179
|
+
)
|
|
180
|
+
expect(canvasElement.querySelector('[data-testid="boxplot-numeric-sort-status"]')?.textContent).toContain(
|
|
181
|
+
'Numeric order preserved'
|
|
182
|
+
)
|
|
183
|
+
expect(getBoxPlotTableValue(canvasElement, 'Values')).toBe(expectedNumericValueOrder)
|
|
184
|
+
expect(getBoxPlotTableValue(canvasElement, 'Non Outliers')).toBe(expectedNumericNonOutlierOrder)
|
|
185
|
+
expect(canvasElement.textContent).toContain(expectedNumericValueOrder)
|
|
186
|
+
expect(canvasElement.textContent).toContain(expectedNumericNonOutlierOrder)
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
}
|