mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
[MMI] Added confirm-remove-jwt component (#18186)
* Added confirm-remove-jwt component * changing folder directory * Fixed lint issues * Added story * Fixed confirm remove jwt imports * Fixed import * Finished implementing component * Fixed capitalize eslint problem * Fixed PR suggestions * Changed CustodyAccountList import * updated snapshot * Fixed typo * Moved folder to confirm-remove-jwt-modal * added index * Adding filter first
This commit is contained in:
parent
9d794e97b3
commit
49f01406c4
12
app/_locales/en/messages.json
generated
12
app/_locales/en/messages.json
generated
@ -103,9 +103,6 @@
|
||||
"SIWEWarningTitle": {
|
||||
"message": "Are you sure?"
|
||||
},
|
||||
"ShowMore": {
|
||||
"message": "Show more"
|
||||
},
|
||||
"about": {
|
||||
"message": "About"
|
||||
},
|
||||
@ -3278,6 +3275,12 @@
|
||||
"removeAccountDescription": {
|
||||
"message": "This account will be removed from your wallet. Please make sure you have the original Secret Recovery Phrase or private key for this imported account before continuing. You can import or create accounts again from the account drop-down. "
|
||||
},
|
||||
"removeJWT": {
|
||||
"message": "Remove custodian token"
|
||||
},
|
||||
"removeJWTDescription": {
|
||||
"message": "Are you sure you want to remove this token? All accounts assigned to this token will be removed from extension as well: "
|
||||
},
|
||||
"removeNFT": {
|
||||
"message": "Remove NFT"
|
||||
},
|
||||
@ -3661,6 +3664,9 @@
|
||||
"message": "This relies on $1 which will have access to your Ethereum address and your IP address. $2",
|
||||
"description": "$1 is the link to etherscan url and $2 is the link to the privacy policy of consensys APIs"
|
||||
},
|
||||
"showMore": {
|
||||
"message": "Show more"
|
||||
},
|
||||
"showPermissions": {
|
||||
"message": "Show permissions"
|
||||
},
|
||||
|
@ -106,6 +106,7 @@
|
||||
@import 'approve-content-card/index';
|
||||
@import 'transaction-alerts/transaction-alerts';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
@import '../institutional/confirm-remove-jwt-modal/index';
|
||||
@import '../institutional/custody-confirm-link-modal/index';
|
||||
@import '../institutional/transaction-failed-modal/index';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
@ -0,0 +1,151 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Confirm Remove JWT should render correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="modal-container"
|
||||
>
|
||||
<div
|
||||
class="modal-container__header"
|
||||
>
|
||||
<div
|
||||
class="modal-container__header-text"
|
||||
>
|
||||
Remove custodian token?
|
||||
</div>
|
||||
<div
|
||||
class="modal-container__header-close"
|
||||
data-testid="modal-header-close"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="modal-container__content"
|
||||
>
|
||||
<div
|
||||
class="box confirm-action-jwt__jwt box--padding-2 box--display-flex box--flex-direction-row box--rounded-sm"
|
||||
>
|
||||
...Dce07538D
|
||||
</div>
|
||||
<p
|
||||
class="box mm-text confirm-action-jwt__show-more mm-text--body-md box--margin-left-2 box--flex-direction-row box--color-goerli"
|
||||
>
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Show more
|
||||
</a>
|
||||
</p>
|
||||
<h6
|
||||
class="box mm-text mm-text--body-sm mm-text--text-align-center box--margin-top-2 box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
Are you sure you want to remove this token? All accounts assigned to this token will be removed from extension as well:
|
||||
</h6>
|
||||
<div
|
||||
class="box confirm-action-jwt__accounts-list box--flex-direction-row"
|
||||
>
|
||||
<div
|
||||
class="box box--padding-top-4 box--padding-right-7 box--padding-bottom-7 box--padding-left-7 box--flex-direction-row"
|
||||
>
|
||||
<div
|
||||
class="box custody-account-list box--display-flex box--flex-direction-column box--width-full"
|
||||
data-testid="custody-account-list"
|
||||
>
|
||||
<div
|
||||
class="box custody-account-list__item box--display-flex box--flex-direction-row"
|
||||
>
|
||||
<div
|
||||
class="box box--display-flex box--flex-direction-row box--align-items-flex-start"
|
||||
data-testid="custody-account-list-item-radio-button"
|
||||
/>
|
||||
<div
|
||||
class="box box--margin-left-2 box--display-flex box--flex-direction-column box--width-full"
|
||||
>
|
||||
<label
|
||||
class="box mm-text mm-label mm-label--html-for custody-account-list__item__title mm-text--body-md mm-text--font-weight-bold box--margin-top-2 box--margin-left-2 box--display-flex box--flex-direction-row box--align-items-center box--color-text-default"
|
||||
for="address-0"
|
||||
>
|
||||
<span
|
||||
class="box mm-text custody-account-list__item__name mm-text--inherit box--padding-right-1 box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
Test name account
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="box mm-text mm-label mm-label--html-for mm-text--body-md mm-text--font-weight-bold box--margin-top-2 box--margin-right-3 box--margin-left-2 box--display-flex box--flex-direction-row box--align-items-center box--color-text-default"
|
||||
for="address-0"
|
||||
>
|
||||
<span
|
||||
class="box mm-text custody-account-list__item mm-text--body-md box--display-flex box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
<a
|
||||
class="box mm-text mm-button-base mm-button-link mm-button-link--size-auto mm-text--body-md box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent"
|
||||
href="https://etherscan.io/address/0xaD6D458402F60fD3Bd25163575031ACDce07538D"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<span
|
||||
class="box mm-text mm-text--inherit box--flex-direction-row box--color-primary-default"
|
||||
>
|
||||
0xaD6...538D
|
||||
<span
|
||||
class="box mm-icon mm-icon--size-md box--margin-left-1 box--display-inline-block box--flex-direction-row box--color-primary-default"
|
||||
style="mask-image: url('./images/icons/undefined.svg');"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
<div>
|
||||
<div
|
||||
aria-describedby="tippy-tooltip-1"
|
||||
class=""
|
||||
data-original-title="Copy to clipboard"
|
||||
data-tooltipped=""
|
||||
style="display: inline; background-color: transparent;"
|
||||
tabindex="0"
|
||||
>
|
||||
<button
|
||||
class="custody-account-list__item__clipboard"
|
||||
>
|
||||
<span
|
||||
class="box mm-icon mm-icon--size-md box--display-inline-block box--flex-direction-row box--color-icon-muted"
|
||||
style="mask-image: url('./images/icons/undefined.svg');"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</label>
|
||||
<div
|
||||
class="box box--display-flex box--flex-direction-row box--justify-content-space-between"
|
||||
>
|
||||
<label
|
||||
class="box mm-text mm-label mm-label--html-for mm-text--body-md mm-text--font-weight-bold box--display-flex box--flex-direction-row box--align-items-center box--color-text-default"
|
||||
for="address-0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="modal-container__footer"
|
||||
>
|
||||
<button
|
||||
class="button btn--rounded btn-secondary modal-container__footer-button"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Nevermind
|
||||
</button>
|
||||
<button
|
||||
class="button btn--rounded btn-primary modal-container__footer-button"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -0,0 +1,125 @@
|
||||
import React, { memo, useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import Modal from '../../app/modal';
|
||||
import CustodyAccountList from '../../../pages/institutional/connect-custody/account-list';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import { removeAccount } from '../../../store/actions';
|
||||
import withModalProps from '../../../helpers/higher-order-components/with-modal-props';
|
||||
import { Text } from '../../component-library';
|
||||
import Box from '../../ui/box';
|
||||
import {
|
||||
BorderRadius,
|
||||
DISPLAY,
|
||||
TEXT_ALIGN,
|
||||
TextColor,
|
||||
TextVariant,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
|
||||
const ConfirmRemoveJWT = ({
|
||||
custodyAccountDetails,
|
||||
accounts,
|
||||
token,
|
||||
hideModal,
|
||||
}) => {
|
||||
const t = useI18nContext();
|
||||
const dispatch = useDispatch();
|
||||
const [showMore, setShowMore] = useState(false);
|
||||
const [tokenAccounts, setTokenAccounts] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const lowercasedTokenAddress = token.address.toLowerCase();
|
||||
|
||||
const filteredAccounts = custodyAccountDetails.filter((item) => {
|
||||
const addressLower = item.address.toLowerCase();
|
||||
return accounts.find((acc) => acc.address.toLowerCase() === addressLower);
|
||||
});
|
||||
|
||||
const tokens = filteredAccounts
|
||||
.filter(({ authDetails }) => {
|
||||
const getToken =
|
||||
authDetails?.token ?? authDetails?.jwt ?? authDetails?.refreshToken;
|
||||
return getToken?.toLowerCase() === lowercasedTokenAddress;
|
||||
})
|
||||
.map(({ address, name, labels, authDetails }) => {
|
||||
const lowercasedAddress = address.toLowerCase();
|
||||
const account = accounts.find(
|
||||
({ address: adressAcc }) =>
|
||||
adressAcc.toLowerCase() === lowercasedAddress,
|
||||
);
|
||||
const balance = account?.balance;
|
||||
const getToken =
|
||||
authDetails?.token ?? authDetails?.jwt ?? authDetails?.refreshToken;
|
||||
return { address, name, labels, balance, token: getToken };
|
||||
});
|
||||
|
||||
setTokenAccounts(tokens);
|
||||
}, [accounts, custodyAccountDetails, token]);
|
||||
|
||||
const handleRemove = async () => {
|
||||
try {
|
||||
for (const account of tokenAccounts) {
|
||||
await dispatch(removeAccount(account.address.toLowerCase()));
|
||||
}
|
||||
hideModal();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleShowMore = () => {
|
||||
setShowMore(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
headerText={`${t('removeJWT')}?`}
|
||||
onClose={hideModal}
|
||||
onSubmit={handleRemove}
|
||||
onCancel={hideModal}
|
||||
submitText={t('remove')}
|
||||
cancelText={t('nevermind')}
|
||||
submitType="primary"
|
||||
>
|
||||
<Box
|
||||
display={DISPLAY.FLEX}
|
||||
padding={2}
|
||||
borderRadius={BorderRadius.SM}
|
||||
className="confirm-action-jwt__jwt"
|
||||
>
|
||||
{showMore && token ? token.address : `...${token.address.slice(-9)}`}
|
||||
</Box>
|
||||
{!showMore && (
|
||||
<Text
|
||||
color={TextColor.goerli}
|
||||
marginLeft={2}
|
||||
className="confirm-action-jwt__show-more"
|
||||
>
|
||||
<a rel="noopener noreferrer" onClick={handleShowMore}>
|
||||
{t('showMore')}
|
||||
</a>
|
||||
</Text>
|
||||
)}
|
||||
<Text
|
||||
as="h6"
|
||||
textAlign={TEXT_ALIGN.CENTER}
|
||||
variant={TextVariant.bodySm}
|
||||
marginTop={2}
|
||||
>
|
||||
{t('removeJWTDescription')}
|
||||
</Text>
|
||||
<Box className="confirm-action-jwt__accounts-list">
|
||||
<CustodyAccountList accounts={tokenAccounts} rawList />
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
ConfirmRemoveJWT.propTypes = {
|
||||
hideModal: PropTypes.func.isRequired,
|
||||
token: PropTypes.object.isRequired,
|
||||
custodyAccountDetails: PropTypes.array.isRequired,
|
||||
accounts: PropTypes.array.isRequired,
|
||||
};
|
||||
|
||||
export default withModalProps(memo(ConfirmRemoveJWT));
|
@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import ConfirmRemoveJWT from '.';
|
||||
|
||||
export default {
|
||||
title: 'Components/Institutional/ConfirmRemoveJWT',
|
||||
component: ConfirmRemoveJWT,
|
||||
args: {
|
||||
hideModal: () => {
|
||||
/**/
|
||||
},
|
||||
removeAccount: () => {
|
||||
/**/
|
||||
},
|
||||
token: { address: '0xaD6D458402F60fD3Bd25163575031ACDce07538D' },
|
||||
custodyAccountDetails: [
|
||||
{
|
||||
address: '0xaD6D458402F60fD3Bd25163575031ACDce07538D',
|
||||
name: 'Test name account',
|
||||
labels: [],
|
||||
authDetails: { token: '0xaD6D458402F60fD3Bd25163575031ACDce07538D' },
|
||||
},
|
||||
],
|
||||
accounts: [
|
||||
{ address: '0xaD6D458402F60fD3Bd25163575031ACDce07538D', balance: '0x0' },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const DefaultStory = (args) => <ConfirmRemoveJWT {...args} />;
|
||||
|
||||
DefaultStory.storyName = 'ConfirmRemoveJWT';
|
@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import { fireEvent, waitFor } from '@testing-library/react';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||
import testData from '../../../../.storybook/test-data';
|
||||
import ConfirmRemoveJwt from '.';
|
||||
|
||||
const mockedRemoveAccount = jest.fn();
|
||||
const mockedHideModal = jest.fn();
|
||||
|
||||
jest.mock('../../../store/actions', () => ({
|
||||
removeAccount: () => mockedRemoveAccount,
|
||||
hideModal: () => mockedHideModal,
|
||||
}));
|
||||
|
||||
const address = '0xaD6D458402F60fD3Bd25163575031ACDce07538D';
|
||||
|
||||
const props = {
|
||||
hideModal: mockedHideModal,
|
||||
token: { address },
|
||||
custodyAccountDetails: [
|
||||
{
|
||||
address,
|
||||
name: 'Test name account',
|
||||
labels: [],
|
||||
authDetails: { token: address },
|
||||
},
|
||||
],
|
||||
accounts: [{ address, balance: '0x0' }],
|
||||
};
|
||||
|
||||
const mockStore = {
|
||||
...testData,
|
||||
metamask: {},
|
||||
};
|
||||
|
||||
const middleware = [thunk];
|
||||
const store = configureMockStore(middleware)(mockStore);
|
||||
|
||||
const render = () => {
|
||||
return renderWithProvider(<ConfirmRemoveJwt {...props} />, store);
|
||||
};
|
||||
|
||||
describe('Confirm Remove JWT', function () {
|
||||
it('should render correctly', () => {
|
||||
const { container } = render();
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should show full token address when "show more" is clicked', () => {
|
||||
const { getByText } = render();
|
||||
|
||||
const showMoreLink = getByText('Show more');
|
||||
fireEvent.click(showMoreLink);
|
||||
|
||||
const fullTokenAddress = getByText(address);
|
||||
expect(fullTokenAddress).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('dispatches removeAccount action when user clicks remove button', async () => {
|
||||
const { getByText } = render();
|
||||
|
||||
const removeButton = getByText('Remove');
|
||||
fireEvent.click(removeButton);
|
||||
|
||||
await waitFor(() => expect(mockedRemoveAccount).toHaveBeenCalled());
|
||||
expect(mockedHideModal).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -0,0 +1 @@
|
||||
export { default } from './confirm-remove-jwt-modal';
|
@ -0,0 +1,13 @@
|
||||
.confirm-action-jwt {
|
||||
&__jwt {
|
||||
border: 1px solid var(--color-border-muted);
|
||||
}
|
||||
|
||||
&__show-more {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__accounts-list {
|
||||
margin-left: -5%;
|
||||
}
|
||||
}
|
@ -79,7 +79,7 @@ const ConfirmAddCustodianToken = () => {
|
||||
setShowMore(true);
|
||||
}}
|
||||
>
|
||||
{t('ShowMore')}
|
||||
{t('showMore')}
|
||||
</ButtonLink>
|
||||
</Box>
|
||||
)}
|
||||
|
@ -34,7 +34,7 @@ export default function CustodyAccountList({
|
||||
rawList,
|
||||
accounts,
|
||||
onAccountChange,
|
||||
selectedAccounts,
|
||||
selectedAccounts = {},
|
||||
onCancel,
|
||||
onAddAccounts,
|
||||
custody,
|
||||
|
Loading…
Reference in New Issue
Block a user