diff --git a/src/components/ButtonWithDropdownMenu/index.tsx b/src/components/ButtonWithDropdownMenu/index.tsx index 8033ac51c1fd..efa05d4482a3 100644 --- a/src/components/ButtonWithDropdownMenu/index.tsx +++ b/src/components/ButtonWithDropdownMenu/index.tsx @@ -59,6 +59,7 @@ function ButtonWithDropdownMenu({ref, ...props}: ButtonWithDropdownM shouldUseModalPaddingStyle = true, shouldUseShortForm = false, shouldUseOptionIcon = false, + shouldStayNormalOnDisable = false, } = props; const theme = useTheme(); @@ -169,6 +170,7 @@ function ButtonWithDropdownMenu({ref, ...props}: ButtonWithDropdownM onPress={handlePress} text={customText ?? selectedItem?.text ?? ''} isDisabled={isDisabled || areAllOptionsDisabled} + shouldStayNormalOnDisable={shouldStayNormalOnDisable} isLoading={isLoading} shouldRemoveRightBorderRadius style={isSplitButton ? [styles.flex1, styles.pr0] : {}} @@ -192,6 +194,7 @@ function ButtonWithDropdownMenu({ref, ...props}: ButtonWithDropdownM ref={dropdownAnchor} success={success} isDisabled={isDisabled} + shouldStayNormalOnDisable={shouldStayNormalOnDisable} style={[styles.pl0]} onPress={() => setIsMenuVisible(!isMenuVisible)} shouldRemoveLeftBorderRadius @@ -232,6 +235,7 @@ function ButtonWithDropdownMenu({ref, ...props}: ButtonWithDropdownM ref={buttonRef} pressOnEnter={pressOnEnter} isDisabled={isDisabled || !!options.at(0)?.disabled} + shouldStayNormalOnDisable={shouldStayNormalOnDisable} style={[styles.w100, style]} disabledStyle={disabledStyle} isLoading={isLoading} diff --git a/src/components/ButtonWithDropdownMenu/types.ts b/src/components/ButtonWithDropdownMenu/types.ts index 1f36af13c57b..70d6aafc292d 100644 --- a/src/components/ButtonWithDropdownMenu/types.ts +++ b/src/components/ButtonWithDropdownMenu/types.ts @@ -85,6 +85,9 @@ type ButtonWithDropdownMenuProps = { /** Should the confirmation button be disabled? */ isDisabled?: boolean; + /** Whether the button should stay visually normal even when disabled. */ + shouldStayNormalOnDisable?: boolean; + /** Additional styles to add to the component */ style?: StyleProp; diff --git a/src/components/SelectionListWithSections/Search/ActionCell.tsx b/src/components/SelectionListWithSections/Search/ActionCell.tsx index 8b837ed093e3..223a419f2f52 100644 --- a/src/components/SelectionListWithSections/Search/ActionCell.tsx +++ b/src/components/SelectionListWithSections/Search/ActionCell.tsx @@ -50,6 +50,7 @@ type ActionCellProps = { hash?: number; amount?: number; extraSmall?: boolean; + shouldDisablePointerEvents?: boolean; }; function ActionCell({ @@ -65,6 +66,7 @@ function ActionCell({ hash, amount, extraSmall = false, + shouldDisablePointerEvents, }: ActionCellProps) { const {translate} = useLocalize(); const theme = useTheme(); @@ -94,7 +96,11 @@ function ActionCell({ if (!isChildListItem && ((parentAction !== CONST.SEARCH.ACTION_TYPES.PAID && action === CONST.SEARCH.ACTION_TYPES.PAID) || action === CONST.SEARCH.ACTION_TYPES.DONE)) { return ( - + confirmPayment(type as ValueOf, payAsBusiness, methodID, paymentMethod)} - style={[styles.w100]} + style={[styles.w100, shouldDisablePointerEvents && styles.pointerEventsNone]} wrapperStyle={[styles.w100]} shouldShowPersonalBankAccountOption={!policyID && !iouReport?.policyID} - isDisabled={isOffline} + isDisabled={isOffline || shouldDisablePointerEvents} + shouldStayNormalOnDisable={shouldDisablePointerEvents} isLoading={isLoading} onlyShowPayElsewhere={shouldOnlyShowElsewhere} /> @@ -169,10 +178,11 @@ function ActionCell({ onPress={goToItem} small={!extraSmall} extraSmall={extraSmall} - style={[styles.w100]} + style={[styles.w100, shouldDisablePointerEvents && styles.pointerEventsNone]} isLoading={isLoading} success - isDisabled={isOffline} + isDisabled={isOffline || shouldDisablePointerEvents} + shouldStayNormalOnDisable={shouldDisablePointerEvents} isNested /> ); diff --git a/src/components/SelectionListWithSections/Search/ReportListItemHeader.tsx b/src/components/SelectionListWithSections/Search/ReportListItemHeader.tsx index b59158413b9b..797c201c674e 100644 --- a/src/components/SelectionListWithSections/Search/ReportListItemHeader.tsx +++ b/src/components/SelectionListWithSections/Search/ReportListItemHeader.tsx @@ -232,7 +232,6 @@ function ReportListItemHeader({ currentSearchHash, reportItem, () => onSelectRow(reportItem as unknown as TItem), - shouldUseNarrowLayout && !!canSelectMultiple, snapshotReport, snapshotPolicy, lastPaymentMethod, @@ -247,6 +246,7 @@ function ReportListItemHeader({ handleActionButtonPress={handleOnButtonPress} shouldShowUserInfo={showUserInfo} containerStyles={[styles.pr3, styles.mb2]} + isInMobileSelectionMode={shouldUseNarrowLayout && !!canSelectMultiple} /> ({ currentSearchHash, transactionItem, () => onSelectRow(item, transactionPreviewData), - shouldUseNarrowLayout && !!canSelectMultiple, snapshotReport, snapshotPolicy, lastPaymentMethod, currentSearchKey, onDEWModalOpen, ); - }, [ - currentSearchHash, - transactionItem, - transactionPreviewData, - shouldUseNarrowLayout, - canSelectMultiple, - snapshotReport, - snapshotPolicy, - lastPaymentMethod, - currentSearchKey, - onSelectRow, - item, - onDEWModalOpen, - ]); + }, [currentSearchHash, transactionItem, transactionPreviewData, snapshotReport, snapshotPolicy, lastPaymentMethod, currentSearchKey, onSelectRow, item, onDEWModalOpen]); const handleCheckboxPress = useCallback(() => { onCheckboxPress?.(item); @@ -179,6 +165,7 @@ function TransactionListItem({ item={transactionItem} handleActionButtonPress={handleActionButtonPress} shouldShowUserInfo={!!transactionItem?.from} + isInMobileSelectionMode={shouldUseNarrowLayout && !!canSelectMultiple} /> )} void; shouldShowUserInfo: boolean; containerStyles?: StyleProp; + isInMobileSelectionMode: boolean; }) { const styles = useThemeStyles(); const {isLargeScreenWidth} = useResponsiveLayout(); @@ -39,6 +41,7 @@ function UserInfoAndActionButtonRow({ const shouldShowToRecipient = hasFromSender && hasToRecipient && !!item?.to?.accountID && !!isCorrectSearchUserName(participantToDisplayName); const snapshotReport = useMemo(() => { + // eslint-disable-next-line @typescript-eslint/no-deprecated return (snapshot?.data?.[`${ONYXKEYS.COLLECTION.REPORT}${transactionItem.reportID}`] ?? {}) as SearchReport; }, [snapshot, transactionItem.reportID]); @@ -80,6 +83,7 @@ function UserInfoAndActionButtonRow({ hash={item.hash} amount={(item as TransactionListItemType)?.amount ?? (item as TransactionReportGroupListItemType)?.total} extraSmall={!isLargeScreenWidth} + shouldDisablePointerEvents={isInMobileSelectionMode} /> diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 7ee3b2146793..83d51cacb11e 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -70,6 +70,7 @@ function SettlementButton({ enablePaymentsRoute, iouReport, isDisabled = false, + shouldStayNormalOnDisable = false, isLoading = false, formattedAmount = '', onPress, @@ -564,6 +565,7 @@ function SettlementButton({ menuHeaderText={isInvoiceReport ? translate('workspace.invoices.paymentMethods.chooseInvoiceMethod') : undefined} isSplitButton={shouldUseSplitButton} isDisabled={isDisabled} + shouldStayNormalOnDisable={shouldStayNormalOnDisable} isLoading={isLoading} defaultSelectedIndex={defaultSelectedIndex !== -1 ? defaultSelectedIndex : 0} onPress={(event, iouPaymentType) => handlePaymentSelection(event, iouPaymentType, triggerKYCFlow)} diff --git a/src/components/SettlementButton/types.ts b/src/components/SettlementButton/types.ts index 06690bac0f40..6355f78566a8 100644 --- a/src/components/SettlementButton/types.ts +++ b/src/components/SettlementButton/types.ts @@ -71,6 +71,9 @@ type SettlementButtonProps = { /** Whether the button should be disabled */ isDisabled?: boolean; + /** Whether the button should stay visually normal even when disabled. */ + shouldStayNormalOnDisable?: boolean; + /** Whether we should show a loading state for the main button */ isLoading?: boolean; diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts index 5b2eb1471591..f9a897a18b26 100644 --- a/src/libs/actions/Search.ts +++ b/src/libs/actions/Search.ts @@ -70,7 +70,6 @@ function handleActionButtonPress( hash: number, item: TransactionListItemType | TransactionReportGroupListItemType, goToItem: () => void, - isInMobileSelectionMode: boolean, // eslint-disable-next-line @typescript-eslint/no-deprecated snapshotReport: SearchReport, snapshotPolicy: Policy, @@ -84,7 +83,7 @@ function handleActionButtonPress( const allReportTransactions = (isTransactionGroupListItemType(item) ? item.transactions : [item]) as SearchTransaction[]; const hasHeldExpense = hasHeldExpenses('', allReportTransactions); - if (hasHeldExpense || isInMobileSelectionMode) { + if (hasHeldExpense) { goToItem(); return; } diff --git a/tests/unit/Search/handleActionButtonPressTest.ts b/tests/unit/Search/handleActionButtonPressTest.ts index 1076c6c92014..0c28b5bf63dd 100644 --- a/tests/unit/Search/handleActionButtonPressTest.ts +++ b/tests/unit/Search/handleActionButtonPressTest.ts @@ -287,21 +287,14 @@ describe('handleActionButtonPress', () => { test('Should navigate to item when report has one transaction on hold', () => { const goToItem = jest.fn(() => {}); // @ts-expect-error: Allow partial record in snapshot update for testing - handleActionButtonPress(searchHash, mockReportItemWithHold, goToItem, false, snapshotReport, snapshotPolicy, mockLastPaymentMethod); + handleActionButtonPress(searchHash, mockReportItemWithHold, goToItem, snapshotReport, snapshotPolicy, mockLastPaymentMethod); expect(goToItem).toHaveBeenCalledTimes(1); }); test('Should not navigate to item when the hold is removed', () => { const goToItem = jest.fn(() => {}); // @ts-expect-error: Allow partial record in snapshot update for testing - handleActionButtonPress(searchHash, updatedMockReportItem, goToItem, false, snapshotReport, snapshotPolicy, mockLastPaymentMethod); + handleActionButtonPress(searchHash, updatedMockReportItem, goToItem, snapshotReport, snapshotPolicy, mockLastPaymentMethod); expect(goToItem).toHaveBeenCalledTimes(0); }); - - test('Should run goToItem callback when user is in mobile selection mode', () => { - const goToItem = jest.fn(() => {}); - // @ts-expect-error: Allow partial record in snapshot update for testing - handleActionButtonPress(searchHash, updatedMockReportItem, goToItem, true, snapshotReport, snapshotPolicy, mockLastPaymentMethod); - expect(goToItem).toHaveBeenCalledTimes(1); - }); });