diff --git a/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap b/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap index c6fa6d08a..86163faaf 100644 --- a/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap +++ b/ui/components/multichain/app-header/__snapshots__/app-header.test.js.snap @@ -3,15 +3,213 @@ exports[`App Header should match snapshot 1`] = `
+ +
+
+
+
+ + Test Account + + + +
+
+ +
+ +
+
+
diff --git a/ui/components/multichain/app-header/app-header.js b/ui/components/multichain/app-header/app-header.js index 081ed2a1b..bf530f3b9 100644 --- a/ui/components/multichain/app-header/app-header.js +++ b/ui/components/multichain/app-header/app-header.js @@ -105,17 +105,17 @@ export const AppHeader = ({ location }) => { // Disable the network and account pickers if the user is in // a critical flow const sendStage = useSelector(getSendStage); + const isTransactionEditPage = [ + SEND_STAGES.EDIT, + SEND_STAGES.DRAFT, + SEND_STAGES.ADD_RECIPIENT, + ].includes(sendStage); const isConfirmationPage = Boolean( matchPath(location.pathname, { path: CONFIRM_TRANSACTION_ROUTE, exact: false, }), ); - const isTransactionEditPage = [ - SEND_STAGES.EDIT, - SEND_STAGES.DRAFT, - SEND_STAGES.ADD_RECIPIENT, - ].includes(sendStage); const isSwapsPage = Boolean( matchPath(location.pathname, { path: SWAPS_ROUTE, exact: false }), ); @@ -123,11 +123,18 @@ export const AppHeader = ({ location }) => { matchPath(location.pathname, { path: BUILD_QUOTE_ROUTE, exact: false }), ); - const disablePickers = - isConfirmationPage || + const hasUnapprovedTransactions = useSelector( + (state) => Object.keys(state.metamask.unapprovedTxs).length > 0, + ); + + const disableAccountPicker = + isConfirmationPage || (isSwapsPage && !isSwapsBuildQuotePage); + + const disableNetworkPicker = + isSwapsPage || isTransactionEditPage || - (isSwapsPage && !isSwapsBuildQuotePage); - const disableNetworkPicker = isSwapsPage || disablePickers; + isConfirmationPage || + hasUnapprovedTransactions; // Callback for network dropdown const networkOpenCallback = useCallback(() => { @@ -261,7 +268,7 @@ export const AppHeader = ({ location }) => { }, }); }} - disabled={disablePickers} + disabled={disableAccountPicker} /> ) : null} { + const store = configureStore({ + ...mockState, + activeTab: { + origin: 'https://remix.ethereum.org', + }, + ...stateChanges, + }); + return renderWithProvider(, store); +}; + describe('App Header', () => { it('should match snapshot', () => { - const mockState = { - activeTab: { - title: 'Eth Sign Tests', - origin: 'https://remix.ethereum.org', - protocol: 'https:', - url: 'https://remix.ethereum.org/', - }, - metamask: { - providerConfig: { - chainId: CHAIN_IDS.GOERLI, - }, - accounts: { - '0x7250739de134d33ec7ab1ee592711e15098c9d2d': { - address: '0x7250739de134d33ec7ab1ee592711e15098c9d2d', - }, - '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5': { - address: '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', - }, - }, - preferences: { - showTestNetworks: true, - }, - selectedAddress: '0x7250739de134d33ec7ab1ee592711e15098c9d2d', - cachedBalances: {}, - subjects: { - 'https://remix.ethereum.org': { - permissions: { - eth_accounts: { - caveats: [ - { - type: 'restrictReturnedAccounts', - value: [ - '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', - '0x7250739de134d33ec7ab1ee592711e15098c9d2d', - ], - }, - ], - date: 1586359844177, - id: '3aa65a8b-3bcb-4944-941b-1baa5fe0ed8b', - invoker: 'https://remix.ethereum.org', - parentCapability: 'eth_accounts', - }, - }, - }, - 'peepeth.com': { - permissions: { - eth_accounts: { - caveats: [ - { - type: 'restrictReturnedAccounts', - value: ['0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5'], - }, - ], - date: 1585676177970, - id: '840d72a0-925f-449f-830a-1aa1dd5ce151', - invoker: 'peepeth.com', - parentCapability: 'eth_accounts', - }, - }, - }, - }, - identities: { - '0x7250739de134d33ec7ab1ee592711e15098c9d2d': { - address: '0x7250739de134d33ec7ab1ee592711e15098c9d2d', - name: 'Really Long Name That Should Be Truncated', - }, - '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5': { - address: '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', - lastSelected: 1586359844192, - name: 'Account 1', - }, - }, - keyrings: [ - { - accounts: [ - '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', - '0x7250739de134d33ec7ab1ee592711e15098c9d2d', - ], - }, - ], - permissionHistory: { - 'https://remix.ethereum.org': { - eth_accounts: { - accounts: { - '0x7250739de134d33ec7ab1ee592711e15098c9d2d': 1586359844192, - '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5': 1586359844192, - }, - lastApproved: 1586359844192, - }, - }, - }, - }, - appState: { - onboardedInThisUISession: false, - }, - send: { - stage: SEND_STAGES.INACTIVE, - }, - }; - - const mockStore = configureStore(); - const store = mockStore(mockState); - const { container } = renderWithProvider( - , - store, - ); + const { container } = render(); expect(container).toMatchSnapshot(); }); + + it('should disable the network picker during a send', () => { + const { getByTestId } = render({ send: { stage: SEND_STAGES.DRAFT } }); + expect(getByTestId('network-display')).toBeDisabled(); + }); + + it('should allow switching accounts during a send', () => { + const { getByTestId } = render({ send: { stage: SEND_STAGES.DRAFT } }); + expect(getByTestId('account-menu-icon')).toBeEnabled(); + }); }); diff --git a/ui/components/multichain/global-menu/global-menu.js b/ui/components/multichain/global-menu/global-menu.js index 57abdbb36..978ec4a1c 100644 --- a/ui/components/multichain/global-menu/global-menu.js +++ b/ui/components/multichain/global-menu/global-menu.js @@ -58,6 +58,10 @@ export const GlobalMenu = ({ closeMenu, anchorElement }) => { const history = useHistory(); const metaMetricsId = useSelector(getMetaMetricsId); + const hasUnapprovedTransactions = useSelector( + (state) => Object.keys(state.metamask.unapprovedTxs).length > 0, + ); + ///: BEGIN:ONLY_INCLUDE_IN(snaps) const unreadNotificationsCount = useSelector(getUnreadNotificationsCount); ///: END:ONLY_INCLUDE_IN @@ -199,6 +203,7 @@ export const GlobalMenu = ({ closeMenu, anchorElement }) => { { history.push(SETTINGS_ROUTE); trackEvent({ @@ -210,6 +215,7 @@ export const GlobalMenu = ({ closeMenu, anchorElement }) => { }); closeMenu(); }} + data-testid="global-menu-settings" > {t('settings')} diff --git a/ui/components/multichain/global-menu/global-menu.test.js b/ui/components/multichain/global-menu/global-menu.test.js index 06fb33800..0f996a0ab 100644 --- a/ui/components/multichain/global-menu/global-menu.test.js +++ b/ui/components/multichain/global-menu/global-menu.test.js @@ -4,10 +4,11 @@ import configureStore from '../../../store/store'; import mockState from '../../../../test/data/mock-state.json'; import { GlobalMenu } from '.'; -const render = () => { +const render = (metamaskStateChanges = {}) => { const store = configureStore({ metamask: { ...mockState.metamask, + ...metamaskStateChanges, }, }); return renderWithProvider( @@ -52,6 +53,20 @@ describe('AccountListItem', () => { }); }); + it('disables the settings item when there is an active transaction', async () => { + const { getByTestId } = render(); + await waitFor(() => { + expect(getByTestId('global-menu-settings')).toBeDisabled(); + }); + }); + + it('enables the settings item when there is no active transaction', async () => { + const { getByTestId } = render({ unapprovedTxs: {} }); + await waitFor(() => { + expect(getByTestId('global-menu-settings')).toBeEnabled(); + }); + }); + it('expands metamask to tab when item is clicked', async () => { global.platform = { openExtensionInBrowser: jest.fn() }; diff --git a/ui/components/ui/menu/menu-item.js b/ui/components/ui/menu/menu-item.js index 6e005e1ea..9e2913d12 100644 --- a/ui/components/ui/menu/menu-item.js +++ b/ui/components/ui/menu/menu-item.js @@ -12,11 +12,13 @@ const MenuItem = ({ iconName, onClick, subtitle, + disabled = false, }) => (