diff options
Merge branch 'master' of github.com:MetaMask/metamask-extension into greenkeeper/initial
Diffstat (limited to 'ui/app/components')
-rw-r--r-- | ui/app/components/account-dropdowns.js | 289 | ||||
-rw-r--r-- | ui/app/components/account-export.js | 2 | ||||
-rw-r--r-- | ui/app/components/account-info-link.js | 41 | ||||
-rw-r--r-- | ui/app/components/drop-menu-item.js | 59 | ||||
-rw-r--r-- | ui/app/components/dropdown.js | 94 | ||||
-rw-r--r-- | ui/app/components/editable-label.js | 7 | ||||
-rw-r--r-- | ui/app/components/loading.js | 48 | ||||
-rw-r--r-- | ui/app/components/menu-droppo.js | 130 | ||||
-rw-r--r-- | ui/app/components/network.js | 1 | ||||
-rw-r--r-- | ui/app/components/notice.js | 6 | ||||
-rw-r--r-- | ui/app/components/shapeshift-form.js | 10 | ||||
-rw-r--r-- | ui/app/components/tab-bar.js | 1 | ||||
-rw-r--r-- | ui/app/components/token-list.js | 42 | ||||
-rw-r--r-- | ui/app/components/transaction-list.js | 18 |
14 files changed, 592 insertions, 156 deletions
diff --git a/ui/app/components/account-dropdowns.js b/ui/app/components/account-dropdowns.js new file mode 100644 index 000000000..b23600e9b --- /dev/null +++ b/ui/app/components/account-dropdowns.js @@ -0,0 +1,289 @@ +const Component = require('react').Component +const PropTypes = require('react').PropTypes +const h = require('react-hyperscript') +const actions = require('../actions') +const genAccountLink = require('../../lib/account-link.js') +const connect = require('react-redux').connect +const Dropdown = require('./dropdown').Dropdown +const DropdownMenuItem = require('./dropdown').DropdownMenuItem +const Identicon = require('./identicon') +const ethUtil = require('ethereumjs-util') +const copyToClipboard = require('copy-to-clipboard') + +class AccountDropdowns extends Component { + constructor (props) { + super(props) + this.state = { + accountSelectorActive: false, + optionsMenuActive: false, + } + this.accountSelectorToggleClassName = 'accounts-selector' + this.optionsMenuToggleClassName = 'fa-ellipsis-h' + } + + renderAccounts () { + const { identities, selected } = this.props + + return Object.keys(identities).map((key, index) => { + const identity = identities[key] + const isSelected = identity.address === selected + + return h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => { + this.props.actions.showAccountDetail(identity.address) + }, + style: { + marginTop: index === 0 ? '5px' : '', + fontSize: '24px', + }, + }, + [ + h( + Identicon, + { + address: identity.address, + diameter: 32, + style: { + marginLeft: '10px', + }, + }, + ), + h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, identity.name || ''), + h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, isSelected ? h('.check', '✓') : null), + ] + ) + }) + } + + renderAccountSelector () { + const { actions } = this.props + const { accountSelectorActive } = this.state + + return h( + Dropdown, + { + useCssTransition: true, // Hardcoded because account selector is temporarily in app-header + style: { + marginLeft: '-238px', + marginTop: '38px', + minWidth: '180px', + overflowY: 'auto', + maxHeight: '300px', + width: '300px', + }, + innerStyle: { + padding: '8px 25px', + }, + isOpen: accountSelectorActive, + onClickOutside: (event) => { + const { classList } = event.target + const isNotToggleElement = !classList.contains(this.accountSelectorToggleClassName) + if (accountSelectorActive && isNotToggleElement) { + this.setState({ accountSelectorActive: false }) + } + }, + }, + [ + ...this.renderAccounts(), + h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => actions.addNewAccount(), + }, + [ + h( + Identicon, + { + style: { + marginLeft: '10px', + }, + diameter: 32, + }, + ), + h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, 'Create Account'), + ], + ), + h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => actions.showImportPage(), + }, + [ + h( + Identicon, + { + style: { + marginLeft: '10px', + }, + diameter: 32, + }, + ), + h('span', { + style: { + marginLeft: '20px', + fontSize: '24px', + marginBottom: '5px', + }, + }, 'Import Account'), + ] + ), + ] + ) + } + + renderAccountOptions () { + const { actions } = this.props + const { optionsMenuActive } = this.state + + return h( + Dropdown, + { + style: { + marginLeft: '-215px', + minWidth: '180px', + }, + isOpen: optionsMenuActive, + onClickOutside: () => { + const { classList } = event.target + const isNotToggleElement = !classList.contains(this.optionsMenuToggleClassName) + if (optionsMenuActive && isNotToggleElement) { + this.setState({ optionsMenuActive: false }) + } + }, + }, + [ + h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => { + const { selected, network } = this.props + const url = genAccountLink(selected, network) + global.platform.openWindow({ url }) + }, + }, + 'View account on Etherscan', + ), + h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => { + const { selected, identities } = this.props + var identity = identities[selected] + actions.showQrView(selected, identity ? identity.name : '') + }, + }, + 'Show QR Code', + ), + h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => { + const { selected } = this.props + const checkSumAddress = selected && ethUtil.toChecksumAddress(selected) + copyToClipboard(checkSumAddress) + }, + }, + 'Copy Address to clipboard', + ), + h( + DropdownMenuItem, + { + closeMenu: () => {}, + onClick: () => { + actions.requestAccountExport() + }, + }, + 'Export Private Key', + ), + ] + ) + } + + render () { + const { style, enableAccountsSelector, enableAccountOptions } = this.props + const { optionsMenuActive, accountSelectorActive } = this.state + + return h( + 'span', + { + style: style, + }, + [ + enableAccountsSelector && h( + // 'i.fa.fa-angle-down', + 'div.cursor-pointer.color-orange.accounts-selector', + { + style: { + // fontSize: '1.8em', + background: 'url(images/switch_acc.svg) white center center no-repeat', + height: '25px', + width: '25px', + transform: 'scale(0.75)', + marginRight: '3px', + }, + onClick: (event) => { + event.stopPropagation() + this.setState({ + accountSelectorActive: !accountSelectorActive, + optionsMenuActive: false, + }) + }, + }, + this.renderAccountSelector(), + ), + enableAccountOptions && h( + 'i.fa.fa-ellipsis-h', + { + style: { + marginRight: '0.5em', + fontSize: '1.8em', + }, + onClick: (event) => { + event.stopPropagation() + this.setState({ + accountSelectorActive: false, + optionsMenuActive: !optionsMenuActive, + }) + }, + }, + this.renderAccountOptions() + ), + ] + ) + } +} + +AccountDropdowns.defaultProps = { + enableAccountsSelector: false, + enableAccountOptions: false, +} + +AccountDropdowns.propTypes = { + identities: PropTypes.objectOf(PropTypes.object), + selected: PropTypes.string, +} + +const mapDispatchToProps = (dispatch) => { + return { + actions: { + showConfigPage: () => dispatch(actions.showConfigPage()), + requestAccountExport: () => dispatch(actions.requestExportAccount()), + showAccountDetail: (address) => dispatch(actions.showAccountDetail(address)), + addNewAccount: () => dispatch(actions.addNewAccount()), + showImportPage: () => dispatch(actions.showImportPage()), + showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)), + }, + } +} + +module.exports = { + AccountDropdowns: connect(null, mapDispatchToProps)(AccountDropdowns), +} diff --git a/ui/app/components/account-export.js b/ui/app/components/account-export.js index 394d878f7..330f73805 100644 --- a/ui/app/components/account-export.js +++ b/ui/app/components/account-export.js @@ -100,7 +100,7 @@ ExportAccountView.prototype.render = function () { textOverflow: 'ellipsis', overflow: 'hidden', webkitUserSelect: 'text', - width: '100%', + maxWidth: '275px', }, onClick: function (event) { copyToClipboard(ethUtil.stripHexPrefix(accountDetail.privateKey)) diff --git a/ui/app/components/account-info-link.js b/ui/app/components/account-info-link.js deleted file mode 100644 index 6526ab502..000000000 --- a/ui/app/components/account-info-link.js +++ /dev/null @@ -1,41 +0,0 @@ -const Component = require('react').Component -const h = require('react-hyperscript') -const inherits = require('util').inherits -const Tooltip = require('./tooltip') -const genAccountLink = require('../../lib/account-link') - -module.exports = AccountInfoLink - -inherits(AccountInfoLink, Component) -function AccountInfoLink () { - Component.call(this) -} - -AccountInfoLink.prototype.render = function () { - const { selected, network } = this.props - const title = 'View account on Etherscan' - const url = genAccountLink(selected, network) - - if (!url) { - return null - } - - return h('.account-info-link', { - style: { - display: 'flex', - alignItems: 'center', - }, - }, [ - - h(Tooltip, { - title, - }, [ - h('i.fa.fa-info-circle.cursor-pointer.color-orange', { - style: { - margin: '5px', - }, - onClick () { global.platform.openWindow({ url }) }, - }), - ]), - ]) -} diff --git a/ui/app/components/drop-menu-item.js b/ui/app/components/drop-menu-item.js deleted file mode 100644 index e42948209..000000000 --- a/ui/app/components/drop-menu-item.js +++ /dev/null @@ -1,59 +0,0 @@ -const Component = require('react').Component -const h = require('react-hyperscript') -const inherits = require('util').inherits - -module.exports = DropMenuItem - -inherits(DropMenuItem, Component) -function DropMenuItem () { - Component.call(this) -} - -DropMenuItem.prototype.render = function () { - return h('li.drop-menu-item', { - onClick: () => { - this.props.closeMenu() - this.props.action() - }, - style: { - listStyle: 'none', - padding: '6px 16px 6px 5px', - fontFamily: 'Montserrat Regular', - color: 'rgb(125, 128, 130)', - cursor: 'pointer', - display: 'flex', - justifyContent: 'flex-start', - }, - }, [ - this.props.icon, - this.props.label, - this.activeNetworkRender(), - ]) -} - -DropMenuItem.prototype.activeNetworkRender = function () { - const activeNetwork = this.props.activeNetworkRender - const { provider } = this.props - const providerType = provider ? provider.type : null - if (activeNetwork === undefined) return - - switch (this.props.label) { - case 'Main Ethereum Network': - if (providerType === 'mainnet') return h('.check', '✓') - break - case 'Ropsten Test Network': - if (providerType === 'ropsten') return h('.check', '✓') - break - case 'Kovan Test Network': - if (providerType === 'kovan') return h('.check', '✓') - break - case 'Rinkeby Test Network': - if (providerType === 'rinkeby') return h('.check', '✓') - break - case 'Localhost 8545': - if (activeNetwork === 'http://localhost:8545') return h('.check', '✓') - break - default: - if (activeNetwork === 'custom') return h('.check', '✓') - } -} diff --git a/ui/app/components/dropdown.js b/ui/app/components/dropdown.js new file mode 100644 index 000000000..34c7149ee --- /dev/null +++ b/ui/app/components/dropdown.js @@ -0,0 +1,94 @@ +const Component = require('react').Component +const PropTypes = require('react').PropTypes +const h = require('react-hyperscript') +const MenuDroppo = require('./menu-droppo') +const extend = require('xtend') + +const noop = () => {} + +class Dropdown extends Component { + render () { + const { isOpen, onClickOutside, style, innerStyle, children, useCssTransition } = this.props + + const innerStyleDefaults = extend({ + borderRadius: '4px', + padding: '8px 16px', + background: 'rgba(0, 0, 0, 0.8)', + boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', + }, innerStyle) + + return h( + MenuDroppo, + { + useCssTransition, + isOpen, + zIndex: 11, + onClickOutside, + style, + innerStyle: innerStyleDefaults, + }, + [ + h( + 'style', + ` + li.dropdown-menu-item:hover { color:rgb(225, 225, 225); } + li.dropdown-menu-item { color: rgb(185, 185, 185); } + ` + ), + ...children, + ] + ) + } +} + +Dropdown.defaultProps = { + isOpen: false, + onClick: noop, + useCssTransition: false, +} + +Dropdown.propTypes = { + isOpen: PropTypes.bool.isRequired, + onClick: PropTypes.func.isRequired, + children: PropTypes.node, + style: PropTypes.object.isRequired, +} + +class DropdownMenuItem extends Component { + render () { + const { onClick, closeMenu, children, style } = this.props + + return h( + 'li.dropdown-menu-item', + { + onClick: () => { + onClick() + closeMenu() + }, + style: Object.assign({ + listStyle: 'none', + padding: '8px 0px 8px 0px', + fontSize: '18px', + fontStyle: 'normal', + fontFamily: 'Montserrat Regular', + cursor: 'pointer', + display: 'flex', + justifyContent: 'flex-start', + alignItems: 'center', + }, style), + }, + children + ) + } +} + +DropdownMenuItem.propTypes = { + closeMenu: PropTypes.func.isRequired, + onClick: PropTypes.func.isRequired, + children: PropTypes.node, +} + +module.exports = { + Dropdown, + DropdownMenuItem, +} diff --git a/ui/app/components/editable-label.js b/ui/app/components/editable-label.js index 41936f5e0..167be7eaf 100644 --- a/ui/app/components/editable-label.js +++ b/ui/app/components/editable-label.js @@ -30,7 +30,12 @@ EditableLabel.prototype.render = function () { } else { return h('div.name-label', { onClick: (event) => { - this.setState({ isEditingLabel: true }) + const nameAttribute = event.target.getAttribute('name') + // checks for class to handle smaller CTA above the account name + const classAttribute = event.target.getAttribute('class') + if (nameAttribute === 'edit' || classAttribute === 'edit-text') { + this.setState({ isEditingLabel: true }) + } }, }, this.props.children) } diff --git a/ui/app/components/loading.js b/ui/app/components/loading.js index 87d6f5d20..163792584 100644 --- a/ui/app/components/loading.js +++ b/ui/app/components/loading.js @@ -1,7 +1,6 @@ const inherits = require('util').inherits const Component = require('react').Component const h = require('react-hyperscript') -const ReactCSSTransitionGroup = require('react-addons-css-transition-group') inherits(LoadingIndicator, Component) @@ -15,35 +14,28 @@ LoadingIndicator.prototype.render = function () { const { isLoading, loadingMessage } = this.props return ( - h(ReactCSSTransitionGroup, { - className: 'css-transition-group', - transitionName: 'loader', - transitionEnterTimeout: 150, - transitionLeaveTimeout: 150, + isLoading ? h('.full-flex-height', { + style: { + left: '0px', + zIndex: 10, + position: 'absolute', + flexDirection: 'column', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + height: '100%', + width: '100%', + background: 'rgba(255, 255, 255, 0.8)', + }, }, [ + h('img', { + src: 'images/loading.svg', + }), - isLoading ? h('div', { - style: { - zIndex: 10, - position: 'absolute', - flexDirection: 'column', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - height: '100%', - width: '100%', - background: 'rgba(255, 255, 255, 0.8)', - }, - }, [ - h('img', { - src: 'images/loading.svg', - }), - - h('br'), - - showMessageIfAny(loadingMessage), - ]) : null, - ]) + h('br'), + + showMessageIfAny(loadingMessage), + ]) : null ) } diff --git a/ui/app/components/menu-droppo.js b/ui/app/components/menu-droppo.js new file mode 100644 index 000000000..66ab18954 --- /dev/null +++ b/ui/app/components/menu-droppo.js @@ -0,0 +1,130 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const findDOMNode = require('react-dom').findDOMNode +const ReactCSSTransitionGroup = require('react-addons-css-transition-group') + +module.exports = MenuDroppoComponent + + +inherits(MenuDroppoComponent, Component) +function MenuDroppoComponent () { + Component.call(this) +} + +MenuDroppoComponent.prototype.render = function () { + const speed = this.props.speed || '300ms' + const useCssTransition = this.props.useCssTransition + const zIndex = ('zIndex' in this.props) ? this.props.zIndex : 0 + + this.manageListeners() + + let style = this.props.style || {} + if (!('position' in style)) { + style.position = 'fixed' + } + style.zIndex = zIndex + + return ( + h('.menu-droppo-container', { + style, + }, [ + h('style', ` + .menu-droppo-enter { + transition: transform ${speed} ease-in-out; + transform: translateY(-200%); + } + + .menu-droppo-enter.menu-droppo-enter-active { + transition: transform ${speed} ease-in-out; + transform: translateY(0%); + } + + .menu-droppo-leave { + transition: transform ${speed} ease-in-out; + transform: translateY(0%); + } + + .menu-droppo-leave.menu-droppo-leave-active { + transition: transform ${speed} ease-in-out; + transform: translateY(-200%); + } + `), + + useCssTransition + ? h(ReactCSSTransitionGroup, { + className: 'css-transition-group', + transitionName: 'menu-droppo', + transitionEnterTimeout: parseInt(speed), + transitionLeaveTimeout: parseInt(speed), + }, this.renderPrimary()) + : this.renderPrimary(), + ]) + ) +} + +MenuDroppoComponent.prototype.renderPrimary = function () { + const isOpen = this.props.isOpen + if (!isOpen) { + return null + } + + const innerStyle = this.props.innerStyle || {} + + return ( + h('.menu-droppo', { + key: 'menu-droppo-drawer', + style: innerStyle, + }, + [ this.props.children ]) + ) +} + +MenuDroppoComponent.prototype.manageListeners = function () { + const isOpen = this.props.isOpen + const onClickOutside = this.props.onClickOutside + + if (isOpen) { + this.outsideClickHandler = onClickOutside + } else if (isOpen) { + this.outsideClickHandler = null + } +} + +MenuDroppoComponent.prototype.componentDidMount = function () { + if (this && document.body) { + this.globalClickHandler = this.globalClickOccurred.bind(this) + document.body.addEventListener('click', this.globalClickHandler) + var container = findDOMNode(this) + this.container = container + } +} + +MenuDroppoComponent.prototype.componentWillUnmount = function () { + if (this && document.body) { + document.body.removeEventListener('click', this.globalClickHandler) + } +} + +MenuDroppoComponent.prototype.globalClickOccurred = function (event) { + const target = event.target + const container = findDOMNode(this) + + if (target !== container && + !isDescendant(this.container, event.target) && + this.outsideClickHandler) { + this.outsideClickHandler(event) + } +} + +function isDescendant (parent, child) { + var node = child.parentNode + while (node !== null) { + if (node === parent) { + return true + } + node = node.parentNode + } + + return false +} diff --git a/ui/app/components/network.js b/ui/app/components/network.js index d5d3e18cd..698a0bbb9 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -39,7 +39,6 @@ Network.prototype.render = function () { }), h('i.fa.fa-sort-desc'), ]) - } else if (providerName === 'mainnet') { hoverText = 'Main Ethereum Network' iconName = 'ethereum-network' diff --git a/ui/app/components/notice.js b/ui/app/components/notice.js index d9f0067cd..c26505193 100644 --- a/ui/app/components/notice.js +++ b/ui/app/components/notice.js @@ -19,7 +19,11 @@ Notice.prototype.render = function () { const disabled = state.disclaimerDisabled return ( - h('.flex-column.flex-center.flex-grow', [ + h('.flex-column.flex-center.flex-grow', { + style: { + width: '100%', + }, + }, [ h('h3.flex-center.text-transform-uppercase.terms-header', { style: { background: '#EBEBEB', diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js index e0a720426..76a265d63 100644 --- a/ui/app/components/shapeshift-form.js +++ b/ui/app/components/shapeshift-form.js @@ -2,7 +2,6 @@ const PersistentForm = require('../../lib/persistent-form') const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect -const ReactCSSTransitionGroup = require('react-addons-css-transition-group') const actions = require('../actions') const Qr = require('./qr-code') const isValidAddress = require('../util').isValidAddress @@ -24,14 +23,7 @@ function ShapeshiftForm () { } ShapeshiftForm.prototype.render = function () { - return h(ReactCSSTransitionGroup, { - className: 'css-transition-group', - transitionName: 'main', - transitionEnterTimeout: 300, - transitionLeaveTimeout: 300, - }, [ - this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain(), - ]) + return this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain() } ShapeshiftForm.prototype.renderMain = function () { diff --git a/ui/app/components/tab-bar.js b/ui/app/components/tab-bar.js index 6295e7dd9..bef444a48 100644 --- a/ui/app/components/tab-bar.js +++ b/ui/app/components/tab-bar.js @@ -21,6 +21,7 @@ TabBar.prototype.render = function () { background: '#EBEBEB', color: '#AEAEAE', paddingTop: '4px', + minHeight: '30px', }, }, tabs.map((tab) => { const { key, content } = tab diff --git a/ui/app/components/token-list.js b/ui/app/components/token-list.js index 20cfa897e..5ea31ae8d 100644 --- a/ui/app/components/token-list.js +++ b/ui/app/components/token-list.js @@ -47,10 +47,11 @@ TokenList.prototype.render = function () { return h(TokenCell, tokenData) }) - return h('div', [ - h('ol', { + return h('.full-flex-height', [ + this.renderTokenStatusBar(), + + h('ol.full-flex-height.flex-column', { style: { - height: '260px', overflowY: 'auto', display: 'flex', flexDirection: 'column', @@ -63,6 +64,7 @@ TokenList.prototype.render = function () { flex-direction: row; align-items: center; padding: 10px; + min-height: 50px; } li.token-cell > h3 { @@ -76,17 +78,37 @@ TokenList.prototype.render = function () { `), ...tokenViews, - tokenViews.length ? null : this.message('No Tokens Found.'), + h('.flex-grow'), ]), - this.addTokenButtonElement(), ]) } -TokenList.prototype.addTokenButtonElement = function () { - return h('div', [ - h('div.footer.hover-white.pointer', { +TokenList.prototype.renderTokenStatusBar = function () { + const { tokens } = this.state + + let msg + if (tokens.length === 1) { + msg = `You own 1 token` + } else if (tokens.length === 1) { + msg = `You own ${tokens.length} tokens` + } else { + msg = `No tokens found` + } + + return h('div', { + style: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + minHeight: '70px', + padding: '10px', + }, + }, [ + h('span', msg), + h('button', { key: 'reveal-account-bar', - onClick: () => { + onClick: (event) => { + event.preventDefault() this.props.addToken() }, style: { @@ -97,7 +119,7 @@ TokenList.prototype.addTokenButtonElement = function () { alignItems: 'center', }, }, [ - h('i.fa.fa-plus.fa-lg'), + 'ADD TOKEN', ]), ]) } diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js index 3b4ba741e..69b72614c 100644 --- a/ui/app/components/transaction-list.js +++ b/ui/app/components/transaction-list.js @@ -24,7 +24,11 @@ TransactionList.prototype.render = function () { return ( - h('section.transaction-list', [ + h('section.transaction-list.full-flex-height', { + style: { + justifyContent: 'center', + }, + }, [ h('style', ` .transaction-list .transaction-list-item:not(:last-of-type) { @@ -39,7 +43,7 @@ TransactionList.prototype.render = function () { h('.tx-list', { style: { overflowY: 'auto', - height: '300px', + height: '100%', padding: '0 20px', textAlign: 'center', }, @@ -64,13 +68,17 @@ TransactionList.prototype.render = function () { }, }) }) - : h('.flex-center', { + : h('.flex-center.full-flex-height', { style: { flexDirection: 'column', - height: '100%', + justifyContent: 'center', }, }, [ - 'No transaction history.', + h('p', { + style: { + marginTop: '50px', + }, + }, 'No transaction history.'), ]), ]), ]) |