mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 09:23:21 +01:00
UX Multichain: Added balance-overview component (#20528)
* added balance-overview component * updated balance overview component to use Currency utility props * added MULTICHAIN feature flag * lint fix * lint fix * lint fix * updated ternary operators
This commit is contained in:
parent
6254fbb98d
commit
b8b94c2c1f
@ -24,6 +24,7 @@ import {
|
||||
DetectedTokensBanner,
|
||||
TokenListItem,
|
||||
ImportTokenLink,
|
||||
BalanceOverview,
|
||||
} from '../../multichain';
|
||||
|
||||
const AssetList = ({ onClickAsset }) => {
|
||||
@ -67,6 +68,7 @@ const AssetList = ({ onClickAsset }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{process.env.MULTICHAIN ? <BalanceOverview /> : null}
|
||||
<TokenListItem
|
||||
onClick={() => onClickAsset(nativeCurrency)}
|
||||
title={nativeCurrency}
|
||||
|
@ -72,4 +72,8 @@ UserPreferencedCurrencyDisplay.propTypes = {
|
||||
]),
|
||||
showFiat: PropTypes.bool,
|
||||
showCurrencySuffix: PropTypes.bool,
|
||||
/**
|
||||
* UserPreferencedCurrencyDisplay component should also accept all the props from Currency component
|
||||
*/
|
||||
...CurrencyDisplay.propTypes,
|
||||
};
|
||||
|
@ -0,0 +1,294 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Balance Overview and Portfolio for Tokens should match snapshot 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mm-box token-balance-overview mm-box--padding-4 mm-box--display-flex mm-box--justify-content-space-between mm-box--align-items-center"
|
||||
>
|
||||
<div
|
||||
class="mm-box token-balance-overview__balance"
|
||||
>
|
||||
<div
|
||||
class="mm-box token-balance-overview__primary-container"
|
||||
>
|
||||
<div
|
||||
class="spinner loading-overlay__spinner"
|
||||
>
|
||||
<svg
|
||||
class="lds-spinner"
|
||||
height="100%"
|
||||
preserveAspectRatio="xMidYMid"
|
||||
style="background: none;"
|
||||
viewBox="0 0 100 100"
|
||||
width="100%"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<g
|
||||
transform="rotate(0 50 50)"
|
||||
>
|
||||
<rect
|
||||
fill="var(--color-secondary-default)"
|
||||
height="30"
|
||||
rx="0"
|
||||
ry="0"
|
||||
width="10"
|
||||
x="45"
|
||||
y="0"
|
||||
>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
begin="-0.9166666666666666s"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
values="1;0"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g
|
||||
transform="rotate(30 50 50)"
|
||||
>
|
||||
<rect
|
||||
fill="var(--color-secondary-default)"
|
||||
height="30"
|
||||
rx="0"
|
||||
ry="0"
|
||||
width="10"
|
||||
x="45"
|
||||
y="0"
|
||||
>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
begin="-0.8333333333333334s"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
values="1;0"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g
|
||||
transform="rotate(60 50 50)"
|
||||
>
|
||||
<rect
|
||||
fill="var(--color-secondary-default)"
|
||||
height="30"
|
||||
rx="0"
|
||||
ry="0"
|
||||
width="10"
|
||||
x="45"
|
||||
y="0"
|
||||
>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
begin="-0.75s"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
values="1;0"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g
|
||||
transform="rotate(90 50 50)"
|
||||
>
|
||||
<rect
|
||||
fill="var(--color-secondary-default)"
|
||||
height="30"
|
||||
rx="0"
|
||||
ry="0"
|
||||
width="10"
|
||||
x="45"
|
||||
y="0"
|
||||
>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
begin="-0.6666666666666666s"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
values="1;0"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g
|
||||
transform="rotate(120 50 50)"
|
||||
>
|
||||
<rect
|
||||
fill="var(--color-secondary-default)"
|
||||
height="30"
|
||||
rx="0"
|
||||
ry="0"
|
||||
width="10"
|
||||
x="45"
|
||||
y="0"
|
||||
>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
begin="-0.5833333333333334s"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
values="1;0"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g
|
||||
transform="rotate(150 50 50)"
|
||||
>
|
||||
<rect
|
||||
fill="var(--color-secondary-default)"
|
||||
height="30"
|
||||
rx="0"
|
||||
ry="0"
|
||||
width="10"
|
||||
x="45"
|
||||
y="0"
|
||||
>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
begin="-0.5s"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
values="1;0"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g
|
||||
transform="rotate(180 50 50)"
|
||||
>
|
||||
<rect
|
||||
fill="var(--color-secondary-default)"
|
||||
height="30"
|
||||
rx="0"
|
||||
ry="0"
|
||||
width="10"
|
||||
x="45"
|
||||
y="0"
|
||||
>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
begin="-0.4166666666666667s"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
values="1;0"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g
|
||||
transform="rotate(210 50 50)"
|
||||
>
|
||||
<rect
|
||||
fill="var(--color-secondary-default)"
|
||||
height="30"
|
||||
rx="0"
|
||||
ry="0"
|
||||
width="10"
|
||||
x="45"
|
||||
y="0"
|
||||
>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
begin="-0.3333333333333333s"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
values="1;0"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g
|
||||
transform="rotate(240 50 50)"
|
||||
>
|
||||
<rect
|
||||
fill="var(--color-secondary-default)"
|
||||
height="30"
|
||||
rx="0"
|
||||
ry="0"
|
||||
width="10"
|
||||
x="45"
|
||||
y="0"
|
||||
>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
begin="-0.25s"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
values="1;0"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g
|
||||
transform="rotate(270 50 50)"
|
||||
>
|
||||
<rect
|
||||
fill="var(--color-secondary-default)"
|
||||
height="30"
|
||||
rx="0"
|
||||
ry="0"
|
||||
width="10"
|
||||
x="45"
|
||||
y="0"
|
||||
>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
begin="-0.16666666666666666s"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
values="1;0"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g
|
||||
transform="rotate(300 50 50)"
|
||||
>
|
||||
<rect
|
||||
fill="var(--color-secondary-default)"
|
||||
height="30"
|
||||
rx="0"
|
||||
ry="0"
|
||||
width="10"
|
||||
x="45"
|
||||
y="0"
|
||||
>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
begin="-0.08333333333333333s"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
values="1;0"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g
|
||||
transform="rotate(330 50 50)"
|
||||
>
|
||||
<rect
|
||||
fill="var(--color-secondary-default)"
|
||||
height="30"
|
||||
rx="0"
|
||||
ry="0"
|
||||
width="10"
|
||||
x="45"
|
||||
y="0"
|
||||
>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
begin="0s"
|
||||
dur="1s"
|
||||
repeatCount="indefinite"
|
||||
values="1;0"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="mm-box mm-text mm-button-base mm-button-base--size-md token-balance-portfolio mm-button-secondary mm-text--body-md-medium mm-box--padding-0 mm-box--padding-right-4 mm-box--padding-left-4 mm-box--display-inline-flex mm-box--justify-content-center mm-box--align-items-center mm-box--color-primary-default mm-box--background-color-transparent mm-box--rounded-pill mm-box--border-color-primary-default box--border-style-solid box--border-width-1"
|
||||
>
|
||||
Portfolio
|
||||
<span
|
||||
class="mm-box mm-icon mm-icon--size-sm mm-box--margin-inline-start-1 mm-box--display-inline-block mm-box--color-inherit"
|
||||
style="mask-image: url('./images/icons/export.svg');"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
171
ui/components/multichain/balance-overview/balance-overview.js
Normal file
171
ui/components/multichain/balance-overview/balance-overview.js
Normal file
@ -0,0 +1,171 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import classnames from 'classnames';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import { MetaMetricsContext } from '../../../contexts/metametrics';
|
||||
import {
|
||||
MetaMetricsEventCategory,
|
||||
MetaMetricsEventName,
|
||||
} from '../../../../shared/constants/metametrics';
|
||||
import { Box, ButtonSecondary, IconName } from '../../component-library';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
import {
|
||||
getMmiPortfolioEnabled,
|
||||
getMmiPortfolioUrl,
|
||||
} from '../../../selectors/institutional/selectors';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-main,build-beta,build-flask)
|
||||
import { getPortfolioUrl } from '../../../helpers/utils/portfolio';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
import {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-main,build-beta,build-flask)
|
||||
getCurrentChainId,
|
||||
getMetaMetricsId,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
getSelectedAccountCachedBalance,
|
||||
isBalanceCached,
|
||||
} from '../../../selectors';
|
||||
import Spinner from '../../ui/spinner';
|
||||
import UserPreferencedCurrencyDisplay from '../../app/user-preferenced-currency-display';
|
||||
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
|
||||
import {
|
||||
AlignItems,
|
||||
Display,
|
||||
JustifyContent,
|
||||
TextColor,
|
||||
TextVariant,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
|
||||
export const BalanceOverview = () => {
|
||||
const trackEvent = useContext(MetaMetricsContext);
|
||||
const t = useI18nContext();
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-main,build-beta,build-flask)
|
||||
const metaMetricsId = useSelector(getMetaMetricsId);
|
||||
const chainId = useSelector(getCurrentChainId);
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
const balanceIsCached = useSelector(isBalanceCached);
|
||||
const balance = useSelector(getSelectedAccountCachedBalance);
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
const mmiPortfolioEnabled = useSelector(getMmiPortfolioEnabled);
|
||||
const mmiPortfolioUrl = useSelector(getMmiPortfolioUrl);
|
||||
|
||||
const portfolioEvent = () => {
|
||||
trackEvent({
|
||||
category: MetaMetricsEventCategory.Navigation,
|
||||
event: MetaMetricsEventName.MMIPortfolioButtonClicked,
|
||||
});
|
||||
};
|
||||
const renderInstitutionalButtons = () => {
|
||||
return mmiPortfolioEnabled ? (
|
||||
<ButtonSecondary
|
||||
className="token-balance-mmi-portfolio"
|
||||
onClick={() => {
|
||||
portfolioEvent();
|
||||
window.open(mmiPortfolioUrl, '_blank');
|
||||
}}
|
||||
endIconName={IconName.Export}
|
||||
>
|
||||
{t('portfolio')}
|
||||
</ButtonSecondary>
|
||||
) : null;
|
||||
};
|
||||
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
return (
|
||||
<Box
|
||||
className="token-balance-overview"
|
||||
display={Display.Flex}
|
||||
justifyContent={JustifyContent.spaceBetween}
|
||||
alignItems={AlignItems.center}
|
||||
padding={4}
|
||||
>
|
||||
<Box className="token-balance-overview__balance">
|
||||
{balance ? (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className={classnames({
|
||||
'token-balance-overview__cached-secondary-balance':
|
||||
balanceIsCached,
|
||||
'token-balance-overview__secondary-balance': !balanceIsCached,
|
||||
})}
|
||||
data-testid="token-balance-overview__secondary-currency"
|
||||
value={balance}
|
||||
type={PRIMARY}
|
||||
ethNumberOfDecimals={4}
|
||||
textProps={{
|
||||
variant: TextVariant.headingLg,
|
||||
color: TextColor.textDefault,
|
||||
}}
|
||||
suffixProps={{
|
||||
variant: TextVariant.headingLg,
|
||||
color: TextColor.textDefault,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<Box className="token-balance-overview__primary-container">
|
||||
{balance ? (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className={classnames('token-balance-overview__primary-balance', {
|
||||
'token-balance-overview__cached-balance': balanceIsCached,
|
||||
})}
|
||||
data-testid="token-balance-overview__primary-currency"
|
||||
value={balance}
|
||||
type={SECONDARY}
|
||||
ethNumberOfDecimals={4}
|
||||
hideTitle
|
||||
textProps={{
|
||||
variant: TextVariant.bodyMd,
|
||||
color: TextColor.textAlternative,
|
||||
}}
|
||||
suffixProps={{
|
||||
variant: TextVariant.bodyMd,
|
||||
color: TextColor.textAlternative,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Spinner
|
||||
color="var(--color-secondary-default)"
|
||||
className="loading-overlay__spinner"
|
||||
/>
|
||||
)}
|
||||
{balanceIsCached ? (
|
||||
<span className="token-balance-overview__cached-star">*</span>
|
||||
) : null}
|
||||
</Box>
|
||||
</Box>
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
renderInstitutionalButtons()
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-main,build-beta,build-flask)
|
||||
<ButtonSecondary
|
||||
className="token-balance-portfolio"
|
||||
endIconName={IconName.Export}
|
||||
onClick={() => {
|
||||
const url = getPortfolioUrl(
|
||||
'',
|
||||
'ext_portfolio_button',
|
||||
metaMetricsId,
|
||||
);
|
||||
global.platform.openTab({ url });
|
||||
trackEvent({
|
||||
category: MetaMetricsEventCategory.Navigation,
|
||||
event: MetaMetricsEventName.PortfolioLinkClicked,
|
||||
properties: {
|
||||
location: 'Home',
|
||||
text: 'Portfolio',
|
||||
chain_id: chainId,
|
||||
token_symbol: 'ETH',
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('portfolio')}
|
||||
</ButtonSecondary>
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { BalanceOverview } from '.';
|
||||
|
||||
export default {
|
||||
title: 'Components/Multichain/BalanceOverview',
|
||||
component: BalanceOverview,
|
||||
};
|
||||
|
||||
export const DefaultStory = () => <BalanceOverview />;
|
||||
|
||||
DefaultStory.storyName = 'Default';
|
@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import configureStore from '../../../store/store';
|
||||
import { renderWithProvider } from '../../../../test/lib/render-helpers';
|
||||
import mockState from '../../../../test/data/mock-state.json';
|
||||
import { BalanceOverview } from '.';
|
||||
|
||||
const render = () => {
|
||||
const store = configureStore({
|
||||
metamask: {
|
||||
...mockState.metamask,
|
||||
},
|
||||
});
|
||||
return renderWithProvider(<BalanceOverview />, store);
|
||||
};
|
||||
|
||||
describe('Balance Overview and Portfolio for Tokens', () => {
|
||||
it('should match snapshot', () => {
|
||||
const { container } = render();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
1
ui/components/multichain/balance-overview/index.js
Normal file
1
ui/components/multichain/balance-overview/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { BalanceOverview } from './balance-overview';
|
@ -5,6 +5,7 @@ export { AccountPicker } from './account-picker';
|
||||
export { ActivityListItem } from './activity-list-item';
|
||||
export { AppHeader } from './app-header';
|
||||
export { AppFooter } from './app-footer';
|
||||
export { BalanceOverview } from './balance-overview';
|
||||
export { DetectedTokensBanner } from './detected-token-banner';
|
||||
export { GlobalMenu } from './global-menu';
|
||||
export { ImportTokenLink } from './import-token-link';
|
||||
|
@ -781,6 +781,7 @@ export default class Home extends PureComponent {
|
||||
} else if (this.state.notificationClosing || this.state.redirecting) {
|
||||
return null;
|
||||
}
|
||||
const tabPadding = process.env.MULTICHAIN ? 4 : 0; // TODO: Remove tabPadding and add paddingTop={4} to parent container Box of Tabs
|
||||
|
||||
const showWhatsNew =
|
||||
completedOnboarding &&
|
||||
@ -848,23 +849,25 @@ export default class Home extends PureComponent {
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
<div className="home__main-view">
|
||||
<div className="home__balance-wrapper">
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-main,build-beta,build-flask)
|
||||
<EthOverview showAddress />
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
<EthOverview
|
||||
showAddress
|
||||
mmiPortfolioEnabled={mmiPortfolioEnabled}
|
||||
mmiPortfolioUrl={mmiPortfolioUrl}
|
||||
/>
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
</div>
|
||||
<Box style={{ flexGrow: '1' }}>
|
||||
{process.env.MULTICHAIN ? null : (
|
||||
<div className="home__balance-wrapper">
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-main,build-beta,build-flask)
|
||||
<EthOverview showAddress />
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
{
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
<EthOverview
|
||||
showAddress
|
||||
mmiPortfolioEnabled={mmiPortfolioEnabled}
|
||||
mmiPortfolioUrl={mmiPortfolioUrl}
|
||||
/>
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
<Box style={{ flexGrow: '1' }} paddingTop={tabPadding}>
|
||||
<Tabs
|
||||
t={this.context.t}
|
||||
defaultActiveTabKey={defaultHomeActiveTabName}
|
||||
|
Loading…
Reference in New Issue
Block a user