diff --git a/src/components/DisplayNames/index.native.tsx b/src/components/DisplayNames/index.native.tsx index ab0e659cdb6f..cae91c80272e 100644 --- a/src/components/DisplayNames/index.native.tsx +++ b/src/components/DisplayNames/index.native.tsx @@ -8,11 +8,8 @@ import TextWithEmojiFragment from '@pages/home/report/comment/TextWithEmojiFragm import type DisplayNamesProps from './types'; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. -function DisplayNames({accessibilityLabel, fullTitle, textStyles = [], numberOfLines = 1, renderAdditionalText, forwardedFSClass, testID, shouldParseHtml = false}: DisplayNamesProps) { +function DisplayNames({accessibilityLabel, fullTitle, textStyles = [], numberOfLines = 1, renderAdditionalText, forwardedFSClass, testID}: DisplayNamesProps) { const {translate} = useLocalize(); - const title = shouldParseHtml - ? StringUtils.lineBreaksToSpaces(Parser.htmlToText(fullTitle)) || translate('common.hidden') - : StringUtils.lineBreaksToSpaces(fullTitle) || translate('common.hidden'); const titleContainsTextAndCustomEmoji = useMemo(() => containsCustomEmoji(fullTitle) && !containsOnlyCustomEmoji(fullTitle), [fullTitle]); return ( {titleContainsTextAndCustomEmoji ? ( ) : ( - title + StringUtils.lineBreaksToSpaces(Parser.htmlToText(fullTitle)) || translate('common.hidden') )} {renderAdditionalText?.()} diff --git a/src/components/DisplayNames/index.tsx b/src/components/DisplayNames/index.tsx index 5e1a82104501..5158747e4c25 100644 --- a/src/components/DisplayNames/index.tsx +++ b/src/components/DisplayNames/index.tsx @@ -16,12 +16,9 @@ function DisplayNames({ displayNamesWithTooltips, renderAdditionalText, forwardedFSClass, - shouldParseHtml = false, }: DisplayNamesProps) { const {translate} = useLocalize(); - const title = shouldParseHtml - ? StringUtils.lineBreaksToSpaces(Parser.htmlToText(fullTitle)) || translate('common.hidden') - : StringUtils.lineBreaksToSpaces(fullTitle) || translate('common.hidden'); + const title = StringUtils.lineBreaksToSpaces(Parser.htmlToText(fullTitle)) || translate('common.hidden'); if (!tooltipEnabled) { return ( diff --git a/src/components/DisplayNames/types.ts b/src/components/DisplayNames/types.ts index a1c3e1d4960b..1b9a69510da3 100644 --- a/src/components/DisplayNames/types.ts +++ b/src/components/DisplayNames/types.ts @@ -49,9 +49,6 @@ type DisplayNamesProps = ForwardedFSClassProps & { /** TestID indicating order */ testID?: number; - - /** Whether to parse HTML in the fullTitle */ - shouldParseHtml?: boolean; }; export default DisplayNamesProps; diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 37b9a27dc267..0b1712e3a07a 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5682,7 +5682,7 @@ function getReportName( } if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION)) { - return Parser.htmlToText(getUnreportedTransactionMessage()); + return getUnreportedTransactionMessage(); } if (isActionOfType(parentReportAction, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_MAX_EXPENSE_AMOUNT_NO_RECEIPT)) { diff --git a/src/pages/ReportDetailsPage.tsx b/src/pages/ReportDetailsPage.tsx index 3900c1deeb18..8b645d1ace3b 100644 --- a/src/pages/ReportDetailsPage.tsx +++ b/src/pages/ReportDetailsPage.tsx @@ -10,6 +10,7 @@ import ConfirmModal from '@components/ConfirmModal'; import DisplayNames from '@components/DisplayNames'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import MentionReportContext from '@components/HTMLEngineProvider/HTMLRenderers/MentionReportRenderer/MentionReportContext'; +import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -26,7 +27,6 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useDeleteTransactions from '@hooks/useDeleteTransactions'; import useDuplicateTransactionsAndViolations from '@hooks/useDuplicateTransactionsAndViolations'; import useGetIOUReportFromReportAction from '@hooks/useGetIOUReportFromReportAction'; -import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; @@ -41,6 +41,7 @@ import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {ReportDetailsNavigatorParamList} from '@libs/Navigation/types'; import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils'; +import Parser from '@libs/Parser'; import Permissions from '@libs/Permissions'; import {isPolicyAdmin as isPolicyAdminUtil, isPolicyEmployee as isPolicyEmployeeUtil, shouldShowPolicy} from '@libs/PolicyUtils'; import {getOneTransactionThreadReportID, getOriginalMessage, getTrackExpenseActionableWhisper, isDeletedAction, isMoneyRequestAction, isTrackExpenseAction} from '@libs/ReportActionsUtils'; @@ -148,7 +149,6 @@ const CASES = { type CaseID = ValueOf; function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetailsPageProps) { - const Expensicons = useMemoizedLazyExpensifyIcons(['Bug', 'Building', 'Camera', 'Checkmark', 'Exit', 'Folder', 'Gear', 'Pencil', 'Send', 'Trashcan', 'UserPlus', 'Users'] as const); const {translate, localeCompare} = useLocalize(); const {isOffline} = useNetwork(); const {isRestrictedToPreferredPolicy, preferredPolicyID} = usePreferredPolicy(); @@ -332,7 +332,8 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail const shouldShowLeaveButton = canLeaveChat(report, policy, !!reportNameValuePairs?.private_isArchived); const shouldShowGoToWorkspace = shouldShowPolicy(policy, false, currentUserPersonalDetails?.email) && !policy?.isJoinRequestPending; - const reportName = getReportName(report, undefined, undefined, undefined, undefined, reportAttributes); + + const reportName = Parser.htmlToText(getReportName(report, undefined, undefined, undefined, undefined, reportAttributes)); const additionalRoomDetails = (isPolicyExpenseChat && !!report?.isOwnPolicyExpenseChat) || isExpenseReportUtil(report) || isPolicyExpenseChat || isInvoiceRoom @@ -577,7 +578,6 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail isRestrictedToPreferredPolicy, preferredPolicyID, introSelected, - Expensicons, ]); const displayNamesWithTooltips = useMemo(() => { @@ -660,7 +660,6 @@ function ReportDetailsPage({policy, report, route, reportMetadata}: ReportDetail policy, participants, moneyRequestReport?.reportID, - Expensicons, ]); const canJoin = canJoinChat(report, parentReportAction, policy, !!reportNameValuePairs?.private_isArchived); diff --git a/tests/unit/DisplayNamesShouldParseHTMLTest.tsx b/tests/unit/DisplayNamesShouldParseHTMLTest.tsx deleted file mode 100644 index 95c99a3ce4c2..000000000000 --- a/tests/unit/DisplayNamesShouldParseHTMLTest.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import {render, screen} from '@testing-library/react-native'; -import React from 'react'; -import DisplayNames from '@components/DisplayNames'; - -jest.mock('@hooks/useLocalize', () => ({ - // eslint-disable-next-line @typescript-eslint/naming-convention - __esModule: true, - default: () => ({ - translate: jest.fn((key: string): string => { - if (key === 'common.hidden') { - return 'hidden'; - } - return key; - }), - }), -})); - -jest.mock('@libs/Parser', () => ({ - // eslint-disable-next-line @typescript-eslint/naming-convention - __esModule: true, - default: { - htmlToText: jest.fn((html: string) => { - // Simulate stripTag behavior: remove anything that looks like HTML tags - return html.replaceAll(/(<([^>]+)>)/gi, ''); - }), - }, -})); - -jest.mock('@libs/StringUtils', () => ({ - // eslint-disable-next-line @typescript-eslint/naming-convention - __esModule: true, - default: { - lineBreaksToSpaces: jest.fn((text: string) => text), - }, -})); - -describe('DisplayNames - shouldParseHtml prop', () => { - const testTitle = '< >'; - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should NOT parse HTML by default (shouldParseHtml defaults to false)', () => { - render( - , - ); - - // With shouldParseHtml = false (default), "< >" should be preserved - expect(screen.getByText('< >')).toBeTruthy(); - }); - - it('should parse HTML when shouldParseHtml is explicitly set to true', () => { - const htmlTitle = 'Bold Text'; - render( - , - ); - - // With shouldParseHtml = true, HTML tags should be stripped - expect(screen.getByText('Bold Text')).toBeTruthy(); - expect(screen.queryByText('Bold Text')).toBeNull(); - }); - - it('should preserve special characters when shouldParseHtml is false', () => { - const specialCharsTitle = '< > & " \' test'; - render( - , - ); - - // Special characters should be preserved when not parsing HTML - expect(screen.getByText(specialCharsTitle)).toBeTruthy(); - }); - - it('should show "hidden" when title is empty and HTML parsing is disabled', () => { - render( - , - ); - - expect(screen.getByText('hidden')).toBeTruthy(); - }); - - it('should show "hidden" when title becomes empty after HTML parsing', () => { - const onlyTagsTitle = '
'; - render( - , - ); - - // After parsing, only tags remain which get stripped to empty string - expect(screen.getByText('hidden')).toBeTruthy(); - }); -}); diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 1acf4cc5fbc4..82607086fef6 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -1125,33 +1125,6 @@ describe('ReportUtils', () => { ); }); }); - - describe('Unreported transaction thread', () => { - test('HTML is stripped from unreported transaction message', () => { - const transactionThread = { - ...LHNTestUtils.getFakeReport(), - type: CONST.REPORT.TYPE.CHAT, - reportID: '123', - parentReportID: '456', - }; - - const unreportedTransactionAction = { - actionName: CONST.REPORT.ACTIONS.TYPE.UNREPORTED_TRANSACTION, - originalMessage: { - fromReportID: '789', - }, - } as ReportAction; - - const reportName = getReportName(transactionThread, undefined, unreportedTransactionAction); - - // Should NOT contain HTML tags - expect(reportName).not.toContain(''); - // Should contain the text content - expect(reportName).toContain('moved this expense'); - expect(reportName).toContain('personal space'); - }); - }); }); // Need to merge the same tests