diff --git a/README.md b/README.md index 3dc5d5df..69b39ed6 100755 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ In typescript you also have to enable `"esModuleInterop": true` in your tsconfig | removeFormatting | (formattedValue) => numericString | none | If you are providing custom format method and it add numbers as format you will need to add custom removeFormatting logic. [Demo](https://jinno.io/app/23/removeFormatting?source=react-number-format&displayType=input) | | mask | String (ex : \_) | `' '` | If mask defined, component will show non entered placed with masked value. [Demo](https://jinno.io/app/23/mask?source=react-number-format&displayType=input&format=###-####&mask=_) | | customInput | Component Reference | input | This allow supporting custom inputs with number format. | -| onValueChange | (values) => {} | none | onValueChange handler accepts [values object](#values-object). [Demo](https://jinno.io/app/23/onValueChange?source=react-number-format&displayType=input) | +| onValueChange | (values, sourceInfo) => {} | none | onValueChange handler accepts [values object](#values-object). [Demo](https://jinno.io/app/23/onValueChange?source=react-number-format&displayType=input) | | isAllowed | ([values](#values-object)) => true or false | none | A checker function to check if input value is valid or not. If this function returns false, the onChange method will not get triggered. [Demo](https://jinno.io/app/23/isAllowed?source=react-number-format&displayType=input) | | renderText | (formattedValue, customProps) => React Element | null | A renderText method useful if you want to render formattedValue in different element other than span. It also returns the custom props that are added to the component which can allow passing down props to the rendered element. [Demo](https://jinno.io/app/23/renderText?source=react-number-format&displayType=input) | | getInputRef | (elm) => void | null | Method to get reference of input, span (based on displayType prop) or the customInput's reference. See [Getting reference](#getting-reference). [Demo](https://jinno.io/app/23/getInputRef?source=react-number-format&displayType=input) | @@ -109,7 +109,7 @@ Its recommended to use formattedValue / value / floatValue based on the initial 6. Its recommended to use formattedValue / value / floatValue based on the initial state (it should be same as the initial state format) which you are passing as value prop. If you are saving the `value` key on state make sure to pass isNumericString prop to true. -7. onValueChange is not same as onChange. It gets called on whenever there is change in value which can be caused by any event like change or blur event or by a prop change. It no longer receives event object as second parameter. +7. onValueChange is not same as onChange. It gets called on whenever there is change in value which can be caused by any event like change or blur event or by a prop change. It also provides a second argument which contains the event object and the reason for this function trigger. ### Examples @@ -190,6 +190,21 @@ Output: ¥1,2345,6789 /> ``` +#### Accessing event and the source for onValueChangeTrigger + +```jsx + { + const { formattedValue, value } = values; + // Event is a Synthetic Event wrapper which holds target and other information. Source tells whether the reason for this function being triggered was an 'event' or due to a 'prop' change + const { event, source } = sourceInfo; + }} +/> +``` + #### Format with pattern : Format credit card in an input. [Demo](https://jinno.io/app/23/?source=react-number-format&value=4111111111111111&displayType=input&format=%23%23%23%23%20%23%23%23%23%20%23%23%23%23%20%23%23%23%23) ```jsx diff --git a/src/number_format.js b/src/number_format.js index 03ab78b3..20af6e38 100644 --- a/src/number_format.js +++ b/src/number_format.js @@ -128,7 +128,13 @@ class NumberFormat extends React.Component { //set state always when not in focus and formatted value is changed (focusedElm === null && formattedValue !== stateValue) ) { - this.updateValue({ formattedValue, numAsString, input: focusedElm }); + this.updateValue({ + formattedValue, + numAsString, + input: focusedElm, + source: 'prop', + event: null, + }); } } } @@ -723,10 +729,12 @@ class NumberFormat extends React.Component { numAsString: string, inputValue: string, input: HTMLInputElement, + event: SyntheticInputEvent, + source: string, caretPos: number, setCaretPosition: Boolean, }) { - const { formattedValue, input, setCaretPosition = true } = params; + const { formattedValue, input, setCaretPosition = true, source, event } = params; let { numAsString, caretPos } = params; const { onValueChange } = this.props; const { value: lastValue } = this.state; @@ -772,7 +780,7 @@ class NumberFormat extends React.Component { this.setState({ value: formattedValue, numAsString }); // trigger onValueChange synchronously, so parent is updated along with the number format. Fix for #277, #287 - onValueChange(this.getValueObject(formattedValue, numAsString)); + onValueChange(this.getValueObject(formattedValue, numAsString), { event, source }); } } @@ -797,7 +805,14 @@ class NumberFormat extends React.Component { formattedValue = lastValue; } - this.updateValue({ formattedValue, numAsString, inputValue, input: el }); + this.updateValue({ + formattedValue, + numAsString, + inputValue, + input: el, + event: e, + source: 'event', + }); if (isChangeAllowed) { props.onChange(e); @@ -834,6 +849,8 @@ class NumberFormat extends React.Component { numAsString, input: e.target, setCaretPosition: false, + event: e, + source: 'event', }); onBlur(e); return; @@ -900,6 +917,8 @@ class NumberFormat extends React.Component { formattedValue: newValue, caretPos: newCaretPosition, input: el, + event: e, + source: 'event', }); } else if (!negativeRegex.test(value[expectedCaretPosition])) { while (!numRegex.test(value[newCaretPosition - 1]) && newCaretPosition > leftBound) { diff --git a/test/library/input.spec.js b/test/library/input.spec.js index d9699e82..186bda9f 100644 --- a/test/library/input.spec.js +++ b/test/library/input.spec.js @@ -412,6 +412,25 @@ describe('NumberFormat as input', () => { }); }); + it('should call onValueChange with the right source information', () => { + const spy = jasmine.createSpy(); + const wrapper = shallow(); + + // Test prop change onValueChange + wrapper.setProps({ thousandSeparator: true }); + expect(spy.calls.argsFor(0)[1]).toEqual({ + event: null, + source: 'prop', + }); + + // Test with input change by simulateKeyInput + simulateKeyInput(wrapper.find('input'), '5', 0); + const { event, source } = spy.calls.argsFor(1)[1]; + const { key } = event; + expect(key).toEqual('5'); + expect(source).toEqual('event'); + }); + it('should treat Infinity value as empty string', () => { const wrapper = shallow(); expect(wrapper.state().value).toEqual(''); diff --git a/typings/number_format.d.ts b/typings/number_format.d.ts index b6ca4ab1..82fa38a4 100755 --- a/typings/number_format.d.ts +++ b/typings/number_format.d.ts @@ -16,6 +16,11 @@ declare module 'react-number-format' { value: string; } + export interface SourceInfo { + event: SyntheticInputEvent; + source: 'prop' | 'event'; + } + export type FormatInputValueFunction = (inputValue: string) => string; export interface SyntheticInputEvent extends React.SyntheticEvent { @@ -42,7 +47,7 @@ declare module 'react-number-format' { allowNegative?: boolean; allowEmptyFormatting?: boolean; allowLeadingZeros?: boolean; - onValueChange?: (values: NumberFormatValues) => void; + onValueChange?: (values: NumberFormatValues, sourceInfo: SourceInfo) => void; /** * these are already included in React.HTMLAttributes * onKeyDown: Function;