Skip to content

Commit

Permalink
Merge pull request #28137 from esh-g/decimals-money-request
Browse files Browse the repository at this point in the history
Limit decimals in select currencies in Money request form
  • Loading branch information
aldo-expensify authored Oct 2, 2023
2 parents bba600a + 7e180d7 commit 850e21e
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 22 deletions.
17 changes: 14 additions & 3 deletions src/libs/MoneyRequestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ function stripSpacesFromAmount(amount: string): string {
return amount.replace(/\s+/g, '');
}

/**
* Strip decimals from the amount
*/
function stripDecimalsFromAmount(amount: string): string {
return amount.replace(/\.\d*$/, '');
}

/**
* Adds a leading zero to the amount if user entered just the decimal separator
*
Expand Down Expand Up @@ -42,8 +49,12 @@ function calculateAmountLength(amount: string): number {
/**
* Check if amount is a decimal up to 3 digits
*/
function validateAmount(amount: string): boolean {
const decimalNumberRegex = new RegExp(/^\d+(,\d+)*(\.\d{0,2})?$/, 'i');
function validateAmount(amount: string, decimals: number): boolean {
const regexString =
decimals === 0
? `^\\d+(,\\d+)*$` // Don't allow decimal point if decimals === 0
: `^\\d+(,\\d+)*(\\.\\d{0,${decimals}})?$`; // Allow the decimal point and the desired number of digits after the point
const decimalNumberRegex = new RegExp(regexString, 'i');
return amount === '' || (decimalNumberRegex.test(amount) && calculateAmountLength(amount) <= CONST.IOU.AMOUNT_MAX_LENGTH);
}

Expand Down Expand Up @@ -78,4 +89,4 @@ function isScanRequest(selectedTab: ValueOf<typeof CONST.TAB>): boolean {
return selectedTab === CONST.TAB.SCAN;
}

export {stripCommaFromAmount, stripSpacesFromAmount, addLeadingZero, validateAmount, replaceAllDigits, isDistanceRequest, isScanRequest};
export {stripCommaFromAmount, stripDecimalsFromAmount, stripSpacesFromAmount, addLeadingZero, validateAmount, replaceAllDigits, isDistanceRequest, isScanRequest};
56 changes: 37 additions & 19 deletions src/pages/iou/steps/MoneyRequestAmountForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu

const textInput = useRef(null);

const decimals = CurrencyUtils.getCurrencyDecimals(currency);
const selectedAmountAsString = amount ? CurrencyUtils.convertToFrontendAmount(amount).toString() : '';

const [currentAmount, setCurrentAmount] = useState(selectedAmountAsString);
Expand Down Expand Up @@ -123,26 +124,43 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu
* Sets the selection and the amount accordingly to the value passed to the input
* @param {String} newAmount - Changed amount from user input
*/
const setNewAmount = (newAmount) => {
// Remove spaces from the newAmount value because Safari on iOS adds spaces when pasting a copied value
// More info: https://github.com/Expensify/App/issues/16974
const newAmountWithoutSpaces = MoneyRequestUtils.stripSpacesFromAmount(newAmount);
// Use a shallow copy of selection to trigger setSelection
// More info: https://github.com/Expensify/App/issues/16385
if (!MoneyRequestUtils.validateAmount(newAmountWithoutSpaces)) {
setSelection((prevSelection) => ({...prevSelection}));
const setNewAmount = useCallback(
(newAmount) => {
// Remove spaces from the newAmount value because Safari on iOS adds spaces when pasting a copied value
// More info: https://github.com/Expensify/App/issues/16974
const newAmountWithoutSpaces = MoneyRequestUtils.stripSpacesFromAmount(newAmount);
// Use a shallow copy of selection to trigger setSelection
// More info: https://github.com/Expensify/App/issues/16385
if (!MoneyRequestUtils.validateAmount(newAmountWithoutSpaces, decimals)) {
setSelection((prevSelection) => ({...prevSelection}));
return;
}
const checkInvalidAmount = isAmountValid(newAmountWithoutSpaces);
setIsInvalidAmount(checkInvalidAmount);
setFormError(checkInvalidAmount ? 'iou.error.invalidAmount' : '');
setCurrentAmount((prevAmount) => {
const strippedAmount = MoneyRequestUtils.stripCommaFromAmount(newAmountWithoutSpaces);
const isForwardDelete = prevAmount.length > strippedAmount.length && forwardDeletePressedRef.current;
setSelection((prevSelection) => getNewSelection(prevSelection, isForwardDelete ? strippedAmount.length : prevAmount.length, strippedAmount.length));
return strippedAmount;
});
},
[decimals],
);

// Modifies the amount to match the decimals for changed currency.
useEffect(() => {
// If the changed currency supports decimals, we can return
if (MoneyRequestUtils.validateAmount(currentAmount, decimals)) {
return;
}
const checkInvalidAmount = isAmountValid(newAmountWithoutSpaces);
setIsInvalidAmount(checkInvalidAmount);
setFormError(checkInvalidAmount ? 'iou.error.invalidAmount' : '');
setCurrentAmount((prevAmount) => {
const strippedAmount = MoneyRequestUtils.stripCommaFromAmount(newAmountWithoutSpaces);
const isForwardDelete = prevAmount.length > strippedAmount.length && forwardDeletePressedRef.current;
setSelection((prevSelection) => getNewSelection(prevSelection, isForwardDelete ? strippedAmount.length : prevAmount.length, strippedAmount.length));
return strippedAmount;
});
};

// If the changed currency doesn't support decimals, we can strip the decimals
setNewAmount(MoneyRequestUtils.stripDecimalsFromAmount(currentAmount));

// we want to update only when decimals change (setNewAmount also changes when decimals change).
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setNewAmount]);

/**
* Update amount with number or Backspace pressed for BigNumberPad.
Expand All @@ -167,7 +185,7 @@ function MoneyRequestAmountForm({amount, currency, isEditing, forwardedRef, onCu
const newAmount = MoneyRequestUtils.addLeadingZero(`${currentAmount.substring(0, selection.start)}${key}${currentAmount.substring(selection.end)}`);
setNewAmount(newAmount);
},
[currentAmount, selection, shouldUpdateSelection],
[currentAmount, selection, shouldUpdateSelection, setNewAmount],
);

/**
Expand Down

0 comments on commit 850e21e

Please sign in to comment.