diff --git a/dist/chromium/content.js b/dist/chromium/content.js index c9f97ff..8de02dc 100644 --- a/dist/chromium/content.js +++ b/dist/chromium/content.js @@ -2601,6 +2601,7 @@ function createContentModal({ opportunitiesFilterState.hasSavedCustomerFilter = true; saveFilterPreferences(); renderCustomerFilter(); + renderOwnerFilter(); renderOpportunitiesTable(); }); @@ -2661,6 +2662,7 @@ function createContentModal({ opportunitiesFilterState.hasSavedOwnerFilter = true; saveFilterPreferences(); renderOwnerFilter(); + renderCustomerFilter(); renderOpportunitiesTable(); }); @@ -7053,21 +7055,35 @@ function createOpportunitiesFilters({ } } + // Keep stored selections intact: narrowing a menu must not turn an implicit + // "all" into a second filter that prevents expanding the original selection. + function getAvailableCustomerOptions() { + const items = opportunitiesTableState.opportunitiesTableState.items; + const allOwners = getOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key)); + return getCustomerOptions(allOwners ? items : items.filter(item => opportunitiesFilterState.selectedOwnerKeys.has(getOwnerKey(item)))); + } + + function getAvailableOwnerOptions() { + const items = opportunitiesTableState.opportunitiesTableState.items; + const allCustomers = getCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key)); + return getOwnerOptions(allCustomers ? items : items.filter(item => opportunitiesFilterState.selectedCustomerKeys.has(getCustomerKey(item)))); + } + function renderCustomerFilter() { const trigger = document.getElementById(CUSTOMER_FILTER_BUTTON_ID); const searchInput = document.getElementById(CUSTOMER_FILTER_SEARCH_ID); const selectAllCheckbox = document.getElementById(CUSTOMER_FILTER_SELECT_ALL_ID); - const customerOptions = getCustomerOptions(); + const customerOptions = getAvailableCustomerOptions(); if (!trigger || !searchInput || !selectAllCheckbox) { return; } const selectedCount = customerOptions.filter((customer) => opportunitiesFilterState.selectedCustomerKeys.has(customer.key)).length; - trigger.disabled = customerOptions.length === 0; + trigger.disabled = getCustomerOptions().length === 0; trigger.textContent = getCustomerFilterSummary(customerOptions.length, selectedCount); searchInput.value = opportunitiesFilterState.customerSearch; - selectAllCheckbox.disabled = customerOptions.length === 0; + selectAllCheckbox.disabled = getCustomerOptions().length === 0; selectAllCheckbox.checked = customerOptions.length > 0 && selectedCount === customerOptions.length; selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < customerOptions.length; renderCustomerFilterList(); @@ -7081,7 +7097,7 @@ function createOpportunitiesFilters({ } const normalizedSearch = opportunitiesFilterState.customerSearch.trim().toLocaleLowerCase(); - const customerOptions = getCustomerOptions().filter((customer) => { + const customerOptions = getAvailableCustomerOptions().filter((customer) => { return customer.label.toLocaleLowerCase().includes(normalizedSearch); }); @@ -7103,7 +7119,7 @@ function createOpportunitiesFilters({ checkbox.type = "checkbox"; checkbox.checked = opportunitiesFilterState.selectedCustomerKeys.has(customer.key); checkbox.addEventListener("change", () => { - if (getCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key))) { + if (getAvailableCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key))) { opportunitiesFilterState.selectedCustomerKeys.clear(); opportunitiesFilterState.selectedCustomerKeys.add(customer.key); } else if (checkbox.checked) { @@ -7115,6 +7131,7 @@ function createOpportunitiesFilters({ opportunitiesFilterState.hasSavedCustomerFilter = true; saveFilterPreferences(); renderCustomerFilter(); + renderOwnerFilter(); renderOpportunitiesTable(); }); @@ -7129,18 +7146,18 @@ function createOpportunitiesFilters({ const trigger = document.getElementById(OWNER_FILTER_BUTTON_ID); const searchInput = document.getElementById(OWNER_FILTER_SEARCH_ID); const selectAllCheckbox = document.getElementById(OWNER_FILTER_SELECT_ALL_ID); - const ownerOptions = getOwnerOptions(); + const ownerOptions = getAvailableOwnerOptions(); if (!trigger || !searchInput || !selectAllCheckbox) { return; } const selectedCount = ownerOptions.filter((owner) => opportunitiesFilterState.selectedOwnerKeys.has(owner.key)).length; - trigger.disabled = ownerOptions.length === 0; + trigger.disabled = getOwnerOptions().length === 0; trigger.textContent = getCustomerFilterSummary(ownerOptions.length, selectedCount) .replace("customer", "owner"); searchInput.value = opportunitiesFilterState.ownerSearch; - selectAllCheckbox.disabled = ownerOptions.length === 0; + selectAllCheckbox.disabled = getOwnerOptions().length === 0; selectAllCheckbox.checked = ownerOptions.length > 0 && selectedCount === ownerOptions.length; selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < ownerOptions.length; renderOwnerFilterList(); @@ -7154,7 +7171,7 @@ function createOpportunitiesFilters({ } const normalizedSearch = opportunitiesFilterState.ownerSearch.trim().toLocaleLowerCase(); - const ownerOptions = getOwnerOptions().filter((owner) => { + const ownerOptions = getAvailableOwnerOptions().filter((owner) => { return owner.label.toLocaleLowerCase().includes(normalizedSearch); }); @@ -7176,7 +7193,7 @@ function createOpportunitiesFilters({ checkbox.type = "checkbox"; checkbox.checked = opportunitiesFilterState.selectedOwnerKeys.has(owner.key); checkbox.addEventListener("change", () => { - if (getOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key))) { + if (getAvailableOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key))) { opportunitiesFilterState.selectedOwnerKeys.clear(); opportunitiesFilterState.selectedOwnerKeys.add(owner.key); } else if (checkbox.checked) { @@ -7188,6 +7205,7 @@ function createOpportunitiesFilters({ opportunitiesFilterState.hasSavedOwnerFilter = true; saveFilterPreferences(); renderOwnerFilter(); + renderCustomerFilter(); renderOpportunitiesTable(); }); @@ -7269,7 +7287,7 @@ function createOpportunitiesFilters({ return opportunitiesFilterState.selectedStatuses.has(statusCode); } - return { resetCustomerAndOwnerFiltersForPeriod, renderStageFilterButtons, toggleStageFilter, renderStatusFilterButtons, toggleStatusFilter, toggleCustomerFilterPanel, closeCustomerFilterPanel, toggleOwnerFilterPanel, closeOwnerFilterPanel, resetCustomerFilter, resetOwnerFilter, renderCustomerFilter, renderCustomerFilterList, renderOwnerFilter, renderOwnerFilterList, getSortedOpportunities, matchesTableSearch, matchesSelectedStatus }; + return { getAvailableCustomerOptions, getAvailableOwnerOptions, resetCustomerAndOwnerFiltersForPeriod, renderStageFilterButtons, toggleStageFilter, renderStatusFilterButtons, toggleStatusFilter, toggleCustomerFilterPanel, closeCustomerFilterPanel, toggleOwnerFilterPanel, closeOwnerFilterPanel, resetCustomerFilter, resetOwnerFilter, renderCustomerFilter, renderCustomerFilterList, renderOwnerFilter, renderOwnerFilterList, getSortedOpportunities, matchesTableSearch, matchesSelectedStatus }; } // Source: src/features/opportunities/formatters.js diff --git a/dist/firefox/content.js b/dist/firefox/content.js index c9f97ff..8de02dc 100644 --- a/dist/firefox/content.js +++ b/dist/firefox/content.js @@ -2601,6 +2601,7 @@ function createContentModal({ opportunitiesFilterState.hasSavedCustomerFilter = true; saveFilterPreferences(); renderCustomerFilter(); + renderOwnerFilter(); renderOpportunitiesTable(); }); @@ -2661,6 +2662,7 @@ function createContentModal({ opportunitiesFilterState.hasSavedOwnerFilter = true; saveFilterPreferences(); renderOwnerFilter(); + renderCustomerFilter(); renderOpportunitiesTable(); }); @@ -7053,21 +7055,35 @@ function createOpportunitiesFilters({ } } + // Keep stored selections intact: narrowing a menu must not turn an implicit + // "all" into a second filter that prevents expanding the original selection. + function getAvailableCustomerOptions() { + const items = opportunitiesTableState.opportunitiesTableState.items; + const allOwners = getOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key)); + return getCustomerOptions(allOwners ? items : items.filter(item => opportunitiesFilterState.selectedOwnerKeys.has(getOwnerKey(item)))); + } + + function getAvailableOwnerOptions() { + const items = opportunitiesTableState.opportunitiesTableState.items; + const allCustomers = getCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key)); + return getOwnerOptions(allCustomers ? items : items.filter(item => opportunitiesFilterState.selectedCustomerKeys.has(getCustomerKey(item)))); + } + function renderCustomerFilter() { const trigger = document.getElementById(CUSTOMER_FILTER_BUTTON_ID); const searchInput = document.getElementById(CUSTOMER_FILTER_SEARCH_ID); const selectAllCheckbox = document.getElementById(CUSTOMER_FILTER_SELECT_ALL_ID); - const customerOptions = getCustomerOptions(); + const customerOptions = getAvailableCustomerOptions(); if (!trigger || !searchInput || !selectAllCheckbox) { return; } const selectedCount = customerOptions.filter((customer) => opportunitiesFilterState.selectedCustomerKeys.has(customer.key)).length; - trigger.disabled = customerOptions.length === 0; + trigger.disabled = getCustomerOptions().length === 0; trigger.textContent = getCustomerFilterSummary(customerOptions.length, selectedCount); searchInput.value = opportunitiesFilterState.customerSearch; - selectAllCheckbox.disabled = customerOptions.length === 0; + selectAllCheckbox.disabled = getCustomerOptions().length === 0; selectAllCheckbox.checked = customerOptions.length > 0 && selectedCount === customerOptions.length; selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < customerOptions.length; renderCustomerFilterList(); @@ -7081,7 +7097,7 @@ function createOpportunitiesFilters({ } const normalizedSearch = opportunitiesFilterState.customerSearch.trim().toLocaleLowerCase(); - const customerOptions = getCustomerOptions().filter((customer) => { + const customerOptions = getAvailableCustomerOptions().filter((customer) => { return customer.label.toLocaleLowerCase().includes(normalizedSearch); }); @@ -7103,7 +7119,7 @@ function createOpportunitiesFilters({ checkbox.type = "checkbox"; checkbox.checked = opportunitiesFilterState.selectedCustomerKeys.has(customer.key); checkbox.addEventListener("change", () => { - if (getCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key))) { + if (getAvailableCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key))) { opportunitiesFilterState.selectedCustomerKeys.clear(); opportunitiesFilterState.selectedCustomerKeys.add(customer.key); } else if (checkbox.checked) { @@ -7115,6 +7131,7 @@ function createOpportunitiesFilters({ opportunitiesFilterState.hasSavedCustomerFilter = true; saveFilterPreferences(); renderCustomerFilter(); + renderOwnerFilter(); renderOpportunitiesTable(); }); @@ -7129,18 +7146,18 @@ function createOpportunitiesFilters({ const trigger = document.getElementById(OWNER_FILTER_BUTTON_ID); const searchInput = document.getElementById(OWNER_FILTER_SEARCH_ID); const selectAllCheckbox = document.getElementById(OWNER_FILTER_SELECT_ALL_ID); - const ownerOptions = getOwnerOptions(); + const ownerOptions = getAvailableOwnerOptions(); if (!trigger || !searchInput || !selectAllCheckbox) { return; } const selectedCount = ownerOptions.filter((owner) => opportunitiesFilterState.selectedOwnerKeys.has(owner.key)).length; - trigger.disabled = ownerOptions.length === 0; + trigger.disabled = getOwnerOptions().length === 0; trigger.textContent = getCustomerFilterSummary(ownerOptions.length, selectedCount) .replace("customer", "owner"); searchInput.value = opportunitiesFilterState.ownerSearch; - selectAllCheckbox.disabled = ownerOptions.length === 0; + selectAllCheckbox.disabled = getOwnerOptions().length === 0; selectAllCheckbox.checked = ownerOptions.length > 0 && selectedCount === ownerOptions.length; selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < ownerOptions.length; renderOwnerFilterList(); @@ -7154,7 +7171,7 @@ function createOpportunitiesFilters({ } const normalizedSearch = opportunitiesFilterState.ownerSearch.trim().toLocaleLowerCase(); - const ownerOptions = getOwnerOptions().filter((owner) => { + const ownerOptions = getAvailableOwnerOptions().filter((owner) => { return owner.label.toLocaleLowerCase().includes(normalizedSearch); }); @@ -7176,7 +7193,7 @@ function createOpportunitiesFilters({ checkbox.type = "checkbox"; checkbox.checked = opportunitiesFilterState.selectedOwnerKeys.has(owner.key); checkbox.addEventListener("change", () => { - if (getOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key))) { + if (getAvailableOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key))) { opportunitiesFilterState.selectedOwnerKeys.clear(); opportunitiesFilterState.selectedOwnerKeys.add(owner.key); } else if (checkbox.checked) { @@ -7188,6 +7205,7 @@ function createOpportunitiesFilters({ opportunitiesFilterState.hasSavedOwnerFilter = true; saveFilterPreferences(); renderOwnerFilter(); + renderCustomerFilter(); renderOpportunitiesTable(); }); @@ -7269,7 +7287,7 @@ function createOpportunitiesFilters({ return opportunitiesFilterState.selectedStatuses.has(statusCode); } - return { resetCustomerAndOwnerFiltersForPeriod, renderStageFilterButtons, toggleStageFilter, renderStatusFilterButtons, toggleStatusFilter, toggleCustomerFilterPanel, closeCustomerFilterPanel, toggleOwnerFilterPanel, closeOwnerFilterPanel, resetCustomerFilter, resetOwnerFilter, renderCustomerFilter, renderCustomerFilterList, renderOwnerFilter, renderOwnerFilterList, getSortedOpportunities, matchesTableSearch, matchesSelectedStatus }; + return { getAvailableCustomerOptions, getAvailableOwnerOptions, resetCustomerAndOwnerFiltersForPeriod, renderStageFilterButtons, toggleStageFilter, renderStatusFilterButtons, toggleStatusFilter, toggleCustomerFilterPanel, closeCustomerFilterPanel, toggleOwnerFilterPanel, closeOwnerFilterPanel, resetCustomerFilter, resetOwnerFilter, renderCustomerFilter, renderCustomerFilterList, renderOwnerFilter, renderOwnerFilterList, getSortedOpportunities, matchesTableSearch, matchesSelectedStatus }; } // Source: src/features/opportunities/formatters.js diff --git a/src/content/modal.js b/src/content/modal.js index d8b9120..0772e0b 100644 --- a/src/content/modal.js +++ b/src/content/modal.js @@ -310,6 +310,7 @@ function createContentModal({ opportunitiesFilterState.hasSavedCustomerFilter = true; saveFilterPreferences(); renderCustomerFilter(); + renderOwnerFilter(); renderOpportunitiesTable(); }); @@ -370,6 +371,7 @@ function createContentModal({ opportunitiesFilterState.hasSavedOwnerFilter = true; saveFilterPreferences(); renderOwnerFilter(); + renderCustomerFilter(); renderOpportunitiesTable(); }); diff --git a/src/features/opportunities/filters.js b/src/features/opportunities/filters.js index c53c225..05c90fd 100644 --- a/src/features/opportunities/filters.js +++ b/src/features/opportunities/filters.js @@ -202,21 +202,35 @@ function createOpportunitiesFilters({ } } + // Keep stored selections intact: narrowing a menu must not turn an implicit + // "all" into a second filter that prevents expanding the original selection. + function getAvailableCustomerOptions() { + const items = opportunitiesTableState.opportunitiesTableState.items; + const allOwners = getOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key)); + return getCustomerOptions(allOwners ? items : items.filter(item => opportunitiesFilterState.selectedOwnerKeys.has(getOwnerKey(item)))); + } + + function getAvailableOwnerOptions() { + const items = opportunitiesTableState.opportunitiesTableState.items; + const allCustomers = getCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key)); + return getOwnerOptions(allCustomers ? items : items.filter(item => opportunitiesFilterState.selectedCustomerKeys.has(getCustomerKey(item)))); + } + function renderCustomerFilter() { const trigger = document.getElementById(CUSTOMER_FILTER_BUTTON_ID); const searchInput = document.getElementById(CUSTOMER_FILTER_SEARCH_ID); const selectAllCheckbox = document.getElementById(CUSTOMER_FILTER_SELECT_ALL_ID); - const customerOptions = getCustomerOptions(); + const customerOptions = getAvailableCustomerOptions(); if (!trigger || !searchInput || !selectAllCheckbox) { return; } const selectedCount = customerOptions.filter((customer) => opportunitiesFilterState.selectedCustomerKeys.has(customer.key)).length; - trigger.disabled = customerOptions.length === 0; + trigger.disabled = getCustomerOptions().length === 0; trigger.textContent = getCustomerFilterSummary(customerOptions.length, selectedCount); searchInput.value = opportunitiesFilterState.customerSearch; - selectAllCheckbox.disabled = customerOptions.length === 0; + selectAllCheckbox.disabled = getCustomerOptions().length === 0; selectAllCheckbox.checked = customerOptions.length > 0 && selectedCount === customerOptions.length; selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < customerOptions.length; renderCustomerFilterList(); @@ -230,7 +244,7 @@ function createOpportunitiesFilters({ } const normalizedSearch = opportunitiesFilterState.customerSearch.trim().toLocaleLowerCase(); - const customerOptions = getCustomerOptions().filter((customer) => { + const customerOptions = getAvailableCustomerOptions().filter((customer) => { return customer.label.toLocaleLowerCase().includes(normalizedSearch); }); @@ -252,7 +266,7 @@ function createOpportunitiesFilters({ checkbox.type = "checkbox"; checkbox.checked = opportunitiesFilterState.selectedCustomerKeys.has(customer.key); checkbox.addEventListener("change", () => { - if (getCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key))) { + if (getAvailableCustomerOptions().every(option => opportunitiesFilterState.selectedCustomerKeys.has(option.key))) { opportunitiesFilterState.selectedCustomerKeys.clear(); opportunitiesFilterState.selectedCustomerKeys.add(customer.key); } else if (checkbox.checked) { @@ -264,6 +278,7 @@ function createOpportunitiesFilters({ opportunitiesFilterState.hasSavedCustomerFilter = true; saveFilterPreferences(); renderCustomerFilter(); + renderOwnerFilter(); renderOpportunitiesTable(); }); @@ -278,18 +293,18 @@ function createOpportunitiesFilters({ const trigger = document.getElementById(OWNER_FILTER_BUTTON_ID); const searchInput = document.getElementById(OWNER_FILTER_SEARCH_ID); const selectAllCheckbox = document.getElementById(OWNER_FILTER_SELECT_ALL_ID); - const ownerOptions = getOwnerOptions(); + const ownerOptions = getAvailableOwnerOptions(); if (!trigger || !searchInput || !selectAllCheckbox) { return; } const selectedCount = ownerOptions.filter((owner) => opportunitiesFilterState.selectedOwnerKeys.has(owner.key)).length; - trigger.disabled = ownerOptions.length === 0; + trigger.disabled = getOwnerOptions().length === 0; trigger.textContent = getCustomerFilterSummary(ownerOptions.length, selectedCount) .replace("customer", "owner"); searchInput.value = opportunitiesFilterState.ownerSearch; - selectAllCheckbox.disabled = ownerOptions.length === 0; + selectAllCheckbox.disabled = getOwnerOptions().length === 0; selectAllCheckbox.checked = ownerOptions.length > 0 && selectedCount === ownerOptions.length; selectAllCheckbox.indeterminate = selectedCount > 0 && selectedCount < ownerOptions.length; renderOwnerFilterList(); @@ -303,7 +318,7 @@ function createOpportunitiesFilters({ } const normalizedSearch = opportunitiesFilterState.ownerSearch.trim().toLocaleLowerCase(); - const ownerOptions = getOwnerOptions().filter((owner) => { + const ownerOptions = getAvailableOwnerOptions().filter((owner) => { return owner.label.toLocaleLowerCase().includes(normalizedSearch); }); @@ -325,7 +340,7 @@ function createOpportunitiesFilters({ checkbox.type = "checkbox"; checkbox.checked = opportunitiesFilterState.selectedOwnerKeys.has(owner.key); checkbox.addEventListener("change", () => { - if (getOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key))) { + if (getAvailableOwnerOptions().every(option => opportunitiesFilterState.selectedOwnerKeys.has(option.key))) { opportunitiesFilterState.selectedOwnerKeys.clear(); opportunitiesFilterState.selectedOwnerKeys.add(owner.key); } else if (checkbox.checked) { @@ -337,6 +352,7 @@ function createOpportunitiesFilters({ opportunitiesFilterState.hasSavedOwnerFilter = true; saveFilterPreferences(); renderOwnerFilter(); + renderCustomerFilter(); renderOpportunitiesTable(); }); @@ -418,5 +434,5 @@ function createOpportunitiesFilters({ return opportunitiesFilterState.selectedStatuses.has(statusCode); } - return { resetCustomerAndOwnerFiltersForPeriod, renderStageFilterButtons, toggleStageFilter, renderStatusFilterButtons, toggleStatusFilter, toggleCustomerFilterPanel, closeCustomerFilterPanel, toggleOwnerFilterPanel, closeOwnerFilterPanel, resetCustomerFilter, resetOwnerFilter, renderCustomerFilter, renderCustomerFilterList, renderOwnerFilter, renderOwnerFilterList, getSortedOpportunities, matchesTableSearch, matchesSelectedStatus }; + return { getAvailableCustomerOptions, getAvailableOwnerOptions, resetCustomerAndOwnerFiltersForPeriod, renderStageFilterButtons, toggleStageFilter, renderStatusFilterButtons, toggleStatusFilter, toggleCustomerFilterPanel, closeCustomerFilterPanel, toggleOwnerFilterPanel, closeOwnerFilterPanel, resetCustomerFilter, resetOwnerFilter, renderCustomerFilter, renderCustomerFilterList, renderOwnerFilter, renderOwnerFilterList, getSortedOpportunities, matchesTableSearch, matchesSelectedStatus }; } diff --git a/tests/dependentFilters.test.cjs b/tests/dependentFilters.test.cjs new file mode 100644 index 0000000..6ed7dab --- /dev/null +++ b/tests/dependentFilters.test.cjs @@ -0,0 +1,43 @@ +const { test } = require('node:test'); +const assert = require('node:assert/strict'); +const { loadApplication } = require('./support/application.cjs'); +function setup() { + const app = loadApplication('content'); + const items = [['A', 'X'], ['A', 'Y'], ['B', 'Y'], ['C', 'Z'], ['A', 'X']].map(([customer, owner]) => ({ + CustomerAccount: { PartyId: customer, PartyUniqueName: customer }, Owner: owner + })); + app.opportunitiesTableState.opportunitiesTableState.items = items; + app.opportunitiesFilters.resetCustomerFilter(items); + app.opportunitiesFilters.resetOwnerFilter(items); + return { filters: app.opportunitiesFilters, state: app.opportunitiesFilterState }; +} +const labels = options => Array.from(options, option => option.label); +test('customers narrow owners without losing all-owner selection; widening restores options', () => { + const { filters, state } = setup(); + state.selectedCustomerKeys = new Set(['party:A']); + assert.deepEqual(labels(filters.getAvailableOwnerOptions()), ['X', 'Y']); + assert.deepEqual(labels(filters.getAvailableCustomerOptions()), ['A', 'B', 'C']); + state.selectedCustomerKeys.add('party:C'); + assert.deepEqual(labels(filters.getAvailableOwnerOptions()), ['X', 'Y', 'Z']); + assert.equal(state.selectedOwnerKeys.size, 3); +}); +test('owners narrow customers and multi-selection combines their customers', () => { + const { filters, state } = setup(); + state.selectedOwnerKeys = new Set(['name:Y']); + assert.deepEqual(labels(filters.getAvailableCustomerOptions()), ['A', 'B']); + assert.deepEqual(labels(filters.getAvailableOwnerOptions()), ['X', 'Y', 'Z']); + state.selectedOwnerKeys.add('name:Z'); + assert.deepEqual(labels(filters.getAvailableCustomerOptions()), ['A', 'B', 'C']); +}); +test('both filters use the opposite selection, independent of search and stage', () => { + const { filters, state } = setup(); + state.selectedOwnerKeys = new Set(['name:Y']); + state.selectedCustomerKeys = new Set(['party:A']); + state.tableSearch = 'no match'; + state.selectedStages.clear(); + assert.deepEqual(labels(filters.getAvailableCustomerOptions()), ['A', 'B']); + assert.deepEqual(labels(filters.getAvailableOwnerOptions()), ['X', 'Y']); + state.selectedCustomerKeys.clear(); + assert.deepEqual(labels(filters.getAvailableOwnerOptions()), []); + assert.deepEqual(labels(filters.getAvailableCustomerOptions()), ['A', 'B']); +});