diff --git a/Mobile-Expensify b/Mobile-Expensify index d9bcd7d4013c..d077aa091789 160000 --- a/Mobile-Expensify +++ b/Mobile-Expensify @@ -1 +1 @@ -Subproject commit d9bcd7d4013cba681aedca236b36a5cd5729d80f +Subproject commit d077aa091789ff299f2f613d7daa325c0be38952 diff --git a/android/app/build.gradle b/android/app/build.gradle index 8a4d57cc5eaa..5c35aa893286 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -114,8 +114,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009018919 - versionName "9.1.89-19" + versionCode 1009018920 + versionName "9.1.89-20" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist index 2e77d1cc13bd..e1c5c542b2e4 100644 --- a/ios/NewExpensify/Info.plist +++ b/ios/NewExpensify/Info.plist @@ -44,7 +44,7 @@ CFBundleVersion - 9.1.89.19 + 9.1.89.20 FullStory OrgId diff --git a/ios/NotificationServiceExtension/Info.plist b/ios/NotificationServiceExtension/Info.plist index 94f4d8aafc88..93b4d2d0b177 100644 --- a/ios/NotificationServiceExtension/Info.plist +++ b/ios/NotificationServiceExtension/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.1.89 CFBundleVersion - 9.1.89.19 + 9.1.89.20 NSExtension NSExtensionPointIdentifier diff --git a/ios/ShareViewController/Info.plist b/ios/ShareViewController/Info.plist index 36ca0ebd4a0f..0a00ae2ad90c 100644 --- a/ios/ShareViewController/Info.plist +++ b/ios/ShareViewController/Info.plist @@ -13,7 +13,7 @@ CFBundleShortVersionString 9.1.89 CFBundleVersion - 9.1.89.19 + 9.1.89.20 NSExtension NSExtensionAttributes diff --git a/package-lock.json b/package-lock.json index d7061619d382..75156cd1b199 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "new.expensify", - "version": "9.1.89-19", + "version": "9.1.89-20", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "new.expensify", - "version": "9.1.89-19", + "version": "9.1.89-20", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 8f7d466a3305..30aa8e14b681 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "new.expensify", - "version": "9.1.89-19", + "version": "9.1.89-20", "author": "Expensify, Inc.", "homepage": "https://new.expensify.com", "description": "New Expensify is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.", diff --git a/src/CONST/index.ts b/src/CONST/index.ts index e2f9663f7843..2d64091345ca 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -1289,7 +1289,6 @@ const CONST = { RECEIPT: 'receipt', DATE: 'date', MERCHANT: 'merchant', - DESCRIPTION: 'description', FROM: 'from', TO: 'to', CATEGORY: 'category', @@ -6471,9 +6470,6 @@ const CONST = { UNAPPROVED_CASH: 'unapprovedCash', UNAPPROVED_CARD: 'unapprovedCard', }, - ANIMATION: { - FADE_DURATION: 200, - }, }, EXPENSE: { diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx index c83f12cb4ac5..e588dde8b829 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx @@ -13,6 +13,25 @@ type ColumnConfig = { isColumnSortable?: boolean; }; +const shouldShowColumnConfig: Record boolean> = { + [CONST.SEARCH.TABLE_COLUMNS.RECEIPT]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.TYPE]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.DATE]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.MERCHANT]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.CATEGORY]: (isIOUReport) => !isIOUReport, + [CONST.SEARCH.TABLE_COLUMNS.TAG]: (isIOUReport) => !isIOUReport, + [CONST.REPORT.TRANSACTION_LIST.COLUMNS.COMMENTS]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.IN]: () => false, + [CONST.SEARCH.TABLE_COLUMNS.FROM]: () => false, + [CONST.SEARCH.TABLE_COLUMNS.TO]: () => false, + [CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION]: () => false, + [CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT]: () => false, + [CONST.SEARCH.TABLE_COLUMNS.ACTION]: () => false, + [CONST.SEARCH.TABLE_COLUMNS.TITLE]: () => false, + [CONST.SEARCH.TABLE_COLUMNS.ASSIGNEE]: () => false, +}; + const columnConfig: ColumnConfig[] = [ { columnName: CONST.SEARCH.TABLE_COLUMNS.RECEIPT, @@ -32,10 +51,6 @@ const columnConfig: ColumnConfig[] = [ columnName: CONST.SEARCH.TABLE_COLUMNS.MERCHANT, translationKey: 'common.merchant', }, - { - columnName: CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION, - translationKey: 'common.description', - }, { columnName: CONST.SEARCH.TABLE_COLUMNS.CATEGORY, translationKey: 'common.category', @@ -63,19 +78,22 @@ type SearchTableHeaderProps = { amountColumnSize: TableColumnSize; taxAmountColumnSize: TableColumnSize; shouldShowSorting: boolean; - columns: SortableColumnName[]; + isIOUReport: boolean; }; -function MoneyRequestReportTableHeader({sortBy, sortOrder, onSortPress, dateColumnSize, shouldShowSorting, amountColumnSize, taxAmountColumnSize, columns}: SearchTableHeaderProps) { +function MoneyRequestReportTableHeader({sortBy, sortOrder, onSortPress, dateColumnSize, shouldShowSorting, isIOUReport, amountColumnSize, taxAmountColumnSize}: SearchTableHeaderProps) { const styles = useThemeStyles(); const shouldShowColumn = useCallback( (columnName: SortableColumnName) => { - return columns.includes(columnName); + const shouldShowFun = shouldShowColumnConfig[columnName]; + if (!shouldShowFun) { + return false; + } + return shouldShowFun(isIOUReport); }, - [columns], + [isIOUReport], ); - return ( void; }; function MoneyRequestReportTransactionItem({ transaction, - columns, report, isSelectionModeEnabled, toggleTransaction, @@ -133,7 +138,7 @@ function MoneyRequestReportTransactionItem({ shouldUseNarrowLayout={shouldUseNarrowLayout || isMediumScreenWidth} shouldShowCheckbox={!!isSelectionModeEnabled || !isSmallScreenWidth} onCheckboxPress={toggleTransaction} - columns={columns as Array>} + columns={allReportColumns} isDisabled={isPendingDelete} /> diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx index e7b5a22f3bd5..95182e0950b5 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx @@ -22,8 +22,8 @@ import {convertToDisplayString} from '@libs/CurrencyUtils'; import {getThreadReportIDsForTransactions} from '@libs/MoneyRequestReportUtils'; import {navigationRef} from '@libs/Navigation/Navigation'; import {getIOUActionForTransactionID} from '@libs/ReportActionsUtils'; -import {getMoneyRequestSpendBreakdown} from '@libs/ReportUtils'; -import {compareValues, getColumnsToShow, isTransactionAmountTooLong, isTransactionTaxAmountTooLong} from '@libs/SearchUIUtils'; +import {getMoneyRequestSpendBreakdown, isIOUReport} from '@libs/ReportUtils'; +import {compareValues, isTransactionAmountTooLong, isTransactionTaxAmountTooLong} from '@libs/SearchUIUtils'; import {getTransactionPendingAction, isTransactionPendingDelete} from '@libs/TransactionUtils'; import shouldShowTransactionYear from '@libs/TransactionUtils/shouldShowTransactionYear'; import Navigation from '@navigation/Navigation'; @@ -160,11 +160,6 @@ function MoneyRequestReportTransactionList({ })); }, [newTransactions, sortBy, sortOrder, transactions, localeCompare]); - const columnsToShow = useMemo(() => { - const columns = getColumnsToShow(transactions, true); - return (Object.keys(columns) as SortableColumnName[]).filter((column) => columns[column]); - }, [transactions]); - const navigateToTransaction = useCallback( (activeTransactionID: string) => { const iouAction = getIOUActionForTransactionID(reportActions, activeTransactionID); @@ -267,7 +262,6 @@ function MoneyRequestReportTransactionList({ shouldShowSorting sortBy={sortBy} sortOrder={sortOrder} - columns={columnsToShow} dateColumnSize={dateColumnSize} amountColumnSize={amountColumnSize} taxAmountColumnSize={taxAmountColumnSize} @@ -278,6 +272,7 @@ function MoneyRequestReportTransactionList({ setSortConfig((prevState) => ({...prevState, sortBy: selectedSortBy, sortOrder: selectedSortOrder})); }} + isIOUReport={isIOUReport(report)} /> )} @@ -288,7 +283,6 @@ function MoneyRequestReportTransactionList({ , 'onScroll' | 'conten /** The search query */ queryJSON: SearchQueryJSON; - /** Columns to show */ - columns: SortableColumnName[]; - /** Called when the viewability of rows changes, as defined by the viewabilityConfig prop. */ onViewableItemsChanged?: (info: {changed: ViewToken[]; viewableItems: ViewToken[]}) => void; @@ -134,7 +124,6 @@ function SearchList( shouldPreventDefaultFocusOnSelectRow, shouldPreventLongPressRow, queryJSON, - columns, onViewableItemsChanged, onLayout, estimatedItemSize = ITEM_HEIGHTS.NARROW_WITHOUT_DRAWER.STANDARD, @@ -350,7 +339,6 @@ function SearchList( }} shouldPreventDefaultFocusOnSelectRow={shouldPreventDefaultFocusOnSelectRow} queryJSONHash={hash} - columns={columns} policies={policies} isDisabled={isDisabled} allReports={allReports} @@ -371,7 +359,6 @@ function SearchList( onCheckboxPress, onSelectRow, policies, - columns, hash, groupBy, setFocusedIndex, @@ -473,7 +460,7 @@ function SearchList( onScroll={onScroll} showsVerticalScrollIndicator={false} ref={listRef} - extraData={[focusedIndex, isFocused, columns]} + extraData={[focusedIndex, isFocused]} onEndReached={onEndReached} onEndReachedThreshold={onEndReachedThreshold} ListFooterComponent={ListFooterComponent} diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index fd6409a96af8..e8d7ec00d1bf 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1,9 +1,8 @@ import {findFocusedRoute, useFocusEffect, useIsFocused, useNavigation} from '@react-navigation/native'; import type {ContentStyle} from '@shopify/flash-list'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; -import type {NativeScrollEvent, NativeSyntheticEvent} from 'react-native'; +import type {NativeScrollEvent, NativeSyntheticEvent, ViewToken} from 'react-native'; import {View} from 'react-native'; -import Animated, {FadeIn, FadeOut, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated'; import FullPageErrorView from '@components/BlockingViews/FullPageErrorView'; import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; import SearchTableHeader from '@components/SelectionList/SearchTableHeader'; @@ -29,7 +28,6 @@ import {getIOUActionForTransactionID, isExportIntegrationAction, isIntegrationMe import {canEditFieldOfMoneyRequest, generateReportID, isArchivedReport} from '@libs/ReportUtils'; import {buildSearchQueryString} from '@libs/SearchQueryUtils'; import { - getColumnsToShow, getListItem, getSections, getSortedSections, @@ -57,7 +55,6 @@ import SCREENS from '@src/SCREENS'; import type {ReportAction} from '@src/types/onyx'; import type SearchResults from '@src/types/onyx/SearchResults'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import arraysEqual from '@src/utils/arraysEqual'; import {useSearchContext} from './SearchContext'; import SearchList from './SearchList'; import SearchScopeProvider from './SearchScopeProvider'; @@ -548,39 +545,26 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS [hash, isMobileSelectionModeEnabled, toggleTransaction], ); - const currentColumns = useMemo(() => { - if (!searchResults?.data) { - return []; - } - const columns = getColumnsToShow(searchResults?.data); - - return (Object.keys(columns) as SearchColumnType[]).filter((col) => columns[col]); - }, [searchResults?.data]); - - // Custom animation for fade effect - const opacity = useSharedValue(1); - const animatedStyle = useAnimatedStyle(() => ({ - opacity: opacity.get(), - })); - - const previousColumns = usePrevious(currentColumns); - const [columnsToShow, setColumnsToShow] = useState([]); + const onViewableItemsChanged = useCallback( + ({viewableItems}: {viewableItems: ViewToken[]}) => { + if (!isFocused) { + return; + } - // If columns have changed, trigger an animation before settings columnsToShow to prevent - // new columns appearing before the fade out animation happens - useEffect(() => { - if ((previousColumns && currentColumns && arraysEqual(previousColumns, currentColumns)) || offset === 0 || isSmallScreenWidth) { - setColumnsToShow(currentColumns); - return; - } + const isFirstItemVisible = viewableItems.at(0)?.index === 1; + // If the user is still loading the search results, or if they are scrolling down, don't refresh the search results + if (shouldShowLoadingState || !isFirstItemVisible) { + return; + } - opacity.set( - withTiming(0, {duration: CONST.SEARCH.ANIMATION.FADE_DURATION}, () => { - setColumnsToShow(currentColumns); - opacity.set(withTiming(1, {duration: CONST.SEARCH.ANIMATION.FADE_DURATION})); - }), - ); - }, [previousColumns, currentColumns, setColumnsToShow, opacity, offset, isSmallScreenWidth]); + // This line makes sure the app refreshes the search results when the user scrolls to the top. + // The backend sends items in parts based on the offset, with a limit on the number of items sent (pagination). + // As a result, it skips some items, for example, if the offset is 100, it sends the next items without the first ones. + // Therefore, when the user scrolls to the top, we need to refresh the search results. + setOffset(0); + }, + [shouldShowLoadingState, isFocused], + ); const isChat = type === CONST.SEARCH.DATA_TYPES.CHAT; const isTask = type === CONST.SEARCH.DATA_TYPES.TASK; @@ -653,24 +637,14 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS ); }, [clearSelectedTransactions, data, groupBy, reportActionsArray, selectedTransactions, setSelectedTransactions]); - const onLayoutWithScrollRestore = useCallback(() => { - handleSelectionListScroll(sortedSelectedData, searchListRef.current); - // Restore scroll position after layout if needed - // Removed scroll position restoration logic - }, [handleSelectionListScroll, sortedSelectedData]); + const onLayout = useCallback(() => handleSelectionListScroll(sortedSelectedData, searchListRef.current), [handleSelectionListScroll, sortedSelectedData]); if (shouldShowLoadingState) { return ( - - - + ); } @@ -718,56 +692,50 @@ function Search({queryJSON, searchResults, onSearchListScroll, contentContainerS return ( - - - ) - } - contentContainerStyle={{...contentContainerStyle, ...styles.pb3}} - containerStyle={[styles.pv0, type === CONST.SEARCH.DATA_TYPES.CHAT && !isSmallScreenWidth && styles.pt3]} - shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()} - onScroll={onSearchListScroll} - onEndReachedThreshold={0.75} - onEndReached={fetchMoreResults} - ListFooterComponent={ - shouldShowLoadingMoreItems ? ( - - ) : undefined - } - queryJSON={queryJSON} - columns={columnsToShow} - onLayout={onLayoutWithScrollRestore} - isMobileSelectionModeEnabled={isMobileSelectionModeEnabled} - /> - + + ) + } + contentContainerStyle={{...contentContainerStyle, ...styles.pb3}} + containerStyle={[styles.pv0, type === CONST.SEARCH.DATA_TYPES.CHAT && !isSmallScreenWidth && styles.pt3]} + shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()} + onScroll={onSearchListScroll} + onEndReachedThreshold={0.75} + onEndReached={fetchMoreResults} + ListFooterComponent={ + shouldShowLoadingMoreItems ? ( + + ) : undefined + } + queryJSON={queryJSON} + onViewableItemsChanged={onViewableItemsChanged} + onLayout={onLayout} + isMobileSelectionModeEnabled={isMobileSelectionModeEnabled} + /> ); } diff --git a/src/components/SelectionList/Search/TransactionGroupListItem.tsx b/src/components/SelectionList/Search/TransactionGroupListItem.tsx index 31e98b3f87ad..2fd2e2631658 100644 --- a/src/components/SelectionList/Search/TransactionGroupListItem.tsx +++ b/src/components/SelectionList/Search/TransactionGroupListItem.tsx @@ -44,7 +44,6 @@ function TransactionGroupListItem({ onFocus, onLongPressRow, shouldSyncFocus, - columns, groupBy, }: TransactionGroupListItemProps) { const groupItem = item as unknown as TransactionGroupListItemType; @@ -88,6 +87,23 @@ function TransactionGroupListItem({ }); }; + const sampleTransaction = groupItem.transactions.at(0); + const {COLUMNS} = CONST.REPORT.TRANSACTION_LIST; + + const columns = [ + COLUMNS.RECEIPT, + COLUMNS.TYPE, + COLUMNS.DATE, + COLUMNS.MERCHANT, + COLUMNS.FROM, + COLUMNS.TO, + ...(sampleTransaction?.shouldShowCategory ? [COLUMNS.CATEGORY] : []), + ...(sampleTransaction?.shouldShowTag ? [COLUMNS.TAG] : []), + ...(sampleTransaction?.shouldShowTax ? [COLUMNS.TAX] : []), + COLUMNS.TOTAL_AMOUNT, + COLUMNS.ACTION, + ] satisfies Array>; + const getHeader = useMemo(() => { const headers: Record = { [CONST.SEARCH.GROUP_BY.REPORTS]: ( @@ -185,7 +201,7 @@ function TransactionGroupListItem({ shouldUseNarrowLayout={!isLargeScreenWidth} shouldShowCheckbox={!!canSelectMultiple} onCheckboxPress={() => onCheckboxPress?.(transaction as unknown as TItem)} - columns={columns as Array>} + columns={columns} onButtonPress={() => { openReportInRHP(transaction); }} diff --git a/src/components/SelectionList/Search/TransactionListItem.tsx b/src/components/SelectionList/Search/TransactionListItem.tsx index 87b9f1ca8b4c..4ca95e845a5d 100644 --- a/src/components/SelectionList/Search/TransactionListItem.tsx +++ b/src/components/SelectionList/Search/TransactionListItem.tsx @@ -33,7 +33,6 @@ function TransactionListItem({ onLongPressRow, shouldSyncFocus, isLoading, - columns, }: TransactionListItemProps) { const transactionItem = item as unknown as TransactionListItemType; const styles = useThemeStyles(); @@ -73,6 +72,24 @@ function TransactionListItem({ }; }, [transactionItem]); + const columns = useMemo( + () => + [ + CONST.REPORT.TRANSACTION_LIST.COLUMNS.RECEIPT, + CONST.REPORT.TRANSACTION_LIST.COLUMNS.TYPE, + CONST.REPORT.TRANSACTION_LIST.COLUMNS.DATE, + CONST.REPORT.TRANSACTION_LIST.COLUMNS.MERCHANT, + CONST.REPORT.TRANSACTION_LIST.COLUMNS.FROM, + CONST.REPORT.TRANSACTION_LIST.COLUMNS.TO, + ...(transactionItem?.shouldShowCategory ? [CONST.REPORT.TRANSACTION_LIST.COLUMNS.CATEGORY] : []), + ...(transactionItem?.shouldShowTag ? [CONST.REPORT.TRANSACTION_LIST.COLUMNS.TAG] : []), + ...(transactionItem?.shouldShowTax ? [CONST.REPORT.TRANSACTION_LIST.COLUMNS.TAX] : []), + CONST.REPORT.TRANSACTION_LIST.COLUMNS.TOTAL_AMOUNT, + CONST.REPORT.TRANSACTION_LIST.COLUMNS.ACTION, + ] satisfies Array>, + [transactionItem?.shouldShowCategory, transactionItem?.shouldShowTag, transactionItem?.shouldShowTax], + ); + const handleActionButtonPress = useCallback(() => { handleActionButtonPressUtil( currentSearchHash, @@ -138,7 +155,7 @@ function TransactionListItem({ onButtonPress={handleActionButtonPress} onCheckboxPress={handleCheckboxPress} shouldUseNarrowLayout={!isLargeScreenWidth} - columns={columns as Array>} + columns={columns} isActionLoading={isLoading ?? transactionItem.isActionLoading} isSelected={!!transactionItem.isSelected} dateColumnSize={dateColumnSize} diff --git a/src/components/SelectionList/SearchTableHeader.tsx b/src/components/SelectionList/SearchTableHeader.tsx index 7e6509a8a1ef..21130da633ea 100644 --- a/src/components/SelectionList/SearchTableHeader.tsx +++ b/src/components/SelectionList/SearchTableHeader.tsx @@ -2,18 +2,41 @@ import React, {useCallback} from 'react'; import type {SearchColumnType, SortOrder} from '@components/Search/types'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; +import {getShouldShowMerchant} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import type * as OnyxTypes from '@src/types/onyx'; import SortableTableHeader from './SortableTableHeader'; import type {SortableColumnName} from './types'; +type ShouldShowSearchColumnFn = (data: OnyxTypes.SearchResults['data'], metadata: OnyxTypes.SearchResults['search']) => boolean; + type SearchColumnConfig = { columnName: SearchColumnType; translationKey: TranslationPaths; isColumnSortable?: boolean; }; +const shouldShowColumnConfig: Record = { + [CONST.SEARCH.TABLE_COLUMNS.RECEIPT]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.TYPE]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.DATE]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.MERCHANT]: (data: OnyxTypes.SearchResults['data']) => getShouldShowMerchant(data), + [CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION]: (data: OnyxTypes.SearchResults['data']) => !getShouldShowMerchant(data), + [CONST.SEARCH.TABLE_COLUMNS.FROM]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.TO]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.CATEGORY]: (data, metadata) => metadata?.columnsToShow?.shouldShowCategoryColumn ?? false, + [CONST.SEARCH.TABLE_COLUMNS.TAG]: (data, metadata) => metadata?.columnsToShow?.shouldShowTagColumn ?? false, + [CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT]: (data, metadata) => metadata?.columnsToShow?.shouldShowTaxColumn ?? false, + [CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.ACTION]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.TITLE]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.ASSIGNEE]: () => true, + [CONST.SEARCH.TABLE_COLUMNS.IN]: () => true, + // This column is never displayed on Search + [CONST.REPORT.TRANSACTION_LIST.COLUMNS.COMMENTS]: () => false, +}; + const expenseHeaders: SearchColumnConfig[] = [ { columnName: CONST.SEARCH.TABLE_COLUMNS.RECEIPT, @@ -111,6 +134,7 @@ const SearchColumns = { }; type SearchTableHeaderProps = { + data: OnyxTypes.SearchResults['data']; metadata: OnyxTypes.SearchResults['search']; sortBy?: SearchColumnType; sortOrder?: SortOrder; @@ -120,10 +144,10 @@ type SearchTableHeaderProps = { isTaxAmountColumnWide: boolean; shouldShowSorting: boolean; canSelectMultiple: boolean; - columns: SortableColumnName[]; }; function SearchTableHeader({ + data, metadata, sortBy, sortOrder, @@ -133,7 +157,6 @@ function SearchTableHeader({ canSelectMultiple, isAmountColumnWide, isTaxAmountColumnWide, - columns, }: SearchTableHeaderProps) { const styles = useThemeStyles(); // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth @@ -142,9 +165,10 @@ function SearchTableHeader({ const shouldShowColumn = useCallback( (columnName: SortableColumnName) => { - return columns.includes(columnName); + const shouldShowFun = shouldShowColumnConfig[columnName]; + return shouldShowFun(data, metadata); }, - [columns], + [data, metadata], ); if (displayNarrowVersion) { diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 08d4eb1c4068..7da7e4776687 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -246,9 +246,6 @@ type TransactionListItemType = ListItem & /** Whether we should show the merchant column */ shouldShowMerchant: boolean; - /** Whether the description column should be shown */ - shouldShowDescription: boolean; - /** Whether we should show the category column */ shouldShowCategory: boolean; @@ -258,12 +255,6 @@ type TransactionListItemType = ListItem & /** Whether we should show the tax column */ shouldShowTax: boolean; - /** Whether we should show the From column */ - shouldShowFrom: boolean; - - /** Whether we should show the to column */ - shouldShowTo: boolean; - /** Whether we should show the transaction year. * This is true if at least one transaction in the dataset was created in past years */ @@ -466,7 +457,6 @@ type TableListItemProps = ListItemProps; type TransactionListItemProps = ListItemProps & { /** Whether the item's action is loading */ isLoading?: boolean; - columns?: SortableColumnName[]; }; type TaskListItemProps = ListItemProps & { @@ -476,7 +466,7 @@ type TaskListItemProps = ListItemProps & { type TransactionGroupListItemProps = ListItemProps & { groupBy?: SearchGroupBy; - columns?: SortableColumnName[]; + policies?: OnyxCollection; }; type ChatListItemProps = ListItemProps & { diff --git a/src/components/TransactionItemRow/index.tsx b/src/components/TransactionItemRow/index.tsx index 0f7bbb208fef..7a6abf1ef400 100644 --- a/src/components/TransactionItemRow/index.tsx +++ b/src/components/TransactionItemRow/index.tsx @@ -13,6 +13,7 @@ import useLocalize from '@hooks/useLocalize'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import {isCategoryMissing} from '@libs/CategoryUtils'; +import Parser from '@libs/Parser'; import StringUtils from '@libs/StringUtils'; import { getDescription, @@ -67,9 +68,6 @@ type TransactionWithOptionalSearchFields = TransactionWithOptionalHighlight & { /** information about whether to show merchant, that is provided on Reports page */ shouldShowMerchant?: boolean; - /** information about whether to show the description, that is provided on Reports page */ - shouldShowDescription?: boolean; - /** Type of transaction */ transactionType?: ValueOf; @@ -97,17 +95,24 @@ type TransactionItemRowProps = { isDisabled?: boolean; }; -function getMerchantName(transactionItem: TransactionWithOptionalSearchFields, translate: (key: TranslationPaths) => string) { +/** If merchant name is empty or (none), then it falls back to description if screen is narrow */ +function getMerchantNameWithFallback(transactionItem: TransactionWithOptionalSearchFields, translate: (key: TranslationPaths) => string, shouldUseNarrowLayout?: boolean | undefined) { const shouldShowMerchant = transactionItem.shouldShowMerchant ?? true; + const description = getDescription(transactionItem); + let merchantOrDescriptionToDisplay = transactionItem?.formattedMerchant ?? getMerchant(transactionItem); + const merchantNameEmpty = !merchantOrDescriptionToDisplay || merchantOrDescriptionToDisplay === CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT; + if (merchantNameEmpty && shouldUseNarrowLayout) { + merchantOrDescriptionToDisplay = Parser.htmlToText(description); + } - let merchant = transactionItem?.formattedMerchant ?? getMerchant(transactionItem); + let merchant = shouldShowMerchant ? merchantOrDescriptionToDisplay : Parser.htmlToText(description); if (isScanning(transactionItem) && shouldShowMerchant) { merchant = translate('iou.receiptStatusTitle'); } const merchantName = StringUtils.getFirstLine(merchant); - return merchantName !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT ? merchantName : ''; + return merchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT ? merchantName : ''; } function TransactionItemRow({ @@ -147,12 +152,7 @@ function TransactionItemRow({ return styles.activeComponentBG; }, [isSelected, styles.activeComponentBG]); - const merchant = useMemo(() => getMerchantName(transactionItem, translate), [transactionItem, translate]); - const description = getDescription(transactionItem); - - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const merchantOrDescription = merchant || description; - + const merchantOrDescriptionName = useMemo(() => getMerchantNameWithFallback(transactionItem, translate, shouldUseNarrowLayout), [shouldUseNarrowLayout, transactionItem, translate]); const missingFieldError = useMemo(() => { const isCustomUnitOutOfPolicy = isUnreportedAndHasInvalidDistanceRateTransaction(transactionItem); const hasFieldErrors = hasMissingSmartscanFields(transactionItem) || isCustomUnitOutOfPolicy; @@ -258,23 +258,9 @@ function TransactionItemRow({ key={CONST.REPORT.TRANSACTION_LIST.COLUMNS.MERCHANT} style={[StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.MERCHANT)]} > - {!!merchant && ( - - )} - - ), - [CONST.REPORT.TRANSACTION_LIST.COLUMNS.DESCRIPTION]: ( - - {!!description && ( + {!!merchantOrDescriptionName && ( @@ -354,8 +340,7 @@ function TransactionItemRow({ isTaxAmountColumnWide, isInSingleTransactionReport, isSelected, - merchant, - description, + merchantOrDescriptionName, onButtonPress, shouldShowTooltip, shouldUseNarrowLayout, @@ -401,7 +386,7 @@ function TransactionItemRow({ shouldShowTooltip={shouldShowTooltip} shouldUseNarrowLayout={shouldUseNarrowLayout} /> - {!merchantOrDescription && ( + {!merchantOrDescriptionName && ( )} - {!!merchantOrDescription && ( + {!!merchantOrDescriptionName && ( diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 432842719e08..2a5d57086dc5 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -17,7 +17,6 @@ import type { ListItem, ReportActionListItemType, SearchListItem, - SortableColumnName, TaskListItemType, TransactionCardGroupListItemType, TransactionGroupListItemType, @@ -77,22 +76,17 @@ import { isInvoiceReport, isMoneyRequestReport, isOpenExpenseReport, - isOpenReport, isSettled, } from './ReportUtils'; import {buildCannedSearchQuery, buildQueryStringFromFilterFormValues, buildSearchQueryJSON, getTodoSearchQuery} from './SearchQueryUtils'; import StringUtils from './StringUtils'; import {shouldRestrictUserBillableActions} from './SubscriptionUtils'; import { - getCategory, - getDescription, - getTag, getTaxAmount, getAmount as getTransactionAmount, getCreated as getTransactionCreatedDate, getMerchant as getTransactionMerchant, isPendingCardOrScanningTransaction, - isScanning, isUnreportedAndHasInvalidDistanceRateTransaction, isViolationDismissed, } from './TransactionUtils'; @@ -733,9 +727,6 @@ function getTransactionsSections(data: OnyxTypes.SearchResults['data'], metadata const shouldShowCategory = metadata?.columnsToShow?.shouldShowCategoryColumn; const shouldShowTag = metadata?.columnsToShow?.shouldShowTagColumn; const shouldShowTax = metadata?.columnsToShow?.shouldShowTaxColumn; - const shouldShowTo = metadata?.columnsToShow?.shouldShowToColumn; - const shouldShowFrom = metadata?.columnsToShow?.shouldShowFromColumn; - const shouldShowDescription = metadata?.columnsToShow?.shouldShowDescriptionColumn; // Pre-filter transaction keys to avoid repeated checks const transactionKeys = Object.keys(data).filter(isTransactionEntry); @@ -774,9 +765,6 @@ function getTransactionsSections(data: OnyxTypes.SearchResults['data'], metadata shouldShowCategory, shouldShowTag, shouldShowTax, - shouldShowTo, - shouldShowFrom, - shouldShowDescription, keyForList: transactionItem.transactionID, shouldShowYear: doesDataContainAPastYearTransaction, isAmountColumnWide: shouldShowAmountInWideColumn, @@ -1211,9 +1199,6 @@ function getReportSections( formattedMerchant, date, shouldShowMerchant, - shouldShowDescription: metadata?.columnsToShow.shouldShowDescriptionColumn, - shouldShowFrom: metadata?.columnsToShow.shouldShowFromColumn, - shouldShowTo: metadata?.columnsToShow.shouldShowToColumn, shouldShowCategory: metadata?.columnsToShow?.shouldShowCategoryColumn, shouldShowTag: metadata?.columnsToShow?.shouldShowTagColumn, shouldShowTax: metadata?.columnsToShow?.shouldShowTaxColumn, @@ -1342,95 +1327,6 @@ function getSortedSections( return getSortedTransactionData(data as TransactionListItemType[], localeCompare, sortBy, sortOrder); } -/** - * Determines what columns to show based on available data - * @param isExpenseReportView: true when we are inside an expense report view, false if we're in the Reports page. - */ -function getColumnsToShow(data: OnyxTypes.SearchResults['data'] | OnyxTypes.Transaction[], isExpenseReportView = false): Record { - const columns: Record = isExpenseReportView - ? { - [CONST.REPORT.TRANSACTION_LIST.COLUMNS.RECEIPT]: true, - [CONST.REPORT.TRANSACTION_LIST.COLUMNS.TYPE]: true, - [CONST.REPORT.TRANSACTION_LIST.COLUMNS.DATE]: true, - [CONST.REPORT.TRANSACTION_LIST.COLUMNS.MERCHANT]: false, - [CONST.REPORT.TRANSACTION_LIST.COLUMNS.DESCRIPTION]: false, - [CONST.REPORT.TRANSACTION_LIST.COLUMNS.CATEGORY]: false, - [CONST.REPORT.TRANSACTION_LIST.COLUMNS.TAG]: false, - [CONST.REPORT.TRANSACTION_LIST.COLUMNS.COMMENTS]: true, - [CONST.REPORT.TRANSACTION_LIST.COLUMNS.TOTAL_AMOUNT]: true, - [CONST.REPORT.TRANSACTION_LIST.COLUMNS.ACTION]: false, - } - : { - [CONST.SEARCH.TABLE_COLUMNS.RECEIPT]: true, - [CONST.SEARCH.TABLE_COLUMNS.TYPE]: true, - [CONST.SEARCH.TABLE_COLUMNS.DATE]: true, - [CONST.SEARCH.TABLE_COLUMNS.MERCHANT]: false, - [CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION]: false, - [CONST.SEARCH.TABLE_COLUMNS.FROM]: false, - [CONST.SEARCH.TABLE_COLUMNS.TO]: false, - [CONST.SEARCH.TABLE_COLUMNS.CATEGORY]: false, - [CONST.SEARCH.TABLE_COLUMNS.TAG]: false, - [CONST.SEARCH.TABLE_COLUMNS.TAX_AMOUNT]: false, - [CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT]: true, - [CONST.SEARCH.TABLE_COLUMNS.ACTION]: true, - [CONST.SEARCH.TABLE_COLUMNS.TITLE]: true, - [CONST.SEARCH.TABLE_COLUMNS.ASSIGNEE]: true, - [CONST.SEARCH.TABLE_COLUMNS.IN]: true, - }; - - const updateColumns = (transaction: OnyxTypes.Transaction | SearchTransaction) => { - const merchant = transaction.modifiedMerchant ? transaction.modifiedMerchant : (transaction.merchant ?? ''); - if ((merchant !== '' && merchant !== CONST.TRANSACTION.PARTIAL_TRANSACTION_MERCHANT) || isScanning(transaction)) { - columns[CONST.REPORT.TRANSACTION_LIST.COLUMNS.MERCHANT] = true; - } - - if (getDescription(transaction) !== '') { - columns[CONST.REPORT.TRANSACTION_LIST.COLUMNS.DESCRIPTION] = true; - } - - const category = getCategory(transaction); - const categoryEmptyValues = CONST.SEARCH.CATEGORY_EMPTY_VALUE.split(','); - if (category !== '' && !categoryEmptyValues.includes(category)) { - columns[CONST.REPORT.TRANSACTION_LIST.COLUMNS.CATEGORY] = true; - } - - const tag = getTag(transaction); - if (tag !== '' && tag !== CONST.SEARCH.TAG_EMPTY_VALUE) { - columns[CONST.REPORT.TRANSACTION_LIST.COLUMNS.TAG] = true; - } - - if (isExpenseReportView) { - return; - } - - // Handle From&To columns that are only shown in the Reports page - // if From or To differ from current user in any transaction, show the columns - const accountID = (transaction as SearchTransaction).accountID; - if (accountID !== currentAccountID) { - columns[CONST.REPORT.TRANSACTION_LIST.COLUMNS.FROM] = true; - } - - const managerID = (transaction as SearchTransaction).managerID; - if (managerID && managerID !== currentAccountID && !columns[CONST.REPORT.TRANSACTION_LIST.COLUMNS.TO]) { - const report = (data as OnyxTypes.SearchResults['data'])[`${ONYXKEYS.COLLECTION.REPORT}${transaction.reportID}`]; - columns[CONST.REPORT.TRANSACTION_LIST.COLUMNS.TO] = !!report && !isOpenReport(report); - } - }; - - if (Array.isArray(data)) { - data.forEach(updateColumns); - } else { - Object.keys(data).forEach((key) => { - if (!isTransactionEntry(key)) { - return; - } - updateColumns(data[key]); - }); - } - - return columns; -} - /** * Compares two values based on a specified sorting order and column. * Handles both string and numeric comparisons, with special handling for absolute values when sorting by total amount. @@ -1878,7 +1774,6 @@ export { isReportActionEntry, isTaskListItemType, getAction, - getColumnsToShow, createTypeMenuSections, createBaseSavedSearchMenuItem, shouldShowEmptyState, diff --git a/src/types/onyx/SearchResults.ts b/src/types/onyx/SearchResults.ts index 85ea2213270c..d370b62706e2 100644 --- a/src/types/onyx/SearchResults.ts +++ b/src/types/onyx/SearchResults.ts @@ -35,12 +35,6 @@ type ListItemDataType = C ext /** Model of columns to show for search results */ type ColumnsToShow = { - /** Whether the From column should be shown */ - shouldShowFromColumn: boolean; - - /** Whether the To column should be shown */ - shouldShowToColumn: boolean; - /** Whether the category column should be shown */ shouldShowCategoryColumn: boolean; @@ -49,9 +43,6 @@ type ColumnsToShow = { /** Whether the tax column should be shown */ shouldShowTaxColumn: boolean; - - /** Whether the description column should be shown */ - shouldShowDescriptionColumn: boolean; }; /** Model of search result state */ diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 968c45a9fe9f..0fc08b67c39b 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -431,9 +431,6 @@ const searchResults: OnyxTypes.SearchResults = { shouldShowCategoryColumn: true, shouldShowTagColumn: false, shouldShowTaxColumn: false, - shouldShowFromColumn: true, - shouldShowToColumn: true, - shouldShowDescriptionColumn: false, }, hasMoreResults: false, hasResults: true, @@ -516,13 +513,10 @@ const transactionsListItems = [ reportID: '123456789', reportType: 'expense', shouldShowCategory: true, - shouldShowDescription: false, shouldShowMerchant: true, shouldShowTag: false, shouldShowTax: false, shouldShowYear: true, - shouldShowFrom: true, - shouldShowTo: true, isAmountColumnWide: false, isTaxAmountColumnWide: false, tag: '', @@ -587,9 +581,6 @@ const transactionsListItems = [ shouldShowTag: false, shouldShowTax: false, shouldShowYear: true, - shouldShowFrom: true, - shouldShowTo: true, - shouldShowDescription: false, isAmountColumnWide: false, isTaxAmountColumnWide: false, tag: '', @@ -670,9 +661,6 @@ const transactionsListItems = [ shouldShowTax: false, keyForList: '3', shouldShowYear: true, - shouldShowFrom: true, - shouldShowTo: true, - shouldShowDescription: false, isAmountColumnWide: false, isTaxAmountColumnWide: false, receipt: undefined, @@ -738,9 +726,6 @@ const transactionsListItems = [ shouldShowTax: false, keyForList: '3', shouldShowYear: true, - shouldShowFrom: true, - shouldShowTo: true, - shouldShowDescription: false, isAmountColumnWide: false, isTaxAmountColumnWide: false, receipt: undefined, @@ -835,9 +820,6 @@ const transactionReportGroupListItems = [ shouldShowTag: false, shouldShowTax: false, shouldShowYear: true, - shouldShowFrom: true, - shouldShowTo: true, - shouldShowDescription: false, isAmountColumnWide: false, isTaxAmountColumnWide: false, tag: '', @@ -945,9 +927,6 @@ const transactionReportGroupListItems = [ shouldShowTag: false, shouldShowTax: false, shouldShowYear: true, - shouldShowFrom: true, - shouldShowTo: true, - shouldShowDescription: false, isAmountColumnWide: false, isTaxAmountColumnWide: false, tag: '', @@ -1609,9 +1588,6 @@ describe('SearchUIUtils', () => { shouldShowCategoryColumn: true, shouldShowTagColumn: true, shouldShowTaxColumn: true, - shouldShowFromColumn: true, - shouldShowToColumn: true, - shouldShowDescriptionColumn: false, }, }, }; @@ -1654,189 +1630,4 @@ describe('SearchUIUtils', () => { expect(isAmountLengthLong3).toBe(true); expect(isTaxAmountLengthLong2).toBe(true); }); - - describe('Test getColumnsToShow', () => { - test('Should only show columns when at least one transaction has a value for them', async () => { - await Onyx.merge(ONYXKEYS.SESSION, {accountID: submitterAccountID}); - - // Use the existing transaction as a base and modify only the fields we need to test - const baseTransaction = searchResults.data[`transactions_${transactionID}`]; - - // Create test transactions as arrays (getColumnsToShow accepts arrays) - const emptyTransaction = { - ...baseTransaction, - transactionID: 'empty', - merchant: '', - modifiedMerchant: '', - comment: {comment: ''}, - category: '', - tag: '', - accountID: submitterAccountID, - managerID: submitterAccountID, - }; - - const merchantTransaction = { - ...baseTransaction, - transactionID: 'merchant', - merchant: 'Test Merchant', - modifiedMerchant: '', - comment: {comment: ''}, - category: '', - tag: '', - accountID: submitterAccountID, - managerID: submitterAccountID, - }; - - const categoryTransaction = { - ...baseTransaction, - transactionID: 'category', - merchant: '', - modifiedMerchant: '', - comment: {comment: ''}, - category: 'Office Supplies', - tag: '', - accountID: submitterAccountID, - managerID: submitterAccountID, - }; - - const tagTransaction = { - ...baseTransaction, - transactionID: 'tag', - merchant: '', - modifiedMerchant: '', - comment: {comment: ''}, - category: '', - tag: 'Project A', - accountID: submitterAccountID, - managerID: submitterAccountID, - }; - - const descriptionTransaction = { - ...baseTransaction, - transactionID: 'description', - merchant: '', - modifiedMerchant: '', - comment: {comment: 'Business meeting lunch'}, - category: '', - tag: '', - accountID: submitterAccountID, - managerID: submitterAccountID, - }; - - const differentUsersTransaction = { - ...baseTransaction, - transactionID: 'differentUsers', - merchant: '', - modifiedMerchant: '', - comment: {comment: ''}, - category: '', - tag: '', - accountID: approverAccountID, // Different from current user - managerID: adminAccountID, // Different from current user - reportID: reportID2, // Needs to be a submitter report for 'To' to show - }; - - // Test 1: No optional fields should be shown when all transactions are empty - let columns = SearchUIUtils.getColumnsToShow([emptyTransaction, emptyTransaction], false); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.MERCHANT]).toBe(false); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.CATEGORY]).toBe(false); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.TAG]).toBe(false); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION]).toBe(false); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.FROM]).toBe(false); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.TO]).toBe(false); - - // Test 2: Merchant column should show when at least one transaction has merchant - columns = SearchUIUtils.getColumnsToShow([emptyTransaction, merchantTransaction], false); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.MERCHANT]).toBe(true); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.CATEGORY]).toBe(false); - - // Test 3: Category column should show when at least one transaction has category - columns = SearchUIUtils.getColumnsToShow([emptyTransaction, categoryTransaction], false); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.CATEGORY]).toBe(true); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.MERCHANT]).toBe(false); - - // Test 4: Tag column should show when at least one transaction has tag - columns = SearchUIUtils.getColumnsToShow([emptyTransaction, tagTransaction], false); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.TAG]).toBe(true); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.CATEGORY]).toBe(false); - - // Test 5: Description column should show when at least one transaction has description - columns = SearchUIUtils.getColumnsToShow([emptyTransaction, descriptionTransaction], false); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION]).toBe(true); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.MERCHANT]).toBe(false); - - // Test 6: From/To columns should show when at least one transaction has different users - // @ts-expect-error -- no need to construct all data again, the function below only needs the report and transactions - const data: OnyxTypes.SearchResults['data'] = { - [`report_${reportID2}`]: searchResults.data[`report_${reportID2}`], - [`transactions_${emptyTransaction.transactionID}`]: emptyTransaction, - [`transactions_${differentUsersTransaction.transactionID}`]: differentUsersTransaction, - }; - columns = SearchUIUtils.getColumnsToShow(data, false); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.FROM]).toBe(true); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.TO]).toBe(true); - - // Test 7: Multiple columns should show when transactions have different fields - columns = SearchUIUtils.getColumnsToShow([merchantTransaction, categoryTransaction, tagTransaction], false); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.MERCHANT]).toBe(true); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.CATEGORY]).toBe(true); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.TAG]).toBe(true); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.DESCRIPTION]).toBe(false); - }); - - test('Should respect isExpenseReportView flag and not show From/To columns', () => { - // Create transaction with different users using existing transaction as base - const baseTransaction = searchResults.data[`transactions_${transactionID}`]; - const testTransaction = { - ...baseTransaction, - transactionID: 'test', - merchant: 'Test Merchant', - modifiedMerchant: '', - comment: {comment: 'Test description'}, - category: 'Office Supplies', - tag: 'Project A', - accountID: submitterAccountID, // Different from current user - managerID: approverAccountID, // Different from current user - }; - - // In expense report view, From/To columns should not be shown - const columns = SearchUIUtils.getColumnsToShow([testTransaction], true); - - // These columns should be shown based on data - expect(columns[CONST.REPORT.TRANSACTION_LIST.COLUMNS.MERCHANT]).toBe(true); - expect(columns[CONST.REPORT.TRANSACTION_LIST.COLUMNS.CATEGORY]).toBe(true); - expect(columns[CONST.REPORT.TRANSACTION_LIST.COLUMNS.TAG]).toBe(true); - expect(columns[CONST.REPORT.TRANSACTION_LIST.COLUMNS.DESCRIPTION]).toBe(true); - - // From/To columns should not exist in expense report view - expect(columns[CONST.SEARCH.TABLE_COLUMNS.FROM]).toBeUndefined(); - expect(columns[CONST.SEARCH.TABLE_COLUMNS.TO]).toBeUndefined(); - }); - - test('Should handle modifiedMerchant and empty category/tag values correctly', () => { - const baseTransaction = searchResults.data[`transactions_${transactionID}`]; - const testTransaction = { - ...baseTransaction, - transactionID: 'modified', - merchant: '', - modifiedMerchant: 'Modified Merchant', - comment: {comment: ''}, - category: 'Uncategorized', // This is in CONST.SEARCH.CATEGORY_EMPTY_VALUE - tag: CONST.SEARCH.TAG_EMPTY_VALUE, // This is the empty tag value - accountID: adminAccountID, - managerID: adminAccountID, - }; - - const columns = SearchUIUtils.getColumnsToShow([testTransaction], false); - - // Should show merchant column because modifiedMerchant has value - expect(columns[CONST.SEARCH.TABLE_COLUMNS.MERCHANT]).toBe(true); - - // Should not show category column because 'Uncategorized' is an empty value - expect(columns[CONST.SEARCH.TABLE_COLUMNS.CATEGORY]).toBe(false); - - // Should not show tag column because it's the empty tag value - expect(columns[CONST.SEARCH.TABLE_COLUMNS.TAG]).toBe(false); - }); - }); }); diff --git a/tests/unit/Search/handleActionButtonPressTest.ts b/tests/unit/Search/handleActionButtonPressTest.ts index 0c1902165a85..7a9e48a280cf 100644 --- a/tests/unit/Search/handleActionButtonPressTest.ts +++ b/tests/unit/Search/handleActionButtonPressTest.ts @@ -193,9 +193,6 @@ const mockReportItemWithHold = { shouldShowCategory: true, shouldShowTag: false, shouldShowTax: false, - shouldShowTo: true, - shouldShowFrom: true, - shouldShowDescription: false, keyForList: '5345995386715609966', shouldShowYear: false, isAmountColumnWide: false, diff --git a/tests/unit/useSearchHighlightAndScrollTest.ts b/tests/unit/useSearchHighlightAndScrollTest.ts index 832d4e696c4e..e42d23c65f3c 100644 --- a/tests/unit/useSearchHighlightAndScrollTest.ts +++ b/tests/unit/useSearchHighlightAndScrollTest.ts @@ -36,14 +36,7 @@ describe('useSearchHighlightAndScroll', () => { personalDetailsList: {}, }, search: { - columnsToShow: { - shouldShowCategoryColumn: true, - shouldShowTagColumn: true, - shouldShowTaxColumn: true, - shouldShowToColumn: true, - shouldShowFromColumn: true, - shouldShowDescriptionColumn: true, - }, + columnsToShow: {shouldShowCategoryColumn: true, shouldShowTagColumn: true, shouldShowTaxColumn: true}, hasMoreResults: false, hasResults: true, offset: 0,