From bc26df7f730e48816e192d3ad22cf4fda81c2022 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Wed, 25 Mar 2026 01:40:03 +0530 Subject: [PATCH 01/46] Add compact UI styles for search reports and expenses tables Signed-off-by: krishna2323 --- .../SearchReportAvatar.tsx | 10 ++++++--- src/components/ReportSearchHeader/index.tsx | 2 ++ .../ListItem/BaseListItemHeader.tsx | 1 - .../ListItem/CardListItemHeader.tsx | 1 - .../ListItem/ExpenseReportListItem.tsx | 14 ++++++++---- .../ListItem/ExpenseReportListItemRow.tsx | 6 ++--- .../ListItem/MemberListItemHeader.tsx | 1 - .../SearchList/ListItem/TaskListItem.tsx | 12 +++++++--- .../Search/SearchList/ListItem/TextCell.tsx | 4 ++-- .../ListItem/TransactionGroupListItem.tsx | 18 +++++++++++---- .../ListItem/TransactionListItem.tsx | 18 +++++++++++---- .../ListItem/UserInfoAndActionButtonRow.tsx | 2 +- src/components/Search/SearchList/index.tsx | 2 +- .../SearchPageHeader/SearchFiltersBarWide.tsx | 2 +- src/components/Search/SearchTableHeader.tsx | 3 +-- .../SelectionList/BaseSelectionList.tsx | 1 + .../ListItem/ListItemRenderer.tsx | 3 +++ .../SelectionList/ListItem/TableListItem.tsx | 22 ++++++++++++++++--- .../SelectionList/ListItem/types.ts | 5 ++++- .../DataCells/CategoryCell.tsx | 4 ++-- .../DataCells/MerchantCell.tsx | 4 ++-- .../DataCells/ReceiptCell.tsx | 2 +- .../TransactionItemRow/DataCells/TagCell.tsx | 4 ++-- .../TransactionItemRow/DataCells/TypeCell.tsx | 4 ++-- src/components/TransactionItemRow/index.tsx | 1 - src/libs/SearchUIUtils.ts | 20 +++++++---------- src/styles/index.ts | 14 +++++++----- src/styles/utils/index.ts | 10 +++++---- src/styles/variables.ts | 3 +++ 29 files changed, 127 insertions(+), 66 deletions(-) diff --git a/src/components/ReportActionAvatars/SearchReportAvatar.tsx b/src/components/ReportActionAvatars/SearchReportAvatar.tsx index 89a89d8394aa..dc4837886abb 100644 --- a/src/components/ReportActionAvatars/SearchReportAvatar.tsx +++ b/src/components/ReportActionAvatars/SearchReportAvatar.tsx @@ -1,6 +1,7 @@ import React from 'react'; import type {ColorValue} from 'react-native'; import type {ValueOf} from 'type-fest'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; import CONST from '@src/CONST'; import type {Icon} from '@src/types/onyx/OnyxCommon'; import ReportActionAvatar from './ReportActionAvatar'; @@ -15,6 +16,9 @@ type SearchReportAvatarProps = { }; function SearchReportAvatar({primaryAvatar, secondaryAvatar, avatarType, shouldShowTooltip, subscriptAvatarBorderColor, reportID}: SearchReportAvatarProps) { + const {isLargeScreenWidth} = useResponsiveLayout(); + const avatarSize = isLargeScreenWidth ? CONST.AVATAR_SIZE.SMALL : CONST.AVATAR_SIZE.DEFAULT; + if (!primaryAvatar) { return null; } @@ -24,7 +28,7 @@ function SearchReportAvatar({primaryAvatar, secondaryAvatar, avatarType, shouldS ({ isIndeterminate={isIndeterminate} disabled={!!isDisabled || item.isDisabledCheckbox} accessibilityLabel={translate('common.select')} - style={isLargeScreenWidth && styles.mr1} sentryLabel={CONST.SENTRY_LABEL.SEARCH.GROUP_SELECT_ALL_CHECKBOX} /> )} diff --git a/src/components/Search/SearchList/ListItem/CardListItemHeader.tsx b/src/components/Search/SearchList/ListItem/CardListItemHeader.tsx index 6a14e9e479bb..9e2f2c64363a 100644 --- a/src/components/Search/SearchList/ListItem/CardListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/CardListItemHeader.tsx @@ -131,7 +131,6 @@ function CardListItemHeader({ isIndeterminate={isIndeterminate} disabled={!!isDisabled || cardItem.isDisabledCheckbox} accessibilityLabel={translate('common.select')} - style={isLargeScreenWidth && styles.mr1} /> )} {!isLargeScreenWidth && ( diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx index f9ad28842fdd..0a8cfe5d1861 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx @@ -164,12 +164,18 @@ function ExpenseReportListItem({ styles.selectionListPressableItemWrapper, styles.pv3, styles.ph3, - // Removing background style because they are added to the parent OpacityView via animatedHighlightStyle styles.bgTransparent, item.isSelected && styles.activeComponentBG, styles.mh0, + isLargeScreenWidth && { + minHeight: variables.tableRowHeight, + borderRadius: 0, + paddingVertical: 8, + borderBottomWidth: 1, + borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, + }, ], - [styles, item.isSelected], + [styles, item.isSelected, isLargeScreenWidth, theme.buttonHoveredBG, theme.border], ); const listItemWrapperStyle = useMemo( @@ -182,7 +188,7 @@ function ExpenseReportListItem({ ); const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: variables.componentBorderRadius, + borderRadius: isLargeScreenWidth ? 0 : variables.componentBorderRadius, shouldHighlight: item?.shouldAnimateInHighlight ?? false, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, @@ -232,7 +238,7 @@ function ExpenseReportListItem({ item={item} pressableStyle={listItemPressableStyle} wrapperStyle={listItemWrapperStyle} - containerStyle={[styles.mb2]} + containerStyle={!isLargeScreenWidth && [styles.mb2]} isFocused={isFocused} showTooltip={showTooltip} canSelectMultiple={canSelectMultiple} diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx index cd2eae503aaa..fbeaa6d4f671 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx @@ -153,7 +153,7 @@ function ExpenseReportListItemRow({ ), [CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE_TOTAL]: ( - + ), [CONST.SEARCH.TABLE_COLUMNS.NON_REIMBURSABLE_TOTAL]: ( - + )} diff --git a/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx b/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx index 8d267c8001b4..1219f357a9ea 100644 --- a/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx @@ -114,7 +114,6 @@ function MemberListItemHeader({ isIndeterminate={isIndeterminate} disabled={!!isDisabled || memberItem.isDisabledCheckbox} accessibilityLabel={translate('common.select')} - style={isLargeScreenWidth && styles.mr1} /> )} {!isLargeScreenWidth && ( diff --git a/src/components/Search/SearchList/ListItem/TaskListItem.tsx b/src/components/Search/SearchList/ListItem/TaskListItem.tsx index 5fd73e6ea1cc..d5585eda882e 100644 --- a/src/components/Search/SearchList/ListItem/TaskListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TaskListItem.tsx @@ -34,10 +34,16 @@ function TaskListItem({ styles.selectionListPressableItemWrapper, styles.pv3, styles.ph3, - // Removing background style because they are added to the parent OpacityView via animatedHighlightStyle styles.bgTransparent, item.isSelected && styles.activeComponentBG, styles.mh0, + isLargeScreenWidth && { + minHeight: variables.tableRowHeight, + borderRadius: 0, + paddingVertical: 8, + borderBottomWidth: 1, + borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, + }, ]; const listItemWrapperStyle = [ @@ -47,7 +53,7 @@ function TaskListItem({ ]; const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: variables.componentBorderRadius, + borderRadius: isLargeScreenWidth ? 0 : variables.componentBorderRadius, shouldHighlight: item?.shouldAnimateInHighlight ?? false, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, @@ -60,7 +66,7 @@ function TaskListItem({ item={item} pressableStyle={listItemPressableStyle} wrapperStyle={listItemWrapperStyle} - containerStyle={[styles.mb2]} + containerStyle={!isLargeScreenWidth && [styles.mb2]} isFocused={isFocused} isDisabled={isDisabled} showTooltip={showTooltip} diff --git a/src/components/Search/SearchList/ListItem/TextCell.tsx b/src/components/Search/SearchList/ListItem/TextCell.tsx index a0eca53db729..4f5c5857f39a 100644 --- a/src/components/Search/SearchList/ListItem/TextCell.tsx +++ b/src/components/Search/SearchList/ListItem/TextCell.tsx @@ -14,8 +14,8 @@ function TextCell({text = '', isLargeScreenWidth = true}: TextCellProps) { ); } diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx index fb672c4eddb9..49805c0b6a7c 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx @@ -176,7 +176,7 @@ function TransactionGroupListItem({ }; const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: variables.componentBorderRadius, + borderRadius: isLargeScreenWidth ? 0 : variables.componentBorderRadius, shouldHighlight: item?.shouldAnimateInHighlight ?? false, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, @@ -184,7 +184,17 @@ function TransactionGroupListItem({ const isItemSelected = isSelectAllChecked || item?.isSelected; - const pressableStyle = [styles.transactionGroupListItemStyle, isItemSelected && styles.activeComponentBG]; + const pressableStyle = [ + styles.transactionGroupListItemStyle, + isItemSelected && styles.activeComponentBG, + isLargeScreenWidth && { + minHeight: variables.tableRowHeight, + borderRadius: 0, + paddingVertical: 4, + borderBottomWidth: 1, + borderColor: isItemSelected ? theme.buttonHoveredBG : theme.border, + }, + ]; const StyleUtils = useStyleUtils(); const pressableRef = useRef(null); @@ -470,7 +480,7 @@ function TransactionGroupListItem({ isFocused && StyleUtils.getItemBackgroundColorStyle(!!isItemSelected, !!isFocused, !!item.isDisabled, theme.activeComponentBG, theme.hoverComponentBG), ]} onFocus={onFocus} - wrapperStyle={[styles.mb2, styles.mh5, animatedHighlightStyle, styles.userSelectNone]} + wrapperStyle={[!isLargeScreenWidth && styles.mb2, styles.mh5, animatedHighlightStyle, styles.userSelectNone]} > {({hovered}) => ( @@ -478,7 +488,7 @@ function TransactionGroupListItem({ isExpanded={isExpanded} header={getHeader(hovered)} onPress={onExpandIconPress} - expandButtonStyle={styles.pv4Half} + expandButtonStyle={styles.pv2} shouldShowToggleButton={isLargeScreenWidth} sentryLabel={CONST.SENTRY_LABEL.SEARCH.GROUP_EXPAND_TOGGLE} > diff --git a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx index cdb1c4863870..3e341b09cea2 100644 --- a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx @@ -102,11 +102,21 @@ function TransactionListItem({ styles.transactionListItemStyle, !isLargeScreenWidth && styles.pt3, item.isSelected && styles.activeComponentBG, - isLargeScreenWidth ? {...styles.flexRow, ...styles.justifyContentBetween, ...styles.alignItemsCenter} : {...styles.flexColumn, ...styles.alignItemsStretch}, + isLargeScreenWidth + ? { + ...styles.flexRow, + ...styles.justifyContentBetween, + ...styles.alignItemsCenter, + minHeight: variables.tableRowHeight, + borderRadius: 0, + borderBottomWidth: 1, + borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, + } + : {...styles.flexColumn, ...styles.alignItemsStretch}, ]; const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: variables.componentBorderRadius, + borderRadius: isLargeScreenWidth ? 0 : variables.componentBorderRadius, shouldHighlight: item?.shouldAnimateInHighlight ?? false, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, @@ -195,7 +205,7 @@ function TransactionListItem({ isFocused && StyleUtils.getItemBackgroundColorStyle(!!item.isSelected, !!isFocused, !!item.isDisabled, theme.activeComponentBG, theme.hoverComponentBG), ]} onFocus={onFocus} - wrapperStyle={[styles.mb2, styles.mh5, styles.flex1, animatedHighlightStyle, styles.userSelectNone]} + wrapperStyle={[!isLargeScreenWidth && styles.mb2, styles.mh5, styles.flex1, animatedHighlightStyle, styles.userSelectNone]} > {({hovered}) => ( <> @@ -229,7 +239,7 @@ function TransactionListItem({ taxAmountColumnSize={taxAmountColumnSize} shouldShowCheckbox={!!canSelectMultiple} checkboxSentryLabel={CONST.SENTRY_LABEL.SEARCH.TRANSACTION_LIST_ITEM_CHECKBOX} - style={[styles.p3, styles.pv2, shouldUseNarrowLayout ? styles.pt2 : {}]} + style={[styles.p3, styles.pv2, shouldUseNarrowLayout ? styles.pt2 : styles.noBorderRadius]} violations={transactionViolations} onArrowRightPress={() => onSelectRow(item, transactionPreviewData)} isHover={hovered} diff --git a/src/components/Search/SearchList/ListItem/UserInfoAndActionButtonRow.tsx b/src/components/Search/SearchList/ListItem/UserInfoAndActionButtonRow.tsx index 70648ad3004c..97608045d84e 100644 --- a/src/components/Search/SearchList/ListItem/UserInfoAndActionButtonRow.tsx +++ b/src/components/Search/SearchList/ListItem/UserInfoAndActionButtonRow.tsx @@ -64,7 +64,7 @@ function UserInfoAndActionButtonRow({ shouldUseArrowIcon={false} /> )} - + {tableHeaderVisible && ( - + {canSelectMultiple && ( + {shouldShowSelectedDropdown ? ( ) : ( diff --git a/src/components/Search/SearchTableHeader.tsx b/src/components/Search/SearchTableHeader.tsx index 60931f20c28f..1e05c00dc88c 100644 --- a/src/components/Search/SearchTableHeader.tsx +++ b/src/components/Search/SearchTableHeader.tsx @@ -530,8 +530,7 @@ function SearchTableHeader({ sortOrder={sortOrder} // In GroupBy views, disable flex expansion for Total columns so Expenses column gets more space shouldRemoveTotalColumnFlex={!!groupBy && !isExpenseReportView} - // Don't butt up against the 'select all' checkbox if present - containerStyles={canSelectMultiple && [styles.pl4]} + containerStyles={canSelectMultiple && [styles.pl3]} onSortPress={(columnName, order) => { if (columnName === CONST.SEARCH.TABLE_COLUMNS.COMMENTS) { return; diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 2280563fa322..a5713e851dfc 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -363,6 +363,7 @@ function BaseSelectionList({ shouldSyncFocus={!isTextInputFocusedRef.current && hasKeyBeenPressed.current} shouldDisableHoverStyle={shouldDisableHoverStyle} shouldShowRightCaret={shouldShowRightCaret} + isLastItem={index === data.length - 1} /> ); }; diff --git a/src/components/SelectionList/ListItem/ListItemRenderer.tsx b/src/components/SelectionList/ListItem/ListItemRenderer.tsx index 35ee72e3d89c..252991ea2b85 100644 --- a/src/components/SelectionList/ListItem/ListItemRenderer.tsx +++ b/src/components/SelectionList/ListItem/ListItemRenderer.tsx @@ -17,6 +17,7 @@ type ListItemRendererProps = Omit; titleContainerStyles?: StyleProp; shouldHighlightSelectedItem: boolean; + isLastItem?: boolean; }; function ListItemRenderer({ @@ -53,6 +54,7 @@ function ListItemRenderer({ shouldDisableHoverStyle, shouldShowRightCaret, errorRowStyles, + isLastItem, }: ListItemRendererProps) { const handleOnCheckboxPress = () => { if (isTransactionGroupListItemType(item)) { @@ -110,6 +112,7 @@ function ListItemRenderer({ shouldHighlightSelectedItem={shouldHighlightSelectedItem} shouldDisableHoverStyle={shouldDisableHoverStyle} shouldShowRightCaret={shouldShowRightCaret} + isLastItem={isLastItem} /> {item.footerContent && item.footerContent} diff --git a/src/components/SelectionList/ListItem/TableListItem.tsx b/src/components/SelectionList/ListItem/TableListItem.tsx index ec40a586e273..753034c655ca 100644 --- a/src/components/SelectionList/ListItem/TableListItem.tsx +++ b/src/components/SelectionList/ListItem/TableListItem.tsx @@ -4,9 +4,11 @@ import Checkbox from '@components/Checkbox'; import ReportActionAvatars from '@components/ReportActionAvatars'; import TextWithTooltip from '@components/TextWithTooltip'; import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import variables from '@styles/variables'; import BaseListItem from './BaseListItem'; import type {ListItem, TableListItemProps} from './types'; @@ -27,13 +29,15 @@ function TableListItem({ shouldUseDefaultRightHandSideCheckmark, shouldShowRightCaret, errorRowStyles, + isLastItem, }: TableListItemProps) { const styles = useThemeStyles(); const theme = useTheme(); const StyleUtils = useStyleUtils(); + const {isLargeScreenWidth} = useResponsiveLayout(); const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: styles.selectionListPressableItemWrapper.borderRadius, + borderRadius: isLargeScreenWidth ? 0 : styles.selectionListPressableItemWrapper.borderRadius, shouldHighlight: !!item.shouldAnimateInHighlight, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, @@ -50,20 +54,32 @@ function TableListItem({ } }; + const compactRowStyle = isLargeScreenWidth + ? { + minHeight: variables.tableRowHeight, + borderRadius: 0, + paddingVertical: 8, + paddingHorizontal: 12, + borderBottomWidth: isLastItem ? 0 : 1, + borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, + ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {}), + } + : {}; + return ( = ListItemProps< type UserListItemProps = ListItemProps & ForwardedFSClassProps; -type TableListItemProps = ListItemProps; +type TableListItemProps = ListItemProps & { + /** Whether this is the last item in the list (for border radius on desktop) */ + isLastItem?: boolean; +}; type InviteMemberListItemProps = UserListItemProps & { /** Whether product training tooltips can be displayed */ diff --git a/src/components/TransactionItemRow/DataCells/CategoryCell.tsx b/src/components/TransactionItemRow/DataCells/CategoryCell.tsx index 6a7cacaa6814..80e8801c9549 100644 --- a/src/components/TransactionItemRow/DataCells/CategoryCell.tsx +++ b/src/components/TransactionItemRow/DataCells/CategoryCell.tsx @@ -23,8 +23,8 @@ function CategoryCell({shouldUseNarrowLayout, shouldShowTooltip, transactionItem ); } diff --git a/src/components/TransactionItemRow/DataCells/MerchantCell.tsx b/src/components/TransactionItemRow/DataCells/MerchantCell.tsx index 7aec8d8fdfda..924aa5168854 100644 --- a/src/components/TransactionItemRow/DataCells/MerchantCell.tsx +++ b/src/components/TransactionItemRow/DataCells/MerchantCell.tsx @@ -27,8 +27,8 @@ function MerchantOrDescriptionCell({ ); } diff --git a/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx b/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx index b833a4ec6d92..3d38388fefe1 100644 --- a/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx +++ b/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx @@ -56,7 +56,7 @@ function ReceiptCell({transactionItem, isSelected, style}: {transactionItem: Tra return ( ); } diff --git a/src/components/TransactionItemRow/DataCells/TypeCell.tsx b/src/components/TransactionItemRow/DataCells/TypeCell.tsx index 03d9ff0cf769..c93aebd28477 100644 --- a/src/components/TransactionItemRow/DataCells/TypeCell.tsx +++ b/src/components/TransactionItemRow/DataCells/TypeCell.tsx @@ -83,8 +83,8 @@ function TypeCell({transactionItem, shouldUseNarrowLayout, shouldShowTooltip}: T diff --git a/src/components/TransactionItemRow/index.tsx b/src/components/TransactionItemRow/index.tsx index 4ce29ff74e99..4855f1272472 100644 --- a/src/components/TransactionItemRow/index.tsx +++ b/src/components/TransactionItemRow/index.tsx @@ -780,7 +780,6 @@ function TransactionItemRow({ }} accessibilityLabel={CONST.ROLE.CHECKBOX} isChecked={isSelected} - style={styles.mr1} wrapperStyle={styles.justifyContentCenter} sentryLabel={checkboxSentryLabel} /> diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index c02e5adfab32..1bc8ba2656b6 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -1498,14 +1498,6 @@ function shouldShowYear( if (exportedAction?.created && DateUtils.doesDateBelongToAPastYear(exportedAction.created)) { result.shouldShowYearExported = true; } - } else if (!checkOnlyReports && isReportActionEntry(key)) { - const item = data[key]; - for (const action of Object.values(item)) { - const date = action.created; - if (DateUtils.doesDateBelongToAPastYear(date)) { - result.shouldShowYearCreated = true; - } - } } else if (isReportEntry(key)) { const item = data[key]; @@ -4625,18 +4617,22 @@ function getTableMinWidth(columns: SearchColumnType[]) { let minWidth = 24; for (const column of columns) { - if (column === CONST.SEARCH.TABLE_COLUMNS.RECEIPT || column === CONST.SEARCH.TABLE_COLUMNS.COMMENTS) { + if (column === CONST.SEARCH.TABLE_COLUMNS.COMMENTS) { minWidth += 36; + } else if (column === CONST.SEARCH.TABLE_COLUMNS.RECEIPT) { + minWidth += 28; } else if (column === CONST.SEARCH.TABLE_COLUMNS.AVATAR) { - minWidth += 40; - } else if (column === CONST.SEARCH.TABLE_COLUMNS.STATUS || column === CONST.SEARCH.TABLE_COLUMNS.ACTION) { + minWidth += 28; + } else if (column === CONST.SEARCH.TABLE_COLUMNS.STATUS) { minWidth += 80; + } else if (column === CONST.SEARCH.TABLE_COLUMNS.ACTION) { + minWidth += 68; } else if (column === CONST.SEARCH.TABLE_COLUMNS.DATE) { minWidth += 48; } else if (column === CONST.SEARCH.TABLE_COLUMNS.SUBMITTED || column === CONST.SEARCH.TABLE_COLUMNS.APPROVED || column === CONST.SEARCH.TABLE_COLUMNS.POSTED) { minWidth += 72; } else if (column === CONST.SEARCH.TABLE_COLUMNS.TYPE) { - minWidth += 20; + minWidth += 16; } else if (column === CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE || column === CONST.SEARCH.TABLE_COLUMNS.BILLABLE) { minWidth += 80; } else { diff --git a/src/styles/index.ts b/src/styles/index.ts index 002af4179f91..a35b44ff2c2b 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3480,13 +3480,17 @@ const staticStyles = (theme: ThemeColors) => }, searchListHeaderContainerStyle: { - width: '100%', flexDirection: 'row', alignItems: 'center', ...userSelect.userSelectNone, - paddingBottom: 12, - backgroundColor: theme.appBG, + paddingBottom: 8, + backgroundColor: theme.highlightBG, justifyContent: 'flex-start', + borderTopLeftRadius: 8, + borderTopRightRadius: 8, + borderBottomWidth: 1, + borderColor: theme.border, + minHeight: 40, }, groupSearchListTableContainerStyle: { @@ -4754,8 +4758,8 @@ const staticStyles = (theme: ThemeColors) => }, listTableHeader: { - paddingVertical: 12, - paddingHorizontal: 32, + paddingVertical: 8, + paddingHorizontal: 12, }, tableHeaderIconSpacing: { diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 9ee72a3f1386..4882d0840661 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1800,11 +1800,13 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ let columnWidth; switch (columnName) { case CONST.SEARCH.TABLE_COLUMNS.COMMENTS: - case CONST.SEARCH.TABLE_COLUMNS.RECEIPT: columnWidth = {...getWidthStyle(variables.w36), ...styles.alignItemsCenter}; break; + case CONST.SEARCH.TABLE_COLUMNS.RECEIPT: + columnWidth = {...getWidthStyle(variables.w28), ...styles.alignItemsCenter}; + break; case CONST.SEARCH.TABLE_COLUMNS.AVATAR: - columnWidth = {...getWidthStyle(variables.w40), ...styles.alignItemsCenter}; + columnWidth = {...getWidthStyle(variables.w28), ...styles.alignItemsCenter}; break; case CONST.SEARCH.TABLE_COLUMNS.STATUS: columnWidth = {...getWidthStyle(variables.w80), ...styles.alignItemsCenter}; @@ -1857,7 +1859,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ columnWidth = {...getWidthStyle(isAmountColumnWide ? variables.w130 : variables.w96), ...(!shouldRemoveTotalColumnFlex && styles.flex1), ...styles.alignItemsEnd}; break; case CONST.SEARCH.TABLE_COLUMNS.TYPE: - columnWidth = {...getWidthStyle(variables.w20), ...styles.alignItemsCenter}; + columnWidth = {...getWidthStyle(variables.w16), ...styles.alignItemsCenter}; break; case CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE: case CONST.SEARCH.TABLE_COLUMNS.BILLABLE: @@ -1867,7 +1869,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ columnWidth = {...getWidthStyle(variables.w92), ...styles.flex1}; break; case CONST.SEARCH.TABLE_COLUMNS.ACTION: - columnWidth = {...getWidthStyle(variables.w80), ...styles.alignItemsCenter}; + columnWidth = {...getWidthStyle(variables.w68), ...styles.alignItemsCenter}; break; case CONST.SEARCH.TABLE_COLUMNS.EXPORTED_TO: columnWidth = {...getWidthStyle(variables.w72), ...styles.alignItemsCenter}; diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 0510b832e12e..9d7ad323010e 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -120,6 +120,7 @@ export default { gutterWidth: 12, optionRowHeight: 64, optionRowHeightCompact: 52, + tableRowHeight: 56, sectionMenuItemHeight: 52, sectionMenuItemHeightCompact: 44, optionsListSectionHeaderHeight: getValueUsingPixelRatio(32, 38), @@ -352,6 +353,7 @@ export default { h20: 20, h28: 28, + h32: 32, h36: 36, h40: 40, h70: 70, @@ -366,6 +368,7 @@ export default { w44: 44, w46: 46, w52: 52, + w68: 68, w72: 72, w80: 80, w92: 92, From 300463e897c883a14bb75c979a720175263dd6ab Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Wed, 25 Mar 2026 03:02:25 +0530 Subject: [PATCH 02/46] Refine compact table rows with proper last-item border radius, 52px min-height, and bottom border separation Signed-off-by: krishna2323 --- .../ListItem/ExpenseReportListItem.tsx | 6 ++++-- .../ListItem/MemberListItemHeader.tsx | 1 + .../SearchList/ListItem/TaskListItem.tsx | 4 +++- .../ListItem/TransactionGroupListExpanded.tsx | 13 ++++++++++--- .../ListItem/TransactionGroupListItem.tsx | 18 ++++++++++-------- .../ListItem/TransactionListItem.tsx | 4 +++- src/components/SelectionList/ListItem/types.ts | 3 +++ src/components/TransactionItemRow/index.tsx | 2 +- src/styles/variables.ts | 2 +- 9 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx index 0a8cfe5d1861..4f756f2512dd 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx @@ -48,6 +48,7 @@ function ExpenseReportListItem({ isDEWBetaEnabled, lastPaymentMethod, personalPolicyID, + isLastItem, }: ExpenseReportListItemProps) { const reportItem = item as unknown as ExpenseReportListItemType; const styles = useThemeStyles(); @@ -171,11 +172,12 @@ function ExpenseReportListItem({ minHeight: variables.tableRowHeight, borderRadius: 0, paddingVertical: 8, - borderBottomWidth: 1, + borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, + ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {}), }, ], - [styles, item.isSelected, isLargeScreenWidth, theme.buttonHoveredBG, theme.border], + [styles, item.isSelected, isLargeScreenWidth, theme.buttonHoveredBG, theme.border, isLastItem], ); const listItemWrapperStyle = useMemo( diff --git a/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx b/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx index 1219f357a9ea..06232c5e671f 100644 --- a/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx @@ -150,6 +150,7 @@ function MemberListItemHeader({ type={CONST.ICON_TYPE_AVATAR} name={formattedDisplayName} avatarID={memberItem.accountID} + size={CONST.AVATAR_SIZE.SMALL} /> diff --git a/src/components/Search/SearchList/ListItem/TaskListItem.tsx b/src/components/Search/SearchList/ListItem/TaskListItem.tsx index d5585eda882e..500ba6de27bc 100644 --- a/src/components/Search/SearchList/ListItem/TaskListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TaskListItem.tsx @@ -22,6 +22,7 @@ function TaskListItem({ onLongPressRow, shouldSyncFocus, allReports, + isLastItem, }: TaskListItemProps) { const taskItem = item as unknown as TaskListItemType; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${taskItem?.parentReportID}`]; @@ -41,8 +42,9 @@ function TaskListItem({ minHeight: variables.tableRowHeight, borderRadius: 0, paddingVertical: 8, - borderBottomWidth: 1, + borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, + ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {}), }, ]; diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx index 940cf65530b2..319b21d8660d 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx @@ -25,6 +25,7 @@ import {getReportOrDraftReport} from '@libs/ReportUtils'; import {createAndOpenSearchTransactionThread, getColumnsToShow, getTableMinWidth} from '@libs/SearchUIUtils'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {getTransactionViolations} from '@libs/TransactionUtils'; +import variables from '@styles/variables'; import type {TransactionPreviewData} from '@userActions/Search'; import {setActiveTransactionIDs} from '@userActions/TransactionThreadNavigation'; import CONST from '@src/CONST'; @@ -190,7 +191,7 @@ function TransactionGroupListExpanded({ const content = ( {isLargeScreenWidth && ( - + ({ )} {visibleTransactions.map((transaction, index) => { - const shouldShowBottomBorder = !isLastTransaction(index) && !isLargeScreenWidth; + const shouldShowBottomBorder = !isLastTransaction(index); const exportedReportActions = Object.values(transactionsSnapshot?.data?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${transaction?.reportID}`] ?? {}); const transactionRow = ( @@ -230,7 +231,13 @@ function TransactionGroupListExpanded({ onButtonPress={() => { openReportInRHP(transaction); }} - style={[styles.noBorderRadius, styles.p3, isLargeScreenWidth && [styles.pv1Half], styles.flex1]} + style={[ + styles.noBorderRadius, + styles.p3, + isLargeScreenWidth && [styles.pv1Half, {minHeight: variables.tableRowHeight}], + styles.flex1, + isLargeScreenWidth && isLastTransaction(index) && {borderBottomLeftRadius: 8, borderBottomRightRadius: 8}, + ]} isReportItemChild isInSingleTransactionReport={isInSingleTransactionReport} shouldShowBottomBorder={shouldShowBottomBorder} diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx index 49805c0b6a7c..9ca671d5c436 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx @@ -79,6 +79,7 @@ function TransactionGroupListItem({ isDEWBetaEnabled, lastPaymentMethod, personalPolicyID, + isLastItem, }: TransactionGroupListItemProps) { const groupItem = item as unknown as TransactionGroupListItemType; @@ -186,14 +187,8 @@ function TransactionGroupListItem({ const pressableStyle = [ styles.transactionGroupListItemStyle, + isLargeScreenWidth && {minHeight: variables.tableRowHeight, borderRadius: 0, paddingVertical: 4, ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {})}, isItemSelected && styles.activeComponentBG, - isLargeScreenWidth && { - minHeight: variables.tableRowHeight, - borderRadius: 0, - paddingVertical: 4, - borderBottomWidth: 1, - borderColor: isItemSelected ? theme.buttonHoveredBG : theme.border, - }, ]; const StyleUtils = useStyleUtils(); @@ -480,7 +475,14 @@ function TransactionGroupListItem({ isFocused && StyleUtils.getItemBackgroundColorStyle(!!isItemSelected, !!isFocused, !!item.isDisabled, theme.activeComponentBG, theme.hoverComponentBG), ]} onFocus={onFocus} - wrapperStyle={[!isLargeScreenWidth && styles.mb2, styles.mh5, animatedHighlightStyle, styles.userSelectNone]} + wrapperStyle={[ + !isLargeScreenWidth && styles.mb2, + styles.mh5, + animatedHighlightStyle, + styles.userSelectNone, + isLargeScreenWidth && {borderRadius: 0, borderBottomWidth: isLastItem ? 0 : 1, borderColor: isItemSelected ? theme.buttonHoveredBG : theme.border}, + isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: 8, borderBottomRightRadius: 8}, + ]} > {({hovered}) => ( diff --git a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx index 3e341b09cea2..b679f23dc79b 100644 --- a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx @@ -53,6 +53,7 @@ function TransactionListItem({ isDEWBetaEnabled, lastPaymentMethod, personalPolicyID, + isLastItem, }: TransactionListItemProps) { const transactionItem = item as unknown as TransactionListItemType; const styles = useThemeStyles(); @@ -109,8 +110,9 @@ function TransactionListItem({ ...styles.alignItemsCenter, minHeight: variables.tableRowHeight, borderRadius: 0, - borderBottomWidth: 1, + borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, + ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {}), } : {...styles.flexColumn, ...styles.alignItemsStretch}, ]; diff --git a/src/components/SelectionList/ListItem/types.ts b/src/components/SelectionList/ListItem/types.ts index 32a1d3ec67b8..6bf9f0f34bfa 100644 --- a/src/components/SelectionList/ListItem/types.ts +++ b/src/components/SelectionList/ListItem/types.ts @@ -294,6 +294,9 @@ type ListItemProps = CommonListItemProps & { /** Whether the network is offline */ isOffline?: boolean; + + /** Whether this is the last item in the list (for border radius on desktop) */ + isLastItem?: boolean; }; type ValidListItem = diff --git a/src/components/TransactionItemRow/index.tsx b/src/components/TransactionItemRow/index.tsx index 4855f1272472..c69046da4bf9 100644 --- a/src/components/TransactionItemRow/index.tsx +++ b/src/components/TransactionItemRow/index.tsx @@ -800,7 +800,7 @@ function TransactionItemRow({ onArrowRightPress?.()} - style={[styles.p3Half, styles.pl0half, styles.pr0half, styles.justifyContentCenter, styles.alignItemsEnd]} + style={[styles.pv2, styles.justifyContentCenter, styles.alignItemsEnd]} accessibilityRole={CONST.ROLE.BUTTON} accessibilityLabel={CONST.ROLE.BUTTON} sentryLabel={CONST.SENTRY_LABEL.TRANSACTION_ITEM_ROW.ARROW_RIGHT} diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 9d7ad323010e..960cbfc78d31 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -120,7 +120,7 @@ export default { gutterWidth: 12, optionRowHeight: 64, optionRowHeightCompact: 52, - tableRowHeight: 56, + tableRowHeight: 52, sectionMenuItemHeight: 52, sectionMenuItemHeightCompact: 44, optionsListSectionHeaderHeight: getValueUsingPixelRatio(32, 38), From 3cc64560f9f91154dbcc251855770ac779db300a Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Wed, 25 Mar 2026 04:31:31 +0530 Subject: [PATCH 03/46] Fix last row rounded corners on search expenses, reports, and tasks pages Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/ExpenseReportListItem.tsx | 6 +++++- .../Search/SearchList/ListItem/TaskListItem.tsx | 6 +++++- .../Search/SearchList/ListItem/TransactionListItem.tsx | 9 ++++++++- src/components/Search/SearchList/index.tsx | 1 + 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx index 4f756f2512dd..b166ac9b1991 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx @@ -251,7 +251,11 @@ function ExpenseReportListItem({ onLongPressRow={onLongPressRow} shouldSyncFocus={shouldSyncFocus} hoverStyle={item.isSelected && styles.activeComponentBG} - pressableWrapperStyle={[styles.mh5, animatedHighlightStyle]} + pressableWrapperStyle={[ + styles.mh5, + animatedHighlightStyle, + isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius}, + ]} accessible={false} shouldShowRightCaret={false} shouldUseDefaultRightHandSideCheckmark={false} diff --git a/src/components/Search/SearchList/ListItem/TaskListItem.tsx b/src/components/Search/SearchList/ListItem/TaskListItem.tsx index 500ba6de27bc..052cf148780a 100644 --- a/src/components/Search/SearchList/ListItem/TaskListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TaskListItem.tsx @@ -80,7 +80,11 @@ function TaskListItem({ onLongPressRow={onLongPressRow} shouldSyncFocus={shouldSyncFocus} hoverStyle={item.isSelected && styles.activeComponentBG} - pressableWrapperStyle={[styles.mh5, animatedHighlightStyle]} + pressableWrapperStyle={[ + styles.mh5, + animatedHighlightStyle, + isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius}, + ]} forwardedFSClass={fsClass} > ({ isFocused && StyleUtils.getItemBackgroundColorStyle(!!item.isSelected, !!isFocused, !!item.isDisabled, theme.activeComponentBG, theme.hoverComponentBG), ]} onFocus={onFocus} - wrapperStyle={[!isLargeScreenWidth && styles.mb2, styles.mh5, styles.flex1, animatedHighlightStyle, styles.userSelectNone]} + wrapperStyle={[ + !isLargeScreenWidth && styles.mb2, + styles.mh5, + styles.flex1, + animatedHighlightStyle, + styles.userSelectNone, + isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius}, + ]} > {({hovered}) => ( <> diff --git a/src/components/Search/SearchList/index.tsx b/src/components/Search/SearchList/index.tsx index fa7f52d926c9..d8b011c6cc7c 100644 --- a/src/components/Search/SearchList/index.tsx +++ b/src/components/Search/SearchList/index.tsx @@ -461,6 +461,7 @@ function SearchList({ onFocus={onFocus} newTransactionID={newTransactionID} keyForList={item.keyForList} + isLastItem={index === data.length - 1} /> ); From cf7d5e6cd88db8d66ef7fded8a16b15e6e2c89f6 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Wed, 25 Mar 2026 04:50:30 +0530 Subject: [PATCH 04/46] Update search skeleton to match compact table layout with proper row height and border separators Signed-off-by: krishna2323 --- src/components/Search/index.tsx | 3 +- .../Skeletons/ItemListSkeletonView.tsx | 22 ++- .../Skeletons/SearchRowSkeleton.tsx | 132 ++++++++++-------- 3 files changed, 96 insertions(+), 61 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 50a224c71a1b..3c9fc90464be 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -433,7 +433,7 @@ function Search({ (hasErrors && searchRequestResponseStatusCode === null) || isCardFeedsLoading)); - const shouldShowLoadingMoreItems = !shouldShowLoadingState && searchResults?.search?.isLoading && searchResults?.search?.offset > 0; + const shouldShowLoadingMoreItems = true; const loadMoreSkeletonReasonAttributes = useMemo( () => ({ @@ -1475,6 +1475,7 @@ function Search({ ) : undefined diff --git a/src/components/Skeletons/ItemListSkeletonView.tsx b/src/components/Skeletons/ItemListSkeletonView.tsx index 779a18c66956..2d774a3c425b 100644 --- a/src/components/Skeletons/ItemListSkeletonView.tsx +++ b/src/components/Skeletons/ItemListSkeletonView.tsx @@ -12,6 +12,7 @@ type ListItemSkeletonProps = { fixedNumItems?: number; gradientOpacityEnabled?: boolean; itemViewStyle?: StyleProp; + itemContainerStyle?: StyleProp; itemViewHeight?: number; speed?: number; style?: StyleProp; @@ -37,6 +38,7 @@ function ItemListSkeletonView({ fixedNumItems, gradientOpacityEnabled = false, itemViewStyle = {}, + itemContainerStyle, itemViewHeight = CONST.LHN_SKELETON_VIEW_ITEM_HEIGHT, speed, style, @@ -70,7 +72,7 @@ function ItemListSkeletonView({ const items = []; for (let i = 0; i < numItems; i++) { const opacity = gradientOpacityEnabled ? 1 - i / (numItems - 1) : 1; - items.push( + const loader = ( {renderSkeletonItem({itemIndex: i})} - , + ); + + if (itemContainerStyle) { + const isLastItem = i === numItems - 1; + items.push( + + {loader} + , + ); + } else { + items.push(loader); + } } return items; - }, [numItems, shouldAnimate, theme, themeStyles, renderSkeletonItem, gradientOpacityEnabled, itemViewHeight, itemViewStyle, speed]); + }, [numItems, shouldAnimate, theme, themeStyles, renderSkeletonItem, gradientOpacityEnabled, itemViewHeight, itemViewStyle, speed, itemContainerStyle]); return ( ; reasonAttributes: SkeletonSpanReasonAttributes; + isLoadMore?: boolean; }; const barHeight = 8; @@ -36,7 +38,8 @@ const centralPanePadding = 40; // 80 is the width of the button on the right side const rightButtonWidth = 80; -function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacityEnabled = false, containerStyle, reasonAttributes}: SearchRowSkeletonProps) { +function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacityEnabled = false, containerStyle, reasonAttributes, isLoadMore = false}: SearchRowSkeletonProps) { + const theme = useTheme(); const styles = useThemeStyles(); const {windowWidth} = useWindowDimensions(); const {shouldUseNarrowLayout, isLargeScreenWidth} = useResponsiveLayout(); @@ -116,62 +119,77 @@ function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacity return ( - ( - <> - - - - {isLargeScreenWidth && ( - <> - - - - - )} - - - - - - )} - /> + + ( + <> + + + + {isLargeScreenWidth && ( + <> + + + + + )} + + + + + + )} + /> + ); } From 099536cb7721048824a600203ea838a8ecaa3e49 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Wed, 25 Mar 2026 04:58:13 +0530 Subject: [PATCH 05/46] fix shouldShowLoadingMoreItems condition. Signed-off-by: krishna2323 --- src/components/Search/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 3c9fc90464be..6a58dca8454f 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -433,7 +433,7 @@ function Search({ (hasErrors && searchRequestResponseStatusCode === null) || isCardFeedsLoading)); - const shouldShowLoadingMoreItems = true; + const shouldShowLoadingMoreItems = !shouldShowLoadingState && searchResults?.search?.isLoading && searchResults?.search?.offset > 0; const loadMoreSkeletonReasonAttributes = useMemo( () => ({ From 5343876a87aca135c6f2e5ba61fa15fbf7ae46be Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Wed, 25 Mar 2026 05:04:07 +0530 Subject: [PATCH 06/46] Replace hardcoded border-radius values with variables.componentBorderRadius and remove duplicate type Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/ExpenseReportListItem.tsx | 2 +- .../Search/SearchList/ListItem/TaskListItem.tsx | 2 +- .../SearchList/ListItem/TransactionGroupListExpanded.tsx | 3 ++- .../SearchList/ListItem/TransactionGroupListItem.tsx | 9 +++++++-- .../Search/SearchList/ListItem/TransactionListItem.tsx | 2 +- src/components/Search/index.tsx | 1 - src/components/SelectionList/ListItem/TableListItem.tsx | 2 +- src/components/SelectionList/ListItem/types.ts | 5 +---- src/styles/index.ts | 4 ++-- 9 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx index b166ac9b1991..82246af44eac 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx @@ -174,7 +174,7 @@ function ExpenseReportListItem({ paddingVertical: 8, borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, - ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {}), + ...(isLastItem ? {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius} : {}), }, ], [styles, item.isSelected, isLargeScreenWidth, theme.buttonHoveredBG, theme.border, isLastItem], diff --git a/src/components/Search/SearchList/ListItem/TaskListItem.tsx b/src/components/Search/SearchList/ListItem/TaskListItem.tsx index 052cf148780a..58245a0c0140 100644 --- a/src/components/Search/SearchList/ListItem/TaskListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TaskListItem.tsx @@ -44,7 +44,7 @@ function TaskListItem({ paddingVertical: 8, borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, - ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {}), + ...(isLastItem ? {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius} : {}), }, ]; diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx index 319b21d8660d..c29f6abed4c7 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx @@ -236,7 +236,8 @@ function TransactionGroupListExpanded({ styles.p3, isLargeScreenWidth && [styles.pv1Half, {minHeight: variables.tableRowHeight}], styles.flex1, - isLargeScreenWidth && isLastTransaction(index) && {borderBottomLeftRadius: 8, borderBottomRightRadius: 8}, + isLargeScreenWidth && + isLastTransaction(index) && {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius}, ]} isReportItemChild isInSingleTransactionReport={isInSingleTransactionReport} diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx index 9ca671d5c436..4088e23035b7 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx @@ -187,7 +187,12 @@ function TransactionGroupListItem({ const pressableStyle = [ styles.transactionGroupListItemStyle, - isLargeScreenWidth && {minHeight: variables.tableRowHeight, borderRadius: 0, paddingVertical: 4, ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {})}, + isLargeScreenWidth && { + minHeight: variables.tableRowHeight, + borderRadius: 0, + paddingVertical: 4, + ...(isLastItem ? {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius} : {}), + }, isItemSelected && styles.activeComponentBG, ]; @@ -481,7 +486,7 @@ function TransactionGroupListItem({ animatedHighlightStyle, styles.userSelectNone, isLargeScreenWidth && {borderRadius: 0, borderBottomWidth: isLastItem ? 0 : 1, borderColor: isItemSelected ? theme.buttonHoveredBG : theme.border}, - isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: 8, borderBottomRightRadius: 8}, + isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius}, ]} > {({hovered}) => ( diff --git a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx index c86a4bf8892c..92ac9c670fcd 100644 --- a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx @@ -112,7 +112,7 @@ function TransactionListItem({ borderRadius: 0, borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, - ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {}), + ...(isLastItem ? {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius} : {}), } : {...styles.flexColumn, ...styles.alignItemsStretch}, ]; diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 6a58dca8454f..50a224c71a1b 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1475,7 +1475,6 @@ function Search({ ) : undefined diff --git a/src/components/SelectionList/ListItem/TableListItem.tsx b/src/components/SelectionList/ListItem/TableListItem.tsx index 753034c655ca..b7420cabe88f 100644 --- a/src/components/SelectionList/ListItem/TableListItem.tsx +++ b/src/components/SelectionList/ListItem/TableListItem.tsx @@ -62,7 +62,7 @@ function TableListItem({ paddingHorizontal: 12, borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, - ...(isLastItem ? {borderBottomLeftRadius: 8, borderBottomRightRadius: 8} : {}), + ...(isLastItem ? {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius} : {}), } : {}; diff --git a/src/components/SelectionList/ListItem/types.ts b/src/components/SelectionList/ListItem/types.ts index 6bf9f0f34bfa..246795301c62 100644 --- a/src/components/SelectionList/ListItem/types.ts +++ b/src/components/SelectionList/ListItem/types.ts @@ -398,10 +398,7 @@ type SpendCategorySelectorListItemProps = ListItemProps< type UserListItemProps = ListItemProps & ForwardedFSClassProps; -type TableListItemProps = ListItemProps & { - /** Whether this is the last item in the list (for border radius on desktop) */ - isLastItem?: boolean; -}; +type TableListItemProps = ListItemProps; type InviteMemberListItemProps = UserListItemProps & { /** Whether product training tooltips can be displayed */ diff --git a/src/styles/index.ts b/src/styles/index.ts index a35b44ff2c2b..582797e7dd57 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3486,8 +3486,8 @@ const staticStyles = (theme: ThemeColors) => paddingBottom: 8, backgroundColor: theme.highlightBG, justifyContent: 'flex-start', - borderTopLeftRadius: 8, - borderTopRightRadius: 8, + borderTopLeftRadius: variables.componentBorderRadius, + borderTopRightRadius: variables.componentBorderRadius, borderBottomWidth: 1, borderColor: theme.border, minHeight: 40, From 362e5371fa919b4b00ebf72589a962d3c26c3a5c Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 26 Mar 2026 01:33:12 +0530 Subject: [PATCH 07/46] Address PR review feedback: 56px row height, avatar sizing, column widths, skeleton alignment, and spinner centering Signed-off-by: krishna2323 --- .../SearchList/ListItem/CardListItem.tsx | 6 ++- .../ListItem/CardListItemHeader.tsx | 1 + .../ListItem/ExpenseReportListItemRow.tsx | 6 +-- .../ListItem/TransactionGroupListExpanded.tsx | 12 +----- .../Search/SearchList/ListItem/types.ts | 3 ++ .../Skeletons/SearchRowSkeleton.tsx | 39 ++++++++++--------- src/libs/SearchUIUtils.ts | 11 +++++- src/styles/variables.ts | 2 +- 8 files changed, 45 insertions(+), 35 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/CardListItem.tsx b/src/components/Search/SearchList/ListItem/CardListItem.tsx index 112eb6823c2a..375aab0c4de7 100644 --- a/src/components/Search/SearchList/ListItem/CardListItem.tsx +++ b/src/components/Search/SearchList/ListItem/CardListItem.tsx @@ -12,6 +12,7 @@ import TextWithTooltip from '@components/TextWithTooltip'; import UserDetailsTooltip from '@components/UserDetailsTooltip'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -49,6 +50,7 @@ function CardListItem({ const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const theme = useTheme(); + const {isLargeScreenWidth} = useResponsiveLayout(); const handleCheckboxPress = useCallback(() => { if (onCheckboxPress) { @@ -103,7 +105,9 @@ function CardListItem({ > ({ subscriptAvatarBorderColor={backgroundColor} noRightMarginOnSubscriptContainer accountIDs={[cardItem.accountID]} + size={CONST.AVATAR_SIZE.SMALL} /> diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx index fbeaa6d4f671..6f40a5dc04a2 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx @@ -153,7 +153,7 @@ function ExpenseReportListItemRow({ ), [CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE_TOTAL]: ( - + ), [CONST.SEARCH.TABLE_COLUMNS.NON_REIMBURSABLE_TOTAL]: ( - + ), [CONST.SEARCH.TABLE_COLUMNS.TOTAL]: ( - + ({ onButtonPress={() => { openReportInRHP(transaction); }} - style={[ - styles.noBorderRadius, - styles.p3, - isLargeScreenWidth && [styles.pv1Half, {minHeight: variables.tableRowHeight}], - styles.flex1, - isLargeScreenWidth && - isLastTransaction(index) && {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius}, - ]} + style={[styles.noBorderRadius, styles.p3, isLargeScreenWidth && [styles.pv1, {minHeight: variables.tableRowHeight}], styles.flex1]} isReportItemChild isInSingleTransactionReport={isInSingleTransactionReport} shouldShowBottomBorder={shouldShowBottomBorder} @@ -288,11 +281,10 @@ function TransactionGroupListExpanded({ )} {shouldDisplayLoadingIndicator && ( - + diff --git a/src/components/Search/SearchList/ListItem/types.ts b/src/components/Search/SearchList/ListItem/types.ts index 490a3203e903..b3525b27a075 100644 --- a/src/components/Search/SearchList/ListItem/types.ts +++ b/src/components/Search/SearchList/ListItem/types.ts @@ -256,6 +256,9 @@ type TransactionReportGroupListItemType = TransactionGroupListItemType & {groupe /** Pre-computed reimbursable spend amount */ reimbursableSpend?: number; + /** Whether the amount column should use the wide layout */ + isAmountColumnWide?: boolean; + /** Pre-computed flag indicating whether all transactions are scanning */ isAllScanning?: boolean; diff --git a/src/components/Skeletons/SearchRowSkeleton.tsx b/src/components/Skeletons/SearchRowSkeleton.tsx index ed3032104695..7de124b1c5c7 100644 --- a/src/components/Skeletons/SearchRowSkeleton.tsx +++ b/src/components/Skeletons/SearchRowSkeleton.tsx @@ -19,7 +19,6 @@ type SearchRowSkeletonProps = { gradientOpacityEnabled?: boolean; containerStyle?: StyleProp; reasonAttributes: SkeletonSpanReasonAttributes; - isLoadMore?: boolean; }; const barHeight = 8; @@ -29,16 +28,19 @@ const leftPaneWidth = variables.sideBarWithLHBWidth + variables.navigationTabBar // 12 is the gap between the element and the right button const gapWidth = 12; -// 80 is the width of the element itself -const rightSideElementWidth = 80; +// 68 is the width of the action button +const rightSideElementWidth = 68; -// 24 is the padding of the central pane summing two sides +// 40 is the padding of the central pane summing two sides const centralPanePadding = 40; -// 80 is the width of the button on the right side -const rightButtonWidth = 80; +// 16 is the width of the right arrow icon + padding +const rightArrowWidth = 28; -function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacityEnabled = false, containerStyle, reasonAttributes, isLoadMore = false}: SearchRowSkeletonProps) { +// 68 is the width of the action button +const rightButtonWidth = 68; + +function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacityEnabled = false, containerStyle, reasonAttributes}: SearchRowSkeletonProps) { const theme = useTheme(); const styles = useThemeStyles(); const {windowWidth} = useWindowDimensions(); @@ -126,7 +128,8 @@ function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacity { borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius, - ...(isLoadMore ? {} : {borderTopLeftRadius: variables.componentBorderRadius, borderTopRightRadius: variables.componentBorderRadius}), + borderTopWidth: 1, + borderColor: theme.border, }, ]} > @@ -140,31 +143,31 @@ function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacity renderSkeletonItem={() => ( <> {isLargeScreenWidth && ( <> @@ -173,17 +176,17 @@ function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacity diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 1bc8ba2656b6..e39675284f13 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -1414,6 +1414,7 @@ function shouldShowYear( data: TransactionListItemType[] | TransactionGroupListItemType[] | TaskListItemType[] | OnyxTypes.SearchResults['data'], checkOnlyReports = false, precomputedLastExportedMap?: Map, + skipReportCreatedDate = false, ): ShouldShowYearResult { const result: ShouldShowYearResult = { shouldShowYearCreated: false, @@ -1501,7 +1502,7 @@ function shouldShowYear( } else if (isReportEntry(key)) { const item = data[key]; - if (item.created && DateUtils.doesDateBelongToAPastYear(item.created)) { + if (!skipReportCreatedDate && item.created && DateUtils.doesDateBelongToAPastYear(item.created)) { result.shouldShowYearCreated = true; } if (item.submitted && DateUtils.doesDateBelongToAPastYear(item.submitted)) { @@ -1685,7 +1686,12 @@ function getTransactionsSections({ }: GetTransactionSectionsParams): [TransactionListItemType[], number] { const shouldShowMerchant = getShouldShowMerchant(data); const lastExportedActionByReportID = buildLastExportedActionByReportIDMap(data); - const {shouldShowYearCreated, shouldShowYearSubmitted, shouldShowYearApproved, shouldShowYearPosted, shouldShowYearExported} = shouldShowYear(data, false, lastExportedActionByReportID); + const {shouldShowYearCreated, shouldShowYearSubmitted, shouldShowYearApproved, shouldShowYearPosted, shouldShowYearExported} = shouldShowYear( + data, + false, + lastExportedActionByReportID, + true, + ); const {shouldShowAmountInWideColumn, shouldShowTaxAmountInWideColumn} = getWideAmountIndicators(data); // Pre-filter transaction keys to avoid repeated checks @@ -2399,6 +2405,7 @@ function getReportSections({ totalDisplaySpend, nonReimbursableSpend, reimbursableSpend, + isAmountColumnWide: shouldShowAmountInWideColumn, isAllScanning: false, ...avatarProps, }; diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 960cbfc78d31..9d7ad323010e 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -120,7 +120,7 @@ export default { gutterWidth: 12, optionRowHeight: 64, optionRowHeightCompact: 52, - tableRowHeight: 52, + tableRowHeight: 56, sectionMenuItemHeight: 52, sectionMenuItemHeightCompact: 44, optionsListSectionHeaderHeight: getValueUsingPixelRatio(32, 38), From 927f7dc3e546f9c9e6d4e451fd327cf7e5fda4a7 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 26 Mar 2026 02:08:26 +0530 Subject: [PATCH 08/46] Extract reusable border radius styles, fix skeleton loader, and restore isReportActionEntry block Signed-off-by: krishna2323 --- .../ListItem/ExpenseReportListItem.tsx | 8 +- .../SearchList/ListItem/TaskListItem.tsx | 8 +- .../ListItem/TransactionGroupListItem.tsx | 4 +- .../ListItem/TransactionListItem.tsx | 4 +- src/components/Search/index.tsx | 1 + .../SelectionList/ListItem/TableListItem.tsx | 2 +- .../Skeletons/SearchRowSkeleton.tsx | 136 ++++++++---------- src/libs/SearchUIUtils.ts | 8 ++ src/styles/index.ts | 10 ++ 9 files changed, 89 insertions(+), 92 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx index 82246af44eac..a15b9768d65f 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx @@ -174,7 +174,7 @@ function ExpenseReportListItem({ paddingVertical: 8, borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, - ...(isLastItem ? {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius} : {}), + ...(isLastItem ? styles.searchTableBottomRadius : {}), }, ], [styles, item.isSelected, isLargeScreenWidth, theme.buttonHoveredBG, theme.border, isLastItem], @@ -251,11 +251,7 @@ function ExpenseReportListItem({ onLongPressRow={onLongPressRow} shouldSyncFocus={shouldSyncFocus} hoverStyle={item.isSelected && styles.activeComponentBG} - pressableWrapperStyle={[ - styles.mh5, - animatedHighlightStyle, - isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius}, - ]} + pressableWrapperStyle={[styles.mh5, animatedHighlightStyle, isLargeScreenWidth && isLastItem && styles.searchTableBottomRadius]} accessible={false} shouldShowRightCaret={false} shouldUseDefaultRightHandSideCheckmark={false} diff --git a/src/components/Search/SearchList/ListItem/TaskListItem.tsx b/src/components/Search/SearchList/ListItem/TaskListItem.tsx index 58245a0c0140..ab8ce018347b 100644 --- a/src/components/Search/SearchList/ListItem/TaskListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TaskListItem.tsx @@ -44,7 +44,7 @@ function TaskListItem({ paddingVertical: 8, borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, - ...(isLastItem ? {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius} : {}), + ...(isLastItem ? styles.searchTableBottomRadius : {}), }, ]; @@ -80,11 +80,7 @@ function TaskListItem({ onLongPressRow={onLongPressRow} shouldSyncFocus={shouldSyncFocus} hoverStyle={item.isSelected && styles.activeComponentBG} - pressableWrapperStyle={[ - styles.mh5, - animatedHighlightStyle, - isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius}, - ]} + pressableWrapperStyle={[styles.mh5, animatedHighlightStyle, isLargeScreenWidth && isLastItem && styles.searchTableBottomRadius]} forwardedFSClass={fsClass} > ({ minHeight: variables.tableRowHeight, borderRadius: 0, paddingVertical: 4, - ...(isLastItem ? {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius} : {}), + ...(isLastItem ? styles.searchTableBottomRadius : {}), }, isItemSelected && styles.activeComponentBG, ]; @@ -486,7 +486,7 @@ function TransactionGroupListItem({ animatedHighlightStyle, styles.userSelectNone, isLargeScreenWidth && {borderRadius: 0, borderBottomWidth: isLastItem ? 0 : 1, borderColor: isItemSelected ? theme.buttonHoveredBG : theme.border}, - isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius}, + isLargeScreenWidth && isLastItem && styles.searchTableBottomRadius, ]} > {({hovered}) => ( diff --git a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx index 92ac9c670fcd..6d41bbbdffea 100644 --- a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx @@ -112,7 +112,7 @@ function TransactionListItem({ borderRadius: 0, borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, - ...(isLastItem ? {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius} : {}), + ...(isLastItem ? styles.searchTableBottomRadius : {}), } : {...styles.flexColumn, ...styles.alignItemsStretch}, ]; @@ -213,7 +213,7 @@ function TransactionListItem({ styles.flex1, animatedHighlightStyle, styles.userSelectNone, - isLargeScreenWidth && isLastItem && {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius}, + isLargeScreenWidth && isLastItem && styles.searchTableBottomRadius, ]} > {({hovered}) => ( diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 50a224c71a1b..6a58dca8454f 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1475,6 +1475,7 @@ function Search({ ) : undefined diff --git a/src/components/SelectionList/ListItem/TableListItem.tsx b/src/components/SelectionList/ListItem/TableListItem.tsx index b7420cabe88f..d394d3858e2b 100644 --- a/src/components/SelectionList/ListItem/TableListItem.tsx +++ b/src/components/SelectionList/ListItem/TableListItem.tsx @@ -62,7 +62,7 @@ function TableListItem({ paddingHorizontal: 12, borderBottomWidth: isLastItem ? 0 : 1, borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, - ...(isLastItem ? {borderBottomLeftRadius: variables.componentBorderRadius, borderBottomRightRadius: variables.componentBorderRadius} : {}), + ...(isLastItem ? styles.searchTableBottomRadius : {}), } : {}; diff --git a/src/components/Skeletons/SearchRowSkeleton.tsx b/src/components/Skeletons/SearchRowSkeleton.tsx index 7de124b1c5c7..ca4dca8ec21a 100644 --- a/src/components/Skeletons/SearchRowSkeleton.tsx +++ b/src/components/Skeletons/SearchRowSkeleton.tsx @@ -4,7 +4,6 @@ import {View} from 'react-native'; import {Circle} from 'react-native-svg'; import SkeletonRect from '@components/SkeletonRect'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; @@ -19,6 +18,7 @@ type SearchRowSkeletonProps = { gradientOpacityEnabled?: boolean; containerStyle?: StyleProp; reasonAttributes: SkeletonSpanReasonAttributes; + isLoadMore?: boolean; }; const barHeight = 8; @@ -40,8 +40,7 @@ const rightArrowWidth = 28; // 68 is the width of the action button const rightButtonWidth = 68; -function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacityEnabled = false, containerStyle, reasonAttributes}: SearchRowSkeletonProps) { - const theme = useTheme(); +function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacityEnabled = false, containerStyle, reasonAttributes, isLoadMore = false}: SearchRowSkeletonProps) { const styles = useThemeStyles(); const {windowWidth} = useWindowDimensions(); const {shouldUseNarrowLayout, isLargeScreenWidth} = useResponsiveLayout(); @@ -121,78 +120,65 @@ function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacity return ( - - ( - <> - - - - {isLargeScreenWidth && ( - <> - - - - - )} - - - - - - )} - /> - + ( + <> + + + + {isLargeScreenWidth && ( + <> + + + + + )} + + + + + + )} + /> ); } diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index e39675284f13..18524fe000ff 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -1499,6 +1499,14 @@ function shouldShowYear( if (exportedAction?.created && DateUtils.doesDateBelongToAPastYear(exportedAction.created)) { result.shouldShowYearExported = true; } + } else if (!checkOnlyReports && isReportActionEntry(key)) { + const item = data[key]; + for (const action of Object.values(item)) { + const date = action.created; + if (DateUtils.doesDateBelongToAPastYear(date)) { + result.shouldShowYearCreated = true; + } + } } else if (isReportEntry(key)) { const item = data[key]; diff --git a/src/styles/index.ts b/src/styles/index.ts index 582797e7dd57..764cbce566e1 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -2480,6 +2480,16 @@ const staticStyles = (theme: ThemeColors) => borderBottomRightRadius: variables.componentBorderRadiusNormal, }, + searchTableTopRadius: { + borderTopLeftRadius: variables.componentBorderRadius, + borderTopRightRadius: variables.componentBorderRadius, + }, + + searchTableBottomRadius: { + borderBottomLeftRadius: variables.componentBorderRadius, + borderBottomRightRadius: variables.componentBorderRadius, + }, + borderBottom: { borderBottomWidth: 1, borderColor: theme.border, From e0b8571e9f460b14162c0fcb0227029f7d809ab6 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 26 Mar 2026 02:20:51 +0530 Subject: [PATCH 09/46] fix skeleton view. Signed-off-by: krishna2323 --- src/components/Skeletons/SearchRowSkeleton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Skeletons/SearchRowSkeleton.tsx b/src/components/Skeletons/SearchRowSkeleton.tsx index fbddc513b432..52082063a65d 100644 --- a/src/components/Skeletons/SearchRowSkeleton.tsx +++ b/src/components/Skeletons/SearchRowSkeleton.tsx @@ -127,7 +127,7 @@ function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacity fixedNumItems={fixedNumItems} gradientOpacityEnabled={gradientOpacityEnabled} onLayout={onLayout} - itemViewStyle={[styles.highlightBG, styles.mb2, styles.br3, styles.ml5]} + itemViewStyle={[styles.highlightBG, {marginRight: 0}]} itemViewHeight={variables.tableRowHeight} style={[styles.mh5, styles.overflowHidden, isLoadMore && styles.searchTableBottomRadius, !isLoadMore && styles.searchTableTopRadius]} renderSkeletonItem={() => ( From 609c9bec61e42a9477974a5a45f12b7a5e41aeab Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 26 Mar 2026 02:31:45 +0530 Subject: [PATCH 10/46] fix border radius issue. Signed-off-by: krishna2323 --- src/components/Search/SearchList/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Search/SearchList/index.tsx b/src/components/Search/SearchList/index.tsx index 3656fbc7bd4d..f30b7ed9cc2b 100644 --- a/src/components/Search/SearchList/index.tsx +++ b/src/components/Search/SearchList/index.tsx @@ -451,7 +451,7 @@ function SearchList({ onFocus={onFocus} newTransactionID={newTransactionID} keyForList={item.keyForList} - isLastItem={index === data.length - 1} + isLastItem={index === data.length - 1 && !ListFooterComponent} /> ); @@ -483,6 +483,7 @@ function SearchList({ personalPolicyID, customCardNames, selectedTransactions, + ListFooterComponent, ], ); From 6604fc1731966503ab8aaf28a72b79db39f411b6 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 26 Mar 2026 02:56:57 +0530 Subject: [PATCH 11/46] Hide expanded group table header during initial loading state Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/TransactionGroupListExpanded.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx index d5f24d08f527..361afed5e483 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx @@ -190,7 +190,7 @@ function TransactionGroupListExpanded({ const content = ( - {isLargeScreenWidth && ( + {isLargeScreenWidth && !(isEmpty && shouldDisplayLoadingIndicator) && ( Date: Thu, 26 Mar 2026 03:21:05 +0530 Subject: [PATCH 12/46] Scope table header styles to wide screens to preserve narrow layout Signed-off-by: krishna2323 --- src/components/Search/SearchList/index.tsx | 4 ++-- src/styles/index.ts | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/Search/SearchList/index.tsx b/src/components/Search/SearchList/index.tsx index f30b7ed9cc2b..97ce4f1acb0e 100644 --- a/src/components/Search/SearchList/index.tsx +++ b/src/components/Search/SearchList/index.tsx @@ -286,7 +286,7 @@ function SearchList({ // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout here because there is a race condition that causes shouldUseNarrowLayout to change indefinitely in this component // See https://github.com/Expensify/App/issues/48675 for more details // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth - const {isSmallScreenWidth} = useResponsiveLayout(); + const {isSmallScreenWidth, isLargeScreenWidth} = useResponsiveLayout(); const [isModalVisible, setIsModalVisible] = useState(false); const [longPressedItem, setLongPressedItem] = useState(); @@ -494,7 +494,7 @@ function SearchList({ const content = ( {tableHeaderVisible && ( - + {canSelectMultiple && ( flexDirection: 'row', alignItems: 'center', ...userSelect.userSelectNone, - paddingBottom: 8, - backgroundColor: theme.highlightBG, + paddingBottom: 12, justifyContent: 'flex-start', + }, + + searchListHeaderTableStyle: { + backgroundColor: theme.highlightBG, borderTopLeftRadius: variables.componentBorderRadius, borderTopRightRadius: variables.componentBorderRadius, borderBottomWidth: 1, borderColor: theme.border, minHeight: 40, + paddingBottom: 8, }, groupSearchListTableContainerStyle: { @@ -4784,6 +4788,11 @@ const staticStyles = (theme: ThemeColors) => }, listTableHeader: { + paddingVertical: 12, + paddingHorizontal: 32, + }, + + listTableHeaderCompact: { paddingVertical: 8, paddingHorizontal: 12, }, From a4ce760360217dcb37a266f8c1d7da148284b6cd Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 26 Mar 2026 03:22:32 +0530 Subject: [PATCH 13/46] fix prettier. Signed-off-by: krishna2323 --- src/components/Search/SearchList/index.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/Search/SearchList/index.tsx b/src/components/Search/SearchList/index.tsx index 97ce4f1acb0e..ef6e2adcbae7 100644 --- a/src/components/Search/SearchList/index.tsx +++ b/src/components/Search/SearchList/index.tsx @@ -494,7 +494,12 @@ function SearchList({ const content = ( {tableHeaderVisible && ( - + {canSelectMultiple && ( Date: Thu, 26 Mar 2026 05:16:02 +0530 Subject: [PATCH 14/46] Refactor search table row styles into reusable utilities and fix mobile regressions Signed-off-by: krishna2323 --- src/components/Icon/BankIcons/index.native.ts | 9 +++++---- src/components/Icon/BankIcons/index.ts | 12 ++++++++---- src/components/Icon/BankIconsUtils.ts | 1 + .../SearchList/ListItem/ExpenseReportListItem.tsx | 12 ++++++------ .../Search/SearchList/ListItem/TaskListItem.tsx | 10 +++++----- .../Search/SearchList/ListItem/TextCell.tsx | 2 +- .../ListItem/TransactionGroupListExpanded.tsx | 7 ++++--- .../SearchList/ListItem/TransactionGroupListItem.tsx | 7 +++---- .../SearchList/ListItem/TransactionListItem.tsx | 9 +++------ .../ListItem/WithdrawalIDListItemHeader.tsx | 2 +- src/components/Search/SearchTableHeader.tsx | 1 + .../SelectionList/ListItem/TableListItem.tsx | 9 +++------ .../TransactionItemRow/DataCells/ReceiptCell.tsx | 4 +++- src/styles/index.ts | 4 ++++ src/styles/utils/index.ts | 7 +++++++ 15 files changed, 55 insertions(+), 41 deletions(-) diff --git a/src/components/Icon/BankIcons/index.native.ts b/src/components/Icon/BankIcons/index.native.ts index 5ad3b0220186..c02e7cbd7076 100644 --- a/src/components/Icon/BankIcons/index.native.ts +++ b/src/components/Icon/BankIcons/index.native.ts @@ -9,7 +9,7 @@ import type {BankIcon} from '@src/types/onyx/Bank'; /** * Returns Bank Icon Object that matches to existing bank icons or default icons */ -export default function getBankIcon({styles, bankName, isCard = false}: BankIconParams): BankIcon { +export default function getBankIcon({styles, bankName, isCard = false, maxIconSize}: BankIconParams): BankIcon { const bankIcon: BankIcon = { icon: isCard ? GenericBankCard : GenericBank, }; @@ -23,11 +23,12 @@ export default function getBankIcon({styles, bankName, isCard = false}: BankIcon // For default Credit Card icon the icon size should not be set. if (!isCard) { - bankIcon.iconSize = variables.iconSizeExtraLarge; + bankIcon.iconSize = maxIconSize ? Math.min(variables.iconSizeExtraLarge, maxIconSize) : variables.iconSizeExtraLarge; bankIcon.iconStyles = [styles.bankIconContainer]; } else { - bankIcon.iconHeight = variables.cardIconHeight; - bankIcon.iconWidth = variables.cardIconWidth; + const scale = maxIconSize ? Math.min(1, maxIconSize / variables.cardIconWidth) : 1; + bankIcon.iconHeight = Math.round(variables.cardIconHeight * scale); + bankIcon.iconWidth = Math.round(variables.cardIconWidth * scale); bankIcon.iconStyles = [styles.cardIcon]; } diff --git a/src/components/Icon/BankIcons/index.ts b/src/components/Icon/BankIcons/index.ts index 9e06b7696bfa..27cd5da3d91f 100644 --- a/src/components/Icon/BankIcons/index.ts +++ b/src/components/Icon/BankIcons/index.ts @@ -17,7 +17,7 @@ type BankIconAsset = { /** * Returns Bank Icon Object that matches to existing bank icons or default icons */ -export default function getBankIcon({styles, bankName, isCard = false}: BankIconParams): BankIcon { +export default function getBankIcon({styles, bankName, isCard = false, maxIconSize}: BankIconParams): BankIcon { const bankIcon: BankIcon = { icon: isCard ? GenericBankCard : GenericBank, }; @@ -31,11 +31,15 @@ export default function getBankIcon({styles, bankName, isCard = false}: BankIcon // For default Credit Card icon the icon size should not be set. if (!isCard) { - bankIcon.iconSize = variables.iconSizeExtraLarge; + const defaultSize: number = variables.iconSizeExtraLarge; + bankIcon.iconSize = maxIconSize ? Math.min(defaultSize, maxIconSize) : defaultSize; bankIcon.iconStyles = [styles.bankIconContainer]; } else { - bankIcon.iconHeight = variables.cardIconHeight; - bankIcon.iconWidth = variables.cardIconWidth; + const cardWidth: number = variables.cardIconWidth; + const cardHeight: number = variables.cardIconHeight; + const scale = maxIconSize ? Math.min(1, maxIconSize / cardWidth) : 1; + bankIcon.iconHeight = Math.round(cardHeight * scale); + bankIcon.iconWidth = Math.round(cardWidth * scale); bankIcon.iconStyles = [styles.cardIcon]; } diff --git a/src/components/Icon/BankIconsUtils.ts b/src/components/Icon/BankIconsUtils.ts index c12397080a48..483d363ed3ac 100644 --- a/src/components/Icon/BankIconsUtils.ts +++ b/src/components/Icon/BankIconsUtils.ts @@ -7,6 +7,7 @@ type BankIconParams = { styles: ThemeStyles; bankName?: BankName; isCard?: boolean; + maxIconSize?: number; }; /** diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx index 3ab38652008d..6e8b8f2f2080 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx @@ -17,6 +17,7 @@ import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {handleActionButtonPress} from '@libs/actions/Search'; @@ -50,6 +51,7 @@ function ExpenseReportListItem({ }: ExpenseReportListItemProps) { const reportItem = item as unknown as ExpenseReportListItemType; const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); const theme = useTheme(); const {translate} = useLocalize(); const {isLargeScreenWidth} = useResponsiveLayout(); @@ -163,20 +165,18 @@ function ExpenseReportListItem({ styles.selectionListPressableItemWrapper, styles.pv3, styles.ph3, + // Removing background style because they are added to the parent OpacityView via animatedHighlightStyle styles.bgTransparent, item.isSelected && styles.activeComponentBG, styles.mh0, isPendingDelete && styles.cursorDisabled, isLargeScreenWidth && { - minHeight: variables.tableRowHeight, - borderRadius: 0, + ...styles.searchTableRowHeight, + ...StyleUtils.getSearchTableRowBorderStyle(isLastItem, item.isSelected), paddingVertical: 8, - borderBottomWidth: isLastItem ? 0 : 1, - borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, - ...(isLastItem ? styles.searchTableBottomRadius : {}), }, ], - [styles, item.isSelected, isLargeScreenWidth, theme.buttonHoveredBG, theme.border, isLastItem, isPendingDelete], + [styles, item.isSelected, isLargeScreenWidth, isLastItem, isPendingDelete, StyleUtils], ); const listItemWrapperStyle = useMemo( diff --git a/src/components/Search/SearchList/ListItem/TaskListItem.tsx b/src/components/Search/SearchList/ListItem/TaskListItem.tsx index ab8ce018347b..91e2e2165f88 100644 --- a/src/components/Search/SearchList/ListItem/TaskListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TaskListItem.tsx @@ -3,6 +3,7 @@ import BaseListItem from '@components/SelectionList/ListItem/BaseListItem'; import type {ListItem} from '@components/SelectionList/types'; import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import FS from '@libs/Fullstory'; @@ -27,6 +28,7 @@ function TaskListItem({ const taskItem = item as unknown as TaskListItemType; const parentReport = allReports?.[`${ONYXKEYS.COLLECTION.REPORT}${taskItem?.parentReportID}`]; const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); const theme = useTheme(); const {isLargeScreenWidth} = useResponsiveLayout(); @@ -35,16 +37,14 @@ function TaskListItem({ styles.selectionListPressableItemWrapper, styles.pv3, styles.ph3, + // Removing background style because they are added to the parent OpacityView via animatedHighlightStyle styles.bgTransparent, item.isSelected && styles.activeComponentBG, styles.mh0, isLargeScreenWidth && { - minHeight: variables.tableRowHeight, - borderRadius: 0, + ...styles.searchTableRowHeight, + ...StyleUtils.getSearchTableRowBorderStyle(isLastItem, item.isSelected), paddingVertical: 8, - borderBottomWidth: isLastItem ? 0 : 1, - borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, - ...(isLastItem ? styles.searchTableBottomRadius : {}), }, ]; diff --git a/src/components/Search/SearchList/ListItem/TextCell.tsx b/src/components/Search/SearchList/ListItem/TextCell.tsx index 4f5c5857f39a..a1e9176ef8c8 100644 --- a/src/components/Search/SearchList/ListItem/TextCell.tsx +++ b/src/components/Search/SearchList/ListItem/TextCell.tsx @@ -14,7 +14,7 @@ function TextCell({text = '', isLargeScreenWidth = true}: TextCellProps) { ); diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx index 361afed5e483..bcc382d3ffdb 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx @@ -25,7 +25,7 @@ import {getReportOrDraftReport} from '@libs/ReportUtils'; import {createAndOpenSearchTransactionThread, getColumnsToShow, getTableMinWidth} from '@libs/SearchUIUtils'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {getTransactionViolations} from '@libs/TransactionUtils'; -import variables from '@styles/variables'; + import type {TransactionPreviewData} from '@userActions/Search'; import {setActiveTransactionIDs} from '@userActions/TransactionThreadNavigation'; import CONST from '@src/CONST'; @@ -231,7 +231,7 @@ function TransactionGroupListExpanded({ onButtonPress={() => { openReportInRHP(transaction); }} - style={[styles.noBorderRadius, styles.p3, isLargeScreenWidth && [styles.pv1, {minHeight: variables.tableRowHeight}], styles.flex1]} + style={[styles.noBorderRadius, styles.p3, isLargeScreenWidth && [styles.pv1, styles.searchTableRowHeight], styles.flex1]} isReportItemChild isInSingleTransactionReport={isInSingleTransactionReport} shouldShowBottomBorder={shouldShowBottomBorder} @@ -281,10 +281,11 @@ function TransactionGroupListExpanded({ )} {shouldDisplayLoadingIndicator && ( - + diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx index 416a59f92507..0c8022aba000 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx @@ -186,7 +186,7 @@ function TransactionGroupListItem({ const pressableStyle = [ styles.transactionGroupListItemStyle, isLargeScreenWidth && { - minHeight: variables.tableRowHeight, + ...styles.searchTableRowHeight, borderRadius: 0, paddingVertical: 4, ...(isLastItem ? styles.searchTableBottomRadius : {}), @@ -481,8 +481,7 @@ function TransactionGroupListItem({ styles.mh5, animatedHighlightStyle, styles.userSelectNone, - isLargeScreenWidth && {borderRadius: 0, borderBottomWidth: isLastItem ? 0 : 1, borderColor: isItemSelected ? theme.buttonHoveredBG : theme.border}, - isLargeScreenWidth && isLastItem && styles.searchTableBottomRadius, + isLargeScreenWidth && StyleUtils.getSearchTableRowBorderStyle(isLastItem, isItemSelected), ]} > {({hovered}) => ( @@ -491,7 +490,7 @@ function TransactionGroupListItem({ isExpanded={isExpanded} header={getHeader(hovered)} onPress={onExpandIconPress} - expandButtonStyle={styles.pv2} + expandButtonStyle={isLargeScreenWidth ? styles.pv2 : styles.pv4Half} shouldShowToggleButton={isLargeScreenWidth} sentryLabel={CONST.SENTRY_LABEL.SEARCH.GROUP_EXPAND_TOGGLE} > diff --git a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx index 6de5526d6bcd..838273b20f24 100644 --- a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx @@ -56,6 +56,7 @@ function TransactionListItem({ const transactionItem = item as unknown as TransactionListItemType; const styles = useThemeStyles(); const theme = useTheme(); + const StyleUtils = useStyleUtils(); const {isLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); const {currentSearchHash, currentSearchKey, currentSearchResults} = useSearchStateContext(); @@ -106,11 +107,8 @@ function TransactionListItem({ ...styles.flexRow, ...styles.justifyContentBetween, ...styles.alignItemsCenter, - minHeight: variables.tableRowHeight, - borderRadius: 0, - borderBottomWidth: isLastItem ? 0 : 1, - borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, - ...(isLastItem ? styles.searchTableBottomRadius : {}), + ...styles.searchTableRowHeight, + ...StyleUtils.getSearchTableRowBorderStyle(isLastItem, item.isSelected), } : {...styles.flexColumn, ...styles.alignItemsStretch}, ]; @@ -178,7 +176,6 @@ function TransactionListItem({ }); }; - const StyleUtils = useStyleUtils(); const pressableRef = useRef(null); useSyncFocus(pressableRef, !!isFocused, shouldSyncFocus); diff --git a/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx b/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx index 0baa0591a901..56e195fe93b7 100644 --- a/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx @@ -77,7 +77,7 @@ function WithdrawalIDListItemHeader({ const maskedNumber = withdrawalIDItem.accountNumber ? `xx${withdrawalIDItem.accountNumber.slice(-4)}` : ''; const accountLabel = `${formattedBankName} ${maskedNumber}`; - const {icon, iconSize, iconStyles} = getBankIcon({bankName: withdrawalIDItem.bankName, styles}); + const {icon, iconSize, iconStyles} = getBankIcon({bankName: withdrawalIDItem.bankName, styles, maxIconSize: isLargeScreenWidth ? variables.w28 : undefined}); const formattedWithdrawalDate = DateUtils.formatWithUTCTimeZone( withdrawalIDItem.debitPosted, DateUtils.doesDateBelongToAPastYear(withdrawalIDItem.debitPosted) ? CONST.DATE.MONTH_DAY_YEAR_ABBR_FORMAT : CONST.DATE.MONTH_DAY_ABBR_FORMAT, diff --git a/src/components/Search/SearchTableHeader.tsx b/src/components/Search/SearchTableHeader.tsx index 1e05c00dc88c..3d2fe36b5958 100644 --- a/src/components/Search/SearchTableHeader.tsx +++ b/src/components/Search/SearchTableHeader.tsx @@ -530,6 +530,7 @@ function SearchTableHeader({ sortOrder={sortOrder} // In GroupBy views, disable flex expansion for Total columns so Expenses column gets more space shouldRemoveTotalColumnFlex={!!groupBy && !isExpenseReportView} + // Don't butt up against the 'select all' checkbox if present containerStyles={canSelectMultiple && [styles.pl3]} onSortPress={(columnName, order) => { if (columnName === CONST.SEARCH.TABLE_COLUMNS.COMMENTS) { diff --git a/src/components/SelectionList/ListItem/TableListItem.tsx b/src/components/SelectionList/ListItem/TableListItem.tsx index d394d3858e2b..312e78cd813c 100644 --- a/src/components/SelectionList/ListItem/TableListItem.tsx +++ b/src/components/SelectionList/ListItem/TableListItem.tsx @@ -8,7 +8,6 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import variables from '@styles/variables'; import BaseListItem from './BaseListItem'; import type {ListItem, TableListItemProps} from './types'; @@ -56,13 +55,10 @@ function TableListItem({ const compactRowStyle = isLargeScreenWidth ? { - minHeight: variables.tableRowHeight, - borderRadius: 0, + ...styles.searchTableRowHeight, + ...StyleUtils.getSearchTableRowBorderStyle(isLastItem, item.isSelected), paddingVertical: 8, paddingHorizontal: 12, - borderBottomWidth: isLastItem ? 0 : 1, - borderColor: item.isSelected ? theme.buttonHoveredBG : theme.border, - ...(isLastItem ? styles.searchTableBottomRadius : {}), } : {}; @@ -72,6 +68,7 @@ function TableListItem({ pressableStyle={[ styles.selectionListPressableItemWrapper, styles.mh0, + // Removing background style because they are added to the parent OpacityView via animatedHighlightStyle item.shouldAnimateInHighlight ? styles.bgTransparent : undefined, item.isSelected && styles.activeComponentBG, item.cursorStyle, diff --git a/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx b/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx index 3d38388fefe1..0047baefa49f 100644 --- a/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx +++ b/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx @@ -6,6 +6,7 @@ import ReceiptImage from '@components/ReceiptImage'; import ReceiptPreview from '@components/TransactionItemRow/ReceiptPreview'; import useHover from '@hooks/useHover'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -20,6 +21,7 @@ function ReceiptCell({transactionItem, isSelected, style}: {transactionItem: Tra const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); + const {isLargeScreenWidth} = useResponsiveLayout(); const icons = useMemoizedLazyExpensifyIcons(['Receipt']); const backgroundStyles = isSelected ? StyleUtils.getBackgroundColorStyle(theme.buttonHoveredBG) : StyleUtils.getBackgroundColorStyle(theme.border); const {hovered, bind} = useHover(); @@ -56,7 +58,7 @@ function ReceiptCell({transactionItem, isSelected, style}: {transactionItem: Tra return ( borderBottomRightRadius: variables.componentBorderRadius, }, + searchTableRowHeight: { + minHeight: variables.tableRowHeight, + }, + borderBottom: { borderBottomWidth: 1, borderColor: theme.border, diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 7bca6b0c13ee..ec56122dbafd 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1786,6 +1786,13 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ return isDragging ? styles.cursorGrabbing : styles.cursorZoomOut; }, + getSearchTableRowBorderStyle: (isLastItem?: boolean, isSelected?: boolean): ViewStyle => ({ + borderRadius: 0, + borderBottomWidth: isLastItem ? 0 : 1, + borderColor: isSelected ? theme.buttonHoveredBG : theme.border, + ...(isLastItem ? styles.searchTableBottomRadius : {}), + }), + getReportTableColumnStyles: ( columnName: string, isDateColumnWide = false, From be3a2e50159de0cc2cc234905d9ff4a00218de97 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 26 Mar 2026 05:17:44 +0530 Subject: [PATCH 15/46] fix prettier. Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/TransactionGroupListExpanded.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx index bcc382d3ffdb..01fd4fc0eee6 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx @@ -25,7 +25,6 @@ import {getReportOrDraftReport} from '@libs/ReportUtils'; import {createAndOpenSearchTransactionThread, getColumnsToShow, getTableMinWidth} from '@libs/SearchUIUtils'; import type {SkeletonSpanReasonAttributes} from '@libs/telemetry/useSkeletonSpan'; import {getTransactionViolations} from '@libs/TransactionUtils'; - import type {TransactionPreviewData} from '@userActions/Search'; import {setActiveTransactionIDs} from '@userActions/TransactionThreadNavigation'; import CONST from '@src/CONST'; From 51d85ab21ffad269e9bb39fac3484699d0ee6013 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 27 Mar 2026 06:59:10 +0530 Subject: [PATCH 16/46] add border bottom to skeleton rows. Signed-off-by: krishna2323 --- src/components/Skeletons/SearchRowSkeleton.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Skeletons/SearchRowSkeleton.tsx b/src/components/Skeletons/SearchRowSkeleton.tsx index 52082063a65d..6183853fb006 100644 --- a/src/components/Skeletons/SearchRowSkeleton.tsx +++ b/src/components/Skeletons/SearchRowSkeleton.tsx @@ -129,6 +129,7 @@ function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacity onLayout={onLayout} itemViewStyle={[styles.highlightBG, {marginRight: 0}]} itemViewHeight={variables.tableRowHeight} + itemContainerStyle={styles.borderBottom} style={[styles.mh5, styles.overflowHidden, isLoadMore && styles.searchTableBottomRadius, !isLoadMore && styles.searchTableTopRadius]} renderSkeletonItem={() => ( <> From 3a8560cae4edfa38e1da8bdcc8555f1ffe941468 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 27 Mar 2026 07:05:45 +0530 Subject: [PATCH 17/46] fix scrollbar misalignment issue. Signed-off-by: krishna2323 --- src/components/Search/SearchList/BaseSearchList/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Search/SearchList/BaseSearchList/index.tsx b/src/components/Search/SearchList/BaseSearchList/index.tsx index 43b77898839f..13c04e34ab1e 100644 --- a/src/components/Search/SearchList/BaseSearchList/index.tsx +++ b/src/components/Search/SearchList/BaseSearchList/index.tsx @@ -116,7 +116,7 @@ function BaseSearchList({ renderItem={renderItemWithKeyboardFocus} keyExtractor={keyExtractor} onScroll={onScroll} - showsVerticalScrollIndicator + showsVerticalScrollIndicator={false} ref={ref} extraData={extraData} onEndReached={onEndReached} From ad91276b8f93220bdf1e3d9da63fc7f5b6e46a97 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 27 Mar 2026 07:11:39 +0530 Subject: [PATCH 18/46] fix: remove flex expansion from amount columns on Reports > Reports page Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/ExpenseReportListItemRow.tsx | 6 +++--- src/components/Search/SearchTableHeader.tsx | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx index 57f426bd7145..5570cd737066 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx @@ -157,7 +157,7 @@ function ExpenseReportListItemRow({ ), [CONST.SEARCH.TABLE_COLUMNS.REIMBURSABLE_TOTAL]: ( - + ), [CONST.SEARCH.TABLE_COLUMNS.NON_REIMBURSABLE_TOTAL]: ( - + ), [CONST.SEARCH.TABLE_COLUMNS.TOTAL]: ( - + { From 8cb9447f44f6f9bb3d514f2e673cfe0f3f74ccd8 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 27 Mar 2026 07:15:37 +0530 Subject: [PATCH 19/46] fix: scale bank icon container to match icon size when maxIconSize is used Signed-off-by: krishna2323 --- src/components/Icon/BankIcons/index.native.ts | 5 +++-- src/components/Icon/BankIcons/index.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Icon/BankIcons/index.native.ts b/src/components/Icon/BankIcons/index.native.ts index c02e7cbd7076..b338a47ae83c 100644 --- a/src/components/Icon/BankIcons/index.native.ts +++ b/src/components/Icon/BankIcons/index.native.ts @@ -23,8 +23,9 @@ export default function getBankIcon({styles, bankName, isCard = false, maxIconSi // For default Credit Card icon the icon size should not be set. if (!isCard) { - bankIcon.iconSize = maxIconSize ? Math.min(variables.iconSizeExtraLarge, maxIconSize) : variables.iconSizeExtraLarge; - bankIcon.iconStyles = [styles.bankIconContainer]; + const defaultSize = variables.iconSizeExtraLarge; + bankIcon.iconSize = maxIconSize ? Math.min(defaultSize, maxIconSize) : defaultSize; + bankIcon.iconStyles = maxIconSize && maxIconSize < defaultSize ? [{...styles.bankIconContainer, width: bankIcon.iconSize, height: bankIcon.iconSize}] : [styles.bankIconContainer]; } else { const scale = maxIconSize ? Math.min(1, maxIconSize / variables.cardIconWidth) : 1; bankIcon.iconHeight = Math.round(variables.cardIconHeight * scale); diff --git a/src/components/Icon/BankIcons/index.ts b/src/components/Icon/BankIcons/index.ts index 27cd5da3d91f..ae8bb35c5637 100644 --- a/src/components/Icon/BankIcons/index.ts +++ b/src/components/Icon/BankIcons/index.ts @@ -33,7 +33,7 @@ export default function getBankIcon({styles, bankName, isCard = false, maxIconSi if (!isCard) { const defaultSize: number = variables.iconSizeExtraLarge; bankIcon.iconSize = maxIconSize ? Math.min(defaultSize, maxIconSize) : defaultSize; - bankIcon.iconStyles = [styles.bankIconContainer]; + bankIcon.iconStyles = maxIconSize && maxIconSize < defaultSize ? [{...styles.bankIconContainer, width: bankIcon.iconSize, height: bankIcon.iconSize}] : [styles.bankIconContainer]; } else { const cardWidth: number = variables.cardIconWidth; const cardHeight: number = variables.cardIconHeight; From 84cf37900a0e8d26e190944b4877782878f26486 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Sat, 28 Mar 2026 04:10:22 +0530 Subject: [PATCH 20/46] fix: reduce group header row height to 56px and increase expanded child row padding to 8px Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/BaseListItemHeader.tsx | 4 +++- .../Search/SearchList/ListItem/CardListItemHeader.tsx | 4 +++- .../Search/SearchList/ListItem/MemberListItemHeader.tsx | 4 +++- .../SearchList/ListItem/TransactionGroupListExpanded.tsx | 2 +- .../Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx | 4 +++- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/BaseListItemHeader.tsx b/src/components/Search/SearchList/ListItem/BaseListItemHeader.tsx index 36ee228bc94b..4bd450493b62 100644 --- a/src/components/Search/SearchList/ListItem/BaseListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/BaseListItemHeader.tsx @@ -140,7 +140,9 @@ function BaseListItemHeader({ return ( - + {!!canSelectMultiple && ( ({ return ( - + {!!canSelectMultiple && ( ({ return ( - + {!!canSelectMultiple && ( ({ onButtonPress={() => { openReportInRHP(transaction); }} - style={[styles.noBorderRadius, styles.p3, isLargeScreenWidth && [styles.pv1, styles.searchTableRowHeight], styles.flex1]} + style={[styles.noBorderRadius, styles.p3, isLargeScreenWidth && [styles.pv2, styles.searchTableRowHeight], styles.flex1]} isReportItemChild isInSingleTransactionReport={isInSingleTransactionReport} shouldShowBottomBorder={shouldShowBottomBorder} diff --git a/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx b/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx index 56e195fe93b7..c4ed374994b5 100644 --- a/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx @@ -166,7 +166,9 @@ function WithdrawalIDListItemHeader({ return ( - + {!!canSelectMultiple && ( Date: Sat, 28 Mar 2026 04:54:58 +0530 Subject: [PATCH 21/46] fix total cell width. Signed-off-by: krishna2323 --- 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 919d0ec207fd..d4cf95acb4cc 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1569,6 +1569,7 @@ function Search({ isTaxAmountColumnWide={shouldShowTaxAmountInWideColumn} shouldShowSorting groupBy={validGroupBy} + isExpenseReportView={isExpenseReportType} /> ) From ab226618a5f4162f3921ca1e5dd41af44d9bf962 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Sat, 28 Mar 2026 04:58:27 +0530 Subject: [PATCH 22/46] fix date column mismatch. Signed-off-by: krishna2323 --- src/components/Search/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index d4cf95acb4cc..b8589e5f0642 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1485,6 +1485,8 @@ function Search({ const {shouldShowYearCreated, shouldShowYearSubmitted, shouldShowYearApproved, shouldShowYearPosted, shouldShowYearExported} = shouldShowYearUtil( searchResults?.data, isExpenseReportType ?? false, + undefined, + !isExpenseReportType, ); const {shouldShowAmountInWideColumn, shouldShowTaxAmountInWideColumn} = getWideAmountIndicators(searchResults?.data); const shouldShowTableHeader = isLargeScreenWidth && !isChat; From 1bbb418f02ba0db9ce570140fbd48f8524b91dcc Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Tue, 31 Mar 2026 00:59:09 +0530 Subject: [PATCH 23/46] fix prettier. Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/BaseListItemHeader.tsx | 4 +--- .../Search/SearchList/ListItem/CardListItemHeader.tsx | 4 +--- .../Search/SearchList/ListItem/MemberListItemHeader.tsx | 4 +--- .../Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/BaseListItemHeader.tsx b/src/components/Search/SearchList/ListItem/BaseListItemHeader.tsx index 4bd450493b62..e9a0504263d4 100644 --- a/src/components/Search/SearchList/ListItem/BaseListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/BaseListItemHeader.tsx @@ -140,9 +140,7 @@ function BaseListItemHeader({ return ( - + {!!canSelectMultiple && ( ({ return ( - + {!!canSelectMultiple && ( ({ return ( - + {!!canSelectMultiple && ( ({ return ( - + {!!canSelectMultiple && ( Date: Tue, 31 Mar 2026 04:02:05 +0530 Subject: [PATCH 24/46] Use dynamic width for Withdrawn column based on year visibility Signed-off-by: krishna2323 --- .../ListItem/WithdrawalIDListItemHeader.tsx | 13 ++++++++++++- .../Search/SearchList/ListItem/types.ts | 3 +++ src/components/Search/SearchTableHeader.tsx | 3 +++ src/components/Search/SortableTableHeader.tsx | 3 +++ src/components/Search/index.tsx | 3 ++- src/libs/SearchUIUtils.ts | 17 ++++++++++++++++- src/styles/utils/index.ts | 3 ++- 7 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx b/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx index 8052050e9645..ab4711495632 100644 --- a/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx @@ -116,7 +116,18 @@ function WithdrawalIDListItemHeader({ [CONST.SEARCH.TABLE_COLUMNS.GROUP_WITHDRAWN]: ( ; @@ -46,6 +47,7 @@ function SortableTableHeader({ approvedColumnSize, postedColumnSize, exportedColumnSize, + withdrawnColumnSize, containerStyles, shouldShowSorting, onSortPress, @@ -89,6 +91,7 @@ function SortableTableHeader({ postedColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE, exportedColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE, shouldRemoveTotalColumnFlex, + withdrawnColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE, ), ]} isSortable={isSortable} diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 9bfc84ba92b9..7623503771bb 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1485,7 +1485,7 @@ function Search({ navigation.setParams({q: newQuery, rawQuery: undefined}); }; - const {shouldShowYearCreated, shouldShowYearSubmitted, shouldShowYearApproved, shouldShowYearPosted, shouldShowYearExported} = shouldShowYearUtil( + const {shouldShowYearCreated, shouldShowYearSubmitted, shouldShowYearApproved, shouldShowYearPosted, shouldShowYearExported, shouldShowYearWithdrawn} = shouldShowYearUtil( searchResults?.data, isExpenseReportType ?? false, undefined, @@ -1570,6 +1570,7 @@ function Search({ shouldShowYearApproved={shouldShowYearApproved} shouldShowYearPosted={shouldShowYearPosted} shouldShowYearExported={shouldShowYearExported} + shouldShowYearWithdrawn={shouldShowYearWithdrawn} isAmountColumnWide={shouldShowAmountInWideColumn} isTaxAmountColumnWide={shouldShowTaxAmountInWideColumn} shouldShowSorting diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 7faf76439a28..e60882588550 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -1383,6 +1383,7 @@ type ShouldShowYearResult = { shouldShowYearApproved: boolean; shouldShowYearPosted: boolean; shouldShowYearExported: boolean; + shouldShowYearWithdrawn: boolean; }; /** @@ -1437,6 +1438,7 @@ function shouldShowYear( shouldShowYearApproved: false, shouldShowYearPosted: false, shouldShowYearExported: false, + shouldShowYearWithdrawn: false, }; const currentYear = new Date().getFullYear(); @@ -1539,6 +1541,11 @@ function shouldShowYear( if (exportedAction?.created && DateUtils.doesDateBelongToAPastYear(exportedAction.created)) { result.shouldShowYearExported = true; } + } else if (!result.shouldShowYearWithdrawn && isGroupEntry(key)) { + const group = data[key]; + if ('debitPosted' in group && group.debitPosted && DateUtils.doesDateBelongToAPastYear(group.debitPosted)) { + result.shouldShowYearWithdrawn = true; + } } // Early exit if all flags are true @@ -2648,6 +2655,7 @@ function getCardSections( * Do not use directly, use only via `getSections()` facade. */ function getWithdrawalIDSections(data: OnyxTypes.SearchResults['data'], queryJSON: SearchQueryJSON | undefined): [TransactionWithdrawalIDGroupListItemType[], number] { + const {shouldShowYearWithdrawn} = shouldShowYear(data); const withdrawalIDSections: Record = {}; for (const key in data) { @@ -2665,6 +2673,7 @@ function getWithdrawalIDSections(data: OnyxTypes.SearchResults['data'], queryJSO transactions: [], transactionsQueryJSON, ...withdrawalIDGroup, + shouldShowYearWithdrawn, formattedWithdrawalID: String(withdrawalIDGroup.entryID), keyForList: key, }; @@ -4959,7 +4968,13 @@ function getTableMinWidth(columns: SearchColumnType[]) { minWidth += 68; } else if (column === CONST.SEARCH.TABLE_COLUMNS.DATE) { minWidth += 48; - } else if (column === CONST.SEARCH.TABLE_COLUMNS.SUBMITTED || column === CONST.SEARCH.TABLE_COLUMNS.APPROVED || column === CONST.SEARCH.TABLE_COLUMNS.POSTED) { + } else if ( + column === CONST.SEARCH.TABLE_COLUMNS.SUBMITTED || + column === CONST.SEARCH.TABLE_COLUMNS.APPROVED || + column === CONST.SEARCH.TABLE_COLUMNS.POSTED || + column === CONST.SEARCH.TABLE_COLUMNS.WITHDRAWN || + column === CONST.SEARCH.TABLE_COLUMNS.GROUP_WITHDRAWN + ) { minWidth += 72; } else if (column === CONST.SEARCH.TABLE_COLUMNS.TYPE) { minWidth += 16; diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index ec56122dbafd..f9502b046e6d 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1803,6 +1803,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ isPostedColumnWide = false, isExportedColumnWide = false, shouldRemoveTotalColumnFlex = false, + isWithdrawnColumnWide = false, ): ViewStyle => { let columnWidth; switch (columnName) { @@ -1838,7 +1839,7 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ break; case CONST.SEARCH.TABLE_COLUMNS.WITHDRAWN: case CONST.SEARCH.TABLE_COLUMNS.GROUP_WITHDRAWN: - columnWidth = {...getWidthStyle(variables.w96)}; + columnWidth = {...getWidthStyle(isWithdrawnColumnWide ? variables.w92 : variables.w72)}; break; case CONST.SEARCH.TABLE_COLUMNS.CATEGORY: case CONST.SEARCH.TABLE_COLUMNS.GROUP_CATEGORY: From 33aa994230fcd4b2c189ac9337b4c5797aeb3e15 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Wed, 1 Apr 2026 07:31:44 +0530 Subject: [PATCH 25/46] Fix total column alignment in expanded group rows and arrow spacing in report rows Signed-off-by: krishna2323 --- .../ListItem/ExpenseReportListItemRow.tsx | 20 ++++++------- src/components/TransactionItemRow/index.tsx | 28 +++++++++++++++++-- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx index 5570cd737066..28ca3cf6ea2b 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx @@ -323,8 +323,8 @@ function ExpenseReportListItemRow({ } return ( - - + + {!!canSelectMultiple && ( {CellComponent}; })} - - - + ); } diff --git a/src/components/TransactionItemRow/index.tsx b/src/components/TransactionItemRow/index.tsx index 776f9b70db58..d2e20cdc3960 100644 --- a/src/components/TransactionItemRow/index.tsx +++ b/src/components/TransactionItemRow/index.tsx @@ -522,7 +522,19 @@ function TransactionItemRow({ return ( Date: Wed, 1 Apr 2026 07:43:49 +0530 Subject: [PATCH 26/46] Refactor: extract getSearchTableRowPressableStyle to consolidate row height and border styles. Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/ExpenseReportListItem.tsx | 3 +-- .../Search/SearchList/ListItem/TaskListItem.tsx | 3 +-- .../Search/SearchList/ListItem/TransactionListItem.tsx | 3 +-- src/components/SelectionList/ListItem/TableListItem.tsx | 3 +-- src/styles/utils/index.ts | 8 ++++++++ 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx index a6a04cab0692..5e6a80431685 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx @@ -174,8 +174,7 @@ function ExpenseReportListItem({ styles.mh0, isPendingDelete && styles.cursorDisabled, isLargeScreenWidth && { - ...styles.searchTableRowHeight, - ...StyleUtils.getSearchTableRowBorderStyle(isLastItem, item.isSelected), + ...StyleUtils.getSearchTableRowPressableStyle(!!isLastItem, item.isSelected), paddingVertical: 8, }, ], diff --git a/src/components/Search/SearchList/ListItem/TaskListItem.tsx b/src/components/Search/SearchList/ListItem/TaskListItem.tsx index 91e2e2165f88..bdfdf29ba22a 100644 --- a/src/components/Search/SearchList/ListItem/TaskListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TaskListItem.tsx @@ -42,8 +42,7 @@ function TaskListItem({ item.isSelected && styles.activeComponentBG, styles.mh0, isLargeScreenWidth && { - ...styles.searchTableRowHeight, - ...StyleUtils.getSearchTableRowBorderStyle(isLastItem, item.isSelected), + ...StyleUtils.getSearchTableRowPressableStyle(!!isLastItem, item.isSelected), paddingVertical: 8, }, ]; diff --git a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx index f82e5e6e8375..bec878ce2aac 100644 --- a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx @@ -110,8 +110,7 @@ function TransactionListItem({ ...styles.flexRow, ...styles.justifyContentBetween, ...styles.alignItemsCenter, - ...styles.searchTableRowHeight, - ...StyleUtils.getSearchTableRowBorderStyle(isLastItem, item.isSelected), + ...StyleUtils.getSearchTableRowPressableStyle(!!isLastItem, item.isSelected), } : {...styles.flexColumn, ...styles.alignItemsStretch}, ]; diff --git a/src/components/SelectionList/ListItem/TableListItem.tsx b/src/components/SelectionList/ListItem/TableListItem.tsx index d9838296dd41..173713e4a838 100644 --- a/src/components/SelectionList/ListItem/TableListItem.tsx +++ b/src/components/SelectionList/ListItem/TableListItem.tsx @@ -47,8 +47,7 @@ function TableListItem({ const compactRowStyle = isLargeScreenWidth ? { - ...styles.searchTableRowHeight, - ...StyleUtils.getSearchTableRowBorderStyle(isLastItem, item.isSelected), + ...StyleUtils.getSearchTableRowPressableStyle(!!isLastItem, !!item.isSelected), paddingVertical: 8, paddingHorizontal: 12, } diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index f9502b046e6d..4f35ecb79b4e 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1793,6 +1793,14 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ ...(isLastItem ? styles.searchTableBottomRadius : {}), }), + getSearchTableRowPressableStyle: (isLastItem?: boolean, isSelected?: boolean): ViewStyle => ({ + minHeight: variables.tableRowHeight, + borderRadius: 0, + borderBottomWidth: isLastItem ? 0 : 1, + borderColor: isSelected ? theme.buttonHoveredBG : theme.border, + ...(isLastItem ? styles.searchTableBottomRadius : {}), + }), + getReportTableColumnStyles: ( columnName: string, isDateColumnWide = false, From 21faa79bd312c6e1dba2f83f7031427659661471 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 2 Apr 2026 13:54:33 +0530 Subject: [PATCH 27/46] Remove 2px margin from search table checkboxes and reduce header min-height to 36px Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/BaseListItemHeader.tsx | 1 + src/components/Search/SearchList/ListItem/CardListItem.tsx | 1 + .../Search/SearchList/ListItem/CardListItemHeader.tsx | 1 + .../Search/SearchList/ListItem/ExpenseReportListItemRow.tsx | 4 ++-- .../Search/SearchList/ListItem/MemberListItemHeader.tsx | 1 + .../Search/SearchList/ListItem/ReportListItemHeader.tsx | 2 +- .../Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx | 1 + src/components/Search/SearchList/index.tsx | 1 + src/components/SelectionList/ListItem/TableListItem.tsx | 2 +- src/styles/index.ts | 2 +- 10 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/BaseListItemHeader.tsx b/src/components/Search/SearchList/ListItem/BaseListItemHeader.tsx index e9a0504263d4..aa32db1f5a13 100644 --- a/src/components/Search/SearchList/ListItem/BaseListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/BaseListItemHeader.tsx @@ -149,6 +149,7 @@ function BaseListItemHeader({ isIndeterminate={isIndeterminate} disabled={!!isDisabled || item.isDisabledCheckbox} accessibilityLabel={translate('common.select')} + containerStyle={styles.m0} sentryLabel={CONST.SENTRY_LABEL.SEARCH.GROUP_SELECT_ALL_CHECKBOX} /> )} diff --git a/src/components/Search/SearchList/ListItem/CardListItem.tsx b/src/components/Search/SearchList/ListItem/CardListItem.tsx index 112eb6823c2a..c908a8e30856 100644 --- a/src/components/Search/SearchList/ListItem/CardListItem.tsx +++ b/src/components/Search/SearchList/ListItem/CardListItem.tsx @@ -174,6 +174,7 @@ function CardListItem({ onPress={handleCheckboxPress} disabled={!!isDisabled} style={styles.ml3} + containerStyle={styles.m0} /> )} diff --git a/src/components/Search/SearchList/ListItem/CardListItemHeader.tsx b/src/components/Search/SearchList/ListItem/CardListItemHeader.tsx index 8a35310f7c95..7be0979df58b 100644 --- a/src/components/Search/SearchList/ListItem/CardListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/CardListItemHeader.tsx @@ -131,6 +131,7 @@ function CardListItemHeader({ isIndeterminate={isIndeterminate} disabled={!!isDisabled || cardItem.isDisabledCheckbox} accessibilityLabel={translate('common.select')} + containerStyle={styles.m0} /> )} {!isLargeScreenWidth && ( diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx index 28ca3cf6ea2b..32ede2970c63 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx @@ -278,7 +278,7 @@ function ExpenseReportListItemRow({ onPress={onCheckboxPress} isChecked={isSelectAllChecked} isIndeterminate={isIndeterminate} - containerStyle={[StyleUtils.getCheckboxContainerStyle(20), StyleUtils.getMultiselectListStyles(!!item.isSelected, !!item.isDisabled)]} + containerStyle={[StyleUtils.getCheckboxContainerStyle(20), StyleUtils.getMultiselectListStyles(!!item.isSelected, !!item.isDisabled), styles.m0]} disabled={isDisabledCheckbox} accessibilityLabel={item.text ?? ''} shouldStopMouseDownPropagation @@ -330,7 +330,7 @@ function ExpenseReportListItemRow({ onPress={onCheckboxPress} isChecked={isSelectAllChecked} isIndeterminate={isIndeterminate} - containerStyle={[StyleUtils.getCheckboxContainerStyle(20), StyleUtils.getMultiselectListStyles(!!item.isSelected, !!item.isDisabled)]} + containerStyle={[StyleUtils.getCheckboxContainerStyle(20), StyleUtils.getMultiselectListStyles(!!item.isSelected, !!item.isDisabled), styles.m0]} disabled={isDisabledCheckbox} accessibilityLabel={item.text ?? ''} shouldStopMouseDownPropagation diff --git a/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx b/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx index 55d9904bd2da..bcb0a60c259e 100644 --- a/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx @@ -114,6 +114,7 @@ function MemberListItemHeader({ isIndeterminate={isIndeterminate} disabled={!!isDisabled || memberItem.isDisabledCheckbox} accessibilityLabel={translate('common.select')} + containerStyle={styles.m0} /> )} {!isLargeScreenWidth && ( diff --git a/src/components/Search/SearchList/ListItem/ReportListItemHeader.tsx b/src/components/Search/SearchList/ListItem/ReportListItemHeader.tsx index 89103da7da81..bb0584a44a10 100644 --- a/src/components/Search/SearchList/ListItem/ReportListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/ReportListItemHeader.tsx @@ -135,7 +135,7 @@ function HeaderFirstRow({ onPress={() => onCheckboxPress?.(reportItem as unknown as TItem)} isChecked={isSelectAllChecked} isIndeterminate={isIndeterminate} - containerStyle={[StyleUtils.getCheckboxContainerStyle(20), StyleUtils.getMultiselectListStyles(!!reportItem.isSelected, !!reportItem.isDisabled)]} + containerStyle={[StyleUtils.getCheckboxContainerStyle(20), StyleUtils.getMultiselectListStyles(!!reportItem.isSelected, !!reportItem.isDisabled), styles.m0]} disabled={!!isDisabled || reportItem.isDisabledCheckbox} accessibilityLabel={reportItem.text ?? ''} shouldStopMouseDownPropagation diff --git a/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx b/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx index ab4711495632..157e257d4a25 100644 --- a/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx @@ -186,6 +186,7 @@ function WithdrawalIDListItemHeader({ disabled={!!isDisabled || withdrawalIDItem.isDisabledCheckbox} accessibilityLabel={translate('common.select')} isIndeterminate={isIndeterminate} + containerStyle={styles.m0} /> )} {!isLargeScreenWidth && ( diff --git a/src/components/Search/SearchList/index.tsx b/src/components/Search/SearchList/index.tsx index ef6e2adcbae7..b9ec33a06af8 100644 --- a/src/components/Search/SearchList/index.tsx +++ b/src/components/Search/SearchList/index.tsx @@ -509,6 +509,7 @@ function SearchList({ onAllCheckboxPress(); }} disabled={totalItems === 0} + containerStyle={styles.m0} sentryLabel={CONST.SENTRY_LABEL.SEARCH.SELECT_ALL_CHECKBOX} /> )} diff --git a/src/components/SelectionList/ListItem/TableListItem.tsx b/src/components/SelectionList/ListItem/TableListItem.tsx index 173713e4a838..c4642245667a 100644 --- a/src/components/SelectionList/ListItem/TableListItem.tsx +++ b/src/components/SelectionList/ListItem/TableListItem.tsx @@ -104,7 +104,7 @@ function TableListItem({ onPress={handleCheckboxPress} shouldStopMouseDownPropagation style={[item.cursorStyle, styles.p5, styles.mln5, styles.mhv5, styles.mrn2]} - containerStyle={[StyleUtils.getMultiselectListStyles(!!item.isSelected, !!item.isDisabled), item.cursorStyle]} + containerStyle={[StyleUtils.getMultiselectListStyles(!!item.isSelected, !!item.isDisabled), item.cursorStyle, styles.m0]} testID={`TableListItemCheckbox-${item.text}`} /> )} diff --git a/src/styles/index.ts b/src/styles/index.ts index 105329f37490..a24602164f12 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3528,7 +3528,7 @@ const staticStyles = (theme: ThemeColors) => borderTopRightRadius: variables.componentBorderRadius, borderBottomWidth: 1, borderColor: theme.border, - minHeight: 40, + minHeight: 36, paddingBottom: 8, }, From 0757b57d5b7c09ef9d62426971bd5b86a4c33b13 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 3 Apr 2026 02:44:47 +0530 Subject: [PATCH 28/46] add border bottom to group list item header. Signed-off-by: krishna2323 --- .../ListItem/TransactionGroupListExpanded.tsx | 35 ++++++++++--------- .../ListItem/TransactionGroupListItem.tsx | 1 + 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx index 3ab49aea6051..e0b3a177d6f1 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx @@ -192,22 +192,25 @@ function TransactionGroupListExpanded({ const content = ( {isLargeScreenWidth && !(isEmpty && shouldDisplayLoadingIndicator) && ( - - {}} - sortOrder={undefined} - sortBy={undefined} - shouldShowYear={dateColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE} - isAmountColumnWide={amountColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE} - isTaxAmountColumnWide={taxAmountColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE} - shouldShowSorting={false} - columns={currentColumns} - groupBy={groupBy} - isExpenseReportView - /> - + <> + + {}} + sortOrder={undefined} + sortBy={undefined} + shouldShowYear={dateColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE} + isAmountColumnWide={amountColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE} + isTaxAmountColumnWide={taxAmountColumnSize === CONST.SEARCH.TABLE_COLUMN_SIZES.WIDE} + shouldShowSorting={false} + columns={currentColumns} + groupBy={groupBy} + isExpenseReportView + /> + + + )} {visibleTransactions.map((transaction, index) => { const shouldShowBottomBorder = !isLastTransaction(index); diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx index 3190ba607df1..a9c33736d811 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx @@ -490,6 +490,7 @@ function TransactionGroupListItem({ onPress={onExpandIconPress} expandButtonStyle={isLargeScreenWidth ? styles.pv2 : styles.pv4Half} shouldShowToggleButton={isLargeScreenWidth} + borderBottomStyle={isLargeScreenWidth && styles.borderNone} sentryLabel={CONST.SENTRY_LABEL.SEARCH.GROUP_EXPAND_TOGGLE} > Date: Fri, 3 Apr 2026 04:18:57 +0530 Subject: [PATCH 29/46] fix: use XOR for shouldRemoveTotalColumnFlex to align expanded group columns Signed-off-by: krishna2323 --- src/components/Search/SearchTableHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Search/SearchTableHeader.tsx b/src/components/Search/SearchTableHeader.tsx index 0995a795b6e5..51e7ebd54b86 100644 --- a/src/components/Search/SearchTableHeader.tsx +++ b/src/components/Search/SearchTableHeader.tsx @@ -539,7 +539,7 @@ function SearchTableHeader({ shouldShowSorting={shouldShowSorting} sortBy={sortBy} sortOrder={sortOrder} - shouldRemoveTotalColumnFlex={!!groupBy || isExpenseReportView} + shouldRemoveTotalColumnFlex={!!groupBy !== !!isExpenseReportView} // Don't butt up against the 'select all' checkbox if present containerStyles={canSelectMultiple && [styles.pl3]} onSortPress={(columnName, order) => { From 0c40f4962b8abc04995a8af2e3c54f0b499987e7 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 3 Apr 2026 04:30:06 +0530 Subject: [PATCH 30/46] Refactor table row styles for reusability Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/ExpenseReportListItem.tsx | 7 ++----- .../Search/SearchList/ListItem/TaskListItem.tsx | 7 ++----- .../SearchList/ListItem/TransactionGroupListItem.tsx | 8 ++++---- .../Search/SearchList/ListItem/TransactionListItem.tsx | 3 +-- src/components/SelectionList/ListItem/TableListItem.tsx | 7 ++----- src/styles/utils/index.ts | 6 +++++- src/styles/variables.ts | 3 +++ 7 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx index 5e6a80431685..a236524a8ebd 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx @@ -173,10 +173,7 @@ function ExpenseReportListItem({ item.isSelected && styles.activeComponentBG, styles.mh0, isPendingDelete && styles.cursorDisabled, - isLargeScreenWidth && { - ...StyleUtils.getSearchTableRowPressableStyle(!!isLastItem, item.isSelected), - paddingVertical: 8, - }, + isLargeScreenWidth && StyleUtils.getSearchTableRowPressableStyle(!!isLastItem, item.isSelected, {vertical: variables.tableRowPaddingVertical}), ], [styles, item.isSelected, isLargeScreenWidth, isLastItem, isPendingDelete, StyleUtils], ); @@ -191,7 +188,7 @@ function ExpenseReportListItem({ ); const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: isLargeScreenWidth ? 0 : variables.componentBorderRadius, + borderRadius: StyleUtils.getSearchTableHighlightBorderRadius(isLargeScreenWidth), shouldHighlight: item?.shouldAnimateInHighlight ?? false, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, diff --git a/src/components/Search/SearchList/ListItem/TaskListItem.tsx b/src/components/Search/SearchList/ListItem/TaskListItem.tsx index bdfdf29ba22a..e4b43812e733 100644 --- a/src/components/Search/SearchList/ListItem/TaskListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TaskListItem.tsx @@ -41,10 +41,7 @@ function TaskListItem({ styles.bgTransparent, item.isSelected && styles.activeComponentBG, styles.mh0, - isLargeScreenWidth && { - ...StyleUtils.getSearchTableRowPressableStyle(!!isLastItem, item.isSelected), - paddingVertical: 8, - }, + isLargeScreenWidth && StyleUtils.getSearchTableRowPressableStyle(!!isLastItem, item.isSelected, {vertical: variables.tableRowPaddingVertical}), ]; const listItemWrapperStyle = [ @@ -54,7 +51,7 @@ function TaskListItem({ ]; const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: isLargeScreenWidth ? 0 : variables.componentBorderRadius, + borderRadius: StyleUtils.getSearchTableHighlightBorderRadius(isLargeScreenWidth), shouldHighlight: item?.shouldAnimateInHighlight ?? false, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx index a9c33736d811..c12b3db0372d 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx @@ -172,8 +172,10 @@ function TransactionGroupListItem({ }); }; + const StyleUtils = useStyleUtils(); + const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: isLargeScreenWidth ? 0 : variables.componentBorderRadius, + borderRadius: StyleUtils.getSearchTableHighlightBorderRadius(isLargeScreenWidth), shouldHighlight: item?.shouldAnimateInHighlight ?? false, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, @@ -186,13 +188,11 @@ function TransactionGroupListItem({ isLargeScreenWidth && { ...styles.searchTableRowHeight, borderRadius: 0, - paddingVertical: 4, + paddingVertical: variables.tableGroupRowPaddingVertical, ...(isLastItem ? styles.searchTableBottomRadius : {}), }, isItemSelected && styles.activeComponentBG, ]; - - const StyleUtils = useStyleUtils(); const pressableRef = useRef(null); useEffect(() => { diff --git a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx index e94598bfe8c8..72782dfae3f0 100644 --- a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx @@ -29,7 +29,6 @@ import {syncMissingAttendeesViolation} from '@libs/AttendeeUtils'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {isInvoiceReport} from '@libs/ReportUtils'; import {isViolationDismissed, mergeProhibitedViolations, shouldShowViolation} from '@libs/TransactionUtils'; -import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import {isActionLoadingSelector} from '@src/selectors/ReportMetaData'; @@ -118,7 +117,7 @@ function TransactionListItem({ ]; const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: isLargeScreenWidth ? 0 : variables.componentBorderRadius, + borderRadius: StyleUtils.getSearchTableHighlightBorderRadius(isLargeScreenWidth), shouldHighlight: item?.shouldAnimateInHighlight ?? false, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, diff --git a/src/components/SelectionList/ListItem/TableListItem.tsx b/src/components/SelectionList/ListItem/TableListItem.tsx index c4642245667a..63279b59938c 100644 --- a/src/components/SelectionList/ListItem/TableListItem.tsx +++ b/src/components/SelectionList/ListItem/TableListItem.tsx @@ -8,6 +8,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import variables from '@styles/variables'; import BaseListItem from './BaseListItem'; import type {ListItem, TableListItemProps} from './types'; @@ -46,11 +47,7 @@ function TableListItem({ const hoveredBackgroundColor = styles.sidebarLinkHover?.backgroundColor ? styles.sidebarLinkHover.backgroundColor : theme.sidebar; const compactRowStyle = isLargeScreenWidth - ? { - ...StyleUtils.getSearchTableRowPressableStyle(!!isLastItem, !!item.isSelected), - paddingVertical: 8, - paddingHorizontal: 12, - } + ? StyleUtils.getSearchTableRowPressableStyle(!!isLastItem, !!item.isSelected, {vertical: variables.tableRowPaddingVertical, horizontal: variables.tableRowPaddingHorizontal}) : {}; const handleCheckboxPress = () => { if (onCheckboxPress) { diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 92f404e36bbd..5075adf002e7 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1798,14 +1798,18 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ ...(isLastItem ? styles.searchTableBottomRadius : {}), }), - getSearchTableRowPressableStyle: (isLastItem?: boolean, isSelected?: boolean): ViewStyle => ({ + getSearchTableRowPressableStyle: (isLastItem?: boolean, isSelected?: boolean, padding?: {vertical?: number; horizontal?: number}): ViewStyle => ({ minHeight: variables.tableRowHeight, borderRadius: 0, borderBottomWidth: isLastItem ? 0 : 1, borderColor: isSelected ? theme.buttonHoveredBG : theme.border, ...(isLastItem ? styles.searchTableBottomRadius : {}), + ...(padding?.vertical !== undefined && {paddingVertical: padding.vertical}), + ...(padding?.horizontal !== undefined && {paddingHorizontal: padding.horizontal}), }), + getSearchTableHighlightBorderRadius: (isLargeScreenWidth: boolean): number => (isLargeScreenWidth ? 0 : variables.componentBorderRadius), + getReportTableColumnStyles: ( columnName: string, isDateColumnWide = false, diff --git a/src/styles/variables.ts b/src/styles/variables.ts index 7d767845a2b9..418b34ff6520 100644 --- a/src/styles/variables.ts +++ b/src/styles/variables.ts @@ -121,6 +121,9 @@ export default { optionRowHeight: 64, optionRowHeightCompact: 52, tableRowHeight: 56, + tableRowPaddingVertical: 8, + tableRowPaddingHorizontal: 12, + tableGroupRowPaddingVertical: 4, sectionMenuItemHeight: 52, sectionMenuItemHeightCompact: 44, optionsListSectionHeaderHeight: getValueUsingPixelRatio(32, 38), From 3b6f63ed4c998f4836fbfdb92d31803a9a1f34be Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 3 Apr 2026 04:44:23 +0530 Subject: [PATCH 31/46] fix: remove isReportItemChild from column style calls to fix expanded group alignment Signed-off-by: krishna2323 --- src/components/TransactionItemRow/index.tsx | 28 ++------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/src/components/TransactionItemRow/index.tsx b/src/components/TransactionItemRow/index.tsx index 794049417633..b1766f3fe5d3 100644 --- a/src/components/TransactionItemRow/index.tsx +++ b/src/components/TransactionItemRow/index.tsx @@ -557,19 +557,7 @@ function TransactionItemRow({ return ( Date: Fri, 3 Apr 2026 04:56:35 +0530 Subject: [PATCH 32/46] perf: pass layout props to ReceiptCell and SearchReportAvatar instead of per-row context subscriptions Signed-off-by: krishna2323 --- src/components/ReportActionAvatars/SearchReportAvatar.tsx | 5 ++--- .../Search/SearchList/ListItem/ExpenseReportListItemRow.tsx | 1 + src/components/TransactionItemRow/DataCells/ReceiptCell.tsx | 5 ++--- src/components/TransactionItemRow/index.tsx | 2 ++ 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/ReportActionAvatars/SearchReportAvatar.tsx b/src/components/ReportActionAvatars/SearchReportAvatar.tsx index dc4837886abb..9c1126ae2182 100644 --- a/src/components/ReportActionAvatars/SearchReportAvatar.tsx +++ b/src/components/ReportActionAvatars/SearchReportAvatar.tsx @@ -1,7 +1,6 @@ import React from 'react'; import type {ColorValue} from 'react-native'; import type {ValueOf} from 'type-fest'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; import CONST from '@src/CONST'; import type {Icon} from '@src/types/onyx/OnyxCommon'; import ReportActionAvatar from './ReportActionAvatar'; @@ -13,10 +12,10 @@ type SearchReportAvatarProps = { shouldShowTooltip: boolean; subscriptAvatarBorderColor: ColorValue; reportID: string; + isLargeScreenWidth?: boolean; }; -function SearchReportAvatar({primaryAvatar, secondaryAvatar, avatarType, shouldShowTooltip, subscriptAvatarBorderColor, reportID}: SearchReportAvatarProps) { - const {isLargeScreenWidth} = useResponsiveLayout(); +function SearchReportAvatar({primaryAvatar, secondaryAvatar, avatarType, shouldShowTooltip, subscriptAvatarBorderColor, reportID, isLargeScreenWidth}: SearchReportAvatarProps) { const avatarSize = isLargeScreenWidth ? CONST.AVATAR_SIZE.SMALL : CONST.AVATAR_SIZE.DEFAULT; if (!primaryAvatar) { diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx index 32ede2970c63..858004128de9 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx @@ -346,6 +346,7 @@ function ExpenseReportListItemRow({ shouldShowTooltip={showTooltip} subscriptAvatarBorderColor={finalAvatarBorderColor} reportID={item.reportID} + isLargeScreenWidth={isLargeScreenWidth} /> diff --git a/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx b/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx index 0047baefa49f..c5f7bddc9ea2 100644 --- a/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx +++ b/src/components/TransactionItemRow/DataCells/ReceiptCell.tsx @@ -6,7 +6,6 @@ import ReceiptImage from '@components/ReceiptImage'; import ReceiptPreview from '@components/TransactionItemRow/ReceiptPreview'; import useHover from '@hooks/useHover'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -17,11 +16,11 @@ import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; import variables from '@styles/variables'; import type {Transaction} from '@src/types/onyx'; -function ReceiptCell({transactionItem, isSelected, style}: {transactionItem: Transaction; isSelected: boolean; style?: ViewStyle}) { +function ReceiptCell({transactionItem, isSelected, style, shouldUseNarrowLayout}: {transactionItem: Transaction; isSelected: boolean; style?: ViewStyle; shouldUseNarrowLayout?: boolean}) { const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const {isLargeScreenWidth} = useResponsiveLayout(); + const isLargeScreenWidth = !shouldUseNarrowLayout; const icons = useMemoizedLazyExpensifyIcons(['Receipt']); const backgroundStyles = isSelected ? StyleUtils.getBackgroundColorStyle(theme.buttonHoveredBG) : StyleUtils.getBackgroundColorStyle(theme.border); const {hovered, bind} = useHover(); diff --git a/src/components/TransactionItemRow/index.tsx b/src/components/TransactionItemRow/index.tsx index b1766f3fe5d3..849b479b1ad5 100644 --- a/src/components/TransactionItemRow/index.tsx +++ b/src/components/TransactionItemRow/index.tsx @@ -313,6 +313,7 @@ function TransactionItemRow({ ); @@ -712,6 +713,7 @@ function TransactionItemRow({ transactionItem={transactionItem} isSelected={isSelected} style={styles.mr3} + shouldUseNarrowLayout={shouldUseNarrowLayout} /> From a1805d80faa2e1be86711d45d2147ce74858f732 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 3 Apr 2026 05:36:51 +0530 Subject: [PATCH 33/46] fix: remove 4px gap between name and email in member group header Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/MemberListItemHeader.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx b/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx index bcb0a60c259e..7f5ab9aa20c2 100644 --- a/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx @@ -70,7 +70,7 @@ function MemberListItemHeader({ key={CONST.SEARCH.TABLE_COLUMNS.GROUP_FROM} style={StyleUtils.getReportTableColumnStyles(CONST.SEARCH.TABLE_COLUMNS.FROM)} > - + ({ /> - + Date: Fri, 3 Apr 2026 09:25:32 +0530 Subject: [PATCH 34/46] minor update. Signed-off-by: krishna2323 --- src/components/Skeletons/SearchRowSkeleton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Skeletons/SearchRowSkeleton.tsx b/src/components/Skeletons/SearchRowSkeleton.tsx index 6183853fb006..20b1c71f8c8c 100644 --- a/src/components/Skeletons/SearchRowSkeleton.tsx +++ b/src/components/Skeletons/SearchRowSkeleton.tsx @@ -127,7 +127,7 @@ function SearchRowSkeleton({shouldAnimate = true, fixedNumItems, gradientOpacity fixedNumItems={fixedNumItems} gradientOpacityEnabled={gradientOpacityEnabled} onLayout={onLayout} - itemViewStyle={[styles.highlightBG, {marginRight: 0}]} + itemViewStyle={[styles.highlightBG, styles.mr0]} itemViewHeight={variables.tableRowHeight} itemContainerStyle={styles.borderBottom} style={[styles.mh5, styles.overflowHidden, isLoadMore && styles.searchTableBottomRadius, !isLoadMore && styles.searchTableTopRadius]} From a70c51417a9e38992715938abbf428543cc6cf14 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Fri, 3 Apr 2026 12:49:12 +0530 Subject: [PATCH 35/46] update checkbox size. Signed-off-by: krishna2323 --- src/components/TransactionItemRow/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/TransactionItemRow/index.tsx b/src/components/TransactionItemRow/index.tsx index 849b479b1ad5..3f92fe75f13f 100644 --- a/src/components/TransactionItemRow/index.tsx +++ b/src/components/TransactionItemRow/index.tsx @@ -705,6 +705,7 @@ function TransactionItemRow({ accessibilityLabel={CONST.ROLE.CHECKBOX} isChecked={isSelected} style={styles.mr3} + containerStyle={styles.m0} wrapperStyle={styles.justifyContentCenter} sentryLabel={checkboxSentryLabel} /> @@ -836,6 +837,7 @@ function TransactionItemRow({ }} accessibilityLabel={CONST.ROLE.CHECKBOX} isChecked={isSelected} + containerStyle={styles.m0} wrapperStyle={styles.justifyContentCenter} sentryLabel={checkboxSentryLabel} /> From b807b8213f914013f506fef2e52bd7be1d17c990 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Mon, 6 Apr 2026 09:35:15 +0530 Subject: [PATCH 36/46] Revert compact UI changes in TableListItem to avoid affecting non-search pages Signed-off-by: krishna2323 --- .../SelectionList/ListItem/TableListItem.tsx | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/components/SelectionList/ListItem/TableListItem.tsx b/src/components/SelectionList/ListItem/TableListItem.tsx index 63279b59938c..ec40a586e273 100644 --- a/src/components/SelectionList/ListItem/TableListItem.tsx +++ b/src/components/SelectionList/ListItem/TableListItem.tsx @@ -4,11 +4,9 @@ import Checkbox from '@components/Checkbox'; import ReportActionAvatars from '@components/ReportActionAvatars'; import TextWithTooltip from '@components/TextWithTooltip'; import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import variables from '@styles/variables'; import BaseListItem from './BaseListItem'; import type {ListItem, TableListItemProps} from './types'; @@ -29,15 +27,13 @@ function TableListItem({ shouldUseDefaultRightHandSideCheckmark, shouldShowRightCaret, errorRowStyles, - isLastItem, }: TableListItemProps) { const styles = useThemeStyles(); const theme = useTheme(); const StyleUtils = useStyleUtils(); - const {isLargeScreenWidth} = useResponsiveLayout(); const animatedHighlightStyle = useAnimatedHighlightStyle({ - borderRadius: isLargeScreenWidth ? 0 : styles.selectionListPressableItemWrapper.borderRadius, + borderRadius: styles.selectionListPressableItemWrapper.borderRadius, shouldHighlight: !!item.shouldAnimateInHighlight, highlightColor: theme.messageHighlightBG, backgroundColor: theme.highlightBG, @@ -46,9 +42,6 @@ function TableListItem({ const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor; const hoveredBackgroundColor = styles.sidebarLinkHover?.backgroundColor ? styles.sidebarLinkHover.backgroundColor : theme.sidebar; - const compactRowStyle = isLargeScreenWidth - ? StyleUtils.getSearchTableRowPressableStyle(!!isLastItem, !!item.isSelected, {vertical: variables.tableRowPaddingVertical, horizontal: variables.tableRowPaddingHorizontal}) - : {}; const handleCheckboxPress = () => { if (onCheckboxPress) { onCheckboxPress(item); @@ -67,11 +60,10 @@ function TableListItem({ item.shouldAnimateInHighlight ? styles.bgTransparent : undefined, item.isSelected && styles.activeComponentBG, item.cursorStyle, - compactRowStyle, ]} pressableWrapperStyle={[styles.mh5, animatedHighlightStyle]} wrapperStyle={[styles.flexRow, styles.flex1, styles.justifyContentBetween, styles.userSelectNone, styles.alignItemsCenter]} - containerStyle={!isLargeScreenWidth && styles.mb2} + containerStyle={styles.mb2} isFocused={isFocused} isDisabled={isDisabled} showTooltip={showTooltip} @@ -101,7 +93,7 @@ function TableListItem({ onPress={handleCheckboxPress} shouldStopMouseDownPropagation style={[item.cursorStyle, styles.p5, styles.mln5, styles.mhv5, styles.mrn2]} - containerStyle={[StyleUtils.getMultiselectListStyles(!!item.isSelected, !!item.isDisabled), item.cursorStyle, styles.m0]} + containerStyle={[StyleUtils.getMultiselectListStyles(!!item.isSelected, !!item.isDisabled), item.cursorStyle]} testID={`TableListItemCheckbox-${item.text}`} /> )} From d8da4f67020a51c5055227de365a087a43defbf1 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Mon, 6 Apr 2026 10:37:58 +0530 Subject: [PATCH 37/46] fix tests. Signed-off-by: krishna2323 --- tests/unit/Search/SearchUIUtilsTest.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/unit/Search/SearchUIUtilsTest.ts b/tests/unit/Search/SearchUIUtilsTest.ts index 52073fbad279..7cb2e4cd8f7e 100644 --- a/tests/unit/Search/SearchUIUtilsTest.ts +++ b/tests/unit/Search/SearchUIUtilsTest.ts @@ -1126,6 +1126,7 @@ const transactionReportGroupListItems = [ nonReimbursableSpend: -0, reimbursableSpend: 5000, isAllScanning: false, + isAmountColumnWide: false, primaryAvatar: adminAvatarIcon, secondaryAvatar: policyWorkspaceIcon, avatarType: CONST.REPORT_ACTION_AVATARS.TYPE.SUBSCRIPT, @@ -1240,6 +1241,7 @@ const transactionReportGroupListItems = [ nonReimbursableSpend: -0, reimbursableSpend: 5000, isAllScanning: false, + isAmountColumnWide: false, primaryAvatar: adminAvatarIcon, secondaryAvatar: policyWorkspaceIcon, avatarType: CONST.REPORT_ACTION_AVATARS.TYPE.SUBSCRIPT, @@ -1353,6 +1355,7 @@ const transactionReportGroupListItems = [ nonReimbursableSpend: 0, reimbursableSpend: 4400, isAllScanning: false, + isAmountColumnWide: false, primaryAvatar: approverAvatarIcon, secondaryAvatar: adminAvatarIcon, avatarType: CONST.REPORT_ACTION_AVATARS.TYPE.SUBSCRIPT, @@ -1541,6 +1544,7 @@ const transactionReportGroupListItems = [ nonReimbursableSpend: 0, reimbursableSpend: 0, isAllScanning: false, + isAmountColumnWide: false, primaryAvatar: adminAvatarIcon, secondaryAvatar: policyWorkspaceIcon, avatarType: CONST.REPORT_ACTION_AVATARS.TYPE.SUBSCRIPT, @@ -1710,6 +1714,7 @@ const transactionWithdrawalIDGroupListItems: TransactionWithdrawalIDGroupListIte transactions: [], transactionsQueryJSON: undefined, keyForList: 'group_5', + shouldShowYearWithdrawn: true, }, { bankName: CONST.BANK_NAMES.CITIBANK, @@ -1725,6 +1730,7 @@ const transactionWithdrawalIDGroupListItems: TransactionWithdrawalIDGroupListIte transactions: [], transactionsQueryJSON: undefined, keyForList: 'group_30303030', + shouldShowYearWithdrawn: true, }, ]; @@ -1743,6 +1749,7 @@ const transactionWithdrawalIDGroupListItemsSorted: TransactionWithdrawalIDGroupL transactions: [], transactionsQueryJSON: undefined, keyForList: 'group_5', + shouldShowYearWithdrawn: true, }, { bankName: CONST.BANK_NAMES.CITIBANK, @@ -1758,6 +1765,7 @@ const transactionWithdrawalIDGroupListItemsSorted: TransactionWithdrawalIDGroupL transactions: [], transactionsQueryJSON: undefined, keyForList: 'group_30303030', + shouldShowYearWithdrawn: true, }, ]; From e1341907857a95be0ef75bc99f72b55aa567feae Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Mon, 6 Apr 2026 14:18:11 +0530 Subject: [PATCH 38/46] Optimize Search component performance by consolidating hooks and memoizing render-path computations Signed-off-by: krishna2323 --- src/components/Search/index.tsx | 73 +++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index dd198996e4f3..7430397624ec 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -281,14 +281,12 @@ function Search({ const {isOffline} = useNetwork(); const prevIsOffline = usePrevious(isOffline); - const {shouldUseNarrowLayout} = useResponsiveLayout(); - const styles = useThemeStyles(); - // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout for enabling the selection mode on small screens only // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth - const {isSmallScreenWidth, isLargeScreenWidth} = useResponsiveLayout(); + const {shouldUseNarrowLayout, isSmallScreenWidth, isLargeScreenWidth} = useResponsiveLayout(); + const styles = useThemeStyles(); const navigation = useNavigation>(); const isFocused = useIsFocused(); - const {markReportIDAsExpense} = useWideRHPActions(); + const {markReportIDAsExpense, markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense} = useWideRHPActions(); const { currentSearchHash, currentSearchKey, @@ -321,7 +319,6 @@ function Search({ const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isExpenseReportType = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; - const {markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense} = useWideRHPActions(); const archivedReportsIdSet = useArchivedReportsIdSet(); @@ -1445,6 +1442,47 @@ function Search({ // dep-free useEffect above — see comment on didBailToFallbackState. didBailToFallbackState.current = false; + const isAnyVisibleActionLoading = useMemo( + () => filteredData.some((item) => 'reportID' in item && item.reportID && isActionLoadingSet.has(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${item.reportID}`)), + [filteredData, isActionLoadingSet], + ); + + const visibleDataLength = useMemo(() => filteredData.filter((item) => item.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || isOffline).length, [filteredData, isOffline]); + + const yearIndicators = useMemo( + () => + searchResults?.data + ? shouldShowYearUtil(searchResults.data, isExpenseReportType ?? false, undefined, !isExpenseReportType) + : { + shouldShowYearCreated: false, + shouldShowYearSubmitted: false, + shouldShowYearApproved: false, + shouldShowYearPosted: false, + shouldShowYearExported: false, + shouldShowYearWithdrawn: false, + }, + [searchResults?.data, isExpenseReportType], + ); + + const amountIndicators = useMemo( + () => (searchResults?.data ? getWideAmountIndicators(searchResults.data) : {shouldShowAmountInWideColumn: false, shouldShowTaxAmountInWideColumn: false}), + [searchResults?.data], + ); + + const onSortPress = useCallback( + (column: SearchColumnType, order: SortOrder) => { + clearSelectedTransactions(); + const newQuery = buildSearchQueryString({ + ...queryJSON, + sortBy: column, + sortOrder: order, + }); + onSortPressedCallback?.(); + navigation.setParams({q: newQuery, rawQuery: undefined}); + }, + [clearSelectedTransactions, queryJSON, onSortPressedCallback, navigation], + ); + // This is a performance optimization for the submit-expense->search path only. // The SearchPage skeleton (useSearchLoadingState) doesn't cover this case because // Search must mount for its onLayout to flush the deferred CreateMoneyRequest API write, which would block the JS thread causing a slowdown on post expense creation navigation @@ -1495,8 +1533,6 @@ function Search({ ); } - const isAnyVisibleActionLoading = filteredData.some((item) => 'reportID' in item && item.reportID && isActionLoadingSet.has(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${item.reportID}`)); - const visibleDataLength = filteredData.filter((item) => item.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || isOffline).length; // Guard: don't render the empty view while the data is transiently empty. // - shouldDeferHeavySearchWork: skeleton is still showing, data hasn't been computed yet. // - showPendingExpensePlaceholder: deferred write hasn't produced the optimistic item yet. @@ -1522,25 +1558,8 @@ function Search({ ); } - const onSortPress = (column: SearchColumnType, order: SortOrder) => { - clearSelectedTransactions(); - const newQuery = buildSearchQueryString({ - ...queryJSON, - sortBy: column, - sortOrder: order, - }); - onSortPressedCallback?.(); - // We want to explicitly clear stale rawQuery since it's only used for manually typed-in queries. - navigation.setParams({q: newQuery, rawQuery: undefined}); - }; - - const {shouldShowYearCreated, shouldShowYearSubmitted, shouldShowYearApproved, shouldShowYearPosted, shouldShowYearExported, shouldShowYearWithdrawn} = shouldShowYearUtil( - searchResults?.data, - isExpenseReportType ?? false, - undefined, - !isExpenseReportType, - ); - const {shouldShowAmountInWideColumn, shouldShowTaxAmountInWideColumn} = getWideAmountIndicators(searchResults?.data); + const {shouldShowYearCreated, shouldShowYearSubmitted, shouldShowYearApproved, shouldShowYearPosted, shouldShowYearExported, shouldShowYearWithdrawn} = yearIndicators; + const {shouldShowAmountInWideColumn, shouldShowTaxAmountInWideColumn} = amountIndicators; const shouldShowTableHeader = isLargeScreenWidth && !isChat; const tableHeaderVisible = canSelectMultiple || shouldShowTableHeader; From 010dc606ff81a15fe7403e044e3ad60cfccd310b Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Mon, 6 Apr 2026 14:32:04 +0530 Subject: [PATCH 39/46] Remove per-row useResponsiveLayout calls and pass isLargeScreenWidth as prop from parent components Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/AvatarWithTextCell.tsx | 5 ++--- .../Search/SearchList/ListItem/ExpenseReportListItem.tsx | 1 + .../SearchList/ListItem/ExpenseReportListItemRow.tsx | 7 +++++-- .../Search/SearchList/ListItem/MemberListItemHeader.tsx | 8 +++++--- .../Search/SearchList/ListItem/TaskListItemRow.tsx | 5 +++++ .../SearchList/ListItem/TransactionGroupListItem.tsx | 1 + .../Search/SearchList/ListItem/TransactionListItem.tsx | 1 + .../Search/SearchList/ListItem/UserInfoCell.tsx | 5 ++--- .../Search/SearchList/ListItem/UserInfoCellsWithArrow.tsx | 4 ++++ src/components/TransactionItemRow/index.tsx | 7 +++++-- 10 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/components/Search/SearchList/ListItem/AvatarWithTextCell.tsx b/src/components/Search/SearchList/ListItem/AvatarWithTextCell.tsx index 265d1a8b4322..89e876e8cd4d 100644 --- a/src/components/Search/SearchList/ListItem/AvatarWithTextCell.tsx +++ b/src/components/Search/SearchList/ListItem/AvatarWithTextCell.tsx @@ -2,7 +2,6 @@ import React from 'react'; import {View} from 'react-native'; import Avatar from '@components/Avatar'; import Text from '@components/Text'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import CONST from '@src/CONST'; import type {Icon} from '@src/types/onyx/OnyxCommon'; @@ -10,11 +9,11 @@ import type {Icon} from '@src/types/onyx/OnyxCommon'; type AvatarWithTextCellProps = { reportName?: string; icon?: Icon; + isLargeScreenWidth?: boolean; }; -function AvatarWithTextCell({reportName, icon}: AvatarWithTextCellProps) { +function AvatarWithTextCell({reportName, icon, isLargeScreenWidth}: AvatarWithTextCellProps) { const styles = useThemeStyles(); - const {isLargeScreenWidth} = useResponsiveLayout(); if (!reportName || !icon) { return null; diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx index a236524a8ebd..a4f7fee2f37b 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItem.tsx @@ -288,6 +288,7 @@ function ExpenseReportListItem({ isHovered={hovered} isFocused={isFocused} isPendingDelete={isPendingDelete} + isLargeScreenWidth={isLargeScreenWidth} /> {getDescription} diff --git a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx index 858004128de9..995a63590107 100644 --- a/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx +++ b/src/components/Search/SearchList/ListItem/ExpenseReportListItemRow.tsx @@ -10,7 +10,6 @@ import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; import usePolicy from '@hooks/usePolicy'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -49,6 +48,7 @@ type ExpenseReportListItemRowProps = { isFocused?: boolean; isPendingDelete?: boolean; columns?: SearchColumnType[]; + isLargeScreenWidth?: boolean; }; function ExpenseReportListItemRow({ @@ -67,13 +67,14 @@ function ExpenseReportListItemRow({ isHovered = false, isFocused = false, isPendingDelete = false, + isLargeScreenWidth = false, }: ExpenseReportListItemRowProps) { const StyleUtils = useStyleUtils(); const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const {isLargeScreenWidth, shouldUseNarrowLayout} = useResponsiveLayout(); + const shouldUseNarrowLayout = !isLargeScreenWidth; const policy = usePolicy(item.policyID); const expensifyIcons = useMemoizedLazyExpensifyIcons(['ArrowRight']); @@ -141,6 +142,7 @@ function ExpenseReportListItemRow({ accountID={item.from.accountID} avatar={item.from.avatar} displayName={item.formattedFrom ?? ''} + isLargeScreenWidth={isLargeScreenWidth} /> )} @@ -152,6 +154,7 @@ function ExpenseReportListItemRow({ accountID={item.to.accountID} avatar={item.to.avatar} displayName={item.formattedTo ?? ''} + isLargeScreenWidth={isLargeScreenWidth} /> )} diff --git a/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx b/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx index 7f5ab9aa20c2..cc5f3f518163 100644 --- a/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx +++ b/src/components/Search/SearchList/ListItem/MemberListItemHeader.tsx @@ -7,7 +7,6 @@ import type {ListItem} from '@components/SelectionList/types'; import TextWithTooltip from '@components/TextWithTooltip'; import UserDetailsTooltip from '@components/UserDetailsTooltip'; import useLocalize from '@hooks/useLocalize'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import {getDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; @@ -44,6 +43,9 @@ type MemberListItemHeaderProps = { /** The visible columns for the header */ columns?: SearchColumnType[]; + + /** Whether the screen is large */ + isLargeScreenWidth?: boolean; }; function MemberListItemHeader({ @@ -56,10 +58,10 @@ function MemberListItemHeader({ isExpanded, onDownArrowClick, columns, + isLargeScreenWidth, }: MemberListItemHeaderProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const {isLargeScreenWidth} = useResponsiveLayout(); const {translate, formatPhoneNumber} = useLocalize(); const formattedDisplayName = formatPhoneNumber(getDisplayNameOrDefault(memberItem)); const formattedLogin = formatPhoneNumber(memberItem.login ?? ''); @@ -141,7 +143,7 @@ function MemberListItemHeader({ )} - {isLargeScreenWidth && ( + {!!isLargeScreenWidth && ( <> diff --git a/src/components/Search/SearchList/ListItem/TaskListItemRow.tsx b/src/components/Search/SearchList/ListItem/TaskListItemRow.tsx index 01a33213dc2f..ad7c78b1dbe0 100644 --- a/src/components/Search/SearchList/ListItem/TaskListItemRow.tsx +++ b/src/components/Search/SearchList/ListItem/TaskListItemRow.tsx @@ -134,6 +134,7 @@ function TaskListItemRow({item, containerStyle, showTooltip}: TaskListItemRowPro accountID={item.createdBy.accountID} avatar={item.createdBy.avatar} displayName={item.formattedCreatedBy} + isLargeScreenWidth={isLargeScreenWidth} /> @@ -150,6 +151,7 @@ function TaskListItemRow({item, containerStyle, showTooltip}: TaskListItemRowPro @@ -229,12 +231,14 @@ function TaskListItemRow({item, containerStyle, showTooltip}: TaskListItemRowPro accountID={item.createdBy.accountID} avatar={item.createdBy.avatar} displayName={item.formattedCreatedBy} + isLargeScreenWidth={isLargeScreenWidth} /> @@ -242,6 +246,7 @@ function TaskListItemRow({item, containerStyle, showTooltip}: TaskListItemRowPro accountID={item.assignee.accountID} avatar={item.assignee.avatar} displayName={item.formattedAssignee} + isLargeScreenWidth={isLargeScreenWidth} /> diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx index c12b3db0372d..9ecfcce6ea13 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx @@ -259,6 +259,7 @@ function TransactionGroupListItem({ isIndeterminate={isIndeterminate} onDownArrowClick={onExpandIconPress} isExpanded={isExpanded} + isLargeScreenWidth={isLargeScreenWidth} /> ), [CONST.SEARCH.GROUP_BY.CARD]: ( diff --git a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx index 72782dfae3f0..75318a39a1f7 100644 --- a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx @@ -232,6 +232,7 @@ function TransactionListItem({ onButtonPress={handleActionButtonPress} onCheckboxPress={() => onCheckboxPress?.(item)} shouldUseNarrowLayout={!isLargeScreenWidth} + isLargeScreenWidth={isLargeScreenWidth} columns={columns} isActionLoading={isLoading ?? isActionLoading} isSelected={!!transactionItem.isSelected} diff --git a/src/components/Search/SearchList/ListItem/UserInfoCell.tsx b/src/components/Search/SearchList/ListItem/UserInfoCell.tsx index fe3e429e7ef0..07677d9229ac 100644 --- a/src/components/Search/SearchList/ListItem/UserInfoCell.tsx +++ b/src/components/Search/SearchList/ListItem/UserInfoCell.tsx @@ -3,7 +3,6 @@ import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; import {View} from 'react-native'; import Avatar from '@components/Avatar'; import Text from '@components/Text'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; import {isCorrectSearchUserName} from '@libs/SearchUIUtils'; import type {AvatarSource} from '@libs/UserAvatarUtils'; @@ -18,11 +17,11 @@ type UserInfoCellProps = { containerStyle?: StyleProp; textStyle?: TextStyle; avatarStyle?: ViewStyle; + isLargeScreenWidth?: boolean; }; -function UserInfoCell({avatar, accountID, displayName, avatarSize, containerStyle, textStyle, avatarStyle}: UserInfoCellProps) { +function UserInfoCell({avatar, accountID, displayName, avatarSize, containerStyle, textStyle, avatarStyle, isLargeScreenWidth}: UserInfoCellProps) { const styles = useThemeStyles(); - const {isLargeScreenWidth} = useResponsiveLayout(); if (!isCorrectSearchUserName(displayName) || !accountID) { return null; diff --git a/src/components/Search/SearchList/ListItem/UserInfoCellsWithArrow.tsx b/src/components/Search/SearchList/ListItem/UserInfoCellsWithArrow.tsx index 3f1fbb04a6c7..be256cf3f69c 100644 --- a/src/components/Search/SearchList/ListItem/UserInfoCellsWithArrow.tsx +++ b/src/components/Search/SearchList/ListItem/UserInfoCellsWithArrow.tsx @@ -24,6 +24,7 @@ function UserInfoCellsWithArrow({ infoCellsAvatarStyle, fromRecipientStyle, shouldUseArrowIcon = true, + isLargeScreenWidth, }: { shouldShowToRecipient: boolean; participantFrom: PersonalDetails; @@ -36,6 +37,7 @@ function UserInfoCellsWithArrow({ infoCellsAvatarStyle?: ViewStyle; fromRecipientStyle?: ViewStyle; shouldUseArrowIcon?: boolean; + isLargeScreenWidth?: boolean; }) { const icons = useMemoizedLazyExpensifyIcons(['ArrowRightLong']); const styles = useThemeStyles(); @@ -56,6 +58,7 @@ function UserInfoCellsWithArrow({ textStyle={infoCellsTextStyle} avatarStyle={infoCellsAvatarStyle} containerStyle={[styles.mw50, styles.flexShrink1, fromRecipientStyle]} + isLargeScreenWidth={isLargeScreenWidth} /> {shouldShowToRecipient && ( <> @@ -83,6 +86,7 @@ function UserInfoCellsWithArrow({ textStyle={infoCellsTextStyle} avatarStyle={infoCellsAvatarStyle} containerStyle={[styles.mw50, styles.flexShrink1, fromRecipientStyle, styles.mlHalf]} + isLargeScreenWidth={isLargeScreenWidth} /> )} diff --git a/src/components/TransactionItemRow/index.tsx b/src/components/TransactionItemRow/index.tsx index d93f6f78d5b7..dd69ba2830a2 100644 --- a/src/components/TransactionItemRow/index.tsx +++ b/src/components/TransactionItemRow/index.tsx @@ -20,7 +20,6 @@ import Text from '@components/Text'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; -import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -147,6 +146,7 @@ type TransactionItemRowProps = { customCardNames?: Record; reportActions?: ReportAction[]; checkboxSentryLabel?: string; + isLargeScreenWidth?: boolean; }; const EMPTY_ACTIVE_STYLE: StyleProp = []; @@ -199,12 +199,13 @@ function TransactionItemRow({ customCardNames, reportActions, checkboxSentryLabel, + isLargeScreenWidth: isLargeScreenWidthProp, }: TransactionItemRowProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const StyleUtils = useStyleUtils(); const theme = useTheme(); - const {isLargeScreenWidth} = useResponsiveLayout(); + const isLargeScreenWidth = isLargeScreenWidthProp ?? !shouldUseNarrowLayout; const hasCategoryOrTag = !isCategoryMissing(transactionItem?.category) || !!transactionItem.tag; const createdAt = getTransactionCreated(transactionItem); const expensicons = useMemoizedLazyExpensifyIcons(['ArrowRight']); @@ -490,6 +491,7 @@ function TransactionItemRow({ accountID={transactionItem.to.accountID} avatar={transactionItem.to.avatar} displayName={transactionItem.formattedTo ?? transactionItem.to.displayName ?? ''} + isLargeScreenWidth={isLargeScreenWidth} /> )} @@ -505,6 +507,7 @@ function TransactionItemRow({ accountID={transactionItem.from.accountID} avatar={transactionItem.from.avatar} displayName={transactionItem.formattedFrom ?? transactionItem.from.displayName ?? ''} + isLargeScreenWidth={isLargeScreenWidth} /> )} From 53804379bbcea392cf1e3f681c0134b919743249 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Mon, 6 Apr 2026 14:32:56 +0530 Subject: [PATCH 40/46] Revert "Optimize Search component performance by consolidating hooks and memoizing render-path computations" This reverts commit e1341907857a95be0ef75bc99f72b55aa567feae. --- src/components/Search/index.tsx | 73 ++++++++++++--------------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 7430397624ec..dd198996e4f3 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -281,12 +281,14 @@ function Search({ const {isOffline} = useNetwork(); const prevIsOffline = usePrevious(isOffline); - // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth - const {shouldUseNarrowLayout, isSmallScreenWidth, isLargeScreenWidth} = useResponsiveLayout(); + const {shouldUseNarrowLayout} = useResponsiveLayout(); const styles = useThemeStyles(); + // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout for enabling the selection mode on small screens only + // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth + const {isSmallScreenWidth, isLargeScreenWidth} = useResponsiveLayout(); const navigation = useNavigation>(); const isFocused = useIsFocused(); - const {markReportIDAsExpense, markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense} = useWideRHPActions(); + const {markReportIDAsExpense} = useWideRHPActions(); const { currentSearchHash, currentSearchKey, @@ -319,6 +321,7 @@ function Search({ const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isExpenseReportType = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; + const {markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense} = useWideRHPActions(); const archivedReportsIdSet = useArchivedReportsIdSet(); @@ -1442,47 +1445,6 @@ function Search({ // dep-free useEffect above — see comment on didBailToFallbackState. didBailToFallbackState.current = false; - const isAnyVisibleActionLoading = useMemo( - () => filteredData.some((item) => 'reportID' in item && item.reportID && isActionLoadingSet.has(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${item.reportID}`)), - [filteredData, isActionLoadingSet], - ); - - const visibleDataLength = useMemo(() => filteredData.filter((item) => item.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || isOffline).length, [filteredData, isOffline]); - - const yearIndicators = useMemo( - () => - searchResults?.data - ? shouldShowYearUtil(searchResults.data, isExpenseReportType ?? false, undefined, !isExpenseReportType) - : { - shouldShowYearCreated: false, - shouldShowYearSubmitted: false, - shouldShowYearApproved: false, - shouldShowYearPosted: false, - shouldShowYearExported: false, - shouldShowYearWithdrawn: false, - }, - [searchResults?.data, isExpenseReportType], - ); - - const amountIndicators = useMemo( - () => (searchResults?.data ? getWideAmountIndicators(searchResults.data) : {shouldShowAmountInWideColumn: false, shouldShowTaxAmountInWideColumn: false}), - [searchResults?.data], - ); - - const onSortPress = useCallback( - (column: SearchColumnType, order: SortOrder) => { - clearSelectedTransactions(); - const newQuery = buildSearchQueryString({ - ...queryJSON, - sortBy: column, - sortOrder: order, - }); - onSortPressedCallback?.(); - navigation.setParams({q: newQuery, rawQuery: undefined}); - }, - [clearSelectedTransactions, queryJSON, onSortPressedCallback, navigation], - ); - // This is a performance optimization for the submit-expense->search path only. // The SearchPage skeleton (useSearchLoadingState) doesn't cover this case because // Search must mount for its onLayout to flush the deferred CreateMoneyRequest API write, which would block the JS thread causing a slowdown on post expense creation navigation @@ -1533,6 +1495,8 @@ function Search({ ); } + const isAnyVisibleActionLoading = filteredData.some((item) => 'reportID' in item && item.reportID && isActionLoadingSet.has(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${item.reportID}`)); + const visibleDataLength = filteredData.filter((item) => item.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || isOffline).length; // Guard: don't render the empty view while the data is transiently empty. // - shouldDeferHeavySearchWork: skeleton is still showing, data hasn't been computed yet. // - showPendingExpensePlaceholder: deferred write hasn't produced the optimistic item yet. @@ -1558,8 +1522,25 @@ function Search({ ); } - const {shouldShowYearCreated, shouldShowYearSubmitted, shouldShowYearApproved, shouldShowYearPosted, shouldShowYearExported, shouldShowYearWithdrawn} = yearIndicators; - const {shouldShowAmountInWideColumn, shouldShowTaxAmountInWideColumn} = amountIndicators; + const onSortPress = (column: SearchColumnType, order: SortOrder) => { + clearSelectedTransactions(); + const newQuery = buildSearchQueryString({ + ...queryJSON, + sortBy: column, + sortOrder: order, + }); + onSortPressedCallback?.(); + // We want to explicitly clear stale rawQuery since it's only used for manually typed-in queries. + navigation.setParams({q: newQuery, rawQuery: undefined}); + }; + + const {shouldShowYearCreated, shouldShowYearSubmitted, shouldShowYearApproved, shouldShowYearPosted, shouldShowYearExported, shouldShowYearWithdrawn} = shouldShowYearUtil( + searchResults?.data, + isExpenseReportType ?? false, + undefined, + !isExpenseReportType, + ); + const {shouldShowAmountInWideColumn, shouldShowTaxAmountInWideColumn} = getWideAmountIndicators(searchResults?.data); const shouldShowTableHeader = isLargeScreenWidth && !isChat; const tableHeaderVisible = canSelectMultiple || shouldShowTableHeader; From 281652084723e79e7ca9ec17ba3c25d71872195a Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Mon, 6 Apr 2026 14:41:42 +0530 Subject: [PATCH 41/46] Reapply "Optimize Search component performance by consolidating hooks and memoizing render-path computations" This reverts commit 53804379bbcea392cf1e3f681c0134b919743249. --- src/components/Search/index.tsx | 73 +++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index dd198996e4f3..7430397624ec 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -281,14 +281,12 @@ function Search({ const {isOffline} = useNetwork(); const prevIsOffline = usePrevious(isOffline); - const {shouldUseNarrowLayout} = useResponsiveLayout(); - const styles = useThemeStyles(); - // We need to use isSmallScreenWidth instead of shouldUseNarrowLayout for enabling the selection mode on small screens only // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth - const {isSmallScreenWidth, isLargeScreenWidth} = useResponsiveLayout(); + const {shouldUseNarrowLayout, isSmallScreenWidth, isLargeScreenWidth} = useResponsiveLayout(); + const styles = useThemeStyles(); const navigation = useNavigation>(); const isFocused = useIsFocused(); - const {markReportIDAsExpense} = useWideRHPActions(); + const {markReportIDAsExpense, markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense} = useWideRHPActions(); const { currentSearchHash, currentSearchKey, @@ -321,7 +319,6 @@ function Search({ const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); const isExpenseReportType = type === CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT; - const {markReportIDAsMultiTransactionExpense, unmarkReportIDAsMultiTransactionExpense} = useWideRHPActions(); const archivedReportsIdSet = useArchivedReportsIdSet(); @@ -1445,6 +1442,47 @@ function Search({ // dep-free useEffect above — see comment on didBailToFallbackState. didBailToFallbackState.current = false; + const isAnyVisibleActionLoading = useMemo( + () => filteredData.some((item) => 'reportID' in item && item.reportID && isActionLoadingSet.has(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${item.reportID}`)), + [filteredData, isActionLoadingSet], + ); + + const visibleDataLength = useMemo(() => filteredData.filter((item) => item.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || isOffline).length, [filteredData, isOffline]); + + const yearIndicators = useMemo( + () => + searchResults?.data + ? shouldShowYearUtil(searchResults.data, isExpenseReportType ?? false, undefined, !isExpenseReportType) + : { + shouldShowYearCreated: false, + shouldShowYearSubmitted: false, + shouldShowYearApproved: false, + shouldShowYearPosted: false, + shouldShowYearExported: false, + shouldShowYearWithdrawn: false, + }, + [searchResults?.data, isExpenseReportType], + ); + + const amountIndicators = useMemo( + () => (searchResults?.data ? getWideAmountIndicators(searchResults.data) : {shouldShowAmountInWideColumn: false, shouldShowTaxAmountInWideColumn: false}), + [searchResults?.data], + ); + + const onSortPress = useCallback( + (column: SearchColumnType, order: SortOrder) => { + clearSelectedTransactions(); + const newQuery = buildSearchQueryString({ + ...queryJSON, + sortBy: column, + sortOrder: order, + }); + onSortPressedCallback?.(); + navigation.setParams({q: newQuery, rawQuery: undefined}); + }, + [clearSelectedTransactions, queryJSON, onSortPressedCallback, navigation], + ); + // This is a performance optimization for the submit-expense->search path only. // The SearchPage skeleton (useSearchLoadingState) doesn't cover this case because // Search must mount for its onLayout to flush the deferred CreateMoneyRequest API write, which would block the JS thread causing a slowdown on post expense creation navigation @@ -1495,8 +1533,6 @@ function Search({ ); } - const isAnyVisibleActionLoading = filteredData.some((item) => 'reportID' in item && item.reportID && isActionLoadingSet.has(`${ONYXKEYS.COLLECTION.REPORT_METADATA}${item.reportID}`)); - const visibleDataLength = filteredData.filter((item) => item.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || isOffline).length; // Guard: don't render the empty view while the data is transiently empty. // - shouldDeferHeavySearchWork: skeleton is still showing, data hasn't been computed yet. // - showPendingExpensePlaceholder: deferred write hasn't produced the optimistic item yet. @@ -1522,25 +1558,8 @@ function Search({ ); } - const onSortPress = (column: SearchColumnType, order: SortOrder) => { - clearSelectedTransactions(); - const newQuery = buildSearchQueryString({ - ...queryJSON, - sortBy: column, - sortOrder: order, - }); - onSortPressedCallback?.(); - // We want to explicitly clear stale rawQuery since it's only used for manually typed-in queries. - navigation.setParams({q: newQuery, rawQuery: undefined}); - }; - - const {shouldShowYearCreated, shouldShowYearSubmitted, shouldShowYearApproved, shouldShowYearPosted, shouldShowYearExported, shouldShowYearWithdrawn} = shouldShowYearUtil( - searchResults?.data, - isExpenseReportType ?? false, - undefined, - !isExpenseReportType, - ); - const {shouldShowAmountInWideColumn, shouldShowTaxAmountInWideColumn} = getWideAmountIndicators(searchResults?.data); + const {shouldShowYearCreated, shouldShowYearSubmitted, shouldShowYearApproved, shouldShowYearPosted, shouldShowYearExported, shouldShowYearWithdrawn} = yearIndicators; + const {shouldShowAmountInWideColumn, shouldShowTaxAmountInWideColumn} = amountIndicators; const shouldShowTableHeader = isLargeScreenWidth && !isChat; const tableHeaderVisible = canSelectMultiple || shouldShowTableHeader; From d917788cabeb0835b39c9607f25608a554cd9a2c Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Wed, 8 Apr 2026 14:22:37 +0530 Subject: [PATCH 42/46] Fix intermittent border disappearance on group list items by using borderTop instead of borderBottom Signed-off-by: krishna2323 --- .../SearchList/ListItem/TransactionGroupListItem.tsx | 3 ++- src/components/Search/SearchList/index.tsx | 1 + src/components/SelectionList/ListItem/types.ts | 3 +++ src/styles/utils/index.ts | 7 +++++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx index 9ecfcce6ea13..e4bcc59f75c6 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListItem.tsx @@ -77,6 +77,7 @@ function TransactionGroupListItem({ newTransactionID, lastPaymentMethod, personalPolicyID, + isFirstItem, isLastItem, }: TransactionGroupListItemProps) { const groupItem = item as unknown as TransactionGroupListItemType; @@ -480,7 +481,7 @@ function TransactionGroupListItem({ styles.mh5, animatedHighlightStyle, styles.userSelectNone, - isLargeScreenWidth && StyleUtils.getSearchTableRowBorderStyle(isLastItem, isItemSelected), + isLargeScreenWidth && StyleUtils.getSearchTableGroupRowBorderStyle(isFirstItem, isLastItem, isItemSelected), ]} > {({hovered}) => ( diff --git a/src/components/Search/SearchList/index.tsx b/src/components/Search/SearchList/index.tsx index b9ec33a06af8..391a1699a199 100644 --- a/src/components/Search/SearchList/index.tsx +++ b/src/components/Search/SearchList/index.tsx @@ -451,6 +451,7 @@ function SearchList({ onFocus={onFocus} newTransactionID={newTransactionID} keyForList={item.keyForList} + isFirstItem={index === 0} isLastItem={index === data.length - 1 && !ListFooterComponent} /> diff --git a/src/components/SelectionList/ListItem/types.ts b/src/components/SelectionList/ListItem/types.ts index 246795301c62..0dbd19174807 100644 --- a/src/components/SelectionList/ListItem/types.ts +++ b/src/components/SelectionList/ListItem/types.ts @@ -297,6 +297,9 @@ type ListItemProps = CommonListItemProps & { /** Whether this is the last item in the list (for border radius on desktop) */ isLastItem?: boolean; + + /** Whether this is the first item in the list (for border styling on desktop) */ + isFirstItem?: boolean; }; type ValidListItem = diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 2382b093b39d..d48a65e6555a 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1800,6 +1800,13 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ ...(isLastItem ? styles.searchTableBottomRadius : {}), }), + getSearchTableGroupRowBorderStyle: (isFirstItem?: boolean, isLastItem?: boolean, isSelected?: boolean): ViewStyle => ({ + borderRadius: 0, + borderTopWidth: isFirstItem ? 0 : 1, + borderColor: isSelected ? theme.buttonHoveredBG : theme.border, + ...(isLastItem ? styles.searchTableBottomRadius : {}), + }), + getSearchTableRowPressableStyle: (isLastItem?: boolean, isSelected?: boolean, padding?: {vertical?: number; horizontal?: number}): ViewStyle => ({ minHeight: variables.tableRowHeight, borderRadius: 0, From dc1fa5cd6e2a6b4edfc866f992251bdb49e4b9ba Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Wed, 8 Apr 2026 19:54:00 +0530 Subject: [PATCH 43/46] Fix missing border radius on TransactionListItem cards on medium screen widths Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/TransactionListItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx index 75318a39a1f7..2f0e09865055 100644 --- a/src/components/Search/SearchList/ListItem/TransactionListItem.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionListItem.tsx @@ -246,7 +246,7 @@ function TransactionListItem({ taxAmountColumnSize={taxAmountColumnSize} shouldShowCheckbox={!!canSelectMultiple} checkboxSentryLabel={CONST.SENTRY_LABEL.SEARCH.TRANSACTION_LIST_ITEM_CHECKBOX} - style={[styles.p3, styles.pv2, shouldUseNarrowLayout ? styles.pt2 : styles.noBorderRadius]} + style={[styles.p3, styles.pv2, shouldUseNarrowLayout ? styles.pt2 : isLargeScreenWidth && styles.noBorderRadius]} violations={transactionViolations} onArrowRightPress={() => onSelectRow(item, transactionPreviewData)} isHover={hovered} From 8740afe8928c67613df53236b01cd1749a5fe598 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 9 Apr 2026 18:25:59 +0530 Subject: [PATCH 44/46] Fix expanded group header column alignment and restore skeleton comments Signed-off-by: krishna2323 --- .../Search/SearchList/ListItem/TransactionGroupListExpanded.tsx | 2 +- src/components/Skeletons/SearchRowSkeleton.tsx | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx index 4bff1e468d91..f1e9ff633a65 100644 --- a/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx +++ b/src/components/Search/SearchList/ListItem/TransactionGroupListExpanded.tsx @@ -193,7 +193,7 @@ function TransactionGroupListExpanded({ {isLargeScreenWidth && !(isEmpty && shouldDisplayLoadingIndicator) && ( <> - + Date: Thu, 9 Apr 2026 18:35:00 +0530 Subject: [PATCH 45/46] Fix early exit in shouldShowYear skipping shouldShowYearWithdrawn flag Signed-off-by: krishna2323 --- src/libs/SearchUIUtils.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libs/SearchUIUtils.ts b/src/libs/SearchUIUtils.ts index 9646f53072fc..d37f6b8a45d6 100644 --- a/src/libs/SearchUIUtils.ts +++ b/src/libs/SearchUIUtils.ts @@ -1574,7 +1574,14 @@ function shouldShowYear( } // Early exit if all flags are true - if (result.shouldShowYearCreated && result.shouldShowYearSubmitted && result.shouldShowYearApproved && result.shouldShowYearPosted && result.shouldShowYearExported) { + if ( + result.shouldShowYearCreated && + result.shouldShowYearSubmitted && + result.shouldShowYearApproved && + result.shouldShowYearPosted && + result.shouldShowYearExported && + result.shouldShowYearWithdrawn + ) { return result; } } From 56ea84043df85bb09e08db2ccc968faffab860c4 Mon Sep 17 00:00:00 2001 From: krishna2323 Date: Thu, 9 Apr 2026 18:48:04 +0530 Subject: [PATCH 46/46] Fix shouldShowYear edge cases for withdrawn flag and task date columns Signed-off-by: krishna2323 --- src/components/Search/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 7430397624ec..414b475b3b94 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -1452,7 +1452,7 @@ function Search({ const yearIndicators = useMemo( () => searchResults?.data - ? shouldShowYearUtil(searchResults.data, isExpenseReportType ?? false, undefined, !isExpenseReportType) + ? shouldShowYearUtil(searchResults.data, isExpenseReportType ?? false, undefined, type === CONST.SEARCH.DATA_TYPES.EXPENSE) : { shouldShowYearCreated: false, shouldShowYearSubmitted: false, @@ -1461,7 +1461,7 @@ function Search({ shouldShowYearExported: false, shouldShowYearWithdrawn: false, }, - [searchResults?.data, isExpenseReportType], + [searchResults?.data, isExpenseReportType, type], ); const amountIndicators = useMemo(