From c3cb46422986ffd93fb3a055d4ed21a7506f87c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Oliv=C3=A9?= Date: Mon, 17 Apr 2023 17:06:41 +0200 Subject: [PATCH] [MMI] Added custody-labels and account-list components (#18197) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added custody-labels and account-list components * Modified link * Renamed custody labels and improved code * Added snapshot * Fixed snapshot * Finished connect-custody account-list * Fixing css prettier * Fixed comments of the PR * Fixed code --------- Co-authored-by: António Regadas --- .../__snapshots__/custody-labels.test.js.snap | 16 ++ .../custody-labels/custody-labels.js | 58 +++++ .../custody-labels/custody-labels.scss | 9 + .../custody-labels/custody-labels.stories.js | 16 ++ .../custody-labels/custody-labels.test.js | 17 ++ .../institutional/custody-labels/index.js | 1 + .../__snapshots__/account-list.test.js.snap | 188 +++++++++++++++ .../connect-custody/account-list.js | 218 ++++++++++++++++++ .../connect-custody/account-list.stories.js | 34 +++ .../connect-custody/account-list.test.js | 109 +++++++++ .../institutional/connect-custody/index.js | 1 + .../institutional/connect-custody/index.scss | 49 ++++ ui/pages/pages.scss | 3 + 13 files changed, 719 insertions(+) create mode 100644 ui/components/institutional/custody-labels/__snapshots__/custody-labels.test.js.snap create mode 100644 ui/components/institutional/custody-labels/custody-labels.js create mode 100644 ui/components/institutional/custody-labels/custody-labels.scss create mode 100644 ui/components/institutional/custody-labels/custody-labels.stories.js create mode 100644 ui/components/institutional/custody-labels/custody-labels.test.js create mode 100644 ui/components/institutional/custody-labels/index.js create mode 100644 ui/pages/create-account/institutional/connect-custody/__snapshots__/account-list.test.js.snap create mode 100644 ui/pages/create-account/institutional/connect-custody/account-list.js create mode 100644 ui/pages/create-account/institutional/connect-custody/account-list.stories.js create mode 100644 ui/pages/create-account/institutional/connect-custody/account-list.test.js create mode 100644 ui/pages/create-account/institutional/connect-custody/index.js create mode 100644 ui/pages/create-account/institutional/connect-custody/index.scss diff --git a/ui/components/institutional/custody-labels/__snapshots__/custody-labels.test.js.snap b/ui/components/institutional/custody-labels/__snapshots__/custody-labels.test.js.snap new file mode 100644 index 000000000..78c08a9a0 --- /dev/null +++ b/ui/components/institutional/custody-labels/__snapshots__/custody-labels.test.js.snap @@ -0,0 +1,16 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CustodyLabels Component should render correctly 1`] = ` +
+ +
+`; diff --git a/ui/components/institutional/custody-labels/custody-labels.js b/ui/components/institutional/custody-labels/custody-labels.js new file mode 100644 index 000000000..4cbd4acd0 --- /dev/null +++ b/ui/components/institutional/custody-labels/custody-labels.js @@ -0,0 +1,58 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Text, Label } from '../../component-library'; +import { + TEXT_TRANSFORM, + BackgroundColor, + TextColor, + FONT_WEIGHT, + BorderRadius, + TypographyVariant, +} from '../../../helpers/constants/design-system'; + +const CustodyLabels = (props) => { + const { labels, index, background, hideNetwork } = props; + const filteredLabels = hideNetwork + ? labels.filter((item) => item.key !== 'network_name') + : labels; + + return ( + + ); +}; + +CustodyLabels.propTypes = { + labels: PropTypes.array, + index: PropTypes.string, + background: PropTypes.string, + hideNetwork: PropTypes.bool, +}; + +export default CustodyLabels; diff --git a/ui/components/institutional/custody-labels/custody-labels.scss b/ui/components/institutional/custody-labels/custody-labels.scss new file mode 100644 index 000000000..99695902a --- /dev/null +++ b/ui/components/institutional/custody-labels/custody-labels.scss @@ -0,0 +1,9 @@ +.custody-label { + z-index: 1; + letter-spacing: 0.5px; + white-space: nowrap; + max-width: 80px; + text-overflow: ellipsis; + overflow: hidden; + display: block; +} diff --git a/ui/components/institutional/custody-labels/custody-labels.stories.js b/ui/components/institutional/custody-labels/custody-labels.stories.js new file mode 100644 index 000000000..9db28434d --- /dev/null +++ b/ui/components/institutional/custody-labels/custody-labels.stories.js @@ -0,0 +1,16 @@ +import React from 'react'; +import CustodyLabels from '.'; + +export default { + title: 'Components/Institutional/CustodyLabels', + component: CustodyLabels, + args: { + labels: [{ key: 'testKey', value: 'value' }], + index: 'index', + hideNetwork: 'true', + }, +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'CustodyLabels'; diff --git a/ui/components/institutional/custody-labels/custody-labels.test.js b/ui/components/institutional/custody-labels/custody-labels.test.js new file mode 100644 index 000000000..5fdc52c43 --- /dev/null +++ b/ui/components/institutional/custody-labels/custody-labels.test.js @@ -0,0 +1,17 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import CustodyLabels from './custody-labels'; + +describe('CustodyLabels Component', () => { + it('should render correctly', () => { + const props = { + labels: [{ key: 'testKey', value: 'value' }], + index: 'index', + hideNetwork: 'true', + }; + + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); +}); diff --git a/ui/components/institutional/custody-labels/index.js b/ui/components/institutional/custody-labels/index.js new file mode 100644 index 000000000..d7d705707 --- /dev/null +++ b/ui/components/institutional/custody-labels/index.js @@ -0,0 +1 @@ +export { default } from './custody-labels'; diff --git a/ui/pages/create-account/institutional/connect-custody/__snapshots__/account-list.test.js.snap b/ui/pages/create-account/institutional/connect-custody/__snapshots__/account-list.test.js.snap new file mode 100644 index 000000000..8b3c9ad9c --- /dev/null +++ b/ui/pages/create-account/institutional/connect-custody/__snapshots__/account-list.test.js.snap @@ -0,0 +1,188 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CustodyAccountList renders accounts 1`] = ` +
+
+ +`; diff --git a/ui/pages/create-account/institutional/connect-custody/account-list.js b/ui/pages/create-account/institutional/connect-custody/account-list.js new file mode 100644 index 000000000..2e541d7d2 --- /dev/null +++ b/ui/pages/create-account/institutional/connect-custody/account-list.js @@ -0,0 +1,218 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Button from '../../../../components/ui/button'; +import CustodyLabels from '../../../../components/institutional/custody-labels'; +import { SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP } from '../../../../../shared/constants/swaps'; +import { CHAIN_IDS } from '../../../../../shared/constants/network'; +import { shortenAddress } from '../../../../helpers/utils/util'; +import Tooltip from '../../../../components/ui/tooltip'; +import { + TextVariant, + JustifyContent, + BLOCK_SIZES, + DISPLAY, + IconColor, +} from '../../../../helpers/constants/design-system'; +import { useI18nContext } from '../../../../hooks/useI18nContext'; +import Box from '../../../../components/ui/box'; +import { + Text, + Label, + Icon, + IconName, + IconSize, + ButtonLink, +} from '../../../../components/component-library'; +import { useCopyToClipboard } from '../../../../hooks/useCopyToClipboard'; + +const getButtonLinkHref = (account) => { + const url = SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[CHAIN_IDS.MAINNET]; + return `${url}address/${account.address}`; +}; + +export default function CustodyAccountList({ + rawList, + accounts, + onAccountChange, + selectedAccounts, + onCancel, + onAddAccounts, + custody, +}) { + const t = useI18nContext(); + const [copied, handleCopy] = useCopyToClipboard(); + const tooltipText = copied ? t('copiedExclamation') : t('copyToClipboard'); + const disabled = Object.keys(selectedAccounts).length === 0; + + return ( + <> + + + {accounts.map((account, idx) => ( + + + {!rawList && ( + + onAccountChange({ + name: account.name, + address: e.target.value, + custodianDetails: account.custodianDetails, + labels: account.labels, + chainId: account.chainId, + }) + } + checked={ + selectedAccounts && selectedAccounts[account.address] + } + /> + )} + + + + + + {account.labels && ( + + )} + + + + ))} + + + {!rawList && ( + + + + + )} + + ); +} + +CustodyAccountList.propTypes = { + custody: PropTypes.string, + accounts: PropTypes.array.isRequired, + onAccountChange: PropTypes.func, + selectedAccounts: PropTypes.object, + onAddAccounts: PropTypes.func, + onCancel: PropTypes.func, + rawList: PropTypes.bool, +}; diff --git a/ui/pages/create-account/institutional/connect-custody/account-list.stories.js b/ui/pages/create-account/institutional/connect-custody/account-list.stories.js new file mode 100644 index 000000000..a7847b73c --- /dev/null +++ b/ui/pages/create-account/institutional/connect-custody/account-list.stories.js @@ -0,0 +1,34 @@ +import React from 'react'; +import CustodyAccountList from '.'; + +const testAccounts = [ + { + address: '0x1234567890123456789012345678901234567890', + name: 'Test Account 1', + chainId: 1, + }, + { + address: '0x0987654321098765432109876543210987654321', + name: 'Test Account 2', + chainId: 1, + }, +]; + +export default { + title: 'Pages/Institutional/CustodyAccountList', + component: CustodyAccountList, + args: { + custody: 'Test', + accounts: testAccounts, + onAccountChange: () => undefined, + selectedAccounts: {}, + onAddAccounts: () => undefined, + onCancel: () => undefined, + provider: 'Test', + rawList: false, + }, +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'CustodyAccountList'; diff --git a/ui/pages/create-account/institutional/connect-custody/account-list.test.js b/ui/pages/create-account/institutional/connect-custody/account-list.test.js new file mode 100644 index 000000000..5536c656c --- /dev/null +++ b/ui/pages/create-account/institutional/connect-custody/account-list.test.js @@ -0,0 +1,109 @@ +import React from 'react'; +import { fireEvent, render, screen } from '@testing-library/react'; +import CustodyAccountList from './account-list'; + +const testAccounts = [ + { + address: '0x1234567890123456789012345678901234567890', + name: 'Test Account 1', + chainId: 1, + }, + { + address: '0x0987654321098765432109876543210987654321', + name: 'Test Account 2', + chainId: 1, + }, +]; + +describe('CustodyAccountList', () => { + const onAccountChangeMock = jest.fn(); + const onCancelMock = jest.fn(); + const onAddAccountsMock = jest.fn(); + const selectedAccountsMock = {}; + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('renders accounts', () => { + const { container } = render( + , + ); + + expect(container).toMatchSnapshot(); + }); + + it('calls onAccountChange when an account is selected', () => { + render( + , + ); + + const firstAccountCheckbox = screen.getAllByRole('checkbox')[0]; + fireEvent.click(firstAccountCheckbox); + + expect(onAccountChangeMock).toHaveBeenCalledTimes(1); + expect(onAccountChangeMock).toHaveBeenCalledWith({ + name: 'Test Account 1', + address: '0x1234567890123456789012345678901234567890', + custodianDetails: undefined, + labels: undefined, + chainId: 1, + }); + }); + + it('calls onCancel when the Cancel button is clicked', () => { + render( + , + ); + + const cancelButton = screen.getByTestId('custody-account-cancel-button'); + fireEvent.click(cancelButton); + + expect(onCancelMock).toHaveBeenCalledTimes(1); + }); + + it('calls onAddAccounts when the Connect button is clicked', () => { + selectedAccountsMock['0x1234567890123456789012345678901234567890'] = true; + selectedAccountsMock['0x0987654321098765432109876543210987654321'] = true; + + render( + , + ); + + const addAccountsButton = screen.getByTestId( + 'custody-account-connect-button', + ); + fireEvent.click(addAccountsButton); + + expect(onAddAccountsMock).toHaveBeenCalledTimes(1); + expect(onAddAccountsMock).toHaveBeenCalledWith('Test'); + }); +}); diff --git a/ui/pages/create-account/institutional/connect-custody/index.js b/ui/pages/create-account/institutional/connect-custody/index.js new file mode 100644 index 000000000..8ce06b6f8 --- /dev/null +++ b/ui/pages/create-account/institutional/connect-custody/index.js @@ -0,0 +1 @@ +export { default } from './account-list'; diff --git a/ui/pages/create-account/institutional/connect-custody/index.scss b/ui/pages/create-account/institutional/connect-custody/index.scss new file mode 100644 index 000000000..b63903969 --- /dev/null +++ b/ui/pages/create-account/institutional/connect-custody/index.scss @@ -0,0 +1,49 @@ +.custody-account-list { + flex: 1; + max-height: 50vh; + overflow: auto; + + &__item { + border-bottom: 1px solid #d2d8dd; + + input { + margin: 12px 0 0 10px; + } + + &__title { + flex-flow: row; + } + + &__name { + display: block; + white-space: nowrap; + max-width: 200px; + text-overflow: ellipsis; + overflow: hidden; + } + + &__clipboard { + background-color: transparent; + } + } + + &__item:first-child { + border-top: 1px solid #d2d8dd; + } + + &__item:last-child { + border: 0; + } + + &__item:hover { + background-color: rgba(0, 0, 0, 0.03); + } + + &__buttons { + border-top: 1px solid #d2d8dd; + } + + &__button:not(:last-child) { + margin-right: 16px; + } +} diff --git a/ui/pages/pages.scss b/ui/pages/pages.scss index 1d78f3af6..925feceb0 100644 --- a/ui/pages/pages.scss +++ b/ui/pages/pages.scss @@ -12,6 +12,9 @@ @import 'connected-accounts/index'; @import 'connected-sites/index'; @import 'create-account/index'; +///: BEGIN:ONLY_INCLUDE_IN(mmi) +@import "create-account/institutional/connect-custody/index"; +///: END:ONLY_INCLUDE_IN @import 'error/index'; @import 'send/gas-display/index'; @import 'home/index';