mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-28 20:53:24 +10:00
Rename usePoint (real meaning useComma) and refactoring
This commit is contained in:
parent
9344bdc00d
commit
ae7b999774
7 changed files with 104 additions and 57 deletions
|
|
@ -3,11 +3,11 @@ import React, { PropTypes } from 'react';
|
|||
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||
import en from 'react-intl/locale-data/en';
|
||||
import fi from 'react-intl/locale-data/fi';
|
||||
import { currencyDefaultConfig } from '../../util/currency';
|
||||
import { currencyConfig } from '../../util/test-data';
|
||||
import CurrencyInput from './CurrencyInput';
|
||||
|
||||
const defaultConfig = {
|
||||
...currencyDefaultConfig,
|
||||
...currencyConfig,
|
||||
currency: 'USD',
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import { intlShape, injectIntl } from 'react-intl';
|
|||
import { types } from '../../util/sdkLoader';
|
||||
import {
|
||||
convertUnitToSubUnit,
|
||||
currencyDefaultConfig,
|
||||
ensureDotSeparator,
|
||||
ensureSeparator,
|
||||
truncateToSubUnitPrecision,
|
||||
|
|
@ -19,7 +18,7 @@ const allowedInputProps = allProps => {
|
|||
// Strip away props that are not passed to input element (or are overwritten)
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { currencyConfig, defaultValue, intl, input, meta, ...inputProps } = allProps;
|
||||
return inputProps || {};
|
||||
return inputProps;
|
||||
};
|
||||
|
||||
// Convert unformatted value (e.g. 10,00) to Money (or null)
|
||||
|
|
@ -38,19 +37,19 @@ class CurrencyInput extends Component {
|
|||
super(props);
|
||||
const { currencyConfig, defaultValue, intl } = props;
|
||||
|
||||
// We need to handle number format - some locales use dots and some points as decimal separator
|
||||
// We need to handle number format - some locales use dots and some commas as decimal separator
|
||||
// TODO Figure out if this could be digged from React-Intl directly somehow
|
||||
const testSubUnitFormat = intl.formatNumber('1.1', currencyConfig);
|
||||
const usesPoint = testSubUnitFormat.indexOf(',') >= 0;
|
||||
const usesComma = testSubUnitFormat.indexOf(',') >= 0;
|
||||
|
||||
try {
|
||||
// whatever is passed as a default value, will be converted to currency string
|
||||
// Unformatted value is digits + localized sub unit separator ("9,99")
|
||||
const unformattedValue = defaultValue
|
||||
? truncateToSubUnitPrecision(
|
||||
ensureSeparator(defaultValue.toString(), usesPoint),
|
||||
ensureSeparator(defaultValue.toString(), usesComma),
|
||||
currencyConfig.subUnitDivisor,
|
||||
usesPoint
|
||||
usesComma
|
||||
)
|
||||
: '';
|
||||
// Formatted value fully localized currency string ("$1,000.99")
|
||||
|
|
@ -62,7 +61,7 @@ class CurrencyInput extends Component {
|
|||
formattedValue,
|
||||
unformattedValue,
|
||||
value: formattedValue,
|
||||
usesPoint,
|
||||
usesComma,
|
||||
};
|
||||
} catch (e) {
|
||||
// Print error, if default value isn't supported (see specs: truncateToSubUnitPrecision).
|
||||
|
|
@ -134,7 +133,7 @@ class CurrencyInput extends Component {
|
|||
const truncatedValueString = truncateToSubUnitPrecision(
|
||||
valueOrZero,
|
||||
currencyConfig.subUnitDivisor,
|
||||
this.state.usesPoint
|
||||
this.state.usesComma
|
||||
);
|
||||
const unformattedValue = !isEmptyString ? truncatedValueString : '';
|
||||
const formattedValue = !isEmptyString
|
||||
|
|
@ -177,7 +176,7 @@ class CurrencyInput extends Component {
|
|||
}
|
||||
|
||||
CurrencyInput.defaultProps = {
|
||||
currencyConfig: currencyDefaultConfig,
|
||||
currencyConfig: null,
|
||||
defaultValue: 0,
|
||||
input: null,
|
||||
placeholder: null,
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@ class EditListingForm extends Component {
|
|||
const descriptionRequiredMessage = intl.formatMessage({
|
||||
id: 'EditListingForm.descriptionRequired',
|
||||
});
|
||||
const pricePlaceholderMessage = intl.formatMessage({ id: 'EditListingForm.pricePlaceholder' });
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
|
|
@ -185,7 +186,7 @@ class EditListingForm extends Component {
|
|||
component={EnhancedCurrencyInput}
|
||||
currencyConfig={config.currencyConfig}
|
||||
validate={[required(priceRequiredMessage)]}
|
||||
placeholder="10.0"
|
||||
placeholder={pricePlaceholderMessage}
|
||||
/>
|
||||
|
||||
<h3>Images</h3>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
"EditListingForm.maxLength": "Must be {maxLength} characters or less",
|
||||
"EditListingForm.locationRequired": "You need to provide a location",
|
||||
"EditListingForm.locationNotRecognized": "We didn't recognize this location. Please try another location.",
|
||||
"EditListingForm.pricePlaceholder": "$0.00",
|
||||
"EditListingPage.titleCreateListing": "Create a listing",
|
||||
"EditListingPage.titleEditListing": "Edit listing",
|
||||
"EditListingForm.descriptionRequired": "You need to add a description.",
|
||||
|
|
|
|||
|
|
@ -1,53 +1,64 @@
|
|||
import { trimEnd } from 'lodash';
|
||||
import Decimal from 'decimal.js';
|
||||
|
||||
// Default config for currency formatting (for React-Intl).
|
||||
export const currencyDefaultConfig = {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
currencyDisplay: 'symbol',
|
||||
useGrouping: true,
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
subUnitDivisor: 100,
|
||||
};
|
||||
|
||||
////////// Currency manipulation in string format //////////
|
||||
|
||||
// Ensures that the given string uses only dots or points
|
||||
// ensureSeparator('9999999,99', false) // => '9999999.99'
|
||||
export const ensureSeparator = (str, usePoint = false) => {
|
||||
/**
|
||||
* Ensures that the given string uses only dots or commas
|
||||
* e.g. ensureSeparator('9999999,99', false) // => '9999999.99'
|
||||
*
|
||||
* @param {String} str - string to be formatted
|
||||
*
|
||||
* @return {String} converted string
|
||||
*/
|
||||
export const ensureSeparator = (str, useComma = false) => {
|
||||
if (typeof str !== 'string') {
|
||||
throw new Error('Parameter must be a string');
|
||||
throw new TypeError('Parameter must be a string');
|
||||
}
|
||||
return usePoint ? str.split('.').join(',') : str.split(',').join('.');
|
||||
return useComma ? str.replace(/\./g, ',') : str.replace(/,/g, '.');
|
||||
};
|
||||
|
||||
// Ensures that the given string uses only dots (e.g. JavaScript floats use dots)
|
||||
/**
|
||||
* Ensures that the given string uses only dots
|
||||
* (e.g. JavaScript floats use dots)
|
||||
*
|
||||
* @param {String} str - string to be formatted
|
||||
*
|
||||
* @return {String} converted string
|
||||
*/
|
||||
export const ensureDotSeparator = str => {
|
||||
return ensureSeparator(str, false);
|
||||
};
|
||||
|
||||
// Convert string to Decimal object (from Decimal.js math library)
|
||||
/**
|
||||
* Convert string to Decimal object (from Decimal.js math library)
|
||||
* Handles both dots and commas as decimal separators
|
||||
*
|
||||
* @param {String} str - string to be converted
|
||||
*
|
||||
* @return {Decimal} numeral value
|
||||
*/
|
||||
export const convertToDecimal = str => {
|
||||
const dotFormattedStr = ensureDotSeparator(str);
|
||||
try {
|
||||
return new Decimal(dotFormattedStr);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
return new Decimal(dotFormattedStr);
|
||||
};
|
||||
|
||||
// Converts Decimal object to string
|
||||
export const convertDecimalToString = (decimalValue, usePoint = false) => {
|
||||
try {
|
||||
const d = new Decimal(decimalValue);
|
||||
return ensureSeparator(d.toString(), usePoint);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
/**
|
||||
* Converts Decimal value to a string (from Decimal.js math library)
|
||||
*
|
||||
* @param {Decimal|Number|String} decimalValue
|
||||
*
|
||||
* @param {boolean} useComma - optional.
|
||||
* Specify if return value should use comma as separator
|
||||
*
|
||||
* @return {String} converted value
|
||||
*/
|
||||
export const convertDecimalToString = (decimalValue, useComma = false) => {
|
||||
const d = new Decimal(decimalValue);
|
||||
return ensureSeparator(d.toString(), useComma);
|
||||
};
|
||||
|
||||
// Divisor can be positive value given as Decimal, Number, or String
|
||||
const convertDivisorToDecimal = divisor => {
|
||||
try {
|
||||
const divisorAsDecimal = new Decimal(divisor);
|
||||
|
|
@ -60,15 +71,27 @@ const convertDivisorToDecimal = divisor => {
|
|||
}
|
||||
};
|
||||
|
||||
// Limits value to sub-unit precision: "1.4567" -> "1.45"
|
||||
// Useful in input fields so this doesn't use rounding.
|
||||
export const truncateToSubUnitPrecision = (inputString, subUnitDivisor, usePoint = false) => {
|
||||
/**
|
||||
* Limits value to sub-unit precision: "1.4567" -> "1.45"
|
||||
* Useful in input fields so this doesn't use rounding.
|
||||
*
|
||||
* @param {String} inputString - positive number presentation.
|
||||
*
|
||||
* @param {Decimal|Number|String} subUnitDivisor - a ratio between currency's
|
||||
* main unit and sub units
|
||||
*
|
||||
* @param {boolean} useComma - optional.
|
||||
* Specify if return value should use comma as separator
|
||||
*
|
||||
* @return {String} truncated value
|
||||
*/
|
||||
export const truncateToSubUnitPrecision = (inputString, subUnitDivisor, useComma = false) => {
|
||||
const subUnitDivisorAsDecimal = convertDivisorToDecimal(subUnitDivisor);
|
||||
|
||||
// '10,' should be passed through, but that format is not supported as valid number
|
||||
const trimmed = trimEnd(inputString, usePoint ? ',' : '.');
|
||||
const trimmed = trimEnd(inputString, useComma ? ',' : '.');
|
||||
// create another instance and check if value is convertable
|
||||
const value = convertToDecimal(trimmed, usePoint);
|
||||
const value = convertToDecimal(trimmed, useComma);
|
||||
|
||||
if (value.isNegative()) {
|
||||
throw new Error(`Parameter (${inputString}) must be a positive number.`);
|
||||
|
|
@ -86,25 +109,37 @@ export const truncateToSubUnitPrecision = (inputString, subUnitDivisor, usePoint
|
|||
const decimalPrecisionMax2 = decimalCount2.length >= inputString.length
|
||||
? inputString
|
||||
: value.toFixed(2);
|
||||
return ensureSeparator(decimalPrecisionMax2, usePoint);
|
||||
return ensureSeparator(decimalPrecisionMax2, useComma);
|
||||
} else {
|
||||
// truncate strings ('9.999' => '9.99')
|
||||
const truncated = amount.truncated().dividedBy(subUnitDivisorAsDecimal);
|
||||
return convertDecimalToString(truncated, usePoint);
|
||||
return convertDecimalToString(truncated, useComma);
|
||||
}
|
||||
};
|
||||
|
||||
////////// Currency - Money helpers //////////
|
||||
|
||||
// Converts given value to sub unit value and returns it as a number
|
||||
export const convertUnitToSubUnit = (value, subUnitDivisor, usePoint = false) => {
|
||||
/**
|
||||
* Converts given value to sub unit value and returns it as a number
|
||||
*
|
||||
* @param {Number|String} value
|
||||
*
|
||||
* @param {Decimal|Number|String} subUnitDivisor - a ratio between currency's
|
||||
* main unit and sub units
|
||||
*
|
||||
* @param {boolean} useComma - optional.
|
||||
* Specify if return value should use comma as separator
|
||||
*
|
||||
* @return {number} converted value
|
||||
*/
|
||||
export const convertUnitToSubUnit = (value, subUnitDivisor, useComma = false) => {
|
||||
const subUnitDivisorAsDecimal = convertDivisorToDecimal(subUnitDivisor);
|
||||
|
||||
if (!(typeof value === 'string' || typeof value === 'number')) {
|
||||
throw new Error('Value must be either number or string');
|
||||
throw new TypeError('Value must be either number or string');
|
||||
}
|
||||
|
||||
const val = typeof value === 'string' ? convertToDecimal(value, usePoint) : new Decimal(value);
|
||||
const val = typeof value === 'string' ? convertToDecimal(value, useComma) : new Decimal(value);
|
||||
const amount = val.times(subUnitDivisorAsDecimal);
|
||||
|
||||
if (amount.isInteger()) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
|
||||
describe('currency utils', () => {
|
||||
describe('ensureSeparator(str)', () => {
|
||||
it('changes points in string to dots ', () => {
|
||||
it('changes commas in string to dots ', () => {
|
||||
expect(ensureSeparator('0')).toEqual('0');
|
||||
expect(ensureSeparator('0.0')).toEqual('0.0');
|
||||
expect(ensureSeparator('0.')).toEqual('0.');
|
||||
|
|
@ -19,7 +19,7 @@ describe('currency utils', () => {
|
|||
expect(ensureSeparator('asdf')).toEqual('asdf');
|
||||
});
|
||||
|
||||
it('changes dots in string to points ', () => {
|
||||
it('changes dots in string to commas ', () => {
|
||||
expect(ensureSeparator('0', true)).toEqual('0');
|
||||
expect(ensureSeparator('0.0', true)).toEqual('0,0');
|
||||
expect(ensureSeparator('0.', true)).toEqual('0,');
|
||||
|
|
@ -98,7 +98,7 @@ describe('currency utils', () => {
|
|||
|
||||
describe('truncateToSubUnitPrecision(valueStr, subUnitDivisor)', () => {
|
||||
const subUnitDivisor = 100;
|
||||
it('positive values', () => {
|
||||
it('Values with no truncation needed', () => {
|
||||
expect(truncateToSubUnitPrecision('0', subUnitDivisor)).toEqual('0');
|
||||
expect(truncateToSubUnitPrecision('1', subUnitDivisor)).toEqual('1');
|
||||
expect(truncateToSubUnitPrecision('10', subUnitDivisor)).toEqual('10');
|
||||
|
|
|
|||
|
|
@ -23,3 +23,14 @@ export const createListing = id => ({
|
|||
geolocation: new LatLng(40, 60),
|
||||
},
|
||||
});
|
||||
|
||||
// Default config for currency formatting in tests and examples.
|
||||
export const currencyConfig = {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
currencyDisplay: 'symbol',
|
||||
useGrouping: true,
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
subUnitDivisor: 100,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue