diff options
author | Chi Kei Chan <chikeichan@gmail.com> | 2019-02-06 23:14:17 +0800 |
---|---|---|
committer | Whymarrh Whitby <whymarrh.whitby@gmail.com> | 2019-02-06 23:14:17 +0800 |
commit | 798930afbaa01d691059db1cec4caf12414493b9 (patch) | |
tree | e39377ef7ddcac23afe6698996f8bcbff79b79e8 /ui | |
parent | 83109c3dc7fbb3e89c047363ec3baeac4df99f77 (diff) | |
download | tangerine-wallet-browser-798930afbaa01d691059db1cec4caf12414493b9.tar tangerine-wallet-browser-798930afbaa01d691059db1cec4caf12414493b9.tar.gz tangerine-wallet-browser-798930afbaa01d691059db1cec4caf12414493b9.tar.bz2 tangerine-wallet-browser-798930afbaa01d691059db1cec4caf12414493b9.tar.lz tangerine-wallet-browser-798930afbaa01d691059db1cec4caf12414493b9.tar.xz tangerine-wallet-browser-798930afbaa01d691059db1cec4caf12414493b9.tar.zst tangerine-wallet-browser-798930afbaa01d691059db1cec4caf12414493b9.zip |
Add Swap feature to CurrencyInput (#6091)
* Add Swap feature to CurrencyInput
* Fix linter error
* Fix and Add unit tests
Diffstat (limited to 'ui')
7 files changed, 151 insertions, 45 deletions
diff --git a/ui/app/components/currency-input/currency-input.component.js b/ui/app/components/currency-input/currency-input.component.js index 0761a75c5..8fa3131ae 100644 --- a/ui/app/components/currency-input/currency-input.component.js +++ b/ui/app/components/currency-input/currency-input.component.js @@ -17,9 +17,10 @@ export default class CurrencyInput extends PureComponent { nativeCurrency: PropTypes.string, onChange: PropTypes.func, onBlur: PropTypes.func, - suffix: PropTypes.string, useFiat: PropTypes.bool, value: PropTypes.string, + fiatSuffix: PropTypes.string, + nativeSuffix: PropTypes.string, } constructor (props) { @@ -31,6 +32,7 @@ export default class CurrencyInput extends PureComponent { this.state = { decimalValue, hexValue, + isSwapped: false, } } @@ -46,8 +48,8 @@ export default class CurrencyInput extends PureComponent { } getDecimalValue (props) { - const { value: hexValue, useFiat, currentCurrency, conversionRate } = props - const decimalValueString = useFiat + const { value: hexValue, currentCurrency, conversionRate } = props + const decimalValueString = this.shouldUseFiat() ? getValueFromWeiHex({ value: hexValue, toCurrency: currentCurrency, conversionRate, numberOfDecimals: 2, }) @@ -58,10 +60,23 @@ export default class CurrencyInput extends PureComponent { return Number(decimalValueString) || 0 } + shouldUseFiat = () => { + const { useFiat } = this.props + const { isSwapped } = this.state || {} + return isSwapped ? !useFiat : useFiat + } + + swap = () => { + const { isSwapped, decimalValue } = this.state + this.setState({isSwapped: !isSwapped}, () => { + this.handleChange(decimalValue) + }) + } + handleChange = decimalValue => { - const { useFiat, currentCurrency: fromCurrency, conversionRate, onChange } = this.props + const { currentCurrency: fromCurrency, conversionRate, onChange } = this.props - const hexValue = useFiat + const hexValue = this.shouldUseFiat() ? getWeiHexFromDecimalValue({ value: decimalValue, fromCurrency, conversionRate, invertConversionRate: true, }) @@ -78,11 +93,11 @@ export default class CurrencyInput extends PureComponent { } renderConversionComponent () { - const { useFiat, currentCurrency, nativeCurrency } = this.props + const { currentCurrency, nativeCurrency } = this.props const { hexValue } = this.state let currency, numberOfDecimals - if (useFiat) { + if (this.shouldUseFiat()) { // Display ETH currency = nativeCurrency || ETH numberOfDecimals = 6 @@ -103,19 +118,25 @@ export default class CurrencyInput extends PureComponent { } render () { - const { suffix, ...restProps } = this.props + const { fiatSuffix, nativeSuffix, ...restProps } = this.props const { decimalValue } = this.state return ( - <UnitInput - {...restProps} - suffix={suffix} - onChange={this.handleChange} - onBlur={this.handleBlur} - value={decimalValue} - > - { this.renderConversionComponent() } - </UnitInput> + <UnitInput + {...restProps} + suffix={this.shouldUseFiat() ? fiatSuffix : nativeSuffix} + onChange={this.handleChange} + onBlur={this.handleBlur} + value={decimalValue} + actionComponent={( + <div + className="currency-input__swap-component" + onClick={this.swap} + /> + )} + > + { this.renderConversionComponent() } + </UnitInput> ) } } diff --git a/ui/app/components/currency-input/currency-input.container.js b/ui/app/components/currency-input/currency-input.container.js index 1d1ed7b41..4c89d6c1d 100644 --- a/ui/app/components/currency-input/currency-input.container.js +++ b/ui/app/components/currency-input/currency-input.container.js @@ -14,14 +14,13 @@ const mapStateToProps = state => { const mergeProps = (stateProps, dispatchProps, ownProps) => { const { nativeCurrency, currentCurrency } = stateProps - const { useFiat } = ownProps - const suffix = useFiat ? currentCurrency.toUpperCase() : nativeCurrency || ETH return { ...stateProps, ...dispatchProps, ...ownProps, - suffix, + nativeSuffix: nativeCurrency || ETH, + fiatSuffix: currentCurrency.toUpperCase(), } } diff --git a/ui/app/components/currency-input/index.scss b/ui/app/components/currency-input/index.scss index fcb2db461..f659f5b35 100644 --- a/ui/app/components/currency-input/index.scss +++ b/ui/app/components/currency-input/index.scss @@ -4,4 +4,23 @@ line-height: 12px; padding-left: 1px; } + + &__swap-component { + flex: 0 0 auto; + height: 24px; + width: 24px; + background-image: url("images/icons/swap.svg"); + background-size: contain; + background-repeat: no-repeat; + cursor: pointer; + opacity: .4; + + &:hover { + opacity: .3; + } + + &:active { + opacity: .5; + } + } } diff --git a/ui/app/components/currency-input/tests/currency-input.component.test.js b/ui/app/components/currency-input/tests/currency-input.component.test.js index a33889f94..e1ab29187 100644 --- a/ui/app/components/currency-input/tests/currency-input.component.test.js +++ b/ui/app/components/currency-input/tests/currency-input.component.test.js @@ -32,7 +32,8 @@ describe('CurrencyInput Component', () => { const wrapper = mount( <Provider store={store}> <CurrencyInput - suffix="ETH" + nativeSuffix="ETH" + fiatSuffix="USD" nativeCurrency="ETH" /> </Provider> @@ -58,7 +59,8 @@ describe('CurrencyInput Component', () => { <Provider store={store}> <CurrencyInput value="de0b6b3a7640000" - suffix="ETH" + fiatSuffix="USD" + nativeSuffix="ETH" nativeCurrency="ETH" currentCurrency="usd" conversionRate={231.06} @@ -90,7 +92,8 @@ describe('CurrencyInput Component', () => { <Provider store={store}> <CurrencyInput value="f602f2234d0ea" - suffix="USD" + fiatSuffix="USD" + nativeSuffix="ETH" useFiat nativeCurrency="ETH" currentCurrency="usd" @@ -247,5 +250,56 @@ describe('CurrencyInput Component', () => { assert.equal(currencyInputInstance.state('hexValue'), '1ec05e43e72400') assert.equal(currencyInputInstance.find(UnitInput).props().value, 2) }) + + it('should swap selected currency when swap icon is clicked', () => { + const mockStore = { + metamask: { + nativeCurrency: 'ETH', + currentCurrency: 'usd', + conversionRate: 231.06, + }, + } + const store = configureMockStore()(mockStore) + const wrapper = mount( + <Provider store={store}> + <CurrencyInput + onChange={handleChangeSpy} + onBlur={handleBlurSpy} + nativeSuffix="ETH" + fiatSuffix="USD" + nativeCurrency="ETH" + currentCurrency="usd" + conversionRate={231.06} + /> + </Provider> + ) + + assert.ok(wrapper) + assert.equal(handleChangeSpy.callCount, 0) + assert.equal(handleBlurSpy.callCount, 0) + + const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance() + assert.equal(currencyInputInstance.state.decimalValue, 0) + assert.equal(currencyInputInstance.state.hexValue, undefined) + assert.equal(wrapper.find('.currency-display-component').text(), '$0.00USD') + const input = wrapper.find('input') + assert.equal(input.props().value, 0) + + input.simulate('change', { target: { value: 1 } }) + assert.equal(handleChangeSpy.callCount, 1) + assert.ok(handleChangeSpy.calledWith('de0b6b3a7640000')) + assert.equal(wrapper.find('.currency-display-component').text(), '$231.06USD') + assert.equal(currencyInputInstance.state.decimalValue, 1) + assert.equal(currencyInputInstance.state.hexValue, 'de0b6b3a7640000') + + assert.equal(handleBlurSpy.callCount, 0) + input.simulate('blur') + assert.equal(handleBlurSpy.callCount, 1) + assert.ok(handleBlurSpy.calledWith('de0b6b3a7640000')) + + const swap = wrapper.find('.currency-input__swap-component') + swap.simulate('click') + assert.equal(wrapper.find('.currency-display-component').text(), '0.004328ETH') + }) }) }) diff --git a/ui/app/components/currency-input/tests/currency-input.container.test.js b/ui/app/components/currency-input/tests/currency-input.container.test.js index 5d72958e6..10f530eff 100644 --- a/ui/app/components/currency-input/tests/currency-input.container.test.js +++ b/ui/app/components/currency-input/tests/currency-input.container.test.js @@ -46,14 +46,16 @@ describe('CurrencyInput container', () => { currentCurrency: 'usd', nativeCurrency: 'ETH', useFiat: true, - suffix: 'USD', + nativeSuffix: 'ETH', + fiatSuffix: 'USD', }) assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, {}), { conversionRate: 280.45, currentCurrency: 'usd', nativeCurrency: 'ETH', - suffix: 'ETH', + nativeSuffix: 'ETH', + fiatSuffix: 'USD', }) }) }) diff --git a/ui/app/components/unit-input/index.scss b/ui/app/components/unit-input/index.scss index 7995696aa..e4075d225 100644 --- a/ui/app/components/unit-input/index.scss +++ b/ui/app/components/unit-input/index.scss @@ -1,4 +1,7 @@ .unit-input { + display: flex; + flex-flow: row nowrap; + align-items: center; min-height: 54px; border: 1px solid #dedede; border-radius: 4px; @@ -24,6 +27,10 @@ display: none; } + &__inputs { + flex: 1 0 auto; + } + &__input { color: #4d4d4d; font-size: 1rem; diff --git a/ui/app/components/unit-input/unit-input.component.js b/ui/app/components/unit-input/unit-input.component.js index 0c6b21797..230eecfe6 100644 --- a/ui/app/components/unit-input/unit-input.component.js +++ b/ui/app/components/unit-input/unit-input.component.js @@ -11,6 +11,7 @@ import { removeLeadingZeroes } from '../send/send.utils' export default class UnitInput extends PureComponent { static propTypes = { children: PropTypes.node, + actionComponent: PropTypes.node, error: PropTypes.bool, onBlur: PropTypes.func, onChange: PropTypes.func, @@ -70,7 +71,7 @@ export default class UnitInput extends PureComponent { } render () { - const { error, placeholder, suffix, children } = this.props + const { error, placeholder, suffix, actionComponent, children } = this.props const { value } = this.state return ( @@ -78,26 +79,29 @@ export default class UnitInput extends PureComponent { className={classnames('unit-input', { 'unit-input--error': error })} onClick={this.handleFocus} > - <div className="unit-input__input-container"> - <input - type="number" - className="unit-input__input" - value={value} - placeholder={placeholder} - onChange={this.handleChange} - onBlur={this.handleBlur} - style={{ width: this.getInputWidth(value) }} - ref={ref => { this.unitInput = ref }} - /> - { - suffix && ( - <div className="unit-input__suffix"> - { suffix } - </div> - ) - } + <div className="unit-input__inputs"> + <div className="unit-input__input-container"> + <input + type="number" + className="unit-input__input" + value={value} + placeholder={placeholder} + onChange={this.handleChange} + onBlur={this.handleBlur} + style={{ width: this.getInputWidth(value) }} + ref={ref => { this.unitInput = ref }} + /> + { + suffix && ( + <div className="unit-input__suffix"> + { suffix } + </div> + ) + } + </div> + { children } </div> - { children } + {actionComponent} </div> ) } |