Skip to content

Commit

Permalink
feat: Add source details in onValueChange (#591)
Browse files Browse the repository at this point in the history
  • Loading branch information
nikhil-varma authored Nov 8, 2021
1 parent bbac53d commit 1a70575
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 7 deletions.
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -190,6 +190,21 @@ Output: ¥1,2345,6789
/>
```

#### Accessing event and the source for onValueChangeTrigger

```jsx
<NumberFormat
value={this.state.profit}
thousandSeparator={true}
prefix={'$'}
onValueChange={(values, sourceInfo) => {
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
Expand Down
27 changes: 23 additions & 4 deletions src/number_format.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
}
}
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 });
}
}

Expand All @@ -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);
Expand Down Expand Up @@ -834,6 +849,8 @@ class NumberFormat extends React.Component {
numAsString,
input: e.target,
setCaretPosition: false,
event: e,
source: 'event',
});
onBlur(e);
return;
Expand Down Expand Up @@ -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) {
Expand Down
19 changes: 19 additions & 0 deletions test/library/input.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,25 @@ describe('NumberFormat as input', () => {
});
});

it('should call onValueChange with the right source information', () => {
const spy = jasmine.createSpy();
const wrapper = shallow(<NumberFormat value="1234" onValueChange={spy} />);

// 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(<NumberFormat value={Infinity} />);
expect(wrapper.state().value).toEqual('');
Expand Down
7 changes: 6 additions & 1 deletion typings/number_format.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLInputElement> {
Expand All @@ -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<HTMLInputElement>
* onKeyDown: Function;
Expand Down

0 comments on commit 1a70575

Please sign in to comment.