Final cleanup

This commit is contained in:
Victor Baranov 2019-09-03 22:38:21 +03:00
parent 799baff067
commit 4bce1e4678
186 changed files with 2 additions and 10267 deletions

View File

@ -46,14 +46,8 @@ async function start () {
// Code commented out until we begin auto adding users to NewUI
let css = OldMetaMaskUiCss()
let deleteInjectedCss = injectCss(css)
store.subscribe(() => {
deleteInjectedCss()
css = OldMetaMaskUiCss()
deleteInjectedCss = injectCss(css)
})
const css = OldMetaMaskUiCss()
injectCss(css)
})

View File

@ -1,61 +0,0 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
export default class ButtonGroup extends PureComponent {
static propTypes = {
defaultActiveButtonIndex: PropTypes.number,
disabled: PropTypes.bool,
children: PropTypes.array,
className: PropTypes.string,
style: PropTypes.object,
}
static defaultProps = {
className: 'button-group',
}
state = {
activeButtonIndex: this.props.defaultActiveButtonIndex || 0,
}
handleButtonClick (activeButtonIndex) {
this.setState({ activeButtonIndex })
}
renderButtons () {
const { children, disabled } = this.props
return React.Children.map(children, (child, index) => {
return child && (
<button
className={classnames(
'button-group__button',
{ 'button-group__button--active': index === this.state.activeButtonIndex },
)}
onClick={() => {
this.handleButtonClick(index)
child.props.onClick && child.props.onClick()
}}
disabled={disabled || child.props.disabled}
key={index}
>
{ child.props.children }
</button>
)
})
}
render () {
const { className, style } = this.props
return (
<div
className={className}
style={style}
>
{ this.renderButtons() }
</div>
)
}
}

View File

@ -1,49 +0,0 @@
import React from 'react'
import { storiesOf } from '@storybook/react'
import { action } from '@storybook/addon-actions'
import ButtonGroup from './'
import Button from '../button'
import { text, boolean } from '@storybook/addon-knobs/react'
storiesOf('ButtonGroup', module)
.add('with Buttons', () =>
<ButtonGroup
style={{ width: '300px' }}
disabled={boolean('Disabled', false)}
defaultActiveButtonIndex={1}
>
<Button
onClick={action('cheap')}
>
{text('Button1', 'Cheap')}
</Button>
<Button
onClick={action('average')}
>
{text('Button2', 'Average')}
</Button>
<Button
onClick={action('fast')}
>
{text('Button3', 'Fast')}
</Button>
</ButtonGroup>
)
.add('with a disabled Button', () =>
<ButtonGroup
style={{ width: '300px' }}
disabled={boolean('Disabled', false)}
>
<Button
onClick={action('enabled')}
>
{text('Button1', 'Enabled')}
</Button>
<Button
onClick={action('disabled')}
disabled
>
{text('Button2', 'Disabled')}
</Button>
</ButtonGroup>
)

View File

@ -1 +0,0 @@
export { default } from './button-group.component'

View File

@ -1,38 +0,0 @@
.button-group {
display: flex;
justify-content: center;
align-items: center;
&__button {
font-family: Roboto;
font-size: 1rem;
color: $tundora;
border-style: solid;
border-color: $alto;
border-width: 1px 1px 1px;
border-left: 0;
flex: 1;
padding: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&:first-child {
border-left: 1px solid $alto;
border-radius: 4px 0 0 4px;
}
&:last-child {
border-radius: 0 4px 4px 0;
}
&--active {
background-color: $dodger-blue;
color: $white;
}
&:disabled {
opacity: .5;
}
}
}

View File

@ -1,97 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import sinon from 'sinon'
import ButtonGroup from '../button-group.component.js'
const childButtonSpies = {
onClick: sinon.spy(),
}
sinon.spy(ButtonGroup.prototype, 'handleButtonClick')
sinon.spy(ButtonGroup.prototype, 'renderButtons')
const mockButtons = [
<button onClick={childButtonSpies.onClick} key={'a'}><div className="mockClass" /></button>,
<button onClick={childButtonSpies.onClick} key={'b'}></button>,
<button onClick={childButtonSpies.onClick} key={'c'}></button>,
]
describe('ButtonGroup Component', function () {
let wrapper
beforeEach(() => {
wrapper = shallow(<ButtonGroup
defaultActiveButtonIndex={1}
disabled={false}
className="someClassName"
style={ { color: 'red' } }
>{mockButtons}</ButtonGroup>)
})
afterEach(() => {
childButtonSpies.onClick.resetHistory()
ButtonGroup.prototype.handleButtonClick.resetHistory()
ButtonGroup.prototype.renderButtons.resetHistory()
})
describe('handleButtonClick', () => {
it('should set the activeButtonIndex', () => {
assert.equal(wrapper.state('activeButtonIndex'), 1)
wrapper.instance().handleButtonClick(2)
assert.equal(wrapper.state('activeButtonIndex'), 2)
})
})
describe('renderButtons', () => {
it('should render a button for each child', () => {
const childButtons = wrapper.find('.button-group__button')
assert.equal(childButtons.length, 3)
})
it('should render the correct button with an active state', () => {
const childButtons = wrapper.find('.button-group__button')
const activeChildButton = wrapper.find('.button-group__button--active')
assert.deepEqual(childButtons.get(1), activeChildButton.get(0))
})
it('should call handleButtonClick and the respective button\'s onClick method when a button is clicked', () => {
assert.equal(ButtonGroup.prototype.handleButtonClick.callCount, 0)
assert.equal(childButtonSpies.onClick.callCount, 0)
const childButtons = wrapper.find('.button-group__button')
childButtons.at(0).props().onClick()
childButtons.at(1).props().onClick()
childButtons.at(2).props().onClick()
assert.equal(ButtonGroup.prototype.handleButtonClick.callCount, 3)
assert.equal(childButtonSpies.onClick.callCount, 3)
})
it('should render all child buttons as disabled if props.disabled is true', () => {
const childButtons = wrapper.find('.button-group__button')
childButtons.forEach(button => {
assert.equal(button.props().disabled, undefined)
})
wrapper.setProps({ disabled: true })
const disabledChildButtons = wrapper.find('[disabled=true]')
assert.equal(disabledChildButtons.length, 3)
})
it('should render the children of the button', () => {
const mockClass = wrapper.find('.mockClass')
assert.equal(mockClass.length, 1)
})
})
describe('render', () => {
it('should render a div with the expected class and style', () => {
assert.equal(wrapper.find('div').at(0).props().className, 'someClassName')
assert.deepEqual(wrapper.find('div').at(0).props().style, { color: 'red' })
})
it('should call renderButtons when rendering', () => {
assert.equal(ButtonGroup.prototype.renderButtons.callCount, 1)
wrapper.instance().render()
assert.equal(ButtonGroup.prototype.renderButtons.callCount, 2)
})
})
})

View File

@ -1,25 +0,0 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
export default class Card extends PureComponent {
static propTypes = {
className: PropTypes.string,
overrideClassName: PropTypes.bool,
title: PropTypes.string,
children: PropTypes.node,
}
render () {
const { className, overrideClassName, title } = this.props
return (
<div className={classnames({ 'card': !overrideClassName }, className)}>
<div className="card__title">
{ title }
</div>
{ this.props.children }
</div>
)
}
}

View File

@ -1 +0,0 @@
export { default } from './card.component'

View File

@ -1,11 +0,0 @@
.card {
border-radius: 4px;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08);
padding: 8px;
&__title {
border-bottom: 1px solid #d8d8d8;
padding-bottom: 4px;
text-transform: capitalize;
}
}

View File

@ -1,25 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import Card from '../card.component'
describe('Card Component', () => {
it('should render a card with a title and child element', () => {
const wrapper = shallow(
<Card
title="Test"
className="card-test-class"
>
<div className="child-test-class">Child</div>
</Card>
)
assert.ok(wrapper.hasClass('card-test-class'))
const title = wrapper.find('.card__title')
assert.ok(title)
assert.equal(title.text(), 'Test')
const child = wrapper.find('.child-test-class')
assert.ok(child)
assert.equal(child.text(), 'Child')
})
})

View File

@ -1,84 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display'
import { PRIMARY, SECONDARY } from '../../../constants/common'
const ConfirmDetailRow = props => {
const {
label,
primaryText,
secondaryText,
onHeaderClick,
primaryValueTextColor,
headerText,
headerTextClassName,
value,
} = props
return (
<div className="confirm-detail-row">
<div className="confirm-detail-row__label">
{ label }
</div>
<div className="confirm-detail-row__details">
<div
className={classnames('confirm-detail-row__header-text', headerTextClassName)}
onClick={() => onHeaderClick && onHeaderClick()}
>
{ headerText }
</div>
{
primaryText
? (
<div
className="confirm-detail-row__primary"
style={{ color: primaryValueTextColor }}
>
{ primaryText }
</div>
) : (
<UserPreferencedCurrencyDisplay
className="confirm-detail-row__primary"
type={PRIMARY}
value={value}
showEthLogo
ethLogoHeight="18"
style={{ color: primaryValueTextColor }}
hideLabel
/>
)
}
{
secondaryText
? (
<div className="confirm-detail-row__secondary">
{ secondaryText }
</div>
) : (
<UserPreferencedCurrencyDisplay
className="confirm-detail-row__secondary"
type={SECONDARY}
value={value}
showEthLogo
hideLabel
/>
)
}
</div>
</div>
)
}
ConfirmDetailRow.propTypes = {
headerText: PropTypes.string,
headerTextClassName: PropTypes.string,
label: PropTypes.string,
onHeaderClick: PropTypes.func,
primaryValueTextColor: PropTypes.string,
primaryText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
secondaryText: PropTypes.string,
value: PropTypes.string,
}
export default ConfirmDetailRow

View File

@ -1 +0,0 @@
export { default } from './confirm-detail-row.component'

View File

@ -1,46 +0,0 @@
.confirm-detail-row {
padding: 14px 0;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
&__label {
font-size: .75rem;
font-weight: 500;
color: $scorpion;
text-transform: uppercase;
}
&__details {
flex: 1;
text-align: end;
min-width: 0;
}
&__primary {
font-size: 1.5rem;
justify-content: flex-end;
}
&__secondary {
color: $oslo-gray;
justify-content: flex-end;
}
&__header-text {
font-size: .75rem;
text-transform: uppercase;
margin-bottom: 6px;
color: $scorpion;
&--edit {
color: $curious-blue;
cursor: pointer;
}
&--total {
font-size: .625rem;
}
}
}

View File

@ -1,64 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import ConfirmDetailRow from '../confirm-detail-row.component.js'
import sinon from 'sinon'
const propsMethodSpies = {
onHeaderClick: sinon.spy(),
}
describe('Confirm Detail Row Component', function () {
let wrapper
beforeEach(() => {
wrapper = shallow(
<ConfirmDetailRow
errorType={'mockErrorType'}
label={'mockLabel'}
showError={false}
primaryText = {'mockFiatText'}
secondaryText = {'mockEthText'}
primaryValueTextColor= {'mockColor'}
onHeaderClick= {propsMethodSpies.onHeaderClick}
headerText = {'mockHeaderText'}
headerTextClassName = {'mockHeaderClass'}
/>
)
})
describe('render', () => {
it('should render a div with a confirm-detail-row class', () => {
assert.equal(wrapper.find('div.confirm-detail-row').length, 1)
})
it('should render the label as a child of the confirm-detail-row__label', () => {
assert.equal(wrapper.find('.confirm-detail-row > .confirm-detail-row__label').childAt(0).text(), 'mockLabel')
})
it('should render the headerText as a child of the confirm-detail-row__header-text', () => {
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__header-text').childAt(0).text(), 'mockHeaderText')
})
it('should render the primaryText as a child of the confirm-detail-row__primary', () => {
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__primary').childAt(0).text(), 'mockFiatText')
})
it('should render the ethText as a child of the confirm-detail-row__secondary', () => {
assert.equal(wrapper.find('.confirm-detail-row__details > .confirm-detail-row__secondary').childAt(0).text(), 'mockEthText')
})
it('should set the fiatTextColor on confirm-detail-row__primary', () => {
assert.equal(wrapper.find('.confirm-detail-row__primary').props().style.color, 'mockColor')
})
it('should assure the confirm-detail-row__header-text classname is correct', () => {
assert.equal(wrapper.find('.confirm-detail-row__header-text').props().className, 'confirm-detail-row__header-text mockHeaderClass')
})
it('should call onHeaderClick when headerText div gets clicked', () => {
wrapper.find('.confirm-detail-row__header-text').props().onClick()
assert.equal(assert.equal(propsMethodSpies.onHeaderClick.callCount, 1))
})
})
})

View File

@ -1,110 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import { Tabs, Tab } from '../../tabs'
import { ConfirmPageContainerSummary, ConfirmPageContainerWarning } from './'
import ErrorMessage from '../../error-message'
export default class ConfirmPageContainerContent extends Component {
static propTypes = {
action: PropTypes.string,
dataComponent: PropTypes.node,
detailsComponent: PropTypes.node,
errorKey: PropTypes.string,
errorMessage: PropTypes.string,
hideSubtitle: PropTypes.bool,
identiconAddress: PropTypes.string,
nonce: PropTypes.string,
assetImage: PropTypes.string,
subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
subtitleComponent: PropTypes.node,
summaryComponent: PropTypes.node,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
titleComponent: PropTypes.node,
warning: PropTypes.string,
}
renderContent () {
const { detailsComponent, dataComponent } = this.props
if (detailsComponent && dataComponent) {
return this.renderTabs()
} else {
return detailsComponent || dataComponent
}
}
renderTabs () {
const { detailsComponent, dataComponent } = this.props
return (
<Tabs>
<Tab name="Details">
{ detailsComponent }
</Tab>
<Tab name="Data">
{ dataComponent }
</Tab>
</Tabs>
)
}
render () {
const {
action,
errorKey,
errorMessage,
title,
titleComponent,
subtitle,
subtitleComponent,
hideSubtitle,
identiconAddress,
nonce,
assetImage,
summaryComponent,
detailsComponent,
dataComponent,
warning,
} = this.props
return (
<div className="confirm-page-container-content">
{
warning && (
<ConfirmPageContainerWarning warning={warning} />
)
}
{
summaryComponent || (
<ConfirmPageContainerSummary
className={classnames({
'confirm-page-container-summary--border': !detailsComponent || !dataComponent,
})}
action={action}
title={title}
titleComponent={titleComponent}
subtitle={subtitle}
subtitleComponent={subtitleComponent}
hideSubtitle={hideSubtitle}
identiconAddress={identiconAddress}
nonce={nonce}
assetImage={assetImage}
/>
)
}
{ this.renderContent() }
{
(errorKey || errorMessage) && (
<div className="confirm-page-container-content__error-container">
<ErrorMessage
errorMessage={errorMessage}
errorKey={errorKey}
/>
</div>
)
}
</div>
)
}
}

View File

@ -1,71 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import Identicon from '../../../identicon'
const ConfirmPageContainerSummary = props => {
const {
action,
title,
titleComponent,
subtitle,
subtitleComponent,
hideSubtitle,
className,
identiconAddress,
nonce,
assetImage,
} = props
return (
<div className={classnames('confirm-page-container-summary', className)}>
<div className="confirm-page-container-summary__action-row">
<div className="confirm-page-container-summary__action">
{ action }
</div>
{
nonce && (
<div className="confirm-page-container-summary__nonce">
{ `#${nonce}` }
</div>
)
}
</div>
<div className="confirm-page-container-summary__title">
{
identiconAddress && (
<Identicon
className="confirm-page-container-summary__identicon"
diameter={36}
address={identiconAddress}
image={assetImage}
/>
)
}
<div className="confirm-page-container-summary__title-text">
{ titleComponent || title }
</div>
</div>
{
hideSubtitle || <div className="confirm-page-container-summary__subtitle">
{ subtitleComponent || subtitle }
</div>
}
</div>
)
}
ConfirmPageContainerSummary.propTypes = {
action: PropTypes.string,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
titleComponent: PropTypes.node,
subtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
subtitleComponent: PropTypes.node,
hideSubtitle: PropTypes.bool,
className: PropTypes.string,
identiconAddress: PropTypes.string,
nonce: PropTypes.string,
assetImage: PropTypes.string,
}
export default ConfirmPageContainerSummary

View File

@ -1 +0,0 @@
export { default } from './confirm-page-container-summary.component'

View File

@ -1,54 +0,0 @@
.confirm-page-container-summary {
padding: 16px 24px 0;
background-color: #f9fafa;
height: 133px;
box-sizing: border-box;
&__action-row {
display: flex;
justify-content: space-between;
}
&__action {
text-transform: uppercase;
color: $oslo-gray;
font-size: .75rem;
padding: 3px 8px;
border: 1px solid $oslo-gray;
border-radius: 4px;
display: inline-block;
}
&__nonce {
color: $oslo-gray;
}
&__title {
padding: 4px 0;
display: flex;
align-items: center;
}
&__identicon {
flex: 0 0 auto;
margin-right: 8px;
}
&__title-text {
font-size: 2.25rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&__subtitle {
color: $oslo-gray;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&--border {
border-bottom: 1px solid $geyser;
}
}

View File

@ -1,22 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
const ConfirmPageContainerWarning = props => {
return (
<div className="confirm-page-container-warning">
<img
className="confirm-page-container-warning__icon"
src="/images/alert.svg"
/>
<div className="confirm-page-container-warning__warning">
{ props.warning }
</div>
</div>
)
}
ConfirmPageContainerWarning.propTypes = {
warning: PropTypes.string,
}
export default ConfirmPageContainerWarning

View File

@ -1 +0,0 @@
export { default } from './confirm-page-container-warning.component'

View File

@ -1,18 +0,0 @@
.confirm-page-container-warning {
background-color: #fffcdb;
display: flex;
justify-content: center;
align-items: center;
border-bottom: 1px solid $geyser;
padding: 12px 24px;
&__icon {
flex: 0 0 auto;
margin-right: 16px;
}
&__warning {
font-size: .75rem;
color: #5f5922;
}
}

View File

@ -1,3 +0,0 @@
export { default } from './confirm-page-container-content.component'
export { default as ConfirmPageContainerSummary } from './confirm-page-container-summary'
export { default as ConfirmPageContainerWarning } from './confirm-page-container-warning'

View File

@ -1,64 +0,0 @@
@import './confirm-page-container-warning/index';
@import './confirm-page-container-summary/index';
.confirm-page-container-content {
overflow-y: auto;
flex: 1;
&__error-container {
padding: 0 16px 16px 16px;
}
&__details {
box-sizing: border-box;
padding: 0 24px;
}
&__data {
padding: 16px;
color: $oslo-gray;
}
&__data-box {
background-color: #f9fafa;
padding: 12px;
font-size: .75rem;
margin-bottom: 16px;
word-wrap: break-word;
max-height: 200px;
overflow-y: auto;
&-label {
text-transform: uppercase;
padding: 8px 0 12px;
font-size: 12px;
}
}
&__data-field {
display: flex;
flex-direction: row;
&-label {
font-weight: 500;
padding-right: 16px;
}
&:not(:last-child) {
margin-bottom: 5px;
}
}
&__gas-fee {
border-bottom: 1px solid $geyser;
}
&__function-type {
font-size: .875rem;
font-weight: 500;
text-transform: capitalize;
color: $black;
padding-left: 5px;
}
}

View File

@ -1,63 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {
ENVIRONMENT_TYPE_POPUP,
ENVIRONMENT_TYPE_NOTIFICATION,
} from '../../../../../app/scripts/lib/enums'
import NetworkDisplay from '../../network-display'
export default class ConfirmPageContainer extends Component {
static contextTypes = {
t: PropTypes.func,
}
static propTypes = {
showEdit: PropTypes.bool,
onEdit: PropTypes.func,
children: PropTypes.node,
}
renderTop () {
const { onEdit, showEdit } = this.props
const windowType = window.METAMASK_UI_TYPE
const isFullScreen = windowType !== ENVIRONMENT_TYPE_NOTIFICATION &&
windowType !== ENVIRONMENT_TYPE_POPUP
if (!showEdit && isFullScreen) {
return null
}
return (
<div className="confirm-page-container-header__row">
<div
className="confirm-page-container-header__back-button-container"
style={{
visibility: showEdit ? 'initial' : 'hidden',
}}
>
<img
src="/images/caret-left.svg"
/>
<span
className="confirm-page-container-header__back-button"
onClick={() => onEdit()}
>
{ this.context.t('edit') }
</span>
</div>
{ !isFullScreen && <NetworkDisplay /> }
</div>
)
}
render () {
const { children } = this.props
return (
<div className="confirm-page-container-header">
{ this.renderTop() }
{ children }
</div>
)
}
}

View File

@ -1 +0,0 @@
export { default } from './confirm-page-container-header.component'

View File

@ -1,27 +0,0 @@
.confirm-page-container-header {
display: flex;
flex-direction: column;
flex: 0 0 auto;
&__row {
display: flex;
justify-content: space-between;
border-bottom: 1px solid $geyser;
padding: 13px 13px 13px 24px;
flex: 0 0 auto;
}
&__back-button-container {
display: flex;
justify-content: center;
align-items: center;
}
&__back-button {
color: #2f9ae0;
font-size: 1rem;
cursor: pointer;
font-weight: 400;
padding-left: 5px;
}
}

View File

@ -1,137 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import SenderToRecipient from '../sender-to-recipient'
import { PageContainerFooter } from '../page-container'
import { ConfirmPageContainerHeader, ConfirmPageContainerContent } from './'
export default class ConfirmPageContainer extends Component {
static contextTypes = {
t: PropTypes.func,
}
static propTypes = {
// Header
action: PropTypes.string,
hideSubtitle: PropTypes.bool,
onEdit: PropTypes.func,
showEdit: PropTypes.bool,
subtitle: PropTypes.string,
subtitleComponent: PropTypes.node,
title: PropTypes.string,
titleComponent: PropTypes.node,
// Sender to Recipient
fromAddress: PropTypes.string,
fromName: PropTypes.string,
toAddress: PropTypes.string,
toName: PropTypes.string,
// Content
contentComponent: PropTypes.node,
errorKey: PropTypes.string,
errorMessage: PropTypes.string,
fiatTransactionAmount: PropTypes.string,
fiatTransactionFee: PropTypes.string,
fiatTransactionTotal: PropTypes.string,
ethTransactionAmount: PropTypes.string,
ethTransactionFee: PropTypes.string,
ethTransactionTotal: PropTypes.string,
onEditGas: PropTypes.func,
dataComponent: PropTypes.node,
detailsComponent: PropTypes.node,
identiconAddress: PropTypes.string,
nonce: PropTypes.string,
assetImage: PropTypes.string,
summaryComponent: PropTypes.node,
warning: PropTypes.string,
unapprovedTxCount: PropTypes.number,
// Footer
onCancelAll: PropTypes.func,
onCancel: PropTypes.func,
onSubmit: PropTypes.func,
disabled: PropTypes.bool,
}
render () {
const {
showEdit,
onEdit,
fromName,
fromAddress,
toName,
toAddress,
disabled,
errorKey,
errorMessage,
contentComponent,
action,
title,
titleComponent,
subtitle,
subtitleComponent,
hideSubtitle,
summaryComponent,
detailsComponent,
dataComponent,
onCancelAll,
onCancel,
onSubmit,
identiconAddress,
nonce,
unapprovedTxCount,
assetImage,
warning,
} = this.props
const renderAssetImage = contentComponent || (!contentComponent && !identiconAddress)
return (
<div className="page-container">
<ConfirmPageContainerHeader
showEdit={showEdit}
onEdit={() => onEdit()}
>
<SenderToRecipient
senderName={fromName}
senderAddress={fromAddress}
recipientName={toName}
recipientAddress={toAddress}
assetImage={renderAssetImage ? assetImage : undefined}
/>
</ConfirmPageContainerHeader>
{
contentComponent || (
<ConfirmPageContainerContent
action={action}
title={title}
titleComponent={titleComponent}
subtitle={subtitle}
subtitleComponent={subtitleComponent}
hideSubtitle={hideSubtitle}
summaryComponent={summaryComponent}
detailsComponent={detailsComponent}
dataComponent={dataComponent}
errorMessage={errorMessage}
errorKey={errorKey}
identiconAddress={identiconAddress}
nonce={nonce}
assetImage={assetImage}
warning={warning}
/>
)
}
<PageContainerFooter
onCancel={() => onCancel()}
cancelText={this.context.t('reject')}
onSubmit={() => onSubmit()}
submitText={this.context.t('confirm')}
submitButtonType="confirm"
disabled={disabled}
>
{unapprovedTxCount > 1 && (
<a onClick={() => onCancelAll()}>
{this.context.t('rejectTxsN', [unapprovedTxCount])}
</a>
)}
</PageContainerFooter>
</div>
)
}
}

View File

@ -1,8 +0,0 @@
export { default } from './confirm-page-container.component'
export { default as ConfirmPageContainerHeader } from './confirm-page-container-header'
export { default as ConfirmDetailRow } from './confirm-detail-row'
export {
default as ConfirmPageContainerContent,
ConfirmPageContainerSummary,
ConfirmPageContainerError,
} from './confirm-page-container-content'

View File

@ -1,5 +0,0 @@
@import './confirm-page-container-content/index';
@import './confirm-page-container-header/index';
@import './confirm-detail-row/index';

View File

@ -1,66 +0,0 @@
const Component = require('react').Component
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const copyToClipboard = require('copy-to-clipboard')
const connect = require('react-redux').connect
const Tooltip = require('./tooltip')
CopyButton.contextTypes = {
t: PropTypes.func,
}
module.exports = connect()(CopyButton)
inherits(CopyButton, Component)
function CopyButton () {
Component.call(this)
}
// As parameters, accepts:
// "value", which is the value to copy (mandatory)
// "title", which is the text to show on hover (optional, defaults to 'Copy')
CopyButton.prototype.render = function () {
const props = this.props
const state = this.state || {}
const value = props.value
const copied = state.copied
const message = copied ? this.context.t('copiedButton') : props.title || this.context.t('copyButton')
return h('.copy-button', {
style: {
display: 'flex',
alignItems: 'center',
},
}, [
h(Tooltip, {
title: message,
}, [
h('i.fa.fa-clipboard.cursor-pointer.color-orange', {
style: {
margin: '5px',
},
onClick: (event) => {
event.preventDefault()
event.stopPropagation()
copyToClipboard(value)
this.debounceRestore()
},
}),
]),
])
}
CopyButton.prototype.debounceRestore = function () {
this.setState({ copied: true })
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.setState({ copied: false })
}, 850)
}

View File

@ -1,53 +0,0 @@
const Component = require('react').Component
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const Tooltip = require('./tooltip')
const copyToClipboard = require('copy-to-clipboard')
const connect = require('react-redux').connect
Copyable.contextTypes = {
t: PropTypes.func,
}
module.exports = connect()(Copyable)
inherits(Copyable, Component)
function Copyable () {
Component.call(this)
this.state = {
copied: false,
}
}
Copyable.prototype.render = function () {
const props = this.props
const state = this.state
const { value, children } = props
const { copied } = state
return h(Tooltip, {
title: copied ? this.context.t('copiedExclamation') : this.context.t('copy'),
position: 'bottom',
}, h('span', {
style: {
cursor: 'pointer',
},
onClick: (event) => {
event.preventDefault()
event.stopPropagation()
copyToClipboard(value)
this.debounceRestore()
},
}, children))
}
Copyable.prototype.debounceRestore = function () {
this.setState({ copied: true })
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.setState({ copied: false })
}, 850)
}

View File

@ -1,36 +0,0 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import { ETH, GWEI } from '../../constants/common'
export default class CurrencyDisplay extends PureComponent {
static propTypes = {
className: PropTypes.string,
displayValue: PropTypes.string,
prefix: PropTypes.string,
prefixComponent: PropTypes.node,
style: PropTypes.object,
// Used in container
currency: PropTypes.oneOf([ETH]),
denomination: PropTypes.oneOf([GWEI]),
value: PropTypes.string,
numberOfDecimals: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
hideLabel: PropTypes.bool,
}
render () {
const { className, displayValue, prefix, prefixComponent, style } = this.props
const text = `${prefix || ''}${displayValue}`
return (
<div
className={classnames('currency-display-component', className)}
style={style}
title={text}
>
{ prefixComponent}
<span className="currency-display-component__text">{ text }</span>
</div>
)
}
}

View File

@ -1,40 +0,0 @@
import { connect } from 'react-redux'
import CurrencyDisplay from './currency-display.component'
import { getValueFromWeiHex, formatCurrency } from '../../helpers/confirm-transaction/util'
const mapStateToProps = state => {
const { metamask: { currentCurrency, conversionRate } } = state
return {
currentCurrency,
conversionRate,
}
}
const mergeProps = (stateProps, dispatchProps, ownProps) => {
const { currentCurrency, conversionRate, ...restStateProps } = stateProps
const {
value,
numberOfDecimals = 2,
currency,
denomination,
hideLabel,
...restOwnProps
} = ownProps
const toCurrency = currency || currentCurrency
const convertedValue = getValueFromWeiHex({
value, toCurrency, conversionRate, numberOfDecimals, toDenomination: denomination,
})
const formattedValue = formatCurrency(convertedValue, toCurrency)
const displayValue = hideLabel ? formattedValue : `${formattedValue} ${toCurrency.toUpperCase()}`
return {
...restStateProps,
...dispatchProps,
...restOwnProps,
displayValue,
}
}
export default connect(mapStateToProps, null, mergeProps)(CurrencyDisplay)

View File

@ -1 +0,0 @@
export { default } from './currency-display.container'

View File

@ -1,10 +0,0 @@
.currency-display-component {
display: flex;
align-items: center;
&__text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}

View File

@ -1,27 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import CurrencyDisplay from '../currency-display.component'
describe('CurrencyDisplay Component', () => {
it('should render text with a className', () => {
const wrapper = shallow(<CurrencyDisplay
displayValue="$123.45"
className="currency-display"
/>)
assert.ok(wrapper.hasClass('currency-display'))
assert.equal(wrapper.text(), '$123.45')
})
it('should render text with a prefix', () => {
const wrapper = shallow(<CurrencyDisplay
displayValue="$123.45"
className="currency-display"
prefix="-"
/>)
assert.ok(wrapper.hasClass('currency-display'))
assert.equal(wrapper.text(), '-$123.45')
})
})

View File

@ -1,120 +0,0 @@
import assert from 'assert'
import proxyquire from 'proxyquire'
let mapStateToProps, mergeProps
proxyquire('../currency-display.container.js', {
'react-redux': {
connect: (ms, md, mp) => {
mapStateToProps = ms
mergeProps = mp
return () => ({})
},
},
})
describe('CurrencyDisplay container', () => {
describe('mapStateToProps()', () => {
it('should return the correct props', () => {
const mockState = {
metamask: {
conversionRate: 280.45,
currentCurrency: 'usd',
},
}
assert.deepEqual(mapStateToProps(mockState), {
conversionRate: 280.45,
currentCurrency: 'usd',
})
})
})
describe('mergeProps()', () => {
it('should return the correct props', () => {
const mockStateProps = {
conversionRate: 280.45,
currentCurrency: 'usd',
}
const tests = [
{
props: {
value: '0x2386f26fc10000',
numberOfDecimals: 2,
currency: 'usd',
},
result: {
displayValue: '$2.80 USD',
},
},
{
props: {
value: '0x2386f26fc10000',
},
result: {
displayValue: '$2.80 USD',
},
},
{
props: {
value: '0x1193461d01595930',
currency: 'ETH',
numberOfDecimals: 3,
},
result: {
displayValue: '1.266 ETH',
},
},
{
props: {
value: '0x1193461d01595930',
currency: 'ETH',
numberOfDecimals: 3,
hideLabel: true,
},
result: {
displayValue: '1.266',
},
},
{
props: {
value: '0x3b9aca00',
currency: 'ETH',
denomination: 'GWEI',
hideLabel: true,
},
result: {
displayValue: '1',
},
},
{
props: {
value: '0x3b9aca00',
currency: 'ETH',
denomination: 'WEI',
hideLabel: true,
},
result: {
displayValue: '1000000000',
},
},
{
props: {
value: '0x3b9aca00',
currency: 'ETH',
numberOfDecimals: 100,
hideLabel: true,
},
result: {
displayValue: '1e-9',
},
},
]
tests.forEach(({ props, result }) => {
assert.deepEqual(mergeProps(mockStateProps, {}, { ...props }), result)
})
})
})
})

View File

@ -1,30 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
const ErrorMessage = (props, context) => {
const { errorMessage, errorKey } = props
const error = errorKey ? context.t(errorKey) : errorMessage
return (
<div className="error-message">
<img
src="/images/alert-red.svg"
className="error-message__icon"
/>
<div className="error-message__text">
{ `ALERT: ${error}` }
</div>
</div>
)
}
ErrorMessage.propTypes = {
errorMessage: PropTypes.string,
errorKey: PropTypes.string,
}
ErrorMessage.contextTypes = {
t: PropTypes.func,
}
export default ErrorMessage

View File

@ -1 +0,0 @@
export { default } from './error-message.component'

View File

@ -1,21 +0,0 @@
.error-message {
min-height: 32px;
border: 1px solid $monzo;
color: $monzo;
background: lighten($monzo, 56%);
border-radius: 4px;
font-size: .75rem;
display: flex;
justify-content: flex-start;
align-items: center;
padding: 8px 16px;
&__icon {
margin-right: 8px;
flex: 0 0 auto;
}
&__text {
overflow: auto;
}
}

View File

@ -1,36 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import ErrorMessage from '../error-message.component'
describe('ErrorMessage Component', () => {
const t = key => `translate ${key}`
it('should render a message from props.errorMessage', () => {
const wrapper = shallow(
<ErrorMessage
errorMessage="This is an error."
/>,
{ context: { t }}
)
assert.ok(wrapper)
assert.equal(wrapper.find('.error-message').length, 1)
assert.equal(wrapper.find('.error-message__icon').length, 1)
assert.equal(wrapper.find('.error-message__text').text(), 'ALERT: This is an error.')
})
it('should render a message translated from props.errorKey', () => {
const wrapper = shallow(
<ErrorMessage
errorKey="testKey"
/>,
{ context: { t }}
)
assert.ok(wrapper)
assert.equal(wrapper.find('.error-message').length, 1)
assert.equal(wrapper.find('.error-message__icon').length, 1)
assert.equal(wrapper.find('.error-message__text').text(), 'ALERT: translate testKey')
})
})

View File

@ -1,21 +0,0 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import { hexToDecimal } from '../../helpers/conversions.util'
export default class HexToDecimal extends PureComponent {
static propTypes = {
className: PropTypes.string,
value: PropTypes.string,
}
render () {
const { className, value } = this.props
const decimalValue = hexToDecimal(value)
return (
<div className={className}>
{ decimalValue }
</div>
)
}
}

View File

@ -1 +0,0 @@
export { default } from './hex-to-decimal.component'

View File

@ -1,26 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import HexToDecimal from '../hex-to-decimal.component'
describe('HexToDecimal Component', () => {
it('should render a prefixed hex as a decimal with a className', () => {
const wrapper = shallow(<HexToDecimal
value="0x3039"
className="hex-to-decimal"
/>)
assert.ok(wrapper.hasClass('hex-to-decimal'))
assert.equal(wrapper.text(), '12345')
})
it('should render an unprefixed hex as a decimal with a className', () => {
const wrapper = shallow(<HexToDecimal
value="1A85"
className="hex-to-decimal"
/>)
assert.ok(wrapper.hasClass('hex-to-decimal'))
assert.equal(wrapper.text(), '6789')
})
})

View File

@ -1,81 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const {
addCurrencies,
conversionGTE,
conversionLTE,
subtractCurrencies,
} = require('../conversion-util')
module.exports = InputNumber
inherits(InputNumber, Component)
function InputNumber () {
Component.call(this)
this.setValue = this.setValue.bind(this)
}
function isValidInput (text) {
const re = /^([1-9]\d*|0)(\.|\.\d*)?$/
return re.test(text)
}
function removeLeadingZeroes (str) {
return str.replace(/^0*(?=\d)/, '')
}
InputNumber.prototype.setValue = function (newValue) {
newValue = removeLeadingZeroes(newValue)
if (newValue && !isValidInput(newValue)) return
const { fixed, min = -1, max = Infinity, onChange } = this.props
newValue = fixed ? newValue.toFixed(4) : newValue
const newValueGreaterThanMin = conversionGTE(
{ value: newValue || '0', fromNumericBase: 'dec' },
{ value: min, fromNumericBase: 'hex' },
)
const newValueLessThanMax = conversionLTE(
{ value: newValue || '0', fromNumericBase: 'dec' },
{ value: max, fromNumericBase: 'hex' },
)
if (newValueGreaterThanMin && newValueLessThanMax) {
onChange(newValue)
} else if (!newValueGreaterThanMin) {
onChange(min)
} else if (!newValueLessThanMax) {
onChange(max)
}
}
InputNumber.prototype.render = function () {
const { unitLabel, step = 1, placeholder, value } = this.props
return h('div.customize-gas-input-wrapper', {}, [
h('input', {
className: 'customize-gas-input',
value,
placeholder,
type: 'number',
onChange: e => {
this.setValue(e.target.value)
},
min: 0,
}),
h('span.gas-tooltip-input-detail', {}, [unitLabel]),
h('div.gas-tooltip-input-arrows', {}, [
h('div.gas-tooltip-input-arrow-wrapper', {
onClick: () => this.setValue(addCurrencies(value, step, { toNumericBase: 'dec' })),
}, [
h('i.fa.fa-angle-up'),
]),
h('div.gas-tooltip-input-arrow-wrapper', {
onClick: () => this.setValue(subtractCurrencies(value, step, { toNumericBase: 'dec' })),
}, [
h('i.fa.fa-angle-down'),
]),
]),
])
}

View File

@ -1,76 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { checksumAddress } from '../../../util'
import Identicon from '../../identicon'
import UserPreferencedCurrencyDisplay from '../../user-preferenced-currency-display'
import { PRIMARY, SECONDARY } from '../../../constants/common'
export default class AccountListItem extends Component {
static propTypes = {
account: PropTypes.object,
className: PropTypes.string,
conversionRate: PropTypes.number,
currentCurrency: PropTypes.string,
displayAddress: PropTypes.bool,
displayBalance: PropTypes.bool,
handleClick: PropTypes.func,
icon: PropTypes.node,
};
static contextTypes = {
t: PropTypes.func,
};
render () {
const {
account,
className,
displayAddress = false,
displayBalance = true,
handleClick,
icon = null,
} = this.props
const { name, address, balance } = account || {}
return (<div
className={`account-list-item ${className}`}
onClick={() => handleClick({ name, address, balance })}
>
<div className="account-list-item__top-row">
<Identicon
address={address}
className="account-list-item__identicon"
diameter={18}
/>
<div className="account-list-item__account-name">{ name || address }</div>
{icon && <div className="account-list-item__icon">{ icon }</div>}
</div>
{displayAddress && name && <div className="account-list-item__account-address">
{ checksumAddress(address) }
</div>}
{
displayBalance && (
<div className="account-list-item__account-balances">
<UserPreferencedCurrencyDisplay
type={PRIMARY}
value={balance}
/>
<UserPreferencedCurrencyDisplay
type={SECONDARY}
value={balance}
/>
</div>
)
}
</div>)
}
}

View File

@ -1,15 +0,0 @@
import { connect } from 'react-redux'
import {
getConversionRate,
getCurrentCurrency,
} from '../send.selectors.js'
import AccountListItem from './account-list-item.component'
export default connect(mapStateToProps)(AccountListItem)
function mapStateToProps (state) {
return {
conversionRate: getConversionRate(state),
currentCurrency: getCurrentCurrency(state),
}
}

View File

@ -1 +0,0 @@
export { default } from './account-list-item.container'

View File

@ -1,132 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import sinon from 'sinon'
import proxyquire from 'proxyquire'
import Identicon from '../../../identicon'
import UserPreferencedCurrencyDisplay from '../../../user-preferenced-currency-display'
const utilsMethodStubs = {
checksumAddress: sinon.stub().returns('mockCheckSumAddress'),
}
const AccountListItem = proxyquire('../account-list-item.component.js', {
'../../../util': utilsMethodStubs,
}).default
const propsMethodSpies = {
handleClick: sinon.spy(),
}
describe('AccountListItem Component', function () {
let wrapper
beforeEach(() => {
wrapper = shallow(<AccountListItem
account={ { address: 'mockAddress', name: 'mockName', balance: 'mockBalance' } }
className={'mockClassName'}
conversionRate={4}
currentCurrency={'mockCurrentyCurrency'}
displayAddress={false}
displayBalance={false}
handleClick={propsMethodSpies.handleClick}
icon={<i className="mockIcon" />}
/>, { context: { t: str => str + '_t' } })
})
afterEach(() => {
propsMethodSpies.handleClick.resetHistory()
})
describe('render', () => {
it('should render a div with the passed className', () => {
assert.equal(wrapper.find('.mockClassName').length, 1)
assert(wrapper.find('.mockClassName').is('div'))
assert(wrapper.find('.mockClassName').hasClass('account-list-item'))
})
it('should call handleClick with the expected props when the root div is clicked', () => {
const { onClick } = wrapper.find('.mockClassName').props()
assert.equal(propsMethodSpies.handleClick.callCount, 0)
onClick()
assert.equal(propsMethodSpies.handleClick.callCount, 1)
assert.deepEqual(
propsMethodSpies.handleClick.getCall(0).args,
[{ address: 'mockAddress', name: 'mockName', balance: 'mockBalance' }]
)
})
it('should have a top row div', () => {
assert.equal(wrapper.find('.mockClassName > .account-list-item__top-row').length, 1)
assert(wrapper.find('.mockClassName > .account-list-item__top-row').is('div'))
})
it('should have an identicon, name and icon in the top row', () => {
const topRow = wrapper.find('.mockClassName > .account-list-item__top-row')
assert.equal(topRow.find(Identicon).length, 1)
assert.equal(topRow.find('.account-list-item__account-name').length, 1)
assert.equal(topRow.find('.account-list-item__icon').length, 1)
})
it('should show the account name if it exists', () => {
const topRow = wrapper.find('.mockClassName > .account-list-item__top-row')
assert.equal(topRow.find('.account-list-item__account-name').text(), 'mockName')
})
it('should show the account address if there is no name', () => {
wrapper.setProps({ account: { address: 'addressButNoName' } })
const topRow = wrapper.find('.mockClassName > .account-list-item__top-row')
assert.equal(topRow.find('.account-list-item__account-name').text(), 'addressButNoName')
})
it('should render the passed icon', () => {
const topRow = wrapper.find('.mockClassName > .account-list-item__top-row')
assert(topRow.find('.account-list-item__icon').childAt(0).is('i'))
assert(topRow.find('.account-list-item__icon').childAt(0).hasClass('mockIcon'))
})
it('should not render an icon if none is passed', () => {
wrapper.setProps({ icon: null })
const topRow = wrapper.find('.mockClassName > .account-list-item__top-row')
assert.equal(topRow.find('.account-list-item__icon').length, 0)
})
it('should render the account address as a checksumAddress if displayAddress is true and name is provided', () => {
wrapper.setProps({ displayAddress: true })
assert.equal(wrapper.find('.account-list-item__account-address').length, 1)
assert.equal(wrapper.find('.account-list-item__account-address').text(), 'mockCheckSumAddress')
assert.deepEqual(
utilsMethodStubs.checksumAddress.getCall(0).args,
['mockAddress']
)
})
it('should not render the account address as a checksumAddress if displayAddress is false', () => {
wrapper.setProps({ displayAddress: false })
assert.equal(wrapper.find('.account-list-item__account-address').length, 0)
})
it('should not render the account address as a checksumAddress if name is not provided', () => {
wrapper.setProps({ account: { address: 'someAddressButNoName' } })
assert.equal(wrapper.find('.account-list-item__account-address').length, 0)
})
it('should render a CurrencyDisplay with the correct props if displayBalance is true', () => {
wrapper.setProps({ displayBalance: true })
assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 2)
assert.deepEqual(
wrapper.find(UserPreferencedCurrencyDisplay).at(0).props(),
{
type: 'PRIMARY',
value: 'mockBalance',
}
)
})
it('should not render a CurrencyDisplay if displayBalance is false', () => {
wrapper.setProps({ displayBalance: false })
assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 0)
})
})
})

View File

@ -1,32 +0,0 @@
import assert from 'assert'
import proxyquire from 'proxyquire'
let mapStateToProps
proxyquire('../account-list-item.container.js', {
'react-redux': {
connect: (ms, md) => {
mapStateToProps = ms
return () => ({})
},
},
'../send.selectors.js': {
getConversionRate: (s) => `mockConversionRate:${s}`,
getCurrentCurrency: (s) => `mockCurrentCurrency:${s}`,
},
})
describe('account-list-item container', () => {
describe('mapStateToProps()', () => {
it('should map the correct properties to props', () => {
assert.deepEqual(mapStateToProps('mockState'), {
conversionRate: 'mockConversionRate:mockState',
currentCurrency: 'mockCurrentCurrency:mockState',
})
})
})
})

View File

@ -1 +0,0 @@
export { default } from './send.container'

View File

@ -1 +0,0 @@
export { default } from './send-content.component'

View File

@ -1,60 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class AmountMaxButton extends Component {
static propTypes = {
balance: PropTypes.string,
gasTotal: PropTypes.string,
maxModeOn: PropTypes.bool,
selectedToken: PropTypes.object,
setAmountToMax: PropTypes.func,
setMaxModeTo: PropTypes.func,
tokenBalance: PropTypes.string,
};
static contextTypes = {
t: PropTypes.func,
};
setMaxAmount () {
const {
balance,
gasTotal,
selectedToken,
setAmountToMax,
tokenBalance,
} = this.props
setAmountToMax({
balance,
gasTotal,
selectedToken,
tokenBalance,
})
}
onMaxClick = (event) => {
const { setMaxModeTo } = this.props
event.preventDefault()
setMaxModeTo(true)
this.setMaxAmount()
}
render () {
return this.props.maxModeOn
? null
: (
<div>
<span
className="send-v2__amount-max"
onClick={this.onMaxClick}
>
{this.context.t('max')}
</span>
</div>
)
}
}

View File

@ -1,40 +0,0 @@
import { connect } from 'react-redux'
import {
getGasTotal,
getSelectedToken,
getSendFromBalance,
getTokenBalance,
} from '../../../send.selectors.js'
import { getMaxModeOn } from './amount-max-button.selectors.js'
import { calcMaxAmount } from './amount-max-button.utils.js'
import {
updateSendAmount,
setMaxModeTo,
} from '../../../../../actions'
import AmountMaxButton from './amount-max-button.component'
import {
updateSendErrors,
} from '../../../../../ducks/send.duck'
export default connect(mapStateToProps, mapDispatchToProps)(AmountMaxButton)
function mapStateToProps (state) {
return {
balance: getSendFromBalance(state),
gasTotal: getGasTotal(state),
maxModeOn: getMaxModeOn(state),
selectedToken: getSelectedToken(state),
tokenBalance: getTokenBalance(state),
}
}
function mapDispatchToProps (dispatch) {
return {
setAmountToMax: maxAmountDataObject => {
dispatch(updateSendErrors({ amount: null }))
dispatch(updateSendAmount(calcMaxAmount(maxAmountDataObject)))
},
setMaxModeTo: bool => dispatch(setMaxModeTo(bool)),
}
}

View File

@ -1,9 +0,0 @@
const selectors = {
getMaxModeOn,
}
module.exports = selectors
function getMaxModeOn (state) {
return state.metamask.send.maxModeOn
}

View File

@ -1,22 +0,0 @@
const {
multiplyCurrencies,
subtractCurrencies,
} = require('../../../../../conversion-util')
const ethUtil = require('ethereumjs-util')
function calcMaxAmount ({ balance, gasTotal, selectedToken, tokenBalance }) {
const { decimals } = selectedToken || {}
const multiplier = Math.pow(10, Number(decimals || 0))
return selectedToken
? multiplyCurrencies(tokenBalance, multiplier, {toNumericBase: 'hex'})
: subtractCurrencies(
ethUtil.addHexPrefix(balance),
ethUtil.addHexPrefix(gasTotal),
{ toNumericBase: 'hex' }
)
}
module.exports = {
calcMaxAmount,
}

View File

@ -1 +0,0 @@
export { default } from './amount-max-button.container'

View File

@ -1,89 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import sinon from 'sinon'
import AmountMaxButton from '../amount-max-button.component.js'
const propsMethodSpies = {
setAmountToMax: sinon.spy(),
setMaxModeTo: sinon.spy(),
}
const MOCK_EVENT = { preventDefault: () => {} }
sinon.spy(AmountMaxButton.prototype, 'setMaxAmount')
describe('AmountMaxButton Component', function () {
let wrapper
let instance
beforeEach(() => {
wrapper = shallow(<AmountMaxButton
balance={'mockBalance'}
gasTotal={'mockGasTotal'}
maxModeOn={false}
selectedToken={ { address: 'mockTokenAddress' } }
setAmountToMax={propsMethodSpies.setAmountToMax}
setMaxModeTo={propsMethodSpies.setMaxModeTo}
tokenBalance={'mockTokenBalance'}
/>, { context: { t: str => str + '_t' } })
instance = wrapper.instance()
})
afterEach(() => {
propsMethodSpies.setAmountToMax.resetHistory()
propsMethodSpies.setMaxModeTo.resetHistory()
AmountMaxButton.prototype.setMaxAmount.resetHistory()
})
describe('setMaxAmount', () => {
it('should call setAmountToMax with the correct params', () => {
assert.equal(propsMethodSpies.setAmountToMax.callCount, 0)
instance.setMaxAmount()
assert.equal(propsMethodSpies.setAmountToMax.callCount, 1)
assert.deepEqual(
propsMethodSpies.setAmountToMax.getCall(0).args,
[{
balance: 'mockBalance',
gasTotal: 'mockGasTotal',
selectedToken: { address: 'mockTokenAddress' },
tokenBalance: 'mockTokenBalance',
}]
)
})
})
describe('render', () => {
it('should render an element with a send-v2__amount-max class', () => {
assert(wrapper.exists('.send-v2__amount-max'))
})
it('should call setMaxModeTo and setMaxAmount when the send-v2__amount-max div is clicked', () => {
const {
onClick,
} = wrapper.find('.send-v2__amount-max').props()
assert.equal(AmountMaxButton.prototype.setMaxAmount.callCount, 0)
assert.equal(propsMethodSpies.setMaxModeTo.callCount, 0)
onClick(MOCK_EVENT)
assert.equal(AmountMaxButton.prototype.setMaxAmount.callCount, 1)
assert.equal(propsMethodSpies.setMaxModeTo.callCount, 1)
assert.deepEqual(
propsMethodSpies.setMaxModeTo.getCall(0).args,
[true]
)
})
it('should not render anything when maxModeOn is true', () => {
wrapper.setProps({ maxModeOn: true })
assert.ok(!wrapper.exists('.send-v2__amount-max'))
})
it('should render the expected text when maxModeOn is false', () => {
wrapper.setProps({ maxModeOn: false })
assert.equal(wrapper.find('.send-v2__amount-max').text(), 'max_t')
})
})
})

View File

@ -1,91 +0,0 @@
import assert from 'assert'
import proxyquire from 'proxyquire'
import sinon from 'sinon'
let mapStateToProps
let mapDispatchToProps
const actionSpies = {
setMaxModeTo: sinon.spy(),
updateSendAmount: sinon.spy(),
}
const duckActionSpies = {
updateSendErrors: sinon.spy(),
}
proxyquire('../amount-max-button.container.js', {
'react-redux': {
connect: (ms, md) => {
mapStateToProps = ms
mapDispatchToProps = md
return () => ({})
},
},
'../../../send.selectors.js': {
getGasTotal: (s) => `mockGasTotal:${s}`,
getSelectedToken: (s) => `mockSelectedToken:${s}`,
getSendFromBalance: (s) => `mockBalance:${s}`,
getTokenBalance: (s) => `mockTokenBalance:${s}`,
},
'./amount-max-button.selectors.js': { getMaxModeOn: (s) => `mockMaxModeOn:${s}` },
'./amount-max-button.utils.js': { calcMaxAmount: (mockObj) => mockObj.val + 1 },
'../../../../../actions': actionSpies,
'../../../../../ducks/send.duck': duckActionSpies,
})
describe('amount-max-button container', () => {
describe('mapStateToProps()', () => {
it('should map the correct properties to props', () => {
assert.deepEqual(mapStateToProps('mockState'), {
balance: 'mockBalance:mockState',
gasTotal: 'mockGasTotal:mockState',
maxModeOn: 'mockMaxModeOn:mockState',
selectedToken: 'mockSelectedToken:mockState',
tokenBalance: 'mockTokenBalance:mockState',
})
})
})
describe('mapDispatchToProps()', () => {
let dispatchSpy
let mapDispatchToPropsObject
beforeEach(() => {
dispatchSpy = sinon.spy()
mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy)
})
describe('setAmountToMax()', () => {
it('should dispatch an action', () => {
mapDispatchToPropsObject.setAmountToMax({ val: 11, foo: 'bar' })
assert(dispatchSpy.calledTwice)
assert(duckActionSpies.updateSendErrors.calledOnce)
assert.deepEqual(
duckActionSpies.updateSendErrors.getCall(0).args[0],
{ amount: null }
)
assert(actionSpies.updateSendAmount.calledOnce)
assert.equal(
actionSpies.updateSendAmount.getCall(0).args[0],
12
)
})
})
describe('setMaxModeTo()', () => {
it('should dispatch an action', () => {
mapDispatchToPropsObject.setMaxModeTo('mockVal')
assert(dispatchSpy.calledOnce)
assert.equal(
actionSpies.setMaxModeTo.getCall(0).args[0],
'mockVal'
)
})
})
})
})

View File

@ -1,22 +0,0 @@
import assert from 'assert'
import {
getMaxModeOn,
} from '../amount-max-button.selectors.js'
describe('amount-max-button selectors', () => {
describe('getMaxModeOn()', () => {
it('should', () => {
const state = {
metamask: {
send: {
maxModeOn: null,
},
},
}
assert.equal(getMaxModeOn(state), null)
})
})
})

View File

@ -1,27 +0,0 @@
import assert from 'assert'
import {
calcMaxAmount,
} from '../amount-max-button.utils.js'
describe('amount-max-button utils', () => {
describe('calcMaxAmount()', () => {
it('should calculate the correct amount when no selectedToken defined', () => {
assert.deepEqual(calcMaxAmount({
balance: 'ffffff',
gasTotal: 'ff',
selectedToken: false,
}), 'ffff00')
})
it('should calculate the correct amount when a selectedToken is defined', () => {
assert.deepEqual(calcMaxAmount({
selectedToken: {
decimals: 10,
},
tokenBalance: 100,
}), 'e8d4a51000')
})
})
})

View File

@ -1 +0,0 @@
export { default } from './send-amount-row.container'

View File

@ -1,120 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import SendRowWrapper from '../send-row-wrapper/'
import AmountMaxButton from './amount-max-button/'
import UserPreferencedCurrencyInput from '../../../user-preferenced-currency-input'
import UserPreferencedTokenInput from '../../../user-preferenced-token-input'
export default class SendAmountRow extends Component {
static propTypes = {
amount: PropTypes.string,
amountConversionRate: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
balance: PropTypes.string,
conversionRate: PropTypes.number,
convertedCurrency: PropTypes.string,
gasTotal: PropTypes.string,
inError: PropTypes.bool,
primaryCurrency: PropTypes.string,
selectedToken: PropTypes.object,
setMaxModeTo: PropTypes.func,
tokenBalance: PropTypes.string,
updateGasFeeError: PropTypes.func,
updateSendAmount: PropTypes.func,
updateSendAmountError: PropTypes.func,
updateGas: PropTypes.func,
};
static contextTypes = {
t: PropTypes.func,
};
validateAmount (amount) {
const {
amountConversionRate,
balance,
conversionRate,
gasTotal,
primaryCurrency,
selectedToken,
tokenBalance,
updateGasFeeError,
updateSendAmountError,
} = this.props
updateSendAmountError({
amount,
amountConversionRate,
balance,
conversionRate,
gasTotal,
primaryCurrency,
selectedToken,
tokenBalance,
})
if (selectedToken) {
updateGasFeeError({
amount,
amountConversionRate,
balance,
conversionRate,
gasTotal,
primaryCurrency,
selectedToken,
tokenBalance,
})
}
}
updateAmount (amount) {
const { updateSendAmount, setMaxModeTo } = this.props
setMaxModeTo(false)
updateSendAmount(amount)
}
updateGas (amount) {
const { selectedToken, updateGas } = this.props
if (selectedToken) {
updateGas({ amount })
}
}
renderInput () {
const { amount, inError, selectedToken } = this.props
const Component = selectedToken ? UserPreferencedTokenInput : UserPreferencedCurrencyInput
return (
<Component
onChange={newAmount => this.validateAmount(newAmount)}
onBlur={newAmount => {
this.updateGas(newAmount)
this.updateAmount(newAmount)
}}
error={inError}
value={amount}
/>
)
}
render () {
const { gasTotal, inError } = this.props
return (
<SendRowWrapper
label={`${this.context.t('amount')}:`}
showError={inError}
errorType={'amount'}
>
{!inError && gasTotal && <AmountMaxButton />}
{ this.renderInput() }
</SendRowWrapper>
)
}
}

View File

@ -1,54 +0,0 @@
import { connect } from 'react-redux'
import {
getAmountConversionRate,
getConversionRate,
getCurrentCurrency,
getGasTotal,
getPrimaryCurrency,
getSelectedToken,
getSendAmount,
getSendFromBalance,
getTokenBalance,
} from '../../send.selectors'
import {
sendAmountIsInError,
} from './send-amount-row.selectors'
import { getAmountErrorObject, getGasFeeErrorObject } from '../../send.utils'
import {
setMaxModeTo,
updateSendAmount,
} from '../../../../actions'
import {
updateSendErrors,
} from '../../../../ducks/send.duck'
import SendAmountRow from './send-amount-row.component'
export default connect(mapStateToProps, mapDispatchToProps)(SendAmountRow)
function mapStateToProps (state) {
return {
amount: getSendAmount(state),
amountConversionRate: getAmountConversionRate(state),
balance: getSendFromBalance(state),
conversionRate: getConversionRate(state),
convertedCurrency: getCurrentCurrency(state),
gasTotal: getGasTotal(state),
inError: sendAmountIsInError(state),
primaryCurrency: getPrimaryCurrency(state),
selectedToken: getSelectedToken(state),
tokenBalance: getTokenBalance(state),
}
}
function mapDispatchToProps (dispatch) {
return {
setMaxModeTo: bool => dispatch(setMaxModeTo(bool)),
updateSendAmount: newAmount => dispatch(updateSendAmount(newAmount)),
updateGasFeeError: (amountDataObject) => {
dispatch(updateSendErrors(getGasFeeErrorObject(amountDataObject)))
},
updateSendAmountError: (amountDataObject) => {
dispatch(updateSendErrors(getAmountErrorObject(amountDataObject)))
},
}
}

View File

@ -1,9 +0,0 @@
const selectors = {
sendAmountIsInError,
}
module.exports = selectors
function sendAmountIsInError (state) {
return Boolean(state.send.errors.amount)
}

View File

@ -1,189 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import sinon from 'sinon'
import SendAmountRow from '../send-amount-row.component.js'
import SendRowWrapper from '../../send-row-wrapper/send-row-wrapper.component'
import AmountMaxButton from '../amount-max-button/amount-max-button.container'
import UserPreferencedTokenInput from '../../../../user-preferenced-token-input'
const propsMethodSpies = {
setMaxModeTo: sinon.spy(),
updateSendAmount: sinon.spy(),
updateSendAmountError: sinon.spy(),
updateGas: sinon.spy(),
updateGasFeeError: sinon.spy(),
}
sinon.spy(SendAmountRow.prototype, 'updateAmount')
sinon.spy(SendAmountRow.prototype, 'validateAmount')
sinon.spy(SendAmountRow.prototype, 'updateGas')
describe('SendAmountRow Component', function () {
let wrapper
let instance
beforeEach(() => {
wrapper = shallow(<SendAmountRow
amount={'mockAmount'}
amountConversionRate={'mockAmountConversionRate'}
balance={'mockBalance'}
conversionRate={7}
convertedCurrency={'mockConvertedCurrency'}
gasTotal={'mockGasTotal'}
inError={false}
primaryCurrency={'mockPrimaryCurrency'}
selectedToken={ { address: 'mockTokenAddress' } }
setMaxModeTo={propsMethodSpies.setMaxModeTo}
tokenBalance={'mockTokenBalance'}
updateGasFeeError={propsMethodSpies.updateGasFeeError}
updateSendAmount={propsMethodSpies.updateSendAmount}
updateSendAmountError={propsMethodSpies.updateSendAmountError}
updateGas={propsMethodSpies.updateGas}
/>, { context: { t: str => str + '_t' } })
instance = wrapper.instance()
})
afterEach(() => {
propsMethodSpies.setMaxModeTo.resetHistory()
propsMethodSpies.updateSendAmount.resetHistory()
propsMethodSpies.updateSendAmountError.resetHistory()
propsMethodSpies.updateGasFeeError.resetHistory()
SendAmountRow.prototype.validateAmount.resetHistory()
SendAmountRow.prototype.updateAmount.resetHistory()
})
describe('validateAmount', () => {
it('should call updateSendAmountError with the correct params', () => {
assert.equal(propsMethodSpies.updateSendAmountError.callCount, 0)
instance.validateAmount('someAmount')
assert.equal(propsMethodSpies.updateSendAmountError.callCount, 1)
assert.deepEqual(
propsMethodSpies.updateSendAmountError.getCall(0).args,
[{
amount: 'someAmount',
amountConversionRate: 'mockAmountConversionRate',
balance: 'mockBalance',
conversionRate: 7,
gasTotal: 'mockGasTotal',
primaryCurrency: 'mockPrimaryCurrency',
selectedToken: { address: 'mockTokenAddress' },
tokenBalance: 'mockTokenBalance',
}]
)
})
it('should call updateGasFeeError if selectedToken is truthy', () => {
assert.equal(propsMethodSpies.updateGasFeeError.callCount, 0)
instance.validateAmount('someAmount')
assert.equal(propsMethodSpies.updateGasFeeError.callCount, 1)
assert.deepEqual(
propsMethodSpies.updateGasFeeError.getCall(0).args,
[{
amount: 'someAmount',
amountConversionRate: 'mockAmountConversionRate',
balance: 'mockBalance',
conversionRate: 7,
gasTotal: 'mockGasTotal',
primaryCurrency: 'mockPrimaryCurrency',
selectedToken: { address: 'mockTokenAddress' },
tokenBalance: 'mockTokenBalance',
}]
)
})
it('should call not updateGasFeeError if selectedToken is falsey', () => {
wrapper.setProps({ selectedToken: null })
assert.equal(propsMethodSpies.updateGasFeeError.callCount, 0)
instance.validateAmount('someAmount')
assert.equal(propsMethodSpies.updateGasFeeError.callCount, 0)
})
})
describe('updateAmount', () => {
it('should call setMaxModeTo', () => {
assert.equal(propsMethodSpies.setMaxModeTo.callCount, 0)
instance.updateAmount('someAmount')
assert.equal(propsMethodSpies.setMaxModeTo.callCount, 1)
assert.deepEqual(
propsMethodSpies.setMaxModeTo.getCall(0).args,
[false]
)
})
it('should call updateSendAmount', () => {
assert.equal(propsMethodSpies.updateSendAmount.callCount, 0)
instance.updateAmount('someAmount')
assert.equal(propsMethodSpies.updateSendAmount.callCount, 1)
assert.deepEqual(
propsMethodSpies.updateSendAmount.getCall(0).args,
['someAmount']
)
})
})
describe('render', () => {
it('should render a SendRowWrapper component', () => {
assert.equal(wrapper.find(SendRowWrapper).length, 1)
})
it('should pass the correct props to SendRowWrapper', () => {
const {
errorType,
label,
showError,
} = wrapper.find(SendRowWrapper).props()
assert.equal(errorType, 'amount')
assert.equal(label, 'amount_t:')
assert.equal(showError, false)
})
it('should render an AmountMaxButton as the first child of the SendRowWrapper', () => {
assert(wrapper.find(SendRowWrapper).childAt(0).is(AmountMaxButton))
})
it('should render a UserPreferencedTokenInput as the second child of the SendRowWrapper', () => {
console.log('HI', wrapper.find(SendRowWrapper).childAt(1))
assert(wrapper.find(SendRowWrapper).childAt(1).is(UserPreferencedTokenInput))
})
it('should render the UserPreferencedTokenInput with the correct props', () => {
const {
onBlur,
onChange,
error,
value,
} = wrapper.find(SendRowWrapper).childAt(1).props()
assert.equal(error, false)
assert.equal(value, 'mockAmount')
assert.equal(SendAmountRow.prototype.updateGas.callCount, 0)
assert.equal(SendAmountRow.prototype.updateAmount.callCount, 0)
onBlur('mockNewAmount')
assert.equal(SendAmountRow.prototype.updateGas.callCount, 1)
assert.deepEqual(
SendAmountRow.prototype.updateGas.getCall(0).args,
['mockNewAmount']
)
assert.equal(SendAmountRow.prototype.updateAmount.callCount, 1)
assert.deepEqual(
SendAmountRow.prototype.updateAmount.getCall(0).args,
['mockNewAmount']
)
assert.equal(SendAmountRow.prototype.validateAmount.callCount, 0)
onChange('mockNewAmount')
assert.equal(SendAmountRow.prototype.validateAmount.callCount, 1)
assert.deepEqual(
SendAmountRow.prototype.validateAmount.getCall(0).args,
['mockNewAmount']
)
})
})
})

View File

@ -1,125 +0,0 @@
import assert from 'assert'
import proxyquire from 'proxyquire'
import sinon from 'sinon'
let mapStateToProps
let mapDispatchToProps
const actionSpies = {
setMaxModeTo: sinon.spy(),
updateSendAmount: sinon.spy(),
}
const duckActionSpies = {
updateSendErrors: sinon.spy(),
}
proxyquire('../send-amount-row.container.js', {
'react-redux': {
connect: (ms, md) => {
mapStateToProps = ms
mapDispatchToProps = md
return () => ({})
},
},
'../../send.selectors': {
getAmountConversionRate: (s) => `mockAmountConversionRate:${s}`,
getConversionRate: (s) => `mockConversionRate:${s}`,
getCurrentCurrency: (s) => `mockConvertedCurrency:${s}`,
getGasTotal: (s) => `mockGasTotal:${s}`,
getPrimaryCurrency: (s) => `mockPrimaryCurrency:${s}`,
getSelectedToken: (s) => `mockSelectedToken:${s}`,
getSendAmount: (s) => `mockAmount:${s}`,
getSendFromBalance: (s) => `mockBalance:${s}`,
getTokenBalance: (s) => `mockTokenBalance:${s}`,
},
'./send-amount-row.selectors': { sendAmountIsInError: (s) => `mockInError:${s}` },
'../../send.utils': {
getAmountErrorObject: (mockDataObject) => ({ ...mockDataObject, mockChange: true }),
getGasFeeErrorObject: (mockDataObject) => ({ ...mockDataObject, mockGasFeeErrorChange: true }),
},
'../../../../actions': actionSpies,
'../../../../ducks/send.duck': duckActionSpies,
})
describe('send-amount-row container', () => {
describe('mapStateToProps()', () => {
it('should map the correct properties to props', () => {
assert.deepEqual(mapStateToProps('mockState'), {
amount: 'mockAmount:mockState',
amountConversionRate: 'mockAmountConversionRate:mockState',
balance: 'mockBalance:mockState',
conversionRate: 'mockConversionRate:mockState',
convertedCurrency: 'mockConvertedCurrency:mockState',
gasTotal: 'mockGasTotal:mockState',
inError: 'mockInError:mockState',
primaryCurrency: 'mockPrimaryCurrency:mockState',
selectedToken: 'mockSelectedToken:mockState',
tokenBalance: 'mockTokenBalance:mockState',
})
})
})
describe('mapDispatchToProps()', () => {
let dispatchSpy
let mapDispatchToPropsObject
beforeEach(() => {
dispatchSpy = sinon.spy()
mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy)
duckActionSpies.updateSendErrors.resetHistory()
})
describe('setMaxModeTo()', () => {
it('should dispatch an action', () => {
mapDispatchToPropsObject.setMaxModeTo('mockBool')
assert(dispatchSpy.calledOnce)
assert(actionSpies.setMaxModeTo.calledOnce)
assert.equal(
actionSpies.setMaxModeTo.getCall(0).args[0],
'mockBool'
)
})
})
describe('updateSendAmount()', () => {
it('should dispatch an action', () => {
mapDispatchToPropsObject.updateSendAmount('mockAmount')
assert(dispatchSpy.calledOnce)
assert(actionSpies.updateSendAmount.calledOnce)
assert.equal(
actionSpies.updateSendAmount.getCall(0).args[0],
'mockAmount'
)
})
})
describe('updateGasFeeError()', () => {
it('should dispatch an action', () => {
mapDispatchToPropsObject.updateGasFeeError({ some: 'data' })
assert(dispatchSpy.calledOnce)
assert(duckActionSpies.updateSendErrors.calledOnce)
assert.deepEqual(
duckActionSpies.updateSendErrors.getCall(0).args[0],
{ some: 'data', mockGasFeeErrorChange: true }
)
})
})
describe('updateSendAmountError()', () => {
it('should dispatch an action', () => {
mapDispatchToPropsObject.updateSendAmountError({ some: 'data' })
assert(dispatchSpy.calledOnce)
assert(duckActionSpies.updateSendErrors.calledOnce)
assert.deepEqual(
duckActionSpies.updateSendErrors.getCall(0).args[0],
{ some: 'data', mockChange: true }
)
})
})
})
})

View File

@ -1,34 +0,0 @@
import assert from 'assert'
import {
sendAmountIsInError,
} from '../send-amount-row.selectors.js'
describe('send-amount-row selectors', () => {
describe('sendAmountIsInError()', () => {
it('should return true if send.errors.amount is truthy', () => {
const state = {
send: {
errors: {
amount: 'abc',
},
},
}
assert.equal(sendAmountIsInError(state), true)
})
it('should return false if send.errors.amount is falsy', () => {
const state = {
send: {
errors: {
amount: null,
},
},
}
assert.equal(sendAmountIsInError(state), false)
})
})
})

View File

@ -1,41 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import PageContainerContent from '../../page-container/page-container-content.component'
import SendAmountRow from './send-amount-row/'
import SendFromRow from './send-from-row/'
import SendGasRow from './send-gas-row/'
import SendHexDataRow from './send-hex-data-row'
import SendToRow from './send-to-row/'
export default class SendContent extends Component {
static propTypes = {
updateGas: PropTypes.func,
scanQrCode: PropTypes.func,
showHexData: PropTypes.bool,
};
updateGas = (updateData) => this.props.updateGas(updateData)
render () {
return (
<PageContainerContent>
<div className="send-v2__form">
<SendFromRow />
<SendToRow
updateGas={this.updateGas}
scanQrCode={ _ => this.props.scanQrCode()}
/>
<SendAmountRow updateGas={this.updateGas} />
<SendGasRow />
{(this.props.showHexData && (
<SendHexDataRow
updateGas={this.updateGas}
/>
))}
</div>
</PageContainerContent>
)
}
}

View File

@ -1 +0,0 @@
export { default } from './send-dropdown-list.component'

View File

@ -1,52 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import AccountListItem from '../../account-list-item/'
export default class SendDropdownList extends Component {
static propTypes = {
accounts: PropTypes.array,
closeDropdown: PropTypes.func,
onSelect: PropTypes.func,
activeAddress: PropTypes.string,
};
static contextTypes = {
t: PropTypes.func,
};
getListItemIcon (accountAddress, activeAddress) {
return accountAddress === activeAddress
? <i className={`fa fa-check fa-lg`} style={ { color: '#02c9b1' } }/>
: null
}
render () {
const {
accounts,
closeDropdown,
onSelect,
activeAddress,
} = this.props
return (<div>
<div
className="send-v2__from-dropdown__close-area"
onClick={() => closeDropdown()}
/>
<div className="send-v2__from-dropdown__list">
{accounts.map((account, index) => <AccountListItem
account={account}
className="account-list-item__dropdown"
handleClick={() => {
onSelect(account)
closeDropdown()
}}
icon={this.getListItemIcon(account.address, activeAddress)}
key={`send-dropdown-account-#${index}`}
/>)}
</div>
</div>)
}
}

View File

@ -1,105 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import sinon from 'sinon'
import SendDropdownList from '../send-dropdown-list.component.js'
import AccountListItem from '../../../account-list-item/account-list-item.container'
const propsMethodSpies = {
closeDropdown: sinon.spy(),
onSelect: sinon.spy(),
}
sinon.spy(SendDropdownList.prototype, 'getListItemIcon')
describe('SendDropdownList Component', function () {
let wrapper
beforeEach(() => {
wrapper = shallow(<SendDropdownList
accounts={[
{ address: 'mockAccount0' },
{ address: 'mockAccount1' },
{ address: 'mockAccount2' },
]}
closeDropdown={propsMethodSpies.closeDropdown}
onSelect={propsMethodSpies.onSelect}
activeAddress={'mockAddress2'}
/>, { context: { t: str => str + '_t' } })
})
afterEach(() => {
propsMethodSpies.closeDropdown.resetHistory()
propsMethodSpies.onSelect.resetHistory()
SendDropdownList.prototype.getListItemIcon.resetHistory()
})
describe('getListItemIcon', () => {
it('should return check icon if the passed addresses are the same', () => {
assert.deepEqual(
wrapper.instance().getListItemIcon('mockAccount0', 'mockAccount0'),
<i className={`fa fa-check fa-lg`} style={ { color: '#02c9b1' } }/>
)
})
it('should return null if the passed addresses are different', () => {
assert.equal(
wrapper.instance().getListItemIcon('mockAccount0', 'mockAccount1'),
null
)
})
})
describe('render', () => {
it('should render a single div with two children', () => {
assert(wrapper.is('div'))
assert.equal(wrapper.children().length, 2)
})
it('should render the children with the correct classes', () => {
assert(wrapper.childAt(0).hasClass('send-v2__from-dropdown__close-area'))
assert(wrapper.childAt(1).hasClass('send-v2__from-dropdown__list'))
})
it('should call closeDropdown onClick of the send-v2__from-dropdown__close-area', () => {
assert.equal(propsMethodSpies.closeDropdown.callCount, 0)
wrapper.childAt(0).props().onClick()
assert.equal(propsMethodSpies.closeDropdown.callCount, 1)
})
it('should render an AccountListItem for each item in accounts', () => {
assert.equal(wrapper.childAt(1).children().length, 3)
assert(wrapper.childAt(1).children().every(AccountListItem))
})
it('should pass the correct props to the AccountListItem', () => {
wrapper.childAt(1).children().forEach((accountListItem, index) => {
const {
account,
className,
handleClick,
} = accountListItem.props()
assert.deepEqual(account, { address: 'mockAccount' + index })
assert.equal(className, 'account-list-item__dropdown')
assert.equal(propsMethodSpies.onSelect.callCount, 0)
handleClick()
assert.equal(propsMethodSpies.onSelect.callCount, 1)
assert.deepEqual(propsMethodSpies.onSelect.getCall(0).args[0], { address: 'mockAccount' + index })
propsMethodSpies.onSelect.resetHistory()
propsMethodSpies.closeDropdown.resetHistory()
assert.equal(propsMethodSpies.closeDropdown.callCount, 0)
handleClick()
assert.equal(propsMethodSpies.closeDropdown.callCount, 1)
propsMethodSpies.onSelect.resetHistory()
propsMethodSpies.closeDropdown.resetHistory()
})
})
it('should call this.getListItemIcon for each AccountListItem', () => {
assert.equal(SendDropdownList.prototype.getListItemIcon.callCount, 3)
const getListItemIconCalls = SendDropdownList.prototype.getListItemIcon.getCalls()
assert(getListItemIconCalls.every(({ args }, index) => args[0] === 'mockAccount' + index))
})
})
})

View File

@ -1,46 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import AccountListItem from '../../../account-list-item/'
import SendDropdownList from '../../send-dropdown-list/'
export default class FromDropdown extends Component {
static propTypes = {
accounts: PropTypes.array,
closeDropdown: PropTypes.func,
dropdownOpen: PropTypes.bool,
onSelect: PropTypes.func,
openDropdown: PropTypes.func,
selectedAccount: PropTypes.object,
};
static contextTypes = {
t: PropTypes.func,
};
render () {
const {
accounts,
closeDropdown,
dropdownOpen,
openDropdown,
selectedAccount,
onSelect,
} = this.props
return <div className="send-v2__from-dropdown">
<AccountListItem
account={selectedAccount}
handleClick={openDropdown}
icon={<i className={`fa fa-caret-down fa-lg`} style={ { color: '#dedede' } }/>}
/>
{dropdownOpen && <SendDropdownList
accounts={accounts}
closeDropdown={closeDropdown}
onSelect={onSelect}
activeAddress={selectedAccount.address}
/>}
</div>
}
}

View File

@ -1 +0,0 @@
export { default } from './from-dropdown.component'

View File

@ -1,88 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import sinon from 'sinon'
import FromDropdown from '../from-dropdown.component.js'
import AccountListItem from '../../../../account-list-item/account-list-item.container'
import SendDropdownList from '../../../send-dropdown-list/send-dropdown-list.component'
const propsMethodSpies = {
closeDropdown: sinon.spy(),
openDropdown: sinon.spy(),
onSelect: sinon.spy(),
}
describe('FromDropdown Component', function () {
let wrapper
beforeEach(() => {
wrapper = shallow(<FromDropdown
accounts={['mockAccount']}
closeDropdown={propsMethodSpies.closeDropdown}
dropdownOpen={false}
onSelect={propsMethodSpies.onSelect}
openDropdown={propsMethodSpies.openDropdown}
selectedAccount={ { address: 'mockAddress' } }
/>, { context: { t: str => str + '_t' } })
})
afterEach(() => {
propsMethodSpies.closeDropdown.resetHistory()
propsMethodSpies.openDropdown.resetHistory()
propsMethodSpies.onSelect.resetHistory()
})
describe('render', () => {
it('should render a div with a .send-v2__from-dropdown class', () => {
assert.equal(wrapper.find('.send-v2__from-dropdown').length, 1)
})
it('should render an AccountListItem as the first child of the .send-v2__from-dropdown div', () => {
assert(wrapper.find('.send-v2__from-dropdown').childAt(0).is(AccountListItem))
})
it('should pass the correct props to AccountListItem', () => {
const {
account,
handleClick,
icon,
} = wrapper.find('.send-v2__from-dropdown').childAt(0).props()
assert.deepEqual(account, { address: 'mockAddress' })
assert.deepEqual(
icon,
<i className={`fa fa-caret-down fa-lg`} style={ { color: '#dedede' } }/>
)
assert.equal(propsMethodSpies.openDropdown.callCount, 0)
handleClick()
assert.equal(propsMethodSpies.openDropdown.callCount, 1)
})
it('should not render a SendDropdownList when dropdownOpen is false', () => {
assert.equal(wrapper.find(SendDropdownList).length, 0)
})
it('should render a SendDropdownList when dropdownOpen is true', () => {
wrapper.setProps({ dropdownOpen: true })
assert(wrapper.find(SendDropdownList).length, 1)
})
it('should pass the correct props to the SendDropdownList]', () => {
wrapper.setProps({ dropdownOpen: true })
const {
accounts,
closeDropdown,
onSelect,
activeAddress,
} = wrapper.find(SendDropdownList).props()
assert.deepEqual(accounts, ['mockAccount'])
assert.equal(activeAddress, 'mockAddress')
assert.equal(propsMethodSpies.closeDropdown.callCount, 0)
closeDropdown()
assert.equal(propsMethodSpies.closeDropdown.callCount, 1)
assert.equal(propsMethodSpies.onSelect.callCount, 0)
onSelect()
assert.equal(propsMethodSpies.onSelect.callCount, 1)
})
})
})

View File

@ -1 +0,0 @@
export { default } from './send-from-row.container'

View File

@ -1,63 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import SendRowWrapper from '../send-row-wrapper/'
import FromDropdown from './from-dropdown/'
export default class SendFromRow extends Component {
static propTypes = {
closeFromDropdown: PropTypes.func,
conversionRate: PropTypes.number,
from: PropTypes.object,
fromAccounts: PropTypes.array,
fromDropdownOpen: PropTypes.bool,
openFromDropdown: PropTypes.func,
tokenContract: PropTypes.object,
updateSendFrom: PropTypes.func,
setSendTokenBalance: PropTypes.func,
};
static contextTypes = {
t: PropTypes.func,
};
async handleFromChange (newFrom) {
const {
updateSendFrom,
tokenContract,
setSendTokenBalance,
} = this.props
if (tokenContract) {
const usersToken = await tokenContract.balanceOf(newFrom.address)
setSendTokenBalance(usersToken)
}
updateSendFrom(newFrom)
}
render () {
const {
closeFromDropdown,
conversionRate,
from,
fromAccounts,
fromDropdownOpen,
openFromDropdown,
} = this.props
return (
<SendRowWrapper label={`${this.context.t('from')}:`}>
<FromDropdown
accounts={fromAccounts}
closeDropdown={() => closeFromDropdown()}
conversionRate={conversionRate}
dropdownOpen={fromDropdownOpen}
onSelect={newFrom => this.handleFromChange(newFrom)}
openDropdown={() => openFromDropdown()}
selectedAccount={from}
/>
</SendRowWrapper>
)
}
}

View File

@ -1,46 +0,0 @@
import { connect } from 'react-redux'
import {
accountsWithSendEtherInfoSelector,
getConversionRate,
getSelectedTokenContract,
getSendFromObject,
} from '../../send.selectors.js'
import {
getFromDropdownOpen,
} from './send-from-row.selectors.js'
import { calcTokenBalance } from '../../send.utils.js'
import {
updateSendFrom,
setSendTokenBalance,
} from '../../../../actions'
import {
closeFromDropdown,
openFromDropdown,
} from '../../../../ducks/send.duck'
import SendFromRow from './send-from-row.component'
export default connect(mapStateToProps, mapDispatchToProps)(SendFromRow)
function mapStateToProps (state) {
return {
conversionRate: getConversionRate(state),
from: getSendFromObject(state),
fromAccounts: accountsWithSendEtherInfoSelector(state),
fromDropdownOpen: getFromDropdownOpen(state),
tokenContract: getSelectedTokenContract(state),
}
}
function mapDispatchToProps (dispatch) {
return {
closeFromDropdown: () => dispatch(closeFromDropdown()),
openFromDropdown: () => dispatch(openFromDropdown()),
updateSendFrom: newFrom => dispatch(updateSendFrom(newFrom)),
setSendTokenBalance: (usersToken, selectedToken) => {
if (!usersToken) return
const tokenBalance = calcTokenBalance({ usersToken, selectedToken })
dispatch(setSendTokenBalance(tokenBalance))
},
}
}

View File

@ -1,9 +0,0 @@
const selectors = {
getFromDropdownOpen,
}
module.exports = selectors
function getFromDropdownOpen (state) {
return state.send.fromDropdownOpen
}

View File

@ -1,121 +0,0 @@
import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import sinon from 'sinon'
import SendFromRow from '../send-from-row.component.js'
import SendRowWrapper from '../../send-row-wrapper/send-row-wrapper.component'
import FromDropdown from '../from-dropdown/from-dropdown.component'
const propsMethodSpies = {
closeFromDropdown: sinon.spy(),
openFromDropdown: sinon.spy(),
updateSendFrom: sinon.spy(),
setSendTokenBalance: sinon.spy(),
}
sinon.spy(SendFromRow.prototype, 'handleFromChange')
describe('SendFromRow Component', function () {
let wrapper
let instance
beforeEach(() => {
wrapper = shallow(<SendFromRow
closeFromDropdown={propsMethodSpies.closeFromDropdown}
conversionRate={15}
from={ { address: 'mockAddress' } }
fromAccounts={['mockAccount']}
fromDropdownOpen={false}
openFromDropdown={propsMethodSpies.openFromDropdown}
setSendTokenBalance={propsMethodSpies.setSendTokenBalance}
tokenContract={null}
updateSendFrom={propsMethodSpies.updateSendFrom}
/>, { context: { t: str => str + '_t' } })
instance = wrapper.instance()
})
afterEach(() => {
propsMethodSpies.closeFromDropdown.resetHistory()
propsMethodSpies.openFromDropdown.resetHistory()
propsMethodSpies.updateSendFrom.resetHistory()
propsMethodSpies.setSendTokenBalance.resetHistory()
SendFromRow.prototype.handleFromChange.resetHistory()
})
describe('handleFromChange', () => {
it('should call updateSendFrom', () => {
assert.equal(propsMethodSpies.updateSendFrom.callCount, 0)
instance.handleFromChange('mockFrom')
assert.equal(propsMethodSpies.updateSendFrom.callCount, 1)
assert.deepEqual(
propsMethodSpies.updateSendFrom.getCall(0).args,
['mockFrom']
)
})
it('should call tokenContract.balanceOf and setSendTokenBalance if tokenContract is defined', async () => {
wrapper.setProps({
tokenContract: {
balanceOf: () => new Promise((resolve) => resolve('mockUsersToken')),
},
})
assert.equal(propsMethodSpies.setSendTokenBalance.callCount, 0)
await instance.handleFromChange('mockFrom')
assert.equal(propsMethodSpies.setSendTokenBalance.callCount, 1)
assert.deepEqual(
propsMethodSpies.setSendTokenBalance.getCall(0).args,
['mockUsersToken']
)
})
})
describe('render', () => {
it('should render a SendRowWrapper component', () => {
assert.equal(wrapper.find(SendRowWrapper).length, 1)
})
it('should pass the correct props to SendRowWrapper', () => {
const {
label,
} = wrapper.find(SendRowWrapper).props()
assert.equal(label, 'from_t:')
})
it('should render an FromDropdown as a child of the SendRowWrapper', () => {
assert(wrapper.find(SendRowWrapper).childAt(0).is(FromDropdown))
})
it('should render the FromDropdown with the correct props', () => {
const {
accounts,
closeDropdown,
conversionRate,
dropdownOpen,
onSelect,
openDropdown,
selectedAccount,
} = wrapper.find(SendRowWrapper).childAt(0).props()
assert.deepEqual(accounts, ['mockAccount'])
assert.equal(dropdownOpen, false)
assert.equal(conversionRate, 15)
assert.deepEqual(selectedAccount, { address: 'mockAddress' })
assert.equal(propsMethodSpies.closeFromDropdown.callCount, 0)
closeDropdown()
assert.equal(propsMethodSpies.closeFromDropdown.callCount, 1)
assert.equal(propsMethodSpies.openFromDropdown.callCount, 0)
openDropdown()
assert.equal(propsMethodSpies.openFromDropdown.callCount, 1)
assert.equal(SendFromRow.prototype.handleFromChange.callCount, 0)
onSelect('mockNewFrom')
assert.equal(SendFromRow.prototype.handleFromChange.callCount, 1)
assert.deepEqual(
SendFromRow.prototype.handleFromChange.getCall(0).args,
['mockNewFrom']
)
})
})
})

View File

@ -1,110 +0,0 @@
import assert from 'assert'
import proxyquire from 'proxyquire'
import sinon from 'sinon'
let mapStateToProps
let mapDispatchToProps
const actionSpies = {
updateSendFrom: sinon.spy(),
setSendTokenBalance: sinon.spy(),
}
const duckActionSpies = {
closeFromDropdown: sinon.spy(),
openFromDropdown: sinon.spy(),
}
proxyquire('../send-from-row.container.js', {
'react-redux': {
connect: (ms, md) => {
mapStateToProps = ms
mapDispatchToProps = md
return () => ({})
},
},
'../../send.selectors.js': {
accountsWithSendEtherInfoSelector: (s) => `mockFromAccounts:${s}`,
getConversionRate: (s) => `mockConversionRate:${s}`,
getSelectedTokenContract: (s) => `mockTokenContract:${s}`,
getSendFromObject: (s) => `mockFrom:${s}`,
},
'./send-from-row.selectors.js': { getFromDropdownOpen: (s) => `mockFromDropdownOpen:${s}` },
'../../send.utils.js': { calcTokenBalance: ({ usersToken, selectedToken }) => usersToken + selectedToken },
'../../../../actions': actionSpies,
'../../../../ducks/send.duck': duckActionSpies,
})
describe('send-from-row container', () => {
describe('mapStateToProps()', () => {
it('should map the correct properties to props', () => {
assert.deepEqual(mapStateToProps('mockState'), {
conversionRate: 'mockConversionRate:mockState',
from: 'mockFrom:mockState',
fromAccounts: 'mockFromAccounts:mockState',
fromDropdownOpen: 'mockFromDropdownOpen:mockState',
tokenContract: 'mockTokenContract:mockState',
})
})
})
describe('mapDispatchToProps()', () => {
let dispatchSpy
let mapDispatchToPropsObject
beforeEach(() => {
dispatchSpy = sinon.spy()
mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy)
})
describe('closeFromDropdown()', () => {
it('should dispatch a closeFromDropdown action', () => {
mapDispatchToPropsObject.closeFromDropdown()
assert(dispatchSpy.calledOnce)
assert(duckActionSpies.closeFromDropdown.calledOnce)
assert.equal(
duckActionSpies.closeFromDropdown.getCall(0).args[0],
undefined
)
})
})
describe('openFromDropdown()', () => {
it('should dispatch a openFromDropdown action', () => {
mapDispatchToPropsObject.openFromDropdown()
assert(dispatchSpy.calledOnce)
assert(duckActionSpies.openFromDropdown.calledOnce)
assert.equal(
duckActionSpies.openFromDropdown.getCall(0).args[0],
undefined
)
})
})
describe('updateSendFrom()', () => {
it('should dispatch an updateSendFrom action', () => {
mapDispatchToPropsObject.updateSendFrom('mockFrom')
assert(dispatchSpy.calledOnce)
assert.equal(
actionSpies.updateSendFrom.getCall(0).args[0],
'mockFrom'
)
})
})
describe('setSendTokenBalance()', () => {
it('should dispatch an setSendTokenBalance action', () => {
mapDispatchToPropsObject.setSendTokenBalance('mockUsersToken', 'mockSelectedToken')
assert(dispatchSpy.calledOnce)
assert.equal(
actionSpies.setSendTokenBalance.getCall(0).args[0],
'mockUsersTokenmockSelectedToken'
)
})
})
})
})

View File

@ -1,20 +0,0 @@
import assert from 'assert'
import {
getFromDropdownOpen,
} from '../send-from-row.selectors.js'
describe('send-from-row selectors', () => {
describe('getFromDropdownOpen()', () => {
it('should get send.fromDropdownOpen', () => {
const state = {
send: {
fromDropdownOpen: null,
},
}
assert.equal(getFromDropdownOpen(state), null)
})
})
})

View File

@ -1,58 +0,0 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import UserPreferencedCurrencyDisplay from '../../../../user-preferenced-currency-display'
import { PRIMARY, SECONDARY } from '../../../../../constants/common'
export default class GasFeeDisplay extends Component {
static propTypes = {
conversionRate: PropTypes.number,
primaryCurrency: PropTypes.string,
convertedCurrency: PropTypes.string,
gasLoadingError: PropTypes.bool,
gasTotal: PropTypes.string,
onClick: PropTypes.func,
};
static contextTypes = {
t: PropTypes.func,
};
render () {
const { gasTotal, onClick, gasLoadingError } = this.props
return (
<div className="send-v2__gas-fee-display">
{gasTotal
? (
<div className="currency-display">
<UserPreferencedCurrencyDisplay
value={gasTotal}
type={PRIMARY}
/>
<UserPreferencedCurrencyDisplay
className="currency-display__converted-value"
value={gasTotal}
type={SECONDARY}
/>
</div>
)
: gasLoadingError
? <div className="currency-display.currency-display--message">
{this.context.t('setGasPrice')}
</div>
: <div className="currency-display">
{this.context.t('loading')}
</div>
}
<button
className="sliders-icon-container"
onClick={onClick}
disabled={!gasTotal && !gasLoadingError}
>
<i className="fa fa-sliders sliders-icon" />
</button>
</div>
)
}
}

View File

@ -1 +0,0 @@
export { default } from './gas-fee-display.component'

View File

@ -1,53 +0,0 @@
import React from 'react'
import assert from 'assert'
import {shallow} from 'enzyme'
import GasFeeDisplay from '../gas-fee-display.component'
import UserPreferencedCurrencyDisplay from '../../../../../user-preferenced-currency-display'
import sinon from 'sinon'
const propsMethodSpies = {
showCustomizeGasModal: sinon.spy(),
}
describe('SendGasRow Component', function () {
let wrapper
beforeEach(() => {
wrapper = shallow(<GasFeeDisplay
conversionRate={20}
gasTotal={'mockGasTotal'}
onClick={propsMethodSpies.showCustomizeGasModal}
primaryCurrency={'mockPrimaryCurrency'}
convertedCurrency={'mockConvertedCurrency'}
/>, {context: {t: str => str + '_t'}})
})
afterEach(() => {
propsMethodSpies.showCustomizeGasModal.resetHistory()
})
describe('render', () => {
it('should render a CurrencyDisplay component', () => {
assert.equal(wrapper.find(UserPreferencedCurrencyDisplay).length, 2)
})
it('should render the CurrencyDisplay with the correct props', () => {
const {
type,
value,
} = wrapper.find(UserPreferencedCurrencyDisplay).at(0).props()
assert.equal(type, 'PRIMARY')
assert.equal(value, 'mockGasTotal')
})
it('should render the Button with the correct props', () => {
const {
onClick,
} = wrapper.find('button').props()
assert.equal(propsMethodSpies.showCustomizeGasModal.callCount, 0)
onClick()
assert.equal(propsMethodSpies.showCustomizeGasModal.callCount, 1)
})
})
})

View File

@ -1 +0,0 @@
export { default } from './send-gas-row.container'

View File

@ -1,48 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import SendRowWrapper from '../send-row-wrapper/'
import GasFeeDisplay from './gas-fee-display/gas-fee-display.component'
export default class SendGasRow extends Component {
static propTypes = {
conversionRate: PropTypes.number,
convertedCurrency: PropTypes.string,
gasFeeError: PropTypes.bool,
gasLoadingError: PropTypes.bool,
gasTotal: PropTypes.string,
showCustomizeGasModal: PropTypes.func,
};
static contextTypes = {
t: PropTypes.func,
};
render () {
const {
conversionRate,
convertedCurrency,
gasLoadingError,
gasTotal,
gasFeeError,
showCustomizeGasModal,
} = this.props
return (
<SendRowWrapper
label={`${this.context.t('gasFee')}:`}
showError={gasFeeError}
errorType={'gasFee'}
>
<GasFeeDisplay
conversionRate={conversionRate}
convertedCurrency={convertedCurrency}
gasLoadingError={gasLoadingError}
gasTotal={gasTotal}
onClick={() => showCustomizeGasModal()}
/>
</SendRowWrapper>
)
}
}

View File

@ -1,27 +0,0 @@
import { connect } from 'react-redux'
import {
getConversionRate,
getCurrentCurrency,
getGasTotal,
} from '../../send.selectors.js'
import { getGasLoadingError, gasFeeIsInError } from './send-gas-row.selectors.js'
import { showModal } from '../../../../actions'
import SendGasRow from './send-gas-row.component'
export default connect(mapStateToProps, mapDispatchToProps)(SendGasRow)
function mapStateToProps (state) {
return {
conversionRate: getConversionRate(state),
convertedCurrency: getCurrentCurrency(state),
gasTotal: getGasTotal(state),
gasFeeError: gasFeeIsInError(state),
gasLoadingError: getGasLoadingError(state),
}
}
function mapDispatchToProps (dispatch) {
return {
showCustomizeGasModal: () => dispatch(showModal({ name: 'CUSTOMIZE_GAS' })),
}
}

Some files were not shown because too many files have changed in this diff Show More