mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Refactor asset list items (#8586)
All asset list items now use the same component (`AssetListItem`). Previously the tokens and the Ethereum balance were totally separate components, despite being styled similarly. Various unnecessary DOM elements and style rules were removed, but the overall list looks identical to how it looked before.
This commit is contained in:
parent
f64106ce21
commit
0ca5d1dc8a
@ -151,7 +151,7 @@ describe('MetaMask', function () {
|
||||
})
|
||||
|
||||
it('balance renders', async function () {
|
||||
const balance = await driver.findElement(By.css('.balance-display .token-amount'))
|
||||
const balance = await driver.findElement(By.css('[data-testid="wallet-balance"] .asset-list__primary-amount'))
|
||||
await driver.wait(until.elementTextMatches(balance, /25\s*ETH/))
|
||||
await driver.delay(regularDelayMs)
|
||||
})
|
||||
|
@ -206,7 +206,7 @@ describe('MetaMask', function () {
|
||||
})
|
||||
|
||||
it('balance renders', async function () {
|
||||
const balance = await driver.findElement(By.css('.balance-display .token-amount'))
|
||||
const balance = await driver.findElement(By.css('[data-testid="wallet-balance"] .asset-list__primary-amount'))
|
||||
await driver.wait(until.elementTextMatches(balance, /100\s*ETH/))
|
||||
await driver.delay(regularDelayMs)
|
||||
})
|
||||
@ -991,7 +991,7 @@ describe('MetaMask', function () {
|
||||
const txStatuses = await driver.findElements(By.css('.transaction-list-item__action'))
|
||||
await driver.wait(until.elementTextMatches(txStatuses[0], /Sent\sToken/), 10000)
|
||||
|
||||
await driver.clickElement(By.css('.wallet-balance'))
|
||||
await driver.clickElement(By.css('[data-testid="wallet-balance"]'))
|
||||
|
||||
await driver.clickElement(By.css('.token-cell'))
|
||||
await driver.delay(1000)
|
||||
|
@ -96,7 +96,7 @@ describe('MetaMask', function () {
|
||||
})
|
||||
|
||||
it('balance renders', async function () {
|
||||
const balance = await driver.findElement(By.css('.balance-display .token-amount'))
|
||||
const balance = await driver.findElement(By.css('[data-testid="wallet-balance"] .asset-list__primary-amount'))
|
||||
await driver.wait(until.elementTextMatches(balance, /25\s*ETH/))
|
||||
await driver.delay(regularDelayMs)
|
||||
})
|
||||
@ -202,7 +202,7 @@ describe('MetaMask', function () {
|
||||
})
|
||||
|
||||
it('balance renders', async function () {
|
||||
const balance = await driver2.findElement(By.css('.balance-display .token-amount'))
|
||||
const balance = await driver2.findElement(By.css('[data-testid="wallet-balance"] .asset-list__primary-amount'))
|
||||
await driver2.wait(until.elementTextMatches(balance, /25\s*ETH/))
|
||||
await driver2.delay(regularDelayMs)
|
||||
})
|
||||
|
67
ui/app/components/app/asset-list-item/asset-list-item.js
Normal file
67
ui/app/components/app/asset-list-item/asset-list-item.js
Normal file
@ -0,0 +1,67 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import Identicon from '../../ui/identicon'
|
||||
|
||||
const AssetListItem = ({
|
||||
active,
|
||||
children,
|
||||
className,
|
||||
'data-testid': dataTestId,
|
||||
iconClassName,
|
||||
menu,
|
||||
onClick,
|
||||
tokenAddress,
|
||||
tokenImage,
|
||||
warning,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={classnames('asset-list-item__container', className, {
|
||||
'asset-list-item__container--active': active,
|
||||
})}
|
||||
data-testid={dataTestId}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Identicon
|
||||
className={iconClassName}
|
||||
diameter={50}
|
||||
address={tokenAddress}
|
||||
image={tokenImage}
|
||||
/>
|
||||
<div
|
||||
className="asset-list-item__balance"
|
||||
>
|
||||
{ children }
|
||||
</div>
|
||||
{ warning }
|
||||
{ menu }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
AssetListItem.propTypes = {
|
||||
active: PropTypes.bool,
|
||||
children: PropTypes.node.isRequired,
|
||||
className: PropTypes.string,
|
||||
'data-testid': PropTypes.string,
|
||||
iconClassName: PropTypes.string,
|
||||
menu: PropTypes.node,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
tokenAddress: PropTypes.string,
|
||||
tokenImage: PropTypes.string,
|
||||
warning: PropTypes.node,
|
||||
}
|
||||
|
||||
AssetListItem.defaultProps = {
|
||||
active: undefined,
|
||||
className: undefined,
|
||||
'data-testid': undefined,
|
||||
menu: undefined,
|
||||
iconClassName: undefined,
|
||||
tokenAddress: undefined,
|
||||
tokenImage: undefined,
|
||||
warning: undefined,
|
||||
}
|
||||
|
||||
export default AssetListItem
|
20
ui/app/components/app/asset-list-item/asset-list-item.scss
Normal file
20
ui/app/components/app/asset-list-item/asset-list-item.scss
Normal file
@ -0,0 +1,20 @@
|
||||
.asset-list-item {
|
||||
&__container {
|
||||
display: flex;
|
||||
padding: 20px 25px;
|
||||
align-items: center;
|
||||
|
||||
&--active {
|
||||
background: $manatee;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
&__balance {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 15px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
1
ui/app/components/app/asset-list-item/index.js
Normal file
1
ui/app/components/app/asset-list-item/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './asset-list-item'
|
@ -1,10 +1,11 @@
|
||||
import classnames from 'classnames'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { Component } from 'react'
|
||||
import BalanceComponent from '../../ui/balance'
|
||||
import AddTokenButton from '../add-token-button'
|
||||
import TokenList from '../token-list'
|
||||
import { ADD_TOKEN_ROUTE } from '../../../helpers/constants/routes'
|
||||
import AssetListItem from '../asset-list-item'
|
||||
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common'
|
||||
|
||||
export default class AssetList extends Component {
|
||||
static contextTypes = {
|
||||
@ -18,32 +19,44 @@ export default class AssetList extends Component {
|
||||
|
||||
static propTypes = {
|
||||
history: PropTypes.object.isRequired,
|
||||
selectedAccountBalance: PropTypes.string,
|
||||
selectedTokenAddress: PropTypes.string,
|
||||
setSelectedToken: PropTypes.func.isRequired,
|
||||
showFiat: PropTypes.bool.isRequired,
|
||||
unsetSelectedToken: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
renderWalletBalance () {
|
||||
const {
|
||||
selectedAccountBalance,
|
||||
selectedTokenAddress,
|
||||
showFiat,
|
||||
unsetSelectedToken,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('flex-column', 'wallet-balance-wrapper', {
|
||||
'wallet-balance-wrapper--active': !selectedTokenAddress,
|
||||
})}
|
||||
<AssetListItem
|
||||
active={!selectedTokenAddress}
|
||||
onClick={unsetSelectedToken}
|
||||
data-testid="wallet-balance"
|
||||
>
|
||||
<div
|
||||
className="wallet-balance"
|
||||
onClick={() => {
|
||||
unsetSelectedToken()
|
||||
}}
|
||||
>
|
||||
<BalanceComponent />
|
||||
</div>
|
||||
</div>
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="asset-list__primary-amount"
|
||||
ethNumberOfDecimals={4}
|
||||
type={PRIMARY}
|
||||
value={selectedAccountBalance}
|
||||
/>
|
||||
{
|
||||
showFiat && (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="asset-list__secondary-amount"
|
||||
ethNumberOfDecimals={4}
|
||||
type={SECONDARY}
|
||||
value={selectedAccountBalance}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</AssetListItem>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -3,10 +3,13 @@ import { withRouter } from 'react-router-dom'
|
||||
import { compose } from 'redux'
|
||||
import AssetList from './asset-list.component'
|
||||
import { setSelectedToken } from '../../../store/actions'
|
||||
import { getCurrentAccountWithSendEtherInfo, getShouldShowFiat } from '../../../selectors/selectors'
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
selectedAccountBalance: getCurrentAccountWithSendEtherInfo(state).balance,
|
||||
selectedTokenAddress: state.metamask.selectedTokenAddress,
|
||||
showFiat: getShouldShowFiat(state),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,64 +1,10 @@
|
||||
$wallet-balance-bg: #e7e7e7;
|
||||
$wallet-balance-breakpoint: 890px;
|
||||
$wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (max-width: #{$wallet-balance-breakpoint})";
|
||||
.asset-list {
|
||||
&__primary-amount {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.wallet-balance-wrapper {
|
||||
flex: 0 0 auto;
|
||||
transition: linear 200ms;
|
||||
background: rgba($wallet-balance-bg, 0);
|
||||
|
||||
&--active {
|
||||
background: $manatee;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.wallet-balance {
|
||||
background: inherit;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
cursor: pointer;
|
||||
border-top: 1px solid $wallet-balance-bg;
|
||||
|
||||
.balance-container {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin: 20px 24px;
|
||||
flex-direction: row;
|
||||
min-width: 0;
|
||||
|
||||
@media #{$wallet-balance-breakpoint-range} {
|
||||
margin: 10% 4%;
|
||||
}
|
||||
}
|
||||
|
||||
.balance-display {
|
||||
margin-left: 15px;
|
||||
min-width: 0;
|
||||
|
||||
.token-amount {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.fiat-amount {
|
||||
margin-top: .25%;
|
||||
font-size: 105%;
|
||||
}
|
||||
|
||||
@media #{$wallet-balance-breakpoint-range} {
|
||||
margin-left: 4%;
|
||||
|
||||
.token-amount {
|
||||
font-size: 105%;
|
||||
}
|
||||
|
||||
.fiat-amount {
|
||||
font-size: 95%;
|
||||
}
|
||||
}
|
||||
&__secondary-amount {
|
||||
margin-top: .25%;
|
||||
font-size: 105%;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
@import 'asset-list/asset-list';
|
||||
|
||||
@import 'asset-list-item/asset-list-item';
|
||||
|
||||
@import '../ui/breadcrumbs/index';
|
||||
|
||||
@import '../ui/button-group/index';
|
||||
|
@ -1,11 +1,11 @@
|
||||
import classnames from 'classnames'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { Component } from 'react'
|
||||
import Identicon from '../../ui/identicon'
|
||||
import { conversionUtil, multiplyCurrencies } from '../../../helpers/utils/conversion-util'
|
||||
import TokenMenuDropdown from '../dropdowns/token-menu-dropdown.js'
|
||||
import Tooltip from '../../ui/tooltip-v2'
|
||||
import { I18nContext } from '../../../contexts/i18n'
|
||||
import AssetListItem from '../asset-list-item'
|
||||
|
||||
export default class TokenCell extends Component {
|
||||
static contextType = I18nContext
|
||||
@ -71,55 +71,8 @@ export default class TokenCell extends Component {
|
||||
|
||||
const showFiat = Boolean(currentTokenInFiat) && currentCurrency.toUpperCase() !== symbol
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('token-cell', {
|
||||
'token-cell--active': selectedTokenAddress === address,
|
||||
'token-cell--outdated': outdatedBalance,
|
||||
})}
|
||||
onClick={onClick.bind(null, address)}
|
||||
>
|
||||
<Identicon
|
||||
className="token-cell__identicon"
|
||||
diameter={50}
|
||||
address={address}
|
||||
image={image}
|
||||
/>
|
||||
<div className="token-cell__balance-ellipsis">
|
||||
<div className="token-cell__balance-wrapper">
|
||||
<div className="token-cell__token-balance">{string || 0}</div>
|
||||
<div className="token-cell__token-symbol">{symbol}</div>
|
||||
{showFiat && (
|
||||
<div className="token-cell__fiat-amount">
|
||||
{formattedFiat}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
outdatedBalance && (
|
||||
<Tooltip
|
||||
interactive
|
||||
position="bottom"
|
||||
html={(
|
||||
<div className="token-cell__outdated-tooltip">
|
||||
{ t('troubleTokenBalances') }
|
||||
<a
|
||||
href={`https://ethplorer.io/address/${userAddress}`}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
style={{ color: '#F7861C' }}
|
||||
>
|
||||
{ t('here') }
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<i className={classnames(['fa', 'fa-exclamation-circle', 'token-cell__outdated-icon'])} />
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
const menu = (
|
||||
<>
|
||||
<div>
|
||||
<i
|
||||
className="fa fa-ellipsis-h fa-lg token-cell__ellipsis cursor-pointer"
|
||||
@ -135,7 +88,54 @@ export default class TokenCell extends Component {
|
||||
token={{ symbol, address }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
const warning = outdatedBalance
|
||||
? (
|
||||
<Tooltip
|
||||
interactive
|
||||
position="bottom"
|
||||
html={(
|
||||
<div className="token-cell__outdated-tooltip">
|
||||
{ t('troubleTokenBalances') }
|
||||
<a
|
||||
href={`https://ethplorer.io/address/${userAddress}`}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
style={{ color: '#F7861C' }}
|
||||
>
|
||||
{ t('here') }
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<i className={classnames(['fa', 'fa-exclamation-circle', 'token-cell__outdated-icon'])} />
|
||||
</Tooltip>
|
||||
)
|
||||
: null
|
||||
|
||||
return (
|
||||
<AssetListItem
|
||||
active={selectedTokenAddress === address}
|
||||
className={classnames('token-cell', { 'token-cell--outdated': outdatedBalance })}
|
||||
iconClassName="token-cell__icon"
|
||||
menu={menu}
|
||||
onClick={onClick.bind(null, address)}
|
||||
tokenAddress={address}
|
||||
tokenImage={image}
|
||||
warning={warning}
|
||||
>
|
||||
<div className="token-cell__balance-wrapper">
|
||||
<div className="token-cell__token-balance">{string || 0}</div>
|
||||
<div className="token-cell__token-symbol">{symbol}</div>
|
||||
{showFiat && (
|
||||
<div className="token-cell__fiat-amount">
|
||||
{formattedFiat}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</AssetListItem>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,8 @@ $wallet-balance-breakpoint: 890px;
|
||||
$wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (max-width: #{$wallet-balance-breakpoint})";
|
||||
|
||||
.token-cell {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
padding: 20px 24px;
|
||||
cursor: pointer;
|
||||
transition: linear 200ms;
|
||||
background-color: rgba($wallet-balance-bg, 0);
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
&__token-balance {
|
||||
margin-right: 4px;
|
||||
@ -42,37 +34,10 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and (
|
||||
}
|
||||
}
|
||||
|
||||
@media #{$wallet-balance-breakpoint-range} {
|
||||
padding: 10% 4%;
|
||||
}
|
||||
|
||||
&--active {
|
||||
background-color: $manatee;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&__identicon {
|
||||
margin-right: 15px;
|
||||
border: '1px solid #dedede';
|
||||
min-width: 50px;
|
||||
|
||||
@media #{$wallet-balance-breakpoint-range} {
|
||||
margin-right: 4%;
|
||||
}
|
||||
}
|
||||
|
||||
&--outdated > &__identicon {
|
||||
&--outdated &__icon {
|
||||
opacity: 0.5
|
||||
}
|
||||
|
||||
&__balance-ellipsis {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&--outdated > &__balance-ellipsis {
|
||||
&--outdated &__balance-wrapper {
|
||||
opacity: 0.5
|
||||
}
|
||||
|
||||
|
@ -1,48 +0,0 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Identicon from '../identicon'
|
||||
import UserPreferencedCurrencyDisplay from '../../app/user-preferenced-currency-display'
|
||||
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common'
|
||||
|
||||
export default class Balance extends PureComponent {
|
||||
static propTypes = {
|
||||
account: PropTypes.object,
|
||||
showFiat: PropTypes.bool,
|
||||
}
|
||||
|
||||
renderBalance () {
|
||||
const { account, showFiat } = this.props
|
||||
const balanceValue = account && account.balance
|
||||
|
||||
return (
|
||||
<div className="flex-column balance-display">
|
||||
<UserPreferencedCurrencyDisplay
|
||||
className="token-amount"
|
||||
value={balanceValue}
|
||||
type={PRIMARY}
|
||||
ethNumberOfDecimals={4}
|
||||
/>
|
||||
{
|
||||
showFiat && (
|
||||
<UserPreferencedCurrencyDisplay
|
||||
value={balanceValue}
|
||||
type={SECONDARY}
|
||||
ethNumberOfDecimals={4}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className="balance-container">
|
||||
<Identicon
|
||||
diameter={50}
|
||||
/>
|
||||
{ this.renderBalance() }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
import { connect } from 'react-redux'
|
||||
import Balance from './balance.component'
|
||||
import {
|
||||
getMetaMaskAccounts,
|
||||
getIsMainnet,
|
||||
preferencesSelector,
|
||||
} from '../../../selectors'
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { showFiatInTestnets } = preferencesSelector(state)
|
||||
const isMainnet = getIsMainnet(state)
|
||||
const accounts = getMetaMaskAccounts(state)
|
||||
const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
|
||||
const account = accounts[selectedAddress]
|
||||
|
||||
return {
|
||||
account,
|
||||
showFiat: (isMainnet || !!showFiatInTestnets),
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(Balance)
|
@ -1 +0,0 @@
|
||||
export { default } from './balance.container'
|
Loading…
Reference in New Issue
Block a user