diff --git a/app/component-library/components-temp/Accounts/AccountBase/AccountBase.styles.ts b/app/component-library/components-temp/Accounts/AccountBase/AccountBase.styles.ts index cda7d7fe248..f755c07b3eb 100644 --- a/app/component-library/components-temp/Accounts/AccountBase/AccountBase.styles.ts +++ b/app/component-library/components-temp/Accounts/AccountBase/AccountBase.styles.ts @@ -29,7 +29,6 @@ const styleSheet = StyleSheet.create({ justifyContent: 'flex-start', }, accountNameLabelText: { - marginLeft: 4, paddingHorizontal: 8, borderWidth: 1, borderRadius: 8, diff --git a/app/component-library/components-temp/Accounts/AccountBase/AccountBase.tsx b/app/component-library/components-temp/Accounts/AccountBase/AccountBase.tsx index 14f619c98e0..88e0f809d05 100644 --- a/app/component-library/components-temp/Accounts/AccountBase/AccountBase.tsx +++ b/app/component-library/components-temp/Accounts/AccountBase/AccountBase.tsx @@ -49,15 +49,17 @@ const AccountBase = ({ {accountName} - {accountTypeLabel && ( + + {accountTypeLabel && ( + {strings(accountTypeLabel)} - )} + )} diff --git a/app/components/UI/Navbar/index.js b/app/components/UI/Navbar/index.js index 7cc0d590781..c267c2186d1 100644 --- a/app/components/UI/Navbar/index.js +++ b/app/components/UI/Navbar/index.js @@ -1857,34 +1857,61 @@ export const getSettingsNavigationOptions = (title, themeColors) => { }; }; -export function getStakingNavbar(title, navigation, themeColors) { +/** + * + * @param {String} title - Navbar Title. + * @param {NavigationProp} navigation Navigation object returned from useNavigation hook. + * @param {ThemeColors} themeColors theme.colors returned from useStyles hook. + * @param {{ backgroundColor?: string, hasCancelButton?: boolean, hasBackButton?: boolean }} [options] - Optional options for navbar. + * @returns Staking Navbar Component. + */ +export function getStakingNavbar(title, navigation, themeColors, options) { + const { hasBackButton = true, hasCancelButton = true } = options ?? {}; + const innerStyles = StyleSheet.create({ + headerStyle: { + backgroundColor: + options?.backgroundColor ?? themeColors.background.default, + shadowOffset: null, + }, + headerLeft: { + marginHorizontal: 16, + }, headerButtonText: { color: themeColors.primary.default, fontSize: 14, ...fontStyles.normal, }, - headerStyle: { - backgroundColor: themeColors.background.default, - shadowColor: importedColors.transparent, - elevation: 0, - }, }); + + function navigationPop() { + navigation.goBack(); + } + return { headerTitle: () => ( - - ), - headerLeft: () => , - headerRight: () => ( - navigation.dangerouslyGetParent()?.pop()} - style={styles.closeButton} - > - - {strings('navigation.cancel')} - - + {title} ), headerStyle: innerStyles.headerStyle, + headerLeft: () => + hasBackButton ? ( + + ) : null, + headerRight: () => + hasCancelButton ? ( + navigation.dangerouslyGetParent()?.pop()} + style={styles.closeButton} + > + + {strings('navigation.cancel')} + + + ) : null, }; } diff --git a/app/components/UI/Stake/Views/StakeConfirmationView/StakeConfirmationView.styles.ts b/app/components/UI/Stake/Views/StakeConfirmationView/StakeConfirmationView.styles.ts new file mode 100644 index 00000000000..d351fc7302a --- /dev/null +++ b/app/components/UI/Stake/Views/StakeConfirmationView/StakeConfirmationView.styles.ts @@ -0,0 +1,23 @@ +import type { Theme } from '../../../../../util/theme/models'; +import { StyleSheet } from 'react-native'; + +const stylesSheet = (params: { theme: Theme }) => { + const { theme } = params; + const { colors } = theme; + + return StyleSheet.create({ + mainContainer: { + flex: 1, + paddingTop: 8, + paddingHorizontal: 16, + backgroundColor: colors.background.alternative, + justifyContent: 'space-between', + }, + cardsContainer: { + paddingTop: 16, + gap: 8, + }, + }); +}; + +export default stylesSheet; diff --git a/app/components/UI/Stake/Views/StakeConfirmationView/StakeConfirmationView.test.tsx b/app/components/UI/Stake/Views/StakeConfirmationView/StakeConfirmationView.test.tsx new file mode 100644 index 00000000000..109fe3e7fac --- /dev/null +++ b/app/components/UI/Stake/Views/StakeConfirmationView/StakeConfirmationView.test.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import renderWithProvider from '../../../../../util/test/renderWithProvider'; +import StakeConfirmationView from './StakeConfirmationView'; +import { Image } from 'react-native'; +import { createMockAccountsControllerState } from '../../../../../util/test/accountsControllerTestUtils'; +import { backgroundState } from '../../../../../util/test/initial-root-state'; +import configureMockStore from 'redux-mock-store'; +import { Provider } from 'react-redux'; +import { StakeConfirmationViewProps } from './StakeConfirmationView.types'; + +jest.mock('../../../../hooks/useIpfsGateway', () => jest.fn()); + +Image.getSize = jest.fn((_uri, success) => { + success(100, 100); // Mock successful response for ETH native Icon Image +}); + +const MOCK_ADDRESS_1 = '0x0'; +const MOCK_ADDRESS_2 = '0x1'; + +const MOCK_ACCOUNTS_CONTROLLER_STATE = createMockAccountsControllerState([ + MOCK_ADDRESS_1, + MOCK_ADDRESS_2, +]); + +const mockStore = configureMockStore(); + +const mockInitialState = { + settings: {}, + engine: { + backgroundState: { + ...backgroundState, + AccountsController: MOCK_ACCOUNTS_CONTROLLER_STATE, + }, + }, +}; +const store = mockStore(mockInitialState); + +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useSelector: jest + .fn() + .mockImplementation((callback) => callback(mockInitialState)), +})); + +jest.mock('@react-navigation/native', () => { + const actualNav = jest.requireActual('@react-navigation/native'); + return { + ...actualNav, + useNavigation: () => ({ + navigate: jest.fn(), + setOptions: jest.fn(), + }), + }; +}); + +describe('StakeConfirmationView', () => { + it('render matches snapshot', () => { + const props: StakeConfirmationViewProps = { + route: { + key: '1', + params: { amountWei: '3210000000000000', amountFiat: '7.46' }, + name: 'params', + }, + }; + + const { toJSON } = renderWithProvider( + + + , + ); + + expect(toJSON()).toMatchSnapshot(); + }); +}); diff --git a/app/components/UI/Stake/Views/StakeConfirmationView/StakeConfirmationView.tsx b/app/components/UI/Stake/Views/StakeConfirmationView/StakeConfirmationView.tsx new file mode 100644 index 00000000000..2f1a4890286 --- /dev/null +++ b/app/components/UI/Stake/Views/StakeConfirmationView/StakeConfirmationView.tsx @@ -0,0 +1,60 @@ +import React, { useEffect } from 'react'; +import { View } from 'react-native'; +import { useNavigation } from '@react-navigation/native'; +import { useStyles } from '../../../../hooks/useStyles'; +import { getStakingNavbar } from '../../../Navbar'; +import styleSheet from './StakeConfirmationView.styles'; +import TokenValueStack from '../../components/StakingConfirmation/TokenValueStack/TokenValueStack'; +import AccountHeaderCard from '../../components/StakingConfirmation/AccountHeaderCard/AccountHeaderCard'; +import RewardsCard from '../../components/StakingConfirmation/RewardsCard/RewardsCard'; +import ConfirmationFooter from '../../components/StakingConfirmation/ConfirmationFooter/ConfirmationFooter'; +import { StakeConfirmationViewProps } from './StakeConfirmationView.types'; +import { MOCK_GET_VAULT_RESPONSE } from '../../components/StakingBalance/mockData'; +import { strings } from '../../../../../../locales/i18n'; + +const MOCK_REWARD_DATA = { + REWARDS: { + ETH: '0.13 ETH', + FIAT: '$334.93', + }, +}; + +const MOCK_STAKING_CONTRACT_NAME = 'MM Pooled Staking'; + +const StakeConfirmationView = ({ route }: StakeConfirmationViewProps) => { + const navigation = useNavigation(); + + const { styles, theme } = useStyles(styleSheet, {}); + + useEffect(() => { + navigation.setOptions( + getStakingNavbar(strings('stake.stake'), navigation, theme.colors, { + backgroundColor: theme.colors.background.alternative, + hasCancelButton: false, + }), + ); + }, [navigation, theme.colors]); + + return ( + + + + + + + + + + + ); +}; + +export default StakeConfirmationView; diff --git a/app/components/UI/Stake/Views/StakeConfirmationView/StakeConfirmationView.types.ts b/app/components/UI/Stake/Views/StakeConfirmationView/StakeConfirmationView.types.ts new file mode 100644 index 00000000000..8c723135f4f --- /dev/null +++ b/app/components/UI/Stake/Views/StakeConfirmationView/StakeConfirmationView.types.ts @@ -0,0 +1,10 @@ +import { RouteProp } from '@react-navigation/native'; + +interface StakeConfirmationViewRouteParams { + amountWei: string; + amountFiat: string; +} + +export interface StakeConfirmationViewProps { + route: RouteProp<{ params: StakeConfirmationViewRouteParams }, 'params'>; +} diff --git a/app/components/UI/Stake/Views/StakeConfirmationView/__snapshots__/StakeConfirmationView.test.tsx.snap b/app/components/UI/Stake/Views/StakeConfirmationView/__snapshots__/StakeConfirmationView.test.tsx.snap new file mode 100644 index 00000000000..9d14c100f63 --- /dev/null +++ b/app/components/UI/Stake/Views/StakeConfirmationView/__snapshots__/StakeConfirmationView.test.tsx.snap @@ -0,0 +1,1424 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`StakeConfirmationView render matches snapshot 1`] = ` + + + + + + + + + + + + + + + + + + + + + + + 0.00321 + + ETH + + + $7.46 + + + + + + + + + + + + Staking from + + + + + + + + + + + + + + + + + + + + + + + Account 1 + + + + + + + + + + + + + Interacting with + + + + + + + + + + + + + MM Pooled Staking + + + + + + + + + + + + + + + Network + + + + + + + + + + + + + Ethereum Main Network + + + + + + + + + + + + + + + Reward rate + + + + + + + + + + + + 2.8% + + + + + + + + + + + Estimated annual rewards + + + + + + + + + + $334.93 + + + 0.13 ETH + + + + + + + + + + + + Reward frequency + + + + + + + + + + + + 12 hours + + + + + + + + + + + + + Terms of service + + + + + Risk disclosure + + + + + + + Cancel + + + + + Confirm + + + + + +`; diff --git a/app/components/UI/Stake/Views/StakeInputView/StakeInputView.tsx b/app/components/UI/Stake/Views/StakeInputView/StakeInputView.tsx index 1ca6560d7f4..0431e67a77f 100644 --- a/app/components/UI/Stake/Views/StakeInputView/StakeInputView.tsx +++ b/app/components/UI/Stake/Views/StakeInputView/StakeInputView.tsx @@ -50,8 +50,14 @@ const StakeInputView = () => { }; const handleStakePress = useCallback(() => { - // TODO: Display the Review bottom sheet: STAKE-824 - }, []); + navigation.navigate('StakeScreens', { + screen: Routes.STAKING.STAKE_CONFIRMATION, + params: { + amountWei: amountWei.toString(), + amountFiat: fiatAmount, + }, + }); + }, [amountWei, fiatAmount, navigation]); const balanceText = strings('stake.balance'); @@ -66,7 +72,11 @@ const StakeInputView = () => { : `${balanceFiatNumber?.toString()} ${currentCurrency.toUpperCase()}`; useEffect(() => { - navigation.setOptions(getStakingNavbar(title, navigation, theme.colors)); + navigation.setOptions( + getStakingNavbar(title, navigation, theme.colors, { + hasBackButton: false, + }), + ); }, [navigation, theme.colors, title]); useEffect(() => { diff --git a/app/components/UI/Stake/Views/StakeInputView/__snapshots__/StakeInputView.test.tsx.snap b/app/components/UI/Stake/Views/StakeInputView/__snapshots__/StakeInputView.test.tsx.snap index 5085ab0c15e..4a35aa2b83e 100644 --- a/app/components/UI/Stake/Views/StakeInputView/__snapshots__/StakeInputView.test.tsx.snap +++ b/app/components/UI/Stake/Views/StakeInputView/__snapshots__/StakeInputView.test.tsx.snap @@ -56,13 +56,9 @@ exports[`StakeInputView render matches snapshot 1`] = ` { "backgroundColor": "#ffffff", "borderBottomColor": "rgb(216, 216, 216)", - "elevation": 0, "flex": 1, - "shadowColor": "transparent", - "shadowOffset": { - "height": 0.5, - "width": 0, - }, + "shadowColor": "rgb(216, 216, 216)", + "shadowOffset": null, "shadowOpacity": 0.85, "shadowRadius": 0, } @@ -106,96 +102,26 @@ exports[`StakeInputView render matches snapshot 1`] = ` pointerEvents="box-none" style={ { - "alignItems": "flex-start", - "bottom": 0, - "justifyContent": "center", - "left": 0, - "opacity": 1, - "position": "absolute", - "top": 0, - } - } - > - - - - - - Stake ETH - - - - - Ethereum Main Network - - - + Stake ETH + { : strings('stake.review'); useEffect(() => { - navigation.setOptions(getStakingNavbar(title, navigation, theme.colors)); + navigation.setOptions( + getStakingNavbar(title, navigation, theme.colors, { + hasBackButton: false, + }), + ); }, [navigation, theme.colors, title]); const handleUnstakePress = useCallback(() => { diff --git a/app/components/UI/Stake/Views/UnstakeInputView/__snapshots__/UnstakeInputView.test.tsx.snap b/app/components/UI/Stake/Views/UnstakeInputView/__snapshots__/UnstakeInputView.test.tsx.snap index 15e289f23e7..5e7927b0b5c 100644 --- a/app/components/UI/Stake/Views/UnstakeInputView/__snapshots__/UnstakeInputView.test.tsx.snap +++ b/app/components/UI/Stake/Views/UnstakeInputView/__snapshots__/UnstakeInputView.test.tsx.snap @@ -56,13 +56,9 @@ exports[`UnstakeInputView render matches snapshot 1`] = ` { "backgroundColor": "#ffffff", "borderBottomColor": "rgb(216, 216, 216)", - "elevation": 0, "flex": 1, - "shadowColor": "transparent", - "shadowOffset": { - "height": 0.5, - "width": 0, - }, + "shadowColor": "rgb(216, 216, 216)", + "shadowOffset": null, "shadowOpacity": 0.85, "shadowRadius": 0, } @@ -106,96 +102,26 @@ exports[`UnstakeInputView render matches snapshot 1`] = ` pointerEvents="box-none" style={ { - "alignItems": "flex-start", - "bottom": 0, - "justifyContent": "center", - "left": 0, - "opacity": 1, - "position": "absolute", - "top": 0, - } - } - > - - - - - - Unstake ETH - - - - - Ethereum Main Network - - - + Unstake ETH + jest.fn()); + +Image.getSize = jest.fn((_uri, success) => { + success(100, 100); // Mock successful response for ETH native Icon Image +}); const mockNavigate = jest.fn(); @@ -39,15 +29,17 @@ afterEach(() => { }); describe('StakingBalance', () => { + beforeEach(() => jest.resetAllMocks()); + it('render matches snapshot', () => { - render(StakingBalance); - expect(screen.toJSON()).toMatchSnapshot(); + const { toJSON } = renderWithProvider(); + expect(toJSON()).toMatchSnapshot(); }); it('redirects to StakeInputView on stake button click', () => { - render(StakingBalance); + const { getByText } = renderWithProvider(); - fireEvent.press(screen.getByText(strings('stake.stake_more'))); + fireEvent.press(getByText(strings('stake.stake_more'))); expect(mockNavigate).toHaveBeenCalledTimes(1); expect(mockNavigate).toHaveBeenCalledWith('StakeScreens', { @@ -56,9 +48,9 @@ describe('StakingBalance', () => { }); it('redirects to UnstakeInputView on unstake button click', () => { - render(StakingBalance); + const { getByText } = renderWithProvider(); - fireEvent.press(screen.getByText(strings('stake.unstake'))); + fireEvent.press(getByText(strings('stake.unstake'))); expect(mockNavigate).toHaveBeenCalledTimes(1); expect(mockNavigate).toHaveBeenCalledWith('StakeScreens', { diff --git a/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.tsx b/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.tsx index 1ab816b649d..13b3d2c8629 100644 --- a/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.tsx +++ b/app/components/UI/Stake/components/StakingBalance/StakingCta/StakingCta.tsx @@ -36,10 +36,10 @@ const StakingCta = ({ estimatedRewardRate, style }: StakingCtaProps) => { {strings('stake.stake_your_eth_cta.base')} - {estimatedRewardRate} - - {strings('stake.stake_your_eth_cta.annually')} + + {estimatedRewardRate} + {strings('stake.stake_your_eth_cta.annually')}