mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
UX: Multichain: Add address to account picker, change connection to copy button (#20520)
* UX: Multichain: Add address to account picker, change connection to copy button * Fix for very long account names
This commit is contained in:
parent
0489911cc8
commit
79d9c18cb1
3
app/_locales/en/messages.json
generated
3
app/_locales/en/messages.json
generated
@ -287,6 +287,9 @@
|
||||
"address": {
|
||||
"message": "Address"
|
||||
},
|
||||
"addressCopied": {
|
||||
"message": "Address copied!"
|
||||
},
|
||||
"advanced": {
|
||||
"message": "Advanced"
|
||||
},
|
||||
|
@ -237,6 +237,8 @@ env:
|
||||
- BLOCKAID_FILE_CDN
|
||||
# Blockaid public key for verifying signatures of data files downloaded from CDN
|
||||
- BLOCKAID_PUBLIC_KEY
|
||||
# Determines if feature flagged Multichain UI should be used
|
||||
- MULTICHAIN: ''
|
||||
|
||||
###
|
||||
# Meta variables
|
||||
|
@ -8,6 +8,12 @@ exports[`AccountPicker renders properly 1`] = `
|
||||
>
|
||||
<span
|
||||
class="mm-box mm-text mm-text--inherit mm-text--ellipsis mm-box--display-flex mm-box--gap-2 mm-box--align-items-center mm-box--color-primary-inverse"
|
||||
>
|
||||
<div
|
||||
class="mm-box multichain-account-picker-container mm-box--display-flex mm-box--flex-direction-column"
|
||||
>
|
||||
<div
|
||||
class="mm-box mm-box--display-flex mm-box--gap-1 mm-box--align-items-center"
|
||||
>
|
||||
<div
|
||||
class="mm-box mm-text mm-avatar-base mm-avatar-base--size-xs mm-avatar-account mm-text--body-xs mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-background-default box--border-style-solid box--border-width-1"
|
||||
@ -34,6 +40,8 @@ exports[`AccountPicker renders properly 1`] = `
|
||||
class="mm-box mm-icon mm-icon--size-sm mm-box--display-inline-block mm-box--color-icon-default"
|
||||
style="mask-image: url('./images/icons/arrow-down.svg');"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -1,7 +1,9 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { toChecksumHexAddress } from '@metamask/controller-utils';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
AvatarAccount,
|
||||
AvatarAccountVariant,
|
||||
@ -14,13 +16,25 @@ import {
|
||||
BackgroundColor,
|
||||
BorderRadius,
|
||||
Display,
|
||||
FlexDirection,
|
||||
FontWeight,
|
||||
IconColor,
|
||||
Size,
|
||||
TextAlign,
|
||||
TextColor,
|
||||
TextVariant,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import { shortenAddress } from '../../../helpers/utils/util';
|
||||
|
||||
export const AccountPicker = ({ address, name, onClick, disabled }) => {
|
||||
export const AccountPicker = ({
|
||||
address,
|
||||
name,
|
||||
onClick,
|
||||
disabled,
|
||||
showAddress = false,
|
||||
}) => {
|
||||
const useBlockie = useSelector((state) => state.metamask.useBlockie);
|
||||
const shortenedAddress = shortenAddress(toChecksumHexAddress(address));
|
||||
|
||||
return (
|
||||
<Button
|
||||
@ -37,6 +51,12 @@ export const AccountPicker = ({ address, name, onClick, disabled }) => {
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Box
|
||||
display={Display.Flex}
|
||||
className="multichain-account-picker-container"
|
||||
flexDirection={FlexDirection.Column}
|
||||
>
|
||||
<Box display={Display.Flex} alignItems={AlignItems.center} gap={1}>
|
||||
<AvatarAccount
|
||||
variant={
|
||||
useBlockie
|
||||
@ -55,6 +75,17 @@ export const AccountPicker = ({ address, name, onClick, disabled }) => {
|
||||
color={IconColor.iconDefault}
|
||||
size={Size.SM}
|
||||
/>
|
||||
</Box>
|
||||
{showAddress ? (
|
||||
<Text
|
||||
color={TextColor.textAlternative}
|
||||
textAlign={TextAlign.Center}
|
||||
variant={TextVariant.bodySm}
|
||||
>
|
||||
{shortenedAddress}
|
||||
</Text>
|
||||
) : null}
|
||||
</Box>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@ -76,4 +107,8 @@ AccountPicker.propTypes = {
|
||||
* Represents if the AccountPicker should be actionable
|
||||
*/
|
||||
disabled: PropTypes.bool.isRequired,
|
||||
/**
|
||||
* Represents if the account address should display
|
||||
*/
|
||||
showAddress: PropTypes.bool,
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ export default {
|
||||
},
|
||||
},
|
||||
args: {
|
||||
address: '"0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e"',
|
||||
address: '0xb19ac54efa18cc3a14a5b821bfec73d284bf0c5e',
|
||||
name: 'Account 1',
|
||||
onClick: () => undefined,
|
||||
},
|
||||
@ -30,6 +30,12 @@ export default {
|
||||
export const DefaultStory = (args) => <AccountPicker {...args} />;
|
||||
DefaultStory.storyName = 'Default';
|
||||
|
||||
export const WithAddressStory = (args) => <AccountPicker {...args} />;
|
||||
WithAddressStory.storyName = 'With Address';
|
||||
WithAddressStory.args = {
|
||||
showAddress: true,
|
||||
};
|
||||
|
||||
export const ChaosStory = (args) => (
|
||||
<div
|
||||
style={{ maxWidth: '300px', border: '1px solid var(--color-border-muted)' }}
|
||||
@ -39,3 +45,13 @@ export const ChaosStory = (args) => (
|
||||
);
|
||||
ChaosStory.storyName = 'Chaos';
|
||||
ChaosStory.args = { name: CHAOS_ACCOUNT.name };
|
||||
|
||||
export const ChaosWithAddressStory = (args) => (
|
||||
<div
|
||||
style={{ maxWidth: '300px', border: '1px solid var(--color-border-muted)' }}
|
||||
>
|
||||
<AccountPicker {...args} />
|
||||
</div>
|
||||
);
|
||||
ChaosWithAddressStory.storyName = 'Chaos with Address';
|
||||
ChaosWithAddressStory.args = { name: CHAOS_ACCOUNT.name, showAddress: true };
|
||||
|
@ -45,4 +45,9 @@ describe('AccountPicker', () => {
|
||||
const { container } = render({}, { useBlockie: false });
|
||||
expect(container.querySelector('svg')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should show the address in the account button for multichain', () => {
|
||||
const { getByText } = render({ showAddress: true });
|
||||
expect(getByText('0x0DC...E7bc')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -4,4 +4,8 @@
|
||||
box-shadow: none;
|
||||
background: var(--color-background-default-hover);
|
||||
}
|
||||
|
||||
&-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
@ -233,6 +233,12 @@ exports[`App Header should match snapshot 1`] = `
|
||||
>
|
||||
<span
|
||||
class="mm-box mm-text mm-text--inherit mm-text--ellipsis mm-box--display-flex mm-box--gap-2 mm-box--align-items-center mm-box--color-primary-inverse"
|
||||
>
|
||||
<div
|
||||
class="mm-box multichain-account-picker-container mm-box--display-flex mm-box--flex-direction-column"
|
||||
>
|
||||
<div
|
||||
class="mm-box mm-box--display-flex mm-box--gap-1 mm-box--align-items-center"
|
||||
>
|
||||
<div
|
||||
class="mm-box mm-text mm-avatar-base mm-avatar-base--size-xs mm-avatar-account mm-text--body-xs mm-text--text-transform-uppercase mm-box--display-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-text-default mm-box--background-color-background-alternative mm-box--rounded-full mm-box--border-color-background-default box--border-style-solid box--border-width-1"
|
||||
@ -286,6 +292,8 @@ exports[`App Header should match snapshot 1`] = `
|
||||
class="mm-box mm-icon mm-icon--size-sm mm-box--display-inline-block mm-box--color-icon-default"
|
||||
style="mask-image: url('./images/icons/arrow-down.svg');"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
|
@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
|
||||
import browser from 'webextension-polyfill';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useHistory, matchPath } from 'react-router-dom';
|
||||
import { toChecksumHexAddress } from '@metamask/controller-utils';
|
||||
import { MetaMetricsContext } from '../../../contexts/metametrics';
|
||||
import {
|
||||
MetaMetricsEventCategory,
|
||||
@ -30,6 +31,7 @@ import {
|
||||
IconName,
|
||||
PickerNetwork,
|
||||
Box,
|
||||
IconSize,
|
||||
} from '../../component-library';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
import { getCustodianIconForAddress } from '../../../selectors/institutional/selectors';
|
||||
@ -42,8 +44,8 @@ import {
|
||||
getSelectedIdentity,
|
||||
getShowProductTour,
|
||||
getTestNetworkBackgroundColor,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
getSelectedAddress,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
getTheme,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../../selectors';
|
||||
@ -62,6 +64,8 @@ import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import { getCompletedOnboarding } from '../../../ducks/metamask/metamask';
|
||||
import { getSendStage, SEND_STAGES } from '../../../ducks/send';
|
||||
import Tooltip from '../../ui/tooltip';
|
||||
import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard';
|
||||
import { MINUTE } from '../../../../shared/constants/time';
|
||||
|
||||
export const AppHeader = ({ location }) => {
|
||||
const trackEvent = useContext(MetaMetricsContext);
|
||||
@ -94,11 +98,17 @@ export const AppHeader = ({ location }) => {
|
||||
const currentNetwork = useSelector(getCurrentNetwork);
|
||||
const testNetworkBackgroundColor = useSelector(getTestNetworkBackgroundColor);
|
||||
|
||||
// Used for copy button
|
||||
const currentAddress = useSelector(getSelectedAddress);
|
||||
const checksummedCurrentAddress = toChecksumHexAddress(currentAddress);
|
||||
const [copied, handleCopy] = useCopyToClipboard(MINUTE);
|
||||
|
||||
const popupStatus = getEnvironmentType() === ENVIRONMENT_TYPE_POPUP;
|
||||
const showStatus =
|
||||
getEnvironmentType() === ENVIRONMENT_TYPE_POPUP &&
|
||||
const showConnectedStatus =
|
||||
process.env.MULTICHAIN ||
|
||||
(getEnvironmentType() === ENVIRONMENT_TYPE_POPUP &&
|
||||
origin &&
|
||||
origin !== browser.runtime.id;
|
||||
origin !== browser.runtime.id);
|
||||
const showProductTour =
|
||||
completedOnboarding && !onboardedInThisUISession && showProductTourPopup;
|
||||
const productTourDirection = document
|
||||
@ -285,6 +295,7 @@ export const AppHeader = ({ location }) => {
|
||||
});
|
||||
}}
|
||||
disabled={disableAccountPicker}
|
||||
showAddress={process.env.MULTICHAIN}
|
||||
/>
|
||||
) : null}
|
||||
<Box
|
||||
@ -293,19 +304,35 @@ export const AppHeader = ({ location }) => {
|
||||
justifyContent={JustifyContent.flexEnd}
|
||||
>
|
||||
<Box display={Display.Flex} gap={4}>
|
||||
{showStatus ? (
|
||||
{showConnectedStatus &&
|
||||
(process.env.MULTICHAIN ? (
|
||||
<Tooltip
|
||||
position="left"
|
||||
title={copied ? t('addressCopied') : null}
|
||||
>
|
||||
<ButtonIcon
|
||||
onClick={() => handleCopy(checksummedCurrentAddress)}
|
||||
iconName={
|
||||
copied ? IconName.CopySuccess : IconName.Copy
|
||||
}
|
||||
size={IconSize.Sm}
|
||||
data-testid="app-header-copy-button"
|
||||
/>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Box ref={menuRef}>
|
||||
<ConnectedStatusIndicator
|
||||
onClick={() => {
|
||||
history.push(CONNECTED_ACCOUNTS_ROUTE);
|
||||
trackEvent({
|
||||
event: MetaMetricsEventName.NavConnectedSitesOpened,
|
||||
event:
|
||||
MetaMetricsEventName.NavConnectedSitesOpened,
|
||||
category: MetaMetricsEventCategory.Navigation,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
) : null}{' '}
|
||||
))}{' '}
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
custodianIcon && (
|
||||
|
@ -31,4 +31,10 @@ describe('App Header', () => {
|
||||
const { getByTestId } = render({ send: { stage: SEND_STAGES.DRAFT } });
|
||||
expect(getByTestId('account-menu-icon')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should show the copy button for multichain', () => {
|
||||
process.env.MULTICHAIN = 1;
|
||||
const { getByTestId } = render({ send: { stage: SEND_STAGES.INACTIVE } });
|
||||
expect(getByTestId('app-header-copy-button')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user