1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-02 06:07:06 +01:00
metamask-extension/ui/components/multichain/account-list-item/account-list-item.js
David Walsh c079c4320e
UX: Multichain: Account Menu List (#17947)
* UX: Multichain: Account Menu List

* Move to using stylesheet

* Add hover state

* Implement George's suggestions

* Add connected site avatar

* Add hardware tag

* Create story for selected hardware item

* Progress on the AccountListItemMenu

* Add story for AccountListItemMenu

* Better position the account menu

* Fix AvatarFavicon missing name prop

* Update menu options label to be account specific

* Update text of 'View on Explorer'

* Add AccountListMenu component

* Move all items to multichain directory

* Fix paths

* Fix linting, use AvatarIcon

* Add title and close button to account menu

* Center the popover title

* Add search functionality

* Implementation WIP

* Add MULTICHAIN feature flag

* Add MULTICHAIN feature flag, add actions for menu items

* Properly dispatch events

* Fix search box padding

* Fix sizing of menu item text

* Fix isRequired

* Fix alignment of the popover

* Update label for hardware wallet items, add text for no search results

* Update keyring retreival to remove account and add label

* Fix storybook

* Fix double link click issue, prevent wrapping of values

* Use labelProps for tag variant

* Restructure item menu story

* Empower storybooks for all new components

* Allow only 3 decimals for currencies

* Avoid inline styles

* Prefix classes with multichain, fix account-list-menu storybook

* Close the accounts menu when account details is clicked

* Restore tag.js

* Create global file for multichain css

* Add index file for multichain js

* Update file paths

* Ensure the block domain is present in menu

* Add AccountListItem test

* Add AccountListItemMenu tests

* Show account connect to current dapp

* Improve tests

* Make avatar smaller

* Add tooltip for account menu

* Align icon better

* Update snapshot

* Rename files to DS standard

* Add index files for export

* Export all multichain components

* Update snapshot

* Remove embedded style in popover

* Add comments for props, cleanup storybook

* Improve test coverage

* Improve test code quality

* Remove border form avatar

* Switch to using the ButtonLink iconName prop

* Only show tooltip if character limit is reached

* Restore prior search settings

* Add test for tooltip
2023-03-22 15:30:08 +05:30

256 lines
7.3 KiB
JavaScript

import React, { useState, useRef } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { useSelector } from 'react-redux';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { getRpcPrefsForCurrentProvider } from '../../../selectors';
import { getURLHostName, shortenAddress } from '../../../helpers/utils/util';
import { AccountListItemMenu } from '..';
import Box from '../../ui/box/box';
import {
AvatarAccount,
ButtonIcon,
Text,
ICON_NAMES,
ICON_SIZES,
AvatarFavicon,
Tag,
} from '../../component-library';
import {
Color,
TEXT_ALIGN,
AlignItems,
DISPLAY,
TextVariant,
FLEX_DIRECTION,
BorderRadius,
JustifyContent,
Size,
BorderColor,
} from '../../../helpers/constants/design-system';
import {
HardwareKeyringTypes,
HardwareKeyringNames,
} from '../../../../shared/constants/hardware-wallets';
import UserPreferencedCurrencyDisplay from '../../app/user-preferenced-currency-display/user-preferenced-currency-display.component';
import { SECONDARY, PRIMARY } from '../../../helpers/constants/common';
import { findKeyringForAddress } from '../../../ducks/metamask/metamask';
import Tooltip from '../../ui/tooltip/tooltip';
const MAXIMUM_CURRENCY_DECIMALS = 3;
const MAXIMUM_CHARACTERS_WITHOUT_TOOLTIP = 17;
function getLabel(keyring = {}, t) {
const { type } = keyring;
switch (type) {
case HardwareKeyringTypes.qr:
return HardwareKeyringNames.qr;
case HardwareKeyringTypes.imported:
return t('imported');
case HardwareKeyringTypes.trezor:
return HardwareKeyringNames.trezor;
case HardwareKeyringTypes.ledger:
return HardwareKeyringNames.ledger;
case HardwareKeyringTypes.lattice:
return HardwareKeyringNames.lattice;
default:
return null;
}
}
export const AccountListItem = ({
identity,
selected = false,
onClick,
closeMenu,
connectedAvatar,
connectedAvatarName,
}) => {
const t = useI18nContext();
const [accountOptionsMenuOpen, setAccountOptionsMenuOpen] = useState(false);
const ref = useRef(false);
const keyring = useSelector((state) =>
findKeyringForAddress(state, identity.address),
);
const label = getLabel(keyring, t);
const rpcPrefs = useSelector(getRpcPrefsForCurrentProvider);
const { blockExplorerUrl } = rpcPrefs;
const blockExplorerUrlSubTitle = getURLHostName(blockExplorerUrl);
return (
<Box
display={DISPLAY.FLEX}
padding={4}
gap={2}
backgroundColor={selected ? Color.primaryMuted : Color.transparent}
className={classnames('multichain-account-list-item', {
'multichain-account-list-item--selected': selected,
})}
as="button"
onClick={(e) => {
e.preventDefault();
// Without this check, the account will be selected after
// the account options menu closes
if (!accountOptionsMenuOpen) {
onClick();
}
}}
>
{selected && (
<Box
className="multichain-account-list-item__selected-indicator"
borderRadius={BorderRadius.pill}
backgroundColor={Color.primaryDefault}
/>
)}
<AvatarAccount
borderColor={BorderColor.transparent}
size={Size.SM}
address={identity.address}
></AvatarAccount>
<Box
display={DISPLAY.FLEX}
flexDirection={FLEX_DIRECTION.COLUMN}
className="multichain-account-list-item__content"
>
<Box display={DISPLAY.FLEX} flexDirection={FLEX_DIRECTION.COLUMN}>
<Box
display={DISPLAY.FLEX}
justifyContent={JustifyContent.spaceBetween}
gap={2}
>
<Text ellipsis as="div">
{identity.name.length > MAXIMUM_CHARACTERS_WITHOUT_TOOLTIP ? (
<Tooltip
title={identity.name}
position="bottom"
wrapperClassName="multichain-account-list-item__tooltip"
>
{identity.name}
</Tooltip>
) : (
identity.name
)}
</Text>
<Box
display={DISPLAY.FLEX}
flexDirection={FLEX_DIRECTION.ROW}
alignItems={AlignItems.center}
>
{connectedAvatar ? (
<AvatarFavicon
size={Size.XS}
src={connectedAvatar}
name={connectedAvatarName}
marginInlineEnd={2}
/>
) : null}
<Text textAlign={TEXT_ALIGN.END} as="div">
<UserPreferencedCurrencyDisplay
ethNumberOfDecimals={MAXIMUM_CURRENCY_DECIMALS}
value={identity.balance}
type={SECONDARY}
/>
</Text>
</Box>
</Box>
</Box>
<Box
display={DISPLAY.FLEX}
justifyContent={JustifyContent.spaceBetween}
>
<Text variant={TextVariant.bodySm} color={Color.textAlternative}>
{shortenAddress(identity.address)}
</Text>
<Text
variant={TextVariant.bodySm}
color={Color.textAlternative}
textAlign={TEXT_ALIGN.END}
as="div"
>
<UserPreferencedCurrencyDisplay
ethNumberOfDecimals={MAXIMUM_CURRENCY_DECIMALS}
value={identity.balance}
type={PRIMARY}
/>
</Text>
</Box>
{label ? (
<Tag
label={label}
labelProps={{
variant: TextVariant.bodyXs,
color: Color.textAlternative,
}}
/>
) : null}
</Box>
<div ref={ref}>
<ButtonIcon
ariaLabel={`${identity.name} ${t('options')}`}
iconName={ICON_NAMES.MORE_VERTICAL}
size={ICON_SIZES.SM}
onClick={(e) => {
e.stopPropagation();
setAccountOptionsMenuOpen(true);
}}
as="div"
tabIndex={0}
onKeyPress={(e) => {
if (e.key === 'Enter') {
setAccountOptionsMenuOpen(true);
}
}}
data-testid="account-list-item-menu-button"
/>
{accountOptionsMenuOpen ? (
<AccountListItemMenu
anchorElement={ref.current}
blockExplorerUrlSubTitle={blockExplorerUrlSubTitle}
identity={identity}
onClose={() => setAccountOptionsMenuOpen(false)}
isRemovable={keyring?.type !== HardwareKeyringTypes.hdKeyTree}
closeMenu={closeMenu}
/>
) : null}
</div>
</Box>
);
};
AccountListItem.propTypes = {
/**
* Identity of the account
*/
identity: PropTypes.shape({
name: PropTypes.string.isRequired,
address: PropTypes.string.isRequired,
balance: PropTypes.string.isRequired,
}).isRequired,
/**
* Represents if this account is currently selected
*/
selected: PropTypes.bool,
/**
* Function to execute when the item is clicked
*/
onClick: PropTypes.func.isRequired,
/**
* Function that closes the menu
*/
closeMenu: PropTypes.func,
/**
* File location of the avatar icon
*/
connectedAvatar: PropTypes.string,
/**
* Text used as the avatar alt text
*/
connectedAvatarName: PropTypes.string,
};
AccountListItem.displayName = 'AccountListItem';