diff --git a/app/images/icons/swap.svg b/app/images/icons/swap.svg new file mode 100644 index 000000000..9d4bbbda5 --- /dev/null +++ b/app/images/icons/swap.svg @@ -0,0 +1 @@ + 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 ( - - { this.renderConversionComponent() } - + + )} + > + { this.renderConversionComponent() } + ) } } 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( @@ -58,7 +59,8 @@ 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( + + + + ) + + 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} > - - { this.unitInput = ref }} - /> - { - suffix && ( - - { suffix } - - ) - } + + + { this.unitInput = ref }} + /> + { + suffix && ( + + { suffix } + + ) + } + + { children } - { children } + {actionComponent} ) }