Skip to content

Commit b70dee2

Browse files
committed
- Fix for 691.
1 parent ce43570 commit b70dee2

File tree

4 files changed

+97
-30
lines changed

4 files changed

+97
-30
lines changed

example/src/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ class App extends React.Component {
2121
return (
2222
<div>
2323
<div className="example">
24-
<NumericFormat prefix='100-' suffix={"000 USD"} />
2524
<h3>Prefix and thousand separator : Format currency as text</h3>
2625
<NumericFormat value={2456981} displayType="text" thousandSeparator={true} prefix="$" />
2726
</div>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "react-number-format",
33
"description": "React component to format number in an input or as a text.",
4-
"version": "5.1.3",
4+
"version": "5.1.4",
55
"main": "dist/react-number-format.cjs.js",
66
"module": "dist/react-number-format.es.js",
77
"types": "types/index.d.ts",

src/numeric_format.tsx

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export function format<BaseType = InputAttributes>(
3535
fixedDecimalScale,
3636
prefix = '',
3737
suffix = '',
38-
allowNegative = true,
38+
allowNegative,
3939
thousandsGroupStyle = 'thousand',
4040
} = props;
4141

@@ -123,13 +123,25 @@ export function removeFormatting<BaseType = InputAttributes>(
123123
changeMeta: ChangeMeta = getDefaultChangeMeta(value),
124124
props: NumericFormatProps<BaseType>,
125125
) {
126-
const { allowNegative = true, prefix = '', suffix = '', decimalScale } = props;
126+
const { allowNegative, prefix = '', suffix = '', decimalScale } = props;
127127
const { from, to } = changeMeta;
128128
let { start, end } = to;
129129
const { allowedDecimalSeparators, decimalSeparator } = getSeparators(props);
130130

131131
const isBeforeDecimalSeparator = value[end] === decimalSeparator;
132132

133+
/**
134+
* If only a number is added on empty input which matches with the prefix or suffix,
135+
* then don't remove it, just return the same
136+
*/
137+
if (
138+
charIsNumber(value) &&
139+
(value === prefix || value === suffix) &&
140+
changeMeta.lastValue === ''
141+
) {
142+
return value;
143+
}
144+
133145
/** Check for any allowed decimal separator is added in the numeric format and replace it with decimal separator */
134146
if (end - start === 1 && allowedDecimalSeparators.indexOf(value[start]) !== -1) {
135147
const separator = decimalScale === 0 ? '' : decimalSeparator;
@@ -138,18 +150,23 @@ export function removeFormatting<BaseType = InputAttributes>(
138150

139151
const stripNegation = (value: string, start: number, end: number) => {
140152
/**
141-
* if prefix starts with - the number hast to have two - at the start
153+
* if prefix starts with - we don't allow negative number to avoid confusion
142154
* if suffix starts with - and the value length is same as suffix length, then the - sign is from the suffix
143155
* In other cases, if the value starts with - then it is a negation
144156
*/
145157
let hasNegation = false;
146158
let hasDoubleNegation = false;
147-
if (prefix.startsWith('-')) hasNegation = !value.startsWith('--');
148-
else if (value.startsWith('--')) {
159+
160+
if (prefix.startsWith('-')) {
161+
hasNegation = false;
162+
} else if (value.startsWith('--')) {
149163
hasNegation = false;
150164
hasDoubleNegation = true;
151-
} else if (suffix.startsWith('-') && value.length === suffix.length) hasNegation = false;
152-
else if (value[0] === '-') hasNegation = true;
165+
} else if (suffix.startsWith('-') && value.length === suffix.length) {
166+
hasNegation = false;
167+
} else if (value[0] === '-') {
168+
hasNegation = true;
169+
}
153170

154171
let charsToRemove = hasNegation ? 1 : 0;
155172
if (hasDoubleNegation) charsToRemove = 2;
@@ -267,8 +284,10 @@ export function getCaretBoundary<BaseType = InputAttributes>(
267284
return boundaryAry;
268285
}
269286

270-
function validateProps<BaseType = InputAttributes>(props: NumericFormatProps<BaseType>) {
287+
function validateAndUpdateProps<BaseType = InputAttributes>(props: NumericFormatProps<BaseType>) {
271288
const { thousandSeparator, decimalSeparator } = getSeparators(props);
289+
// eslint-disable-next-line prefer-const
290+
let { prefix = '', allowNegative = true } = props;
272291

273292
if (thousandSeparator === decimalSeparator) {
274293
throw new Error(`
@@ -277,11 +296,30 @@ function validateProps<BaseType = InputAttributes>(props: NumericFormatProps<Bas
277296
decimalSeparator: ${decimalSeparator} (default value for decimalSeparator is .)
278297
`);
279298
}
299+
300+
if (prefix.startsWith('-') && allowNegative) {
301+
// TODO: throw error in next major version
302+
console.error(`
303+
Prefix can't start with '-' when allowNegative is true.
304+
prefix: ${prefix}
305+
allowNegative: ${allowNegative}
306+
`);
307+
308+
allowNegative = false;
309+
}
310+
311+
return {
312+
...props,
313+
allowNegative,
314+
};
280315
}
281316

282317
export function useNumericFormat<BaseType = InputAttributes>(
283318
props: NumericFormatProps<BaseType>,
284319
): NumberFormatBaseProps<BaseType> {
320+
// validate props
321+
props = validateAndUpdateProps(props);
322+
285323
const {
286324
decimalSeparator = '.',
287325
/* eslint-disable no-unused-vars */
@@ -304,9 +342,6 @@ export function useNumericFormat<BaseType = InputAttributes>(
304342
...restProps
305343
} = props;
306344

307-
// validate props
308-
validateProps(props);
309-
310345
const _format: FormatInputValueFunction = (numStr) => format(numStr, props);
311346

312347
const _removeFormatting: RemoveFormattingFunction = (inputValue, changeMeta) =>
@@ -359,7 +394,12 @@ export function useNumericFormat<BaseType = InputAttributes>(
359394
}
360395

361396
// if user hits backspace, while the cursor is before prefix, and the input has negation, remove the negation
362-
if (key === 'Backspace' && value[0] === '-' && selectionStart === prefix.length + 1) {
397+
if (
398+
key === 'Backspace' &&
399+
value[0] === '-' &&
400+
selectionStart === prefix.length + 1 &&
401+
allowNegative
402+
) {
363403
// bring the cursor to after negation
364404
setCaretPosition(el, 1);
365405
}

test/library/input_numeric_format.spec.js

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
mount,
1111
shallow,
1212
getInputValue,
13+
render,
14+
simulateNativeKeyInput,
1315
} from '../test_util';
1416

1517
/**
@@ -685,33 +687,59 @@ describe('Test NumberFormat as input with numeric format options', () => {
685687
expect(wrapper.find('input').prop('value')).toEqual('0.00000001');
686688
});
687689

690+
describe('should allow typing number if prefix or suffix is just an number #691', () => {
691+
it('when prefix is number', async () => {
692+
const { input } = await render(<NumericFormat prefix="1" />);
693+
simulateNativeKeyInput(input, '1', 0, 0);
694+
expect(input.value).toEqual('11');
695+
});
696+
697+
it('when prefix is multiple digit', async () => {
698+
const { input } = await render(<NumericFormat prefix="11" />);
699+
simulateNativeKeyInput(input, '11', 0, 0);
700+
expect(input.value).toEqual('1111');
701+
});
702+
703+
it('when suffix is number', async () => {
704+
const { input } = await render(<NumericFormat suffix="1" />);
705+
simulateNativeKeyInput(input, '1', 0, 0);
706+
expect(input.value).toEqual('11');
707+
});
708+
709+
it('when prefix and suffix both are number', async () => {
710+
const { input } = await render(<NumericFormat prefix="1" suffix="1" />);
711+
simulateNativeKeyInput(input, '1', 0, 0);
712+
expect(input.value).toEqual('111');
713+
});
714+
});
715+
688716
describe('should handle if only partial prefix or suffix is removed using double click select and the remove. #694', () => {
689-
it('while changing suffix', () => {
690-
const wrapper = mount(<NumericFormat prefix="100-" value={1} suffix="000 USD" />);
717+
it('while changing suffix', async () => {
718+
const { input } = await render(<NumericFormat prefix="100-" value={1} suffix="000 USD" />);
691719

692-
simulateKeyInput(wrapper.find('input'), '2', 9, 12);
693-
expect(wrapper.find('input').prop('value')).toEqual('100-1000 USD');
720+
simulateNativeKeyInput(input, '2', 9, 12);
721+
expect(input.value).toEqual('100-1000 USD');
694722
});
695723

696-
it('while changing prefix', () => {
697-
const wrapper = mount(<NumericFormat prefix="100-" value={1} suffix="000 USD" />);
724+
it('while changing prefix', async () => {
725+
const { input } = await render(<NumericFormat prefix="100-" value={1} suffix="000 USD" />);
698726

699-
simulateKeyInput(wrapper.find('input'), '2', 0, 2);
700-
expect(wrapper.find('input').prop('value')).toEqual('100-1000 USD');
727+
simulateNativeKeyInput(input, '2', 0, 2);
728+
expect(input.value).toEqual('100-1000 USD');
701729
});
702730

703-
it('while deleting suffix', () => {
704-
const wrapper = mount(<NumericFormat prefix="100-" value={1} suffix="000 USD" />);
731+
it('while deleting suffix', async () => {
732+
const { input } = await render(<NumericFormat prefix="100-" value={1} suffix="000 USD" />);
705733

706-
simulateKeyInput(wrapper.find('input'), 'Backspace', 9, 12);
707-
expect(wrapper.find('input').prop('value')).toEqual('100-1000 USD');
734+
simulateNativeKeyInput(input, 'Backspace', 9, 12);
735+
expect(input.value).toEqual('100-1000 USD');
708736
});
709737

710-
fit('while deleting prefix', () => {
711-
const wrapper = mount(<NumericFormat prefix="100-" value={1} suffix="000 USD" />);
738+
it('while deleting prefix', async () => {
739+
const { input } = await render(<NumericFormat prefix="100-" value={1} suffix="000 USD" />);
712740

713-
simulateKeyInput(wrapper.find('input'), 'Backspace', 0, 3);
714-
expect(wrapper.find('input').prop('value')).toEqual('100-1000 USD');
741+
simulateNativeKeyInput(input, 'Backspace', 0, 3);
742+
expect(input.value).toEqual('100-1000 USD');
715743
});
716744
});
717745

0 commit comments

Comments
 (0)