diff --git a/js/src/ui/AccountCard/accountCard.css b/js/src/ui/AccountCard/accountCard.css index c3cd17e7d..fcf1522f5 100644 --- a/js/src/ui/AccountCard/accountCard.css +++ b/js/src/ui/AccountCard/accountCard.css @@ -20,8 +20,8 @@ margin: 0.5em 0; display: flex; - flex-direction: row; - align-items: center; + flex-direction: column; + align-items: flex-start; background-color: rgba(0, 0, 0, 0.8); @@ -53,6 +53,13 @@ } } +.infoContainer { + display: flex; + flex-direction: row; + margin-bottom: 0.5em; + width: 100%; +} + .description { font-size: 0.75em; color: rgba(255, 255, 255, 0.5); @@ -86,14 +93,10 @@ .accountName { font-weight: 700 !important; } - } .balance { - .tag { - margin-left: 0.5em; - font-size: 0.85em; - } + margin-top: 0; } @keyframes copied { diff --git a/js/src/ui/AccountCard/accountCard.js b/js/src/ui/AccountCard/accountCard.js index 716440568..61d88a37f 100644 --- a/js/src/ui/AccountCard/accountCard.js +++ b/js/src/ui/AccountCard/accountCard.js @@ -18,21 +18,20 @@ import React, { Component, PropTypes } from 'react'; import ReactDOM from 'react-dom'; import keycode from 'keycode'; +import Balance from '~/ui/Balance'; import IdentityIcon from '~/ui/IdentityIcon'; import IdentityName from '~/ui/IdentityName'; import Tags from '~/ui/Tags'; -import { fromWei } from '~/api/util/wei'; - import styles from './accountCard.css'; export default class AccountCard extends Component { static propTypes = { account: PropTypes.object.isRequired, - onClick: PropTypes.func.isRequired, - onFocus: PropTypes.func.isRequired, - - balance: PropTypes.object + balance: PropTypes.object, + className: PropTypes.string, + onClick: PropTypes.func, + onFocus: PropTypes.func }; state = { @@ -40,11 +39,11 @@ export default class AccountCard extends Component { }; render () { - const { account } = this.props; + const { account, balance, className } = this.props; const { copied } = this.state; const { address, description, meta = {}, name } = account; const { tags = [] } = meta; - const classes = [ styles.account ]; + const classes = [ styles.account, className ]; if (copied) { classes.push(styles.copied); @@ -59,21 +58,28 @@ export default class AccountCard extends Component { onFocus={ this.onFocus } onKeyDown={ this.handleKeyDown } > - -
-
- +
+ +
+
+ +
+ { this.renderDescription(description) } + { this.renderAddress(address) }
- - { this.renderTags(tags, address) } - { this.renderDescription(description) } - { this.renderAddress(address) } - { this.renderBalance(address) }
+ + +
); } @@ -105,40 +111,6 @@ export default class AccountCard extends Component { ); } - renderTags (tags = [], address) { - if (tags.length === 0) { - return null; - } - - return ( - - ); - } - - renderBalance (address) { - const { balance = {} } = this.props; - - if (!balance.tokens) { - return null; - } - - const ethToken = balance.tokens - .find((tok) => tok.token && (tok.token.tag || '').toLowerCase() === 'eth'); - - if (!ethToken) { - return null; - } - - const value = fromWei(ethToken.value).toFormat(3); - - return ( -
- { value } - ETH -
- ); - } - handleKeyDown = (event) => { const codeName = keycode(event); @@ -182,13 +154,13 @@ export default class AccountCard extends Component { onClick = () => { const { account, onClick } = this.props; - onClick(account.address); + onClick && onClick(account.address); } onFocus = () => { const { account, onFocus } = this.props; - onFocus(account.index); + onFocus && onFocus(account.index); } preventEvent = (e) => { diff --git a/js/src/ui/AccountCard/accountCard.spec.js b/js/src/ui/AccountCard/accountCard.spec.js index b94aca325..ba4791778 100644 --- a/js/src/ui/AccountCard/accountCard.spec.js +++ b/js/src/ui/AccountCard/accountCard.spec.js @@ -61,6 +61,23 @@ describe('ui/AccountCard', () => { }); describe('components', () => { + describe('Balance', () => { + let balance; + + beforeEach(() => { + balance = component.find('Connect(Balance)'); + }); + + it('renders the balance', () => { + expect(balance.length).to.equal(1); + }); + + it('sets showOnlyEth & showZeroValues', () => { + expect(balance.props().showOnlyEth).to.be.true; + expect(balance.props().showZeroValues).to.be.true; + }); + }); + describe('IdentityIcon', () => { let icon; @@ -100,5 +117,17 @@ describe('ui/AccountCard', () => { expect(name.props().unknown).to.be.true; }); }); + + describe('Tags', () => { + let tags; + + beforeEach(() => { + tags = component.find('Tags'); + }); + + it('renders the tags', () => { + expect(tags.length).to.equal(1); + }); + }); }); }); diff --git a/js/src/ui/Balance/balance.js b/js/src/ui/Balance/balance.js index e667cf67c..c024b1d06 100644 --- a/js/src/ui/Balance/balance.js +++ b/js/src/ui/Balance/balance.js @@ -16,31 +16,46 @@ import BigNumber from 'bignumber.js'; import React, { Component, PropTypes } from 'react'; +import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; -import unknownImage from '../../../assets/images/contracts/unknown-64x64.png'; +import unknownImage from '~/../assets/images/contracts/unknown-64x64.png'; + import styles from './balance.css'; class Balance extends Component { static contextTypes = { api: PropTypes.object - } + }; static propTypes = { balance: PropTypes.object, - images: PropTypes.object.isRequired - } + className: PropTypes.string, + images: PropTypes.object.isRequired, + showOnlyEth: PropTypes.bool, + showZeroValues: PropTypes.bool + }; + + static defaultProps = { + showOnlyEth: false, + showZeroValues: false + }; render () { const { api } = this.context; - const { balance, images } = this.props; + const { balance, className, images, showZeroValues, showOnlyEth } = this.props; - if (!balance) { + if (!balance || !balance.tokens) { return null; } - let body = (balance.tokens || []) - .filter((balance) => new BigNumber(balance.value).gt(0)) + let body = balance.tokens + .filter((balance) => { + const hasBalance = showZeroValues || new BigNumber(balance.value).gt(0); + const isValidToken = !showOnlyEth || (balance.token.tag || '').toLowerCase() === 'eth'; + + return hasBalance && isValidToken; + }) .map((balance, index) => { const token = balance.token; @@ -95,13 +110,16 @@ class Balance extends Component { if (!body.length) { body = (
- There are no balances associated with this account +
); } return ( -
+
{ body }
); diff --git a/js/src/ui/Balance/balance.spec.js b/js/src/ui/Balance/balance.spec.js new file mode 100644 index 000000000..f12e84851 --- /dev/null +++ b/js/src/ui/Balance/balance.spec.js @@ -0,0 +1,122 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +import { shallow } from 'enzyme'; +import React from 'react'; +import sinon from 'sinon'; + +import apiutil from '~/api/util'; + +import Balance from './'; + +const BALANCE = { + tokens: [ + { value: '122', token: { tag: 'ETH' } }, + { value: '345', token: { tag: 'GAV', format: 1 } }, + { value: '0', token: { tag: 'TST', format: 1 } } + ] +}; + +let api; +let component; +let store; + +function createApi () { + api = { + dappsUrl: 'http://testDapps:1234/', + util: apiutil + }; + + return api; +} + +function createStore () { + store = { + dispatch: sinon.stub(), + subscribe: sinon.stub(), + getState: () => { + return { + images: {} + }; + } + }; + + return store; +} + +function render (props = {}) { + if (!props.balance) { + props.balance = BALANCE; + } + + component = shallow( + , + { + context: { + store: createStore() + } + } + ).find('Balance').shallow({ context: { api: createApi() } }); + + return component; +} + +describe('ui/Balance', () => { + beforeEach(() => { + render(); + }); + + it('renders defaults', () => { + expect(component).to.be.ok; + }); + + it('passes the specified className', () => { + expect(component.hasClass('testClass')).to.be.true; + }); + + it('renders all the non-zero balances', () => { + expect(component.find('img')).to.have.length(2); + }); + + describe('render specifiers', () => { + it('renders only the single token with showOnlyEth', () => { + render({ showOnlyEth: true }); + expect(component.find('img')).to.have.length(1); + }); + + it('renders all the tokens with showZeroValues', () => { + render({ showZeroValues: true }); + expect(component.find('img')).to.have.length(3); + }); + + it('shows ETH with zero value with showOnlyEth & showZeroValues', () => { + render({ + showOnlyEth: true, + showZeroValues: true, + balance: { + tokens: [ + { value: '0', token: { tag: 'ETH' } }, + { value: '345', token: { tag: 'GAV', format: 1 } } + ] + } + }); + expect(component.find('img')).to.have.length(1); + }); + }); +}); diff --git a/js/src/ui/Tags/tags.js b/js/src/ui/Tags/tags.js index 3ea705e0b..5c44127c4 100644 --- a/js/src/ui/Tags/tags.js +++ b/js/src/ui/Tags/tags.js @@ -28,14 +28,21 @@ export default class Tags extends Component { } render () { - return (
- { this.renderTags() } -
); + const { tags } = this.props; + + if (!tags || tags.length === 0) { + return null; + } + + return ( +
+ { this.renderTags() } +
+ ); } renderTags () { - const { handleAddSearchToken, setRefs } = this.props; - const tags = this.props.tags || []; + const { handleAddSearchToken, setRefs, tags } = this.props; const tagClasses = handleAddSearchToken ? [ styles.tag, styles.tagClickable ] @@ -47,14 +54,14 @@ export default class Tags extends Component { return tags .sort() - .map((tag, idx) => { + .map((tag, index) => { const onClick = handleAddSearchToken ? () => handleAddSearchToken(tag) : null; return (
. +import AccountCard from './AccountCard'; import Actionbar from './Actionbar'; import ActionbarExport from './Actionbar/Export'; import ActionbarImport from './Actionbar/Import'; @@ -59,6 +60,7 @@ import TxList from './TxList'; import Warning from './Warning'; export { + AccountCard, Actionbar, ActionbarExport, ActionbarImport,