diff --git a/frontend/src/__tests__/plans-column-filters.test.ts b/frontend/src/__tests__/plans-column-filters.test.ts index 1bc85e937..6d99fd1d2 100644 --- a/frontend/src/__tests__/plans-column-filters.test.ts +++ b/frontend/src/__tests__/plans-column-filters.test.ts @@ -189,6 +189,23 @@ describe('Planned Purchases column filters (issue #166 follow-up)', () => { expect(labels).toEqual(['Jan 2, 2026', 'Jan 10, 2026', 'Jun 2, 2026', 'Dec 1, 2026']); }); + // #1851: a bound plan spanning providers is shown as provider "multiple" and lists + // its real providers; the provider options are those providers, never "multiple". + test('provider options list a bound plan\'s providers, not "multiple"', async () => { + (api.getPlannedPurchases as jest.Mock).mockResolvedValue({ purchases: [ + { ...seedPurchases[0], id: 'b1', provider: 'multiple', providers: ['aws', 'gcp'] }, + { ...seedPurchases[1], id: 'u1', provider: 'azure' }, + { ...seedPurchases[0], id: 'e1', provider: 'oci', providers: [] }, + ] }); + await loadPlans(); + document.querySelector( + '#planned-purchases-list th .column-filter-btn[data-column="provider"]', + )?.click(); + const values = Array.from(document.querySelectorAll('.column-filter-popover .column-filter-item input[type="checkbox"]')) + .map((i) => i.dataset['value']); + expect(values).toEqual(['aws', 'azure', 'gcp', 'oci']); + }); + test('clicking a trigger opens a popover detached to document.body', async () => { await loadPlans(); const providerBtn = document.querySelector( diff --git a/frontend/src/plans.ts b/frontend/src/plans.ts index d562ca4ab..c11251af0 100644 --- a/frontend/src/plans.ts +++ b/frontend/src/plans.ts @@ -338,6 +338,15 @@ const PLANS_NUMERIC_COLUMNS: ReadonlySet = new Set (p.providers && p.providers.length > 0 ? p.providers : [p.provider ?? ''])), + (value) => value, + ); +} + function plansColumnLabel(column: state.PlansColumnId): string { switch (column) { case 'plan_name': return 'Plan'; @@ -382,7 +391,9 @@ function openPlansColumnPopover(column: state.PlansColumnId, anchor: HTMLElement column, label: plansColumnLabel(column), kind: PLANS_NUMERIC_COLUMNS.has(column) ? 'numeric' : 'categorical', - distinctValues: sortedDistinctValues(lastLoadedPurchases, (p) => categoricalCellValueForPlan(p, column)), + distinctValues: column === 'provider' + ? plansProviderOptions(lastLoadedPurchases) + : sortedDistinctValues(lastLoadedPurchases, (p) => categoricalCellValueForPlan(p, column)), displayLabel: (value) => plansCategoricalDisplayLabel(column, value), getFilter: () => state.getPlansColumnFilters()[column], onCommit: (filter) => {