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');
- });
});
});