Fixed incrementing/decrementing formatted numbers

The float formatter function now takes a number of fraction digits to return.
This commit is contained in:
Minh Nguyễn
2023-03-04 18:36:06 -08:00
parent 4e1129709c
commit 023907f9e2
3 changed files with 63 additions and 4 deletions
+42 -2
View File
@@ -424,15 +424,28 @@ export function coreLocalizer() {
return code; // if not found, use the code
};
/**
* Returns a function that formats a floating-point number in the given
* locale.
*/
localizer.floatFormatter = (locale) => {
if (!('Intl' in window && 'NumberFormat' in Intl &&
'formatToParts' in Intl.NumberFormat.prototype)) {
return (number) => number.toString();
return (number, fractionDigits) => {
return fractionDigits === undefined ? number.toString() : number.toFixed(fractionDigits);
};
} else {
return (number) => number.toLocaleString(locale, { maximumFractionDigits: 20 });
return (number, fractionDigits) => number.toLocaleString(locale, {
minimumFractionDigits: fractionDigits,
maximumFractionDigits: fractionDigits === undefined ? 20 : fractionDigits,
});
}
};
/**
* Returns a function that parses a number formatted according to the given
* locale as a floating-point number.
*/
localizer.floatParser = (locale) => {
// https://stackoverflow.com/a/55366435/4585461
const polyfill = (string) => parseFloat(string.trim());
@@ -460,5 +473,32 @@ export function coreLocalizer() {
};
};
/**
* Returns a function that returns the number of decimal places in a
* formatted number string.
*/
localizer.decimalPlaceCounter = (locale) => {
var literal, group, decimal;
if ('Intl' in window && 'NumberFormat' in Intl) {
const format = new Intl.NumberFormat(locale, { maximumFractionDigits: 20 });
if (('formatToParts' in format)) {
const parts = format.formatToParts(-12345.6);
const literalPart = parts.find(d => d.type === 'literal');
literal = literalPart && new RegExp(`[${literalPart.value}]`, 'g');
const groupPart = parts.find(d => d.type === 'group');
group = groupPart && new RegExp(`[${groupPart.value}]`, 'g');
const decimalPart = parts.find(d => d.type === 'decimal');
decimal = decimalPart && new RegExp(`[${decimalPart.value}]`);
}
}
return (string) => {
string = string.trim();
if (literal) string = string.replace(literal, '');
if (group) string = string.replace(group, '');
const parts = string.split(decimal || '.');
return parts && parts[1] && parts[1].length || 0;
};
};
return localizer;
}
+2 -2
View File
@@ -34,6 +34,7 @@ export function uiFieldText(field, context) {
const isDirectionField = field.key.split(':').some(keyPart => keyPart === 'direction');
const formatFloat = localizer.floatFormatter(localizer.languageCode());
const parseLocaleFloat = localizer.floatParser(localizer.languageCode());
const countDecimalPlaces = localizer.decimalPlaceCounter(localizer.languageCode());
if (field.type === 'tel') {
fileFetcher.get('phone_formats')
@@ -155,8 +156,7 @@ export function uiFieldText(field, context) {
num = ((num % 360) + 360) % 360;
}
// make sure no extra decimals are introduced
const numDecimals = v.includes('.') ? v.split('.')[1].length : 0;
return formatFloat(clamped(num).toFixed(numDecimals));
return formatFloat(clamped(num), countDecimalPlaces(v));
});
input.node().value = vals.join(';');
change()();
+19
View File
@@ -6,6 +6,15 @@ describe('iD.coreLocalizer', function() {
expect(selection.selectChild().classed('localized-text')).to.be.true;
});
});
describe('#floatFormatter', function () {
it('uses the specified number of fraction digits', function () {
var localizer = iD.coreLocalizer();
var formatFloat = localizer.floatFormatter('en');
expect(formatFloat(-0.1)).to.eql('-0.1');
expect(formatFloat(-0.1, 0)).to.eql('-0');
expect(formatFloat(-0.1, 2)).to.eql('-0.10');
});
});
describe('#floatParser', function () {
it('roundtrips English numbers', function () {
var localizer = iD.coreLocalizer();
@@ -58,4 +67,14 @@ describe('iD.coreLocalizer', function() {
expect(parseFloat(formatFloat(3.14159))).to.eql(3.14159);
});
});
describe('#decimalPlaceCounter', function () {
it('counts decimal places in English numbers', function () {
var localizer = iD.coreLocalizer();
var countDecimalPlaces = localizer.decimalPlaceCounter('en');
expect(countDecimalPlaces('-0')).to.eql(0);
expect(countDecimalPlaces('-0.1')).to.eql(1);
expect(countDecimalPlaces('1.234')).to.eql(3);
expect(countDecimalPlaces('10')).to.eql(0);
});
});
});