Skip to content

Commit 07cd9d6

Browse files
Refactored localeCompare in OptionsListUtils
1 parent 8b4ae17 commit 07cd9d6

File tree

11 files changed

+70
-24
lines changed

11 files changed

+70
-24
lines changed

src/components/ApprovalWorkflowSection.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type ApprovalWorkflowSectionProps = {
2424
function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSectionProps) {
2525
const styles = useThemeStyles();
2626
const theme = useTheme();
27-
const {translate, toLocaleOrdinal} = useLocalize();
27+
const {translate, toLocaleOrdinal, localeCompare} = useLocalize();
2828
const {shouldUseNarrowLayout} = useResponsiveLayout();
2929

3030
const approverTitle = useCallback(
@@ -38,10 +38,10 @@ function ApprovalWorkflowSection({approvalWorkflow, onPress}: ApprovalWorkflowSe
3838
return translate('workspace.common.everyone');
3939
}
4040

41-
return sortAlphabetically(approvalWorkflow.members, 'displayName')
41+
return sortAlphabetically(approvalWorkflow.members, 'displayName', localeCompare)
4242
.map((m) => Str.removeSMSDomain(m.displayName))
4343
.join(', ');
44-
}, [approvalWorkflow.isDefault, approvalWorkflow.members, translate]);
44+
}, [approvalWorkflow.isDefault, approvalWorkflow.members, translate, localeCompare]);
4545

4646
return (
4747
<PressableWithoutFeedback

src/components/WorkspaceMembersSelectionList.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ type WorkspaceMembersSelectionListProps = {
3535
};
3636

3737
function WorkspaceMembersSelectionList({policyID, selectedApprover, setApprover}: WorkspaceMembersSelectionListProps) {
38-
const {translate} = useLocalize();
38+
const {translate, localeCompare} = useLocalize();
3939
const {didScreenTransitionEnd} = useScreenWrapperTransitionStatus();
4040
const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
4141
const personalDetails = usePersonalDetails();
@@ -83,11 +83,11 @@ function WorkspaceMembersSelectionList({policyID, selectedApprover, setApprover}
8383
return [
8484
{
8585
title: undefined,
86-
data: sortAlphabetically(filteredApprovers, 'text'),
86+
data: sortAlphabetically(filteredApprovers, 'text', localeCompare),
8787
shouldShow: true,
8888
},
8989
];
90-
}, [debouncedSearchTerm, personalDetails, policy?.employeeList, policy?.owner, selectedApprover]);
90+
}, [debouncedSearchTerm, personalDetails, policy?.employeeList, policy?.owner, selectedApprover, localeCompare]);
9191

9292
const handleOnSelectRow = (approver: SelectionListApprover) => {
9393
setApprover(approver.login);

src/libs/OptionsListUtils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
66
import Onyx from 'react-native-onyx';
77
import type {SetNonNullable} from 'type-fest';
88
import {FallbackAvatar} from '@components/Icon/Expensicons';
9+
import type {LocaleContextProps} from '@components/LocaleContextProvider';
910
import type {PolicyTagList} from '@pages/workspace/tags/types';
1011
import type {IOUAction} from '@src/CONST';
1112
import CONST from '@src/CONST';
@@ -37,7 +38,6 @@ import Timing from './actions/Timing';
3738
import {getEnabledCategoriesCount} from './CategoryUtils';
3839
import filterArrayByMatch from './filterArrayByMatch';
3940
import {isReportMessageAttachment} from './isReportMessageAttachment';
40-
import localeCompare from './LocaleCompare';
4141
import {formatPhoneNumber} from './LocalePhoneNumber';
4242
import {translateLocal} from './Localize';
4343
import {appendCountryCode, getPhoneNumberWithoutSpecialChars} from './LoginUtils';
@@ -2571,7 +2571,7 @@ function filterAndOrderOptions(options: Options, searchInputValue: string, confi
25712571
};
25722572
}
25732573

2574-
function sortAlphabetically<T extends Partial<Record<TKey, string | undefined>>, TKey extends keyof T>(items: T[], key: TKey): T[] {
2574+
function sortAlphabetically<T extends Partial<Record<TKey, string | undefined>>, TKey extends keyof T>(items: T[], key: TKey, localeCompare: LocaleContextProps['localeCompare']): T[] {
25752575
return items.sort((a, b) => localeCompare(a[key]?.toLowerCase() ?? '', b[key]?.toLowerCase() ?? ''));
25762576
}
25772577

src/pages/workspace/WorkspaceMembersPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
108108
const [isDownloadFailureModalVisible, setIsDownloadFailureModalVisible] = useState(false);
109109
const isOfflineAndNoMemberDataAvailable = isEmptyObject(policy?.employeeList) && isOffline;
110110
const prevPersonalDetails = usePrevious(personalDetails);
111-
const {translate, formatPhoneNumber} = useLocalize();
111+
const {translate, formatPhoneNumber, localeCompare} = useLocalize();
112112
const {isAccountLocked, showLockedAccountModal} = useContext(LockedAccountContext);
113113
const filterEmployees = useCallback(
114114
(employee?: PolicyEmployee) => {
@@ -482,7 +482,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
482482
const normalizedSearchQuery = StringUtils.normalize(searchQuery);
483483
return memberText.includes(normalizedSearchQuery) || alternateText.includes(normalizedSearchQuery);
484484
}, []);
485-
const sortMembers = useCallback((memberOptions: MemberOption[]) => sortAlphabetically(memberOptions, 'text'), []);
485+
const sortMembers = useCallback((memberOptions: MemberOption[]) => sortAlphabetically(memberOptions, 'text', localeCompare), [localeCompare]);
486486
const [inputValue, setInputValue, filteredData] = useSearchResults(data, filterMember, sortMembers);
487487

488488
useEffect(() => {

src/pages/workspace/companyCards/assignCard/AssigneeStep.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type AssigneeStepProps = {
3838
};
3939

4040
function AssigneeStep({policy, feed}: AssigneeStepProps) {
41-
const {translate, formatPhoneNumber} = useLocalize();
41+
const {translate, formatPhoneNumber, localeCompare} = useLocalize();
4242
const styles = useThemeStyles();
4343
const {isOffline} = useNetwork();
4444
const [assignCard] = useOnyx(ONYXKEYS.ASSIGN_CARD, {canBeMissing: true});
@@ -137,10 +137,10 @@ function AssigneeStep({policy, feed}: AssigneeStepProps) {
137137
});
138138
});
139139

140-
membersList = sortAlphabetically(membersList, 'text');
140+
membersList = sortAlphabetically(membersList, 'text', localeCompare);
141141

142142
return membersList;
143-
}, [isOffline, policy?.employeeList, selectedMember, formatPhoneNumber]);
143+
}, [isOffline, policy?.employeeList, selectedMember, formatPhoneNumber, localeCompare]);
144144

145145
const sections = useMemo(() => {
146146
if (!debouncedSearchTerm) {

src/pages/workspace/expensifyCard/issueNew/AssigneeStep.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type AssigneeStepProps = {
3030
};
3131

3232
function AssigneeStep({policy}: AssigneeStepProps) {
33-
const {translate, formatPhoneNumber} = useLocalize();
33+
const {translate, formatPhoneNumber, localeCompare} = useLocalize();
3434
const styles = useThemeStyles();
3535
const {isOffline} = useNetwork();
3636
const policyID = policy?.id;
@@ -99,10 +99,10 @@ function AssigneeStep({policy}: AssigneeStepProps) {
9999
});
100100
});
101101

102-
membersList = sortAlphabetically(membersList, 'text');
102+
membersList = sortAlphabetically(membersList, 'text', localeCompare);
103103

104104
return membersList;
105-
}, [isOffline, policy?.employeeList, formatPhoneNumber]);
105+
}, [isOffline, policy?.employeeList, formatPhoneNumber, localeCompare]);
106106

107107
const sections = useMemo(() => {
108108
if (!debouncedSearchTerm) {

src/pages/workspace/workflows/approvals/ApprovalWorkflowEditor.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type ApprovalWorkflowEditorProps = {
3838

3939
function ApprovalWorkflowEditor({approvalWorkflow, removeApprovalWorkflow, policy, policyID}: ApprovalWorkflowEditorProps, ref: ForwardedRef<ScrollViewRN>) {
4040
const styles = useThemeStyles();
41-
const {translate, toLocaleOrdinal} = useLocalize();
41+
const {translate, toLocaleOrdinal, localeCompare} = useLocalize();
4242
const approverCount = approvalWorkflow.approvers.length;
4343

4444
const approverDescription = useCallback(
@@ -67,10 +67,10 @@ function ApprovalWorkflowEditor({approvalWorkflow, removeApprovalWorkflow, polic
6767
return translate('workspace.common.everyone');
6868
}
6969

70-
return sortAlphabetically(approvalWorkflow.members, 'displayName')
70+
return sortAlphabetically(approvalWorkflow.members, 'displayName', localeCompare)
7171
.map((m) => Str.removeSMSDomain(m.displayName))
7272
.join(', ');
73-
}, [approvalWorkflow.isDefault, approvalWorkflow.members, translate]);
73+
}, [approvalWorkflow.isDefault, approvalWorkflow.members, translate, localeCompare]);
7474

7575
const approverErrorMessage = useCallback(
7676
(approver: Approver | undefined, approverIndex: number) => {

src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsApproverPage.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ type ApproverSection = SectionListData<SelectionListApprover, Section<SelectionL
5454

5555
function WorkspaceWorkflowsApprovalsApproverPage({policy, personalDetails, isLoadingReportData = true, route}: WorkspaceWorkflowsApprovalsApproverPageProps) {
5656
const styles = useThemeStyles();
57-
const {translate} = useLocalize();
57+
const {translate, localeCompare} = useLocalize();
5858
const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
5959
const [approvalWorkflow, approvalWorkflowMetadata] = useOnyx(ONYXKEYS.APPROVAL_WORKFLOW, {canBeMissing: true});
6060
const isApprovalWorkflowLoading = isLoadingOnyxValue(approvalWorkflowMetadata);
@@ -144,7 +144,7 @@ function WorkspaceWorkflowsApprovalsApproverPage({policy, personalDetails, isLoa
144144
const filteredApprovers =
145145
debouncedSearchTerm !== '' ? tokenizedSearch(approvers, getSearchValueForPhoneOrEmail(debouncedSearchTerm), (option) => [option.text ?? '', option.login ?? '']) : approvers;
146146

147-
const data = sortAlphabetically(filteredApprovers, 'text');
147+
const data = sortAlphabetically(filteredApprovers, 'text', localeCompare);
148148
return [
149149
{
150150
title: undefined,
@@ -165,6 +165,7 @@ function WorkspaceWorkflowsApprovalsApproverPage({policy, personalDetails, isLoa
165165
membersEmail,
166166
policy?.preventSelfApproval,
167167
policy?.owner,
168+
localeCompare,
168169
]);
169170

170171
const shouldShowListEmptyContent = !debouncedSearchTerm && !!approvalWorkflow && !sections.at(0)?.data.length && !isApprovalWorkflowLoading;

src/pages/workspace/workflows/approvals/WorkspaceWorkflowsApprovalsExpensesFromPage.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ type WorkspaceWorkflowsApprovalsExpensesFromPageProps = WithPolicyAndFullscreenL
5656

5757
function WorkspaceWorkflowsApprovalsExpensesFromPage({policy, isLoadingReportData = true, route}: WorkspaceWorkflowsApprovalsExpensesFromPageProps) {
5858
const styles = useThemeStyles();
59-
const {translate} = useLocalize();
59+
const {translate, localeCompare} = useLocalize();
6060
const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
6161
const [approvalWorkflow, approvalWorkflowResults] = useOnyx(ONYXKEYS.APPROVAL_WORKFLOW, {canBeMissing: true});
6262
const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false});
@@ -142,11 +142,21 @@ function WorkspaceWorkflowsApprovalsExpensesFromPage({policy, isLoadingReportDat
142142
return [
143143
{
144144
title: undefined,
145-
data: sortAlphabetically(filteredMembers, 'text'),
145+
data: sortAlphabetically(filteredMembers, 'text', localeCompare),
146146
shouldShow: true,
147147
},
148148
];
149-
}, [approvalWorkflow?.availableMembers, debouncedSearchTerm, policy?.preventSelfApproval, policy?.employeeList, policy?.owner, selectedMembers, approversEmail, personalDetailLogins]);
149+
}, [
150+
approvalWorkflow?.availableMembers,
151+
debouncedSearchTerm,
152+
policy?.preventSelfApproval,
153+
policy?.employeeList,
154+
policy?.owner,
155+
selectedMembers,
156+
approversEmail,
157+
personalDetailLogins,
158+
localeCompare,
159+
]);
150160

151161
const goBack = useCallback(() => {
152162
let backTo;

tests/unit/OptionsListUtilsTest.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
orderOptions,
2222
orderWorkspaceOptions,
2323
recentReportComparator,
24+
sortAlphabetically,
2425
} from '@libs/OptionsListUtils';
2526
import {canCreateTaskInReport, canUserPerformWriteAction, isCanceledTaskReport, isExpensifyOnlyParticipantInReport} from '@libs/ReportUtils';
2627
import type {OptionData} from '@libs/ReportUtils';
@@ -29,6 +30,7 @@ import IntlStore from '@src/languages/IntlStore';
2930
import ONYXKEYS from '@src/ONYXKEYS';
3031
import type {PersonalDetails, Policy, Report} from '@src/types/onyx';
3132
import {getFakeAdvancedReportAction} from '../utils/LHNTestUtils';
33+
import {localeCompare} from '../utils/TestHelper';
3234
import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
3335

3436
jest.mock('@rnmapbox/maps', () => {
@@ -1933,4 +1935,26 @@ describe('OptionsListUtils', () => {
19331935
expect(result.at(1)!.reportID).toBe('3');
19341936
});
19351937
});
1938+
1939+
describe('sortAlphabetically', () => {
1940+
it('should sort options alphabetically by text', () => {
1941+
const options: OptionData[] = [{text: 'Banana', reportID: '1'} as OptionData, {text: 'Apple', reportID: '2'} as OptionData, {text: 'Cherry', reportID: '3'} as OptionData];
1942+
const sortedOptions = sortAlphabetically(options, 'text', localeCompare);
1943+
expect(sortedOptions.at(0)?.reportID).toBe('2');
1944+
expect(sortedOptions.at(1)?.reportID).toBe('1');
1945+
expect(sortedOptions.at(2)?.reportID).toBe('3');
1946+
});
1947+
1948+
it('should handle empty array', () => {
1949+
const sortedOptions = sortAlphabetically([], 'abc', localeCompare);
1950+
expect(sortedOptions).toEqual([]);
1951+
});
1952+
1953+
it('should handle single option', () => {
1954+
const options: OptionData[] = [{text: 'Single', reportID: '1'} as OptionData];
1955+
const sortedOptions = sortAlphabetically(options, 'text', localeCompare);
1956+
expect(sortedOptions.length).toBe(1);
1957+
expect(sortedOptions.at(0)?.text).toBe('Single');
1958+
});
1959+
});
19361960
});

0 commit comments

Comments
 (0)