diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewHeader.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewHeader.tsx index e065af642c5d..cba808917a03 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewHeader.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/ReportPreviewHeader.tsx @@ -66,9 +66,6 @@ function ReportPreviewHeader() { [translate, numberOfRequests], ); - // A cancelled payment is a report level event and it isn't surfaced by the status badge, so we show it next to the expense count. - const supportingText = iouReport?.isCancelledIOU ? `${translate('iou.canceled')} ${CONST.DOT_SEPARATOR} ${expenseCount}` : expenseCount; - const reportStateNum = iouReport?.stateNum ?? action?.childStateNum; const reportStatusNum = iouReport?.statusNum ?? action?.childStatusNum; @@ -125,7 +122,7 @@ function ReportPreviewHeader() { tooltipText={reportStatusTooltip} /> )} - {!shouldShowAccessPlaceHolder && {supportingText}} + {!shouldShowAccessPlaceHolder && {expenseCount}} ) )} diff --git a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx index 09f4fb2d22db..836d3cf27123 100644 --- a/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx +++ b/src/components/ReportActionItem/TransactionPreview/TransactionPreviewContent.tsx @@ -2,7 +2,6 @@ import Button from '@components/Button'; import Icon from '@components/Icon'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import ReportActionAvatars from '@components/ReportActionAvatars'; -import {ReportPreviewDataContext} from '@components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewContext'; import ReportActionItemImages from '@components/ReportActionItem/ReportActionItemImages'; import UserInfoCellsWithArrow from '@components/Search/SearchList/ListItem/UserInfoCellsWithArrow'; import Text from '@components/Text'; @@ -26,6 +25,7 @@ import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {calculateAmount} from '@libs/IOUUtils'; import Parser from '@libs/Parser'; import {getLoginByAccountID} from '@libs/PersonalDetailsUtils'; +import {getCommaSeparatedTagNameWithSanitizedColons} from '@libs/PolicyUtils'; import {getThumbnailAndImageURIs} from '@libs/ReceiptUtils'; import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils'; import {isMarkAsCashActionForTransaction} from '@libs/ReportPrimaryActionUtils'; @@ -46,7 +46,7 @@ import {cardByIdSelector} from '@src/selectors/Card'; import {getStableReportSelector} from '@src/selectors/Report'; import truncate from 'lodash/truncate'; -import React, {useContext, useMemo} from 'react'; +import React, {useMemo} from 'react'; import {View} from 'react-native'; import Animated from 'react-native-reanimated'; @@ -77,7 +77,7 @@ function TransactionPreviewContent({ isReviewDuplicateTransactionPage = false, shouldHighlight = false, }: TransactionPreviewContentProps) { - const icons = useMemoizedLazyExpensifyIcons(['DotIndicator']); + const icons = useMemoizedLazyExpensifyIcons(['DotIndicator', 'Folder', 'Tag']); const theme = useTheme(); const styles = useThemeStyles(); const {translate, dateFnsLocale} = useLocalize(); @@ -88,12 +88,14 @@ function TransactionPreviewContent({ () => getTransactionDetails(transaction, undefined, policy, isParentPolicyExpenseChat) ?? {}, [transaction, policy, isParentPolicyExpenseChat], ); - const {amount, comment: requestComment, merchant, category, currency: requestCurrency} = transactionDetails; + const {amount, comment: requestComment, merchant, tag, category, currency: requestCurrency} = transactionDetails; const [originalTransaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transaction?.comment?.originalTransactionID)}`); const filteredViolations = filterReceiptViolations(violations); const firstViolation = filteredViolations.at(0); const cardID = firstViolation?.data?.cardID; - const [card] = useOnyx(ONYXKEYS.CARD_LIST, {selector: cardByIdSelector(String(cardID))}); + const [card] = useOnyx(ONYXKEYS.CARD_LIST, { + selector: cardByIdSelector(String(cardID)), + }); const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(report?.parentReportID)}`, {selector: getStableReportSelector}); const managerID = report?.managerID ?? reportPreviewAction?.childManagerAccountID ?? CONST.DEFAULT_NUMBER_ID; const ownerAccountID = report?.ownerAccountID ?? reportPreviewAction?.childOwnerAccountID ?? CONST.DEFAULT_NUMBER_ID; @@ -131,12 +133,7 @@ function TransactionPreviewContent({ [areThereDuplicates, transactionPreviewCommonArguments, isParentPolicyExpenseChat, currentUserEmail, currentUserAccountID, reportActions], ); - const {shouldShowRBR, shouldShowMerchant, shouldShowSplitShare, shouldShowCategory, shouldShowSkeleton, shouldShowDescription} = conditionals; - - // Raw useContext (not the useReportPreviewData slice hook, which throws when absent): a missing provider means this is a - // standalone preview with no report header to carry the status, so the preview has to report a cancelled payment itself. - const isInsideReportPreview = !!useContext(ReportPreviewDataContext); - const shouldShowCanceledStatus = !isInsideReportPreview; + const {shouldShowRBR, shouldShowMerchant, shouldShowSplitShare, shouldShowTag, shouldShowCategory, shouldShowSkeleton, shouldShowDescription} = conditionals; const isIOUActionType = isMoneyRequestAction(action); const canEdit = isIOUActionType && canEditMoneyRequest(action, transaction, isChatReportArchived, report, policy); @@ -167,26 +164,43 @@ function TransactionPreviewContent({ dateFnsLocale, ...transactionPreviewCommonArguments, shouldShowRBR, - shouldShowCanceledStatus, violationMessage, reportActions, + currentUserEmail, + currentUserAccountID, originalTransaction, convertToDisplayString, }), - [transactionPreviewCommonArguments, shouldShowRBR, shouldShowCanceledStatus, violationMessage, reportActions, originalTransaction, convertToDisplayString, dateFnsLocale], + [ + dateFnsLocale, + transactionPreviewCommonArguments, + shouldShowRBR, + violationMessage, + reportActions, + currentUserEmail, + currentUserAccountID, + originalTransaction, + convertToDisplayString, + ], ); const getTranslatedText = (item: TranslationPathOrText) => (item.translationPath ? translate(item.translationPath) : (item.text ?? '')); + const previewHeaderText = previewText.previewHeaderText.reduce((text, currentKey) => { + return `${text}${getTranslatedText(currentKey)}`; + }, ''); + const RBRMessage = getTranslatedText(previewText.RBRMessage); const displayAmountText = getTranslatedText(previewText.displayAmountText); const displayDeleteAmountText = getTranslatedText(previewText.displayDeleteAmountText); - const displayTypeText = getTranslatedText(previewText.previewTypeText); const isDeleted = action?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || transaction?.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + const shouldShowCategoryOrTag = shouldShowCategory || shouldShowTag; const shouldShowMerchantOrDescription = shouldShowDescription || shouldShowMerchant; const description = truncate(StringUtils.lineBreaksToSpaces(Parser.htmlToText(requestComment ?? '')), {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); - const requestMerchant = truncate(merchant, {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); + const requestMerchant = truncate(merchant, { + length: CONST.REQUEST_PREVIEW.MAX_LENGTH, + }); const isApproved = isReportApproved({report}); const pendingAction = action?.pendingAction; const isIOUSettled = !pendingAction && isSettled(report); @@ -195,10 +209,6 @@ function TransactionPreviewContent({ const displayAmount = isDeleted ? displayDeleteAmountText : displayAmountText; const receiptImages = [{...getThumbnailAndImageURIs(transaction), transaction}]; const merchantOrDescription = shouldShowMerchant ? requestMerchant : description || ''; - const previewSupportingText = [previewText.previewDateText, shouldShowCategory && category ? {text: getDecodedLeafCategoryName(category)} : undefined, ...previewText.previewStatusText] - .filter((item): item is TranslationPathOrText => !!item) - .map(getTranslatedText) - .join(` ${CONST.DOT_SEPARATOR} `); const participantAccountIDs = isMoneyRequestAction(action) && isBillSplit ? (getOriginalMessage(action)?.participantAccountIDs ?? []) : [managerID, ownerAccountID]; const isCardTransaction = isCardTransactionUtils(transaction); @@ -268,6 +278,10 @@ function TransactionPreviewContent({ getCurrencyDecimals, ]); + const shouldWrapDisplayAmount = !(isBillSplit || shouldShowMerchantOrDescription || isTransactionScanning); + const previewTextViewGap = (shouldShowCategoryOrTag || !shouldWrapDisplayAmount) && styles.gap2; + const previewTextMargin = shouldShowIOUHeader && shouldShowMerchantOrDescription && !isBillSplit && !shouldShowCategoryOrTag && styles.mbn1; + const animatedHighlightStyle = useAnimatedHighlightStyle({ shouldHighlight, highlightColor: theme.messageHighlightBG, @@ -310,63 +324,121 @@ function TransactionPreviewContent({ participantToDisplayName={to.displayName ?? to.login ?? translate('common.hidden')} participantTo={to} avatarSize={CONST.AVATAR_SIZE.XXX_SMALL} - infoCellsTextStyle={{...styles.textMicroBold, lineHeight: 14}} + infoCellsTextStyle={{ + ...styles.textMicroBold, + lineHeight: 14, + }} infoCellsAvatarStyle={styles.pr1} style={[styles.flex1, styles.dFlex, styles.alignItemsCenter, styles.gap2, styles.flexRow]} /> )} - - - - {shouldShowMerchantOrDescription && ( - - {merchantOrDescription} + + + {previewHeaderText} + {isBillSplit && ( + + + + )} + {shouldWrapDisplayAmount && ( + + {displayAmount} + + )} + + + + + {shouldShowMerchantOrDescription && ( + + {merchantOrDescription} + + )} + {!shouldWrapDisplayAmount && ( + + {displayAmount} + + )} + + + + {!!splitShare && ( + + {translate('iou.yourSplit', convertToDisplayString(splitShare, requestCurrency))} )} - {isBillSplit && ( - - + + {shouldShowCategoryOrTag && ( + + {shouldShowCategory && ( + + + + {getDecodedLeafCategoryName(category ?? '')} + + + )} + {shouldShowTag && !!tag && ( + + + + {getCommaSeparatedTagNameWithSanitizedColons(tag)} + )} - - - {displayAmount} - - - - - {previewSupportingText} - - - {displayTypeText} - - - {!!splitShare && ( - - - {translate('iou.yourSplit', convertToDisplayString(splitShare, requestCurrency))} - )} diff --git a/src/libs/TransactionPreviewUtils.ts b/src/libs/TransactionPreviewUtils.ts index f896c43a65ad..b1f58aff8230 100644 --- a/src/libs/TransactionPreviewUtils.ts +++ b/src/libs/TransactionPreviewUtils.ts @@ -37,6 +37,7 @@ import { isCreatedMissing, isDistanceRequest, isFetchingWaypointsFromServer, + isManagedCardTransaction, isMerchantMissing, isOnHold, isPending, @@ -124,6 +125,10 @@ type TranslationPathOrText = { text?: string; }; +const dotSeparator: TranslationPathOrText = { + text: ` ${CONST.DOT_SEPARATOR} `, +}; + /** * Normalize the last four digits to always return 4 characters. * If the number is shorter than 4 digits, it will be padded with X's. @@ -196,6 +201,7 @@ function getUniqueActionErrorsForTransaction(reportActions: OnyxTypes.ReportActi function getTransactionPreviewTextAndTranslationPaths({ iouReport, + iouReportOwnerLogin, policy, transaction, action, @@ -203,14 +209,16 @@ function getTransactionPreviewTextAndTranslationPaths({ transactionDetails, isBillSplit, shouldShowRBR, - shouldShowCanceledStatus, violationMessage, reportActions, + currentUserEmail, + currentUserAccountID, originalTransaction, convertToDisplayString, dateFnsLocale, }: { iouReport: OnyxEntry; + iouReportOwnerLogin: string | undefined; policy: OnyxEntry; transaction: OnyxEntry; action: OnyxEntry; @@ -218,18 +226,20 @@ function getTransactionPreviewTextAndTranslationPaths({ transactionDetails: Partial; isBillSplit: boolean; shouldShowRBR: boolean; - /** Whether a cancelled payment has to be reported on this line, because the enclosing surface doesn't show it anywhere else */ - shouldShowCanceledStatus: boolean; violationMessage?: string; reportActions?: OnyxTypes.ReportActions; + currentUserEmail: string; + currentUserAccountID: number; originalTransaction?: OnyxEntry; convertToDisplayString: CurrencyListActionsContextType['convertToDisplayString']; dateFnsLocale: DateFnsLocale | undefined; }) { const isFetchingWaypoints = isFetchingWaypointsFromServer(transaction); const isTransactionOnHold = isOnHold(transaction); + const isTransactionMadeWithCard = isManagedCardTransaction(transaction); const isMoneyRequestSettled = isSettled(iouReport?.reportID); const isSettlementOrApprovalPartial = !!iouReport?.pendingFields?.partial; + const isPartialHold = isSettlementOrApprovalPartial && isTransactionOnHold; // We don't use isOnHold because it's true for duplicated transaction too and we only want to show hold message if the transaction is truly on hold const shouldShowHoldMessage = !(isMoneyRequestSettled && !isSettlementOrApprovalPartial) && !!transaction?.comment?.hold; @@ -237,6 +247,8 @@ function getTransactionPreviewTextAndTranslationPaths({ const hasFieldErrors = hasMissingSmartscanFields(transaction, iouReport); const isGroupPolicy = isGroupPolicyUtil(policy); + const hasViolationsOfTypeNotice = + hasNoticeTypeViolation(transaction, violations, currentUserEmail ?? '', currentUserAccountID, iouReport, iouReportOwnerLogin, policy, true) && isGroupPolicy; const hasActionWithErrors = hasActionWithErrorsForTransaction(iouReport?.reportID, transaction, reportActions); const {amount: requestAmount, currency: requestCurrency} = transactionDetails; @@ -297,21 +309,26 @@ function getTransactionPreviewTextAndTranslationPaths({ } } - let previewTypeText: TranslationPathOrText = {translationPath: getExpenseTypeTranslationKey(getTransactionType(transaction))}; + let previewHeaderText: TranslationPathOrText[] = [ + { + translationPath: getExpenseTypeTranslationKey(getTransactionType(transaction)), + }, + ]; if (isTransactionScanning) { - previewTypeText = {translationPath: 'common.receipt'}; + previewHeaderText = [{translationPath: 'common.receipt'}]; } else if (isBillSplit) { - previewTypeText = {translationPath: 'iou.split'}; + previewHeaderText = [{translationPath: 'iou.split'}]; } if (RBRMessage?.text === CONST.ERROR.BANK_ACCOUNT_SAME_DEPOSIT_AND_WITHDRAWAL_ERROR) { - RBRMessage = {translationPath: 'bankAccount.error.sameDepositAndWithdrawalAccount'}; + RBRMessage = { + translationPath: 'bankAccount.error.sameDepositAndWithdrawalAccount', + }; } RBRMessage ??= {text: ''}; - let previewDateText: TranslationPathOrText | undefined; if (!isCreatedMissing(transaction)) { const created = getFormattedCreated(transaction); const date = DateUtils.formatWithUTCTimeZone( @@ -319,24 +336,42 @@ function getTransactionPreviewTextAndTranslationPaths({ DateUtils.doesDateBelongToAPastYear(created) ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT, dateFnsLocale, ); - previewDateText = {text: date}; + previewHeaderText.unshift({text: date}, dotSeparator); } - // Paid, Approved and Review required are intentionally omitted here because the report status badge and the violation - // row already show them, so repeating them on this line is noise. Canceled is the exception: it can't be derived from - // stateNum/statusNum, so surfaces without their own report status badge have to report it here. - const previewStatusText: TranslationPathOrText[] = []; - if (isPending(transaction)) { - previewStatusText.push({translationPath: 'iou.pending'}); + previewHeaderText.push(dotSeparator, {translationPath: 'iou.pending'}); + } + + if (hasPendingRTERViolation(violations)) { + previewHeaderText.push(dotSeparator, { + translationPath: 'iou.pendingMatch', + }); + } + + let isPreviewHeaderTextComplete = false; + + if (isMoneyRequestSettled && !iouReport?.isCancelledIOU && !isPartialHold && !hasActionWithErrors) { + previewHeaderText.push(dotSeparator, { + translationPath: isTransactionMadeWithCard ? 'common.done' : 'iou.settledExpensify', + }); + isPreviewHeaderTextComplete = true; } - if (shouldShowCanceledStatus && iouReport?.isCancelledIOU) { - previewStatusText.push({translationPath: 'iou.canceled'}); - } else if (hasPendingRTERViolation(violations)) { - previewStatusText.push({translationPath: 'iou.pendingMatch'}); - } else if (shouldShowHoldMessage) { - previewStatusText.push({translationPath: 'violations.hold'}); + if (!isPreviewHeaderTextComplete) { + if (hasViolationsOfTypeNotice && transaction && !isReportApproved({report: iouReport}) && !isSettled(iouReport?.reportID)) { + previewHeaderText.push(dotSeparator, { + translationPath: 'violations.reviewRequired', + }); + } else if (isExpenseReport(iouReport) && isGroupPolicyUtil(policy) && isReportApproved({report: iouReport}) && !isSettled(iouReport?.reportID) && !isPartialHold) { + previewHeaderText.push(dotSeparator, {translationPath: 'iou.approved'}); + } else if (iouReport?.isCancelledIOU) { + previewHeaderText.push(dotSeparator, {translationPath: 'iou.canceled'}); + } else if (shouldShowHoldMessage) { + previewHeaderText.push(dotSeparator, { + translationPath: 'violations.hold', + }); + } } const amount = isBillSplit ? getAmount(originalTransaction ?? transaction) : requestAmount; @@ -346,15 +381,15 @@ function getTransactionPreviewTextAndTranslationPaths({ } const iouOriginalMessage: OnyxEntry = isMoneyRequestAction(action) ? (getOriginalMessage(action) ?? undefined) : undefined; - const displayDeleteAmountText: TranslationPathOrText = {text: convertToDisplayString(iouOriginalMessage?.amount, iouOriginalMessage?.currency)}; + const displayDeleteAmountText: TranslationPathOrText = { + text: convertToDisplayString(iouOriginalMessage?.amount, iouOriginalMessage?.currency), + }; return { RBRMessage, displayAmountText, displayDeleteAmountText, - previewDateText, - previewStatusText, - previewTypeText, + previewHeaderText, }; } @@ -389,8 +424,12 @@ function createTransactionPreviewConditionals({ }) { const {amount: requestAmount, comment: requestComment, merchant, tag, category} = transactionDetails; - const requestMerchant = truncate(merchant, {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); - const description = truncate(StringUtils.lineBreaksToSpaces(requestComment), {length: CONST.REQUEST_PREVIEW.MAX_LENGTH}); + const requestMerchant = truncate(merchant, { + length: CONST.REQUEST_PREVIEW.MAX_LENGTH, + }); + const description = truncate(StringUtils.lineBreaksToSpaces(requestComment), { + length: CONST.REQUEST_PREVIEW.MAX_LENGTH, + }); const isMoneyRequestSettled = isSettled(iouReport?.reportID); const isApproved = isReportApproved({report: iouReport}); diff --git a/tests/ui/MoneyRequestReportPreview.test.tsx b/tests/ui/MoneyRequestReportPreview.test.tsx index 443126b30bb0..9ce6312393e7 100644 --- a/tests/ui/MoneyRequestReportPreview.test.tsx +++ b/tests/ui/MoneyRequestReportPreview.test.tsx @@ -176,15 +176,14 @@ const renderPage = ({isWhisper = false, isHovered = false}: Partial { +const getTransactionDisplayAmountAndHeaderText = (transaction: Transaction) => { const created = getFormattedCreated(transaction); const date = DateUtils.formatWithUTCTimeZone(created, DateUtils.doesDateBelongToAPastYear(created) ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT, undefined); const isTransactionMadeWithCard = isManagedCardTransaction(transaction); - // The date leads the supporting line, which can also carry the category and the report status. - const transactionSupportingText = new RegExp(`^${date}`); - const transactionTypeText = isTransactionMadeWithCard ? TestHelper.translateLocal('iou.card') : TestHelper.translateLocal('iou.cash'); + const cashOrCard = isTransactionMadeWithCard ? TestHelper.translateLocal('iou.card') : TestHelper.translateLocal('iou.cash'); + const transactionHeaderText = `${date} ${CONST.DOT_SEPARATOR} ${cashOrCard}`; const transactionDisplayAmount = TestHelper.convertToDisplayString(-transaction.amount, transaction.currency); - return {transactionSupportingText, transactionTypeText, transactionDisplayAmount}; + return {transactionHeaderText, transactionDisplayAmount}; }; const setCurrentWidth = () => { @@ -274,11 +273,10 @@ describe('MoneyRequestReportPreview', () => { expect(screen.getByText(getReportName(mockIOUReport))).toBeOnTheScreen(); for (const transaction of arrayOfTransactions) { - const {transactionDisplayAmount, transactionSupportingText, transactionTypeText} = getTransactionDisplayAmountAndMetadataText(transaction); + const {transactionDisplayAmount, transactionHeaderText} = getTransactionDisplayAmountAndHeaderText(transaction); expect(screen.getAllByText(transactionDisplayAmount).length).toBeGreaterThan(0); - expect(screen.getAllByText(transactionSupportingText)).toHaveLength(arrayOfTransactions.length); - expect(screen.getAllByText(transactionTypeText)).toHaveLength(arrayOfTransactions.length); + expect(screen.getAllByText(transactionHeaderText)).toHaveLength(arrayOfTransactions.length); expect(screen.getAllByText(transaction.merchant)).toHaveLength(arrayOfTransactions.length); } }); diff --git a/tests/unit/TransactionPreviewUtils.test.ts b/tests/unit/TransactionPreviewUtils.test.ts index 87620b61428c..4b835519ac56 100644 --- a/tests/unit/TransactionPreviewUtils.test.ts +++ b/tests/unit/TransactionPreviewUtils.test.ts @@ -53,7 +53,6 @@ const basicProps = { transactionDetails: {}, isBillSplit: false, shouldShowRBR: false, - shouldShowCanceledStatus: false, isReportAPolicyExpenseChat: false, areThereDuplicates: false, currentUserEmail: '', @@ -117,7 +116,7 @@ describe('TransactionPreviewUtils', () => { const functionArgs = {...basicProps, iouReport: undefined, transaction: undefined, originalTransaction: undefined}; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); expect(result.RBRMessage.text).toEqual(''); - expect(result.previewTypeText).toEqual({translationPath: 'iou.cash'}); + expect(result.previewHeaderText).toContainEqual({translationPath: 'iou.cash'}); expect(result.displayAmountText.text).toEqual('$0.00'); }); @@ -152,7 +151,7 @@ describe('TransactionPreviewUtils', () => { expect(result.RBRMessage.translationPath).toEqual('iou.missingAmount'); }); - it('should display cash or card as the preview type', () => { + it('should display showCashOrCard in previewHeaderText', () => { const functionArgsWithCardTransaction = { ...basicProps, transaction: { @@ -164,14 +163,14 @@ describe('TransactionPreviewUtils', () => { const cardTransaction = getTransactionPreviewTextAndTranslationPaths(functionArgsWithCardTransaction); const cashTransaction = getTransactionPreviewTextAndTranslationPaths({...basicProps}); - expect(cardTransaction.previewTypeText).toEqual({translationPath: 'common.card'}); - expect(cashTransaction.previewTypeText).toEqual({translationPath: 'iou.cash'}); + expect(cardTransaction.previewHeaderText).toEqual(expect.arrayContaining([{translationPath: 'common.card'}])); + expect(cashTransaction.previewHeaderText).toEqual(expect.arrayContaining([{translationPath: 'iou.cash'}])); }); it('displays appropriate header text if the transaction is bill split', () => { const functionArgs = {...basicProps, isBillSplit: true, originalTransaction: undefined}; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); - expect(result.previewTypeText).toEqual({translationPath: 'iou.split'}); + expect(result.previewHeaderText).toEqual(expect.arrayContaining([{translationPath: 'iou.split'}])); }); it('displays description when receipt is being scanned', () => { @@ -182,15 +181,13 @@ describe('TransactionPreviewUtils', () => { merchant: 'Expense', }; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); - expect(result.previewTypeText).toEqual({translationPath: 'common.receipt'}); + expect(result.previewHeaderText).toEqual(expect.arrayContaining([{translationPath: 'common.receipt'}])); }); it('should apply correct text when transaction is pending and not a bill split', () => { const functionArgs = {...basicProps, transaction: {...basicProps.transaction, status: CONST.TRANSACTION.STATUS.PENDING}, originalTransaction: undefined}; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); - // Pending is a transaction status, so it belongs to the supporting line and must not replace the expense type. - expect(result.previewStatusText).toContainEqual({translationPath: 'iou.pending'}); - expect(result.previewTypeText).toEqual({translationPath: 'iou.cash'}); + expect(result.previewHeaderText).toEqual(expect.arrayContaining([{translationPath: 'iou.pending'}])); }); it('handles currency and amount display during scanning correctly', () => { @@ -231,19 +228,13 @@ describe('TransactionPreviewUtils', () => { expect(result.displayAmountText.text).toEqual(convertAmountToDisplayString(modifiedAmount, currency)); }); - it('does not show the canceled status inside a report preview, because the preview header already shows it', () => { - const functionArgs = {...basicProps, iouReport: {...basicProps.iouReport, isCancelledIOU: true}, originalTransaction: undefined, shouldShowCanceledStatus: false}; + it('shows approved message when the iouReport is canceled', () => { + const functionArgs = {...basicProps, iouReport: {...basicProps.iouReport, isCancelledIOU: true}, originalTransaction: undefined}; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); - expect(result.previewStatusText).toEqual([]); + expect(result.previewHeaderText).toContainEqual({translationPath: 'iou.canceled'}); }); - it('shows the canceled status in a standalone preview, because nothing else on that surface reports it', () => { - const functionArgs = {...basicProps, iouReport: {...basicProps.iouReport, isCancelledIOU: true}, originalTransaction: undefined, shouldShowCanceledStatus: true}; - const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); - expect(result.previewStatusText).toContainEqual({translationPath: 'iou.canceled'}); - }); - - it('does not show the approved status when the report is approved, because it is redundant with the report status badge', () => { + it('should include "Approved" in the preview when the report is approved, regardless of whether RBR is shown', () => { const functionArgs = { ...basicProps, iouReport: { @@ -258,7 +249,7 @@ describe('TransactionPreviewUtils', () => { }; const result = getTransactionPreviewTextAndTranslationPaths(functionArgs); - expect(result.previewStatusText).toEqual([]); + expect(result.previewHeaderText).toContainEqual({translationPath: 'iou.approved'}); }); it('should display the correct amount for a bill split transaction', () => {