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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ scripts/reports/
PR_DESC.md

# Performance test results
e2e/specs/performance/reports/*-performance-results.json
tests/smoke/performance/reports/*-performance-results.json

# appwright
appwright/report
Expand Down
21 changes: 20 additions & 1 deletion app/components/Nav/Main/MainNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -1112,7 +1112,26 @@ const MainNavigator = () => {
}),
}}
/>
<Stack.Screen name={Routes.EARN.ROOT} component={EarnScreenStack} />
<Stack.Screen
name={Routes.EARN.ROOT}
component={EarnScreenStack}
options={{
headerShown: false,
animationEnabled: true,
cardStyleInterpolator: ({ current, layouts }) => ({
cardStyle: {
transform: [
{
translateX: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [layouts.screen.width, 0],
}),
},
],
},
}),
}}
/>
<Stack.Screen
name={Routes.EARN.MODALS.ROOT}
component={EarnModalStack}
Expand Down
21 changes: 21 additions & 0 deletions app/components/Nav/Main/__snapshots__/MainNavigator.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,13 @@ exports[`MainNavigator Tab Bar Visibility hides tab bar when browser is active 1
<Screen
component={[Function]}
name="EarnScreens"
options={
{
"animationEnabled": true,
"cardStyleInterpolator": [Function],
"headerShown": false,
}
}
/>
<Screen
component={[Function]}
Expand Down Expand Up @@ -518,6 +525,13 @@ exports[`MainNavigator Tab Bar Visibility shows tab bar when not in browser 1`]
<Screen
component={[Function]}
name="EarnScreens"
options={
{
"animationEnabled": true,
"cardStyleInterpolator": [Function],
"headerShown": false,
}
}
/>
<Screen
component={[Function]}
Expand Down Expand Up @@ -836,6 +850,13 @@ exports[`MainNavigator matches rendered snapshot 1`] = `
<Screen
component={[Function]}
name="EarnScreens"
options={
{
"animationEnabled": true,
"cardStyleInterpolator": [Function],
"headerShown": false,
}
}
/>
<Screen
component={[Function]}
Expand Down
46 changes: 46 additions & 0 deletions app/components/UI/Bridge/Views/BridgeView/BridgeView.view.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,52 @@ describeForPlatforms('BridgeView', () => {
expect(await findByText(expected)).toBeOnTheScreen();
});

it('hides keypad when refreshing quote with input unfocused', () => {
const now = Date.now();
const previousQuote = { ...mockQuoteWithMetadata };

const { queryByTestId } = renderBridgeView({
deterministicFiat: true,
overrides: {
bridge: {
sourceAmount: '1',
sourceToken: {
address: '0x0000000000000000000000000000000000000000',
chainId: '0x1',
decimals: 18,
symbol: 'ETH',
name: 'Ether',
},
destToken: {
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
chainId: '0x1',
decimals: 6,
symbol: 'USDC',
name: 'USD Coin',
},
},
engine: {
backgroundState: {
BridgeController: {
quotes: [previousQuote as unknown as Record<string, unknown>],
recommendedQuote: previousQuote as unknown as Record<
string,
unknown
>,
quotesLastFetched: now - 1000,
quotesLoadingStatus: 'LOADING',
quoteFetchError: null,
},
},
},
} as unknown as Record<string, unknown>,
});

// Keypad should NOT be visible when refreshing quote with valid inputs and unfocused input
// This simulates the scenario after user changes slippage - quote is loading but input is not focused
expect(queryByTestId(BuildQuoteSelectors.KEYPAD_DELETE_BUTTON)).toBeNull();
});

it('navigates to dest token selector on press', async () => {
const TokenSelectorProbe: React.FC<{
route?: { params?: { type?: string } };
Expand Down
6 changes: 5 additions & 1 deletion app/components/UI/Bridge/Views/BridgeView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,12 @@ const BridgeView = () => {
const isError = isNoQuotesAvailable || quoteFetchError;

// Primary condition for keypad visibility - when input is focused or we don't have valid inputs
// Also hide the keypad when a new quote is loading and input field is not focused like after
// user changing the slippage value.
const shouldDisplayKeypad =
isInputFocused || !hasValidBridgeInputs || (!activeQuote && !isError);
isInputFocused ||
!hasValidBridgeInputs ||
(!activeQuote && !isError && !isLoading);
// Hide quote whenever the keypad is displayed
const shouldDisplayQuoteDetails = activeQuote && !shouldDisplayKeypad;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { StyleSheet } from 'react-native';
import type { Theme } from '../../../../../util/theme/models';

const styleSheet = (_params: { theme: Theme }) =>
StyleSheet.create({
balanceText: {
fontWeight: '500',
},
});

export default styleSheet;
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import PerpsEmptyBalance from './PerpsEmptyBalance';
import { PerpsMarketBalanceActionsSelectorsIDs } from '../../Perps.testIds';

jest.mock('../../../../../component-library/hooks', () => ({
useStyles: jest.fn(() => ({
styles: {
balanceText: { fontWeight: '500' },
},
})),
}));

jest.mock('../../../../../../locales/i18n', () => ({
strings: jest.fn((key: string) => {
const mockStrings: Record<string, string> = {
'perps.add_funds': 'Add funds',
};
return mockStrings[key] ?? key;
}),
}));

describe('PerpsEmptyBalance', () => {
beforeEach(() => {
jest.clearAllMocks();
});

describe('rendering', () => {
it('renders $0.00 balance text', () => {
const onAddFunds = jest.fn();

const { getByText } = render(
<PerpsEmptyBalance onAddFunds={onAddFunds} />,
);

expect(getByText('$0.00')).toBeTruthy();
});

it('renders balance value with correct testID', () => {
const onAddFunds = jest.fn();

const { getByTestId } = render(
<PerpsEmptyBalance onAddFunds={onAddFunds} />,
);

expect(
getByTestId(PerpsMarketBalanceActionsSelectorsIDs.BALANCE_VALUE),
).toBeTruthy();
});

it('renders Add funds button', () => {
const onAddFunds = jest.fn();

const { getByText } = render(
<PerpsEmptyBalance onAddFunds={onAddFunds} />,
);

expect(getByText('Add funds')).toBeTruthy();
});

it('renders Add funds button with correct testID', () => {
const onAddFunds = jest.fn();

const { getByTestId } = render(
<PerpsEmptyBalance onAddFunds={onAddFunds} />,
);

expect(
getByTestId(PerpsMarketBalanceActionsSelectorsIDs.ADD_FUNDS_BUTTON),
).toBeTruthy();
});
});

describe('onAddFunds callback', () => {
it('calls onAddFunds when Add funds button is pressed', () => {
const onAddFunds = jest.fn();

const { getByTestId } = render(
<PerpsEmptyBalance onAddFunds={onAddFunds} />,
);

fireEvent.press(
getByTestId(PerpsMarketBalanceActionsSelectorsIDs.ADD_FUNDS_BUTTON),
);

expect(onAddFunds).toHaveBeenCalledTimes(1);
});

it('calls onAddFunds each time Add funds button is pressed', () => {
const onAddFunds = jest.fn();

const { getByTestId } = render(
<PerpsEmptyBalance onAddFunds={onAddFunds} />,
);

const addFundsButton = getByTestId(
PerpsMarketBalanceActionsSelectorsIDs.ADD_FUNDS_BUTTON,
);

fireEvent.press(addFundsButton);
fireEvent.press(addFundsButton);

expect(onAddFunds).toHaveBeenCalledTimes(2);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import {
Box,
BoxAlignItems,
BoxFlexDirection,
BoxJustifyContent,
Button,
ButtonSize,
ButtonVariant,
} from '@metamask/design-system-react-native';
import Text, {
TextVariant,
TextColor,
} from '../../../../../component-library/components/Texts/Text';
import { useStyles } from '../../../../../component-library/hooks';
import { strings } from '../../../../../../locales/i18n';
import { PerpsMarketBalanceActionsSelectorsIDs } from '../../Perps.testIds';
import styleSheet from './PerpsEmptyBalance.styles';

export interface PerpsEmptyBalanceProps {
onAddFunds: () => void;
}

const PerpsEmptyBalance: React.FC<PerpsEmptyBalanceProps> = ({
onAddFunds,
}) => {
const { styles } = useStyles(styleSheet, {});

return (
<Box twClassName="px-4 py-4">
<Box
flexDirection={BoxFlexDirection.Row}
alignItems={BoxAlignItems.Center}
justifyContent={BoxJustifyContent.Between}
twClassName="gap-3"
>
<Text
variant={TextVariant.DisplayLG}
color={TextColor.Default}
style={styles.balanceText}
testID={PerpsMarketBalanceActionsSelectorsIDs.BALANCE_VALUE}
>
$0.00
</Text>
<Button
variant={ButtonVariant.Secondary}
size={ButtonSize.Lg}
onPress={onAddFunds}
testID={PerpsMarketBalanceActionsSelectorsIDs.ADD_FUNDS_BUTTON}
>
{strings('perps.add_funds')}
</Button>
</Box>
</Box>
);
};

export default PerpsEmptyBalance;
2 changes: 2 additions & 0 deletions app/components/UI/Perps/components/PerpsEmptyBalance/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from './PerpsEmptyBalance';
export type { PerpsEmptyBalanceProps } from './PerpsEmptyBalance';
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ jest.mock('@metamask/design-system-react-native', () => {
BoxAlignItems: {
Center: 'center',
},
BoxJustifyContent: {
Between: 'space-between',
},
ButtonSize: {
Sm: 'sm',
Md: 'md',
Expand Down Expand Up @@ -607,20 +610,21 @@ describe('PerpsMarketBalanceActions', () => {
});

// Act
const { getByText, getByTestId, queryByTestId } = renderWithProvider(
const { getByText, getByTestId } = renderWithProvider(
<PerpsMarketBalanceActions />,
{ state: createMockState() },
false, // Disable NavigationContainer
);

// Assert - Should show empty state UI instead of balance display
// Assert - Should show empty state UI: $0.00 balance and Add Funds button
expect(
getByTestId(PerpsMarketBalanceActionsSelectorsIDs.EMPTY_STATE_TITLE),
getByTestId(PerpsMarketBalanceActionsSelectorsIDs.BALANCE_VALUE),
).toBeOnTheScreen();
expect(getByText('perps.add_funds')).toBeOnTheScreen();
expect(getByText('$0.00')).toBeOnTheScreen();
expect(
queryByTestId(PerpsMarketBalanceActionsSelectorsIDs.BALANCE_VALUE),
).toBeNull();
getByTestId(PerpsMarketBalanceActionsSelectorsIDs.ADD_FUNDS_BUTTON),
).toBeOnTheScreen();
expect(getByText('perps.add_funds')).toBeOnTheScreen();
});
});

Expand Down
Loading
Loading