From 3e6b1255748ebbf4d729c811e6c353300c68f6ea Mon Sep 17 00:00:00 2001 From: VH Date: Thu, 21 May 2026 16:07:34 +0700 Subject: [PATCH 1/6] refactor: make reportAttributes required in getReportName Make reportAttributes parameter required in getReportName() and getSearchReportName() to ensure derived report name values are always passed explicitly. Updated all call sites including SearchUIUtils, Search components, and related hooks to pass reportAttributes from useReportAttributes() hook. This enforces awareness of the reportAttributes parameter and prevents accidental omissions while maintaining backward compatibility through default values. Related to #66427 --- .../ListItem/TransactionGroupListItem.tsx | 3 + src/components/Search/SearchStaticList.tsx | 3 + src/components/Search/index.tsx | 3 +- src/hooks/useSearchSections.ts | 2 +- src/libs/ReportUtils.ts | 12 ++-- src/libs/SearchUIUtils.ts | 21 +++++-- .../useSpendOverTimeData.ts | 3 + src/pages/inbox/HeaderView.tsx | 2 +- tests/unit/Search/SearchUIUtilsTest.ts | 63 +++++++++++++++++++ 9 files changed, 99 insertions(+), 13 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx index 7689904aaecf..1d212aa2743b 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx @@ -19,6 +19,7 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; +import useReportAttributes from '@hooks/useReportAttributes'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useSyncFocus from '@hooks/useSyncFocus'; @@ -125,6 +126,7 @@ function TransactionGroupListItem({ const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); const [cardFeeds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); + const reportAttributes = useReportAttributes(); let transactions: TransactionListItemType[]; if (isExpenseReportType) { @@ -145,6 +147,7 @@ function TransactionGroupListItem({ cardFeeds, conciergeReportID, convertToDisplayString, + reportAttributesDerivedValue: reportAttributes ?? {}, }) as [TransactionListItemType[], number, boolean]; transactions = sectionData.map((transactionItem) => ({ ...transactionItem, diff --git a/src/components/Search/SearchStaticList.tsx b/src/components/Search/SearchStaticList.tsx index 4b7ebbf68bc4..0aecfbe2e51d 100644 --- a/src/components/Search/SearchStaticList.tsx +++ b/src/components/Search/SearchStaticList.tsx @@ -25,6 +25,7 @@ import TransactionItemRow from '@components/TransactionItemRow'; import {useCurrencyListActions} from '@hooks/useCurrencyList'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import useReportAttributes from '@hooks/useReportAttributes'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -76,6 +77,7 @@ function SearchStaticList({ const session = useSession(); const accountID = session?.accountID ?? CONST.DEFAULT_NUMBER_ID; const email = session?.email; + const reportAttributes = useReportAttributes(); const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); const [hasCompletedGuidedSetupFlow] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasCompletedGuidedSetupFlowSelector}); @@ -103,6 +105,7 @@ function SearchStaticList({ allReportMetadata: undefined, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: reportAttributes ?? {}, }); return getSortedSections(type, status, filteredData, localeCompare, translate, sortBy, sortOrder, validGroupBy) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 794339e7d43d..ad1aae845982 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -532,7 +532,7 @@ function Search({ conciergeReportID, onyxPersonalDetailsList, policyForMovingExpenses, - reportAttributesDerivedValue, + reportAttributesDerivedValue: reportAttributesDerivedValue ?? {}, convertToDisplayString, optimisticTransactionID: optimisticTrackingState.optimisticWatchKey?.toString().replace(ONYXKEYS.COLLECTION.TRANSACTION, ''), }); @@ -608,6 +608,7 @@ function Search({ allReportMetadata, conciergeReportID, convertToDisplayString, + reportAttributesDerivedValue: reportAttributesDerivedValue ?? {}, }); return { ...item, diff --git a/src/hooks/useSearchSections.ts b/src/hooks/useSearchSections.ts index 7c1f1d012377..20c7fdd7f96d 100644 --- a/src/hooks/useSearchSections.ts +++ b/src/hooks/useSearchSections.ts @@ -63,7 +63,7 @@ function useSearchSections(): UseSearchSectionsResult { cardFeeds, allReportMetadata, conciergeReportID, - reportAttributesDerivedValue, + reportAttributesDerivedValue: reportAttributesDerivedValue ?? {}, convertToDisplayString, }); results = getSortedSections(type, status ?? '', searchData, localeCompare, translate, sortBy, sortOrder, groupBy).map((value) => value.reportID); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index c9cf512dd0aa..cbe83f42be3b 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -973,7 +973,7 @@ type GetReportNameParams = { personalDetails?: Partial; invoiceReceiverPolicy?: OnyxEntry; /** Report attributes used to return a precomputed report name */ - reportAttributes?: ReportAttributesDerivedValue['reports']; + reportAttributes: ReportAttributesDerivedValue['reports']; transactions?: Transaction[]; reports?: Report[]; isReportArchived?: boolean; @@ -5736,10 +5736,9 @@ function getReportName(reportNameInformation: GetReportNameParams): string { // Check if we can use report name in derived values - only when we have report but no other params const canUseDerivedValue = report && policy === undefined && parentReportActionParam === undefined && personalDetails === undefined && invoiceReceiverPolicy === undefined && isReportArchived === undefined; - const attributes = reportAttributes ?? reportAttributesDerivedValue; - const derivedNameExists = report && !!attributes?.[report.reportID]?.reportName; + const derivedNameExists = report && !!reportAttributes?.[report.reportID]?.reportName; if (canUseDerivedValue && derivedNameExists) { - return attributes[report.reportID].reportName; + return reportAttributes[report.reportID].reportName; } let formattedName: string | undefined; @@ -5763,7 +5762,7 @@ function getReportName(reportNameInformation: GetReportNameParams): string { reportPolicy, parentReport, personalDetails as PersonalDetailsList, - attributes, + reportAttributes, ); if (parentReportActionBasedName) { @@ -5773,7 +5772,7 @@ function getReportName(reportNameInformation: GetReportNameParams): string { if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.CREATED_REPORT_FOR_UNAPPROVED_TRANSACTIONS)) { const {originalID} = getOriginalMessage(parentReportAction) ?? {}; const originalReport = deprecatedAllReports?.[`${ONYXKEYS.COLLECTION.REPORT}${originalID}`]; - const reportName = getReportName({report: originalReport}); + const reportName = getReportName({report: originalReport, reportAttributes}); return getCreatedReportForUnapprovedTransactionsMessage(originalID, reportName, !!parentReportAction.isOriginalReportDeleted, translateLocal); } @@ -5927,6 +5926,7 @@ function getSearchReportName(props: GetReportNameParams): string { parentReportActionParam: props.parentReportActionParam, personalDetails: props.personalDetails, invoiceReceiverPolicy: props.invoiceReceiverPolicy, + reportAttributes: props.reportAttributes, transactions: props.transactions, isReportArchived: props.isReportArchived, reports: props.reports, diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index c96f0da1fcb2..e24688b5b5df 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -600,7 +600,7 @@ type GetSectionsParams = { onyxPersonalDetailsList?: OnyxTypes.PersonalDetailsList; policyForMovingExpenses?: OnyxTypes.Policy; optimisticTransactionID?: string; - reportAttributesDerivedValue?: OnyxTypes.ReportAttributesDerivedValue['reports']; + reportAttributesDerivedValue: OnyxTypes.ReportAttributesDerivedValue['reports']; }; /** @@ -2600,7 +2600,11 @@ function createAndOpenSearchTransactionThread({ * * Do not use directly, use only via `getSections()` facade. */ -function getReportActionsSections(data: OnyxTypes.SearchResults['data'], visibleReportActionsData?: OnyxTypes.VisibleReportActionsDerivedValue): [ReportActionListItemType[], number] { +function getReportActionsSections( + data: OnyxTypes.SearchResults['data'], + visibleReportActionsData?: OnyxTypes.VisibleReportActionsDerivedValue, + reportAttributesDerivedValue: OnyxTypes.ReportAttributesDerivedValue['reports'] = {}, +): [ReportActionListItemType[], number] { const reportActionItems: ReportActionListItemType[] = []; const transactions = Object.keys(data) @@ -2651,7 +2655,16 @@ function getReportActionsSections(data: OnyxTypes.SearchResults['data'], visible ...reportAction, reportID, from, - reportName: getSearchReportName({report, policy, personalDetails: data.personalDetailsList, transactions, invoiceReceiverPolicy, reports, isReportArchived}), + reportName: getSearchReportName({ + report, + policy, + personalDetails: data.personalDetailsList, + reportAttributes: reportAttributesDerivedValue, + transactions, + invoiceReceiverPolicy, + reports, + isReportArchived, + }), formattedFrom: from?.displayName ?? from?.login ?? '', date: reportAction.created, keyForList: reportAction.reportActionID, @@ -3473,7 +3486,7 @@ function getSections({ optimisticTransactionID, }: GetSectionsParams): GetSectionsResult { if (type === CONST.SEARCH.DATA_TYPES.CHAT) { - return [...getReportActionsSections(data, visibleReportActionsData), false]; + return [...getReportActionsSections(data, visibleReportActionsData, reportAttributesDerivedValue), false]; } if (type === CONST.SEARCH.DATA_TYPES.TASK) { return [...getTaskSections(data, formatPhoneNumber, conciergeReportID, archivedReportsIDList, reportAttributesDerivedValue), false]; diff --git a/src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts b/src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts index 3c733fc87327..62b31a84a7f5 100644 --- a/src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts +++ b/src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts @@ -8,6 +8,7 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; +import useReportAttributes from '@hooks/useReportAttributes'; import {search} from '@libs/actions/Search'; import {getSections, getSortedSections, getSuggestedSearches, isSearchDataLoaded} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; @@ -57,6 +58,7 @@ function useSpendOverTimeData() { const {accountID, login} = useCurrentUserPersonalDetails(); const [searchResults] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${queryJSON?.hash}`); const isSearchLoading = !!searchResults?.search?.isLoading; + const reportAttributes = useReportAttributes(); const {isOffline} = useNetwork(); const isFocused = useIsFocused(); @@ -101,6 +103,7 @@ function useSpendOverTimeData() { allReportMetadata: undefined, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: reportAttributes ?? {}, })[0], localeCompare, translate, diff --git a/src/pages/inbox/HeaderView.tsx b/src/pages/inbox/HeaderView.tsx index 0a7476f7db9d..a61a5c6631ae 100644 --- a/src/pages/inbox/HeaderView.tsx +++ b/src/pages/inbox/HeaderView.tsx @@ -157,7 +157,7 @@ function HeaderView({onNavigationMenuButtonClicked, reportID}: HeaderViewProps) : undefined; const statusColorForInvoiceReport = isParentInvoiceAndIsChatThread ? getReportStatusColorStyle(theme, reportHeaderData?.stateNum, reportHeaderData?.statusNum) : {}; const isParentReportHeaderDataArchived = useReportIsArchived(reportHeaderData?.parentReportID); - const parentNavigationSubtitleData = getParentNavigationSubtitle(parentNavigationReport, policy, conciergeReportID, isParentReportHeaderDataArchived); + const parentNavigationSubtitleData = getParentNavigationSubtitle(parentNavigationReport, policy, conciergeReportID, isParentReportHeaderDataArchived, reportAttributes); const humanAgentAccountID = getHumanAgentAccountIDFromReportAction(parentReportAction); const humanAgentName = getHumanAgentFirstName(parentReportAction, personalDetails); const reportDescription = StringUtils.lineBreaksToSpaces(Parser.htmlToText(getReportDescription(report))); diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index aa82fb128a9e..c10ee59d814e 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -2501,6 +2501,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }); expect(filteredReportActions).toStrictEqual(reportActionListItems); expect(allReportActionsLength).toBe(6); @@ -2519,6 +2520,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0], ).toEqual(transactionsListItems); }); @@ -2548,6 +2550,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0] as TransactionListItemType[]; const distanceTransaction = result.find((item) => item.transactionID === distanceTransactionID); @@ -2584,6 +2587,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0] as TransactionGroupListItemType[]; const reportGroup = result.find((group) => group.transactions?.some((transaction) => transaction.transactionID === distanceTransactionID)); @@ -2614,6 +2618,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionReportGroupListItems); }); @@ -2641,6 +2646,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0] as TransactionReportGroupListItemType[]; expect(result.find((item) => item.reportID === reportID2)?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); @@ -2679,6 +2685,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionReportGroupListItemType[], number, boolean]; expect(resultWithoutOnyx.at(0)?.primaryAvatar?.source).not.toBe(customAvatarUrl); @@ -2696,6 +2703,7 @@ describe('SearchUIUtils', () => { conciergeReportID: undefined, onyxPersonalDetailsList, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionReportGroupListItemType[], number, boolean]; expect(resultWithOnyx.at(0)?.primaryAvatar?.source).toBe(customAvatarUrl); @@ -2737,6 +2745,7 @@ describe('SearchUIUtils', () => { conciergeReportID: undefined, onyxPersonalDetailsList, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionReportGroupListItemType[], number, boolean]; expect(result?.at(0)?.primaryAvatar?.source).toBe(apiAvatarUrl); @@ -2780,6 +2789,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionReportGroupListItemType[], number, boolean]; const reportWithoutOnyx = resultWithoutOnyx.find((item) => item.reportID === reportID2); @@ -2797,6 +2807,7 @@ describe('SearchUIUtils', () => { conciergeReportID: undefined, onyxPersonalDetailsList, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionReportGroupListItemType[], number, boolean]; const reportWithOnyx = resultWithOnyx.find((item) => item.reportID === reportID2); @@ -2831,6 +2842,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionReportGroupListItemType[], number, boolean]; const reportGroupWithoutOnyx = resultWithoutOnyx.find((item) => item.reportID === reportID); @@ -2849,6 +2861,7 @@ describe('SearchUIUtils', () => { conciergeReportID: undefined, onyxPersonalDetailsList, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionReportGroupListItemType[], number, boolean]; const reportGroupWithOnyx = resultWithOnyx.find((item) => item.reportID === reportID); @@ -2891,6 +2904,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, onyxPersonalDetailsList, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionReportGroupListItemType[], number, boolean]; const reportGroup = result.find((item) => item.reportID === reportID); @@ -2938,6 +2952,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0]; const resultReportFirst = SearchUIUtils.getSections({ type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT, @@ -2950,6 +2965,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0]; expect(resultTransactionFirst).toBeDefined(); @@ -2976,6 +2992,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionMemberGroupListItems); }); @@ -3005,6 +3022,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionCardGroupListItems); }); @@ -3023,6 +3041,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionWithdrawalIDGroupListItems); }); @@ -3050,6 +3069,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionWithdrawalIDGroupListItemType[], number, boolean]; expect(result).toHaveLength(0); @@ -3069,6 +3089,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionCategoryGroupListItems); }); @@ -3102,6 +3123,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionCategoryGroupListItemType[], number, boolean]; expect(result).toHaveLength(2); @@ -3168,6 +3190,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionMonthGroupListItems); }); @@ -3203,6 +3226,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionMonthGroupListItemType[], number, boolean]; expect(result).toHaveLength(2); @@ -3223,6 +3247,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionMonthGroupListItemType[], number, boolean]; expect(result).toHaveLength(2); @@ -3305,6 +3330,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionYearGroupListItems); }); @@ -3338,6 +3364,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionYearGroupListItemType[], number, boolean]; expect(result).toHaveLength(2); @@ -3358,6 +3385,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionYearGroupListItemType[], number, boolean]; expect(result).toHaveLength(2); @@ -3740,6 +3768,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionQuarterGroupListItems); }); @@ -3775,6 +3804,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionQuarterGroupListItemType[], number, boolean]; expect(result).toHaveLength(2); @@ -3795,6 +3825,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionQuarterGroupListItemType[], number, boolean]; expect(result).toHaveLength(2); @@ -3858,6 +3889,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionWeekGroupListItems); }); @@ -3891,6 +3923,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionWeekGroupListItemType[], number, boolean]; expect(result).toHaveLength(2); @@ -3948,6 +3981,7 @@ describe('SearchUIUtils', () => { }, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionCategoryGroupListItemType[], number, boolean]; // Each category section should have a transactionsQueryJSON with a hash @@ -3993,6 +4027,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionCategoryGroupListItemType[], number, boolean]; expect(result).toHaveLength(3); @@ -4036,6 +4071,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionCategoryGroupListItemType[], number, boolean]; expect(result).toHaveLength(3); @@ -4068,6 +4104,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionCategoryGroupListItemType[], number, boolean]; expect(result).toHaveLength(1); @@ -4105,6 +4142,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionCategoryGroupListItemType[], number, boolean]; expect(result).toHaveLength(2); @@ -4131,6 +4169,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionMerchantGroupListItems); }); @@ -4165,6 +4204,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionMerchantGroupListItemType[], number, boolean]; expect(result).toHaveLength(2); @@ -4213,6 +4253,7 @@ describe('SearchUIUtils', () => { }, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionMerchantGroupListItemType[], number, boolean]; expect(result).toHaveLength(1); @@ -4263,6 +4304,7 @@ describe('SearchUIUtils', () => { }, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionMerchantGroupListItemType[], number, boolean]; expect(result).toHaveLength(1); @@ -4313,6 +4355,7 @@ describe('SearchUIUtils', () => { }, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionMerchantGroupListItemType[], number, boolean]; expect(result).toHaveLength(1); @@ -4363,6 +4406,7 @@ describe('SearchUIUtils', () => { }, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionMerchantGroupListItemType[], number, boolean]; expect(result).toHaveLength(1); @@ -4434,6 +4478,7 @@ describe('SearchUIUtils', () => { }, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionMerchantGroupListItemType[], number, boolean]; // Each merchant section should have a transactionsQueryJSON with a hash @@ -4480,6 +4525,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionMerchantGroupListItemType[], number, boolean]; expect(result).toHaveLength(3); @@ -4524,6 +4570,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionMerchantGroupListItemType[], number, boolean]; expect(result).toHaveLength(3); @@ -4547,6 +4594,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionTagGroupListItems); }); @@ -4576,6 +4624,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionTagGroupListItemType[], number, boolean]; // formattedTag should have unescaped colons for display @@ -4613,6 +4662,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionTagGroupListItemType[], number, boolean]; expect(result).toHaveLength(2); @@ -4643,6 +4693,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionTagGroupListItemType[], number, boolean]; expect(result).toHaveLength(1); @@ -4711,6 +4762,7 @@ describe('SearchUIUtils', () => { }, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionTagGroupListItemType[], number, boolean]; // Each tag section should have a transactionsQueryJSON with a hash @@ -4769,6 +4821,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: '999', convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TaskListItemType[], number, boolean]; expect(result).toHaveLength(1); @@ -4796,6 +4849,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: '999', convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TaskListItemType[], number, boolean]; expect(result).toHaveLength(0); @@ -4864,6 +4918,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: '999', convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TaskListItemType[], number, boolean]; expect(result).toHaveLength(2); @@ -4919,6 +4974,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: '999', convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TaskListItemType[], number, boolean]; expect(result).toHaveLength(1); @@ -4973,6 +5029,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: '999', convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TaskListItemType[], number, boolean]; // Only the task report should be included @@ -5029,6 +5086,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TaskListItemType[], number, boolean]; expect(result).toHaveLength(1); @@ -5079,6 +5137,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0] as TransactionReportGroupListItemType[]; const item = sections.find((s) => s.keyForList === testReportID); @@ -5194,6 +5253,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, })[0] as TransactionReportGroupListItemType[]; const item = sections.find((s) => s.keyForList === avatarTestReportID); @@ -5321,6 +5381,7 @@ describe('SearchUIUtils', () => { conciergeReportID: undefined, ...options, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionListItemType[], number, boolean]; } @@ -5580,6 +5641,7 @@ describe('SearchUIUtils', () => { conciergeReportID: undefined, ...options, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionReportGroupListItemType[], number, boolean]; } @@ -6241,6 +6303,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, + reportAttributesDerivedValue: {}, }) as [TransactionTagGroupListItemType[], number, boolean]; // Then sort the sections From 0afdfae335e0b931c3919c3601ecbe4cce40bf9f Mon Sep 17 00:00:00 2001 From: VH Date: Thu, 21 May 2026 16:08:35 +0700 Subject: [PATCH 2/6] refactor: make reportAttributes required in getParentNavigationSubtitle Make reportAttributes parameter required in getParentNavigationSubtitle() to ensure derived report name values are always passed explicitly when computing parent navigation subtitles. Updated all call sites including ShareCodePage and ReportDetailsPage to pass reportAttributes from useReportAttributes() hook. This enforces awareness of the reportAttributes parameter and prevents accidental omissions while maintaining backward compatibility through a default empty object. Related to #66427 --- src/libs/ReportUtils.ts | 5 +++-- src/pages/ReportDetailsPage.tsx | 4 ++-- src/pages/ShareCodePage.tsx | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index cbe83f42be3b..5121f9fb8429 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5946,6 +5946,7 @@ function getSearchReportName(props: GetReportNameParams): string { parentReportActionParam: props.parentReportActionParam, personalDetails: props.personalDetails, invoiceReceiverPolicy: props.invoiceReceiverPolicy, + reportAttributes: props.reportAttributes, transactions: props.transactions, isReportArchived: props.isReportArchived, reports: props.reports, @@ -6054,7 +6055,7 @@ function getParentNavigationSubtitle( policy: OnyxEntry, conciergeReportID: string | undefined, isParentReportArchived = false, - reportAttributes?: ReportAttributesDerivedValue['reports'], + reportAttributes: ReportAttributesDerivedValue['reports'] = {}, ): ParentNavigationSummaryParams { const parentReport = getParentReport(report); @@ -6099,7 +6100,7 @@ function getParentNavigationSubtitle( } return { - reportName: getReportNameFromNameUtils(parentReport, reportAttributes ?? reportAttributesDerivedValue), + reportName: getReportNameFromNameUtils(parentReport, reportAttributes), workspaceName: getPolicyName({report: parentReport, policy, returnEmptyIfNotFound: true}), }; } diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index c6c1ef6b9e65..6127de2ce4f0 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -213,6 +213,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata, reportLoading const [delegateEmail] = useOnyx(ONYXKEYS.ACCOUNT, {selector: delegateEmailSelector}); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); const {showConfirmModal} = useConfirmModal(); + const reportAttributes = useReportAttributes(); const isPolicyAdmin = useMemo(() => isPolicyAdminUtil(policy), [policy]); const isPolicyEmployee = useMemo(() => isPolicyEmployeeUtil(report?.policyID, policy), [report?.policyID, policy]); const isPolicyExpenseChat = useMemo(() => isPolicyExpenseChatUtil(report), [report]); @@ -240,7 +241,7 @@ function ReportDetailsPage({policy, report, route, reportMetadata, reportLoading const isReportArchived = useReportIsArchived(report?.reportID); const isArchivedRoom = useMemo(() => isArchivedNonExpenseReport(report, isReportArchived), [report, isReportArchived]); const shouldDisableRename = useMemo(() => shouldDisableRenameUtil(report, isReportArchived), [report, isReportArchived]); - const parentNavigationSubtitleData = getParentNavigationSubtitle(report, policy, conciergeReportID, isParentReportArchived); + const parentNavigationSubtitleData = getParentNavigationSubtitle(report, policy, conciergeReportID, isParentReportArchived, reportAttributes); const base62ReportID = getBase62ReportID(Number(report.reportID)); const ancestors = useAncestors(report); @@ -338,7 +339,6 @@ function ReportDetailsPage({policy, report, route, reportMetadata, reportLoading } else if (caseID === CASES.DEFAULT) { deleteMenuItemTitle = translate('common.delete'); } - const reportAttributes = useReportAttributes(); const isWorkspaceChat = useMemo(() => isWorkspaceChatUtil(report?.chatType ?? ''), [report?.chatType]); useEffect(() => { diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx index a77ac01f00f3..6d7783af20c2 100644 --- a/src/pages/ShareCodePage.tsx +++ b/src/pages/ShareCodePage.tsx @@ -97,11 +97,11 @@ function ShareCodePage({report, policy, backTo}: ShareCodePageProps) { .join(' & '); } - return getParentNavigationSubtitle(report, policy, conciergeReportID, isParentReportArchived).workspaceName ?? getChatRoomSubtitle(report, false, isReportArchived); + return getParentNavigationSubtitle(report, policy, conciergeReportID, isParentReportArchived, reportAttributes).workspaceName ?? getChatRoomSubtitle(report, false, isReportArchived); } return currentUserPersonalDetails.login; - }, [report, policy, currentUserPersonalDetails.login, isReport, isReportArchived, isParentReportArchived, formatPhoneNumber, conciergeReportID]); + }, [report, policy, currentUserPersonalDetails.login, isReport, isReportArchived, isParentReportArchived, formatPhoneNumber, conciergeReportID, reportAttributes]); const reportForTitle = useMemo(() => getReportForHeader(report), [report]); From f1510524598ca20b547ce9859a7df3eff95a0f72 Mon Sep 17 00:00:00 2001 From: VH Date: Thu, 21 May 2026 17:05:40 +0700 Subject: [PATCH 3/6] refactor: add default value for reportAttributesDerivedValue in getSections - Make reportAttributesDerivedValue accept undefined in type definition - Add default value (= {}) to getSections parameter - Remove redundant ?? {} from all 6 call sites - Maintains type safety while reducing code repetition --- .../Search/SearchList/ListItem/TransactionGroupListItem.tsx | 2 +- src/components/Search/SearchStaticList.tsx | 2 +- src/components/Search/index.tsx | 4 ++-- src/hooks/useSearchSections.ts | 2 +- src/libs/SearchUIUtils.ts | 4 ++-- src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx index 1d212aa2743b..a88d8c526e75 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx @@ -147,7 +147,7 @@ function TransactionGroupListItem({ cardFeeds, conciergeReportID, convertToDisplayString, - reportAttributesDerivedValue: reportAttributes ?? {}, + reportAttributesDerivedValue: reportAttributes, }) as [TransactionListItemType[], number, boolean]; transactions = sectionData.map((transactionItem) => ({ ...transactionItem, diff --git a/src/components/Search/SearchStaticList.tsx b/src/components/Search/SearchStaticList.tsx index 0aecfbe2e51d..3ea61b6d1fa8 100644 --- a/src/components/Search/SearchStaticList.tsx +++ b/src/components/Search/SearchStaticList.tsx @@ -105,7 +105,7 @@ function SearchStaticList({ allReportMetadata: undefined, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: reportAttributes ?? {}, + reportAttributesDerivedValue: reportAttributes, }); return getSortedSections(type, status, filteredData, localeCompare, translate, sortBy, sortOrder, validGroupBy) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index ad1aae845982..abf4d1b48e3f 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -532,7 +532,7 @@ function Search({ conciergeReportID, onyxPersonalDetailsList, policyForMovingExpenses, - reportAttributesDerivedValue: reportAttributesDerivedValue ?? {}, + reportAttributesDerivedValue, convertToDisplayString, optimisticTransactionID: optimisticTrackingState.optimisticWatchKey?.toString().replace(ONYXKEYS.COLLECTION.TRANSACTION, ''), }); @@ -608,7 +608,7 @@ function Search({ allReportMetadata, conciergeReportID, convertToDisplayString, - reportAttributesDerivedValue: reportAttributesDerivedValue ?? {}, + reportAttributesDerivedValue, }); return { ...item, diff --git a/src/hooks/useSearchSections.ts b/src/hooks/useSearchSections.ts index 20c7fdd7f96d..7c1f1d012377 100644 --- a/src/hooks/useSearchSections.ts +++ b/src/hooks/useSearchSections.ts @@ -63,7 +63,7 @@ function useSearchSections(): UseSearchSectionsResult { cardFeeds, allReportMetadata, conciergeReportID, - reportAttributesDerivedValue: reportAttributesDerivedValue ?? {}, + reportAttributesDerivedValue, convertToDisplayString, }); results = getSortedSections(type, status ?? '', searchData, localeCompare, translate, sortBy, sortOrder, groupBy).map((value) => value.reportID); diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index e24688b5b5df..4ca844e34958 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -600,7 +600,7 @@ type GetSectionsParams = { onyxPersonalDetailsList?: OnyxTypes.PersonalDetailsList; policyForMovingExpenses?: OnyxTypes.Policy; optimisticTransactionID?: string; - reportAttributesDerivedValue: OnyxTypes.ReportAttributesDerivedValue['reports']; + reportAttributesDerivedValue: OnyxTypes.ReportAttributesDerivedValue['reports'] | undefined; }; /** @@ -3481,7 +3481,7 @@ function getSections({ conciergeReportID, onyxPersonalDetailsList, policyForMovingExpenses, - reportAttributesDerivedValue, + reportAttributesDerivedValue = {}, convertToDisplayString, optimisticTransactionID, }: GetSectionsParams): GetSectionsResult { diff --git a/src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts b/src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts index 62b31a84a7f5..c5afcc03daf1 100644 --- a/src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts +++ b/src/pages/home/SpendOverTimeSection/useSpendOverTimeData.ts @@ -103,7 +103,7 @@ function useSpendOverTimeData() { allReportMetadata: undefined, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: reportAttributes ?? {}, + reportAttributesDerivedValue: reportAttributes, })[0], localeCompare, translate, From 40a23378297cac09c05c51916c35b287b82aeacb Mon Sep 17 00:00:00 2001 From: VH Date: Thu, 21 May 2026 18:03:46 +0700 Subject: [PATCH 4/6] Run prettier --- src/pages/ShareCodePage.tsx | 4 +++- tests/unit/Search/SearchUIUtilsTest.ts | 32 +++++++++++++------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/pages/ShareCodePage.tsx b/src/pages/ShareCodePage.tsx index 6d7783af20c2..e2b0649f7853 100644 --- a/src/pages/ShareCodePage.tsx +++ b/src/pages/ShareCodePage.tsx @@ -97,7 +97,9 @@ function ShareCodePage({report, policy, backTo}: ShareCodePageProps) { .join(' & '); } - return getParentNavigationSubtitle(report, policy, conciergeReportID, isParentReportArchived, reportAttributes).workspaceName ?? getChatRoomSubtitle(report, false, isReportArchived); + return ( + getParentNavigationSubtitle(report, policy, conciergeReportID, isParentReportArchived, reportAttributes).workspaceName ?? getChatRoomSubtitle(report, false, isReportArchived) + ); } return currentUserPersonalDetails.login; diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index c10ee59d814e..e619feca9979 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -2520,7 +2520,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0], ).toEqual(transactionsListItems); }); @@ -2618,7 +2618,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionReportGroupListItems); }); @@ -2992,7 +2992,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionMemberGroupListItems); }); @@ -3022,7 +3022,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionCardGroupListItems); }); @@ -3041,7 +3041,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionWithdrawalIDGroupListItems); }); @@ -3089,7 +3089,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionCategoryGroupListItems); }); @@ -3190,7 +3190,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionMonthGroupListItems); }); @@ -3330,7 +3330,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionYearGroupListItems); }); @@ -3768,7 +3768,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionQuarterGroupListItems); }); @@ -3889,7 +3889,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionWeekGroupListItems); }); @@ -4169,7 +4169,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionMerchantGroupListItems); }); @@ -4594,7 +4594,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0], ).toStrictEqual(transactionTagGroupListItems); }); @@ -5137,7 +5137,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0] as TransactionReportGroupListItemType[]; const item = sections.find((s) => s.keyForList === testReportID); @@ -5253,7 +5253,7 @@ describe('SearchUIUtils', () => { allReportMetadata: {}, conciergeReportID: undefined, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, })[0] as TransactionReportGroupListItemType[]; const item = sections.find((s) => s.keyForList === avatarTestReportID); @@ -5381,7 +5381,7 @@ describe('SearchUIUtils', () => { conciergeReportID: undefined, ...options, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, }) as [TransactionListItemType[], number, boolean]; } @@ -5641,7 +5641,7 @@ describe('SearchUIUtils', () => { conciergeReportID: undefined, ...options, convertToDisplayString, - reportAttributesDerivedValue: {}, + reportAttributesDerivedValue: {}, }) as [TransactionReportGroupListItemType[], number, boolean]; } From 40da6faf886265d1fa45142be055a44323735325 Mon Sep 17 00:00:00 2001 From: VH Date: Thu, 21 May 2026 19:49:49 +0700 Subject: [PATCH 5/6] fix(Search): add missing dependency to useMemo The reportAttributesDerivedValue variable is used inside the useMemo callback when calling getSections(). After adding it as a default parameter to getSections, React's exhaustive-deps rule correctly identified it as a missing dependency. This fixes the react-hooks/exhaustive-deps lint error. --- src/components/Search/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index abf4d1b48e3f..5ba75691af49 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -633,6 +633,7 @@ function Search({ allReportMetadata, conciergeReportID, convertToDisplayString, + reportAttributesDerivedValue, ]); const hasLoadedAllTransactions = useMemo(() => { From 29b4971ec7ef17e6100dc7351ad7362a0179ded6 Mon Sep 17 00:00:00 2001 From: VH Date: Thu, 21 May 2026 22:43:58 +0700 Subject: [PATCH 6/6] Trigger workflow