Skip to content

Commit

Permalink
- Fix for 691.
Browse files Browse the repository at this point in the history
  • Loading branch information
s-yadav committed Mar 5, 2023
1 parent ce43570 commit b70dee2
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 30 deletions.
1 change: 0 additions & 1 deletion example/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ class App extends React.Component {
return (
<div>
<div className="example">
<NumericFormat prefix='100-' suffix={"000 USD"} />
<h3>Prefix and thousand separator : Format currency as text</h3>
<NumericFormat value={2456981} displayType="text" thousandSeparator={true} prefix="$" />
</div>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-number-format",
"description": "React component to format number in an input or as a text.",
"version": "5.1.3",
"version": "5.1.4",
"main": "dist/react-number-format.cjs.js",
"module": "dist/react-number-format.es.js",
"types": "types/index.d.ts",
Expand Down
64 changes: 52 additions & 12 deletions src/numeric_format.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function format<BaseType = InputAttributes>(
fixedDecimalScale,
prefix = '',
suffix = '',
allowNegative = true,
allowNegative,
thousandsGroupStyle = 'thousand',
} = props;

Expand Down Expand Up @@ -123,13 +123,25 @@ export function removeFormatting<BaseType = InputAttributes>(
changeMeta: ChangeMeta = getDefaultChangeMeta(value),
props: NumericFormatProps<BaseType>,
) {
const { allowNegative = true, prefix = '', suffix = '', decimalScale } = props;
const { allowNegative, prefix = '', suffix = '', decimalScale } = props;
const { from, to } = changeMeta;
let { start, end } = to;
const { allowedDecimalSeparators, decimalSeparator } = getSeparators(props);

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

/**
* If only a number is added on empty input which matches with the prefix or suffix,
* then don't remove it, just return the same
*/
if (
charIsNumber(value) &&
(value === prefix || value === suffix) &&
changeMeta.lastValue === ''
) {
return value;
}

/** Check for any allowed decimal separator is added in the numeric format and replace it with decimal separator */
if (end - start === 1 && allowedDecimalSeparators.indexOf(value[start]) !== -1) {
const separator = decimalScale === 0 ? '' : decimalSeparator;
Expand All @@ -138,18 +150,23 @@ export function removeFormatting<BaseType = InputAttributes>(

const stripNegation = (value: string, start: number, end: number) => {
/**
* if prefix starts with - the number hast to have two - at the start
* if prefix starts with - we don't allow negative number to avoid confusion
* if suffix starts with - and the value length is same as suffix length, then the - sign is from the suffix
* In other cases, if the value starts with - then it is a negation
*/
let hasNegation = false;
let hasDoubleNegation = false;
if (prefix.startsWith('-')) hasNegation = !value.startsWith('--');
else if (value.startsWith('--')) {

if (prefix.startsWith('-')) {
hasNegation = false;
} else if (value.startsWith('--')) {
hasNegation = false;
hasDoubleNegation = true;
} else if (suffix.startsWith('-') && value.length === suffix.length) hasNegation = false;
else if (value[0] === '-') hasNegation = true;
} else if (suffix.startsWith('-') && value.length === suffix.length) {
hasNegation = false;
} else if (value[0] === '-') {
hasNegation = true;
}

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

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

if (thousandSeparator === decimalSeparator) {
throw new Error(`
Expand All @@ -277,11 +296,30 @@ function validateProps<BaseType = InputAttributes>(props: NumericFormatProps<Bas
decimalSeparator: ${decimalSeparator} (default value for decimalSeparator is .)
`);
}

if (prefix.startsWith('-') && allowNegative) {
// TODO: throw error in next major version
console.error(`
Prefix can't start with '-' when allowNegative is true.
prefix: ${prefix}
allowNegative: ${allowNegative}
`);

allowNegative = false;
}

return {
...props,
allowNegative,
};
}

export function useNumericFormat<BaseType = InputAttributes>(
props: NumericFormatProps<BaseType>,
): NumberFormatBaseProps<BaseType> {
// validate props
props = validateAndUpdateProps(props);

const {
decimalSeparator = '.',
/* eslint-disable no-unused-vars */
Expand All @@ -304,9 +342,6 @@ export function useNumericFormat<BaseType = InputAttributes>(
...restProps
} = props;

// validate props
validateProps(props);

const _format: FormatInputValueFunction = (numStr) => format(numStr, props);

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

// if user hits backspace, while the cursor is before prefix, and the input has negation, remove the negation
if (key === 'Backspace' && value[0] === '-' && selectionStart === prefix.length + 1) {
if (
key === 'Backspace' &&
value[0] === '-' &&
selectionStart === prefix.length + 1 &&
allowNegative
) {
// bring the cursor to after negation
setCaretPosition(el, 1);
}
Expand Down
60 changes: 44 additions & 16 deletions test/library/input_numeric_format.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
mount,
shallow,
getInputValue,
render,
simulateNativeKeyInput,
} from '../test_util';

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

describe('should allow typing number if prefix or suffix is just an number #691', () => {
it('when prefix is number', async () => {
const { input } = await render(<NumericFormat prefix="1" />);
simulateNativeKeyInput(input, '1', 0, 0);
expect(input.value).toEqual('11');
});

it('when prefix is multiple digit', async () => {
const { input } = await render(<NumericFormat prefix="11" />);
simulateNativeKeyInput(input, '11', 0, 0);
expect(input.value).toEqual('1111');
});

it('when suffix is number', async () => {
const { input } = await render(<NumericFormat suffix="1" />);
simulateNativeKeyInput(input, '1', 0, 0);
expect(input.value).toEqual('11');
});

it('when prefix and suffix both are number', async () => {
const { input } = await render(<NumericFormat prefix="1" suffix="1" />);
simulateNativeKeyInput(input, '1', 0, 0);
expect(input.value).toEqual('111');
});
});

describe('should handle if only partial prefix or suffix is removed using double click select and the remove. #694', () => {
it('while changing suffix', () => {
const wrapper = mount(<NumericFormat prefix="100-" value={1} suffix="000 USD" />);
it('while changing suffix', async () => {
const { input } = await render(<NumericFormat prefix="100-" value={1} suffix="000 USD" />);

simulateKeyInput(wrapper.find('input'), '2', 9, 12);
expect(wrapper.find('input').prop('value')).toEqual('100-1000 USD');
simulateNativeKeyInput(input, '2', 9, 12);
expect(input.value).toEqual('100-1000 USD');
});

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

simulateKeyInput(wrapper.find('input'), '2', 0, 2);
expect(wrapper.find('input').prop('value')).toEqual('100-1000 USD');
simulateNativeKeyInput(input, '2', 0, 2);
expect(input.value).toEqual('100-1000 USD');
});

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

simulateKeyInput(wrapper.find('input'), 'Backspace', 9, 12);
expect(wrapper.find('input').prop('value')).toEqual('100-1000 USD');
simulateNativeKeyInput(input, 'Backspace', 9, 12);
expect(input.value).toEqual('100-1000 USD');
});

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

simulateKeyInput(wrapper.find('input'), 'Backspace', 0, 3);
expect(wrapper.find('input').prop('value')).toEqual('100-1000 USD');
simulateNativeKeyInput(input, 'Backspace', 0, 3);
expect(input.value).toEqual('100-1000 USD');
});
});

Expand Down

0 comments on commit b70dee2

Please sign in to comment.