diff --git a/src/features/filters/components/filters-aside/filters-aside.client.test.tsx b/src/features/filters/components/filters-aside/filters-aside.client.test.tsx index fa943b7d..5af63cf4 100644 --- a/src/features/filters/components/filters-aside/filters-aside.client.test.tsx +++ b/src/features/filters/components/filters-aside/filters-aside.client.test.tsx @@ -62,6 +62,27 @@ afterEach(() => { }); describe('main filter panel', () => { + it('opens continent search without applying Africa or navigating before a choice', async () => { + Element.prototype.scrollIntoView = vi.fn(); + const user = userEvent.setup(); + const continents: FilterConfigSchema = { + ...configs[0], + kind: 'SEARCH', + label: 'Continent', + paramKey: 'continents', + options: [ + { label: 'Africa', value: 'africa' }, + { label: 'Europe', value: 'europe' }, + ], + }; + render(); + await user.click(screen.getByRole('button', { name: 'Continent' })); + const search = screen.getByPlaceholderText('Search continent...'); + await user.type(search, 'Europe'); + expect(search).toHaveValue('Europe'); + expect(setParam).not.toHaveBeenCalled(); + }); + it('offers previously hidden filters directly and applies the chosen option without selecting a default first', async () => { const user = userEvent.setup(); render(); diff --git a/src/features/filters/server/dtos/dto-to-filter-config.test.ts b/src/features/filters/server/dtos/dto-to-filter-config.test.ts index d29fb1b6..25f6bd9e 100644 --- a/src/features/filters/server/dtos/dto-to-filter-config.test.ts +++ b/src/features/filters/server/dtos/dto-to-filter-config.test.ts @@ -25,7 +25,7 @@ const locationConfig = (position: number, label: string, paramKey: string) => ({ }); describe('dtoToFilterConfig location facets', () => { - it('keeps each geographic dimension distinct and suggested', () => { + it('keeps each geographic dimension searchable even with only two options', () => { const result = dtoToFilterConfig({ countries: locationConfig(1, 'Country', 'countries'), regions: locationConfig(2, 'Region', 'regions'), @@ -64,6 +64,7 @@ describe('dtoToFilterConfig location facets', () => { ]), ); for (const config of result) { + expect(config.kind).toBe('SEARCH'); if (!('options' in config)) continue; for (const option of config.options) { expect(option.value).toMatch(/^[a-z0-9]+(?:-[a-z0-9]+)*$/); diff --git a/src/features/filters/server/dtos/dto-to-filter-config.ts b/src/features/filters/server/dtos/dto-to-filter-config.ts index 209e5bb3..fc435250 100644 --- a/src/features/filters/server/dtos/dto-to-filter-config.ts +++ b/src/features/filters/server/dtos/dto-to-filter-config.ts @@ -101,6 +101,15 @@ const SUGGESTED_RANGE_PARAM_KEYS = new Set([ 'maxActiveLeads', ]); +// Geography controls stay searchable even when only a few places are available. +const SEARCHABLE_LOCATION_PARAM_KEYS = new Set([ + PARAM_KEYS.CITIES, + PARAM_KEYS.REGIONS, + PARAM_KEYS.COUNTRIES, + PARAM_KEYS.CONTINENTS, + PARAM_KEYS.TIMEZONES, +]); + const RADIO_FILTER_OPTION_THRESHOLD = 6; const CHECKBOX_FILTER_OPTION_THRESHOLD = 6; const SELECT_OPTION_THRESHOLD = 2; @@ -317,7 +326,11 @@ const handleMultiSelect = ( const normalizedDto = withRequiredRemoteWorkModes(dto); // Get rid of issues where options look borked + const searchableLocation = SEARCHABLE_LOCATION_PARAM_KEYS.has( + normalizedDto.paramKey, + ); const minimumOptions = + searchableLocation || normalizedDto.paramKey === PARAM_KEYS.COLLABORATION_HOURS ? 1 : SELECT_OPTION_THRESHOLD; @@ -325,6 +338,7 @@ const handleMultiSelect = ( if (hasNoOptions) return null; const isCheckbox = + !searchableLocation && normalizedDto.options.length <= CHECKBOX_FILTER_OPTION_THRESHOLD; const baseFilter = isCheckbox ? dtoToCheckboxFilterConfig(normalizedDto)