Skip to content

Commit e975331

Browse files
committed
add js docs, modify fn names
1 parent fd15515 commit e975331

6 files changed

Lines changed: 53 additions & 39 deletions

File tree

src/libs/OptionsListUtils/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ import type {
188188
} from '@src/types/onyx';
189189
import type {Attendee, Participant} from '@src/types/onyx/IOU';
190190
import {isEmptyObject} from '@src/types/utils/EmptyObject';
191-
import {getCurrentUserSearchTerms, getPersonalDetailSearchTerms, isPersonalDetailMatchingSearchTerm} from './searchMatchUtils';
191+
import {doesPersonalDetailMatchSearchTerm, getCurrentUserSearchTerms, getPersonalDetailSearchTerms} from './searchMatchUtils';
192192
import type {
193193
FilterUserToInviteConfig,
194194
GetOptionsConfig,
@@ -2670,7 +2670,7 @@ function getValidOptions(
26702670
return false;
26712671
}
26722672
return searchTerms.every((term) =>
2673-
isPersonalDetailMatchingSearchTerm(personalDetail, currentUserAccountID, term, {
2673+
doesPersonalDetailMatchSearchTerm(personalDetail, currentUserAccountID, term, {
26742674
useLocaleLowerCase: true,
26752675
transformSearchText: (concatenatedSearchTerms) => deburr(`${concatenatedSearchTerms} ${(personalDetail.text ?? '').toLocaleLowerCase()}`),
26762676
}),
@@ -3012,7 +3012,7 @@ function formatSectionsFromSearchTerm(
30123012
// This will add them to the list of options, deduping them if they already exist in the other lists
30133013
const selectedParticipantsWithoutDetails = selectedOptions.filter((participant) => {
30143014
const accountID = participant.accountID ?? null;
3015-
const isPartOfSearchTerm = isPersonalDetailMatchingSearchTerm(participant, currentUserAccountID, cleanSearchTerm);
3015+
const isPartOfSearchTerm = doesPersonalDetailMatchSearchTerm(participant, currentUserAccountID, cleanSearchTerm);
30163016
const isReportInRecentReports = filteredRecentReports.some((report) => report.accountID === accountID) || filteredWorkspaceChats.some((report) => report.accountID === accountID);
30173017
const isReportInPersonalDetails = filteredPersonalDetails.some((personalDetail) => personalDetail.accountID === accountID);
30183018

src/libs/OptionsListUtils/searchMatchUtils.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import CONST from '@src/CONST';
44
import type {SearchOptionData} from './types';
55

66
type SearchMatchConfig = {
7-
/** Use toLocaleLowerCase() instead of toLowerCase(). Default: false */
7+
/** Whether to use toLocaleLowerCase() instead of toLowerCase(), defaults to false */
88
useLocaleLowerCase?: boolean;
99

1010
/**
@@ -14,11 +14,25 @@ type SearchMatchConfig = {
1414
transformSearchText?: (concatenatedSearchTerms: string) => string;
1515
};
1616

17+
/**
18+
* Includes localized "You"/"Me" so the current user is findable
19+
* by those terms in any supported language.
20+
*
21+
* @returns Raw (not lowercased) terms: display text, login,
22+
* login with dots stripped before @, and translated "You"/"Me".
23+
*/
1724
function getCurrentUserSearchTerms(item: Partial<SearchOptionData>) {
1825
// eslint-disable-next-line @typescript-eslint/no-deprecated
1926
return [item.text ?? item.displayName ?? '', item.login ?? '', item.login?.replace(CONST.EMAIL_SEARCH_REGEX, '') ?? '', translateLocal('common.you'), translateLocal('common.me')];
2027
}
2128

29+
/**
30+
* For the current user, delegates to getCurrentUserSearchTerms.
31+
* For others, includes display name and login with dots stripped
32+
* before @ (so "john.doe@" matches "johndoe@").
33+
*
34+
* @returns Raw (not lowercased) terms the person is searchable by.
35+
*/
2236
function getPersonalDetailSearchTerms(item: Partial<SearchOptionData>, currentUserAccountID: number) {
2337
if (item.accountID === currentUserAccountID) {
2438
return getCurrentUserSearchTerms(item);
@@ -32,7 +46,7 @@ function getPersonalDetailSearchTerms(item: Partial<SearchOptionData>, currentUs
3246
*
3347
* Expects `searchTerm` to already be lowercased and trimmed.
3448
*/
35-
function isPersonalDetailMatchingSearchTerm(
49+
function doesPersonalDetailMatchSearchTerm(
3650
item: Partial<SearchOptionData>,
3751
currentUserAccountID: number,
3852
searchTerm: string,
@@ -48,5 +62,5 @@ function isPersonalDetailMatchingSearchTerm(
4862
return searchText.includes(searchTerm);
4963
}
5064

51-
export {getCurrentUserSearchTerms, getPersonalDetailSearchTerms, isPersonalDetailMatchingSearchTerm};
65+
export {getCurrentUserSearchTerms, getPersonalDetailSearchTerms, doesPersonalDetailMatchSearchTerm};
5266
export type {SearchMatchConfig};

src/pages/NewChatPage.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {canUseTouchScreen} from '@libs/DeviceCapabilities';
3232
import Log from '@libs/Log';
3333
import Navigation from '@libs/Navigation/Navigation';
3434
import {filterAndOrderOptions, filterSelectedOptions, getHeaderMessage, getUserToInviteOption, getValidOptions} from '@libs/OptionsListUtils';
35-
import {isPersonalDetailMatchingSearchTerm} from '@libs/OptionsListUtils/searchMatchUtils';
35+
import {doesPersonalDetailMatchSearchTerm} from '@libs/OptionsListUtils/searchMatchUtils';
3636
import type {OptionWithKey} from '@libs/OptionsListUtils/types';
3737
import type {OptionData} from '@libs/ReportUtils';
3838
import variables from '@styles/variables';
@@ -133,7 +133,7 @@ function useOptions(reportAttributesDerived: ReportAttributesDerivedValue['repor
133133
!!options.userToInvite,
134134
debouncedSearchTerm.trim(),
135135
countryCode,
136-
selectedOptions.some((participant) => isPersonalDetailMatchingSearchTerm(participant, currentUserAccountID, cleanSearchTerm)),
136+
selectedOptions.some((participant) => doesPersonalDetailMatchSearchTerm(participant, currentUserAccountID, cleanSearchTerm)),
137137
);
138138

139139
useFocusEffect(() => {
@@ -274,7 +274,7 @@ function NewChatPage({ref}: NewChatPageProps) {
274274
const selectedSection =
275275
debouncedSearchTerm === ''
276276
? selectedOptions
277-
: selectedOptions.filter((participant) => isPersonalDetailMatchingSearchTerm(participant, currentUserAccountID, debouncedSearchTerm.trim().toLowerCase()));
277+
: selectedOptions.filter((participant) => doesPersonalDetailMatchSearchTerm(participant, currentUserAccountID, debouncedSearchTerm.trim().toLowerCase()));
278278

279279
sections.push({data: selectedSection, title: undefined, sectionIndex: 0});
280280

src/pages/iou/request/MoneyRequestAttendeeSelector.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
isCurrentUser,
3131
orderOptions,
3232
} from '@libs/OptionsListUtils';
33-
import {isPersonalDetailMatchingSearchTerm} from '@libs/OptionsListUtils/searchMatchUtils';
33+
import {doesPersonalDetailMatchSearchTerm} from '@libs/OptionsListUtils/searchMatchUtils';
3434
import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils';
3535
import {isPaidGroupPolicy as isPaidGroupPolicyFn} from '@libs/PolicyUtils';
3636
import type {OptionData} from '@libs/ReportUtils';
@@ -272,7 +272,7 @@ function MoneyRequestAttendeeSelector({attendees = [], onFinish, onAttendeesAdde
272272
!!orderedAvailableOptions?.userToInvite,
273273
cleanSearchTerm,
274274
countryCode,
275-
attendees.some((attendee) => isPersonalDetailMatchingSearchTerm(attendee, currentUserAccountID, cleanSearchTerm)),
275+
attendees.some((attendee) => doesPersonalDetailMatchSearchTerm(attendee, currentUserAccountID, cleanSearchTerm)),
276276
);
277277
sections = newSections;
278278
}

src/pages/iou/request/MoneyRequestParticipantsSelector.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import {isMovingTransactionFromTrackExpense} from '@libs/IOUUtils';
3737
import Navigation from '@libs/Navigation/Navigation';
3838
import type {Option} from '@libs/OptionsListUtils';
3939
import {formatSectionsFromSearchTerm, getHeaderMessage, getParticipantsOption, getPolicyExpenseReportOption, isCurrentUser} from '@libs/OptionsListUtils';
40-
import {isPersonalDetailMatchingSearchTerm} from '@libs/OptionsListUtils/searchMatchUtils';
40+
import {doesPersonalDetailMatchSearchTerm} from '@libs/OptionsListUtils/searchMatchUtils';
4141
import type {OptionWithKey} from '@libs/OptionsListUtils/types';
4242
import {getActiveAdminWorkspaces, isPaidGroupPolicy as isPaidGroupPolicyUtil} from '@libs/PolicyUtils';
4343
import type {OptionData} from '@libs/ReportUtils';
@@ -266,7 +266,7 @@ function MoneyRequestParticipantsSelector({
266266
!!availableOptions?.userToInvite,
267267
debouncedSearchTerm.trim(),
268268
countryCode,
269-
participants.some((participant) => isPersonalDetailMatchingSearchTerm(participant, currentUserAccountID, cleanSearchTerm)),
269+
participants.some((participant) => doesPersonalDetailMatchSearchTerm(participant, currentUserAccountID, cleanSearchTerm)),
270270
),
271271
// eslint-disable-next-line react-hooks/exhaustive-deps
272272
[

tests/unit/searchMatchUtilsTest.ts

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,108 @@
11
// cspell:ignore René Résumé
22
import deburr from 'lodash/deburr';
3-
import {isPersonalDetailMatchingSearchTerm} from '@libs/OptionsListUtils/searchMatchUtils';
3+
import {doesPersonalDetailMatchSearchTerm} from '@libs/OptionsListUtils/searchMatchUtils';
44

55
const CURRENT_USER_ACCOUNT_ID = 2;
66
const OTHER_USER_ACCOUNT_ID = 99;
77

8-
describe('isPersonalDetailMatchingSearchTerm', () => {
8+
describe('doesPersonalDetailMatchSearchTerm', () => {
99
describe('basic matching', () => {
1010
it('should match by displayName', () => {
11-
expect(isPersonalDetailMatchingSearchTerm({displayName: 'John Doe'}, OTHER_USER_ACCOUNT_ID, 'john')).toBe(true);
11+
expect(doesPersonalDetailMatchSearchTerm({displayName: 'John Doe'}, OTHER_USER_ACCOUNT_ID, 'john')).toBe(true);
1212
});
1313

1414
it('should match by login (email)', () => {
15-
expect(isPersonalDetailMatchingSearchTerm({login: 'john@example.com'}, OTHER_USER_ACCOUNT_ID, 'john@example')).toBe(true);
15+
expect(doesPersonalDetailMatchSearchTerm({login: 'john@example.com'}, OTHER_USER_ACCOUNT_ID, 'john@example')).toBe(true);
1616
});
1717

1818
it('should match by login without dots before @', () => {
19-
expect(isPersonalDetailMatchingSearchTerm({login: 'john.doe@example.com'}, OTHER_USER_ACCOUNT_ID, 'johndoe@')).toBe(true);
19+
expect(doesPersonalDetailMatchSearchTerm({login: 'john.doe@example.com'}, OTHER_USER_ACCOUNT_ID, 'johndoe@')).toBe(true);
2020
});
2121

2222
it('should match by participantsList displayName', () => {
2323
const item = {participantsList: [{displayName: 'Jane Smith', accountID: 123}]};
24-
expect(isPersonalDetailMatchingSearchTerm(item, OTHER_USER_ACCOUNT_ID, 'jane')).toBe(true);
24+
expect(doesPersonalDetailMatchSearchTerm(item, OTHER_USER_ACCOUNT_ID, 'jane')).toBe(true);
2525
});
2626

2727
it('should not match when search term is absent from all fields', () => {
28-
expect(isPersonalDetailMatchingSearchTerm({displayName: 'Alice', login: 'alice@test.com'}, OTHER_USER_ACCOUNT_ID, 'bob')).toBe(false);
28+
expect(doesPersonalDetailMatchSearchTerm({displayName: 'Alice', login: 'alice@test.com'}, OTHER_USER_ACCOUNT_ID, 'bob')).toBe(false);
2929
});
3030

3131
it('should match when search term is empty string', () => {
32-
expect(isPersonalDetailMatchingSearchTerm({displayName: 'Anyone'}, OTHER_USER_ACCOUNT_ID, '')).toBe(true);
32+
expect(doesPersonalDetailMatchSearchTerm({displayName: 'Anyone'}, OTHER_USER_ACCOUNT_ID, '')).toBe(true);
3333
});
3434
});
3535

3636
describe('case insensitivity', () => {
3737
it('should match mixed case displayName against lowercased search', () => {
38-
expect(isPersonalDetailMatchingSearchTerm({displayName: 'John DOE'}, OTHER_USER_ACCOUNT_ID, 'john doe')).toBe(true);
38+
expect(doesPersonalDetailMatchSearchTerm({displayName: 'John DOE'}, OTHER_USER_ACCOUNT_ID, 'john doe')).toBe(true);
3939
});
4040

4141
it('should match mixed case login against lowercased search', () => {
42-
expect(isPersonalDetailMatchingSearchTerm({login: 'John.Doe@Example.COM'}, OTHER_USER_ACCOUNT_ID, 'john.doe@example.com')).toBe(true);
42+
expect(doesPersonalDetailMatchSearchTerm({login: 'John.Doe@Example.COM'}, OTHER_USER_ACCOUNT_ID, 'john.doe@example.com')).toBe(true);
4343
});
4444
});
4545

4646
describe('current user matching', () => {
4747
it('should match by text field for current user', () => {
48-
expect(isPersonalDetailMatchingSearchTerm({accountID: CURRENT_USER_ACCOUNT_ID, text: 'My Display Name'}, CURRENT_USER_ACCOUNT_ID, 'my display')).toBe(true);
48+
expect(doesPersonalDetailMatchSearchTerm({accountID: CURRENT_USER_ACCOUNT_ID, text: 'My Display Name'}, CURRENT_USER_ACCOUNT_ID, 'my display')).toBe(true);
4949
});
5050

5151
it('should fall back to displayName when text is missing for current user', () => {
52-
expect(isPersonalDetailMatchingSearchTerm({accountID: CURRENT_USER_ACCOUNT_ID, displayName: 'Fallback Name'}, CURRENT_USER_ACCOUNT_ID, 'fallback')).toBe(true);
52+
expect(doesPersonalDetailMatchSearchTerm({accountID: CURRENT_USER_ACCOUNT_ID, displayName: 'Fallback Name'}, CURRENT_USER_ACCOUNT_ID, 'fallback')).toBe(true);
5353
});
5454
});
5555

5656
describe('partial / sparse items', () => {
5757
it('should match with only login provided', () => {
58-
expect(isPersonalDetailMatchingSearchTerm({login: 'solo@test.com'}, OTHER_USER_ACCOUNT_ID, 'solo')).toBe(true);
58+
expect(doesPersonalDetailMatchSearchTerm({login: 'solo@test.com'}, OTHER_USER_ACCOUNT_ID, 'solo')).toBe(true);
5959
});
6060

6161
it('should match with only displayName provided', () => {
62-
expect(isPersonalDetailMatchingSearchTerm({displayName: 'Solo'}, OTHER_USER_ACCOUNT_ID, 'solo')).toBe(true);
62+
expect(doesPersonalDetailMatchSearchTerm({displayName: 'Solo'}, OTHER_USER_ACCOUNT_ID, 'solo')).toBe(true);
6363
});
6464

6565
it('should not match empty item with a search term', () => {
66-
expect(isPersonalDetailMatchingSearchTerm({}, OTHER_USER_ACCOUNT_ID, 'anything')).toBe(false);
66+
expect(doesPersonalDetailMatchSearchTerm({}, OTHER_USER_ACCOUNT_ID, 'anything')).toBe(false);
6767
});
6868

6969
it('should match empty item with empty search term', () => {
70-
expect(isPersonalDetailMatchingSearchTerm({}, OTHER_USER_ACCOUNT_ID, '')).toBe(true);
70+
expect(doesPersonalDetailMatchSearchTerm({}, OTHER_USER_ACCOUNT_ID, '')).toBe(true);
7171
});
7272
});
7373

7474
describe('cross-field matching', () => {
7575
it('should match a search term that spans displayName and login', () => {
76-
expect(isPersonalDetailMatchingSearchTerm({displayName: 'John', login: 'doe@test.com'}, OTHER_USER_ACCOUNT_ID, 'john doe')).toBe(true);
76+
expect(doesPersonalDetailMatchSearchTerm({displayName: 'John', login: 'doe@test.com'}, OTHER_USER_ACCOUNT_ID, 'john doe')).toBe(true);
7777
});
7878
});
7979

8080
describe('useLocaleLowerCase config', () => {
8181
it('should lowercase with toLocaleLowerCase when enabled', () => {
8282
expect(
83-
isPersonalDetailMatchingSearchTerm({displayName: 'ISTANBUL'}, OTHER_USER_ACCOUNT_ID, 'istanbul', {
83+
doesPersonalDetailMatchSearchTerm({displayName: 'ISTANBUL'}, OTHER_USER_ACCOUNT_ID, 'istanbul', {
8484
useLocaleLowerCase: true,
8585
}),
8686
).toBe(true);
8787
});
8888

8989
it('should lowercase with toLowerCase by default', () => {
90-
expect(isPersonalDetailMatchingSearchTerm({displayName: 'ISTANBUL'}, OTHER_USER_ACCOUNT_ID, 'istanbul')).toBe(true);
90+
expect(doesPersonalDetailMatchSearchTerm({displayName: 'ISTANBUL'}, OTHER_USER_ACCOUNT_ID, 'istanbul')).toBe(true);
9191
});
9292
});
9393

9494
describe('transformSearchText config', () => {
9595
it('should match against appended text from transform callback', () => {
9696
expect(
97-
isPersonalDetailMatchingSearchTerm({displayName: 'John'}, OTHER_USER_ACCOUNT_ID, 'extra', {
97+
doesPersonalDetailMatchSearchTerm({displayName: 'John'}, OTHER_USER_ACCOUNT_ID, 'extra', {
9898
transformSearchText: (concatenatedSearchTerms) => `${concatenatedSearchTerms} extra stuff`,
9999
}),
100100
).toBe(true);
101101
});
102102

103103
it('should pass already-lowercased terms to the transform callback', () => {
104104
let receivedText = '';
105-
isPersonalDetailMatchingSearchTerm({displayName: 'UPPER Case'}, OTHER_USER_ACCOUNT_ID, 'test', {
105+
doesPersonalDetailMatchSearchTerm({displayName: 'UPPER Case'}, OTHER_USER_ACCOUNT_ID, 'test', {
106106
transformSearchText: (concatenatedSearchTerms) => {
107107
receivedText = concatenatedSearchTerms;
108108
return concatenatedSearchTerms;
@@ -113,15 +113,15 @@ describe('isPersonalDetailMatchingSearchTerm', () => {
113113

114114
it('should use the transform result as the final match target', () => {
115115
expect(
116-
isPersonalDetailMatchingSearchTerm({displayName: 'Alice'}, OTHER_USER_ACCOUNT_ID, 'replaced', {
116+
doesPersonalDetailMatchSearchTerm({displayName: 'Alice'}, OTHER_USER_ACCOUNT_ID, 'replaced', {
117117
transformSearchText: () => 'completely replaced',
118118
}),
119119
).toBe(true);
120120
});
121121

122122
it('should support deburr with appended text (real-world usage)', () => {
123123
expect(
124-
isPersonalDetailMatchingSearchTerm({displayName: 'René'}, OTHER_USER_ACCOUNT_ID, 'rene', {
124+
doesPersonalDetailMatchSearchTerm({displayName: 'René'}, OTHER_USER_ACCOUNT_ID, 'rene', {
125125
useLocaleLowerCase: true,
126126
transformSearchText: (concatenatedSearchTerms) => deburr(`${concatenatedSearchTerms} ${'Résumé'.toLocaleLowerCase()}`),
127127
}),
@@ -131,15 +131,15 @@ describe('isPersonalDetailMatchingSearchTerm', () => {
131131

132132
describe('negative cases', () => {
133133
it('should not match when search term is longer than all fields', () => {
134-
expect(isPersonalDetailMatchingSearchTerm({displayName: 'Jo'}, OTHER_USER_ACCOUNT_ID, 'john')).toBe(false);
134+
expect(doesPersonalDetailMatchSearchTerm({displayName: 'Jo'}, OTHER_USER_ACCOUNT_ID, 'john')).toBe(false);
135135
});
136136

137137
it('should not match when search contains characters not in any field', () => {
138-
expect(isPersonalDetailMatchingSearchTerm({displayName: 'John'}, OTHER_USER_ACCOUNT_ID, 'john!')).toBe(false);
138+
expect(doesPersonalDetailMatchSearchTerm({displayName: 'John'}, OTHER_USER_ACCOUNT_ID, 'john!')).toBe(false);
139139
});
140140

141141
it('should not crash with undefined accountID', () => {
142-
expect(isPersonalDetailMatchingSearchTerm({accountID: undefined, displayName: 'Test'}, CURRENT_USER_ACCOUNT_ID, 'test')).toBe(true);
142+
expect(doesPersonalDetailMatchSearchTerm({accountID: undefined, displayName: 'Test'}, CURRENT_USER_ACCOUNT_ID, 'test')).toBe(true);
143143
});
144144
});
145145
});

0 commit comments

Comments
 (0)