mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
[MMI] Created custody-confirm-link-modal component (#18632)
* Created custody-confirm-link component * Removed selector * Finished component * Changed button to use design system * Fixed eslint issue * updated snapshot * fixed eslint problems * Added more tests and added color design systems * Fixed comments * Removed unnecessary code * Removed unneeded code and fixed eslint issues
This commit is contained in:
parent
cbe438e704
commit
4df363b251
6
app/_locales/en/messages.json
generated
6
app/_locales/en/messages.json
generated
@ -466,6 +466,9 @@
|
||||
"average": {
|
||||
"message": "Average"
|
||||
},
|
||||
"awaitingApproval": {
|
||||
"message": "Awaiting approval..."
|
||||
},
|
||||
"back": {
|
||||
"message": "Back"
|
||||
},
|
||||
@ -932,6 +935,9 @@
|
||||
"custodianAccount": {
|
||||
"message": "Custodian account"
|
||||
},
|
||||
"custodyDeeplinkDescription": {
|
||||
"message": "Approve the transaction in the $1 app. Once all required custody approvals have been performed the transaction will complete. Check your $1 app for status."
|
||||
},
|
||||
"custodyRefreshTokenModalDescription": {
|
||||
"message": "Please go to $1 and click the 'Connect to MMI' button within their user interface to connect your accounts to MMI again."
|
||||
},
|
||||
|
@ -538,6 +538,9 @@ export enum MetaMetricsEventName {
|
||||
OnboardingWalletVideoPlay = 'SRP Intro Video Played',
|
||||
OnboardingTwitterClick = 'External Link Clicked',
|
||||
ServiceWorkerRestarted = 'Service Worker Restarted',
|
||||
///: BEGIN:ONLY_INCLUDE_IN(mmi)
|
||||
UserClickedDeepLink = 'User clicked deeplink',
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
|
||||
export enum MetaMetricsEventAccountType {
|
||||
@ -577,6 +580,9 @@ export enum MetaMetricsEventCategory {
|
||||
Wallet = 'Wallet',
|
||||
Desktop = 'Desktop',
|
||||
ServiceWorkers = 'service_workers',
|
||||
///: BEGIN:ONLY_INCLUDE_IN(mmi)
|
||||
MMI = 'Institutional',
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
|
||||
export enum MetaMetricsEventLinkType {
|
||||
|
@ -105,5 +105,6 @@
|
||||
@import 'approve-content-card/index';
|
||||
@import 'transaction-alerts/transaction-alerts';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(mmi)
|
||||
@import '../institutional/custody-confirm-link-modal/index';
|
||||
@import '../institutional/transaction-failed-modal/index';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
@ -0,0 +1,40 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Custody Confirm Link should match snapshot 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="box box--display-flex box--flex-direction-column"
|
||||
>
|
||||
<div
|
||||
class="box box--padding-top-5 box--display-flex box--flex-direction-row box--justify-content-center box--align-items-center"
|
||||
>
|
||||
<img
|
||||
alt="MMI logo"
|
||||
class="custody-confirm-link__img"
|
||||
src="/images/logo/mmi-logo.svg"
|
||||
/>
|
||||
>
|
||||
<img
|
||||
alt="saturn-dev"
|
||||
class="custody-confirm-link__img"
|
||||
src="https://saturn-custody-ui.dev.metamask-institutional.io/saturn.svg"
|
||||
/>
|
||||
</div>
|
||||
<h4
|
||||
class="box mm-text mm-text--heading-lg mm-text--text-align-center box--padding-top-4 box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
Awaiting approval...
|
||||
</h4>
|
||||
<p
|
||||
class="box mm-text custody-confirm-link__description mm-text--body-sm mm-text--text-align-center box--padding-top-4 box--padding-right-5 box--padding-bottom-10 box--padding-left-5 box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
Approve the transaction in the Saturn Custody app. Once all required custody approvals have been performed the transaction will complete. Check your Saturn Custody app for status.
|
||||
</p>
|
||||
<button
|
||||
class="box mm-text mm-button-base mm-button-base--size-md custody-confirm-link__btn mm-button-primary mm-text--body-md box--padding-right-4 box--padding-left-4 box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-inverse box--background-color-primary-default box--rounded-pill"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -0,0 +1,135 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { MetaMetricsContext } from '../../../contexts/metametrics';
|
||||
import {
|
||||
MetaMetricsEventName,
|
||||
MetaMetricsEventCategory,
|
||||
} from '../../../../shared/constants/metametrics';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import withModalProps from '../../../helpers/higher-order-components/with-modal-props';
|
||||
import { toChecksumHexAddress } from '../../../../shared/modules/hexstring-utils';
|
||||
import { mmiActionsFactory } from '../../../store/institutional/institution-background';
|
||||
import { hideModal, setSelectedAddress } from '../../../store/actions';
|
||||
import { getMetaMaskAccountsRaw } from '../../../selectors';
|
||||
import {
|
||||
getMMIAddressFromModalOrAddress,
|
||||
getCustodyAccountDetails,
|
||||
getMMIConfiguration,
|
||||
} from '../../../selectors/institutional/selectors';
|
||||
import Box from '../../ui/box/box';
|
||||
import {
|
||||
AlignItems,
|
||||
DISPLAY,
|
||||
FLEX_DIRECTION,
|
||||
FontWeight,
|
||||
JustifyContent,
|
||||
TextAlign,
|
||||
TextColor,
|
||||
TextVariant,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import { Text, Button } from '../../component-library';
|
||||
|
||||
const CustodyConfirmLink = () => {
|
||||
const t = useI18nContext();
|
||||
const dispatch = useDispatch();
|
||||
const mmiActions = mmiActionsFactory();
|
||||
const trackEvent = useContext(MetaMetricsContext);
|
||||
const mmiAccounts = useSelector(getMetaMaskAccountsRaw);
|
||||
const address = useSelector(getMMIAddressFromModalOrAddress);
|
||||
const custodyAccountDetails = useSelector(getCustodyAccountDetails);
|
||||
const { custodians } = useSelector(getMMIConfiguration);
|
||||
const { custodianName } =
|
||||
custodyAccountDetails[toChecksumHexAddress(address)] || {};
|
||||
const { displayName, iconUrl } = custodians.find(
|
||||
(item) => item.name === custodianName || {},
|
||||
);
|
||||
const { url, ethereum, text, action } = useSelector(
|
||||
(state) => state.appState.modal.modalState.props.link || {},
|
||||
);
|
||||
|
||||
const onClick = () => {
|
||||
if (url) {
|
||||
global.platform.openTab({ url });
|
||||
}
|
||||
|
||||
if (ethereum) {
|
||||
const ethAccount = Object.keys(mmiAccounts).find((account) =>
|
||||
ethereum.accounts.includes(account.toLowerCase()),
|
||||
);
|
||||
|
||||
ethAccount && dispatch(setSelectedAddress(ethAccount.toLowerCase()));
|
||||
}
|
||||
|
||||
trackEvent({
|
||||
category: MetaMetricsEventCategory.MMI,
|
||||
event: MetaMetricsEventName.UserClickedDeepLink,
|
||||
});
|
||||
dispatch(mmiActions.setWaitForConfirmDeepLinkDialog(false));
|
||||
dispatch(hideModal());
|
||||
};
|
||||
|
||||
return (
|
||||
<Box display={DISPLAY.FLEX} flexDirection={FLEX_DIRECTION.COLUMN}>
|
||||
{iconUrl ? (
|
||||
<Box
|
||||
display={DISPLAY.FLEX}
|
||||
alignItems={AlignItems.center}
|
||||
justifyContent={JustifyContent.center}
|
||||
paddingTop={5}
|
||||
>
|
||||
<img
|
||||
className="custody-confirm-link__img"
|
||||
src="/images/logo/mmi-logo.svg"
|
||||
alt="MMI logo"
|
||||
/>
|
||||
{'>'}
|
||||
<img
|
||||
className="custody-confirm-link__img"
|
||||
src={iconUrl}
|
||||
alt={custodianName}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Box
|
||||
display={DISPLAY.FLEX}
|
||||
alignItems={AlignItems.center}
|
||||
justifyContent={JustifyContent.center}
|
||||
paddingTop={5}
|
||||
>
|
||||
<span>{custodianName}</span>
|
||||
</Box>
|
||||
)}
|
||||
<Text
|
||||
as="h4"
|
||||
paddingTop={4}
|
||||
variant={TextVariant.headingLg}
|
||||
textAlign={TextAlign.Center}
|
||||
fontWeight={FontWeight.bold}
|
||||
>
|
||||
{t('awaitingApproval')}
|
||||
</Text>
|
||||
<Text
|
||||
as="p"
|
||||
paddingTop={4}
|
||||
paddingRight={5}
|
||||
paddingLeft={5}
|
||||
paddingBottom={10}
|
||||
textAlign={TextAlign.Center}
|
||||
color={TextColor.textDefault}
|
||||
variant={TextVariant.bodySm}
|
||||
className="custody-confirm-link__description"
|
||||
>
|
||||
{text || t('custodyDeeplinkDescription', [displayName])}
|
||||
</Text>
|
||||
<Button
|
||||
type="primary"
|
||||
className="custody-confirm-link__btn"
|
||||
onClick={onClick}
|
||||
>
|
||||
{action || (action ? t('openCustodianApp', [displayName]) : t('close'))}
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default withModalProps(CustodyConfirmLink);
|
@ -0,0 +1,73 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import configureStore from '../../../store/store';
|
||||
import testData from '../../../../.storybook/test-data';
|
||||
import CustodyConfirmLink from '.';
|
||||
|
||||
const customData = {
|
||||
...testData,
|
||||
appState: {
|
||||
...testData.appState,
|
||||
modal: {
|
||||
modalState: {
|
||||
props: {
|
||||
link: {
|
||||
url: 'test-url',
|
||||
ethereum: {
|
||||
accounts: [{}],
|
||||
},
|
||||
text: '',
|
||||
action: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
metamask: {
|
||||
...testData.metamask,
|
||||
mmiConfiguration: {
|
||||
custodians: [
|
||||
{
|
||||
refreshTokenUrl:
|
||||
'https://saturn-custody.dev.metamask-institutional.io/oauth/token',
|
||||
name: 'saturn-dev',
|
||||
displayName: 'Saturn Custody',
|
||||
enabled: true,
|
||||
mmiApiUrl: 'https://api.dev.metamask-institutional.io/v1',
|
||||
websocketApiUrl:
|
||||
'wss://websocket.dev.metamask-institutional.io/v1/ws',
|
||||
apiBaseUrl:
|
||||
'https://saturn-custody.dev.metamask-institutional.io/eth',
|
||||
iconUrl:
|
||||
'https://saturn-custody-ui.dev.metamask-institutional.io/saturn.svg',
|
||||
isNoteToTraderSupported: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
custodyAccountDetails: {
|
||||
'0xAddress': {
|
||||
address: '0xAddress',
|
||||
details: 'details',
|
||||
custodyType: 'testCustody - Saturn',
|
||||
custodianName: 'saturn-dev',
|
||||
},
|
||||
},
|
||||
provider: {
|
||||
type: 'test',
|
||||
},
|
||||
selectedAddress: '0xAddress',
|
||||
},
|
||||
};
|
||||
|
||||
const store = configureStore(customData);
|
||||
|
||||
export default {
|
||||
title: 'Components/Institutional/CustodyConfirmLink',
|
||||
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
|
||||
component: CustodyConfirmLink,
|
||||
args: {},
|
||||
};
|
||||
|
||||
export const DefaultStory = (args) => <CustodyConfirmLink {...args} />;
|
||||
|
||||
DefaultStory.storyName = 'CustodyConfirmLink';
|
@ -0,0 +1,192 @@
|
||||
import React from 'react';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||
import testData from '../../../../.storybook/test-data';
|
||||
import { hideModal } from '../../../store/actions';
|
||||
import CustodyConfirmLink from '.';
|
||||
|
||||
const mockedSetWaitForConfirmDeepLinkDialog = jest
|
||||
.fn()
|
||||
.mockReturnValue({ type: 'TYPE' });
|
||||
jest.mock('../../../store/institutional/institution-background', () => ({
|
||||
mmiActionsFactory: () => ({
|
||||
setWaitForConfirmDeepLinkDialog: mockedSetWaitForConfirmDeepLinkDialog,
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../../store/actions', () => ({
|
||||
hideModal: jest.fn().mockReturnValue({ type: 'TYPE' }),
|
||||
}));
|
||||
|
||||
const mockedCustodianName = 'saturn-dev';
|
||||
|
||||
describe('Custody Confirm Link', () => {
|
||||
const mockStore = {
|
||||
...testData,
|
||||
appState: {
|
||||
...testData.appState,
|
||||
modal: {
|
||||
modalState: {
|
||||
props: {
|
||||
link: {
|
||||
url: 'test-url',
|
||||
ethereum: {
|
||||
accounts: [{}],
|
||||
},
|
||||
text: '',
|
||||
action: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
metamask: {
|
||||
...testData.metamask,
|
||||
mmiConfiguration: {
|
||||
custodians: [
|
||||
{
|
||||
refreshTokenUrl:
|
||||
'https://saturn-custody.dev.metamask-institutional.io/oauth/token',
|
||||
name: 'saturn-dev',
|
||||
displayName: 'Saturn Custody',
|
||||
enabled: true,
|
||||
mmiApiUrl: 'https://api.dev.metamask-institutional.io/v1',
|
||||
websocketApiUrl:
|
||||
'wss://websocket.dev.metamask-institutional.io/v1/ws',
|
||||
apiBaseUrl:
|
||||
'https://saturn-custody.dev.metamask-institutional.io/eth',
|
||||
iconUrl:
|
||||
'https://saturn-custody-ui.dev.metamask-institutional.io/saturn.svg',
|
||||
isNoteToTraderSupported: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
custodyAccountDetails: {
|
||||
'0xAddress': {
|
||||
address: '0xAddress',
|
||||
details: 'details',
|
||||
custodyType: 'testCustody - Saturn',
|
||||
custodianName: mockedCustodianName,
|
||||
},
|
||||
},
|
||||
provider: {
|
||||
type: 'test',
|
||||
},
|
||||
selectedAddress: '0xAddress',
|
||||
},
|
||||
};
|
||||
|
||||
let store = configureStore()(mockStore);
|
||||
|
||||
it('tries to open new tab with deeplink URL', () => {
|
||||
global.platform = { openTab: jest.fn() };
|
||||
const { getByRole } = renderWithProvider(<CustodyConfirmLink />, store);
|
||||
fireEvent.click(getByRole('button'));
|
||||
expect(global.platform.openTab).toHaveBeenCalledWith({
|
||||
url: 'test-url',
|
||||
});
|
||||
expect(mockedSetWaitForConfirmDeepLinkDialog).toHaveBeenCalledWith(false);
|
||||
expect(hideModal).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should match snapshot', () => {
|
||||
const { container } = renderWithProvider(<CustodyConfirmLink />, store);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('shows custodian name when iconUrl is undefined', () => {
|
||||
const customMockStore = {
|
||||
...mockStore,
|
||||
metamask: {
|
||||
...testData.metamask,
|
||||
...mockStore.metamask,
|
||||
mmiConfiguration: {
|
||||
custodians: [
|
||||
{
|
||||
refreshTokenUrl:
|
||||
'https://saturn-custody.dev.metamask-institutional.io/oauth/token',
|
||||
name: 'saturn-dev',
|
||||
displayName: 'Saturn Custody',
|
||||
enabled: true,
|
||||
mmiApiUrl: 'https://api.dev.metamask-institutional.io/v1',
|
||||
websocketApiUrl:
|
||||
'wss://websocket.dev.metamask-institutional.io/v1/ws',
|
||||
apiBaseUrl:
|
||||
'https://saturn-custody.dev.metamask-institutional.io/eth',
|
||||
iconUrl: null,
|
||||
isNoteToTraderSupported: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
store = configureStore()(customMockStore);
|
||||
|
||||
const { getByText } = renderWithProvider(<CustodyConfirmLink />, store);
|
||||
|
||||
expect(getByText(mockedCustodianName)).toBeVisible();
|
||||
});
|
||||
|
||||
it('shows text that comes from modal state if defined', () => {
|
||||
const mockModalStateText = 'test modal state text';
|
||||
const customMockStore = {
|
||||
...mockStore,
|
||||
appState: {
|
||||
...testData.appState,
|
||||
modal: {
|
||||
modalState: {
|
||||
props: {
|
||||
link: {
|
||||
url: 'test-url',
|
||||
ethereum: {
|
||||
accounts: [{}],
|
||||
},
|
||||
text: mockModalStateText,
|
||||
action: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
store = configureStore()(customMockStore);
|
||||
|
||||
const { getByText } = renderWithProvider(<CustodyConfirmLink />, store);
|
||||
|
||||
expect(getByText(mockModalStateText)).toBeVisible();
|
||||
});
|
||||
|
||||
it('shows action text that comes from modal state if defined', () => {
|
||||
const mockModalStateActionText = 'test modal state action text';
|
||||
const customMockStore = {
|
||||
...mockStore,
|
||||
appState: {
|
||||
...testData.appState,
|
||||
modal: {
|
||||
modalState: {
|
||||
props: {
|
||||
link: {
|
||||
url: 'test-url',
|
||||
ethereum: {
|
||||
accounts: [{}],
|
||||
},
|
||||
text: '',
|
||||
action: mockModalStateActionText,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
store = configureStore()(customMockStore);
|
||||
|
||||
const { getByText } = renderWithProvider(<CustodyConfirmLink />, store);
|
||||
|
||||
expect(getByText(mockModalStateActionText)).toBeVisible();
|
||||
});
|
||||
});
|
@ -0,0 +1,3 @@
|
||||
import CustodyConfirmLink from './custody-confirm-link-modal';
|
||||
|
||||
export default CustodyConfirmLink;
|
@ -0,0 +1,18 @@
|
||||
.custody-confirm-link {
|
||||
&__description {
|
||||
border-bottom: 1px solid var(--brand-colors-grey-grey200);
|
||||
}
|
||||
|
||||
&__img {
|
||||
margin: 0 20px;
|
||||
display: block;
|
||||
padding: 20px 0;
|
||||
width: 45px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__btn {
|
||||
width: 300px !important;
|
||||
margin: 20px auto;
|
||||
}
|
||||
}
|
@ -63,6 +63,17 @@ export function getIsCustodianSupportedChain(state) {
|
||||
: true;
|
||||
}
|
||||
|
||||
export function getMMIAddressFromModalOrAddress(state) {
|
||||
return (
|
||||
state.appState.modal.modalState.props.address ||
|
||||
state.metamask.selectedAddress
|
||||
);
|
||||
}
|
||||
|
||||
export function getMMIConfiguration(state) {
|
||||
return state.metamask.mmiConfiguration;
|
||||
}
|
||||
|
||||
export function getInteractiveReplacementToken(state) {
|
||||
return state.metamask.interactiveReplacementToken || {};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user