1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Split TransactionViewBalance component (#8637)

The `TransactionViewBalance` component has been split into three
separate components. This was done primarily to make the asset page
easier to implement. Also the name `TransactionViewBalance` didn't
describe this component very well anymore.

Instead of the Ethereum and token-specific logic being in the same
component, the two cases were split into the `EthOverview` and
`TokenOverview` components respectively. They both use the
`WalletOverview` component, which has the structure shared by both
cases.
This commit is contained in:
Mark Stacey 2020-05-21 14:33:48 -03:00 committed by GitHub
parent 57531ad100
commit dd41870f1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 282 additions and 291 deletions

View File

@ -163,7 +163,7 @@ describe('MetaMask', function () {
})
it('balance renders', async function () {
const balance = await driver.findElement(By.css('.transaction-view-balance__primary-balance'))
const balance = await driver.findElement(By.css('.eth-overview__primary-balance'))
await driver.wait(until.elementTextMatches(balance, /100\s*ETH/))
await driver.delay(regularDelayMs)
})

View File

@ -751,7 +751,7 @@ describe('MetaMask', function () {
})
it('renders the correct ETH balance', async function () {
const balance = await driver.findElement(By.css('.transaction-view-balance__primary-balance'))
const balance = await driver.findElement(By.css('.eth-overview__primary-balance'))
await driver.delay(regularDelayMs)
await driver.wait(until.elementTextMatches(balance, /^87.*\s*ETH.*$/), 10000)
const tokenAmount = await balance.getText()
@ -829,7 +829,7 @@ describe('MetaMask', function () {
})
it('renders the balance for the new token', async function () {
const balance = await driver.findElement(By.css('.transaction-view-balance .transaction-view-balance__primary-balance'))
const balance = await driver.findElement(By.css('.wallet-overview .token-overview__primary-balance'))
await driver.wait(until.elementTextMatches(balance, /^10.000\s*TST\s*$/))
const tokenAmount = await balance.getText()
assert.ok(/^10.000\s*TST\s*$/.test(tokenAmount))
@ -996,7 +996,7 @@ describe('MetaMask', function () {
await driver.clickElement(By.css('.token-cell'))
await driver.delay(1000)
const tokenBalanceAmount = await driver.findElements(By.css('.transaction-view-balance__primary-balance'))
const tokenBalanceAmount = await driver.findElements(By.css('.token-overview__primary-balance'))
await driver.wait(until.elementTextMatches(tokenBalanceAmount[0], /7.500\s*TST/), 10000)
})
})
@ -1254,7 +1254,7 @@ describe('MetaMask', function () {
})
it('renders the balance for the chosen token', async function () {
const balance = await driver.findElement(By.css('.transaction-view-balance__primary-balance'))
const balance = await driver.findElement(By.css('.token-overview__primary-balance'))
await driver.wait(until.elementTextMatches(balance, /0\s*BAT/))
await driver.delay(regularDelayMs)
})

View File

@ -15,7 +15,7 @@ describe('MetaMask Browser Extension', function () {
const passwordField = await driver.findElement(By.css('#password'))
await passwordField.sendKeys('correct horse battery staple')
await passwordField.sendKeys(Key.ENTER)
await driver.clickElement(By.css('[data-testid="transaction-view-send"]'))
await driver.clickElement(By.css('[data-testid="eth-overview-send"]'))
const recipientAddressField = await driver.findElement(By.css('[data-testid="ens-input"]'))
await recipientAddressField.sendKeys('0x985c30949c92df7a0bd42e0f3e3d539ece98db24')
const amountField = await driver.findElement(By.css('.unit-input__input'))

View File

@ -37,7 +37,7 @@ async function runCurrencyLocalizationTest (assert) {
reactTriggerChange(selectState[0])
await timeout(1000)
const txView = await queryAsync($, '.home__main-view')
const heroBalance = await findAsync($(txView), '.transaction-view-balance__balance')
const fiatAmount = await findAsync($(heroBalance), '.transaction-view-balance__secondary-balance')
const heroBalance = await findAsync($(txView), '.eth-overview__balance')
const fiatAmount = await findAsync($(heroBalance), '.eth-overview__secondary-balance')
assert.equal(fiatAmount[0].textContent, '₱102,707.97PHP')
}

View File

@ -62,8 +62,6 @@
@import 'transaction-breakdown/index';
@import 'transaction-view-balance/index';
@import 'transaction-list/index';
@import 'transaction-list-item/index';
@ -111,3 +109,5 @@
@import 'permissions-connect-header/index';
@import 'permissions-connect-footer/index';
@import 'wallet-overview/index';

View File

@ -1 +0,0 @@
export { default } from './transaction-view-balance.container'

View File

@ -1,76 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import sinon from 'sinon'
import TokenBalance from '../../../ui/token-balance'
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display'
import { SEND_ROUTE } from '../../../../helpers/constants/routes'
import TransactionViewBalance from '../transaction-view-balance.component'
const propsMethodSpies = {
showDepositModal: sinon.spy(),
}
const historySpies = {
push: sinon.spy(),
}
const t = (str1, str2) => (str2 ? str1 + str2 : str1)
const metricsEvent = () => ({})
describe('TransactionViewBalance Component', function () {
afterEach(function () {
propsMethodSpies.showDepositModal.resetHistory()
historySpies.push.resetHistory()
})
it('should render ETH balance properly', function () {
const wrapper = shallow((
<TransactionViewBalance
showDepositModal={propsMethodSpies.showDepositModal}
history={historySpies}
network="3"
ethBalance={123}
fiatBalance={456}
currentCurrency="usd"
/>
), { context: { t, metricsEvent } })
assert.equal(wrapper.find('.transaction-view-balance').length, 1)
assert.equal(wrapper.find('.transaction-view-balance__button').length, 2)
assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 2)
const buttons = wrapper.find('.transaction-view-balance__buttons')
assert.equal(propsMethodSpies.showDepositModal.callCount, 0)
buttons.childAt(0).simulate('click')
assert.equal(propsMethodSpies.showDepositModal.callCount, 1)
assert.equal(historySpies.push.callCount, 0)
buttons.childAt(1).simulate('click')
assert.equal(historySpies.push.callCount, 1)
assert.equal(historySpies.push.getCall(0).args[0], SEND_ROUTE)
})
it('should render token balance properly', function () {
const token = {
address: '0x35865238f0bec9d5ce6abff0fdaebe7b853dfcc5',
decimals: '2',
symbol: 'ABC',
}
const wrapper = shallow((
<TransactionViewBalance
showDepositModal={propsMethodSpies.showDepositModal}
history={historySpies}
network="3"
ethBalance={123}
fiatBalance={456}
currentCurrency="usd"
selectedToken={token}
/>
), { context: { t } })
assert.equal(wrapper.find('.transaction-view-balance').length, 1)
assert.equal(wrapper.find('.transaction-view-balance__button').length, 1)
assert.equal(wrapper.find(TokenBalance).length, 1)
})
})

View File

@ -1,144 +0,0 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import Button from '../../ui/button'
import Identicon from '../../ui/identicon'
import TokenBalance from '../../ui/token-balance'
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
import { SEND_ROUTE } from '../../../helpers/constants/routes'
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common'
import Tooltip from '../../ui/tooltip-v2'
export default class TransactionViewBalance extends PureComponent {
static contextTypes = {
t: PropTypes.func,
metricsEvent: PropTypes.func,
}
static propTypes = {
showDepositModal: PropTypes.func,
selectedToken: PropTypes.object,
history: PropTypes.object,
balance: PropTypes.string,
assetImage: PropTypes.string,
balanceIsCached: PropTypes.bool,
showFiat: PropTypes.bool,
}
static defaultProps = {
showFiat: true,
}
renderBalance () {
const { selectedToken, balance, balanceIsCached, showFiat } = this.props
return selectedToken
? (
<div className="transaction-view-balance__balance">
<TokenBalance
token={selectedToken}
withSymbol
className="transaction-view-balance__primary-balance"
/>
</div>
) : (
<Tooltip position="top" title={this.context.t('balanceOutdated')} disabled={!balanceIsCached}>
<div className="transaction-view-balance__balance">
<div className="transaction-view-balance__primary-container">
<UserPreferencedCurrencyDisplay
className={classnames('transaction-view-balance__primary-balance', {
'transaction-view-balance__cached-balance': balanceIsCached,
})}
value={balance}
type={PRIMARY}
ethNumberOfDecimals={4}
hideTitle
/>
{
balanceIsCached ? <span className="transaction-view-balance__cached-star">*</span> : null
}
</div>
{
showFiat && (
<UserPreferencedCurrencyDisplay
className={classnames({
'transaction-view-balance__cached-secondary-balance': balanceIsCached,
'transaction-view-balance__secondary-balance': !balanceIsCached,
})}
value={balance}
type={SECONDARY}
ethNumberOfDecimals={4}
hideTitle
/>
)
}
</div>
</Tooltip>
)
}
renderButtons () {
const { t, metricsEvent } = this.context
const { selectedToken, showDepositModal, history } = this.props
return (
<div className="transaction-view-balance__buttons">
{
!selectedToken && (
<Button
type="secondary"
className="transaction-view-balance__button"
onClick={() => {
metricsEvent({
eventOpts: {
category: 'Navigation',
action: 'Home',
name: 'Clicked Deposit',
},
})
showDepositModal()
}}
>
{ t('deposit') }
</Button>
)
}
<Button
type="secondary"
className="transaction-view-balance__button"
onClick={() => {
metricsEvent({
eventOpts: {
category: 'Navigation',
action: 'Home',
name: selectedToken ? 'Clicked Send: Token' : 'Clicked Send: Eth',
},
})
history.push(SEND_ROUTE)
}}
data-testid="transaction-view-send"
>
{ t('send') }
</Button>
</div>
)
}
render () {
const { selectedToken, assetImage } = this.props
return (
<div className="transaction-view-balance">
<div className="transaction-view-balance__balance-container">
<Identicon
diameter={50}
address={selectedToken && selectedToken.address}
image={assetImage}
/>
{ this.renderBalance() }
</div>
{ this.renderButtons() }
</div>
)
}
}

View File

@ -1,44 +0,0 @@
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import { compose } from 'redux'
import TransactionViewBalance from './transaction-view-balance.component'
import {
getSelectedToken,
getSelectedAddress,
getNativeCurrency,
getSelectedTokenAssetImage,
getMetaMaskAccounts,
isBalanceCached,
preferencesSelector,
getIsMainnet,
} from '../../../selectors'
import { showModal } from '../../../store/actions'
const mapStateToProps = (state) => {
const { showFiatInTestnets } = preferencesSelector(state)
const isMainnet = getIsMainnet(state)
const selectedAddress = getSelectedAddress(state)
const accounts = getMetaMaskAccounts(state)
const account = accounts[selectedAddress]
const { balance } = account
return {
selectedToken: getSelectedToken(state),
balance,
nativeCurrency: getNativeCurrency(state),
assetImage: getSelectedTokenAssetImage(state),
balanceIsCached: isBalanceCached(state),
showFiat: (isMainnet || !!showFiatInTestnets),
}
}
const mapDispatchToProps = (dispatch) => {
return {
showDepositModal: () => dispatch(showModal({ name: 'DEPOSIT_ETHER' })),
}
}
export default compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
)(TransactionViewBalance)

View File

@ -0,0 +1,111 @@
import React, { useContext } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import classnames from 'classnames'
import { useHistory } from 'react-router-dom'
import Button from '../../ui/button'
import Identicon from '../../ui/identicon'
import { I18nContext } from '../../../contexts/i18n'
import WalletOverview from './wallet-overview'
import { SEND_ROUTE } from '../../../helpers/constants/routes'
import { useMetricEvent } from '../../../hooks/useMetricEvent'
import Tooltip from '../../ui/tooltip-v2'
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common'
import { showModal } from '../../../store/actions'
import { isBalanceCached, getSelectedAccount, getShouldShowFiat } from '../../../selectors/selectors'
const EthOverview = () => {
const dispatch = useDispatch()
const t = useContext(I18nContext)
const sendEvent = useMetricEvent({
eventOpts: {
category: 'Navigation',
action: 'Home',
name: 'Clicked Send: Eth',
},
})
const depositEvent = useMetricEvent({
eventOpts: {
category: 'Navigation',
action: 'Home',
name: 'Clicked Deposit',
},
})
const history = useHistory()
const balanceIsCached = useSelector(isBalanceCached)
const showFiat = useSelector(getShouldShowFiat)
const selectedAccount = useSelector(getSelectedAccount)
const { balance } = selectedAccount
return (
<WalletOverview
balance={(
<Tooltip position="top" title={t('balanceOutdated')} disabled={!balanceIsCached}>
<div className="eth-overview__balance">
<div className="eth-overview__primary-container">
<UserPreferencedCurrencyDisplay
className={classnames('eth-overview__primary-balance', {
'eth-overview__cached-balance': balanceIsCached,
})}
value={balance}
type={PRIMARY}
ethNumberOfDecimals={4}
hideTitle
/>
{
balanceIsCached ? <span className="eth-overview__cached-star">*</span> : null
}
</div>
{
showFiat && (
<UserPreferencedCurrencyDisplay
className={classnames({
'eth-overview__cached-secondary-balance': balanceIsCached,
'eth-overview__secondary-balance': !balanceIsCached,
})}
value={balance}
type={SECONDARY}
ethNumberOfDecimals={4}
hideTitle
/>
)
}
</div>
</Tooltip>
)}
buttons={(
<>
<Button
type="secondary"
className="eth-overview__button"
onClick={() => {
depositEvent()
dispatch(showModal({ name: 'DEPOSIT_ETHER' }))
}}
>
{ t('deposit') }
</Button>
<Button
type="secondary"
className="eth-overview__button"
onClick={() => {
sendEvent()
history.push(SEND_ROUTE)
}}
data-testid="eth-overview-send"
>
{ t('send') }
</Button>
</>
)}
icon={<Identicon diameter={50} />}
/>
)
}
EthOverview.propTypes = {
}
export default EthOverview

View File

@ -0,0 +1,2 @@
export { default as EthOverview } from './eth-overview'
export { default as TokenOverview } from './token-overview'

View File

@ -1,4 +1,4 @@
.transaction-view-balance {
.wallet-overview {
display: flex;
justify-content: space-between;
align-items: center;
@ -12,6 +12,30 @@
width: 100%;
}
&__balance {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
min-width: 0;
@media screen and (max-width: $break-small) {
flex-direction: column;
width: 100%;
}
}
&__buttons {
display: flex;
flex-direction: row;
@media screen and (max-width: $break-small) {
margin-bottom: 16px;
}
}
}
.eth-overview {
&__balance {
margin: 0 12px;
display: flex;
@ -40,7 +64,6 @@
line-height: 45px;
width: 100%;
justify-content: center;
}
}
@ -65,25 +88,41 @@
color: $Grey-400;
}
&__balance-container {
flex: 1;
&__button {
min-width: initial;
width: 100px;
&:not(:last-child) {
margin-right: 12px;
}
}
}
.token-overview {
&__balance {
margin: 0 12px;
display: flex;
flex-direction: row;
align-items: center;
flex-direction: column;
min-width: 0;
position: relative;
@media screen and (max-width: $break-small) {
flex-direction: column;
width: 100%;
align-items: center;
margin: 16px 0;
padding: 0 16px;
max-width: 100%;
}
}
&__buttons {
display: flex;
flex-direction: row;
&__primary-balance {
font-size: 1.5rem;
color: $black;
@media screen and (max-width: $break-small) {
margin-bottom: 16px;
font-size: 32px;
line-height: 45px;
width: 100%;
justify-content: center;
}
}

View File

@ -0,0 +1,69 @@
import React, { useContext } from 'react'
import PropTypes from 'prop-types'
import { useSelector } from 'react-redux'
import { useHistory } from 'react-router-dom'
import Button from '../../ui/button'
import Identicon from '../../ui/identicon'
import TokenBalance from '../../ui/token-balance'
import { I18nContext } from '../../../contexts/i18n'
import WalletOverview from './wallet-overview'
import { SEND_ROUTE } from '../../../helpers/constants/routes'
import { useMetricEvent } from '../../../hooks/useMetricEvent'
import { getAssetImages } from '../../../selectors/selectors'
const TokenOverview = ({ token }) => {
const t = useContext(I18nContext)
const sendTokenEvent = useMetricEvent({
eventOpts: {
category: 'Navigation',
action: 'Home',
name: 'Clicked Send: Token',
},
})
const history = useHistory()
const assetImages = useSelector(getAssetImages)
return (
<WalletOverview
balance={(
<div className="token-overview__balance">
<TokenBalance
token={token}
withSymbol
className="token-overview__primary-balance"
/>
</div>
)}
buttons={(
<Button
type="secondary"
className="token-overview__button"
onClick={() => {
sendTokenEvent()
history.push(SEND_ROUTE)
}}
>
{ t('send') }
</Button>
)}
icon={(
<Identicon
diameter={50}
address={token.address}
image={assetImages[token.address]}
/>
)}
/>
)
}
TokenOverview.propTypes = {
token: PropTypes.shape({
address: PropTypes.string.isRequired,
decimals: PropTypes.number,
symbol: PropTypes.string,
}).isRequired,
}
export default TokenOverview

View File

@ -0,0 +1,24 @@
import React from 'react'
import PropTypes from 'prop-types'
const WalletOverview = ({ balance, buttons, icon }) => {
return (
<div className="wallet-overview">
<div className="wallet-overview__balance">
{ icon }
{ balance }
</div>
<div className="wallet-overview__buttons">
{ buttons }
</div>
</div>
)
}
WalletOverview.propTypes = {
balance: PropTypes.element.isRequired,
buttons: PropTypes.element.isRequired,
icon: PropTypes.element.isRequired,
}
export default WalletOverview

View File

@ -8,13 +8,13 @@ import HomeNotification from '../../components/app/home-notification'
import MultipleNotifications from '../../components/app/multiple-notifications'
import WalletView from '../../components/app/wallet-view'
import TransactionList from '../../components/app/transaction-list'
import TransactionViewBalance from '../../components/app/transaction-view-balance'
import MenuBar from '../../components/app/menu-bar'
import Popover from '../../components/ui/popover'
import Button from '../../components/ui/button'
import ConnectedSites from '../connected-sites'
import ConnectedAccounts from '../connected-accounts'
import { Tabs, Tab } from '../../components/ui/tabs'
import { EthOverview, TokenOverview } from '../../components/app/wallet-overview'
import {
RESTORE_VAULT_ROUTE,
@ -51,6 +51,11 @@ export default class Home extends PureComponent {
totalUnapprovedCount: PropTypes.number.isRequired,
setConnectedStatusPopoverHasBeenShown: PropTypes.func,
connectedStatusPopoverHasBeenShown: PropTypes.bool,
selectedToken: PropTypes.shape({
address: PropTypes.string.isRequired,
decimals: PropTypes.number,
symbol: PropTypes.string,
}),
}
UNSAFE_componentWillMount () {
@ -208,6 +213,7 @@ export default class Home extends PureComponent {
history,
connectedStatusPopoverHasBeenShown,
isPopup,
selectedToken,
} = this.props
if (forgottenPassword) {
@ -218,6 +224,10 @@ export default class Home extends PureComponent {
return null
}
const homeOverview = selectedToken
? <TokenOverview token={selectedToken} />
: <EthOverview />
return (
<div className="main-container">
<Route path={CONNECTED_ROUTE} component={ConnectedSites} exact />
@ -235,7 +245,7 @@ export default class Home extends PureComponent {
<WalletView />
<div className="home__main-view">
<div className="home__balance-wrapper">
<TransactionViewBalance />
{ homeOverview }
</div>
<TransactionList isWideViewport />
</div>
@ -245,7 +255,7 @@ export default class Home extends PureComponent {
<div className="home__main-view">
<MenuBar />
<div className="home__balance-wrapper">
<TransactionViewBalance />
{ homeOverview }
</div>
<Tabs>
<Tab

View File

@ -4,7 +4,7 @@ import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import {
unconfirmedTransactionsCountSelector,
getSelectedToken,
getCurrentEthBalance,
getFirstPermissionRequest,
getTotalUnapprovedCount,
@ -58,6 +58,7 @@ const mapStateToProps = (state) => {
threeBoxSynced,
showRestorePrompt,
selectedAddress,
selectedToken: getSelectedToken(state),
threeBoxLastUpdated,
firstPermissionsRequestId,
totalUnapprovedCount,