mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Add Swap feature to CurrencyInput (#6091)
* Add Swap feature to CurrencyInput * Fix linter error * Fix and Add unit tests
This commit is contained in:
parent
83109c3dc7
commit
798930afba
1
app/images/icons/swap.svg
Normal file
1
app/images/icons/swap.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
|
After Width: | Height: | Size: 209 B |
@ -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
|
||||
}
|
||||
|
||||
handleChange = decimalValue => {
|
||||
const { useFiat, currentCurrency: fromCurrency, conversionRate, onChange } = this.props
|
||||
shouldUseFiat = () => {
|
||||
const { useFiat } = this.props
|
||||
const { isSwapped } = this.state || {}
|
||||
return isSwapped ? !useFiat : useFiat
|
||||
}
|
||||
|
||||
const hexValue = useFiat
|
||||
swap = () => {
|
||||
const { isSwapped, decimalValue } = this.state
|
||||
this.setState({isSwapped: !isSwapped}, () => {
|
||||
this.handleChange(decimalValue)
|
||||
})
|
||||
}
|
||||
|
||||
handleChange = decimalValue => {
|
||||
const { currentCurrency: fromCurrency, conversionRate, onChange } = this.props
|
||||
|
||||
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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -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',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user