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

Adds redesign for the customize gas advanced tab.

This commit is contained in:
Dan Miller 2018-09-20 13:36:23 -02:30
parent 5354325fab
commit b95eb30ec6
19 changed files with 332 additions and 152 deletions

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import TimeRemaining from './time-remaining'
import GasPriceChart from '../../gas-price-chart'
export default class AdvancedTabContent extends Component {
static contextTypes = {
@ -14,6 +14,7 @@ export default class AdvancedTabContent extends Component {
customGasLimit: PropTypes.number,
millisecondsRemaining: PropTypes.number,
totalFee: PropTypes.string,
timeRemaining: PropTypes.string,
}
gasInput (value, onChange, min, precision, showGWEI) {
@ -27,9 +28,6 @@ export default class AdvancedTabContent extends Component {
precision={precision}
onChange={event => onChange(Number(event.target.value))}
/>
{showGWEI
? <span className="advanced-tab__gas-edit-row__gwei-symbol">GWEI</span>
: null}
</div>
)
}
@ -38,7 +36,7 @@ export default class AdvancedTabContent extends Component {
return <i className="fa fa-info-circle" onClick={onClick} />
}
renderDataSummary (totalFee, millisecondsRemaining) {
renderDataSummary (totalFee, timeRemaining) {
return (
<div className="advanced-tab__transaction-data-summary">
<div className="advanced-tab__transaction-data-summary__titles">
@ -49,9 +47,7 @@ export default class AdvancedTabContent extends Component {
<div className="advanced-tab__transaction-data-summary__fee">
{totalFee}
</div>
<TimeRemaining
milliseconds={millisecondsRemaining}
/>
<div className="time-remaining">{timeRemaining}</div>
</div>
</div>
)
@ -72,7 +68,7 @@ export default class AdvancedTabContent extends Component {
renderGasEditRows (customGasPrice, updateCustomGasPrice, customGasLimit, updateCustomGasLimit) {
return (
<div className="advanced-tab__gas-edit-rows">
{ this.renderGasEditRow('gasPriceNoDenom', customGasPrice, updateCustomGasPrice, customGasPrice, 9, true) }
{ this.renderGasEditRow('gasPrice', customGasPrice, updateCustomGasPrice, customGasPrice, 9, true) }
{ this.renderGasEditRow('gasLimit', customGasLimit, updateCustomGasLimit, customGasLimit, 0) }
</div>
)
@ -82,7 +78,7 @@ export default class AdvancedTabContent extends Component {
const {
updateCustomGasPrice,
updateCustomGasLimit,
millisecondsRemaining,
timeRemaining,
customGasPrice,
customGasLimit,
totalFee,
@ -90,17 +86,16 @@ export default class AdvancedTabContent extends Component {
return (
<div className="advanced-tab">
{ this.renderDataSummary(totalFee, millisecondsRemaining) }
<div className="advanced-tab__fee-chart-title">
{ this.context.t('feeChartTitle') }
{ this.renderDataSummary(totalFee, timeRemaining) }
<div className="advanced-tab__fee-chart">
{ this.renderGasEditRows(
customGasPrice,
updateCustomGasPrice,
customGasLimit,
updateCustomGasLimit
) }
<GasPriceChart />
</div>
<div className="advanced-tab__fee-chart" />
{ this.renderGasEditRows(
customGasPrice,
updateCustomGasPrice,
customGasLimit,
updateCustomGasLimit
) }
</div>
)
}

View File

@ -3,11 +3,9 @@
.advanced-tab {
display: flex;
flex-flow: column;
height: 430px;
&__transaction-data-summary,
&__fee-chart-title,
&__gas-edit-row {
&__fee-chart-title {
padding-left: 24px;
padding-right: 24px;
}
@ -17,6 +15,8 @@
flex-flow: column;
color: $mid-gray;
margin-top: 12px;
padding-left: 18px;
padding-right: 18px;
&__titles,
&__container {
@ -24,11 +24,17 @@
flex-flow: row;
justify-content: space-between;
font-size: 12px;
color: #888EA3;
}
&__container {
font-size: 26px;
margin-top: 6px;
font-size: 16px;
margin-top: 0px;
}
&__fee {
font-size: 16px;
color: #313A5E;
}
}
@ -40,8 +46,11 @@
&__fee-chart {
padding-left: 10px;
margin-top: 24px;
height: 134px;
margin-top: 8px;
height: 258px;
background: #F8F9FB;
border-bottom: 1px solid #d2d8dd;
border-top: 1px solid #d2d8dd;
}
&__slider-container {
@ -50,21 +59,25 @@
}
&__gas-edit-rows {
margin-top: 44px;
height: 87px;
display: flex;
flex-flow: column;
flex-flow: row;
justify-content: space-between;
margin-left: 10px;
margin-right: 10px;
margin-top: 9px;
}
&__gas-edit-row {
display: flex;
flex-flow: row;
justify-content: space-between;
flex-flow: column;
&__label {
color: $mid-gray;
font-size: 16px;
color: #313B5E;
font-size: 14px;
display: flex;
justify-content: space-between;
align-items: center;
.fa-info-circle {
color: $silver;
@ -87,10 +100,11 @@
border-radius: 4px;
color: $mid-gray;
font-size: 16px;
height: 37px;
width: 163px;
height: 24px;
width: 155px;
padding-left: 8px;
padding-top: 2px;
margin-top: 7px;
}
input[type="number"]::-webkit-inner-spin-button {

View File

@ -4,7 +4,7 @@ import shallow from '../../../../../../lib/shallow-with-context'
import sinon from 'sinon'
import AdvancedTabContent from '../advanced-tab-content.component.js'
import TimeRemaining from '../time-remaining'
import GasPriceChart from '../../../gas-price-chart'
const propsMethodSpies = {
updateCustomGasPrice: sinon.spy(),
@ -13,6 +13,8 @@ const propsMethodSpies = {
sinon.spy(AdvancedTabContent.prototype, 'renderGasEditRow')
sinon.spy(AdvancedTabContent.prototype, 'gasInput')
sinon.spy(AdvancedTabContent.prototype, 'renderGasEditRows')
sinon.spy(AdvancedTabContent.prototype, 'renderDataSummary')
describe('AdvancedTabContent Component', function () {
let wrapper
@ -23,7 +25,7 @@ describe('AdvancedTabContent Component', function () {
updateCustomGasLimit={propsMethodSpies.updateCustomGasLimit}
customGasPrice={11}
customGasLimit={23456}
millisecondsRemaining={21500}
timeRemaining={21500}
totalFee={'$0.25'}
/>, { context: { t: (str1, str2) => str2 ? str1 + str2 : str1 } })
})
@ -31,6 +33,10 @@ describe('AdvancedTabContent Component', function () {
afterEach(() => {
propsMethodSpies.updateCustomGasPrice.resetHistory()
propsMethodSpies.updateCustomGasLimit.resetHistory()
AdvancedTabContent.prototype.renderGasEditRow.resetHistory()
AdvancedTabContent.prototype.gasInput.resetHistory()
AdvancedTabContent.prototype.renderGasEditRows.resetHistory()
AdvancedTabContent.prototype.renderDataSummary.resetHistory()
})
describe('render()', () => {
@ -40,12 +46,31 @@ describe('AdvancedTabContent Component', function () {
it('should render the expected four children of the advanced-tab div', () => {
const advancedTabChildren = wrapper.children()
assert.equal(advancedTabChildren.length, 4)
assert.equal(advancedTabChildren.length, 2)
assert(advancedTabChildren.at(0).hasClass('advanced-tab__transaction-data-summary'))
assert(advancedTabChildren.at(1).hasClass('advanced-tab__fee-chart-title'))
assert(advancedTabChildren.at(2).hasClass('advanced-tab__fee-chart'))
assert(advancedTabChildren.at(3).hasClass('advanced-tab__gas-edit-rows'))
assert(advancedTabChildren.at(1).hasClass('advanced-tab__fee-chart'))
const feeChartDiv = advancedTabChildren.at(1)
assert(feeChartDiv.childAt(0).hasClass('advanced-tab__gas-edit-rows'))
assert(feeChartDiv.childAt(1).is(GasPriceChart))
})
it('should call renderDataSummary with the expected params', () => {
assert.equal(AdvancedTabContent.prototype.renderGasEditRows.callCount, 1)
const renderDataSummaryArgs = AdvancedTabContent.prototype.renderDataSummary.getCall(0).args
assert.deepEqual(renderDataSummaryArgs, ['$0.25', 21500])
assert.equal(AdvancedTabContent.prototype.renderGasEditRows.callCount, 1)
const renderGasEditRowArgs = AdvancedTabContent.prototype.renderGasEditRows.getCall(0).args
assert.deepEqual(renderGasEditRowArgs, [
11, propsMethodSpies.updateCustomGasPrice, 23456, propsMethodSpies.updateCustomGasLimit,
])
})
it('should call renderGasEditRows with the expected params', () => {
})
})
@ -71,8 +96,8 @@ describe('AdvancedTabContent Component', function () {
const dataNode = dataSummary.children().at(1)
assert(dataNode.hasClass('advanced-tab__transaction-data-summary__container'))
assert.equal(dataNode.children().at(0).text(), 'mockTotalFee')
assert(dataNode.children().at(1).is(TimeRemaining))
assert.equal(dataNode.children().at(1).props().milliseconds, 'mockMsRemaining')
assert(dataNode.children().at(1).hasClass('time-remaining'))
assert.equal(dataNode.children().at(1).text(), 'mockMsRemaining')
})
})
@ -138,7 +163,7 @@ describe('AdvancedTabContent Component', function () {
const renderGasEditRowSpyArgs = AdvancedTabContent.prototype.renderGasEditRow.args
assert.equal(renderGasEditRowSpyArgs.length, 2)
assert.deepEqual(renderGasEditRowSpyArgs[0].map(String), [
'gasPriceNoDenom', 'mockGasPrice', () => 'mockUpdateCustomGasPriceReturn', 'mockGasPrice', 9, true,
'gasPrice', 'mockGasPrice', () => 'mockUpdateCustomGasPriceReturn', 'mockGasPrice', 9, true,
].map(String))
assert.deepEqual(renderGasEditRowSpyArgs[1].map(String), [
'gasLimit', 'mockGasLimit', () => 'mockUpdateCustomGasLimitReturn', 'mockGasLimit', 0,
@ -186,19 +211,6 @@ describe('AdvancedTabContent Component', function () {
assert(gasInput.children().at(0).hasClass('advanced-tab__gas-edit-row__input'))
})
it('should show GWEI if the showGWEI prop is truthy', () => {
const gasInputWithGWEI = shallow(wrapper.instance().gasInput(
321,
value => value + 7,
0,
8,
true
))
assert.equal(gasInputWithGWEI.children().length, 2)
assert(gasInputWithGWEI.children().at(0).hasClass('advanced-tab__gas-edit-row__input'))
assert(gasInputWithGWEI.children().at(1).hasClass('advanced-tab__gas-edit-row__gwei-symbol'))
})
it('should pass the correct value min and precision props to the input', () => {
const inputProps = gasInput.find('input').props()
assert.equal(inputProps.min, 0)

View File

@ -1,13 +1,17 @@
.time-remaining {
color: #313A5E;
font-size: 16px;
.minutes-num, .seconds-num {
font-size: 26px;
font-size: 16px;
}
.seconds-num {
margin-left: 7px;
font-size: 16px;
}
.minutes-label, .seconds-label {
font-size: 14px;
font-size: 16px;
}
}

View File

@ -3,6 +3,7 @@
flex-direction: column;
align-items: center;
margin-bottom: 22px;
height: 291px;
&__title {
margin-top: 19px;

View File

@ -24,8 +24,9 @@ export default class GasModalPageContainer extends Component {
newTotalEth: PropTypes.string,
}),
onSubmit: PropTypes.func,
customGasPriceInHex: PropTypes.string,
customGasLimitInHex: PropTypes.string,
customModalGasPriceInHex: PropTypes.string,
customModalGasLimitInHex: PropTypes.string,
cancelAndClose: PropTypes.func,
}
state = {}
@ -51,32 +52,34 @@ export default class GasModalPageContainer extends Component {
updateCustomGasLimit={convertThenUpdateCustomGasLimit}
customGasPrice={customGasPrice}
customGasLimit={customGasLimit}
millisecondsRemaining={91000}
timeRemaining={'1 min 31 sec'}
totalFee={newTotalFiat}
/>
)
}
renderInfoRow (className, totalLabelKey, fiatTotal, cryptoTotal) {
return (
<div className={className}>
<div className={`${className}__total-info`}>
<span className={`${className}__total-info__total-label`}>{`${this.context.t(totalLabelKey)}:`}</span>
<span className={`${className}__total-info__total-value`}>{fiatTotal}</span>
</div>
<div className={`${className}__sum-info`}>
<span className={`${className}__sum-info__sum-label`}>{`${this.context.t('amountPlusTxFee')}`}</span>
<span className={`${className}__sum-info__sum-value`}>{cryptoTotal}</span>
</div>
</div>
)
}
renderInfoRows (newTotalFiat, newTotalEth, sendAmount, transactionFee) {
const baseClassName = 'gas-modal-content__info-row'
renderInfoRows (originalTotalFiat, originalTotalEth, newTotalFiat, newTotalEth) {
return (
<div>
{ this.renderInfoRow('gas-modal-content__info-row--fade', 'originalTotal', originalTotalFiat, originalTotalEth) }
{ this.renderInfoRow('gas-modal-content__info-row', 'newTotal', newTotalFiat, newTotalEth) }
<div className={baseClassName}>
<div className={`${baseClassName}__send-info`}>
<span className={`${baseClassName}__send-info__label`}>{`Send Amount`}</span>
<span className={`${baseClassName}__send-info__value`}>{sendAmount}</span>
</div>
<div className={`${baseClassName}__transaction-info`}>
<span className={`${baseClassName}__transaction-info__label`}>{`Transaction Fee`}</span>
<span className={`${baseClassName}__transaction-info__value`}>{transactionFee}</span>
</div>
<div className={`${baseClassName}__total-info`}>
<span className={`${baseClassName}__total-info__label`}>{`New Total`}</span>
<span className={`${baseClassName}__total-info__value`}>{newTotalEth}</span>
</div>
<div className={`${baseClassName}__fiat-total-info`}>
<span className={`${baseClassName}__fiat-total-info__value`}>{newTotalFiat}</span>
</div>
</div>
</div>
)
}
@ -86,6 +89,8 @@ export default class GasModalPageContainer extends Component {
originalTotalEth,
newTotalFiat,
newTotalEth,
sendAmount,
transactionFee,
},
{
gasPriceButtonGroupProps,
@ -106,7 +111,7 @@ export default class GasModalPageContainer extends Component {
{tabsToRender.map(({ name, content }, i) => <Tab name={this.context.t(name)} key={`gas-modal-tab-${i}`}>
<div className="gas-modal-content">
{ content }
{ this.renderInfoRows(originalTotalFiat, originalTotalEth, newTotalFiat, newTotalEth) }
{ this.renderInfoRows(newTotalFiat, newTotalEth, sendAmount, transactionFee) }
</div>
</Tab>
)}
@ -116,11 +121,11 @@ export default class GasModalPageContainer extends Component {
render () {
const {
hideModal,
cancelAndClose,
infoRowProps,
onSubmit,
customGasPriceInHex,
customGasLimitInHex,
customModalGasPriceInHex,
customModalGasLimitInHex,
...tabProps
} = this.props
@ -131,13 +136,15 @@ export default class GasModalPageContainer extends Component {
subtitle={this.context.t('customGasSubTitle')}
tabsComponent={this.renderTabs(infoRowProps, tabProps)}
disabled={false}
onCancel={() => hideModal()}
onClose={() => hideModal()}
onCancel={() => cancelAndClose()}
onClose={() => cancelAndClose()}
onSubmit={() => {
onSubmit(customGasLimitInHex, customGasPriceInHex)
hideModal()
onSubmit(customModalGasLimitInHex, customModalGasPriceInHex)
cancelAndClose()
}}
submitText={this.context.t('save')}
headerCloseText={'Close'}
hideCancel={true}
/>
</div>
)

View File

@ -9,6 +9,7 @@ import {
import {
setCustomGasPrice,
setCustomGasLimit,
resetCustomData,
} from '../../../ducks/gas.duck'
import {
hideGasButtonGroup,
@ -48,12 +49,12 @@ import { addHexPrefix } from 'ethereumjs-util'
const mapStateToProps = state => {
const buttonDataLoading = getBasicGasEstimateLoadingStatus(state)
const { gasPrice, gas: gasLimit, value } = getTxParams(state)
const gasTotal = calcGasTotal(gasLimit, gasPrice)
const { gasPrice: currentGasPrice, gas: currentGasLimit, value } = getTxParams(state)
const gasTotal = calcGasTotal(currentGasLimit, currentGasPrice)
const customGasPriceInHex = getCustomGasPrice(state)
const customGasLimitInHex = getCustomGasLimit(state)
const customGasTotal = calcGasTotal(customGasLimitInHex || gasLimit, customGasPriceInHex || gasPrice)
const customModalGasPriceInHex = getCustomGasPrice(state) || currentGasPrice
const customModalGasLimitInHex = getCustomGasLimit(state) || currentGasLimit
const customGasTotal = calcGasTotal(customModalGasLimitInHex, customModalGasPriceInHex)
const gasButtonInfo = getRenderableBasicEstimateData(state)
@ -67,14 +68,14 @@ const mapStateToProps = state => {
return {
hideBasic,
isConfirm: isConfirm(state),
customGasPriceInHex,
customGasLimitInHex,
customGasPrice: calcCustomGasPrice(customGasPriceInHex, gasPrice),
customGasLimit: calcCustomGasLimit(customGasLimitInHex, gasLimit),
customModalGasPriceInHex,
customModalGasLimitInHex,
customGasPrice: calcCustomGasPrice(customModalGasPriceInHex),
customGasLimit: calcCustomGasLimit(customModalGasLimitInHex),
newTotalFiat,
gasPriceButtonGroupProps: {
buttonDataLoading,
defaultActiveButtonIndex: getDefaultActiveButtonIndex(gasButtonInfo, customGasPriceInHex, gasPrice),
defaultActiveButtonIndex: getDefaultActiveButtonIndex(gasButtonInfo, customModalGasPriceInHex),
gasButtonInfo,
},
infoRowProps: {
@ -82,6 +83,8 @@ const mapStateToProps = state => {
originalTotalEth: addHexWEIsToRenderableEth(value, gasTotal),
newTotalFiat,
newTotalEth: addHexWEIsToRenderableEth(value, customGasTotal),
transactionFee: addHexWEIsToRenderableEth('0x0', customGasTotal),
sendAmount: addHexWEIsToRenderableEth(value, '0x0'),
},
}
}
@ -90,7 +93,10 @@ const mapDispatchToProps = dispatch => {
const updateCustomGasPrice = newPrice => dispatch(setCustomGasPrice(addHexPrefix(newPrice)))
return {
hideModal: () => dispatch(hideModal()),
cancelAndClose: () => {
dispatch(resetCustomData())
dispatch(hideModal())
},
updateCustomGasPrice,
convertThenUpdateCustomGasPrice: newPrice => updateCustomGasPrice(decGWEIToHexWEI(newPrice)),
convertThenUpdateCustomGasLimit: newLimit => dispatch(setCustomGasLimit(addHexPrefix(newLimit.toString(16)))),
@ -138,12 +144,12 @@ function isConfirm (state) {
return Boolean(Object.keys(state.confirmTransaction.txData).length)
}
function calcCustomGasPrice (customGasPriceInHex, gasPrice) {
return Number(hexWEIToDecGWEI(customGasPriceInHex || gasPrice))
function calcCustomGasPrice (customGasPriceInHex) {
return Number(hexWEIToDecGWEI(customGasPriceInHex))
}
function calcCustomGasLimit (customGasLimitInHex, gasLimit) {
return parseInt(customGasLimitInHex || gasLimit, 16)
function calcCustomGasLimit (customGasLimitInHex) {
return parseInt(customGasLimitInHex, 16)
}
function getTxParams (state) {

View File

@ -4,6 +4,57 @@
.gas-modal-page-container {
.page-container {
width: 391px;
&__header {
padding: 0px;
padding-top: 16px;
&--no-padding-bottom {
padding-bottom: 0;
}
}
&__header-close-text {
font-size: 14px;
color: #4EADE7;
position: absolute;
top: 16px;
right: 16px;
cursor: pointer;
overflow: hidden;
}
&__title {
color: $black;
font-size: 16px;
font-weight: 500;
line-height: 16px;
display: flex;
justify-content: center;
align-items: flex-start;
}
&__subtitle {
display: none;
}
&__tabs {
margin-top: 0px;
}
&__tab {
width: 100%;
font-size: 14px;
&:last-of-type {
margin-right: 0;
}
&--selected {
color: $curious-blue;
border-bottom: 2px solid $curious-blue;
}
}
}
}
@ -20,30 +71,35 @@
display: flex;
flex-flow: column;
color: $scorpion;
font-size: 12px;
&__total-info, &__sum-info {
&__send-info, &__transaction-info, &__total-info, &__fiat-total-info {
display: flex;
flex-flow: row;
justify-content: space-between;
}
&__fiat-total-info {
justify-content: flex-end;
}
&__total-info {
&__total-label {
&__label {
font-size: 16px;
}
&__total-value {
&__value {
font-size: 16px;
font-weight: bold;
}
}
&__sum-info {
&__sum-label {
&__transaction-info, &__send-info {
&__label {
font-size: 12px;
}
&__sum-value {
&__value {
font-size: 14px;
}
}

View File

@ -9,7 +9,7 @@ import PageContainer from '../../../page-container'
import { Tab } from '../../../tabs'
const propsMethodSpies = {
hideModal: sinon.spy(),
cancelAndClose: sinon.spy(),
onSubmit: sinon.spy(),
}
@ -39,12 +39,16 @@ const mockGasPriceButtonGroupProps = {
handleGasPriceSelection: 'mockSelectionFunction',
noButtonActiveByDefault: true,
showCheck: true,
newTotalFiat: 'mockNewTotalFiat',
newTotalEth: 'mockNewTotalEth',
}
const mockInfoRowProps = {
originalTotalFiat: 'mockOriginalTotalFiat',
originalTotalEth: 'mockOriginalTotalEth',
newTotalFiat: 'mockNewTotalFiat',
newTotalEth: 'mockNewTotalEth',
sendAmount: 'mockSendAmount',
transactionFee: 'mockTransactionFee',
}
const GP = GasModalPageContainer.prototype
@ -53,7 +57,7 @@ describe('GasModalPageContainer Component', function () {
beforeEach(() => {
wrapper = shallow(<GasModalPageContainer
hideModal={propsMethodSpies.hideModal}
cancelAndClose={propsMethodSpies.cancelAndClose}
onSubmit={propsMethodSpies.onSubmit}
updateCustomGasPrice={() => 'mockupdateCustomGasPrice'}
updateCustomGasLimit={() => 'mockupdateCustomGasLimit'}
@ -67,7 +71,7 @@ describe('GasModalPageContainer Component', function () {
})
afterEach(() => {
propsMethodSpies.hideModal.resetHistory()
propsMethodSpies.cancelAndClose.resetHistory()
})
describe('render', () => {
@ -91,11 +95,11 @@ describe('GasModalPageContainer Component', function () {
onCancel,
onClose,
} = wrapper.find(PageContainer).props()
assert.equal(propsMethodSpies.hideModal.callCount, 0)
assert.equal(propsMethodSpies.cancelAndClose.callCount, 0)
onCancel()
assert.equal(propsMethodSpies.hideModal.callCount, 1)
assert.equal(propsMethodSpies.cancelAndClose.callCount, 1)
onClose()
assert.equal(propsMethodSpies.hideModal.callCount, 2)
assert.equal(propsMethodSpies.cancelAndClose.callCount, 2)
})
it('should pass the correct renderTabs property to PageContainer', () => {
@ -158,8 +162,8 @@ describe('GasModalPageContainer Component', function () {
assert.equal(GP.renderInfoRows.callCount, 2)
assert.deepEqual(GP.renderInfoRows.getCall(0).args, ['mockOriginalTotalFiat', 'mockOriginalTotalEth', 'mockNewTotalFiat', 'mockNewTotalEth'])
assert.deepEqual(GP.renderInfoRows.getCall(1).args, ['mockOriginalTotalFiat', 'mockOriginalTotalEth', 'mockNewTotalFiat', 'mockNewTotalEth'])
assert.deepEqual(GP.renderInfoRows.getCall(0).args, ['mockNewTotalFiat', 'mockNewTotalEth', 'mockSendAmount', 'mockTransactionFee'])
assert.deepEqual(GP.renderInfoRows.getCall(1).args, ['mockNewTotalFiat', 'mockNewTotalEth', 'mockSendAmount', 'mockTransactionFee'])
})
it('should not render the basic tab if hideBasic is true', () => {
@ -176,25 +180,6 @@ describe('GasModalPageContainer Component', function () {
})
})
describe('renderInfoRow', () => {
it('should render a div with the passed className and two children, each with the expected text', () => {
const renderInfoRowResult = wrapper.instance().renderInfoRow('mockClassName', 'mockLabelKey', 'mockFiatAmount', 'mockCryptoAmount')
const renderedInfoRow = shallow(renderInfoRowResult)
assert.equal(renderedInfoRow.props().className, 'mockClassName')
const firstChild = renderedInfoRow.childAt(0)
const secondhild = renderedInfoRow.childAt(1)
assert.equal(firstChild.props().className, 'mockClassName__total-info')
assert.equal(secondhild.props().className, 'mockClassName__sum-info')
assert.equal(firstChild.childAt(0).text(), 'mockLabelKey:')
assert.equal(firstChild.childAt(1).text(), 'mockFiatAmount')
assert.equal(secondhild.childAt(0).text(), 'amountPlusTxFee')
assert.equal(secondhild.childAt(1).text(), 'mockCryptoAmount')
})
})
describe('renderBasicTabContent', () => {
it('should render', () => {
const renderBasicTabContentResult = wrapper.instance().renderBasicTabContent(mockGasPriceButtonGroupProps)
@ -220,8 +205,34 @@ describe('GasModalPageContainer Component', function () {
assert.equal(advancedTabContentProps.updateCustomGasLimit(), 'mockConvertThenUpdateCustomGasLimit')
assert.equal(advancedTabContentProps.customGasPrice, 123)
assert.equal(advancedTabContentProps.customGasLimit, 456)
assert.equal(advancedTabContentProps.millisecondsRemaining, 91000)
assert.equal(advancedTabContentProps.timeRemaining, '1 min 31 sec')
assert.equal(advancedTabContentProps.totalFee, '$0.30')
})
})
describe('renderInfoRows', () => {
it('should render the info rows with the passed data', () => {
const baseClassName = 'gas-modal-content__info-row'
const renderedInfoRowsContainer = shallow(wrapper.instance().renderInfoRows(
'mockNewTotalFiat',
' mockNewTotalEth',
' mockSendAmount',
' mockTransactionFee'
))
assert(renderedInfoRowsContainer.childAt(0).hasClass(baseClassName))
const renderedInfoRows = renderedInfoRowsContainer.childAt(0).children()
assert.equal(renderedInfoRows.length, 4)
assert(renderedInfoRows.at(0).hasClass(`${baseClassName}__send-info`))
assert(renderedInfoRows.at(1).hasClass(`${baseClassName}__transaction-info`))
assert(renderedInfoRows.at(2).hasClass(`${baseClassName}__total-info`))
assert(renderedInfoRows.at(3).hasClass(`${baseClassName}__fiat-total-info`))
assert.equal(renderedInfoRows.at(0).text(), 'Send Amount mockSendAmount')
assert.equal(renderedInfoRows.at(1).text(), 'Transaction Fee mockTransactionFee')
assert.equal(renderedInfoRows.at(2).text(), 'New Total mockNewTotalEth')
assert.equal(renderedInfoRows.at(3).text(), 'mockNewTotalFiat')
})
})
})

View File

@ -15,6 +15,7 @@ const actionSpies = {
const gasActionSpies = {
setCustomGasPrice: sinon.spy(),
setCustomGasLimit: sinon.spy(),
resetCustomData: sinon.spy(),
}
const confirmTransactionActionSpies = {
@ -37,7 +38,7 @@ proxyquire('../gas-modal-page-container.container.js', {
'../../../selectors/custom-gas': {
getBasicGasEstimateLoadingStatus: (s) => `mockBasicGasEstimateLoadingStatus:${Object.keys(s).length}`,
getRenderableBasicEstimateData: (s) => `mockRenderableBasicEstimateData:${Object.keys(s).length}`,
getDefaultActiveButtonIndex: (a, b, c) => a + b + c,
getDefaultActiveButtonIndex: (a, b) => a + b,
},
'../../../actions': actionSpies,
'../../../ducks/gas.duck': gasActionSpies,
@ -89,15 +90,14 @@ describe('gas-modal-page-container container', () => {
assert.deepEqual(result2, {
isConfirm: true,
customGasPriceInHex: 'ffffffff',
customGasLimitInHex: 'aaaaaaaa',
customGasPrice: 4.294967295,
customGasLimit: 2863311530,
newTotalFiat: '637.41',
gasPriceButtonGroupProps:
{
customModalGasLimitInHex: 'aaaaaaaa',
customModalGasPriceInHex: 'ffffffff',
gasPriceButtonGroupProps: {
buttonDataLoading: 'mockBasicGasEstimateLoadingStatus:4',
defaultActiveButtonIndex: 'mockRenderableBasicEstimateData:4ffffffff0x3200000',
defaultActiveButtonIndex: 'mockRenderableBasicEstimateData:4ffffffff',
gasButtonInfo: 'mockRenderableBasicEstimateData:4',
},
hideBasic: true,
@ -106,6 +106,8 @@ describe('gas-modal-page-container container', () => {
originalTotalEth: '0.451569 ETH',
newTotalFiat: '637.41',
newTotalEth: '12.748189 ETH',
sendAmount: '0.45036 ETH',
transactionFee: '12.297829 ETH',
},
})
})
@ -135,11 +137,12 @@ describe('gas-modal-page-container container', () => {
})
})
describe('hideModal()', () => {
describe('cancelAndClose()', () => {
it('should dispatch a hideModal action', () => {
mapDispatchToPropsObject.hideModal()
assert(dispatchSpy.calledOnce)
mapDispatchToPropsObject.cancelAndClose()
assert(dispatchSpy.calledTwice)
assert(actionSpies.hideModal.calledOnce)
assert(gasActionSpies.resetCustomData.calledOnce)
})
})

View File

@ -0,0 +1,16 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class GasPriceChart extends Component {
static contextTypes = {
t: PropTypes.func,
}
render () {
return (
<div className="gas-price-chart">
<div className="gas-price-chart__container" id="chart"></div>
</div>
)
}
}

View File

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

View File

@ -0,0 +1,6 @@
.gas-price-chart {
&__container {
display: flex;
position: relative;
}
}

View File

@ -0,0 +1,25 @@
import React from 'react'
import assert from 'assert'
import shallow from '../../../../../lib/shallow-with-context'
import GasPriceChart from '../gas-price-chart.component.js'
describe('GasPriceChart Component', function () {
let wrapper
beforeEach(() => {
wrapper = shallow(<GasPriceChart />)
})
describe('render()', () => {
it('should render', () => {
console.log('wrapper', wrapper.html())
assert(wrapper.hasClass('gas-price-chart'))
})
it('should render the chart div', () => {
assert(wrapper.childAt(0).hasClass('gas-price-chart__container'))
assert.equal(wrapper.childAt(0).props().id, 'chart')
})
})
})

View File

@ -1,3 +1,5 @@
@import './gas-slider/index';
@import './gas-modal-page-container/index';
@import './gas-price-chart/index';

View File

@ -12,6 +12,7 @@ export default class PageContainerFooter extends Component {
submitText: PropTypes.string,
disabled: PropTypes.bool,
submitButtonType: PropTypes.string,
hideCancel: PropTypes.func,
}
static contextTypes = {
@ -27,20 +28,21 @@ export default class PageContainerFooter extends Component {
submitText,
disabled,
submitButtonType,
hideCancel,
} = this.props
return (
<div className="page-container__footer">
<header>
<Button
{!hideCancel && <Button
type="default"
large
className="page-container__footer-button"
onClick={e => onCancel(e)}
>
{ cancelText || this.context.t('cancel') }
</Button>
</Button>}
<Button
type={submitButtonType || 'primary'}

View File

@ -12,6 +12,7 @@ export default class PageContainerHeader extends Component {
backButtonStyles: PropTypes.object,
backButtonString: PropTypes.string,
tabs: PropTypes.node,
headerCloseText: PropTypes.string,
}
renderTabs () {
@ -41,7 +42,7 @@ export default class PageContainerHeader extends Component {
}
render () {
const { title, subtitle, onClose, tabs } = this.props
const { title, subtitle, onClose, tabs, headerCloseText } = this.props
return (
<div className={
@ -66,10 +67,12 @@ export default class PageContainerHeader extends Component {
}
{
onClose && <div
className="page-container__header-close"
onClick={() => onClose()}
/>
onClose && headerCloseText
? <div className="page-container__header-close-text" onClick={() => onClose()}>{ headerCloseText }</div>
: onClose && <div
className="page-container__header-close"
onClick={() => onClose()}
/>
}
{ this.renderTabs() }

View File

@ -9,6 +9,7 @@ export default class PageContainer extends PureComponent {
// PageContainerHeader props
backButtonString: PropTypes.string,
backButtonStyles: PropTypes.object,
headerCloseText: PropTypes.string,
onBackButtonClick: PropTypes.func,
onClose: PropTypes.func,
showBackButton: PropTypes.bool,
@ -22,6 +23,7 @@ export default class PageContainer extends PureComponent {
// PageContainerFooter props
cancelText: PropTypes.string,
disabled: PropTypes.bool,
hideCancel: PropTypes.string,
onCancel: PropTypes.func,
onSubmit: PropTypes.func,
submitText: PropTypes.string,
@ -93,6 +95,8 @@ export default class PageContainer extends PureComponent {
onSubmit,
submitText,
disabled,
headerCloseText,
hideCancel,
} = this.props
return (
@ -106,6 +110,7 @@ export default class PageContainer extends PureComponent {
backButtonStyles={backButtonStyles}
backButtonString={backButtonString}
tabs={this.renderTabs()}
headerCloseText={headerCloseText}
/>
<div className="page-container__content">
{ this.renderContent() }
@ -113,6 +118,7 @@ export default class PageContainer extends PureComponent {
<PageContainerFooter
onCancel={onCancel}
cancelText={cancelText}
hideCancel={hideCancel}
onSubmit={onSubmit}
submitText={submitText}
disabled={disabled}

View File

@ -4,6 +4,7 @@ import { clone } from 'ramda'
const BASIC_GAS_ESTIMATE_LOADING_FINISHED = 'metamask/gas/BASIC_GAS_ESTIMATE_LOADING_FINISHED'
const BASIC_GAS_ESTIMATE_LOADING_STARTED = 'metamask/gas/BASIC_GAS_ESTIMATE_LOADING_STARTED'
const RESET_CUSTOM_GAS_STATE = 'metamask/gas/RESET_CUSTOM_GAS_STATE'
const RESET_CUSTOM_DATA = 'metamask/gas/RESET_CUSTOM_DATA'
const SET_BASIC_GAS_ESTIMATE_DATA = 'metamask/gas/SET_BASIC_GAS_ESTIMATE_DATA'
const SET_CUSTOM_GAS_ERRORS = 'metamask/gas/SET_CUSTOM_GAS_ERRORS'
const SET_CUSTOM_GAS_LIMIT = 'metamask/gas/SET_CUSTOM_GAS_LIMIT'
@ -85,6 +86,11 @@ export default function reducer ({ gas: gasState = initState }, action = {}) {
...action.value,
},
}
case RESET_CUSTOM_DATA:
return {
...newState,
customData: clone(initState.customData),
}
case RESET_CUSTOM_GAS_STATE:
return clone(initState)
default:
@ -187,3 +193,7 @@ export function setCustomGasErrors (newErrors) {
export function resetCustomGasState () {
return { type: RESET_CUSTOM_GAS_STATE }
}
export function resetCustomData () {
return { type: RESET_CUSTOM_DATA }
}