Gère le changement de prop value sur CurrencyInput
Implémentation avec `getDerivedStateFromProps` discutée sur https://github.com/betagouv/syso/pull/551#discussion_r287703126pull/551/head
parent
9900e7a3c7
commit
e5f63e1d37
|
@ -24,8 +24,8 @@ let currencyFormat = language => ({
|
|||
|
||||
class CurrencyInput extends Component {
|
||||
state = {
|
||||
value: this.props.value || '',
|
||||
valueHasChanged: false,
|
||||
value: this.props.value,
|
||||
initialValue: this.props.value
|
||||
}
|
||||
|
||||
onChange = this.props.debounce
|
||||
|
@ -50,6 +50,14 @@ class CurrencyInput extends Component {
|
|||
this.onChange(event)
|
||||
}
|
||||
|
||||
// See https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#alternative-1-reset-uncontrolled-component-with-an-id-prop
|
||||
static getDerivedStateFromProps({ value }, { initialValue }) {
|
||||
if (value !== initialValue) {
|
||||
return { value, initialValue: value }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
render() {
|
||||
let forwardedProps = omit(
|
||||
['onChange', 'defaultValue', 'language', 'className', 'value'],
|
||||
|
@ -62,6 +70,9 @@ class CurrencyInput extends Component {
|
|||
decimalSeparator
|
||||
} = currencyFormat(this.props.language)
|
||||
|
||||
// We display negative numbers iff this was the provided value (but we allow the user to enter them)
|
||||
const valueHasChanged = this.state.value !== this.state.initialValue
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
|
@ -73,16 +84,17 @@ class CurrencyInput extends Component {
|
|||
{...forwardedProps}
|
||||
thousandSeparator={thousandSeparator}
|
||||
decimalSeparator={decimalSeparator}
|
||||
allowNegative={!this.state.valueHasChanged}
|
||||
allowNegative={!valueHasChanged}
|
||||
className="currencyInput__input"
|
||||
inputMode="numeric"
|
||||
onValueChange={({ value }) => {
|
||||
this.setState({valueHasChanged: true, value})
|
||||
this.value = value.toString().replace(/^\-/,'')
|
||||
this.setState({ value })
|
||||
this.value = value.toString().replace(/^\-/, '')
|
||||
this.handleNextChange = true
|
||||
}}
|
||||
onChange={this.handleChange}
|
||||
value={this.state.value.toString()
|
||||
value={(this.state.value || '')
|
||||
.toString()
|
||||
.replace('.', decimalSeparator)}
|
||||
/>
|
||||
{!isCurrencyPrefixed && <> €</>}
|
||||
|
|
|
@ -64,7 +64,7 @@ describe('CurrencyInput', () => {
|
|||
|
||||
it('should not call onChange while the decimal part is being written', () => {
|
||||
let onChange = spy()
|
||||
const input = getInput(<CurrencyInput onChange={onChange} />)
|
||||
const input = getInput(<CurrencyInput value="111" onChange={onChange} />)
|
||||
input.simulate('change', { target: { value: '111,', focus: () => {} } })
|
||||
expect(onChange).not.to.have.been.called
|
||||
})
|
||||
|
@ -105,25 +105,23 @@ describe('CurrencyInput', () => {
|
|||
clock.restore()
|
||||
})
|
||||
|
||||
it('should initialize with value of the storeValue prop', () => {
|
||||
const input = getInput(<CurrencyInput storeValue={1} />)
|
||||
expect(input.prop('value')).to.eq(1)
|
||||
it('should initialize with value of the value prop', () => {
|
||||
const wrapper = shallow(<CurrencyInput value={1} />)
|
||||
expect(wrapper.state('value')).to.eq(1)
|
||||
})
|
||||
|
||||
it('should update its value if the storeValue prop changes', () => {
|
||||
const wrapper = shallow(<CurrencyInput storeValue={1} />)
|
||||
wrapper.setProps({ storeValue: 2 })
|
||||
it('should update its value if the value prop changes', () => {
|
||||
const wrapper = shallow(<CurrencyInput value={1} />)
|
||||
wrapper.setProps({ value: 2 })
|
||||
expect(wrapper.state('value')).to.equal(2)
|
||||
})
|
||||
it('should not update state if the storeValue is the same as the current input value', () => {
|
||||
const wrapper = shallow(
|
||||
<CurrencyInput storeValue={1000} onChange={() => {}} />
|
||||
)
|
||||
|
||||
it('should not call onChange the value is the same as the current input value', () => {
|
||||
let onChange = spy()
|
||||
const wrapper = mount(<CurrencyInput value={1000} onChange={() => {}} />)
|
||||
const input = wrapper.find('input')
|
||||
input.simulate('change', { target: { value: '2000' } })
|
||||
const state1 = wrapper.state()
|
||||
wrapper.setProps({ storeValue: '2000' })
|
||||
const state2 = wrapper.state()
|
||||
expect(state1).to.equal(state2)
|
||||
input.simulate('change', { target: { value: '2000', focus: () => {} } })
|
||||
wrapper.setProps({ value: '2000' })
|
||||
expect(onChange).not.to.have.been.called
|
||||
})
|
||||
})
|
||||
|
|
|
@ -7,7 +7,7 @@ import withLanguage from 'Components/utils/withLanguage'
|
|||
import withSitePaths from 'Components/utils/withSitePaths'
|
||||
import { encodeRuleName } from 'Engine/rules'
|
||||
import { compose, isEmpty, isNil, propEq } from 'ramda'
|
||||
import React, { Component, PureComponent, useRef } from 'react'
|
||||
import React, { Component, PureComponent } from 'react'
|
||||
import { withTranslation } from 'react-i18next'
|
||||
import { connect } from 'react-redux'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
|
Loading…
Reference in New Issue