Skip to content

Commit e540d21

Browse files
Merge pull request #22 from telerik/fixes
Fixes
2 parents 8868db7 + 747a893 commit e540d21

File tree

9 files changed

+114
-27
lines changed

9 files changed

+114
-27
lines changed

src/common/round.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const MAX_PRECISION = 20;
2+
13
export default function round(value, precision) {
24
let result = value;
35
let decimals = precision || 0;
@@ -8,5 +10,5 @@ export default function round(value, precision) {
810
result = result.toString().split('e');
911
result = Number(result[0] + 'e' + (result[1] ? (Number(result[1]) - decimals) : -decimals));
1012

11-
return result.toFixed(decimals);
13+
return result.toFixed(Math.min(decimals, MAX_PRECISION));
1214
}

src/dates/format-date.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ formatters.Q = formatQuarter;
176176

177177
export default function formatDate(date, format, locale = "en") {
178178
if (!isDate(date)) {
179+
if (date === undefined || date === null) {
180+
return '';
181+
}
179182
return date;
180183
}
181184

src/format.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export function toString(value, format, locale) {
1313
}
1414
}
1515

16-
return value !== undefined ? value : "";
16+
return value !== undefined && value !== null ? value : "";
1717
}
1818

1919
export function format(format, values, locale) {

src/numbers/custom-number-format.js

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import round from '../common/round';
44

55
const CURRENCY_SYMBOL = "$";
66
const PERCENT_SYMBOL = "%";
7-
const PLACEHOLDER = "??";
7+
const PLACEHOLDER = "__??__";
88
const CURRENCY = "currency";
99
const PERCENT = "percent";
1010
const POINT = ".";
@@ -14,6 +14,8 @@ const ZERO = "0";
1414
const EMPTY = "";
1515

1616
const literalRegExp = /(\\.)|(['][^']*[']?)|(["][^"]*["]?)/g;
17+
const trailingZerosRegExp = /(\.(?:[0-9]*[1-9])?)0+$/g;
18+
const trailingPointRegExp = /\.$/;
1719
const commaRegExp = /\,/g;
1820

1921
function setFormatLiterals(formatOptions) {
@@ -31,6 +33,18 @@ function setFormatLiterals(formatOptions) {
3133
}
3234
}
3335

36+
function trimTrailingZeros(value, lastZero) {
37+
let trimRegex;
38+
39+
if (lastZero === 0) {
40+
trimRegex = trailingZerosRegExp;
41+
} else {
42+
trimRegex = new RegExp(`(\\.[0-9]{${ lastZero }}[1-9]*)0+$`, 'g');
43+
}
44+
45+
return value.replace(trimRegex, '$1').replace(trailingPointRegExp, '');
46+
}
47+
3448
function roundNumber(formatOptions) {
3549
let { number, format } = formatOptions;
3650
let decimalIndex = format.indexOf(POINT);
@@ -49,24 +63,30 @@ function roundNumber(formatOptions) {
4963
}
5064
fraction = fraction.split(POINT)[1] || EMPTY;
5165

52-
let idx = fraction.length;
66+
let precision = fraction.length;
67+
let trailingZeros = -1;
5368

5469
if (!hasZero && !hasSharp) {
5570
formatOptions.format = format.substring(0, decimalIndex) + format.substring(decimalIndex + 1);
5671
decimalIndex = -1;
57-
idx = 0;
72+
precision = 0;
5873
} else if (hasZero && zeroIndex > sharpIndex) {
59-
idx = zeroIndex;
74+
precision = zeroIndex;
6075
} else if (sharpIndex > zeroIndex) {
61-
if (hasSharp && idx > sharpIndex) {
62-
idx = sharpIndex;
63-
} else if (hasZero && idx < zeroIndex) {
64-
idx = zeroIndex;
76+
if (hasSharp && precision > sharpIndex) {
77+
precision = sharpIndex;
78+
} else if (hasZero && precision < zeroIndex) {
79+
precision = zeroIndex;
6580
}
81+
82+
trailingZeros = hasZero ? zeroIndex : 0;
6683
}
6784

68-
if (idx > -1) {
69-
number = round(number, idx);
85+
if (precision > -1) {
86+
number = round(number, precision);
87+
if (trailingZeros > -1) {
88+
number = trimTrailingZeros(number, trailingZeros);
89+
}
7090
}
7191
} else {
7292
number = round(number);
@@ -233,7 +253,7 @@ function replacePlaceHolders(formatOptions, info) {
233253
}
234254

235255
if (hasGroup) {
236-
number = groupInteger(number, start + (negative ? 1 : 0), Math.max(end, (integerLength + start)), info.numbers.decimal, info);
256+
number = groupInteger(number, start + (negative && !hasNegativeFormat ? 1 : 0), Math.max(end, (integerLength + start)), info.numbers.decimal, info);
237257
}
238258

239259
if (end >= start) {

src/numbers/group-integer.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ export default function groupInteger(number, start, end, options, info) {
2424
groupSize = newGroupSize !== undefined ? newGroupSize : groupSize;
2525

2626
if (groupSize === 0) {
27-
parts.push(integer.substring(0, idx));
27+
value = integer.substring(0, idx);
28+
if (value) {
29+
parts.push(value);
30+
}
2831
break;
2932
}
3033
}

src/numbers/standard-number-format.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const DECIMAL_PLACEHOLDER = "n";
1212
const CURRENCY = "currency";
1313
const PERCENT = "percent";
1414
const EMPTY = "";
15+
const POINT = ".";
1516

1617
function fractionOptions(options) {
1718
let { minimumFractionDigits, maximumFractionDigits, style } = options;
@@ -69,14 +70,15 @@ function currencyUnitPattern(info, value) {
6970

7071

7172
export default function standardNumberFormat(number, options, info) {
73+
const symbols = info.numbers.symbols;
7274
const { style } = options;
7375

7476
//return number in exponential format
7577
if (style === "scientific") {
76-
return options.minimumFractionDigits !== undefined ? number.toExponential(options.minimumFractionDigits) : number.toExponential();
78+
let exponential = options.minimumFractionDigits !== undefined ? number.toExponential(options.minimumFractionDigits) : number.toExponential();
79+
return exponential.replace(POINT, symbols.decimal);
7780
}
7881

79-
const symbols = info.numbers.symbols;
8082
let value = number;
8183
let symbol;
8284

test/dates.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ describe('date formatting', () => {
2929
expect(formatDate("foo")).toEqual("foo");
3030
});
3131

32+
it('returns empty string if value is null or undefined', () => {
33+
expect(formatDate(undefined)).toEqual("");
34+
expect(formatDate(null)).toEqual("");
35+
});
36+
3237
it('applies short date format if no format is set', () => {
3338
expect(formatDate(date(2000, 1, 30))).toEqual("1/30/2000");
3439
});

test/format.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ describe('toString', () => {
3535
expect(toString(undefined, "c", "en")).toBe("");
3636
});
3737

38+
it('returns empty string if the value is null', () => {
39+
expect(toString(null, "c", "en")).toBe("");
40+
});
41+
3842
it('returns the value if the format is not defined', () => {
3943
expect(toString(10)).toBe(10);
4044
});

test/numbers.js

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,21 @@ describe('formatNumber', () => {
4747
expect(formatNumber()).toEqual("");
4848
});
4949

50-
it('should return empty string if null value is passed', () => {
50+
it('should return empty string if null or undefined value is passed', () => {
5151
expect(formatNumber(null)).toEqual("");
52+
expect(formatNumber(undefined)).toEqual("");
5253
});
5354

5455
it('should return value if not finite value is passed', () => {
5556
expect(formatNumber(Infinity)).toBe(Infinity);
5657
expect(formatNumber("foo")).toEqual("foo");
5758
});
5859

60+
it('should limit precision', () => {
61+
const value = 5.4654647884512e+96;
62+
expect(formatNumber(value, '#.#')).toEqual(value.toFixed(20));
63+
});
64+
5965

6066
describe('errors', () => {
6167
currencyData.supplemental.currencyData.region.CUSTOM = [{ XXX: {} }];
@@ -82,24 +88,26 @@ describe('formatNumber', () => {
8288
});
8389

8490
describe('standard scientific formatting', () => {
91+
const value = 123;
92+
8593
it('should apply format', () => {
86-
const number = 123;
87-
expect(formatNumber(number, 'e')).toEqual(number.toExponential());
94+
expect(formatNumber(value, 'e')).toEqual(value.toExponential());
8895
});
8996

9097
it('should apply format with precision', () => {
91-
const number = 123;
92-
expect(formatNumber(number, 'e10')).toEqual(number.toExponential(10));
98+
expect(formatNumber(value, 'e10')).toEqual(value.toExponential(10));
9399
});
94100

95101
it('should apply format when passing options object', () => {
96-
const number = 123;
97-
expect(formatNumber(number, { style: 'scientific' })).toEqual(number.toExponential());
102+
expect(formatNumber(value, { style: 'scientific' })).toEqual(value.toExponential());
98103
});
99104

100105
it('should apply format with precision when passing options object', () => {
101-
const number = 123;
102-
expect(formatNumber(number, { style: 'scientific', minimumFractionDigits: 10})).toEqual(number.toExponential(10));
106+
expect(formatNumber(value, { style: 'scientific', minimumFractionDigits: 10})).toEqual(value.toExponential(10));
107+
});
108+
109+
it('should use locale specific decimal separator', () => {
110+
expect(formatNumber(value, { style: 'scientific' }, 'bg')).toEqual('1,23e+2');
103111
});
104112
});
105113

@@ -226,6 +234,14 @@ describe('standard decimal formatting', () => {
226234
expect(formatNumber(33111110, "n", "custom")).toEqual("33111110");
227235
});
228236

237+
//doesn't seem to be a locale with zero group size so not sure if this is needed
238+
it('should not add group if the integer length is equal to the non-zero group sizes', () => {
239+
loadCustom({ pattern: ",,###,##0.###"});
240+
console.log(JSON.stringify(cldr.custom, null, 4));
241+
242+
expect(formatNumber(123456, "n", "custom")).toEqual("123,456");
243+
});
244+
229245
});
230246

231247
describe('standard percent formatting', () => {
@@ -336,6 +352,22 @@ describe('custom formatting', () => {
336352
expect(formatNumber(-18000, '#,##0')).toEqual("-18,000");
337353
});
338354

355+
it('formats currency', () => {
356+
expect(formatNumber(10, '$#.#')).toEqual("$10");
357+
});
358+
359+
it('formats currency with locale symbol', () => {
360+
expect(formatNumber(10, '#.#$', 'bg')).toEqual("10лв.");
361+
});
362+
363+
it('formats percentage', () => {
364+
expect(formatNumber(0.5, '#.#%')).toEqual("50%");
365+
});
366+
367+
it('percentage does not leave trailing zeros if multiplication by 100 causes rounding error', () => {
368+
expect(formatNumber(0.035, '#.##%')).toEqual("3.5%");
369+
});
370+
339371
it('applies thousand separator to a longer than the pattern number', () => {
340372
expect(formatNumber(1000000.1, '#,###')).toEqual("1,000,000");
341373
});
@@ -404,6 +436,11 @@ describe('custom formatting', () => {
404436
expect(formatNumber(10, "# \\%")).toEqual("10 %");
405437
});
406438

439+
it("formats with question mark as literal", () => {
440+
expect(formatNumber(10, "?\\$#")).toEqual("?$10");
441+
expect(formatNumber(10, "\\?\\$#")).toEqual("?$10");
442+
});
443+
407444
it("formats with quote as literal", () => {
408445
expect(formatNumber(10, "# \"%\"")).toEqual("10 %");
409446
});
@@ -442,6 +479,13 @@ describe('custom formatting', () => {
442479
expect(formatNumber(3.235555, "0.#0")).toEqual("3.24");
443480
});
444481

482+
it("removes trailing zeros after rounding", () => {
483+
expect(formatNumber(0.016999999999, "#.#####")).toEqual("0.017");
484+
expect(formatNumber(0.016999999999, "#.0000#")).toEqual("0.0170");
485+
expect(formatNumber(1.999, "0.0#")).toEqual("2.0");
486+
expect(formatNumber(1.999, "0.#")).toEqual("2");
487+
});
488+
445489
it("removes decimal part if no number placeholder", () => {
446490
expect(formatNumber(3.222, "0.")).toEqual("3");
447491
});
@@ -455,15 +499,19 @@ describe('custom formatting', () => {
455499
});
456500

457501
it("applies negative format rounding", () => {
458-
expect(formatNumber(-0.001, "####;-(#.#)")).toEqual("-(0.0)");
502+
expect(formatNumber(-0.001, "####;-(#.#)")).toEqual("-(0)");
503+
});
504+
505+
it("toString decimal number -1000 with negative format", () => {
506+
expect(formatNumber(-1000, "#,##0;(#,##0);-")).toEqual("(1,000)");
459507
});
460508

461509
it("applies negative format", () => {
462510
expect(formatNumber(-123, "####;-(#.00)")).toEqual("-(123.00)");
463511
});
464512

465513
it("clears negative sign if rounded number is positive", () => {
466-
expect(formatNumber(-0.00001, "#.##")).toEqual("0.00");
514+
expect(formatNumber(-0.00001, "#.##")).toEqual("0");
467515
});
468516

469517
it("formats 0", () => {

0 commit comments

Comments
 (0)