@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
|
@@ -9,7 +9,7 @@ vi.mock('@cdc/core/components/ui/Icon', () => ({
|
|
|
9
9
|
vi.mock('@cdc/core/components/ui/Tooltip', () => {
|
|
10
10
|
const Tooltip = ({ children }) => <>{children}</>
|
|
11
11
|
Tooltip.Target = ({ children }) => <>{children}</>
|
|
12
|
-
Tooltip.Content = ({ children }) =>
|
|
12
|
+
Tooltip.Content = ({ children }) => <span>{children}</span>
|
|
13
13
|
return { default: Tooltip }
|
|
14
14
|
})
|
|
15
15
|
|
|
@@ -33,7 +33,7 @@ import { createMockChartContext } from '../../LinearChart/tests/mockConfigContex
|
|
|
33
33
|
|
|
34
34
|
beforeAll(() => {
|
|
35
35
|
HTMLCanvasElement.prototype.getContext = vi.fn(() => ({
|
|
36
|
-
measureText: vi.fn(() => ({ width:
|
|
36
|
+
measureText: vi.fn((text: string) => ({ width: text.length * 7 })),
|
|
37
37
|
fillText: vi.fn(),
|
|
38
38
|
fillRect: vi.fn(),
|
|
39
39
|
clearRect: vi.fn()
|
|
@@ -88,8 +88,8 @@ const buildHeatMapContext = () => {
|
|
|
88
88
|
},
|
|
89
89
|
heatmap: {
|
|
90
90
|
cellPadding: 1,
|
|
91
|
-
rowLabelGap:
|
|
92
|
-
columnLabelGap:
|
|
91
|
+
rowLabelGap: 0,
|
|
92
|
+
columnLabelGap: 15,
|
|
93
93
|
colorBucketCount: 9,
|
|
94
94
|
xAxisPosition: 'top',
|
|
95
95
|
showCellValues: false
|
|
@@ -246,8 +246,8 @@ const buildSeriesModeHeatMapContext = () => {
|
|
|
246
246
|
},
|
|
247
247
|
heatmap: {
|
|
248
248
|
cellPadding: 1,
|
|
249
|
-
rowLabelGap:
|
|
250
|
-
columnLabelGap:
|
|
249
|
+
rowLabelGap: 0,
|
|
250
|
+
columnLabelGap: 15,
|
|
251
251
|
colorBucketCount: 9,
|
|
252
252
|
xAxisPosition: 'top',
|
|
253
253
|
showCellValues: false
|
|
@@ -436,13 +436,13 @@ const buildCategoricalAverageAgeHeatMapContext = () => {
|
|
|
436
436
|
return context
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
-
const getTranslateY = (element: Element | null) => {
|
|
439
|
+
const getTranslateY = (element: Element | null | undefined) => {
|
|
440
440
|
const transform = element?.getAttribute('transform') || ''
|
|
441
441
|
const match = transform.match(/translate\([^,]+,\s*([^)]+)\)/)
|
|
442
442
|
return match ? Number(match[1]) : 0
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
-
const getTranslateX = (element: Element | null) => {
|
|
445
|
+
const getTranslateX = (element: Element | null | undefined) => {
|
|
446
446
|
const transform = element?.getAttribute('transform') || ''
|
|
447
447
|
const match = transform.match(/translate\(([^,\s)]+)/)
|
|
448
448
|
return match ? Number(match[1]) : 0
|
|
@@ -454,8 +454,8 @@ describe('HeatMap', () => {
|
|
|
454
454
|
it('renders cells with tooltip metadata from additional columns', () => {
|
|
455
455
|
const context = buildHeatMapContext()
|
|
456
456
|
const rows = [
|
|
457
|
-
{ month: '2024-02-01', North: 5, South: 7, notes: 'Severe', population: 2500 },
|
|
458
|
-
{ month: '2024-01-01', North: 2, South: 4, notes: 'Low', population: 1000 }
|
|
457
|
+
{ month: '2024-02-01', North: 5, South: 7, notes: 'Severe', population: 2500, rank: 20 },
|
|
458
|
+
{ month: '2024-01-01', North: 2, South: 4, notes: 'Low', population: 1000, rank: 10 }
|
|
459
459
|
]
|
|
460
460
|
|
|
461
461
|
context.config.data = rows
|
|
@@ -465,6 +465,7 @@ describe('HeatMap', () => {
|
|
|
465
465
|
context.excludedData = rows
|
|
466
466
|
context.rawData = rows
|
|
467
467
|
context.tableData = rows
|
|
468
|
+
;(context.config as any).dataFormat.suffix = '%'
|
|
468
469
|
;(context.config as any).columns.population = {
|
|
469
470
|
label: 'Population',
|
|
470
471
|
tooltips: true,
|
|
@@ -474,6 +475,13 @@ describe('HeatMap', () => {
|
|
|
474
475
|
roundToPlace: 0,
|
|
475
476
|
commas: true
|
|
476
477
|
}
|
|
478
|
+
;(context.config as any).columns.rank = {
|
|
479
|
+
label: 'Rank',
|
|
480
|
+
tooltips: true,
|
|
481
|
+
dataTable: true,
|
|
482
|
+
roundToPlace: 0,
|
|
483
|
+
commas: true
|
|
484
|
+
}
|
|
477
485
|
|
|
478
486
|
const { container } = render(
|
|
479
487
|
<ConfigContext.Provider value={context}>
|
|
@@ -492,6 +500,8 @@ describe('HeatMap', () => {
|
|
|
492
500
|
expect(tooltipHtml).toContain('Value:')
|
|
493
501
|
expect(tooltipHtml).toContain('Notes')
|
|
494
502
|
expect(tooltipHtml).toContain('Population: $1,000 residents')
|
|
503
|
+
expect(tooltipHtml).toContain('Rank: 10')
|
|
504
|
+
expect(tooltipHtml).not.toContain('Rank: 10%')
|
|
495
505
|
expect(tooltipHtml).not.toContain('<br/>')
|
|
496
506
|
expect(cells[0]?.getAttribute('tabindex')).toBe('0')
|
|
497
507
|
expect(cells[0]?.getAttribute('aria-label')).toContain('Month: Tooltip 2024-01-01')
|
|
@@ -669,16 +679,17 @@ describe('HeatMap', () => {
|
|
|
669
679
|
const firstCellTooltipHtml = cells[0]?.getAttribute('data-tooltip-html') || ''
|
|
670
680
|
const firstCellAriaLabel = cells[0]?.getAttribute('aria-label') || ''
|
|
671
681
|
const firstCellX = Number(cells[0]?.getAttribute('x'))
|
|
682
|
+
const lastCell = cells[cells.length - 1]
|
|
683
|
+
const lastCellRight = Number(lastCell?.getAttribute('x')) + Number(lastCell?.getAttribute('width'))
|
|
672
684
|
const xAxisTitle = Array.from(container.querySelectorAll('text')).find(
|
|
673
685
|
text => text.textContent === 'Community Type'
|
|
674
686
|
)
|
|
675
687
|
const xAxisTitleX = Number(xAxisTitle?.getAttribute('x'))
|
|
676
688
|
|
|
677
689
|
expect(cells).toHaveLength(9)
|
|
678
|
-
expect(firstCellX).
|
|
679
|
-
expect(firstCellX).toBeLessThan(90)
|
|
690
|
+
expect(firstCellX).toBe(0)
|
|
680
691
|
expect(xAxisTitleX).toBeGreaterThan(firstCellX)
|
|
681
|
-
expect(xAxisTitleX).toBeLessThan(
|
|
692
|
+
expect(xAxisTitleX).toBeLessThan(lastCellRight)
|
|
682
693
|
expect(firstCellTooltipHtml).toContain('Community Type: Urban Core')
|
|
683
694
|
expect(firstCellTooltipHtml).toContain('City: Atlanta')
|
|
684
695
|
expect(firstCellTooltipHtml).toContain('Average age: 34')
|
|
@@ -733,10 +744,16 @@ describe('HeatMap', () => {
|
|
|
733
744
|
expect(visibleSeriesTooltipHtml).toContain('Average age: 36')
|
|
734
745
|
})
|
|
735
746
|
|
|
736
|
-
it('
|
|
747
|
+
it('shares existing bottom space with rotated row labels instead of shrinking cells unnecessarily', () => {
|
|
748
|
+
const unrotatedContext = buildCategoricalAverageAgeHeatMapContext()
|
|
737
749
|
const context = buildCategoricalAverageAgeHeatMapContext()
|
|
738
750
|
;(context.config as any).yAxis.tickRotation = 30
|
|
739
751
|
|
|
752
|
+
const { container: unrotatedContainer } = render(
|
|
753
|
+
<ConfigContext.Provider value={unrotatedContext}>
|
|
754
|
+
<HeatMap parentWidth={800} parentHeight={320} />
|
|
755
|
+
</ConfigContext.Provider>
|
|
756
|
+
)
|
|
740
757
|
const { container } = render(
|
|
741
758
|
<ConfigContext.Provider value={context}>
|
|
742
759
|
<HeatMap parentWidth={800} parentHeight={320} />
|
|
@@ -744,8 +761,28 @@ describe('HeatMap', () => {
|
|
|
744
761
|
)
|
|
745
762
|
|
|
746
763
|
const rowLabel = Array.from(container.querySelectorAll('text')).find(text => text.textContent === 'Atlanta')
|
|
764
|
+
const unrotatedCell = unrotatedContainer.querySelector('.visx-heatmap-rect')
|
|
765
|
+
const rotatedCell = container.querySelector('.visx-heatmap-rect')
|
|
747
766
|
|
|
748
767
|
expect(rowLabel?.getAttribute('transform')).toContain('rotate(-30')
|
|
768
|
+
expect(Number(rotatedCell?.getAttribute('height'))).toBe(Number(unrotatedCell?.getAttribute('height')))
|
|
769
|
+
})
|
|
770
|
+
|
|
771
|
+
it('keeps a visible grid when a long rotated row label needs more space than the chart height', () => {
|
|
772
|
+
const context = buildCategoricalAverageAgeHeatMapContext()
|
|
773
|
+
const longRowLabel = 'A'.repeat(500)
|
|
774
|
+
;(context.config as any).yAxis.tickRotation = 90
|
|
775
|
+
;(context.config as any).runtime.seriesLabels.Atlanta = longRowLabel
|
|
776
|
+
|
|
777
|
+
const { container } = render(
|
|
778
|
+
<ConfigContext.Provider value={context}>
|
|
779
|
+
<HeatMap parentWidth={800} parentHeight={320} />
|
|
780
|
+
</ConfigContext.Provider>
|
|
781
|
+
)
|
|
782
|
+
|
|
783
|
+
const firstCell = container.querySelector('.visx-heatmap-rect')
|
|
784
|
+
|
|
785
|
+
expect(Number(firstCell?.getAttribute('height'))).toBeGreaterThan(0)
|
|
749
786
|
})
|
|
750
787
|
|
|
751
788
|
it('renders top y-axis titles above the heatmap and reserves layout space', () => {
|
|
@@ -927,6 +964,28 @@ describe('HeatMap', () => {
|
|
|
927
964
|
expect(columnLabel?.getAttribute('text-anchor')).toBe('start')
|
|
928
965
|
})
|
|
929
966
|
|
|
967
|
+
it('samples x-axis labels without removing heatmap cells when a tick count is configured', () => {
|
|
968
|
+
const context = buildCategoricalAverageAgeHeatMapContext()
|
|
969
|
+
;(context.config as any).xAxis.numTicks = 2
|
|
970
|
+
|
|
971
|
+
const { container } = render(
|
|
972
|
+
<ConfigContext.Provider value={context}>
|
|
973
|
+
<HeatMap parentWidth={800} parentHeight={320} />
|
|
974
|
+
</ConfigContext.Provider>
|
|
975
|
+
)
|
|
976
|
+
|
|
977
|
+
expect(container.querySelectorAll('.visx-heatmap-rect')).toHaveLength(9)
|
|
978
|
+
expect(screen.getByText('Urban Core')).toBeTruthy()
|
|
979
|
+
expect(screen.queryByText('Suburban')).toBeNull()
|
|
980
|
+
expect(screen.getByText('Rural')).toBeTruthy()
|
|
981
|
+
|
|
982
|
+
const plotLeft = getTranslateX(container.querySelector('.cdc-heatmap__plot'))
|
|
983
|
+
const lastCell = container.querySelectorAll('.visx-heatmap-rect')[8]
|
|
984
|
+
const lastCellRight = Number(lastCell?.getAttribute('x')) + Number(lastCell?.getAttribute('width'))
|
|
985
|
+
|
|
986
|
+
expect(800 - (plotLeft + lastCellRight)).toBeGreaterThanOrEqual(25)
|
|
987
|
+
})
|
|
988
|
+
|
|
930
989
|
it('places the x-axis below the heatmap when configured', () => {
|
|
931
990
|
const context = buildCategoricalAverageAgeHeatMapContext()
|
|
932
991
|
;(context.config as any).heatmap.xAxisPosition = 'bottom'
|
|
@@ -972,6 +1031,61 @@ describe('HeatMap', () => {
|
|
|
972
1031
|
expect(columnLabel?.querySelector('tspan')?.getAttribute('dy')).toBe('0.355em')
|
|
973
1032
|
})
|
|
974
1033
|
|
|
1034
|
+
it('uses larger cell text when it fits and hides values wider than the cell', () => {
|
|
1035
|
+
const context = buildHeatMapContext()
|
|
1036
|
+
;(context.config as any).heatmap.showCellValues = true
|
|
1037
|
+
context.formatNumber = () => '1234567890'.repeat(20)
|
|
1038
|
+
const { container, rerender } = render(
|
|
1039
|
+
<ConfigContext.Provider value={context}>
|
|
1040
|
+
<HeatMap parentWidth={800} parentHeight={320} />
|
|
1041
|
+
</ConfigContext.Provider>
|
|
1042
|
+
)
|
|
1043
|
+
expect(container.querySelectorAll('.cdc-heatmap__cell-value')).toHaveLength(0)
|
|
1044
|
+
context.formatNumber = () => '12'
|
|
1045
|
+
rerender(
|
|
1046
|
+
<ConfigContext.Provider value={{ ...context }}>
|
|
1047
|
+
<HeatMap parentWidth={800} parentHeight={320} />
|
|
1048
|
+
</ConfigContext.Provider>
|
|
1049
|
+
)
|
|
1050
|
+
expect(container.querySelector('.cdc-heatmap__cell-value')?.getAttribute('font-size')).toBe('16')
|
|
1051
|
+
})
|
|
1052
|
+
|
|
1053
|
+
it('scales cell text with height below the cap and uses the new column gap fallback', () => {
|
|
1054
|
+
const context = buildHeatMapContext()
|
|
1055
|
+
const config = context.config as any
|
|
1056
|
+
config.heatmap.showCellValues = true
|
|
1057
|
+
delete config.heatmap.columnLabelGap
|
|
1058
|
+
config.hideXAxisLabel = true
|
|
1059
|
+
config.xAxis.hideLabel = true
|
|
1060
|
+
config.xAxis.hideTicks = true
|
|
1061
|
+
config.xAxis.size = 0
|
|
1062
|
+
const { container } = render(
|
|
1063
|
+
<ConfigContext.Provider value={context}>
|
|
1064
|
+
<HeatMap parentWidth={800} parentHeight={80} />
|
|
1065
|
+
</ConfigContext.Provider>
|
|
1066
|
+
)
|
|
1067
|
+
const cell = container.querySelector('.visx-heatmap-rect')
|
|
1068
|
+
const label = container.querySelector('.cdc-heatmap__cell-value')
|
|
1069
|
+
expect(Number(label?.getAttribute('font-size'))).toBeCloseTo(Number(cell?.getAttribute('height')) * 0.5)
|
|
1070
|
+
expect(Number(label?.getAttribute('font-size'))).toBeLessThan(16)
|
|
1071
|
+
expect(container.querySelector('.visx-axis-top text')?.getAttribute('y')).toBe('-20')
|
|
1072
|
+
})
|
|
1073
|
+
|
|
1074
|
+
it('keeps the bottom axis title close to the bottom edge', () => {
|
|
1075
|
+
const context = buildHeatMapContext()
|
|
1076
|
+
;(context.config as any).heatmap.xAxisPosition = 'bottom'
|
|
1077
|
+
;(context.config as any).xAxis.size = 0
|
|
1078
|
+
const { container } = render(
|
|
1079
|
+
<ConfigContext.Provider value={context}>
|
|
1080
|
+
<HeatMap parentWidth={800} parentHeight={320} />
|
|
1081
|
+
</ConfigContext.Provider>
|
|
1082
|
+
)
|
|
1083
|
+
const title = container.querySelector('.cdc-heatmap__axis-title')
|
|
1084
|
+
const baseline = getHeatMapPlotTop(container) + Number(title?.getAttribute('y'))
|
|
1085
|
+
expect(320 - baseline).toBeLessThanOrEqual(14)
|
|
1086
|
+
expect(320 - baseline).toBeGreaterThanOrEqual(8)
|
|
1087
|
+
})
|
|
1088
|
+
|
|
975
1089
|
it('renders readable cell values when HeatMap cell labels are enabled', () => {
|
|
976
1090
|
const context = buildCategoricalAverageAgeHeatMapContext()
|
|
977
1091
|
;(context.config as any).heatmap.showCellValues = true
|
|
@@ -988,9 +1102,10 @@ describe('HeatMap', () => {
|
|
|
988
1102
|
expect(cellValues.every(label => ['#1c1d1f', '#fff'].includes(label.getAttribute('fill') || ''))).toBe(true)
|
|
989
1103
|
})
|
|
990
1104
|
|
|
991
|
-
it('
|
|
1105
|
+
it('adds the row label gap before ticks while keeping the grid against the axis', () => {
|
|
992
1106
|
const context = buildCategoricalAverageAgeHeatMapContext()
|
|
993
1107
|
;(context.config as any).heatmap.rowLabelGap = 18
|
|
1108
|
+
;(context.config as any).xAxis.tickColor = '#123456'
|
|
994
1109
|
|
|
995
1110
|
const { container } = render(
|
|
996
1111
|
<ConfigContext.Provider value={context}>
|
|
@@ -999,8 +1114,207 @@ describe('HeatMap', () => {
|
|
|
999
1114
|
)
|
|
1000
1115
|
|
|
1001
1116
|
const firstCell = container.querySelector('.visx-heatmap-rect')
|
|
1117
|
+
const xAxisLine = container.querySelector('.cdc-heatmap__x-axis-line')
|
|
1118
|
+
|
|
1119
|
+
expect(Number(firstCell?.getAttribute('x'))).toBe(0)
|
|
1120
|
+
expect(Number(container.querySelector('.visx-axis-left text')?.getAttribute('x'))).toBe(-26)
|
|
1121
|
+
expect(xAxisLine?.getAttribute('x1')).toBe('0')
|
|
1122
|
+
expect(Number(xAxisLine?.getAttribute('x2'))).toBeGreaterThan(Number(firstCell?.getAttribute('x')))
|
|
1123
|
+
expect(xAxisLine?.getAttribute('stroke')).toBe('#123456')
|
|
1124
|
+
})
|
|
1125
|
+
|
|
1126
|
+
it('preserves a visible grid when the configured row label gap exceeds the plot width', () => {
|
|
1127
|
+
const context = buildCategoricalAverageAgeHeatMapContext()
|
|
1128
|
+
;(context.config as any).heatmap.rowLabelGap = 10_000
|
|
1129
|
+
|
|
1130
|
+
const { container } = render(
|
|
1131
|
+
<ConfigContext.Provider value={context}>
|
|
1132
|
+
<HeatMap parentWidth={800} parentHeight={320} />
|
|
1133
|
+
</ConfigContext.Provider>
|
|
1134
|
+
)
|
|
1135
|
+
|
|
1136
|
+
const firstCell = container.querySelector('.visx-heatmap-rect')
|
|
1137
|
+
|
|
1138
|
+
expect(Number(firstCell?.getAttribute('width'))).toBeGreaterThan(0)
|
|
1139
|
+
})
|
|
1140
|
+
|
|
1141
|
+
it('uses rectangular cells to fill the available plot width', () => {
|
|
1142
|
+
const context = buildHeatMapContext()
|
|
1143
|
+
|
|
1144
|
+
const { container } = render(
|
|
1145
|
+
<ConfigContext.Provider value={context}>
|
|
1146
|
+
<HeatMap parentWidth={800} parentHeight={320} />
|
|
1147
|
+
</ConfigContext.Provider>
|
|
1148
|
+
)
|
|
1149
|
+
|
|
1150
|
+
const firstCell = container.querySelector('.visx-heatmap-rect')
|
|
1151
|
+
const cells = Array.from(container.querySelectorAll('.visx-heatmap-rect'))
|
|
1152
|
+
const lastCell = cells[cells.length - 1]
|
|
1153
|
+
const xAxisLine = container.querySelector('.cdc-heatmap__x-axis-line')
|
|
1154
|
+
const lastCellRight = Number(lastCell?.getAttribute('x')) + Number(lastCell?.getAttribute('width'))
|
|
1155
|
+
|
|
1156
|
+
expect(Number(firstCell?.getAttribute('width'))).toBeGreaterThan(Number(firstCell?.getAttribute('height')))
|
|
1157
|
+
expect(Math.abs(Number(xAxisLine?.getAttribute('x2')) - lastCellRight)).toBeLessThanOrEqual(2)
|
|
1158
|
+
})
|
|
1159
|
+
|
|
1160
|
+
it('keeps the row axis fixed and opens horizontal scrolling at the first column when enabled', () => {
|
|
1161
|
+
const context = buildHeatMapContext()
|
|
1162
|
+
;(context.config as any).heatmap.horizontalScroll = true
|
|
1163
|
+
;(context.config as any).heatmap.minColumnWidth = 220
|
|
1164
|
+
|
|
1165
|
+
const { container } = render(
|
|
1166
|
+
<ConfigContext.Provider value={context}>
|
|
1167
|
+
<HeatMap parentWidth={320} parentHeight={320} />
|
|
1168
|
+
</ConfigContext.Provider>
|
|
1169
|
+
)
|
|
1170
|
+
|
|
1171
|
+
const scrollArea = screen.getByRole('region', { name: /HeatMap chart.*Month.*scrollable columns/i })
|
|
1172
|
+
const scrollContent = scrollArea.querySelector('.cdc-heatmap__scroll-content')
|
|
1173
|
+
const fixedAxis = container.querySelector('.cdc-heatmap__fixed-axis-svg')
|
|
1174
|
+
const fixedXAxisTitle = container.querySelector('.cdc-heatmap__fixed-x-title')
|
|
1175
|
+
const firstCell = scrollArea.querySelector('.visx-heatmap-rect')
|
|
1176
|
+
const scrollInstructionsId = scrollArea.getAttribute('aria-describedby')
|
|
1177
|
+
const leftScrollCue = container.querySelector('.cdc-heatmap__scroll-cue--left')
|
|
1178
|
+
const rightScrollCue = container.querySelector('.cdc-heatmap__scroll-cue--right')
|
|
1179
|
+
|
|
1180
|
+
expect(scrollArea.scrollLeft).toBe(0)
|
|
1181
|
+
expect(Number(scrollContent?.getAttribute('width'))).toBeGreaterThan(320)
|
|
1182
|
+
expect(Number(firstCell?.getAttribute('width'))).toBeCloseTo(219, 1)
|
|
1183
|
+
expect(fixedAxis?.textContent).toContain('North')
|
|
1184
|
+
expect(fixedAxis?.getAttribute('aria-label')).toContain('Region row axis')
|
|
1185
|
+
expect(scrollArea.contains(fixedAxis)).toBe(false)
|
|
1186
|
+
expect(fixedXAxisTitle?.textContent).toContain('Month')
|
|
1187
|
+
expect(scrollContent?.textContent).not.toContain('Month')
|
|
1188
|
+
expect(scrollInstructionsId).toBeTruthy()
|
|
1189
|
+
expect(document.getElementById(scrollInstructionsId || '')?.textContent).toContain(
|
|
1190
|
+
'Scroll horizontally to view additional columns.'
|
|
1191
|
+
)
|
|
1192
|
+
expect(leftScrollCue).toHaveAttribute('aria-hidden', 'true')
|
|
1193
|
+
expect(rightScrollCue).toHaveAttribute('aria-hidden', 'true')
|
|
1194
|
+
|
|
1195
|
+
Object.defineProperties(scrollArea, {
|
|
1196
|
+
clientWidth: { configurable: true, value: 200 },
|
|
1197
|
+
scrollWidth: { configurable: true, value: 600 }
|
|
1198
|
+
})
|
|
1199
|
+
|
|
1200
|
+
fireEvent.scroll(scrollArea)
|
|
1201
|
+
expect(leftScrollCue).not.toHaveClass('is-visible')
|
|
1202
|
+
expect(rightScrollCue).toHaveClass('is-visible')
|
|
1203
|
+
|
|
1204
|
+
scrollArea.scrollLeft = 200
|
|
1205
|
+
fireEvent.scroll(scrollArea)
|
|
1206
|
+
expect(leftScrollCue).toHaveClass('is-visible')
|
|
1207
|
+
expect(rightScrollCue).toHaveClass('is-visible')
|
|
1208
|
+
|
|
1209
|
+
scrollArea.scrollLeft = 400
|
|
1210
|
+
fireEvent.scroll(scrollArea)
|
|
1211
|
+
expect(leftScrollCue).toHaveClass('is-visible')
|
|
1212
|
+
expect(rightScrollCue).not.toHaveClass('is-visible')
|
|
1213
|
+
})
|
|
1214
|
+
|
|
1215
|
+
it('preserves the responsive non-scroll layout when horizontal scrolling is disabled', () => {
|
|
1216
|
+
const context = buildHeatMapContext()
|
|
1217
|
+
;(context.config as any).heatmap.horizontalScroll = false
|
|
1218
|
+
;(context.config as any).heatmap.minColumnWidth = 220
|
|
1219
|
+
|
|
1220
|
+
const { container } = render(
|
|
1221
|
+
<ConfigContext.Provider value={context}>
|
|
1222
|
+
<HeatMap parentWidth={320} parentHeight={320} />
|
|
1223
|
+
</ConfigContext.Provider>
|
|
1224
|
+
)
|
|
1225
|
+
|
|
1226
|
+
expect(screen.queryByRole('region', { name: /scrollable columns/i })).toBeNull()
|
|
1227
|
+
expect(container.querySelector('.cdc-heatmap__fixed-axis-svg')).toBeNull()
|
|
1228
|
+
expect(container.querySelector('.cdc-heatmap__scroll-cue')).toBeNull()
|
|
1229
|
+
expect(Number(container.querySelector('.cdc-heatmap__svg')?.getAttribute('width'))).toBe(320)
|
|
1230
|
+
})
|
|
1231
|
+
|
|
1232
|
+
it('does not add a scroll region when enabled columns already fit', () => {
|
|
1233
|
+
const context = buildHeatMapContext()
|
|
1234
|
+
;(context.config as any).heatmap.horizontalScroll = true
|
|
1235
|
+
;(context.config as any).heatmap.minColumnWidth = 44
|
|
1236
|
+
|
|
1237
|
+
const { container } = render(
|
|
1238
|
+
<ConfigContext.Provider value={context}>
|
|
1239
|
+
<HeatMap parentWidth={800} parentHeight={320} />
|
|
1240
|
+
</ConfigContext.Provider>
|
|
1241
|
+
)
|
|
1242
|
+
|
|
1243
|
+
expect(screen.queryByRole('region', { name: /scrollable columns/i })).toBeNull()
|
|
1244
|
+
expect(container.querySelector('.cdc-heatmap__scroll-cue')).toBeNull()
|
|
1245
|
+
})
|
|
1246
|
+
|
|
1247
|
+
it('caps authored minimum column widths at the supported maximum', () => {
|
|
1248
|
+
const context = buildHeatMapContext()
|
|
1249
|
+
;(context.config as any).heatmap.horizontalScroll = true
|
|
1250
|
+
;(context.config as any).heatmap.minColumnWidth = 10_000
|
|
1251
|
+
|
|
1252
|
+
render(
|
|
1253
|
+
<ConfigContext.Provider value={context}>
|
|
1254
|
+
<HeatMap parentWidth={320} parentHeight={320} />
|
|
1255
|
+
</ConfigContext.Provider>
|
|
1256
|
+
)
|
|
1257
|
+
|
|
1258
|
+
const scrollArea = screen.getByRole('region', { name: /scrollable columns/i })
|
|
1259
|
+
const firstCell = scrollArea.querySelector('.visx-heatmap-rect')
|
|
1260
|
+
|
|
1261
|
+
expect(Number(firstCell?.getAttribute('width'))).toBeCloseTo(399, 1)
|
|
1262
|
+
})
|
|
1263
|
+
|
|
1264
|
+
it('protects bottom rotated labels while preserving a positive row gap in scroll mode', () => {
|
|
1265
|
+
const context = buildHeatMapContext()
|
|
1266
|
+
;(context.config as any).heatmap.horizontalScroll = true
|
|
1267
|
+
;(context.config as any).heatmap.minColumnWidth = 60
|
|
1268
|
+
;(context.config as any).heatmap.rowLabelGap = 18
|
|
1269
|
+
;(context.config as any).heatmap.xAxisPosition = 'bottom'
|
|
1270
|
+
;(context.config as any).xAxis.tickRotation = 45
|
|
1271
|
+
|
|
1272
|
+
const { container } = render(
|
|
1273
|
+
<ConfigContext.Provider value={context}>
|
|
1274
|
+
<HeatMap parentWidth={200} parentHeight={320} />
|
|
1275
|
+
</ConfigContext.Provider>
|
|
1276
|
+
)
|
|
1277
|
+
|
|
1278
|
+
const scrollArea = screen.getByRole('region', { name: /scrollable columns/i })
|
|
1279
|
+
const firstCell = scrollArea.querySelector('.visx-heatmap-rect')
|
|
1280
|
+
const firstTickLabel = scrollArea.querySelector('.visx-axis-bottom text')
|
|
1281
|
+
const xAxisLine = scrollArea.querySelector('.cdc-heatmap__x-axis-line')
|
|
1282
|
+
const fixedXAxisTitle = container.querySelector('.cdc-heatmap__fixed-x-title text')
|
|
1283
|
+
const fixedXAxisTitleSvg = container.querySelector('.cdc-heatmap__fixed-x-title')
|
|
1284
|
+
|
|
1285
|
+
expect(container.querySelector('.visx-axis-bottom')).toBeTruthy()
|
|
1286
|
+
expect(Number(firstCell?.getAttribute('x'))).toBeGreaterThan(18)
|
|
1287
|
+
expect(firstTickLabel?.getAttribute('transform')).toContain('rotate(-45')
|
|
1288
|
+
expect(xAxisLine?.getAttribute('x1')).toBe('0')
|
|
1289
|
+
expect(Number(fixedXAxisTitle?.getAttribute('x'))).toBeCloseTo(
|
|
1290
|
+
Number(fixedXAxisTitleSvg?.getAttribute('width')) / 2,
|
|
1291
|
+
1
|
|
1292
|
+
)
|
|
1293
|
+
})
|
|
1294
|
+
|
|
1295
|
+
it('caps scrollable endpoint padding for extremely long first labels', () => {
|
|
1296
|
+
const context = buildHeatMapContext()
|
|
1297
|
+
;(context.config as any).heatmap.horizontalScroll = true
|
|
1298
|
+
;(context.config as any).heatmap.minColumnWidth = 60
|
|
1299
|
+
;(context.config as any).heatmap.rowLabelGap = 18
|
|
1300
|
+
;(context.config as any).heatmap.xAxisPosition = 'bottom'
|
|
1301
|
+
;(context.config as any).xAxis.tickRotation = 45
|
|
1302
|
+
context.formatDate = () => 'X'.repeat(10_000)
|
|
1303
|
+
|
|
1304
|
+
render(
|
|
1305
|
+
<ConfigContext.Provider value={context}>
|
|
1306
|
+
<HeatMap parentWidth={200} parentHeight={320} />
|
|
1307
|
+
</ConfigContext.Provider>
|
|
1308
|
+
)
|
|
1309
|
+
|
|
1310
|
+
const scrollArea = screen.getByRole('region', { name: /scrollable columns/i })
|
|
1311
|
+
const firstCell = scrollArea.querySelector('.visx-heatmap-rect')
|
|
1312
|
+
const scrollContent = scrollArea.querySelector('.cdc-heatmap__scroll-content')
|
|
1313
|
+
const fixedXAxisTitle = document.querySelector('.cdc-heatmap__fixed-x-title')
|
|
1002
1314
|
|
|
1003
|
-
expect(Number(firstCell?.getAttribute('x'))).
|
|
1315
|
+
expect(Number(firstCell?.getAttribute('x'))).toBeLessThanOrEqual(258)
|
|
1316
|
+
expect(Number(firstCell?.getAttribute('x'))).toBeLessThan(Number(fixedXAxisTitle?.getAttribute('width')))
|
|
1317
|
+
expect(Number(scrollContent?.getAttribute('width'))).toBeLessThan(500)
|
|
1004
1318
|
})
|
|
1005
1319
|
|
|
1006
1320
|
it('uses the HeatMap column label gap setting to tune spacing above the first row', () => {
|
|
@@ -1020,6 +1334,8 @@ describe('HeatMap', () => {
|
|
|
1020
1334
|
|
|
1021
1335
|
it('shows HeatMap editor controls in their owning accordion sections', () => {
|
|
1022
1336
|
const context = buildHeatMapContext()
|
|
1337
|
+
;(context.config as any).xAxis.manual = true
|
|
1338
|
+
;(context.config as any).heatmap.horizontalScroll = true
|
|
1023
1339
|
|
|
1024
1340
|
render(
|
|
1025
1341
|
<ConfigContext.Provider value={context}>
|
|
@@ -1042,6 +1358,7 @@ describe('HeatMap', () => {
|
|
|
1042
1358
|
fireEvent.click(yAxisHeading)
|
|
1043
1359
|
fireEvent.click(dateCategoryHeading)
|
|
1044
1360
|
fireEvent.click(settingsHeading)
|
|
1361
|
+
expect(screen.getByText(/To adjust cell height, use Chart Height in the General panel\./)).toBeTruthy()
|
|
1045
1362
|
fireEvent.click(dataSeriesHeading)
|
|
1046
1363
|
fireEvent.click(legendHeading)
|
|
1047
1364
|
|
|
@@ -1052,6 +1369,9 @@ describe('HeatMap', () => {
|
|
|
1052
1369
|
expect(screen.getAllByText('Hide Axis')).toHaveLength(2)
|
|
1053
1370
|
expect(screen.getAllByText('Hide Ticks')).toHaveLength(2)
|
|
1054
1371
|
expect(screen.getAllByText('Tick rotation (Degrees)').length).toBeGreaterThan(0)
|
|
1372
|
+
expect(screen.getByText('Number of ticks')).toBeTruthy()
|
|
1373
|
+
expect(screen.queryByText('Manual Ticks')).toBeNull()
|
|
1374
|
+
expect(screen.queryByText('Step count')).toBeNull()
|
|
1055
1375
|
expect(screen.getByLabelText('Label Placement')).toBeTruthy()
|
|
1056
1376
|
expect(screen.getByText('Add Data Series')).toBeTruthy()
|
|
1057
1377
|
expect(
|
|
@@ -1061,6 +1381,9 @@ describe('HeatMap', () => {
|
|
|
1061
1381
|
Boolean(xAxisPositionLabel.compareDocumentPosition(settingsHeading) & Node.DOCUMENT_POSITION_FOLLOWING)
|
|
1062
1382
|
).toBe(true)
|
|
1063
1383
|
expect(screen.getByText('Show Cell Values')).toBeTruthy()
|
|
1384
|
+
expect(screen.getByText('Enable Horizontal Scrolling')).toBeTruthy()
|
|
1385
|
+
expect(screen.getByText('Minimum Column Width')).toBeTruthy()
|
|
1386
|
+
expect(screen.getByLabelText('Minimum Column Width')).toHaveAttribute('max', '400')
|
|
1064
1387
|
expect(screen.getByText('Cell Padding')).toBeTruthy()
|
|
1065
1388
|
expect(screen.getByText('Row Label Gap')).toBeTruthy()
|
|
1066
1389
|
expect(screen.getByText('Column Label Gap')).toBeTruthy()
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
getHeatMapColorScale,
|
|
7
7
|
getHeatMapDataGroupRanges
|
|
8
8
|
} from '../helpers'
|
|
9
|
+
import { getHeatMapXAxisTickValues } from '../helpers/layout'
|
|
9
10
|
import { type ChartConfig } from '../../../types/ChartConfig'
|
|
10
11
|
|
|
11
12
|
describe('buildHeatMapData', () => {
|
|
@@ -165,3 +166,81 @@ describe('HeatMap data grouping', () => {
|
|
|
165
166
|
])
|
|
166
167
|
})
|
|
167
168
|
})
|
|
169
|
+
|
|
170
|
+
describe('HeatMap x-axis tick values', () => {
|
|
171
|
+
const domain = ['a', 'b', 'c', 'd', 'e']
|
|
172
|
+
const formattedLabels = Object.fromEntries(domain.map(value => [value, `Label ${value}`]))
|
|
173
|
+
const measureLabel = () => 50
|
|
174
|
+
|
|
175
|
+
it('samples an explicit tick count evenly while preserving the first and last values', () => {
|
|
176
|
+
expect(
|
|
177
|
+
getHeatMapXAxisTickValues({
|
|
178
|
+
domain,
|
|
179
|
+
formattedLabels,
|
|
180
|
+
gridWidth: 500,
|
|
181
|
+
rotationDegrees: 0,
|
|
182
|
+
requestedCount: 3,
|
|
183
|
+
measureLabel,
|
|
184
|
+
fontSize: 16
|
|
185
|
+
})
|
|
186
|
+
).toEqual(['a', 'c', 'e'])
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('automatically reduces dense labels without changing the domain', () => {
|
|
190
|
+
const tickValues = getHeatMapXAxisTickValues({
|
|
191
|
+
domain,
|
|
192
|
+
formattedLabels,
|
|
193
|
+
gridWidth: 150,
|
|
194
|
+
rotationDegrees: 0,
|
|
195
|
+
measureLabel,
|
|
196
|
+
fontSize: 16
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
expect(tickValues).toEqual(['a', 'e'])
|
|
200
|
+
expect(domain).toEqual(['a', 'b', 'c', 'd', 'e'])
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it('keeps every short categorical label when neighboring labels fit', () => {
|
|
204
|
+
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
|
205
|
+
const labelWidths = [23, 24, 27, 23, 29, 22, 17, 27, 26, 23, 27, 27]
|
|
206
|
+
|
|
207
|
+
expect(
|
|
208
|
+
getHeatMapXAxisTickValues({
|
|
209
|
+
domain: months,
|
|
210
|
+
formattedLabels: Object.fromEntries(months.map(month => [month, month])),
|
|
211
|
+
gridWidth: 468,
|
|
212
|
+
rotationDegrees: 0,
|
|
213
|
+
measureLabel: label => labelWidths[months.indexOf(label)],
|
|
214
|
+
fontSize: 16
|
|
215
|
+
})
|
|
216
|
+
).toEqual(months)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('allows more labels when rotation reduces their horizontal footprint', () => {
|
|
220
|
+
expect(
|
|
221
|
+
getHeatMapXAxisTickValues({
|
|
222
|
+
domain,
|
|
223
|
+
formattedLabels,
|
|
224
|
+
gridWidth: 150,
|
|
225
|
+
rotationDegrees: 90,
|
|
226
|
+
measureLabel,
|
|
227
|
+
fontSize: 16
|
|
228
|
+
})
|
|
229
|
+
).toEqual(domain)
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it('handles very large category domains without spreading every label into Math.max', () => {
|
|
233
|
+
const largeDomain = Array.from({ length: 150_000 }, (_, index) => String(index))
|
|
234
|
+
const tickValues = getHeatMapXAxisTickValues({
|
|
235
|
+
domain: largeDomain,
|
|
236
|
+
formattedLabels: {},
|
|
237
|
+
gridWidth: 100,
|
|
238
|
+
rotationDegrees: 0,
|
|
239
|
+
measureLabel: () => 1,
|
|
240
|
+
fontSize: 1
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
expect(tickValues[0]).toBe('0')
|
|
244
|
+
expect(tickValues[tickValues.length - 1]).toBe('149999')
|
|
245
|
+
})
|
|
246
|
+
})
|