Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions app/components/UI/Bridge/Views/BridgeView/BridgeView.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { MOCK_ENTROPY_SOURCE as mockEntropySource } from '../../../../../util/te
import { RootState } from '../../../../../reducers';
import { mockQuoteWithMetadata } from '../../_mocks_/bridgeQuoteWithMetadata';
import { BridgeTrendingTokensSectionTestIds } from '../../components/BridgeTrendingTokensSection/BridgeTrendingTokensSection.testIds';
import { Button } from '@metamask/design-system-react-native';

// Mock the account-tree-controller file that imports the problematic module
jest.mock(
Expand Down Expand Up @@ -1536,14 +1537,21 @@ describe('BridgeView', () => {
mockState,
);

const { getByTestId } = renderScreen(
const rendered = renderScreen(
BridgeView,
{ name: Routes.BRIDGE.ROOT },
{ state: testState },
);

// The new Pressable-based Button does not expose onPress on the host View
// when disabled, so we find the Button component instance and call onPress directly.
const buttons = rendered.UNSAFE_getAllByType(Button);
const confirmButton = buttons.find(
(b) => b.props.testID === BridgeViewSelectorsIDs.CONFIRM_BUTTON,
);

await act(async () => {
fireEvent.press(getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON));
confirmButton?.props.onPress?.();
});

await waitFor(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ describe('SwapsConfirmButton', () => {
},
};

const { queryByText } = renderWithProvider(
const { getByTestId } = renderWithProvider(
<SwapsConfirmButton
latestSourceBalance={mockLatestSourceBalance}
location={MetaMetricsSwapsEventSource.MainView}
Expand All @@ -334,9 +334,9 @@ describe('SwapsConfirmButton', () => {
},
);

// When submitting, buttonIsInLoadingState is true so Button renders
// an ActivityIndicator instead of the label text
expect(queryByText(strings('bridge.submitting_transaction'))).toBeNull();
// When submitting, buttonIsInLoadingState is true so Button shows
// a spinner overlay (label text is rendered but visually hidden via opacity-0)
expect(getByTestId('spinner-container')).toBeTruthy();
});
});

Expand All @@ -361,7 +361,7 @@ describe('SwapsConfirmButton', () => {
);

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(true);
expect(button.props.accessibilityState?.disabled).toBe(true);
});

it('disables button when balance is insufficient', () => {
Expand All @@ -378,7 +378,7 @@ describe('SwapsConfirmButton', () => {
);

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(true);
expect(button.props.accessibilityState?.disabled).toBe(true);
});

it('disables button when transaction is submitting', () => {
Expand All @@ -401,7 +401,7 @@ describe('SwapsConfirmButton', () => {
);

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(true);
expect(button.props.accessibilityState?.disabled).toBe(true);
});

it('disables button for hardware wallet with Solana source', () => {
Expand Down Expand Up @@ -433,7 +433,7 @@ describe('SwapsConfirmButton', () => {
);

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(true);
expect(button.props.accessibilityState?.disabled).toBe(true);
});

it('disables button when blockaid error exists', () => {
Expand All @@ -455,7 +455,7 @@ describe('SwapsConfirmButton', () => {
);

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(true);
expect(button.props.accessibilityState?.disabled).toBe(true);
});

it('disables button when gas is insufficient', () => {
Expand All @@ -472,7 +472,7 @@ describe('SwapsConfirmButton', () => {
);

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(true);
expect(button.props.accessibilityState?.disabled).toBe(true);
});

it('disables button when walletAddress is missing', () => {
Expand All @@ -489,7 +489,7 @@ describe('SwapsConfirmButton', () => {
);

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(true);
expect(button.props.accessibilityState?.disabled).toBe(true);
});
});

Expand All @@ -503,7 +503,7 @@ describe('SwapsConfirmButton', () => {
activeQuote: null,
}));

const { queryByText, getByTestId } = renderWithProvider(
const { getByTestId } = renderWithProvider(
<SwapsConfirmButton
latestSourceBalance={mockLatestSourceBalance}
location={MetaMetricsSwapsEventSource.MainView}
Expand All @@ -515,9 +515,9 @@ describe('SwapsConfirmButton', () => {

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
// Button is disabled (isLoading && !activeQuote)
expect(button.props.disabled).toBe(true);
// Label text is hidden behind ActivityIndicator
expect(queryByText(strings('bridge.confirm_swap'))).toBeNull();
expect(button.props.accessibilityState?.disabled).toBe(true);
// Spinner overlay is shown (label text rendered but visually hidden via opacity-0)
expect(getByTestId('spinner-container')).toBeTruthy();
});

it('shows loading indicator when submitting transaction', () => {
Expand All @@ -529,7 +529,7 @@ describe('SwapsConfirmButton', () => {
},
};

const { queryByText, getByTestId } = renderWithProvider(
const { getByTestId } = renderWithProvider(
<SwapsConfirmButton
latestSourceBalance={mockLatestSourceBalance}
location={MetaMetricsSwapsEventSource.MainView}
Expand All @@ -540,9 +540,9 @@ describe('SwapsConfirmButton', () => {
);

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(true);
// Label hidden by loading indicator
expect(queryByText(strings('bridge.submitting_transaction'))).toBeNull();
expect(button.props.accessibilityState?.disabled).toBe(true);
// Spinner overlay is shown (label text rendered but visually hidden via opacity-0)
expect(getByTestId('spinner-container')).toBeTruthy();
});

it('shows loading when awaiting quote (valid amount, no quote, not loading)', () => {
Expand All @@ -554,7 +554,7 @@ describe('SwapsConfirmButton', () => {
activeQuote: null,
}));

const { queryByText, getByTestId } = renderWithProvider(
const { getByTestId } = renderWithProvider(
<SwapsConfirmButton
latestSourceBalance={mockLatestSourceBalance}
location={MetaMetricsSwapsEventSource.MainView}
Expand All @@ -565,9 +565,9 @@ describe('SwapsConfirmButton', () => {
);

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(true);
// Label hidden by loading indicator during debounce gap
expect(queryByText(strings('bridge.confirm_swap'))).toBeNull();
expect(button.props.accessibilityState?.disabled).toBe(true);
// Spinner overlay is shown (label text rendered but visually hidden via opacity-0)
expect(getByTestId('spinner-container')).toBeTruthy();
});

it('does not show loading when source amount is empty', () => {
Expand Down Expand Up @@ -656,7 +656,7 @@ describe('SwapsConfirmButton', () => {

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
// Not disabled because activeQuote exists
expect(button.props.disabled).toBe(false);
expect(button.props.accessibilityState?.disabled).toBe(false);
// Label is visible (no loading indicator)
expect(getByText(strings('bridge.confirm_swap'))).toBeTruthy();
});
Expand All @@ -676,7 +676,7 @@ describe('SwapsConfirmButton', () => {

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
// Button is disabled
expect(button.props.disabled).toBe(true);
expect(button.props.accessibilityState?.disabled).toBe(true);
// But not in loading state (isLoading=false, isSubmittingTx=false)
// so the label is visible
expect(getByText(strings('bridge.insufficient_funds'))).toBeTruthy();
Expand All @@ -696,7 +696,7 @@ describe('SwapsConfirmButton', () => {
);

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(true);
expect(button.props.accessibilityState?.disabled).toBe(true);
// Label visible because not loading/submitting
expect(getByText(strings('bridge.insufficient_gas'))).toBeTruthy();
});
Expand All @@ -705,7 +705,7 @@ describe('SwapsConfirmButton', () => {
describe('Stale Quote (isQuoteStale)', () => {
it('shows loading when amount changes to non-zero and quote is stale', () => {
// First render with sourceAmount='1.0' — settledAmountRef latches to '1.0'
const { queryByText, getByTestId, store } = renderWithProvider(
const { getByTestId, store } = renderWithProvider(
<SwapsConfirmButton
latestSourceBalance={mockLatestSourceBalance}
location={MetaMetricsSwapsEventSource.MainView}
Expand All @@ -722,9 +722,9 @@ describe('SwapsConfirmButton', () => {
});

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(true);
// Label hidden by loading indicator
expect(queryByText(strings('bridge.confirm_swap'))).toBeNull();
expect(button.props.accessibilityState?.disabled).toBe(true);
// Spinner overlay is shown (label text rendered but visually hidden via opacity-0)
expect(getByTestId('spinner-container')).toBeTruthy();
});

it('disables button without loading when amount changes to zero and quote is stale', () => {
Expand All @@ -747,7 +747,7 @@ describe('SwapsConfirmButton', () => {
});

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(true);
expect(button.props.accessibilityState?.disabled).toBe(true);
// Label visible (not loading) — shows default "Confirm swap"
expect(getByText(strings('bridge.confirm_swap'))).toBeTruthy();
});
Expand All @@ -765,7 +765,7 @@ describe('SwapsConfirmButton', () => {
);

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(false);
expect(button.props.accessibilityState?.disabled).toBe(false);
expect(getByText(strings('bridge.confirm_swap'))).toBeTruthy();
});
});
Expand Down Expand Up @@ -815,7 +815,7 @@ describe('SwapsConfirmButton', () => {
);

const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(false);
expect(button.props.accessibilityState?.disabled).toBe(false);
});

it('calls resetState and updateQuoteParams when expired quote button is pressed', async () => {
Expand Down Expand Up @@ -874,7 +874,7 @@ describe('SwapsConfirmButton', () => {
).toBeTruthy();
// Button is not disabled — user can press "Get new quote"
const button = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(button.props.disabled).toBe(false);
expect(button.props.accessibilityState?.disabled).toBe(false);
});

it('does not show "Get new quote" when expired and loading with active quote', () => {
Expand Down Expand Up @@ -1099,7 +1099,7 @@ describe('SwapsConfirmButton', () => {

// Button should be disabled when walletAddress is missing
const continueButton = getByTestId(BridgeViewSelectorsIDs.CONFIRM_BUTTON);
expect(continueButton.props.disabled).toBe(true);
expect(continueButton.props.accessibilityState?.disabled).toBe(true);

// Verify submitBridgeTx is not called since button is disabled
expect(mockSubmitBridgeTx).not.toHaveBeenCalled();
Expand Down
23 changes: 12 additions & 11 deletions app/components/UI/Bridge/components/SwapsConfirmButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { useMemo, useEffect, useRef } from 'react';
import Button, {
ButtonSize,
ButtonVariants,
ButtonWidthTypes,
} from '../../../../../component-library/components/Buttons/Button';
import {
Button,
ButtonVariant,
ButtonBaseSize,
} from '@metamask/design-system-react-native';
import { strings } from '../../../../../../locales/i18n';
import { BridgeViewSelectorsIDs } from '../../Views/BridgeView/BridgeView.testIds';
import { useSelector } from 'react-redux';
Expand Down Expand Up @@ -213,14 +213,15 @@ export const SwapsConfirmButton = ({

return (
<Button
variant={ButtonVariants.Primary}
size={ButtonSize.Lg}
loading={buttonIsInLoadingState}
label={label}
variant={ButtonVariant.Primary}
size={ButtonBaseSize.Lg}
isLoading={buttonIsInLoadingState}
onPress={needsNewQuote ? handleGetNewQuote : handleContinue}
width={ButtonWidthTypes.Full}
isFullWidth
testID={testID ?? BridgeViewSelectorsIDs.CONFIRM_BUTTON}
isDisabled={needsNewQuote ? false : isSubmitDisabled}
/>
>
{label}
</Button>
);
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from 'react';
import { View } from 'react-native';
import { quickPickButtonsStyles as styles } from './styles';
import Button, {
ButtonSize,
ButtonVariants,
} from '../../../../../component-library/components/Buttons/Button';
import {
Button,
ButtonVariant,
ButtonBaseSize,
} from '@metamask/design-system-react-native';
import { QuickPickButtonOption } from './types';

interface Props {
Expand All @@ -22,12 +23,13 @@ export const QuickPickButtons = ({ options, show }: Props) => {
{options.map((option) => (
<Button
key={option.label}
variant={ButtonVariants.Secondary}
size={ButtonSize.Lg}
label={option.label}
variant={ButtonVariant.Secondary}
size={ButtonBaseSize.Lg}
onPress={option.onPress}
style={styles.button}
/>
>
{option.label}
</Button>
))}
</View>
);
Expand Down
16 changes: 9 additions & 7 deletions app/components/UI/Bridge/components/TokenInputArea/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import { selectCurrentCurrency } from '../../../../../selectors/currencyRateCont
import { BigNumber } from 'ethers';
import { BridgeToken } from '../../types';
import { Skeleton } from '../../../../../component-library/components-temp/Skeleton';
import Button, {
ButtonVariants,
import { Button, ButtonVariant } from '@metamask/design-system-react-native';
import OldButton, {
ButtonVariants as OldButtonVariants,
} from '../../../../../component-library/components/Buttons/Button';
import { strings } from '../../../../../../locales/i18n';
import Routes from '../../../../../constants/navigation/Routes';
Expand Down Expand Up @@ -309,15 +310,16 @@ export const TokenInputArea = forwardRef<
/>
) : (
<Button
variant={ButtonVariants.Primary}
label={strings(tokenButtonText)}
variant={ButtonVariant.Primary}
onPress={
isSourceToken
? navigateToSourceTokenSelector
: navigateToDestTokenSelector
}
testID={testID}
/>
>
{strings(tokenButtonText)}
</Button>
)}
</Box>
<Box style={styles.row}>
Expand Down Expand Up @@ -355,8 +357,8 @@ export const TokenInputArea = forwardRef<
tokenBalance &&
onMaxPress &&
shouldShowMaxButton && (
<Button
variant={ButtonVariants.Link}
<OldButton
variant={OldButtonVariants.Link}
label={strings('bridge.max')}
onPress={onMaxPress}
disabled={!subtitle}
Expand Down
Loading
Loading