Fix input clearing issue in PriceTextField with improved currency parsing (#47354)

* Fix input clearing issue in PriceTextField with improved currency parsing

This commit addresses a usability issue in the PriceTextField component, highlighted by @dinhtungdu, where the input field would clear itself if users omitted a space between the numeric value and the currency symbol when the currency is configured to appear on the right with a space (e.g., "20$" instead of "20 $"). The problem was rooted in the strict parsing logic that did not account for variations in user input related to currency formatting.

The fix involves an enhancement to the numeric parsing function, which now effectively handles various user inputs regardless of space or placement of the currency symbol. The revised parsing logic uses a regular expression to strip out any characters that are not numeric or the designated decimal separator, and then replaces the locale-specific decimal separator with a period for standard numeric conversion. This approach not only fixes the specific issue but also improves the robustness of the component against similar input variations.

* Refactor currency parsing and improve handling in PriceTextField

1. Renaming `formatValueAsCurrency` to `formatNumberAsCurrency` to better
   reflect its functionality.
2. Moving the `convertCurrencyStringToNumber` function outside of the
   PriceTextField component, enhancing readability and reusability.
3. Changing the internal state handling from `newValue` to `inputValue`,
   directly managing the string input and parsing it as needed for further
   processing. This will force input field to re-render.

* Add changefile(s) from automation for the following project(s): woocommerce-blocks

* Fix: Handle empty string in price conversion to prevent 0 value

This commit fixes an issue where an empty string in the price conversion function resulted in a value of 0. Now, empty strings are properly handled and do not convert to a numerical value.

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Manish Menaria 2024-05-16 13:23:47 +05:30 committed by GitHub
parent b669fc8526
commit a7a1264fec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 51 additions and 57 deletions

View File

@ -45,7 +45,7 @@ const formatNumber = ( val: number, currency: Currency ): string => {
return `${ whole }${ decimalSeparator }${ decimal }`;
};
const formatValueAsCurrency = (
const formatNumberAsCurrency = (
val: number | undefined,
currency: Currency
) => {
@ -70,71 +70,61 @@ const formatValueAsCurrency = (
return formattedNumber;
};
const convertCurrencyStringToNumber = (
currencyString = '',
currency: Currency
): number | undefined => {
/**
* 1. Remove all characters that are not numbers or the decimal separator.
* 2. Replace the decimal separator with a period.
*/
const cleanedCurrencyString = currencyString
.replace(
new RegExp( `[^0-9\\${ currency.decimalSeparator || '' }]`, 'g' ),
''
)
.replace( new RegExp( `\\${ currency.decimalSeparator }`, 'g' ), '.' );
const parsedNumericValue = Number( cleanedCurrencyString );
if ( cleanedCurrencyString === '' || isNaN( parsedNumericValue ) ) {
return undefined;
}
/**
* If the value is negative, return 0.
*/
if ( parsedNumericValue < 0 ) {
return 0;
}
return parsedNumericValue;
};
const PriceTextField: React.FC< PriceTextFieldProps > = ( {
value,
onChange,
label,
} ) => {
const [ newValue, setNewValue ] = useState< number | undefined >( value );
const [ inputValue, setInputValue ] = useState< string >(
`${ value || '' }`
);
const currency = getCurrency();
const convertCurrencyStringToNumber = (
val: string
): number | undefined => {
/**
* First, remove the currency symbol from the value.
* For example, if the currency is USD, the value is $1,000.00
* It should be converted to 1,000.00 before converting to a number.
*/
const valueWithoutCurrencySymbol = val
.replace( currency.prefix, '' )
.replace( currency.suffix, '' );
/**
* Then, normalize the value to a number.
* - Replace the decimal separator with a dot
* - Remove the thousand separator
*
* For example, if the value is 1,000:00
* - Replace the decimal separator with a dot: 1,000.00
* - Remove the thousand separator: 1000.00
*/
let normalizedValue = valueWithoutCurrencySymbol;
if ( currency.decimalSeparator ) {
normalizedValue = normalizedValue.replace(
new RegExp( `\\${ currency.decimalSeparator }` ),
'.'
);
}
if ( currency.thousandSeparator ) {
normalizedValue = normalizedValue.replace(
new RegExp( `\\${ currency.thousandSeparator }`, 'g' ),
''
);
}
const parsedNumericValue = Number( normalizedValue );
if ( isNaN( parsedNumericValue ) ) {
return undefined;
}
/**
* If the value is negative, return 0.
*/
if ( parsedNumericValue < 0 ) {
return 0;
}
return parsedNumericValue;
};
const parsedNumericValue = convertCurrencyStringToNumber(
inputValue,
currency
);
const formattedValue = formatNumberAsCurrency(
parsedNumericValue,
currency
);
const handleOnChange = ( val: string ) => {
const numberValue = convertCurrencyStringToNumber( val );
setNewValue( numberValue );
setInputValue( val );
};
const handleOnBlur = () => {
onChange( newValue );
onChange( parsedNumericValue );
};
/**
@ -144,13 +134,13 @@ const PriceTextField: React.FC< PriceTextFieldProps > = ( {
event: React.KeyboardEvent< HTMLInputElement >
) => {
if ( event.key === 'Enter' ) {
onChange( newValue );
onChange( parsedNumericValue );
}
};
return (
<InputControl
value={ formatValueAsCurrency( newValue, currency ) }
value={ formattedValue }
onChange={ handleOnChange }
onBlur={ handleOnBlur }
onKeyDown={ handleEnterKeyPress }

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Product Collection - Fix edge cases in Price Range filter around value parsing