Skip to content
Merged
7 changes: 4 additions & 3 deletions src/components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS
.flatMap((filteredReportActions) => Object.values(filteredReportActions ?? {})),
[reportActions],
);
const {translate, localeCompare} = useLocalize();
const {translate, localeCompare, formatPhoneNumber} = useLocalize();
const searchListRef = useRef<SelectionListHandle | null>(null);

const clearTransactionsAndSetHashAndKey = useCallback(() => {
Expand Down Expand Up @@ -359,8 +359,9 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS
if (groupBy && (isChat || isTask)) {
return [];
}
return getSections(type, searchResults.data, searchResults.search, groupBy, exportReportActions, searchKey, archivedReportsIdSet);
}, [searchKey, exportReportActions, groupBy, isDataLoaded, searchResults, type, archivedReportsIdSet]);

return getSections(type, searchResults.data, searchResults.search, formatPhoneNumber, groupBy, exportReportActions, searchKey, archivedReportsIdSet);
}, [searchKey, exportReportActions, groupBy, isDataLoaded, searchResults, type, archivedReportsIdSet, formatPhoneNumber]);

useEffect(() => {
/** We only want to display the skeleton for the status filters the first time we load them for a specific data type */
Expand Down
27 changes: 19 additions & 8 deletions src/libs/SearchUIUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
import {convertToDisplayString} from './CurrencyUtils';
import DateUtils from './DateUtils';
import interceptAnonymousUser from './interceptAnonymousUser';
import {formatPhoneNumber} from './LocalePhoneNumber';
import {translateLocal} from './Localize';
import Navigation from './Navigation/Navigation';
import Parser from './Parser';
Expand Down Expand Up @@ -169,7 +168,7 @@
}

let currentAccountID: number | undefined;
Onyx.connect({

Check warning on line 171 in src/libs/SearchUIUtils.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.SESSION,
callback: (session) => {
currentAccountID = session?.accountID;
Expand Down Expand Up @@ -446,6 +445,7 @@
from: SearchPersonalDetails,
to: SearchPersonalDetails,
policy: SearchPolicy,
formatPhoneNumber: LocaleContextProps['formatPhoneNumber'],
): Pick<TransactionListItemType, 'formattedFrom' | 'formattedTo' | 'formattedTotal' | 'formattedMerchant' | 'date'> {
const isExpenseReport = transactionItem.reportType === CONST.REPORT.TYPE.EXPENSE;

Expand Down Expand Up @@ -729,7 +729,12 @@
*
* Do not use directly, use only via `getSections()` facade.
*/
function getTransactionsSections(data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search'], currentSearch: SearchKey): TransactionListItemType[] {
function getTransactionsSections(
data: OnyxTypes.SearchResults['data'],
metadata: OnyxTypes.SearchResults['search'],
currentSearch: SearchKey,
formatPhoneNumber: LocaleContextProps['formatPhoneNumber'],
): TransactionListItemType[] {
const shouldShowMerchant = getShouldShowMerchant(data);
const doesDataContainAPastYearTransaction = shouldShowYear(data);
const {shouldShowAmountInWideColumn, shouldShowTaxAmountInWideColumn} = getWideAmountIndicators(data);
Expand Down Expand Up @@ -759,7 +764,7 @@
const from = personalDetailsMap.get(transactionItem.accountID.toString()) ?? emptyPersonalDetails;
const to = transactionItem.managerID && !shouldShowBlankTo ? (personalDetailsMap.get(transactionItem.managerID.toString()) ?? emptyPersonalDetails) : emptyPersonalDetails;

const {formattedFrom, formattedTo, formattedTotal, formattedMerchant, date} = getTransactionItemCommonFormattedProperties(transactionItem, from, to, policy);
const {formattedFrom, formattedTo, formattedTotal, formattedMerchant, date} = getTransactionItemCommonFormattedProperties(transactionItem, from, to, policy, formatPhoneNumber);

const transactionSection: TransactionListItemType = {
action: getAction(data, allViolations, key, currentSearch),
Expand Down Expand Up @@ -1022,7 +1027,11 @@
*
* Do not use directly, use only via `getSections()` facade.
*/
function getTaskSections(data: OnyxTypes.SearchResults['data'], archivedReportsIDList?: ArchivedReportsIDSet): TaskListItemType[] {
function getTaskSections(
data: OnyxTypes.SearchResults['data'],
formatPhoneNumber: LocaleContextProps['formatPhoneNumber'],
archivedReportsIDList?: ArchivedReportsIDSet,
): TaskListItemType[] {
return (
Object.keys(data)
.filter(isReportEntry)
Expand Down Expand Up @@ -1147,6 +1156,7 @@
data: OnyxTypes.SearchResults['data'],
metadata: OnyxTypes.SearchResults['search'],
currentSearch: SearchKey,
formatPhoneNumber: LocaleContextProps['formatPhoneNumber'],
reportActions: Record<string, OnyxTypes.ReportAction[]> = {},
): TransactionGroupListItemType[] {
const shouldShowMerchant = getShouldShowMerchant(data);
Expand Down Expand Up @@ -1195,7 +1205,7 @@
const from = data.personalDetailsList?.[transactionItem.accountID];
const to = transactionItem.managerID && !shouldShowBlankTo ? (data.personalDetailsList?.[transactionItem.managerID] ?? emptyPersonalDetails) : emptyPersonalDetails;

const {formattedFrom, formattedTo, formattedTotal, formattedMerchant, date} = getTransactionItemCommonFormattedProperties(transactionItem, from, to, policy);
const {formattedFrom, formattedTo, formattedTotal, formattedMerchant, date} = getTransactionItemCommonFormattedProperties(transactionItem, from, to, policy, formatPhoneNumber);

const transaction = {
...transactionItem,
Expand Down Expand Up @@ -1274,6 +1284,7 @@
type: SearchDataTypes,
data: OnyxTypes.SearchResults['data'],
metadata: OnyxTypes.SearchResults['search'],
formatPhoneNumber: LocaleContextProps['formatPhoneNumber'],
groupBy?: SearchGroupBy,
reportActions: Record<string, OnyxTypes.ReportAction[]> = {},
currentSearch: SearchKey = CONST.SEARCH.SEARCH_KEYS.EXPENSES,
Expand All @@ -1283,23 +1294,23 @@
return getReportActionsSections(data);
}
if (type === CONST.SEARCH.DATA_TYPES.TASK) {
return getTaskSections(data, archivedReportsIDList);
return getTaskSections(data, formatPhoneNumber, archivedReportsIDList);
}

if (groupBy) {
// Disabling the default-case lint rule here is actually safer as this forces us to make the switch cases exhaustive
// eslint-disable-next-line default-case
switch (groupBy) {
case CONST.SEARCH.GROUP_BY.REPORTS:
return getReportSections(data, metadata, currentSearch, reportActions);
return getReportSections(data, metadata, currentSearch, formatPhoneNumber, reportActions);
case CONST.SEARCH.GROUP_BY.MEMBERS:
return getMemberSections(data, metadata);
case CONST.SEARCH.GROUP_BY.CARDS:
return getCardSections(data, metadata);
}
}

return getTransactionsSections(data, metadata, currentSearch);
return getTransactionsSections(data, metadata, currentSearch, formatPhoneNumber);
}

/**
Expand Down
18 changes: 9 additions & 9 deletions tests/unit/Search/SearchUIUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as SearchUIUtils from '@src/libs/SearchUIUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import type * as OnyxTypes from '@src/types/onyx';
import type {SearchDataTypes} from '@src/types/onyx/SearchResults';
import {localeCompare} from '../../utils/TestHelper';
import {formatPhoneNumber, localeCompare} from '../../utils/TestHelper';
import waitForBatchedUpdates from '../../utils/waitForBatchedUpdates';

jest.mock('@src/components/ConfirmedRoute.tsx');
Expand Down Expand Up @@ -1298,41 +1298,41 @@ describe('SearchUIUtils', () => {

describe('Test getSections', () => {
it('should return getReportActionsSections result when type is CHAT', () => {
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.CHAT, searchResults.data, searchResults.search)).toStrictEqual(reportActionListItems);
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.CHAT, searchResults.data, searchResults.search, formatPhoneNumber)).toStrictEqual(reportActionListItems);
});

it('should return getTransactionsSections result when groupBy is undefined', () => {
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.EXPENSE, searchResults.data, searchResults.search, undefined)).toStrictEqual(transactionsListItems);
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.EXPENSE, searchResults.data, searchResults.search, formatPhoneNumber, undefined)).toStrictEqual(transactionsListItems);
});

it('should return getReportSections result when type is EXPENSE and groupBy is report', () => {
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.EXPENSE, searchResults.data, searchResults.search, CONST.SEARCH.GROUP_BY.REPORTS)).toStrictEqual(
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.EXPENSE, searchResults.data, searchResults.search, formatPhoneNumber, CONST.SEARCH.GROUP_BY.REPORTS)).toStrictEqual(
transactionReportGroupListItems,
);
});

it('should return getReportSections result when type is TRIP and groupBy is report', () => {
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.TRIP, searchResults.data, searchResults.search, CONST.SEARCH.GROUP_BY.REPORTS)).toStrictEqual(
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.TRIP, searchResults.data, searchResults.search, formatPhoneNumber, CONST.SEARCH.GROUP_BY.REPORTS)).toStrictEqual(
transactionReportGroupListItems,
);
});

it('should return getReportSections result when type is INVOICE and groupBy is report', () => {
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.INVOICE, searchResults.data, searchResults.search, CONST.SEARCH.GROUP_BY.REPORTS)).toStrictEqual(
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.INVOICE, searchResults.data, searchResults.search, formatPhoneNumber, CONST.SEARCH.GROUP_BY.REPORTS)).toStrictEqual(
transactionReportGroupListItems,
);
});

it('should return getMemberSections result when type is EXPENSE and groupBy is member', () => {
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.EXPENSE, searchResults.data, searchResults.search, CONST.SEARCH.GROUP_BY.MEMBERS)).toStrictEqual([]); // s77rt update test
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.EXPENSE, searchResults.data, searchResults.search, formatPhoneNumber, CONST.SEARCH.GROUP_BY.MEMBERS)).toStrictEqual([]); // s77rt update test

@allgandalf allgandalf Aug 7, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@s77rt what does that comment there mean 😆

Screenshot 2025-08-08 at 12 03 49 AM

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's part of the Search project. The new features are being shipped progressively. This comment is just my TODO comment 😁

});

it('should return getMemberSections result when type is TRIP and groupBy is member', () => {
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.TRIP, searchResults.data, searchResults.search, CONST.SEARCH.GROUP_BY.MEMBERS)).toStrictEqual([]); // s77rt update test
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.TRIP, searchResults.data, searchResults.search, formatPhoneNumber, CONST.SEARCH.GROUP_BY.MEMBERS)).toStrictEqual([]); // s77rt update test
});

it('should return getMemberSections result when type is INVOICE and groupBy is member', () => {
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.INVOICE, searchResults.data, searchResults.search, CONST.SEARCH.GROUP_BY.MEMBERS)).toStrictEqual([]); // s77rt update test
expect(SearchUIUtils.getSections(CONST.SEARCH.DATA_TYPES.INVOICE, searchResults.data, searchResults.search, formatPhoneNumber, CONST.SEARCH.GROUP_BY.MEMBERS)).toStrictEqual([]); // s77rt update test
});

// s77rt add test for group by card
Expand Down
Loading