diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewHeader.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewHeader.tsx index 6dfc6c994633..1776bc0e0644 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewHeader.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewHeader.tsx @@ -11,6 +11,7 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import Parser from '@libs/Parser'; import {getReportStatusColorStyle, getReportStatusTooltipTranslation, getReportStatusTranslation} from '@libs/ReportUtils'; import variables from '@styles/variables'; @@ -48,6 +49,7 @@ function ReportPreviewHeader() { const selectReportName = useCallback((attributes: OnyxEntry) => reportNameSelector(attributes, iouReportID), [iouReportID]); const [derivedReportName] = useOnyx(ONYXKEYS.DERIVED.REPORT_ATTRIBUTES, {selector: selectReportName}); const reportName = derivedReportName ?? iouReport?.reportName ?? ''; + const formattedReportName = useMemo(() => Parser.htmlToText(reportName || (action.childReportName ?? '')), [reportName, action.childReportName]); /* Show subtitle if at least one of the expenses is not being smart scanned, and either: @@ -102,7 +104,7 @@ function ReportPreviewHeader() { style={[styles.headerText]} testID="MoneyRequestReportPreview-reportName" > - {reportName || action.childReportName} + {formattedReportName} diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 8a5919ae4c56..8b507eb7a723 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -2953,6 +2953,8 @@ function getReportSections({ if (isIOUReport) { reportIDToTransactions[reportKey].reportName = getIOUReportName(translate, convertToDisplayString, data, reportIDToTransactions[reportKey]); } + + reportIDToTransactions[reportKey].reportName = StringUtils.lineBreaksToSpaces(Parser.htmlToText(reportIDToTransactions[reportKey].reportName ?? '')); } } else if (isTransactionEntry(key)) { const transactionItem = {...data[key]}; diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 2447829c1282..fb0cae2d2686 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -2798,6 +2798,34 @@ describe('SearchUIUtils', () => { expect(result.find((item) => item.reportID === reportID2)?.pendingAction).toBe(CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); }); + it('should decode HTML entities in the expense report name', () => { + const localSearchResults: OnyxTypes.SearchResults['data'] = { + ...searchResults.data, + [`report_${reportID2}`]: { + ...searchResults.data[`report_${reportID2}`], + reportName: 'A & B 'C'', + }, + }; + + const result = getSectionsByType( + SearchUIUtils.getSections({ + type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT, + data: localSearchResults, + currentAccountID: 2074551, + currentUserEmail: '', + translate: translateLocal, + formatPhoneNumber, + bankAccountList: {}, + conciergeReportID: undefined, + convertToDisplayString, + reportAttributesDerivedValue: {}, + }), + SearchUIUtils.isTransactionReportGroupListItemType, + )[0]; + + expect(result.find((item) => item.reportID === reportID2)?.reportName).toBe("A & B 'C'"); + }); + it('should use custom avatar from onyxPersonalDetailsList when search API personalDetailsList is absent', () => { // Reproduce the bug: the search API response does not include the report owner in // personalDetailsList (data.personalDetailsList is empty/undefined), so without the