From 0683565125d497c64269dd0a199723a7dc982a94 Mon Sep 17 00:00:00 2001 From: CW Date: Tue, 7 May 2024 05:35:22 -0700 Subject: [PATCH 1/2] test: add edit custom mainnet scenario (#9502) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Add editing custom default mainnet scenario after adding custom default mainnet. ## **Related issues** Fixes: #https://github.com/MetaMask/mobile-planning/issues/1575 ## **Manual testing steps** 1. Run `pr_smoke_e2e_pipeline` 2. Ensure all tests pass ## **Screenshots/Recordings** ### **Before** No scenario for editing custom default mainnet. ### **After** https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/821bcaa7-f0ef-48e8-a9c7-cc4930b65e4f ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- e2e/pages/Settings/NetworksView.js | 14 ++++ e2e/resources/blacklistURLs.json | 1 + e2e/resources/networks.e2e.js | 2 + .../Settings/NetworksView.selectors.js | 1 + ...js => add-edit-custom-eth-mainnet.spec.js} | 69 +++++++++++++++++-- 5 files changed, 83 insertions(+), 4 deletions(-) rename e2e/specs/onboarding/{add-custom-eth-mainnet.spec.js => add-edit-custom-eth-mainnet.spec.js} (53%) diff --git a/e2e/pages/Settings/NetworksView.js b/e2e/pages/Settings/NetworksView.js index 21fd71eae8d0..39c0f90d7d69 100644 --- a/e2e/pages/Settings/NetworksView.js +++ b/e2e/pages/Settings/NetworksView.js @@ -106,6 +106,10 @@ class NetworkView { return Matchers.getElementByText(NetworkViewSelectorsText.REMOVE_NETWORK); } + get saveButton() { + return Matchers.getElementByText(NetworkViewSelectorsText.SAVE_BUTTON); + } + async getnetworkName(networkName) { return Matchers.getElementByText(networkName); } @@ -175,6 +179,16 @@ class NetworkView { // Because in bitrise the keyboard is blocking the "Add" CTA await Gestures.waitAndTap(this.chainIdLabel); } + + async tapSave() { + device.getPlatform() === 'ios' + ? await (async () => { + //swipe to dismiss iOS keypad + await Gestures.swipe(this.chainIDInput, 'up', 'fast', 0.3); + await Gestures.doubleTap(this.saveButton); + })() + : await Gestures.waitAndTap(this.saveButton); + } } export default new NetworkView(); diff --git a/e2e/resources/blacklistURLs.json b/e2e/resources/blacklistURLs.json index 0501e0db6852..1d1dec718498 100644 --- a/e2e/resources/blacklistURLs.json +++ b/e2e/resources/blacklistURLs.json @@ -12,6 +12,7 @@ ".*stale.*", ".*phishing-detection.metafi.codefi.network/.*", ".*phishing-detection.cx.metamask.io/.*", + ".*eth.llamarpc.com/.*", ".*token-api.metaswap.codefi.network/.*" ] } diff --git a/e2e/resources/networks.e2e.js b/e2e/resources/networks.e2e.js index 5f3f1029d32e..e10e9fdbaf96 100644 --- a/e2e/resources/networks.e2e.js +++ b/e2e/resources/networks.e2e.js @@ -76,7 +76,9 @@ const CustomNetworks = { providerConfig: { type: 'rpc', chainId: '0x1', + rpcUrlInvalid: 'https//rpc.mevblocker.io', rpcUrl: 'https://eth.llamarpc.com', + rpcUrlAlt: 'https://rpc.mevblocker.io', nickname: 'Ethereum Main Custom', ticker: 'ETH', }, diff --git a/e2e/selectors/Settings/NetworksView.selectors.js b/e2e/selectors/Settings/NetworksView.selectors.js index a24dd3fb44d3..cd6d2e06ab79 100644 --- a/e2e/selectors/Settings/NetworksView.selectors.js +++ b/e2e/selectors/Settings/NetworksView.selectors.js @@ -28,4 +28,5 @@ export const NetworkViewSelectorsText = { POPULAR_NETWORK_TAB: enContent.app_settings.popular, NO_MATCHING_SEARCH_RESULTS: enContent.networks.no_match, EMPTY_POPULAR_NETWORKS: enContent.networks.empty_popular_networks, + SAVE_BUTTON: enContent.app_settings.network_save, }; diff --git a/e2e/specs/onboarding/add-custom-eth-mainnet.spec.js b/e2e/specs/onboarding/add-edit-custom-eth-mainnet.spec.js similarity index 53% rename from e2e/specs/onboarding/add-custom-eth-mainnet.spec.js rename to e2e/specs/onboarding/add-edit-custom-eth-mainnet.spec.js index c0d41a6bdaf4..eab2746fd40d 100644 --- a/e2e/specs/onboarding/add-custom-eth-mainnet.spec.js +++ b/e2e/specs/onboarding/add-edit-custom-eth-mainnet.spec.js @@ -13,8 +13,14 @@ import WalletView from '../../pages/WalletView'; import ProtectYourWalletView from '../../pages/Onboarding/ProtectYourWalletView'; import NetworksView from '../../pages/Settings/NetworksView'; import Accounts from '../../../wdio/helpers/Accounts'; -import { DEFAULT_MAINNET_CUSTOM_NAME } from '../../../app/constants/network'; import { CustomNetworks } from '../../resources/networks.e2e'; +import TabBarComponent from '../../pages/TabBarComponent'; +import SettingsView from '../../pages/Settings/SettingsView'; +import NetworkListModal from '../../pages/modals/NetworkListModal'; +import OnboardingWizardModal from '../../pages/modals/OnboardingWizardModal'; +import ProtectYourWalletModal from '../../pages/modals/ProtectYourWalletModal'; +import WhatsNewModal from '../../pages/modals/WhatsNewModal'; +import TestHelpers from '../../helpers'; const validAccount = Accounts.getValidAccount(); @@ -31,7 +37,9 @@ describe(Regression('Add custom default ETH Mainnet'), () => { it('should not edit default network with invalid RPC', async () => { await MetaMetricsOptIn.tapEditDefaultNetworkHere(); - await DefaultNetworkView.typeRpcURL('https//rpc.mevblocker.io'); + await DefaultNetworkView.typeRpcURL( + CustomNetworks.EthereumMainCustom.providerConfig.rpcUrlInvalid, + ); await Assertions.checkIfVisible(NetworksView.rpcWarningBanner); }); @@ -43,7 +51,7 @@ describe(Regression('Add custom default ETH Mainnet'), () => { await Assertions.checkIfVisible(MetaMetricsOptIn.container); }); - it('should show custom default ETH Mainnet as active', async () => { + it('should complete creating wallet', async () => { await MetaMetricsOptIn.tapAgreeButton(); await TermsOfUseModal.tapScrollEndButton(); await TermsOfUseModal.tapAgreeCheckBox(); @@ -57,6 +65,59 @@ describe(Regression('Add custom default ETH Mainnet'), () => { await SkipAccountSecurityModal.tapSkipButton(); await OnboardingSuccessView.tapDone(); await EnableAutomaticSecurityChecksView.tapNoThanks(); - await WalletView.isNetworkNameVisible(DEFAULT_MAINNET_CUSTOM_NAME); + await OnboardingWizardModal.tapNoThanksButton(); + }); + + it('should show custom default ETH Mainnet as active', async () => { + await WalletView.isNetworkNameVisible( + CustomNetworks.EthereumMainCustom.providerConfig.nickname, + ); + }); + + it('should tap to close the whats new modal if displayed', async () => { + // dealing with flakiness on bitrise. + await TestHelpers.delay(2500); + try { + await WhatsNewModal.isVisible(); + await WhatsNewModal.tapCloseButton(); + } catch { + // + } + }); + + it('should navigate to Settings > Networks', async () => { + await Assertions.checkIfVisible(ProtectYourWalletModal.collapseWalletModal); + await ProtectYourWalletModal.tapRemindMeLaterButton(); + await SkipAccountSecurityModal.tapIUnderstandCheckBox(); + await SkipAccountSecurityModal.tapSkipButton(); + await TabBarComponent.tapSettings(); + await SettingsView.scrollToContactSupportButton(); + await SettingsView.tapNetworks(); + await Assertions.checkIfVisible(NetworksView.networkContainer); + }); + + it('should edit custom default mainnet and land on Wallet view', async () => { + await NetworksView.tapNetworkByName( + CustomNetworks.EthereumMainCustom.providerConfig.nickname, + ); + await NetworksView.clearRpcInputBox(); + await NetworksView.typeInRpcUrl( + CustomNetworks.EthereumMainCustom.providerConfig.rpcUrlAlt, + ); + await NetworksView.tapSave(); + await WalletView.isConnectedNetwork( + CustomNetworks.EthereumMainCustom.providerConfig.nickname, + ); + }); + + it('should show Ethereum Main Custom on added network list', async () => { + await WalletView.tapNetworksButtonOnNavBar(); + await NetworkListModal.changeNetworkTo( + CustomNetworks.EthereumMainCustom.providerConfig.nickname, + true, //setting this made this step work for iOS + ); + await WalletView.isConnectedNetwork( + CustomNetworks.EthereumMainCustom.providerConfig.nickname, + ); }); }); From b300329708f5be01da6302d6c9bd8c5e23a22575 Mon Sep 17 00:00:00 2001 From: Jonathan Ferreira <44679989+Jonathansoufer@users.noreply.github.com> Date: Tue, 7 May 2024 17:34:32 +0100 Subject: [PATCH 2/2] feat: notifications onboarding wizard (#9392) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR is based on [9263](https://github.com/MetaMask/metamask-mobile/pull/9263), reverted due an e2e failure, which is fixed on this PR. ## **Related issues** Fixes: ## **Manual testing steps** Install a fresh app and accept the tour at beginning. ## **Screenshots/Recordings** ### **Before** ### **After** https://github.com/MetaMask/metamask-mobile/assets/44679989/f17e1a1d-43a3-4234-840e-9d93731f9370 ## **Pre-merge author checklist** - [x] I’ve followed [MetaMask Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../components/Navigation/TabBar/TabBar.tsx | 3 +- app/components/UI/Navbar/index.js | 4 +- .../__snapshots__/index.test.tsx.snap | 2 +- .../UI/OnboardingWizard/Coachmark/index.js | 4 +- .../Step1/__snapshots__/index.test.tsx.snap | 267 ++++++++++++++-- .../UI/OnboardingWizard/Step1/index.js | 117 ------- .../UI/OnboardingWizard/Step1/index.test.tsx | 15 +- .../UI/OnboardingWizard/Step1/index.tsx | 85 +++++ .../Step2/__snapshots__/index.test.tsx.snap | 296 +++++++++++++++-- .../UI/OnboardingWizard/Step2/index.js | 163 ---------- .../UI/OnboardingWizard/Step2/index.test.tsx | 23 +- .../UI/OnboardingWizard/Step2/index.tsx | 103 ++++++ .../Step3/__snapshots__/index.test.tsx.snap | 299 ++++++++++++++++-- .../UI/OnboardingWizard/Step3/index.js | 147 --------- .../UI/OnboardingWizard/Step3/index.test.tsx | 25 +- .../UI/OnboardingWizard/Step3/index.tsx | 98 ++++++ .../Step4/__snapshots__/index.test.tsx.snap | 297 +++++++++++++++-- .../UI/OnboardingWizard/Step4/index.js | 135 -------- .../UI/OnboardingWizard/Step4/index.test.tsx | 16 +- .../UI/OnboardingWizard/Step4/index.tsx | 112 +++++++ .../Step5/__snapshots__/index.test.tsx.snap | 294 +++++++++++++++-- .../UI/OnboardingWizard/Step5/index.js | 144 --------- .../UI/OnboardingWizard/Step5/index.test.tsx | 22 +- .../UI/OnboardingWizard/Step5/index.tsx | 95 ++++++ .../Step6/__snapshots__/index.test.tsx.snap | 294 +++++++++++++++-- .../UI/OnboardingWizard/Step6/index.test.tsx | 18 +- .../UI/OnboardingWizard/Step6/index.tsx | 99 ++++++ .../Step7/__snapshots__/index.test.tsx.snap | 275 ++++++++++++++++ .../UI/OnboardingWizard/Step7/index.test.tsx | 17 + .../{Step6/index.js => Step7/index.tsx} | 78 ++--- .../__snapshots__/index.test.tsx.snap | 145 +++++++-- .../UI/OnboardingWizard/index.test.tsx | 33 +- .../OnboardingWizard/{index.js => index.tsx} | 113 +++---- .../OnboardingWizard/{styles.js => styles.ts} | 2 +- .../UI/OnboardingWizard/useHandleLayout.tsx | 34 ++ app/components/Views/BrowserTab/index.js | 2 +- app/components/Views/Wallet/index.tsx | 2 +- app/core/Analytics/MetaMetrics.events.ts | 9 +- e2e/pages/modals/OnboardingWizardModal.js | 22 ++ e2e/specs/wallet/start-exploring.spec.js | 3 + .../Modals/OnboardingWizardModal.js | 11 + .../Components/OnboardingWizard.testIds.js | 3 + 42 files changed, 2796 insertions(+), 1130 deletions(-) delete mode 100644 app/components/UI/OnboardingWizard/Step1/index.js create mode 100644 app/components/UI/OnboardingWizard/Step1/index.tsx delete mode 100644 app/components/UI/OnboardingWizard/Step2/index.js create mode 100644 app/components/UI/OnboardingWizard/Step2/index.tsx delete mode 100644 app/components/UI/OnboardingWizard/Step3/index.js create mode 100644 app/components/UI/OnboardingWizard/Step3/index.tsx delete mode 100644 app/components/UI/OnboardingWizard/Step4/index.js create mode 100644 app/components/UI/OnboardingWizard/Step4/index.tsx delete mode 100644 app/components/UI/OnboardingWizard/Step5/index.js create mode 100644 app/components/UI/OnboardingWizard/Step5/index.tsx create mode 100644 app/components/UI/OnboardingWizard/Step6/index.tsx create mode 100644 app/components/UI/OnboardingWizard/Step7/__snapshots__/index.test.tsx.snap create mode 100644 app/components/UI/OnboardingWizard/Step7/index.test.tsx rename app/components/UI/OnboardingWizard/{Step6/index.js => Step7/index.tsx} (56%) rename app/components/UI/OnboardingWizard/{index.js => index.tsx} (68%) rename app/components/UI/OnboardingWizard/{styles.js => styles.ts} (95%) create mode 100644 app/components/UI/OnboardingWizard/useHandleLayout.tsx diff --git a/app/component-library/components/Navigation/TabBar/TabBar.tsx b/app/component-library/components/Navigation/TabBar/TabBar.tsx index ba51d756b889..c9658dab766b 100644 --- a/app/component-library/components/Navigation/TabBar/TabBar.tsx +++ b/app/component-library/components/Navigation/TabBar/TabBar.tsx @@ -34,13 +34,12 @@ const TabBar = ({ state, descriptors, navigation }: TabBarProps) => { * Current onboarding wizard step */ const wizardStep = useSelector((reduxState: any) => reduxState.wizard.step); - /** * Return current step of onboarding wizard if not step 5 nor 0 */ const renderOnboardingWizard = useCallback( () => - [4, 5].includes(wizardStep) && ( + [4, 5, 6].includes(wizardStep) && ( ), [navigation, wizardStep], diff --git a/app/components/UI/Navbar/index.js b/app/components/UI/Navbar/index.js index 4c673027233c..81b67775996c 100644 --- a/app/components/UI/Navbar/index.js +++ b/app/components/UI/Navbar/index.js @@ -91,13 +91,15 @@ const styles = StyleSheet.create({ paddingVertical: Device.isAndroid() ? 14 : 8, }, infoButton: { + paddingRight: Device.isAndroid() ? 0 : 18, + marginTop: 5, }, disabled: { opacity: 0.3, }, leftButtonContainer: { - marginRight: Device.isAndroid() ? 22 : 12, + marginRight: 12, flexDirection: 'row', alignItems: 'flex-end', }, diff --git a/app/components/UI/OnboardingWizard/Coachmark/__snapshots__/index.test.tsx.snap b/app/components/UI/OnboardingWizard/Coachmark/__snapshots__/index.test.tsx.snap index 5ac1b96a3feb..d116d46d8509 100644 --- a/app/components/UI/OnboardingWizard/Coachmark/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/OnboardingWizard/Coachmark/__snapshots__/index.test.tsx.snap @@ -159,7 +159,7 @@ exports[`Coachmark should render correctly 1`] = ` } > 1 - /5 + /6 marginBottom: 10, bottom: -2, alignItems: 'flex-end', - marginRight: 30, + marginRight: 38, }, topLeftCorner: { marginBottom: 10, @@ -322,7 +322,7 @@ export default class Coachmark extends PureComponent { {currentStep !== 0 && ( - {currentStep}/5 + {currentStep}/6 )} diff --git a/app/components/UI/OnboardingWizard/Step1/__snapshots__/index.test.tsx.snap b/app/components/UI/OnboardingWizard/Step1/__snapshots__/index.test.tsx.snap index 0cccc73238de..256d69c4ea8e 100644 --- a/app/components/UI/OnboardingWizard/Step1/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/OnboardingWizard/Step1/__snapshots__/index.test.tsx.snap @@ -1,37 +1,246 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Step1 should render correctly 1`] = ` - - - + + + + + + + Welcome to your wallet! + + + + + + + + On the blockchain, you need a wallet (and maybe some ETH). So let’s take a look at how to use your MetaMask wallet. + + + + + + No thanks + + + + + Take the tour + + + + + + + + + + `; diff --git a/app/components/UI/OnboardingWizard/Step1/index.js b/app/components/UI/OnboardingWizard/Step1/index.js deleted file mode 100644 index b4a120d3363b..000000000000 --- a/app/components/UI/OnboardingWizard/Step1/index.js +++ /dev/null @@ -1,117 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { View, Text, StyleSheet } from 'react-native'; -import Coachmark from '../Coachmark'; -import Device from '../../../../util/device'; -import setOnboardingWizardStep from '../../../../actions/wizard'; -import { strings } from '../../../../../locales/i18n'; -import onboardingStyles from './../styles'; -import { - MetaMetricsEvents, - ONBOARDING_WIZARD_STEP_DESCRIPTION, -} from '../../../../core/Analytics'; - -import { ThemeContext, mockTheme } from '../../../../util/theme'; -import { OnboardingWizardModalSelectorsIDs } from '../../../../../e2e/selectors/Modals/OnboardingWizardModal.selectors'; -import { withMetricsAwareness } from '../../../../components/hooks/useMetrics'; - -const styles = StyleSheet.create({ - main: { - flex: 1, - }, - coachmark: { - marginHorizontal: 16, - }, - coachmarkContainer: { - flex: 1, - position: 'absolute', - left: 0, - right: 0, - bottom: Device.isIphoneX() ? 80 : Device.isIos() ? 40 : 60, - }, -}); - -class Step1 extends PureComponent { - static propTypes = { - /** - * Callback called when closing step - */ - onClose: PropTypes.func, - /** - * Dispatch set onboarding wizard step - */ - setOnboardingWizardStep: PropTypes.func, - /** - * Metrics injected by withMetricsAwareness HOC - */ - metrics: PropTypes.object, - }; - - /** - * Dispatches 'setOnboardingWizardStep' with next step - */ - onNext = () => { - const { setOnboardingWizardStep } = this.props; - setOnboardingWizardStep && setOnboardingWizardStep(2); - - this.props.metrics.trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STARTED, { - tutorial_step_count: 1, - tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[1], - }); - }; - - /** - * Calls props 'onClose' - */ - onClose = () => { - const { onClose } = this.props; - onClose && onClose(false); - }; - - /** - * Returns content for this step - */ - content = () => { - const colors = this.context.colors || mockTheme.colors; - const dynamicOnboardingStyles = onboardingStyles(colors); - - return ( - - - {strings('onboarding_wizard_new.step1.content1')} - - - ); - }; - - render() { - return ( - - - - - - ); - } -} - -const mapDispatchToProps = (dispatch) => ({ - setOnboardingWizardStep: (step) => dispatch(setOnboardingWizardStep(step)), -}); - -Step1.contextType = ThemeContext; - -export default connect(null, mapDispatchToProps)(withMetricsAwareness(Step1)); diff --git a/app/components/UI/OnboardingWizard/Step1/index.test.tsx b/app/components/UI/OnboardingWizard/Step1/index.test.tsx index 6c0947216bee..2e4b74fcbe29 100644 --- a/app/components/UI/OnboardingWizard/Step1/index.test.tsx +++ b/app/components/UI/OnboardingWizard/Step1/index.test.tsx @@ -1,19 +1,14 @@ import React from 'react'; -import { shallow } from 'enzyme'; import Step1 from './'; -import configureMockStore from 'redux-mock-store'; -import { Provider } from 'react-redux'; +import renderWithProvider from '../../../../util/test/renderWithProvider'; -const mockStore = configureMockStore(); -const store = mockStore({}); +const closeOnboardingWizard = jest.fn(); describe('Step1', () => { it('should render correctly', () => { - const wrapper = shallow( - - - , + const { toJSON } = renderWithProvider( + , ); - expect(wrapper).toMatchSnapshot(); + expect(toJSON()).toMatchSnapshot(); }); }); diff --git a/app/components/UI/OnboardingWizard/Step1/index.tsx b/app/components/UI/OnboardingWizard/Step1/index.tsx new file mode 100644 index 000000000000..80dbca129936 --- /dev/null +++ b/app/components/UI/OnboardingWizard/Step1/index.tsx @@ -0,0 +1,85 @@ +import React, { useCallback, useContext } from 'react'; +import { View, Text, StyleSheet, Platform } from 'react-native'; +import { useDispatch } from 'react-redux'; + +import Coachmark from '../Coachmark'; +import Device from '../../../../util/device'; +import setOnboardingWizardStep from '../../../../actions/wizard'; +import { strings } from '../../../../../locales/i18n'; +import onboardingStyles from '../styles'; +import { + MetaMetricsEvents, + ONBOARDING_WIZARD_STEP_DESCRIPTION, +} from '../../../../core/Analytics'; +import { ThemeContext, mockTheme } from '../../../../util/theme'; +import generateTestId from '../../../../../wdio/utils/generateTestId'; +import { ONBOARDING_WIZARD_STEP_1_CONTAINER_ID } from '../../../../../wdio/screen-objects/testIDs/Components/OnboardingWizard.testIds'; +import { useMetrics } from '../../../../components/hooks/useMetrics'; + +const styles = StyleSheet.create({ + main: { + flex: 1, + }, + coachmark: { + marginHorizontal: 16, + }, + coachmarkContainer: { + flex: 1, + position: 'absolute', + left: 0, + right: 0, + bottom: Device.isIphoneX() ? 80 : Device.isIos() ? 40 : 64, + }, +}); +interface Step1Props { + onClose: () => Promise; +} + +const Step1 = ({ onClose }: Step1Props) => { + const theme = useContext(ThemeContext) || mockTheme; + const dynamicOnboardingStyles = onboardingStyles(theme.colors); + const dispatch = useDispatch(); + const { trackEvent } = useMetrics(); + + const content = useCallback( + () => ( + + + {strings('onboarding_wizard_new.step1.content1')} + + + ), + [dynamicOnboardingStyles], + ); + + const onNext = useCallback(() => { + dispatch(setOnboardingWizardStep?.(2)); + + trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STARTED, { + tutorial_step_count: 1, + tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[1], + }); + }, [dispatch, trackEvent]); + + return ( + + + + + + ); +}; + +export default Step1; diff --git a/app/components/UI/OnboardingWizard/Step2/__snapshots__/index.test.tsx.snap b/app/components/UI/OnboardingWizard/Step2/__snapshots__/index.test.tsx.snap index da7b5967fee9..f5ab4cdc2115 100644 --- a/app/components/UI/OnboardingWizard/Step2/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/OnboardingWizard/Step2/__snapshots__/index.test.tsx.snap @@ -1,37 +1,275 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Step2 should render correctly 1`] = ` - - - + + + + + + + + + + + + Your accounts + + + + + + + + This is your account and unique public address (0x...). Create more accounts by tapping the arrow icon. + + + + + + 1 + /6 + + + + + Got it + + + + + + + `; diff --git a/app/components/UI/OnboardingWizard/Step2/index.js b/app/components/UI/OnboardingWizard/Step2/index.js deleted file mode 100644 index 020e359a7525..000000000000 --- a/app/components/UI/OnboardingWizard/Step2/index.js +++ /dev/null @@ -1,163 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { StyleSheet, Text, View } from 'react-native'; -import Coachmark from '../Coachmark'; -import setOnboardingWizardStep from '../../../../actions/wizard'; -import { strings } from '../../../../../locales/i18n'; -import onboardingStyles from './../styles'; -import { - MetaMetricsEvents, - ONBOARDING_WIZARD_STEP_DESCRIPTION, -} from '../../../../core/Analytics'; -import { mockTheme, ThemeContext } from '../../../../util/theme'; -import { OnboardingWizardModalSelectorsIDs } from '../../../../../e2e/selectors/Modals/OnboardingWizardModal.selectors'; -import { withMetricsAwareness } from '../../../../components/hooks/useMetrics'; - -const styles = StyleSheet.create({ - main: { - flex: 1, - }, - coachmarkContainer: { - position: 'absolute', - left: 0, - right: 0, - marginHorizontal: 16, - }, -}); - -class Step2 extends PureComponent { - static propTypes = { - /** - * Dispatch set onboarding wizard step - */ - setOnboardingWizardStep: PropTypes.func, - /** - * Coachmark ref to get position - */ - coachmarkRef: PropTypes.object, - /** - * Callback called when closing step - */ - onClose: PropTypes.func, - /** - * Metrics injected by withMetricsAwareness HOC - */ - metrics: PropTypes.object, - }; - - state = { - coachmarkTop: 0, - }; - - componentDidMount = () => { - this.getPosition(this.props.coachmarkRef.yourAccountRef); - }; - - /** - * If component ref defined, calculate its position and position coachmark accordingly - */ - getPosition = (ref) => { - ref && - ref.current && - ref.current.measure((fx, fy, width, height, px, py) => { - this.setState({ - coachmarkTop: py + height, - }); - }); - }; - - /** - * Dispatches 'setOnboardingWizardStep' with next step - */ - onNext = () => { - const { setOnboardingWizardStep } = this.props; - setOnboardingWizardStep && setOnboardingWizardStep(3); - this.props.metrics.trackEvent( - MetaMetricsEvents.ONBOARDING_TOUR_STEP_COMPLETED, - { - tutorial_step_count: 2, - tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[2], - }, - ); - }; - - /** - * Dispatches 'setOnboardingWizardStep' with back step - */ - onBack = () => { - const { setOnboardingWizardStep } = this.props; - setOnboardingWizardStep && setOnboardingWizardStep(1); - this.props.metrics.trackEvent( - MetaMetricsEvents.ONBOARDING_TOUR_STEP_REVISITED, - { - tutorial_step_count: 2, - tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[2], - }, - ); - }; - - getOnboardingStyles = () => { - const colors = this.context.colors || mockTheme.colors; - return onboardingStyles(colors); - }; - - /** - * Calls props 'onClose' - */ - onClose = () => { - const { onClose } = this.props; - onClose && onClose(false); - }; - - /** - * Returns content for this step - */ - content = () => { - const dynamicOnboardingStyles = this.getOnboardingStyles(); - - return ( - - - {strings('onboarding_wizard_new.step2.content1')} - - - ); - }; - - render() { - return ( - - - - - - ); - } -} - -const mapDispatchToProps = (dispatch) => ({ - setOnboardingWizardStep: (step) => dispatch(setOnboardingWizardStep(step)), -}); - -Step2.contextType = ThemeContext; - -export default connect(null, mapDispatchToProps)(withMetricsAwareness(Step2)); diff --git a/app/components/UI/OnboardingWizard/Step2/index.test.tsx b/app/components/UI/OnboardingWizard/Step2/index.test.tsx index 72c722480bd1..c22e76eb6b05 100644 --- a/app/components/UI/OnboardingWizard/Step2/index.test.tsx +++ b/app/components/UI/OnboardingWizard/Step2/index.test.tsx @@ -1,19 +1,20 @@ import React from 'react'; -import { shallow } from 'enzyme'; import Step2 from './'; -import configureMockStore from 'redux-mock-store'; -import { Provider } from 'react-redux'; - -const mockStore = configureMockStore(); -const store = mockStore({}); +import renderWithProvider from '../../../../util/test/renderWithProvider'; +const coachmarkRef = { + yourAccountRef: { + current: { + measure: jest.fn(), + }, + }, +}; +const closeOnboardingWizard = jest.fn(); describe('Step2', () => { it('should render correctly', () => { - const wrapper = shallow( - - - , + const { toJSON } = renderWithProvider( + , ); - expect(wrapper).toMatchSnapshot(); + expect(toJSON()).toMatchSnapshot(); }); }); diff --git a/app/components/UI/OnboardingWizard/Step2/index.tsx b/app/components/UI/OnboardingWizard/Step2/index.tsx new file mode 100644 index 000000000000..6fda98c61afa --- /dev/null +++ b/app/components/UI/OnboardingWizard/Step2/index.tsx @@ -0,0 +1,103 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React, { useCallback, useEffect, useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { Platform, StyleSheet, Text, View } from 'react-native'; +import setOnboardingWizardStep from '../../../../actions/wizard'; +import { strings } from '../../../../../locales/i18n'; +import Coachmark from '../Coachmark'; + +import onboardingStyles from '../styles'; +import { + MetaMetricsEvents, + ONBOARDING_WIZARD_STEP_DESCRIPTION, +} from '../../../../core/Analytics'; +import { useTheme } from '../../../../util/theme'; +import generateTestId from '../../../../../wdio/utils/generateTestId'; +import { ONBOARDING_WIZARD_SECOND_STEP_CONTENT_ID } from '../../../../../wdio/screen-objects/testIDs/Components/OnboardingWizard.testIds'; +import { useMetrics } from '../../../hooks/useMetrics'; +import useHandleLayout from '../useHandleLayout'; + +const styles = StyleSheet.create({ + main: { + flex: 1, + }, + coachmarkContainer: { + position: 'absolute', + left: 0, + right: 0, + marginHorizontal: 16, + }, +}); + +interface Step2Props { + coachmarkRef: any; + onClose: () => Promise; +} + +const Step2 = ({ coachmarkRef, onClose }: Step2Props) => { + const { colors } = useTheme(); + const { trackEvent } = useMetrics(); + const dispatch = useDispatch(); + const { coachmarkTop } = useHandleLayout(coachmarkRef); + + const onNext = () => { + dispatch(setOnboardingWizardStep?.(3)); + trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_COMPLETED, { + tutorial_step_count: 2, + tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[2], + }); + }; + + const onBack = () => { + dispatch(setOnboardingWizardStep?.(1)); + trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_REVISITED, { + tutorial_step_count: 2, + tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[2], + }); + }; + + const getOnboardingStyles = () => onboardingStyles(colors); + + const content = () => { + const dynamicOnboardingStyles = getOnboardingStyles(); + + return ( + + + {strings('onboarding_wizard_new.step2.content1')} + + + ); + }; + + return ( + + + + + + ); +}; + +export default Step2; diff --git a/app/components/UI/OnboardingWizard/Step3/__snapshots__/index.test.tsx.snap b/app/components/UI/OnboardingWizard/Step3/__snapshots__/index.test.tsx.snap index d3b0d453b60c..d8b575b47ae2 100644 --- a/app/components/UI/OnboardingWizard/Step3/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/OnboardingWizard/Step3/__snapshots__/index.test.tsx.snap @@ -1,39 +1,276 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Step3 should render correctly 1`] = ` - - - + + + + + + + + + + + + Managing your account + + + + + + + + Edit your account name, view transactions on Etherscan, share your public key, and see your private key by tapping this icon + + + + + + 2 + /6 + + + + + Got it + + + + + + + `; diff --git a/app/components/UI/OnboardingWizard/Step3/index.js b/app/components/UI/OnboardingWizard/Step3/index.js deleted file mode 100644 index 2d77bc77579a..000000000000 --- a/app/components/UI/OnboardingWizard/Step3/index.js +++ /dev/null @@ -1,147 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { Dimensions, StyleSheet, Text, View } from 'react-native'; -import Coachmark from '../Coachmark'; -import setOnboardingWizardStep from '../../../../actions/wizard'; -import { strings } from '../../../../../locales/i18n'; -import onboardingStyles from './../styles'; -import { - MetaMetricsEvents, - ONBOARDING_WIZARD_STEP_DESCRIPTION, -} from '../../../../core/Analytics'; -import { useTheme } from '../../../../util/theme'; -import { selectCurrentCurrency } from '../../../../selectors/currencyRateController'; -import { selectAccounts } from '../../../../selectors/accountTrackerController'; -import { - selectIdentities, - selectSelectedAddress, -} from '../../../../selectors/preferencesController'; -import { OnboardingWizardModalSelectorsIDs } from '../../../../../e2e/selectors/Modals/OnboardingWizardModal.selectors'; -import { useMetrics } from '../../../../components/hooks/useMetrics'; - -const styles = StyleSheet.create({ - main: { - flex: 1, - }, - coachmarkContainer: { - position: 'absolute', - }, -}); - -const Step3 = ({ setOnboardingWizardStep, coachmarkRef, onClose }) => { - const { colors } = useTheme(); - const { trackEvent } = useMetrics(); - - const [coachmarkTop, setCoachmarkTop] = useState(0); - const [coachmarkLeft, setCoachmarkLeft] = useState(0); - const [coachmarkRight, setCoachmarkRight] = useState(0); - - const handleLayout = useCallback(() => { - const accActionsRef = coachmarkRef.accountActionsRef?.current; - if (!accActionsRef) return; - - accActionsRef.measure( - ( - accActionsFx, - accActionsFy, - accActionsWidth, - accActionsHeight, - accActionsPageX, - accActionsPageY, - ) => { - const top = accActionsHeight + accActionsPageY; - const right = - Dimensions.get('window').width - (accActionsPageX + accActionsWidth); - setCoachmarkTop(top); - setCoachmarkLeft(accActionsPageX); - setCoachmarkRight(right); - }, - ); - }, [coachmarkRef.accountActionsRef]); - - useEffect(() => { - handleLayout(); - }, [handleLayout]); - - const onNext = () => { - setOnboardingWizardStep && setOnboardingWizardStep(4); - trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_COMPLETED, { - tutorial_step_count: 3, - tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[3], - }); - }; - - const onBack = () => { - setOnboardingWizardStep && setOnboardingWizardStep(2); - trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_REVISITED, { - tutorial_step_count: 3, - tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[3], - }); - }; - - const getOnboardingStyles = () => onboardingStyles(colors); - - const onCloseStep = () => { - onClose && onClose(false); - }; - - const content = () => { - const dynamicOnboardingStyles = getOnboardingStyles(); - - return ( - - - {strings('onboarding_wizard_new.step3.content1')} - - - ); - }; - - return ( - - - - - - ); -}; - -Step3.propTypes = { - setOnboardingWizardStep: PropTypes.func, - coachmarkRef: PropTypes.object, - onClose: PropTypes.func, -}; - -const mapStateToProps = (state) => ({ - accounts: selectAccounts(state), - currentCurrency: selectCurrentCurrency(state), - selectedAddress: selectSelectedAddress(state), - identities: selectIdentities(state), -}); - -const mapDispatchToProps = (dispatch) => ({ - setOnboardingWizardStep: (step) => dispatch(setOnboardingWizardStep(step)), -}); - -export default connect(mapStateToProps, mapDispatchToProps)(Step3); diff --git a/app/components/UI/OnboardingWizard/Step3/index.test.tsx b/app/components/UI/OnboardingWizard/Step3/index.test.tsx index bb1c234aaea2..d7fe82145e5c 100644 --- a/app/components/UI/OnboardingWizard/Step3/index.test.tsx +++ b/app/components/UI/OnboardingWizard/Step3/index.test.tsx @@ -1,25 +1,20 @@ import React from 'react'; import Step3 from './'; -import { shallow } from 'enzyme'; -import configureMockStore from 'redux-mock-store'; -import { Provider } from 'react-redux'; -import initialBackgroundState from '../../../../util/test/initial-background-state.json'; +import renderWithProvider from '../../../../util/test/renderWithProvider'; -const mockStore = configureMockStore(); -const initialState = { - engine: { - backgroundState: initialBackgroundState, +const coachmarkRef = { + yourAccountRef: { + current: { + measure: jest.fn(), + }, }, }; -const store = mockStore(initialState); - +const closeOnboardingWizard = jest.fn(); describe('Step3', () => { it('should render correctly', () => { - const wrapper = shallow( - - - , + const { toJSON } = renderWithProvider( + , ); - expect(wrapper).toMatchSnapshot(); + expect(toJSON()).toMatchSnapshot(); }); }); diff --git a/app/components/UI/OnboardingWizard/Step3/index.tsx b/app/components/UI/OnboardingWizard/Step3/index.tsx new file mode 100644 index 000000000000..3ea13a9f5e44 --- /dev/null +++ b/app/components/UI/OnboardingWizard/Step3/index.tsx @@ -0,0 +1,98 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React, { useCallback, useEffect, useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { StyleSheet, Text, View } from 'react-native'; +import Coachmark from '../Coachmark'; +import setOnboardingWizardStep from '../../../../actions/wizard'; +import { strings } from '../../../../../locales/i18n'; +import onboardingStyles from '../styles'; +import { + MetaMetricsEvents, + ONBOARDING_WIZARD_STEP_DESCRIPTION, +} from '../../../../core/Analytics'; +import { useTheme } from '../../../../util/theme'; +import { useMetrics } from '../../../hooks/useMetrics'; +import { OnboardingWizardModalSelectorsIDs } from '../../../../../e2e/selectors/Modals/OnboardingWizardModal.selectors'; +import useHandleLayout from '../useHandleLayout'; + +const styles = StyleSheet.create({ + main: { + flex: 1, + }, + coachmarkContainer: { + position: 'absolute', + left: 0, + right: 0, + marginHorizontal: 16, + }, +}); + +interface Step3Props { + coachmarkRef: any; + onClose: () => Promise; +} + +const Step3 = ({ coachmarkRef, onClose }: Step3Props) => { + const { colors } = useTheme(); + const { trackEvent } = useMetrics(); + const dispatch = useDispatch(); + const { coachmarkTop } = useHandleLayout(coachmarkRef); + + const onNext = () => { + dispatch(setOnboardingWizardStep?.(4)); + trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_COMPLETED, { + tutorial_step_count: 3, + tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[3], + }); + }; + + const onBack = () => { + dispatch(setOnboardingWizardStep?.(2)); + trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_REVISITED, { + tutorial_step_count: 3, + tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[3], + }); + }; + + const getOnboardingStyles = () => onboardingStyles(colors); + + const content = () => { + const dynamicOnboardingStyles = getOnboardingStyles(); + + return ( + + + {strings('onboarding_wizard_new.step3.content1')} + + + ); + }; + + return ( + + + + + + ); +}; + +export default Step3; diff --git a/app/components/UI/OnboardingWizard/Step4/__snapshots__/index.test.tsx.snap b/app/components/UI/OnboardingWizard/Step4/__snapshots__/index.test.tsx.snap index f8ea23e7a89c..711b0fb7b366 100644 --- a/app/components/UI/OnboardingWizard/Step4/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/OnboardingWizard/Step4/__snapshots__/index.test.tsx.snap @@ -1,37 +1,276 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Step4 should render correctly 1`] = ` - - - + + + + + + + + + + + + Notifications + + + + + + + + Stay in the loop on what's happening in your wallet + + + + + + 3 + /6 + + + + + Got it + + + + + + + `; diff --git a/app/components/UI/OnboardingWizard/Step4/index.js b/app/components/UI/OnboardingWizard/Step4/index.js deleted file mode 100644 index 9deb8604d2ed..000000000000 --- a/app/components/UI/OnboardingWizard/Step4/index.js +++ /dev/null @@ -1,135 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { Dimensions, StyleSheet, Text, View } from 'react-native'; -import Coachmark from '../Coachmark'; -import setOnboardingWizardStep from '../../../../actions/wizard'; -import { strings } from '../../../../../locales/i18n'; -import onboardingStyles from './../styles'; -import { - MetaMetricsEvents, - ONBOARDING_WIZARD_STEP_DESCRIPTION, -} from '../../../../core/Analytics'; -import { useTheme } from '../../../../util/theme'; -import { OnboardingWizardModalSelectorsIDs } from '../../../../../e2e/selectors/Modals/OnboardingWizardModal.selectors'; -import { useMetrics } from '../../../../components/hooks/useMetrics'; - -const styles = StyleSheet.create({ - main: { - flex: 1, - }, - coachmarkContainer: { - position: 'absolute', - left: 0, - right: 0, - }, - coachmark: { marginHorizontal: 16 }, -}); - -const Step4 = (props) => { - const { setOnboardingWizardStep, onClose } = props; - const { trackEvent } = useMetrics(); - const { colors } = useTheme(); - const dynamicOnboardingStyles = onboardingStyles(colors); - const [coachmarkBottom, setCoachmarkBottom] = useState(); - - const getCoachmarkPosition = useCallback(() => { - props?.coachmarkRef?.current?.measure( - (x, y, width, heigh, pageX, pageY) => { - setCoachmarkBottom(Dimensions.get('window').height - pageY); - }, - ); - }, [props?.coachmarkRef]); - - useEffect(() => { - getCoachmarkPosition(); - }, [getCoachmarkPosition]); - - /** - * Dispatches 'setOnboardingWizardStep' with next step - */ - const onNext = () => { - setOnboardingWizardStep && setOnboardingWizardStep(5); - trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_COMPLETED, { - tutorial_step_count: 4, - tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[4], - }); - }; - - /** - * Dispatches 'setOnboardingWizardStep' with back step - */ - const onBack = () => { - setOnboardingWizardStep && setOnboardingWizardStep(3); - trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_REVISITED, { - tutorial_step_count: 4, - tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[4], - }); - }; - - /** - * Calls props 'onClose' - */ - const handleOnClose = () => { - onClose && onClose(false); - }; - - /** - * Returns content for this step - */ - const content = () => ( - - - {strings('onboarding_wizard_new.step4.content1')} - - - ); - - return ( - - - - - - ); -}; - -const mapDispatchToProps = (dispatch) => ({ - setOnboardingWizardStep: (step) => dispatch(setOnboardingWizardStep(step)), -}); - -Step4.propTypes = { - /** - * Dispatch set onboarding wizard step - */ - setOnboardingWizardStep: PropTypes.func, - /** - * Callback called when closing step - */ - onClose: PropTypes.func, - /** - * coachmark ref to get position - */ - coachmarkRef: PropTypes.object, -}; - -export default connect(null, mapDispatchToProps)(Step4); diff --git a/app/components/UI/OnboardingWizard/Step4/index.test.tsx b/app/components/UI/OnboardingWizard/Step4/index.test.tsx index 5d484d0ba58c..9ca06cfc7e9d 100644 --- a/app/components/UI/OnboardingWizard/Step4/index.test.tsx +++ b/app/components/UI/OnboardingWizard/Step4/index.test.tsx @@ -1,19 +1,13 @@ import React from 'react'; -import { shallow } from 'enzyme'; import Step4 from './'; -import configureMockStore from 'redux-mock-store'; -import { Provider } from 'react-redux'; - -const mockStore = configureMockStore(); -const store = mockStore({}); +import renderWithProvider from '../../../../util/test/renderWithProvider'; +const closeOnboardingWizard = jest.fn(); describe('Step4', () => { it('should render correctly', () => { - const wrapper = shallow( - - - , + const { toJSON } = renderWithProvider( + , ); - expect(wrapper).toMatchSnapshot(); + expect(toJSON()).toMatchSnapshot(); }); }); diff --git a/app/components/UI/OnboardingWizard/Step4/index.tsx b/app/components/UI/OnboardingWizard/Step4/index.tsx new file mode 100644 index 000000000000..e1cfeb6bccd8 --- /dev/null +++ b/app/components/UI/OnboardingWizard/Step4/index.tsx @@ -0,0 +1,112 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { Platform, StyleSheet, Text, View } from 'react-native'; +import setOnboardingWizardStep from '../../../../actions/wizard'; +import { strings } from '../../../../../locales/i18n'; +import Coachmark from '../Coachmark'; + +import Device from '../../../../util/device'; + +import onboardingStyles from '../styles'; +import { + MetaMetricsEvents, + ONBOARDING_WIZARD_STEP_DESCRIPTION, +} from '../../../../core/Analytics'; +import { useTheme } from '../../../../util/theme'; +import generateTestId from '../../../../../wdio/utils/generateTestId'; +import { ONBOARDING_WIZARD_FOURTH_STEP_CONTENT_ID } from '../../../../../wdio/screen-objects/testIDs/Components/OnboardingWizard.testIds'; + +import { useMetrics } from '../../../hooks/useMetrics'; + +const styles = StyleSheet.create({ + main: { + flex: 1, + }, + coachmarkContainer: { + position: 'absolute', + left: 0, + right: 0, + marginHorizontal: 16, + }, +}); + +interface Step4Props { + onClose: () => Promise; +} + +const Step4 = ({ onClose }: Step4Props) => { + const { colors } = useTheme(); + const { trackEvent } = useMetrics(); + const dispatch = useDispatch(); + const [coachmarkTop, setCoachmarkTop] = useState(0); + + const handleLayout = useCallback(() => { + const top = Device.isIphoneX() ? 82 : Device.isIos() ? 64 : 60; + setCoachmarkTop(top); + }, []); + + useEffect(() => { + handleLayout(); + }, [handleLayout]); + + const onNext = () => { + dispatch(setOnboardingWizardStep?.(5)); + trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_COMPLETED, { + tutorial_step_count: 4, + tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[4], + }); + }; + + const onBack = () => { + dispatch(setOnboardingWizardStep?.(3)); + trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_REVISITED, { + tutorial_step_count: 4, + tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[4], + }); + }; + + const getOnboardingStyles = () => onboardingStyles(colors); + + const content = () => { + const dynamicOnboardingStyles = getOnboardingStyles(); + + return ( + + + {strings('onboarding_wizard_new.step4.content1')} + + + ); + }; + + return ( + + + + + + ); +}; + +export default Step4; diff --git a/app/components/UI/OnboardingWizard/Step5/__snapshots__/index.test.tsx.snap b/app/components/UI/OnboardingWizard/Step5/__snapshots__/index.test.tsx.snap index 485a64cfed41..bf0ea738ea79 100644 --- a/app/components/UI/OnboardingWizard/Step5/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/OnboardingWizard/Step5/__snapshots__/index.test.tsx.snap @@ -1,37 +1,273 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Step5 should render correctly 1`] = ` - - - + + + + + + + + + Using your wallet + + + + + + + + Buy, send, swap, and receive assets by tapping this icon + + + + + + 4 + /6 + + + + + Got it + + + + + + + + + + `; diff --git a/app/components/UI/OnboardingWizard/Step5/index.js b/app/components/UI/OnboardingWizard/Step5/index.js deleted file mode 100644 index a68c0c8dd764..000000000000 --- a/app/components/UI/OnboardingWizard/Step5/index.js +++ /dev/null @@ -1,144 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { Dimensions, StyleSheet, Text, View } from 'react-native'; -import { colors as importedColors } from '../../../../styles/common'; -import Coachmark from '../Coachmark'; -import setOnboardingWizardStep from '../../../../actions/wizard'; -import { strings } from '../../../../../locales/i18n'; -import onboardingStyles from './../styles'; -import { - MetaMetricsEvents, - ONBOARDING_WIZARD_STEP_DESCRIPTION, -} from '../../../../core/Analytics'; -import { useTheme } from '../../../../util/theme'; -import { createBrowserNavDetails } from '../../../Views/Browser'; -import { OnboardingWizardModalSelectorsIDs } from '../../../../../e2e/selectors/Modals/OnboardingWizardModal.selectors'; -import { useMetrics } from '../../../../components/hooks/useMetrics'; - -const WIDTH = Dimensions.get('window').width; -const styles = StyleSheet.create({ - main: { - flex: 1, - backgroundColor: importedColors.transparent, - marginLeft: 16, - }, - some: { - width: WIDTH - 32, - }, - coachmarkContainer: { - position: 'absolute', - left: 0, - right: 0, - }, -}); - -const Step5 = (props) => { - const { navigation, setOnboardingWizardStep, onClose } = props; - const { trackEvent } = useMetrics(); - const { colors } = useTheme(); - const dynamicOnboardingStyles = onboardingStyles(colors); - const [coachmarkBottom, setCoachmarkBottom] = useState(); - - /** - * Dispatches 'setOnboardingWizardStep' with next step - */ - const onNext = () => { - setOnboardingWizardStep && setOnboardingWizardStep(6); - navigation && navigation.navigate(...createBrowserNavDetails()); - trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_COMPLETED, { - tutorial_step_count: 5, - tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[5], - }); - }; - - /** - * Dispatches 'setOnboardingWizardStep' with next step - */ - const onBack = () => { - navigation && navigation.navigate('WalletView'); - setTimeout(() => { - setOnboardingWizardStep && setOnboardingWizardStep(4); - }, 1); - trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_REVISITED, { - tutorial_step_count: 5, - tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[5], - }); - }; - - /** - * Calls props 'onClose' - */ - const handleOnClose = () => { - onClose && onClose(false); - }; - - /** - * Returns content for this step - */ - const content = () => ( - - - {strings('onboarding_wizard_new.step5.content1')} - - - ); - - const getCoachmarkPosition = useCallback(() => { - props?.coachmarkRef?.current?.measure( - (x, y, width, heigh, pageX, pageY) => { - setCoachmarkBottom(Dimensions.get('window').height - pageY); - }, - ); - }, [props?.coachmarkRef]); - - useEffect(() => { - getCoachmarkPosition(); - }, [getCoachmarkPosition]); - - return ( - - - - - - ); -}; - -const mapDispatchToProps = (dispatch) => ({ - setOnboardingWizardStep: (step) => dispatch(setOnboardingWizardStep(step)), -}); - -Step5.propTypes = { - /** - * Object that represents the navigator - */ - navigation: PropTypes.object, - /** - * Dispatch set onboarding wizard step - */ - setOnboardingWizardStep: PropTypes.func, - /** - * Callback called when closing step - */ - onClose: PropTypes.func, - /** - * ref - */ - coachmarkRef: PropTypes.object, -}; - -export default connect(null, mapDispatchToProps)(Step5); diff --git a/app/components/UI/OnboardingWizard/Step5/index.test.tsx b/app/components/UI/OnboardingWizard/Step5/index.test.tsx index 1761d411e55f..862d16843716 100644 --- a/app/components/UI/OnboardingWizard/Step5/index.test.tsx +++ b/app/components/UI/OnboardingWizard/Step5/index.test.tsx @@ -1,19 +1,21 @@ import React from 'react'; -import { shallow } from 'enzyme'; import Step5 from './'; -import configureMockStore from 'redux-mock-store'; -import { Provider } from 'react-redux'; +import renderWithProvider from '../../../../util/test/renderWithProvider'; -const mockStore = configureMockStore(); -const store = mockStore({}); +const coachmarkRef = { + yourAccountRef: { + current: { + measure: jest.fn(), + }, + }, +}; +const closeOnboardingWizard = jest.fn(); describe('Step5', () => { it('should render correctly', () => { - const wrapper = shallow( - - - , + const { toJSON } = renderWithProvider( + , ); - expect(wrapper).toMatchSnapshot(); + expect(toJSON()).toMatchSnapshot(); }); }); diff --git a/app/components/UI/OnboardingWizard/Step5/index.tsx b/app/components/UI/OnboardingWizard/Step5/index.tsx new file mode 100644 index 000000000000..c49ceabedc91 --- /dev/null +++ b/app/components/UI/OnboardingWizard/Step5/index.tsx @@ -0,0 +1,95 @@ +import React from 'react'; +import { useDispatch } from 'react-redux'; +import { StyleSheet, Text, View } from 'react-native'; +import Coachmark from '../Coachmark'; +import setOnboardingWizardStep from '../../../../actions/wizard'; +import { strings } from '../../../../../locales/i18n'; +import onboardingStyles from '../styles'; +import Device from '../../../../util/device'; +import { + MetaMetricsEvents, + ONBOARDING_WIZARD_STEP_DESCRIPTION, +} from '../../../../core/Analytics'; +import { useTheme } from '../../../../util/theme'; + +import { useMetrics } from '../../../hooks/useMetrics'; +import { OnboardingWizardModalSelectorsIDs } from '../../../../../e2e/selectors/Modals/OnboardingWizardModal.selectors'; + +const styles = StyleSheet.create({ + main: { + flex: 1, + }, + coachmarkContainer: { + position: 'absolute', + left: 0, + right: 0, + marginHorizontal: 16, + bottom: Device.isIphoneX() ? 80 : Device.isIos() ? 40 : 64, + }, +}); + +interface Step5Props { + coachmarkRef: any; + onClose: () => Promise; +} + +const Step5 = ({ onClose }: Step5Props) => { + const { trackEvent } = useMetrics(); + const { colors } = useTheme(); + const dispatch = useDispatch(); + const dynamicOnboardingStyles = onboardingStyles(colors); + + /** + * Dispatches 'setOnboardingWizardStep' with next step + */ + const onNext = () => { + dispatch(setOnboardingWizardStep?.(6)); + trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_COMPLETED, { + tutorial_step_count: 5, + tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[5], + }); + }; + + /** + * Dispatches 'setOnboardingWizardStep' with back step + */ + const onBack = () => { + dispatch(setOnboardingWizardStep?.(4)); + trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_REVISITED, { + tutorial_step_count: 5, + tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[5], + }); + }; + + /** + * Returns content for this step + */ + const content = () => ( + + + {strings('onboarding_wizard_new.step5.content1')} + + + ); + + return ( + + + + + + ); +}; + +export default Step5; diff --git a/app/components/UI/OnboardingWizard/Step6/__snapshots__/index.test.tsx.snap b/app/components/UI/OnboardingWizard/Step6/__snapshots__/index.test.tsx.snap index aa206c67487b..8b1357274ba1 100644 --- a/app/components/UI/OnboardingWizard/Step6/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/OnboardingWizard/Step6/__snapshots__/index.test.tsx.snap @@ -1,37 +1,273 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Step6 should render correctly 1`] = ` - - - + + + + + + + + + Exploring web3 + + + + + + + + Open the browser by tapping this icon + + + + + + 5 + /6 + + + + + Got it + + + + + + + + + + `; diff --git a/app/components/UI/OnboardingWizard/Step6/index.test.tsx b/app/components/UI/OnboardingWizard/Step6/index.test.tsx index 07ea0ad77e89..b3e50ccecb48 100644 --- a/app/components/UI/OnboardingWizard/Step6/index.test.tsx +++ b/app/components/UI/OnboardingWizard/Step6/index.test.tsx @@ -1,19 +1,17 @@ import React from 'react'; -import { shallow } from 'enzyme'; import Step6 from './'; -import configureMockStore from 'redux-mock-store'; -import { Provider } from 'react-redux'; +import renderWithProvider from '../../../../util/test/renderWithProvider'; -const mockStore = configureMockStore(); -const store = mockStore({}); +const navigationMock = { + navigate: jest.fn(), +}; +const closeOnboardingWizard = jest.fn(); describe('Step6', () => { it('should render correctly', () => { - const wrapper = shallow( - - - , + const { toJSON } = renderWithProvider( + , ); - expect(wrapper).toMatchSnapshot(); + expect(toJSON()).toMatchSnapshot(); }); }); diff --git a/app/components/UI/OnboardingWizard/Step6/index.tsx b/app/components/UI/OnboardingWizard/Step6/index.tsx new file mode 100644 index 000000000000..2513707521fb --- /dev/null +++ b/app/components/UI/OnboardingWizard/Step6/index.tsx @@ -0,0 +1,99 @@ +import React from 'react'; +import { useDispatch } from 'react-redux'; +import { Platform, StyleSheet, Text, View } from 'react-native'; +import Device from '../../../../util/device'; +import Coachmark from '../Coachmark'; +import setOnboardingWizardStep from '../../../../actions/wizard'; +import { strings } from '../../../../../locales/i18n'; +import { createBrowserNavDetails } from '../../../Views/Browser'; + +import onboardingStyles from '../styles'; +import { + MetaMetricsEvents, + ONBOARDING_WIZARD_STEP_DESCRIPTION, +} from '../../../../core/Analytics'; +import { useTheme } from '../../../../util/theme'; +import generateTestId from '../../../../../wdio/utils/generateTestId'; +import { ONBOARDING_WIZARD_SIXTH_STEP_CONTENT_ID } from '../../../../../wdio/screen-objects/testIDs/Components/OnboardingWizard.testIds'; +import { useMetrics } from '../../../hooks/useMetrics'; + +const styles = StyleSheet.create({ + main: { + flex: 1, + }, + coachmarkContainer: { + position: 'absolute', + alignSelf: 'center', + left: 0, + right: 0, + marginHorizontal: 16, + bottom: Device.isIphoneX() ? 80 : Device.isIos() ? 40 : 64, + }, +}); + +interface Step6Props { + navigation: any; + onClose: () => Promise; +} + +const Step6 = ({ onClose, navigation }: Step6Props) => { + const { trackEvent } = useMetrics(); + const { colors } = useTheme(); + const dynamicOnboardingStyles = onboardingStyles(colors); + const dispatch = useDispatch(); + /** + * Dispatches 'setOnboardingWizardStep' with next step + */ + const onNext = () => { + dispatch(setOnboardingWizardStep?.(7)); + navigation?.navigate(...createBrowserNavDetails()); + trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_COMPLETED, { + tutorial_step_count: 6, + tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[6], + }); + }; + + /** + * Dispatches 'setOnboardingWizardStep' with next step + */ + const onBack = () => { + dispatch(setOnboardingWizardStep?.(5)); + trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_REVISITED, { + tutorial_step_count: 6, + tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[6], + }); + }; + + /** + * Returns content for this step + */ + const content = () => ( + + + {strings('onboarding_wizard_new.step6.content1')} + + + ); + + return ( + + + + + + ); +}; + +export default Step6; diff --git a/app/components/UI/OnboardingWizard/Step7/__snapshots__/index.test.tsx.snap b/app/components/UI/OnboardingWizard/Step7/__snapshots__/index.test.tsx.snap new file mode 100644 index 000000000000..beed82fae999 --- /dev/null +++ b/app/components/UI/OnboardingWizard/Step7/__snapshots__/index.test.tsx.snap @@ -0,0 +1,275 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Step7 should render correctly 1`] = ` + + + + + + + + + + + + + Using the browser + + + + + + + + Search for sites by keyword or enter a URL. Have fun out there! + + + + + + 6 + /6 + + + + + Got it + + + + + + + +`; diff --git a/app/components/UI/OnboardingWizard/Step7/index.test.tsx b/app/components/UI/OnboardingWizard/Step7/index.test.tsx new file mode 100644 index 000000000000..b810d434c161 --- /dev/null +++ b/app/components/UI/OnboardingWizard/Step7/index.test.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import Step7 from './'; +import renderWithProvider from '../../../../util/test/renderWithProvider'; + +const navigationMock = { + navigate: jest.fn(), +}; + +const closeOnboardingWizard = jest.fn(); +describe('Step7', () => { + it('should render correctly', () => { + const { toJSON } = renderWithProvider( + , + ); + expect(toJSON()).toMatchSnapshot(); + }); +}); diff --git a/app/components/UI/OnboardingWizard/Step6/index.js b/app/components/UI/OnboardingWizard/Step7/index.tsx similarity index 56% rename from app/components/UI/OnboardingWizard/Step6/index.js rename to app/components/UI/OnboardingWizard/Step7/index.tsx index a70d2fa7df47..6cc32b76db0e 100644 --- a/app/components/UI/OnboardingWizard/Step6/index.js +++ b/app/components/UI/OnboardingWizard/Step7/index.tsx @@ -1,27 +1,28 @@ import React, { useEffect, useState } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { StyleSheet, Text, View } from 'react-native'; +import { useDispatch } from 'react-redux'; +import { Platform, StyleSheet, Text, View } from 'react-native'; import Coachmark from '../Coachmark'; import setOnboardingWizardStep from '../../../../actions/wizard'; import { strings } from '../../../../../locales/i18n'; -import onboardingStyles from './../styles'; +import onboardingStyles from '../styles'; +import Routes from '../../../../constants/navigation/Routes'; + import Device from '../../../../util/device'; import { MetaMetricsEvents, ONBOARDING_WIZARD_STEP_DESCRIPTION, } from '../../../../core/Analytics'; import { useTheme } from '../../../../util/theme'; -import Routes from '../../../../constants/navigation/Routes'; -import { OnboardingWizardModalSelectorsIDs } from '../../../../../e2e/selectors/Modals/OnboardingWizardModal.selectors'; -import { useMetrics } from '../../../../components/hooks/useMetrics'; + +import generateTestId from '../../../../../wdio/utils/generateTestId'; +import { ONBOARDING_WIZARD_SEVENTH_STEP_CONTENT_ID } from '../../../../../wdio/screen-objects/testIDs/Components/OnboardingWizard.testIds'; +import { useMetrics } from '../../../hooks/useMetrics'; const styles = StyleSheet.create({ main: { flex: 1, }, coachmarkContainer: { - flex: 1, position: 'absolute', left: 0, right: 0, @@ -29,10 +30,14 @@ const styles = StyleSheet.create({ }, }); -const Step6 = (props) => { - const { setOnboardingWizardStep, onClose, navigation } = props; - const { trackEvent } = useMetrics(); +interface Step7Props { + navigation: any; + onClose: (arg0: boolean) => void; +} +const Step7 = ({ navigation, onClose }: Step7Props) => { + const { trackEvent } = useMetrics(); + const dispatch = useDispatch(); const [ready, setReady] = useState(false); const [coachmarkTop, setCoachmarkTop] = useState(0); const { colors } = useTheme(); @@ -48,10 +53,7 @@ const Step6 = (props) => { }; useEffect(() => { - // As we're changing the view on this step, we have to make sure Browser is rendered - setTimeout(() => { - getPosition(); - }, 1200); + getPosition(); }, []); /** @@ -59,20 +61,15 @@ const Step6 = (props) => { */ const onBack = () => { navigation?.navigate?.(Routes.WALLET.HOME); - setOnboardingWizardStep && setOnboardingWizardStep(5); + setTimeout(() => { + dispatch(setOnboardingWizardStep?.(6)); + }, 1); trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_STEP_REVISITED, { - tutorial_step_count: 6, - tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[6], + tutorial_step_count: 7, + tutorial_step_name: ONBOARDING_WIZARD_STEP_DESCRIPTION[7], }); }; - /** - * Calls props onClose - */ - const triggerOnClose = () => { - onClose && onClose(false); - }; - /** * Returns content for this step */ @@ -80,9 +77,9 @@ const Step6 = (props) => { - {strings('onboarding_wizard_new.step6.content1')} + {strings('onboarding_wizard_new.step7.content1')} ); @@ -93,36 +90,17 @@ const Step6 = (props) => { ); }; -const mapDispatchToProps = (dispatch) => ({ - setOnboardingWizardStep: (step) => dispatch(setOnboardingWizardStep(step)), -}); - -Step6.propTypes = { - /** - * Object that represents the navigator - */ - navigation: PropTypes.object, - /** - * Dispatch set onboarding wizard step - */ - setOnboardingWizardStep: PropTypes.func, - /** - * Callback to call when closing - */ - onClose: PropTypes.func, -}; - -export default connect(null, mapDispatchToProps)(Step6); +export default Step7; diff --git a/app/components/UI/OnboardingWizard/__snapshots__/index.test.tsx.snap b/app/components/UI/OnboardingWizard/__snapshots__/index.test.tsx.snap index 06b4e2bbe31d..11c1595c83c3 100644 --- a/app/components/UI/OnboardingWizard/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/OnboardingWizard/__snapshots__/index.test.tsx.snap @@ -1,37 +1,120 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`OnboardingWizard should render correctly 1`] = ` - - - + + + + + `; diff --git a/app/components/UI/OnboardingWizard/index.test.tsx b/app/components/UI/OnboardingWizard/index.test.tsx index 70630e4c1dae..d80af61b9f8b 100644 --- a/app/components/UI/OnboardingWizard/index.test.tsx +++ b/app/components/UI/OnboardingWizard/index.test.tsx @@ -1,27 +1,28 @@ import React from 'react'; + import OnboardingWizard from './'; -import { shallow } from 'enzyme'; -import configureMockStore from 'redux-mock-store'; -import { Provider } from 'react-redux'; +import renderWithProvider from '../../../util/test/renderWithProvider'; -const mockStore = configureMockStore(); -const initialState = { - wizard: { - step: 1, - }, - security: { - isAutomaticSecurityChecksModalOpen: false, +const navigationMock = { + navigate: jest.fn(), +}; + +const coachmarkRef = { + yourAccountRef: { + current: { + measure: jest.fn(), + }, }, }; -const store = mockStore(initialState); describe('OnboardingWizard', () => { it('should render correctly', () => { - const wrapper = shallow( - - - , + const { toJSON } = renderWithProvider( + , ); - expect(wrapper).toMatchSnapshot(); + expect(toJSON()).toMatchSnapshot(); }); }); diff --git a/app/components/UI/OnboardingWizard/index.js b/app/components/UI/OnboardingWizard/index.tsx similarity index 68% rename from app/components/UI/OnboardingWizard/index.js rename to app/components/UI/OnboardingWizard/index.tsx index 97dcd2a84422..48fecaec0ca3 100644 --- a/app/components/UI/OnboardingWizard/index.js +++ b/app/components/UI/OnboardingWizard/index.tsx @@ -1,30 +1,33 @@ import React, { useContext } from 'react'; -import PropTypes from 'prop-types'; -import { View, StyleSheet } from 'react-native'; +import { View, StyleSheet, TextStyle } from 'react-native'; +import { useDispatch, useSelector } from 'react-redux'; +import DefaultPreference from 'react-native-default-preference'; +import Modal from 'react-native-modal'; +import type { Theme } from '@metamask/design-tokens'; +import { DrawerContext } from '../../../components/Nav/Main/MainNavigator'; import { colors as importedColors } from '../../../styles/common'; -import { connect } from 'react-redux'; + import Step1 from './Step1'; import Step2 from './Step2'; import Step3 from './Step3'; import Step4 from './Step4'; import Step5 from './Step5'; import Step6 from './Step6'; +import Step7 from './Step7'; import setOnboardingWizardStep from '../../../actions/wizard'; -import DefaultPreference from 'react-native-default-preference'; -import Modal from 'react-native-modal'; +import Routes from '../../../constants/navigation/Routes'; import { ONBOARDING_WIZARD, EXPLORED } from '../../../constants/storage'; import { MetaMetricsEvents, ONBOARDING_WIZARD_STEP_DESCRIPTION, } from '../../../core/Analytics'; -import { DrawerContext } from '../../../components/Nav/Main/MainNavigator'; import { useTheme } from '../../../util/theme'; import Device from '../../../util/device'; import AsyncStorageWrapper from '../../../store/async-storage-wrapper'; import { isTest } from '../../../util/test/utils'; -import { useMetrics } from '../../../components/hooks/useMetrics'; +import { useMetrics } from '../../hooks/useMetrics'; -const createStyles = ({ colors, typography }) => +const createStyles = ({ colors, typography }: Theme) => StyleSheet.create({ root: { top: 0, @@ -62,30 +65,38 @@ const createStyles = ({ colors, typography }) => justifyContent: 'center', }, skipText: { - ...typography.BodyMD, + ...typography.sBodyMD, color: colors.primary.default, - }, + } as TextStyle, }); -const OnboardingWizard = (props) => { - const { - setOnboardingWizardStep, - navigation, - wizard: { step }, - coachmarkRef, - isAutomaticSecurityChecksModalOpen, - } = props; +interface OnboardingWizardProps { + navigation: any; + coachmarkRef: React.RefObject | null; +} + +const OnboardingWizard = ({ + navigation, + coachmarkRef, +}: OnboardingWizardProps) => { const { drawerRef } = useContext(DrawerContext); const theme = useTheme(); + const dispatch = useDispatch(); const { trackEvent } = useMetrics(); const styles = createStyles(theme); + const isAutomaticSecurityChecksModalOpen = useSelector( + (state: any) => state.security.isAutomaticSecurityChecksModalOpen, + ); + + const { step } = useSelector((state: any) => state.wizard); + /** * Close onboarding wizard setting step to 0 and closing drawer */ const closeOnboardingWizard = async () => { await DefaultPreference.set(ONBOARDING_WIZARD, EXPLORED); - setOnboardingWizardStep && setOnboardingWizardStep(0); + dispatch(setOnboardingWizardStep(0)); drawerRef?.current?.dismissDrawer?.(); trackEvent(MetaMetricsEvents.ONBOARDING_TOUR_SKIPPED, { tutorial_step_count: step, @@ -107,34 +118,29 @@ const OnboardingWizard = (props) => { inTestCloseOnboardingWizard(); } - const onboardingWizardNavigator = (step) => { - const steps = { + const onboardingWizardNavigator = (s: number) => { + const steps: Record = { 1: , 2: , 3: , - 4: , - 5: ( - - ), + 4: , + 5: , 6: , + 7: , }; - return steps[step]; + return steps[s]; }; const getBackButtonBehavior = () => { if (step === 1) { return closeOnboardingWizard(); - } else if (step === 5) { - setOnboardingWizardStep(4); - navigation.navigate('WalletView'); - drawerRef?.current?.dismissDrawer?.(); } else if (step === 6) { + dispatch(setOnboardingWizardStep(5)); + navigation.navigate(Routes.WALLET.HOME); + drawerRef?.current?.dismissDrawer?.(); + } else if (step === 7) { drawerRef?.current?.showDrawer?.(); - setOnboardingWizardStep(5); + dispatch(setOnboardingWizardStep(6)); } return setOnboardingWizardStep(step - 1); }; @@ -149,8 +155,6 @@ const OnboardingWizard = (props) => { animationOut={{ from: { opacity: 0 }, to: { opacity: 0 } }} isVisible backdropOpacity={0} - disableAnimation - transparent onBackButtonPress={getBackButtonBehavior} style={styles.root} > @@ -159,37 +163,4 @@ const OnboardingWizard = (props) => { ); }; -const mapDispatchToProps = (dispatch) => ({ - setOnboardingWizardStep: (step) => dispatch(setOnboardingWizardStep(step)), -}); - -const mapStateToProps = (state) => ({ - wizard: state.wizard, - isAutomaticSecurityChecksModalOpen: - state.security.isAutomaticSecurityChecksModalOpen, -}); - -OnboardingWizard.propTypes = { - /** - * Object that represents the navigator - */ - navigation: PropTypes.object, - /** - * Wizard state - */ - wizard: PropTypes.object, - /** - * Dispatch set onboarding wizard step - */ - setOnboardingWizardStep: PropTypes.func, - /** - * Coachmark ref to get position - */ - coachmarkRef: PropTypes.object, - /** - * Boolean that determines if the user has selected the automatic security check option - */ - isAutomaticSecurityChecksModalOpen: PropTypes.bool, -}; - -export default connect(mapStateToProps, mapDispatchToProps)(OnboardingWizard); +export default OnboardingWizard; diff --git a/app/components/UI/OnboardingWizard/styles.js b/app/components/UI/OnboardingWizard/styles.ts similarity index 95% rename from app/components/UI/OnboardingWizard/styles.js rename to app/components/UI/OnboardingWizard/styles.ts index 105462cfb36e..3d504c5f7e12 100644 --- a/app/components/UI/OnboardingWizard/styles.js +++ b/app/components/UI/OnboardingWizard/styles.ts @@ -4,7 +4,7 @@ import Device from '../../../util/device'; const SMALL_DEVICE = Device.isSmallDevice(); -export default (colors) => +export default (colors: any) => StyleSheet.create({ container: { flex: 1, diff --git a/app/components/UI/OnboardingWizard/useHandleLayout.tsx b/app/components/UI/OnboardingWizard/useHandleLayout.tsx new file mode 100644 index 000000000000..44aa367e3e94 --- /dev/null +++ b/app/components/UI/OnboardingWizard/useHandleLayout.tsx @@ -0,0 +1,34 @@ +import { useCallback, useEffect, useState } from 'react'; + +const useHandleLayout = (coachmarkRef: any) => { + const [coachmarkTop, setCoachmarkTop] = useState(0); + + const handleLayout = useCallback(() => { + const yourAccRef = coachmarkRef.yourAccountRef?.current; + if (!yourAccRef) return; + + yourAccRef.measure( + ( + _accActionsFx: number, + _accActionsFy: number, + _accActionsWidth: number, + accActionsHeight: number, + _accActionsPageX: number, + accActionsPageY: number, + ) => { + const top = accActionsHeight + accActionsPageY; + setCoachmarkTop(top); + }, + ); + }, [coachmarkRef.yourAccountRef]); + + useEffect(() => { + handleLayout(); + }, [handleLayout]); + + return { + coachmarkTop, + }; +}; + +export default useHandleLayout; diff --git a/app/components/Views/BrowserTab/index.js b/app/components/Views/BrowserTab/index.js index be96aabb413a..1324225e9605 100644 --- a/app/components/Views/BrowserTab/index.js +++ b/app/components/Views/BrowserTab/index.js @@ -1424,7 +1424,7 @@ export const BrowserTab = (props) => { */ const renderOnboardingWizard = () => { const { wizardStep } = props; - if ([6].includes(wizardStep)) { + if ([7].includes(wizardStep)) { if (!wizardScrollAdjusted.current) { setTimeout(() => { reload(); diff --git a/app/components/Views/Wallet/index.tsx b/app/components/Views/Wallet/index.tsx index 1f5c994c555a..1bbaa2813e52 100644 --- a/app/components/Views/Wallet/index.tsx +++ b/app/components/Views/Wallet/index.tsx @@ -418,7 +418,7 @@ const Wallet = ({ navigation }: any) => { */ const renderOnboardingWizard = useCallback( () => - [1, 2, 3].includes(wizardStep) && ( + [1, 2, 3, 4, 5, 6, 7].includes(wizardStep) && ( { // await WalletView.editAccountName(ACCOUNT); await OnboardingWizardModal.tapGotItButton(); await WalletView.isAccountNameCorrect(ACCOUNT); + // Ensure step 3 is shown correctly + await OnboardingWizardModal.isNotificationsTutorialStepVisible(); + await OnboardingWizardModal.tapGotItButton(); // Ensure step 4 is shown correctly await Assertions.checkIfVisible(OnboardingWizardModal.stepFourContainer); await OnboardingWizardModal.tapGotItButton(); diff --git a/wdio/screen-objects/Modals/OnboardingWizardModal.js b/wdio/screen-objects/Modals/OnboardingWizardModal.js index 42588ee0a9dd..83baed7a930d 100644 --- a/wdio/screen-objects/Modals/OnboardingWizardModal.js +++ b/wdio/screen-objects/Modals/OnboardingWizardModal.js @@ -9,6 +9,7 @@ import { ONBOARDING_WIZARD_STEP_1_NO_THANKS_ID, ONBOARDING_WIZARD_STEP_1_TAKE_THE_TOUR_ID, ONBOARDING_WIZARD_THIRD_STEP_CONTENT_ID, + ONBOARDING_WIZARD_SEVENTH_STEP_CONTENT_ID, } from '../testIDs/Components/OnboardingWizard.testIds'; import Selectors from '../../helpers/Selectors'; @@ -67,6 +68,12 @@ class OnboardingWizardModal { ); } + get step7Description() { + return Selectors.getElementByPlatform( + ONBOARDING_WIZARD_SEVENTH_STEP_CONTENT_ID, + ); + } + get onBoardingWizardGotItButton() { return Selectors.getElementByPlatform(ONBOARDING_WIZARD_NEXT_GOT_IT_BUTTON); } @@ -107,6 +114,10 @@ class OnboardingWizardModal { await expect(await this.step6Description).toBeDisplayed(); } + async isStep7ContentDisplayed() { + await expect(await this.step7Description).toBeDisplayed(); + } + async tapGotItButton() { await Gestures.tap(this.onBoardingWizardGotItButton); } diff --git a/wdio/screen-objects/testIDs/Components/OnboardingWizard.testIds.js b/wdio/screen-objects/testIDs/Components/OnboardingWizard.testIds.js index 51bcfe01ecd5..5bf086627cb2 100644 --- a/wdio/screen-objects/testIDs/Components/OnboardingWizard.testIds.js +++ b/wdio/screen-objects/testIDs/Components/OnboardingWizard.testIds.js @@ -26,3 +26,6 @@ export const ONBOARDING_WIZARD_FIFTH_STEP_CONTENT_ID = export const ONBOARDING_WIZARD_SIXTH_STEP_CONTENT_ID = 'onboarding-wizard-step6-content'; + + export const ONBOARDING_WIZARD_SEVENTH_STEP_CONTENT_ID = + 'onboarding-wizard-step7-content';