Skip to content

Commit

Permalink
Merge pull request #35408 from graylewis/improvedCountrySearch2
Browse files Browse the repository at this point in the history
  • Loading branch information
francoisl authored Feb 13, 2024
2 parents 3791db8 + da49d3f commit 2390e2f
Show file tree
Hide file tree
Showing 2 changed files with 321 additions and 4 deletions.
44 changes: 40 additions & 4 deletions src/libs/searchCountryOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,54 @@ function searchCountryOptions(searchValue: string, countriesData: CountryData[])
if (!trimmedSearchValue) {
return [];
}

const filteredData = countriesData.filter((country) => country.searchValue.includes(trimmedSearchValue));

return filteredData.sort((a, b) => {
if (a.value.toLowerCase() === trimmedSearchValue) {
const halfSorted = filteredData.sort((a, b) => {
// Prioritize matches at the beginning of the string
// e.g. For the search term "Bar" "Barbados" should be prioritized over Antigua & Barbuda
// The first two characters are the country code, so we start at index 2
// and end at the length of the search term
const countryNameASubstring = a.searchValue.toLowerCase().substring(2, trimmedSearchValue.length + 2);
const countryNameBSubstring = b.searchValue.toLowerCase().substring(2, trimmedSearchValue.length + 2);
if (countryNameASubstring === trimmedSearchValue.toLowerCase()) {
return -1;
}
if (b.value.toLowerCase() === trimmedSearchValue) {
if (countryNameBSubstring === trimmedSearchValue.toLowerCase()) {
return 1;
}
return 0;
});

let fullSorted;
const unsanitizedSearchValue = searchValue.toLowerCase().trim();
if (trimmedSearchValue !== unsanitizedSearchValue) {
// Diacritic detected, prioritize diacritic matches
// We search for diacritic matches by using the unsanitized country name and search term
fullSorted = halfSorted.sort((a, b) => {
const unsanitizedCountryNameA = a.text.toLowerCase();
const unsanitizedCountryNameB = b.text.toLowerCase();
if (unsanitizedCountryNameA.includes(unsanitizedSearchValue)) {
return -1;
}
if (unsanitizedCountryNameB.includes(unsanitizedSearchValue)) {
return 1;
}
return 0;
});
} else {
// Diacritic not detected, prioritize country code matches (country codes can never contain diacritics)
// E.g. the search term 'US' should push 'United States' to the top
fullSorted = halfSorted.sort((a, b) => {
if (a.value.toLowerCase() === trimmedSearchValue) {
return -1;
}
if (b.value.toLowerCase() === trimmedSearchValue) {
return 1;
}
return 0;
});
}
return fullSorted;
}

export default searchCountryOptions;
Expand Down
281 changes: 281 additions & 0 deletions tests/unit/searchCountryOptionsTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
import searchCountryOptions from '@libs/searchCountryOptions';

describe('searchCountryOptions', () => {
test('when the search term is a country code, the country with that code should be prioritized', () => {
const searchValue = 'US';
const countriesData = [
{
value: 'US',
keyForList: 'US',
text: 'United States',
isSelected: false,
searchValue: 'usunitedstates',
},
{
value: 'CA',
keyForList: 'CA',
text: 'Canada',
isSelected: false,
searchValue: 'cacanada',
},
{
value: 'MX',
keyForList: 'MX',
text: 'Mexico',
isSelected: false,
searchValue: 'mxmexico',
},
{
value: 'AU',
keyForList: 'AU',
text: 'Australia',
isSelected: false,
searchValue: 'auaustralia',
},
];
const expected = [
{
value: 'US',
keyForList: 'US',
text: 'United States',
isSelected: false,
searchValue: 'usunitedstates',
},
{
value: 'AU',
keyForList: 'AU',
text: 'Australia',
isSelected: false,
searchValue: 'auaustralia',
},
];
const actual = searchCountryOptions(searchValue, countriesData);
expect(actual).toEqual(expected);
});
test('when the search term contains diacritics the country names that exactly match should be prioritized', () => {
const searchValue = 'Ål';
const countriesData = [
{
value: 'AX',
keyForList: 'AX',
text: 'Åland Islands',
isSelected: false,
searchValue: 'axalandislands',
},
{
value: 'AL',
keyForList: 'AL',
text: 'Albania',
isSelected: false,
searchValue: 'alalbania',
},
{
value: 'AS',
keyForList: 'AS',
text: 'American Samoa',
isSelected: false,
searchValue: 'asamericansamoa',
},
];
const expected = [
{
value: 'AX',
keyForList: 'AX',
text: 'Åland Islands',
isSelected: false,
searchValue: 'axalandislands',
},
{
value: 'AL',
keyForList: 'AL',
text: 'Albania',
isSelected: false,
searchValue: 'alalbania',
},
];
const actual = searchCountryOptions(searchValue, countriesData);
expect(actual).toEqual(expected);
});
test('when the search term contains diacritics the country names that exactly match should be prioritized, test case #2', () => {
const searchValue = 'é';
const countriesData = [
{
value: 'BE',
keyForList: 'BE',
text: 'Belgium',
isSelected: false,
searchValue: 'bebelgium',
},
{
value: 'US',
keyForList: 'US',
text: 'United States',
isSelected: false,
searchValue: 'usunitedstates',
},
{
value: 'BL',
keyForList: 'BL',
text: 'Saint Barthélemy',
isSelected: false,
searchValue: 'blsaintbarthelemy',
},
];
const expected = [
{
value: 'BL',
keyForList: 'BL',
text: 'Saint Barthélemy',
isSelected: false,
searchValue: 'blsaintbarthelemy',
},
{
value: 'BE',
keyForList: 'BE',
text: 'Belgium',
isSelected: false,
searchValue: 'bebelgium',
},
{
value: 'US',
keyForList: 'US',
text: 'United States',
isSelected: false,
searchValue: 'usunitedstates',
},
];
const actual = searchCountryOptions(searchValue, countriesData);
expect(actual).toEqual(expected);
});
test('when the search term contains no diacritics, countries with diacritics should still be searched by their sanitized names', () => {
const searchValue = 'al';
const countriesData = [
{
value: 'AX',
keyForList: 'AX',
text: 'Åland Islands',
isSelected: false,
searchValue: 'axalandislands',
},
{
value: 'AL',
keyForList: 'AL',
text: 'Albania',
isSelected: false,
searchValue: 'alalbania',
},
{
value: 'AS',
keyForList: 'AS',
text: 'American Samoa',
isSelected: false,
searchValue: 'asamericansamoa',
},
];
const expected = [
{
value: 'AL',
keyForList: 'AL',
text: 'Albania',
isSelected: false,
searchValue: 'alalbania',
},
{
value: 'AX',
keyForList: 'AX',
text: 'Åland Islands',
isSelected: false,
searchValue: 'axalandislands',
},
];
const actual = searchCountryOptions(searchValue, countriesData);
expect(actual).toEqual(expected);
});
test('when a search term exactly matches the beginning of a countries name, that country should be prioritized', () => {
const searchValue = 'bar'; // for barbados
const countriesData = [
{
value: 'BB',
keyForList: 'BB',
text: 'Barbados',
isSelected: false,
searchValue: 'bbbarbados',
},
{
value: 'BY',
keyForList: 'BY',
text: 'Belarus',
isSelected: false,
searchValue: 'bybelarus',
},
{
value: 'BE',
keyForList: 'BE',
text: 'Belgium',
isSelected: false,
searchValue: 'bebelgium',
},
{
value: 'AG',
keyForList: 'AG',
text: 'Antigua and Barbuda',
isSelected: false,
searchValue: 'agantiguaandbarbuda',
},
];
const expected = [
{
value: 'BB',
keyForList: 'BB',
text: 'Barbados',
isSelected: false,
searchValue: 'bbbarbados',
},
{
value: 'AG',
keyForList: 'AG',
text: 'Antigua and Barbuda',
isSelected: false,
searchValue: 'agantiguaandbarbuda',
},
];
const actual = searchCountryOptions(searchValue, countriesData);
expect(actual).toEqual(expected);
});
test('when the search term is empty, all countries should be returned', () => {
const searchValue = '';
const countriesData = [
{
value: 'BB',
keyForList: 'BB',
text: 'Barbados',
isSelected: false,
searchValue: 'bbbarbados',
},
{
value: 'BY',
keyForList: 'BY',
text: 'Belarus',
isSelected: false,
searchValue: 'bybelarus',
},
{
value: 'BE',
keyForList: 'BE',
text: 'Belgium',
isSelected: false,
searchValue: 'bebelgium',
},
{
value: 'AG',
keyForList: 'AG',
text: 'Antigua and Barbuda',
isSelected: false,
searchValue: 'agantiguaandbarbuda',
},
];
const expected = countriesData;
const actual = searchCountryOptions(searchValue, countriesData);
expect(actual).toEqual(expected);
});
});

0 comments on commit 2390e2f

Please sign in to comment.