Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add source details in onValueChange #591

Merged
merged 4 commits into from
Nov 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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