From 6d4c022e268e6ce6b4e260b0eed95e44cae8d27a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 19:29:15 +0000 Subject: [PATCH 1/6] Initial plan From 5cfc5366501584d8e2a7c1d7555a99c2fd51b41c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 19:31:56 +0000 Subject: [PATCH 2/6] Initial exploration complete Co-authored-by: david-mears-2 <60350599+david-mears-2@users.noreply.github.com> --- package-lock.json | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5599e16b..0bb00897 100644 --- a/package-lock.json +++ b/package-lock.json @@ -132,7 +132,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -702,7 +701,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -749,7 +747,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -2942,7 +2939,6 @@ "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", @@ -3863,7 +3859,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4115,7 +4110,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -4629,7 +4623,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -4974,7 +4967,6 @@ "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5035,7 +5027,6 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -5115,7 +5106,6 @@ "integrity": "sha512-SbR9ZBUFKgvWAbq3RrdCtWaW0IKm6wwUiApxf3BVTNfqUIo4IQQmreMg2iHFJJ6C/0wss3LXURBJ1OwS/MhFcQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "natural-compare": "^1.4.0", @@ -7190,7 +7180,6 @@ "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz", "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==", "license": "MIT", - "peer": true, "dependencies": { "@vue/devtools-api": "^7.7.7" }, @@ -7258,7 +7247,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -7307,7 +7295,6 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -7476,7 +7463,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -8391,7 +8377,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8530,7 +8515,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8679,7 +8663,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -8915,7 +8898,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8929,7 +8911,6 @@ "integrity": "sha512-d9B2J9Cm9dN9+6nxMnnNJKJCtcyKfnHj15N6YNJfaFHRLua/d3sRKU9RuKmO9mB0XdFtUizlxfz/VPbd3OxGhw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.0.14", "@vitest/mocker": "4.0.14", @@ -9027,7 +9008,6 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.22.tgz", "integrity": "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==", "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.22", "@vue/compiler-sfc": "3.5.22", From a654ed69dc1c2637f210e2c90afe93cdb3c385f6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 19:33:54 +0000 Subject: [PATCH 3/6] Add tests for new tooltip content (median, mean, 95% CI) Co-authored-by: david-mears-2 <60350599+david-mears-2@users.noreply.github.com> --- .../unit/composables/usePlotTooltips.spec.ts | 112 +++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/tests/unit/composables/usePlotTooltips.spec.ts b/tests/unit/composables/usePlotTooltips.spec.ts index 64695953..e875c1bb 100644 --- a/tests/unit/composables/usePlotTooltips.spec.ts +++ b/tests/unit/composables/usePlotTooltips.spec.ts @@ -6,7 +6,8 @@ import { nextTick } from "vue"; import usePlotTooltips from '@/composables/usePlotTooltips'; import { useAppStore } from '@/stores/appStore'; import { useColorStore } from '@/stores/colorStore'; -import { Axis, Dimension, type PointWithMetadata } from '@/types'; +import { useDataStore } from '@/stores/dataStore'; +import { Axis, Dimension, SummaryTableColumn, type PointWithMetadata, type SummaryTableDataRow } from '@/types'; describe('usePlotTooltips', () => { beforeEach(() => { @@ -118,5 +119,114 @@ describe('usePlotTooltips', () => { expect(campaignTooltip).toContain('Activity type: Campaign'); expect(campaignTooltip).toContain('style="color: #6929c4'); // purple70 }); + + it('displays summary data (median, mean, and 95% confidence interval) in tooltip', () => { + const afgPointMetadata = { metadata: { [Axis.WITHIN_BAND]: 'AFG', [Axis.ROW]: 'Cholera', [Axis.COLUMN]: '' } }; + + const appStore = useAppStore(); + const colorStore = useColorStore(); + const dataStore = useDataStore(); + + // Set up store state for location-based coloring + appStore.filters = { + [Dimension.LOCATION]: ['AFG'], + [Dimension.DISEASE]: ['Cholera'], + }; + + colorStore.setColors([afgPointMetadata]); + + // Set up summary table data with test values + dataStore.summaryTableData = [ + { + [Dimension.LOCATION]: 'AFG', + [Dimension.DISEASE]: 'Cholera', + [SummaryTableColumn.MEDIAN]: 1234.56, + [SummaryTableColumn.MEAN]: 1456.78, + [SummaryTableColumn.CI_LOWER]: 789.12, + [SummaryTableColumn.CI_UPPER]: 2345.67, + } as SummaryTableDataRow, + ]; + + const { tooltipCallback } = usePlotTooltips(); + + const tooltip = tooltipCallback({ x: 1, y: 2, metadata: afgPointMetadata.metadata! }); + + // Check that median and mean are displayed with proper formatting + expect(tooltip).toContain('Median: 1234.56'); + expect(tooltip).toContain('Mean: 1456.78'); + // Check 95% confidence interval is displayed + expect(tooltip).toContain('95% confidence interval:'); + expect(tooltip).toContain('789.12'); + expect(tooltip).toContain('2345.67'); + }); + + it('handles negative confidence interval values with appropriate sign', () => { + const afgPointMetadata = { metadata: { [Axis.WITHIN_BAND]: 'AFG', [Axis.ROW]: 'Cholera', [Axis.COLUMN]: '' } }; + + const appStore = useAppStore(); + const colorStore = useColorStore(); + const dataStore = useDataStore(); + + // Set up store state for location-based coloring + appStore.filters = { + [Dimension.LOCATION]: ['AFG'], + [Dimension.DISEASE]: ['Cholera'], + }; + + colorStore.setColors([afgPointMetadata]); + + // Set up summary table data with negative lower bound (crossing zero) + dataStore.summaryTableData = [ + { + [Dimension.LOCATION]: 'AFG', + [Dimension.DISEASE]: 'Cholera', + [SummaryTableColumn.MEDIAN]: 50.00, + [SummaryTableColumn.MEAN]: 55.00, + [SummaryTableColumn.CI_LOWER]: -100.50, + [SummaryTableColumn.CI_UPPER]: 200.75, + } as SummaryTableDataRow, + ]; + + const { tooltipCallback } = usePlotTooltips(); + + const tooltip = tooltipCallback({ x: 1, y: 2, metadata: afgPointMetadata.metadata! }); + + // Check that the values are displayed correctly + expect(tooltip).toContain('Median: 50.00'); + expect(tooltip).toContain('Mean: 55.00'); + // Check negative lower bound and positive upper bound with + sign when interval crosses zero + expect(tooltip).toContain('-100.50'); + expect(tooltip).toContain('+200.75'); + }); + + it('handles undefined summary data gracefully', () => { + const afgPointMetadata = { metadata: { [Axis.WITHIN_BAND]: 'AFG', [Axis.ROW]: 'Cholera', [Axis.COLUMN]: '' } }; + + const appStore = useAppStore(); + const colorStore = useColorStore(); + const dataStore = useDataStore(); + + // Set up store state + appStore.filters = { + [Dimension.LOCATION]: ['AFG'], + [Dimension.DISEASE]: ['Cholera'], + }; + + colorStore.setColors([afgPointMetadata]); + + // Empty summary table data - no matching row will be found + dataStore.summaryTableData = []; + + const { tooltipCallback } = usePlotTooltips(); + + const tooltip = tooltipCallback({ x: 1, y: 2, metadata: afgPointMetadata.metadata! }); + + // The tooltip should still render, but values will be undefined + expect(tooltip).toContain('Median:'); + expect(tooltip).toContain('Mean:'); + expect(tooltip).toContain('95% confidence interval:'); + // Should contain undefined formatted values + expect(tooltip).toContain('undefined'); + }); }); }); From 7b8abd377838103b08ea58e60804c1d4ab6dfaf6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 16 Jan 2026 19:43:55 +0000 Subject: [PATCH 4/6] Update tests to verify row dimension display and summary data in tooltips Co-authored-by: david-mears-2 <60350599+david-mears-2@users.noreply.github.com> --- tests/unit/composables/usePlotTooltips.spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/unit/composables/usePlotTooltips.spec.ts b/tests/unit/composables/usePlotTooltips.spec.ts index e875c1bb..329cd2ea 100644 --- a/tests/unit/composables/usePlotTooltips.spec.ts +++ b/tests/unit/composables/usePlotTooltips.spec.ts @@ -45,12 +45,16 @@ describe('usePlotTooltips', () => { expect(afgTooltip).toContain('Location: Afghanistan'); expect(afgTooltip).toContain('style="color: #009d9a'); // teal50 + // Row dimension (disease) is shown because it's different from color dimension (location) + expect(afgTooltip).toContain('Disease: Cholera'); expect(afgTooltip).not.toContain('Activity type'); const globalTooltip = tooltipCallback({ x: 1, y: 2, metadata: globalPointMetadata.metadata! }); expect(globalTooltip).toContain('Location: All 117 VIMC countries'); expect(globalTooltip).toContain('style="color: #6929c4'); // purple70 + // Row dimension (disease) is shown because it's different from color dimension (location) + expect(globalTooltip).toContain('Disease: Cholera'); expect(globalTooltip).not.toContain('Activity type'); }); From 05db003ee1c1503234b43b5da41cfb71228ac175 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 Jan 2026 15:31:53 +0000 Subject: [PATCH 5/6] Add assertions to verify row dimension not shown when same as color dimension Co-authored-by: david-mears-2 <60350599+david-mears-2@users.noreply.github.com> --- tests/unit/composables/usePlotTooltips.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/unit/composables/usePlotTooltips.spec.ts b/tests/unit/composables/usePlotTooltips.spec.ts index 329cd2ea..e3dd177e 100644 --- a/tests/unit/composables/usePlotTooltips.spec.ts +++ b/tests/unit/composables/usePlotTooltips.spec.ts @@ -71,6 +71,8 @@ describe('usePlotTooltips', () => { [Dimension.DISEASE]: ['Cholera', 'Measles'], }; expect(colorStore.colorDimension).toBe(Dimension.DISEASE); + // Row dimension is DISEASE by default, same as colorDimension + expect(appStore.dimensions.row).toBe(Dimension.DISEASE); colorStore.setColors([choleraPointMetadata, measlesPointMetadata]); @@ -80,12 +82,16 @@ describe('usePlotTooltips', () => { expect(choleraTooltip).toContain('Disease: Cholera'); expect(choleraTooltip).toContain('style="color: #6929c4'); // purple70 + // Row dimension (disease) is NOT shown separately because it's the same as color dimension + expect(choleraTooltip.match(/Disease:/g)?.length).toBe(1); expect(choleraTooltip).not.toContain('Activity type'); const measlesTooltip = tooltipCallback({ x: 1, y: 2, metadata: measlesPointMetadata.metadata! }); expect(measlesTooltip).toContain('Disease: Measles'); expect(measlesTooltip).toContain('style="color: #009d9a'); // teal50 + // Row dimension (disease) is NOT shown separately because it's the same as color dimension + expect(measlesTooltip.match(/Disease:/g)?.length).toBe(1); expect(measlesTooltip).not.toContain('Activity type'); }); From 2b95c6a8de5584d9fd0aaa3fd03843c0fb21e2c5 Mon Sep 17 00:00:00 2001 From: David Mears Date: Mon, 19 Jan 2026 15:37:05 +0000 Subject: [PATCH 6/6] Update unit tests --- package-lock.json | 20 ++++++++ .../unit/composables/usePlotTooltips.spec.ts | 48 +++---------------- 2 files changed, 26 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0bb00897..5599e16b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -132,6 +132,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -701,6 +702,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -747,6 +749,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -2939,6 +2942,7 @@ "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", @@ -3859,6 +3863,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4110,6 +4115,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -4623,6 +4629,7 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -4967,6 +4974,7 @@ "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5027,6 +5035,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -5106,6 +5115,7 @@ "integrity": "sha512-SbR9ZBUFKgvWAbq3RrdCtWaW0IKm6wwUiApxf3BVTNfqUIo4IQQmreMg2iHFJJ6C/0wss3LXURBJ1OwS/MhFcQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "natural-compare": "^1.4.0", @@ -7180,6 +7190,7 @@ "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz", "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==", "license": "MIT", + "peer": true, "dependencies": { "@vue/devtools-api": "^7.7.7" }, @@ -7247,6 +7258,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -7295,6 +7307,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -7463,6 +7476,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -8377,6 +8391,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8515,6 +8530,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8663,6 +8679,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -8898,6 +8915,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8911,6 +8929,7 @@ "integrity": "sha512-d9B2J9Cm9dN9+6nxMnnNJKJCtcyKfnHj15N6YNJfaFHRLua/d3sRKU9RuKmO9mB0XdFtUizlxfz/VPbd3OxGhw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "4.0.14", "@vitest/mocker": "4.0.14", @@ -9008,6 +9027,7 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.22.tgz", "integrity": "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==", "license": "MIT", + "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.22", "@vue/compiler-sfc": "3.5.22", diff --git a/tests/unit/composables/usePlotTooltips.spec.ts b/tests/unit/composables/usePlotTooltips.spec.ts index e3dd177e..c94f4f12 100644 --- a/tests/unit/composables/usePlotTooltips.spec.ts +++ b/tests/unit/composables/usePlotTooltips.spec.ts @@ -29,14 +29,13 @@ describe('usePlotTooltips', () => { const appStore = useAppStore(); const colorStore = useColorStore(); - // Set up store state for location-based coloring appStore.filters = { [Dimension.LOCATION]: ['AFG', 'global'], [Dimension.DISEASE]: ['Cholera'], }; expect(colorStore.colorDimension).toBe(Dimension.LOCATION); + expect(appStore.dimensions.row).toBe(Dimension.DISEASE); - // Set colors so colorStore has the mapping colorStore.setColors([afgPointMetadata, globalPointMetadata]); const { tooltipCallback } = usePlotTooltips(); @@ -53,7 +52,6 @@ describe('usePlotTooltips', () => { expect(globalTooltip).toContain('Location: All 117 VIMC countries'); expect(globalTooltip).toContain('style="color: #6929c4'); // purple70 - // Row dimension (disease) is shown because it's different from color dimension (location) expect(globalTooltip).toContain('Disease: Cholera'); expect(globalTooltip).not.toContain('Activity type'); }); @@ -65,13 +63,11 @@ describe('usePlotTooltips', () => { const appStore = useAppStore(); const colorStore = useColorStore(); - // Set up store state for disease-based coloring appStore.filters = { [Dimension.LOCATION]: ['AFG'], [Dimension.DISEASE]: ['Cholera', 'Measles'], }; expect(colorStore.colorDimension).toBe(Dimension.DISEASE); - // Row dimension is DISEASE by default, same as colorDimension expect(appStore.dimensions.row).toBe(Dimension.DISEASE); colorStore.setColors([choleraPointMetadata, measlesPointMetadata]); @@ -106,13 +102,13 @@ describe('usePlotTooltips', () => { await nextTick(); expect(appStore.dimensions.column).toBe(Dimension.ACTIVITY_TYPE); - // Set up store state for disease-based coloring (single location) appStore.filters = { [Dimension.LOCATION]: ['AFG'], [Dimension.DISEASE]: ['Cholera'], [Dimension.ACTIVITY_TYPE]: ['routine', 'campaign'], }; expect(colorStore.colorDimension).toBe(Dimension.DISEASE); + expect(appStore.dimensions.row).toBe(Dimension.DISEASE); colorStore.setColors([routinePointMetadata, campaignPointMetadata]); @@ -121,12 +117,16 @@ describe('usePlotTooltips', () => { const routineTooltip = tooltipCallback({ x: 1, y: 2, metadata: routinePointMetadata.metadata! }); expect(routineTooltip).toContain('Disease: Cholera'); expect(routineTooltip).toContain('Activity type: Routine'); + // Row dimension (disease) is NOT shown separately because it's the same as color dimension + expect(routineTooltip.match(/Disease:/g)?.length).toBe(1); expect(routineTooltip).toContain('style="color: #6929c4'); // purple70 const campaignTooltip = tooltipCallback({ x: 1, y: 2, metadata: campaignPointMetadata.metadata! }); expect(campaignTooltip).toContain('Disease: Cholera'); expect(campaignTooltip).toContain('Activity type: Campaign'); + // Row dimension (disease) is NOT shown separately because it's the same as color dimension + expect(campaignTooltip.match(/Disease:/g)?.length).toBe(1); expect(campaignTooltip).toContain('style="color: #6929c4'); // purple70 }); @@ -137,7 +137,6 @@ describe('usePlotTooltips', () => { const colorStore = useColorStore(); const dataStore = useDataStore(); - // Set up store state for location-based coloring appStore.filters = { [Dimension.LOCATION]: ['AFG'], [Dimension.DISEASE]: ['Cholera'], @@ -145,7 +144,6 @@ describe('usePlotTooltips', () => { colorStore.setColors([afgPointMetadata]); - // Set up summary table data with test values dataStore.summaryTableData = [ { [Dimension.LOCATION]: 'AFG', @@ -161,10 +159,8 @@ describe('usePlotTooltips', () => { const tooltip = tooltipCallback({ x: 1, y: 2, metadata: afgPointMetadata.metadata! }); - // Check that median and mean are displayed with proper formatting expect(tooltip).toContain('Median: 1234.56'); expect(tooltip).toContain('Mean: 1456.78'); - // Check 95% confidence interval is displayed expect(tooltip).toContain('95% confidence interval:'); expect(tooltip).toContain('789.12'); expect(tooltip).toContain('2345.67'); @@ -177,7 +173,6 @@ describe('usePlotTooltips', () => { const colorStore = useColorStore(); const dataStore = useDataStore(); - // Set up store state for location-based coloring appStore.filters = { [Dimension.LOCATION]: ['AFG'], [Dimension.DISEASE]: ['Cholera'], @@ -201,42 +196,11 @@ describe('usePlotTooltips', () => { const tooltip = tooltipCallback({ x: 1, y: 2, metadata: afgPointMetadata.metadata! }); - // Check that the values are displayed correctly expect(tooltip).toContain('Median: 50.00'); expect(tooltip).toContain('Mean: 55.00'); // Check negative lower bound and positive upper bound with + sign when interval crosses zero expect(tooltip).toContain('-100.50'); expect(tooltip).toContain('+200.75'); }); - - it('handles undefined summary data gracefully', () => { - const afgPointMetadata = { metadata: { [Axis.WITHIN_BAND]: 'AFG', [Axis.ROW]: 'Cholera', [Axis.COLUMN]: '' } }; - - const appStore = useAppStore(); - const colorStore = useColorStore(); - const dataStore = useDataStore(); - - // Set up store state - appStore.filters = { - [Dimension.LOCATION]: ['AFG'], - [Dimension.DISEASE]: ['Cholera'], - }; - - colorStore.setColors([afgPointMetadata]); - - // Empty summary table data - no matching row will be found - dataStore.summaryTableData = []; - - const { tooltipCallback } = usePlotTooltips(); - - const tooltip = tooltipCallback({ x: 1, y: 2, metadata: afgPointMetadata.metadata! }); - - // The tooltip should still render, but values will be undefined - expect(tooltip).toContain('Median:'); - expect(tooltip).toContain('Mean:'); - expect(tooltip).toContain('95% confidence interval:'); - // Should contain undefined formatted values - expect(tooltip).toContain('undefined'); - }); }); });