aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan J Miller <danjm.com@gmail.com>2018-06-01 01:39:13 +0800
committerGitHub <noreply@github.com>2018-06-01 01:39:13 +0800
commit15f4ce352ddb88c43659ddd6fa29e53126d2a68c (patch)
treef96e1e8d130a045b88dd67af286b3e2a4ec5c9c6
parent6d8344d0d0af3734255a0e9e79d857d84b5fe2aa (diff)
parent3745c1ea4fd62f092c7e277a16b9204b0c0ddaea (diff)
downloadtangerine-wallet-browser-15f4ce352ddb88c43659ddd6fa29e53126d2a68c.tar
tangerine-wallet-browser-15f4ce352ddb88c43659ddd6fa29e53126d2a68c.tar.gz
tangerine-wallet-browser-15f4ce352ddb88c43659ddd6fa29e53126d2a68c.tar.bz2
tangerine-wallet-browser-15f4ce352ddb88c43659ddd6fa29e53126d2a68c.tar.lz
tangerine-wallet-browser-15f4ce352ddb88c43659ddd6fa29e53126d2a68c.tar.xz
tangerine-wallet-browser-15f4ce352ddb88c43659ddd6fa29e53126d2a68c.tar.zst
tangerine-wallet-browser-15f4ce352ddb88c43659ddd6fa29e53126d2a68c.zip
Merge pull request #4386 from MetaMask/i4077-replace-currency-input-with-numeric-input
Replace currency-input.js with NumericInput
-rw-r--r--.eslintrc1
-rw-r--r--package-lock.json12
-rw-r--r--test/integration/lib/send-new-ui.js6
-rw-r--r--ui/app/components/currency-input.js113
-rw-r--r--ui/app/components/input-number.js8
-rw-r--r--ui/app/components/send/currency-display.js58
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js6
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js38
-rw-r--r--ui/app/css/itcss/components/currency-display.scss22
9 files changed, 108 insertions, 156 deletions
diff --git a/.eslintrc b/.eslintrc
index 511d6f216..b49a7a28a 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -142,6 +142,7 @@
"operator-linebreak": [1, "after", { "overrides": { "?": "ignore", ":": "ignore" } }],
"padded-blocks": "off",
"quotes": [2, "single", {"avoidEscape": true, "allowTemplateLiterals": true}],
+ "react/no-deprecated": 0,
"semi": [2, "never"],
"semi-spacing": [2, { "before": false, "after": true }],
"space-before-blocks": [1, "always"],
diff --git a/package-lock.json b/package-lock.json
index d9fe1dfd2..cfaf18be6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26436,6 +26436,18 @@
"object-assign": "4.1.1"
}
},
+ "react": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz",
+ "integrity": "sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=",
+ "requires": {
+ "create-react-class": "15.6.2",
+ "fbjs": "0.8.16",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1",
+ "prop-types": "15.6.1"
+ }
+ },
"react-hyperscript": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/react-hyperscript/-/react-hyperscript-2.4.2.tgz",
diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js
index 3da3f4f95..ed1feec7d 100644
--- a/test/integration/lib/send-new-ui.js
+++ b/test/integration/lib/send-new-ui.js
@@ -101,7 +101,7 @@ async function runSendFlowTest(assert, done) {
const sendAmountField = await queryAsync($, '.send-v2__form-row:eq(2)')
sendAmountField.find('.currency-display')[0].click()
- const sendAmountFieldInput = await findAsync(sendAmountField, 'input:text')
+ const sendAmountFieldInput = await findAsync(sendAmountField, '.currency-display__input')
sendAmountFieldInput.val('5.1')
reactTriggerChange(sendAmountField.find('input')[0])
@@ -127,7 +127,7 @@ async function runSendFlowTest(assert, done) {
)
await customizeGas(assert, 0, 21000, '0', '$0.00 USD')
- await customizeGas(assert, 500, 60000, '0.003', '$3.60 USD')
+ await customizeGas(assert, 500, 60000, '0.03', '$36.03 USD')
const sendButton = await queryAsync($, 'button.btn-primary--lg.page-container__footer-button')
assert.equal(sendButton[0].textContent, 'Next', 'next button rendered')
@@ -165,7 +165,7 @@ async function runSendFlowTest(assert, done) {
const sendAmountFieldInEdit = await queryAsync($, '.send-v2__form-row:eq(2)')
sendAmountFieldInEdit.find('.currency-display')[0].click()
- const sendAmountFieldInputInEdit = sendAmountFieldInEdit.find('input:text')
+ const sendAmountFieldInputInEdit = sendAmountFieldInEdit.find('.currency-display__input')
sendAmountFieldInputInEdit.val('1.0')
reactTriggerChange(sendAmountFieldInputInEdit[0])
diff --git a/ui/app/components/currency-input.js b/ui/app/components/currency-input.js
deleted file mode 100644
index ece3eb43d..000000000
--- a/ui/app/components/currency-input.js
+++ /dev/null
@@ -1,113 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-module.exports = CurrencyInput
-
-inherits(CurrencyInput, Component)
-function CurrencyInput (props) {
- Component.call(this)
-
- const sanitizedValue = sanitizeValue(props.value)
-
- this.state = {
- value: sanitizedValue,
- emptyState: false,
- focused: false,
- }
-}
-
-function removeNonDigits (str) {
- return str.match(/\d|$/g).join('')
-}
-
-// Removes characters that are not digits, then removes leading zeros
-function sanitizeInteger (val) {
- return String(parseInt(removeNonDigits(val) || '0', 10))
-}
-
-function sanitizeDecimal (val) {
- return removeNonDigits(val)
-}
-
-// Take a single string param and returns a non-negative integer or float as a string.
-// Breaks the input into three parts: the integer, the decimal point, and the decimal/fractional part.
-// Removes leading zeros from the integer, and non-digits from the integer and decimal
-// The integer is returned as '0' in cases where it would be empty. A decimal point is
-// included in the returned string if one is included in the param
-// Examples:
-// sanitizeValue('0') -> '0'
-// sanitizeValue('a') -> '0'
-// sanitizeValue('010.') -> '10.'
-// sanitizeValue('0.005') -> '0.005'
-// sanitizeValue('22.200') -> '22.200'
-// sanitizeValue('.200') -> '0.200'
-// sanitizeValue('a.b.1.c,89.123') -> '0.189123'
-function sanitizeValue (value) {
- let [ , integer, point, decimal] = (/([^.]*)([.]?)([^.]*)/).exec(value)
-
- integer = sanitizeInteger(integer) || '0'
- decimal = sanitizeDecimal(decimal)
-
- return `${integer}${point}${decimal}`
-}
-
-CurrencyInput.prototype.handleChange = function (newValue) {
- const { onInputChange } = this.props
- const { value } = this.state
-
- let parsedValue = newValue
- const newValueLastIndex = newValue.length - 1
-
- if (value === '0' && newValue[newValueLastIndex] === '0') {
- parsedValue = parsedValue.slice(0, newValueLastIndex)
- }
- const sanitizedValue = sanitizeValue(parsedValue)
- this.setState({
- value: sanitizedValue,
- emptyState: newValue === '' && sanitizedValue === '0',
- })
- onInputChange(sanitizedValue)
-}
-
-// If state.value === props.value plus a decimal point, or at least one
-// zero or a decimal point and at least one zero, then this returns state.value
-// after it is sanitized with getValueParts
-CurrencyInput.prototype.getValueToRender = function () {
- const { value } = this.props
- const { value: stateValue } = this.state
-
- const trailingStateString = (new RegExp(`^${value}(.+)`)).exec(stateValue)
- const trailingDecimalAndZeroes = trailingStateString && (/^[.0]0*/).test(trailingStateString[1])
-
- return sanitizeValue(trailingDecimalAndZeroes
- ? stateValue
- : value)
-}
-
-CurrencyInput.prototype.render = function () {
- const {
- className,
- placeholder,
- readOnly,
- inputRef,
- type,
- } = this.props
- const { emptyState, focused } = this.state
-
- const inputSizeMultiplier = readOnly ? 1 : 1.2
-
- const valueToRender = this.getValueToRender()
- return h('input', {
- className,
- type,
- value: emptyState ? '' : valueToRender,
- placeholder: focused ? '' : placeholder,
- size: valueToRender.length * inputSizeMultiplier,
- readOnly,
- onFocus: () => this.setState({ focused: true, emptyState: valueToRender === '0' }),
- onBlur: () => this.setState({ focused: false, emptyState: false }),
- onChange: e => this.handleChange(e.target.value),
- ref: inputRef,
- })
-}
diff --git a/ui/app/components/input-number.js b/ui/app/components/input-number.js
index 5600e35ee..de5fcca54 100644
--- a/ui/app/components/input-number.js
+++ b/ui/app/components/input-number.js
@@ -1,7 +1,6 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const CurrencyInput = require('./currency-input')
const {
addCurrencies,
conversionGTE,
@@ -51,14 +50,15 @@ InputNumber.prototype.render = function () {
const { unitLabel, step = 1, placeholder, value = 0 } = this.props
return h('div.customize-gas-input-wrapper', {}, [
- h(CurrencyInput, {
+ h('input', {
className: 'customize-gas-input',
value,
placeholder,
type: 'number',
- onInputChange: newValue => {
- this.setValue(newValue)
+ onChange: e => {
+ this.setValue(e.target.value)
},
+ min: 0,
}),
h('span.gas-tooltip-input-detail', {}, [unitLabel]),
h('div.gas-tooltip-input-arrows', {}, [
diff --git a/ui/app/components/send/currency-display.js b/ui/app/components/send/currency-display.js
index 90fb2b66c..360dd15d4 100644
--- a/ui/app/components/send/currency-display.js
+++ b/ui/app/components/send/currency-display.js
@@ -1,7 +1,6 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
-const CurrencyInput = require('../currency-input')
const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
const currencyFormatter = require('currency-formatter')
const currencies = require('currency-formatter/currencies')
@@ -21,21 +20,36 @@ function toHexWei (value) {
})
}
+CurrencyDisplay.prototype.componentWillMount = function () {
+ this.setState({
+ valueToRender: this.getValueToRender(this.props),
+ })
+}
+
+CurrencyDisplay.prototype.componentWillReceiveProps = function (nextProps) {
+ const currentValueToRender = this.getValueToRender(this.props)
+ const newValueToRender = this.getValueToRender(nextProps)
+ if (currentValueToRender !== newValueToRender) {
+ this.setState({
+ valueToRender: newValueToRender,
+ })
+ }
+}
+
CurrencyDisplay.prototype.getAmount = function (value) {
const { selectedToken } = this.props
const { decimals } = selectedToken || {}
const multiplier = Math.pow(10, Number(decimals || 0))
- const sendAmount = multiplyCurrencies(value, multiplier, {toNumericBase: 'hex'})
+ const sendAmount = multiplyCurrencies(value || '0', multiplier, {toNumericBase: 'hex'})
return selectedToken
? sendAmount
: toHexWei(value)
}
-CurrencyDisplay.prototype.getValueToRender = function () {
- const { selectedToken, conversionRate, value } = this.props
-
+CurrencyDisplay.prototype.getValueToRender = function ({ selectedToken, conversionRate, value }) {
+ if (value === '0x0') return '0'
const { decimals, symbol } = selectedToken || {}
const multiplier = Math.pow(10, Number(decimals || 0))
@@ -76,6 +90,18 @@ CurrencyDisplay.prototype.getConvertedValueToRender = function (nonFormattedValu
: convertedValue
}
+CurrencyDisplay.prototype.handleChange = function (newVal) {
+ this.setState({ valueToRender: newVal })
+ this.props.onChange(this.getAmount(newVal))
+}
+
+CurrencyDisplay.prototype.getInputWidth = function (valueToRender, readOnly) {
+ const valueString = String(valueToRender)
+ const valueLength = valueString.length || 1
+ const decimalPointDeficit = valueString.match(/\./) ? -0.5 : 0
+ return (valueLength + decimalPointDeficit + 0.75) + 'ch'
+}
+
CurrencyDisplay.prototype.render = function () {
const {
className = 'currency-display',
@@ -85,10 +111,10 @@ CurrencyDisplay.prototype.render = function () {
convertedCurrency,
readOnly = false,
inError = false,
- handleChange,
+ onBlur,
} = this.props
+ const { valueToRender } = this.state
- const valueToRender = this.getValueToRender()
const convertedValueToRender = this.getConvertedValueToRender(valueToRender)
return h('div', {
@@ -96,24 +122,30 @@ CurrencyDisplay.prototype.render = function () {
style: {
borderColor: inError ? 'red' : null,
},
- onClick: () => this.currencyInput && this.currencyInput.focus(),
+ onClick: () => {
+ this.currencyInput && this.currencyInput.focus()
+ },
}, [
h('div.currency-display__primary-row', [
h('div.currency-display__input-wrapper', [
- h(readOnly ? 'input' : CurrencyInput, {
+ h('input', {
className: primaryBalanceClassName,
value: `${valueToRender}`,
placeholder: '0',
+ type: 'number',
readOnly,
...(!readOnly ? {
- onInputChange: newValue => {
- handleChange(this.getAmount(newValue))
- },
- inputRef: input => { this.currencyInput = input },
+ onChange: e => this.handleChange(e.target.value),
+ onBlur: () => onBlur(this.getAmount(valueToRender)),
} : {}),
+ ref: input => { this.currencyInput = input },
+ style: {
+ width: this.getInputWidth(valueToRender, readOnly),
+ },
+ min: 0,
}),
h('span.currency-display__currency-symbol', primaryCurrency),
diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js
index b094d0cd5..8aefeed4a 100644
--- a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js
+++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js
@@ -49,11 +49,10 @@ export default class SendAmountRow extends Component {
})
}
- handleAmountChange (amount) {
+ updateAmount (amount) {
const { updateSendAmount, setMaxModeTo } = this.props
setMaxModeTo(false)
- this.validateAmount(amount)
updateSendAmount(amount)
}
@@ -78,7 +77,8 @@ export default class SendAmountRow extends Component {
<CurrencyDisplay
conversionRate={amountConversionRate}
convertedCurrency={convertedCurrency}
- handleChange={newAmount => this.handleAmountChange(newAmount)}
+ onBlur={newAmount => this.updateAmount(newAmount)}
+ onChange={newAmount => this.validateAmount(newAmount)}
inError={inError}
primaryCurrency={primaryCurrency || 'ETH'}
selectedToken={selectedToken}
diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js
index 31d2e2515..2205579ca 100644
--- a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js
+++ b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js
@@ -14,7 +14,7 @@ const propsMethodSpies = {
updateSendAmountError: sinon.spy(),
}
-sinon.spy(SendAmountRow.prototype, 'handleAmountChange')
+sinon.spy(SendAmountRow.prototype, 'updateAmount')
sinon.spy(SendAmountRow.prototype, 'validateAmount')
describe('SendAmountRow Component', function () {
@@ -45,7 +45,7 @@ describe('SendAmountRow Component', function () {
propsMethodSpies.updateSendAmount.resetHistory()
propsMethodSpies.updateSendAmountError.resetHistory()
SendAmountRow.prototype.validateAmount.resetHistory()
- SendAmountRow.prototype.handleAmountChange.resetHistory()
+ SendAmountRow.prototype.updateAmount.resetHistory()
})
describe('validateAmount', () => {
@@ -71,11 +71,11 @@ describe('SendAmountRow Component', function () {
})
- describe('handleAmountChange', () => {
+ describe('updateAmount', () => {
it('should call setMaxModeTo', () => {
assert.equal(propsMethodSpies.setMaxModeTo.callCount, 0)
- instance.handleAmountChange('someAmount')
+ instance.updateAmount('someAmount')
assert.equal(propsMethodSpies.setMaxModeTo.callCount, 1)
assert.deepEqual(
propsMethodSpies.setMaxModeTo.getCall(0).args,
@@ -83,19 +83,9 @@ describe('SendAmountRow Component', function () {
)
})
- it('should call this.validateAmount', () => {
- assert.equal(SendAmountRow.prototype.validateAmount.callCount, 0)
- instance.handleAmountChange('someAmount')
- assert.equal(SendAmountRow.prototype.validateAmount.callCount, 1)
- assert.deepEqual(
- propsMethodSpies.updateSendAmount.getCall(0).args,
- ['someAmount']
- )
- })
-
it('should call updateSendAmount', () => {
assert.equal(propsMethodSpies.updateSendAmount.callCount, 0)
- instance.handleAmountChange('someAmount')
+ instance.updateAmount('someAmount')
assert.equal(propsMethodSpies.updateSendAmount.callCount, 1)
assert.deepEqual(
propsMethodSpies.updateSendAmount.getCall(0).args,
@@ -136,7 +126,8 @@ describe('SendAmountRow Component', function () {
const {
conversionRate,
convertedCurrency,
- handleChange,
+ onBlur,
+ onChange,
inError,
primaryCurrency,
selectedToken,
@@ -148,11 +139,18 @@ describe('SendAmountRow Component', function () {
assert.equal(primaryCurrency, 'mockPrimaryCurrency')
assert.deepEqual(selectedToken, { address: 'mockTokenAddress' })
assert.equal(value, 'mockAmount')
- assert.equal(SendAmountRow.prototype.handleAmountChange.callCount, 0)
- handleChange('mockNewAmount')
- assert.equal(SendAmountRow.prototype.handleAmountChange.callCount, 1)
+ assert.equal(SendAmountRow.prototype.updateAmount.callCount, 0)
+ onBlur('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.handleAmountChange.getCall(0).args,
+ SendAmountRow.prototype.validateAmount.getCall(0).args,
['mockNewAmount']
)
})
diff --git a/ui/app/css/itcss/components/currency-display.scss b/ui/app/css/itcss/components/currency-display.scss
index 36d843c79..3560b0b0c 100644
--- a/ui/app/css/itcss/components/currency-display.scss
+++ b/ui/app/css/itcss/components/currency-display.scss
@@ -47,10 +47,32 @@
&__input-wrapper {
position: relative;
display: flex;
+
+ input[type="number"]::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ display: none;
+ }
+
+ input[type="number"]:hover::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ display: none;
+ }
}
&__currency-symbol {
margin-top: 1px;
color: $scorpion;
}
+
+ .react-numeric-input {
+ input[type="number"]::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ display: none;
+ }
+
+ input[type="number"]:hover::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ display: none;
+ }
+ }
} \ No newline at end of file