From 15ad0b21c75e3b363d300a70541776718afa95e9 Mon Sep 17 00:00:00 2001 From: Ariella Vu <20778143+digiwand@users.noreply.github.com> Date: Fri, 19 Apr 2024 20:40:01 +0400 Subject: [PATCH] feat: add Signature Redesign "Account Details Opened" event (#24095) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Adds "Account Details Opened" event when Account Info Icon in Confirmation Redesign is clicked Internal Thread https://consensys.slack.com/archives/C03ETQA9EPK/p1710437328642929 Event Description https://www.notion.so/Account-Details-Opened-3deec398d4784bcea00b012cdf1cde03?pvs=4 [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/24095?quickstart=1) ## **Related issues** Fixes: https://github.com/MetaMask/metamask-extension/issues/23555 ## **Manual testing steps** 1. set ` ENABLE_CONFIRMATION_REDESIGN=true` in .metamaskrc 2. Turn on MetaMetrics in settings 3. Use test-dapp to test feature in a signature confirmation ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I’ve followed [MetaMask Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] 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. --- shared/constants/metametrics.ts | 3 +- .../menu-items/account-details-menu-item.js | 2 +- .../confirm/header/header-info.test.tsx | 77 ++++++++++++------- .../components/confirm/header/header-info.tsx | 34 +++++++- 4 files changed, 85 insertions(+), 31 deletions(-) diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 9017381f7419..f4a012cf0127 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -499,6 +499,7 @@ export enum MetaMetricsEventName { AccountAdded = 'Account Added', AccountAddSelected = 'Account Add Selected', AccountAddFailed = 'Account Add Failed', + AccountDetailsOpened = 'Account Details Opened', AccountPasswordCreated = 'Account Password Created', AccountReset = 'Account Reset', AccountRenamed = 'Account Renamed', @@ -539,7 +540,6 @@ export enum MetaMetricsEventName { MetricsOptIn = 'Metrics Opt In', MetricsOptOut = 'Metrics Opt Out', NavAccountMenuOpened = 'Account Menu Opened', - NavAccountDetailsOpened = 'Account Details Opened', NavConnectedSitesOpened = 'Connected Sites Opened', NavMainMenuOpened = 'Main Menu Opened', NavPermissionsOpened = 'Permissions Opened', @@ -784,6 +784,7 @@ export enum MetaMetricsTransactionEventSource { } export enum MetaMetricsEventLocation { + SignatureConfirmation = 'signature_confirmation', TokenDetails = 'token_details', TokenDetection = 'token_detection', TokenMenu = 'token_menu', diff --git a/ui/components/multichain/menu-items/account-details-menu-item.js b/ui/components/multichain/menu-items/account-details-menu-item.js index 3a2af09d1536..4cd0ae165c9c 100644 --- a/ui/components/multichain/menu-items/account-details-menu-item.js +++ b/ui/components/multichain/menu-items/account-details-menu-item.js @@ -30,7 +30,7 @@ export const AccountDetailsMenuItem = ({ onClick={() => { dispatch(setAccountDetailsAddress(address)); trackEvent({ - event: MetaMetricsEventName.NavAccountDetailsOpened, + event: MetaMetricsEventName.AccountDetailsOpened, category: MetaMetricsEventCategory.Navigation, properties: { location: metricsLocation, diff --git a/ui/pages/confirmations/components/confirm/header/header-info.test.tsx b/ui/pages/confirmations/components/confirm/header/header-info.test.tsx index d88d298c66bc..ef43b98c69a0 100644 --- a/ui/pages/confirmations/components/confirm/header/header-info.test.tsx +++ b/ui/pages/confirmations/components/confirm/header/header-info.test.tsx @@ -3,24 +3,33 @@ import React from 'react'; import { fireEvent, waitFor } from '@testing-library/react'; import mockState from '../../../../../../test/data/mock-state.json'; import { renderWithProvider } from '../../../../../../test/jest'; +import { MetaMetricsContext } from '../../../../../contexts/metametrics'; import configureStore from '../../../../../store/store'; +import { + MetaMetricsEventCategory, + MetaMetricsEventLocation, + MetaMetricsEventName, +} from '../../../../../../shared/constants/metametrics'; + import HeaderInfo from './header-info'; -const render = () => { - const store = configureStore({ - metamask: { - ...mockState.metamask, - }, - confirm: { - currentConfirmation: { - msgParams: { - from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', - }, +const mockStore = { + metamask: { + ...mockState.metamask, + }, + confirm: { + currentConfirmation: { + msgParams: { + from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', }, + type: 'eth_signTypedData_v4', }, - }); + }, +}; +const render = () => { + const store = configureStore(mockStore); return renderWithProvider(, store); }; @@ -33,22 +42,38 @@ describe('Header', () => { const { getByLabelText } = render(); expect(getByLabelText('Account details')).toBeInTheDocument(); }); - it('shows modal when account info icon is clicked', async () => { - const { getByLabelText, queryByTestId } = render(); - expect(queryByTestId('account-details-modal')).not.toBeInTheDocument(); - const accountInfoIcon = getByLabelText('Account details'); - fireEvent.click(accountInfoIcon); - await waitFor(() => { - expect(queryByTestId('account-details-modal')).toBeInTheDocument(); + + describe('when account info icon is clicked', () => { + it('shows account info modal with address', async () => { + const { getByLabelText, getByText, queryByTestId } = render(); + const accountInfoIcon = getByLabelText('Account details'); + fireEvent.click(accountInfoIcon); + await waitFor(() => { + expect(queryByTestId('account-details-modal')).toBeInTheDocument(); + expect(getByText('0x0DCD5...3E7bc')).toBeInTheDocument(); + }); }); - }); - it('shows account info modal with address', async () => { - const { getByLabelText, getByText, queryByTestId } = render(); - const accountInfoIcon = getByLabelText('Account details'); - fireEvent.click(accountInfoIcon); - await waitFor(() => { - expect(queryByTestId('account-details-modal')).toBeInTheDocument(); - expect(getByText('0x0DCD5...3E7bc')).toBeInTheDocument(); + + it(`sends "${MetaMetricsEventName.AccountDetailsOpened}" metametric`, () => { + const mockTrackEvent = jest.fn(); + const { getByLabelText } = renderWithProvider( + + + , + configureStore(mockStore), + ); + const accountInfoIcon = getByLabelText('Account details'); + fireEvent.click(accountInfoIcon); + + expect(mockTrackEvent).toHaveBeenNthCalledWith(1, { + category: MetaMetricsEventCategory.Transactions, + event: MetaMetricsEventName.AccountDetailsOpened, + properties: { + action: 'Confirm Screen', + location: MetaMetricsEventLocation.SignatureConfirmation, + signature_type: 'eth_signTypedData_v4', + }, + }); }); }); }); diff --git a/ui/pages/confirmations/components/confirm/header/header-info.tsx b/ui/pages/confirmations/components/confirm/header/header-info.tsx index 3b8fba52e01f..67a6867a0089 100644 --- a/ui/pages/confirmations/components/confirm/header/header-info.tsx +++ b/ui/pages/confirmations/components/confirm/header/header-info.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { useSelector } from 'react-redux'; import { AvatarAccount, @@ -27,20 +27,45 @@ import { AddressCopyButton } from '../../../../../components/multichain'; import Tooltip from '../../../../../components/ui/tooltip/tooltip'; import { useI18nContext } from '../../../../../hooks/useI18nContext'; import useConfirmationRecipientInfo from '../../../hooks/useConfirmationRecipientInfo'; -import { getUseBlockie } from '../../../../../selectors'; + +import { + currentConfirmationSelector, + getUseBlockie, +} from '../../../../../selectors'; +import { + MetaMetricsEventCategory, + MetaMetricsEventLocation, + MetaMetricsEventName, +} from '../../../../../../shared/constants/metametrics'; +import { MetaMetricsContext } from '../../../../../contexts/metametrics'; import { ConfirmInfoRowCurrency } from '../../../../../components/app/confirm/info/row/currency'; import { useBalance } from '../../../hooks/useBalance'; const HeaderInfo = () => { const useBlockie = useSelector(getUseBlockie); const [showAccountInfo, setShowAccountInfo] = React.useState(false); + + const currentConfirmation = useSelector(currentConfirmationSelector); const { recipientAddress: fromAddress, recipientName: fromName } = useConfirmationRecipientInfo(); const t = useI18nContext(); + const trackEvent = useContext(MetaMetricsContext); const { balance: balanceToUse } = useBalance(fromAddress); + function trackAccountModalOpened() { + trackEvent({ + category: MetaMetricsEventCategory.Transactions, + event: MetaMetricsEventName.AccountDetailsOpened, + properties: { + action: 'Confirm Screen', + location: MetaMetricsEventLocation.SignatureConfirmation, + signature_type: currentConfirmation?.type, + }, + }); + } + return ( <> { ariaLabel={t('accountDetails')} iconName={IconName.Info} size={ButtonIconSize.Md} - onClick={() => setShowAccountInfo(true)} + onClick={() => { + trackAccountModalOpened(); + setShowAccountInfo(true); + }} />