From ce8bf1ffeea29194efd2cb607364061539d22590 Mon Sep 17 00:00:00 2001
From: Jacob Wood
Date: Thu, 12 Jul 2018 11:15:09 -0400
Subject: [PATCH] Added truncate and truncateAfter options to numeric pipe To
be consumed by future currency input control in development Resolves: #1643
---
src/demos/numeric/numeric-demo.component.html | 16 +++++
src/modules/numeric/numeric.options.ts | 2 +
src/modules/numeric/numeric.pipe.spec.ts | 55 +++++++++++++++++
src/modules/numeric/numeric.pipe.ts | 21 ++++++-
src/modules/numeric/numeric.service.ts | 26 +++++---
src/modules/numeric/numeric.spec.ts | 59 +++++++++++++++++++
6 files changed, 169 insertions(+), 10 deletions(-)
create mode 100644 src/modules/numeric/numeric.pipe.spec.ts
diff --git a/src/demos/numeric/numeric-demo.component.html b/src/demos/numeric/numeric-demo.component.html
index 30e8caa58..95bf58fe3 100644
--- a/src/demos/numeric/numeric-demo.component.html
+++ b/src/demos/numeric/numeric-demo.component.html
@@ -115,6 +115,22 @@
{{1234567 | skyNumeric}}
+
+
+ 1234567 without truncation
+
+
+ {{1234567 | skyNumeric:{truncate: false} }}
+
+
+
+
+ 15501 with truncation after 10000 and 1 digit
+
+
+ {{15501 | skyNumeric:{digits: 1, truncateAfter: 10000} }}
+
+
15.50 as US dollar currency with 2 digits:
diff --git a/src/modules/numeric/numeric.options.ts b/src/modules/numeric/numeric.options.ts
index e018e8da6..cc467c02d 100644
--- a/src/modules/numeric/numeric.options.ts
+++ b/src/modules/numeric/numeric.options.ts
@@ -2,4 +2,6 @@ export class NumericOptions {
public digits = 1;
public format = 'number';
public iso = 'USD';
+ public truncate: boolean = true;
+ public truncateAfter: number = 0;
}
diff --git a/src/modules/numeric/numeric.pipe.spec.ts b/src/modules/numeric/numeric.pipe.spec.ts
new file mode 100644
index 000000000..201fd4fb8
--- /dev/null
+++ b/src/modules/numeric/numeric.pipe.spec.ts
@@ -0,0 +1,55 @@
+import { TestBed } from '@angular/core/testing';
+import { SkyNumericModule } from './numeric.module';
+import { SkyNumericService } from './numeric.service';
+import { CurrencyPipe, DecimalPipe } from '@angular/common';
+import { SkyNumericPipe } from './numeric.pipe';
+
+describe('Numeric pipe', () => {
+ let skyNumericPipe: SkyNumericPipe;
+ let skyNumeric = new SkyNumericService(
+ new CurrencyPipe('en-US'),
+ new DecimalPipe('en-US')
+ );
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ SkyNumericModule
+ ],
+ providers: [
+ SkyNumericPipe,
+ {
+ provide: SkyNumericService,
+ useValue: skyNumeric
+ }
+ ]
+ });
+ });
+
+ beforeEach(() => {
+ skyNumericPipe = TestBed.get(SkyNumericPipe);
+ });
+
+ it('should pass all options to the sky numeric service', () => {
+
+ let options: any = {
+ digits: 2,
+ format: 'currency',
+ iso: 'USD',
+ truncate: true,
+ truncateAfter: 10000
+ };
+ expect(skyNumericPipe.transform(10001.00, options)).toBe('$10K');
+ });
+
+ it(`doesn''t require options to be set`, () => {
+ expect(skyNumericPipe.transform(42.87, undefined)).toBe('42.9');
+ });
+
+ it(`doesn''t require option properties to be set`, () => {
+ let options: any = {
+ random: false
+ };
+ expect(skyNumericPipe.transform(42.87, options)).toBe('42.9');
+ });
+});
diff --git a/src/modules/numeric/numeric.pipe.ts b/src/modules/numeric/numeric.pipe.ts
index 610466152..828c58c2b 100644
--- a/src/modules/numeric/numeric.pipe.ts
+++ b/src/modules/numeric/numeric.pipe.ts
@@ -4,8 +4,8 @@ import { NumericOptions } from './numeric.options';
/*
* Shortens numbers to 1K, 1M, 1B, 1T and can format for currency
- * All three arguments in the options object, Digits, format and ISO, are optional,
- * defaulting to 1, number and USD respectively
+ * All five arguments in the options object, Digits, format, iso, truncate, truncateAfter are optional,
+ * defaulting to 1, number and USD, true, 0 respectively
* Usage:
* number_expression | skyNumeric[:numbericOptions]]]
*
@@ -13,12 +13,23 @@ import { NumericOptions } from './numeric.options';
* digits
* format
* iso
+ * truncate
+ * truncateAfter
* Example:
* {{ 1075 | skyNumeric:{digits: 1, format: 'currency', iso: 'USD'} }}
* formats to: $1.1K
* Example:
* {{ 2075000 | skyNumeric:{digits: 2} }}
* formats to: 2.08M
+ * Example:
+ * {{ 2075000 | skyNumeric:{truncate: false} }}
+ * formats to: 2,075,000
+ * Example:
+ * {{ 9500 | skyNumeric:{truncateAfter: 10000} }}
+ * formats to: 9,500
+ * Example:
+ * {{ 10001 | skyNumeric:{truncateAfter: 10000} }}
+ * formats to: 10K
* Note: Be sure you have a space between the curly bracket surrounding the options object
* and the two curly brackets closing the pipe or it will not work. Thanks angular pipes.
*/
@@ -43,6 +54,12 @@ export class SkyNumericPipe implements PipeTransform {
if (optionsObject.iso !== undefined) {
options.iso = optionsObject.iso;
}
+ if (optionsObject.truncate !== undefined) {
+ options.truncate = optionsObject.truncate;
+ }
+ if (optionsObject.truncateAfter !== undefined) {
+ options.truncateAfter = optionsObject.truncateAfter;
+ }
}
return this.skyNumeric.formatNumber(value, options);
}
diff --git a/src/modules/numeric/numeric.service.ts b/src/modules/numeric/numeric.service.ts
index 3a44e0e01..b23d1e1d1 100644
--- a/src/modules/numeric/numeric.service.ts
+++ b/src/modules/numeric/numeric.service.ts
@@ -46,11 +46,15 @@ export class SkyNumericService {
value: number,
options: NumericOptions
): string {
+ if (isNaN(value)) {
+ return '';
+ }
const decimalPlaceRegExp = /\.0+$|(\.[0-9]*[1-9])0+$/;
const symbol: SkyNumericSymbol = this.symbolIndex.find((si) => {
// Checks both positive and negative of value to ensure
// negative numbers are shortened.
- return (value >= si.value || -value >= si.value);
+ return options.truncate &&
+ ((value >= options.truncateAfter && value >= si.value) || (-value >= options.truncateAfter && -value >= si.value));
});
let output: string;
@@ -70,6 +74,7 @@ export class SkyNumericService {
this.storeShortenSymbol(output);
+ let digits: string;
// Checks the string entered for format. Using toLowerCase to ignore case.
switch (options.format.toLowerCase()) {
@@ -78,11 +83,10 @@ export class SkyNumericService {
// For example, this prevents a value like $15.50 from displaying as $15.5.
// Note: This will need to be reviewed if we support currencies with three decimal digits.
case 'currency':
- const isShortened = (value > this.symbolIndex[this.symbolIndex.length - 1].value);
+ const isShortened = options.truncate && (value > this.symbolIndex[this.symbolIndex.length - 1].value);
const isDecimal = (value % 1 !== 0);
- let digits: string;
- if (!isShortened && isDecimal && options.digits >= 2) {
+ if ((!isShortened && isDecimal && options.digits >= 2) || !options.truncate) {
digits = `1.2-${options.digits}`;
} else {
digits = `1.0-${options.digits}`;
@@ -101,16 +105,22 @@ export class SkyNumericService {
// it will be treated like a number.
default:
// Ensures localization of the number to ensure comma and
- // decimal separators are correct.
+ // decimal separator
+ if (!options.truncate) {
+ digits = `1.${options.digits}-${options.digits}`;
+ } else {
+ digits = `1.0-${options.digits}`;
+ }
output = this.decimalPipe.transform(
parseFloat(output),
- `1.0-${options.digits}`
+ digits
);
break;
}
- output = this.replaceShortenSymbol(output);
-
+ if (options.truncate) {
+ output = this.replaceShortenSymbol(output);
+ }
return output;
}
diff --git a/src/modules/numeric/numeric.spec.ts b/src/modules/numeric/numeric.spec.ts
index a088ce62f..ee183acae 100644
--- a/src/modules/numeric/numeric.spec.ts
+++ b/src/modules/numeric/numeric.spec.ts
@@ -25,6 +25,14 @@ describe('Numeric service', () => {
expect(skyNumeric.formatNumber(value, options)).toBe('0');
});
+ it('formats undefined as blank', () => {
+ let value = undefined;
+ let options = new NumericOptions();
+ options.digits = 0;
+
+ expect(skyNumeric.formatNumber(value, options)).toBe('');
+ });
+
it('formats 100 with 0 digits as 100', () => {
const value = 100;
let options = new NumericOptions();
@@ -40,6 +48,14 @@ describe('Numeric service', () => {
expect(skyNumeric.formatNumber(value, options)).toBe('1K');
});
+ it('does not truncate 1000 with 2 digits as 1K when truncate is false', () => {
+ const value = 1000;
+ let options = new NumericOptions();
+ options.digits = 2;
+ options.truncate = false;
+ expect(skyNumeric.formatNumber(value, options)).toBe('1,000.00');
+ });
+
it('formats 1000000 with 0 digits as 1M', () => {
const value = 1000000;
let options = new NumericOptions();
@@ -47,6 +63,14 @@ describe('Numeric service', () => {
expect(skyNumeric.formatNumber(value, options)).toBe('1M');
});
+ it('does not truncate 1000000 with 2 digits as 1M when truncate is false', () => {
+ const value = 1000000;
+ let options = new NumericOptions();
+ options.digits = 2;
+ options.truncate = false;
+ expect(skyNumeric.formatNumber(value, options)).toBe('1,000,000.00');
+ });
+
it('formats 1000000000 with 0 digits as 1B', () => {
const value = 1000000000;
let options = new NumericOptions();
@@ -54,6 +78,14 @@ describe('Numeric service', () => {
expect(skyNumeric.formatNumber(value, options)).toBe('1B');
});
+ it('does not truncate 1000000000 with 2 digits as 1B when truncate is false', () => {
+ const value = 1000000000;
+ let options = new NumericOptions();
+ options.digits = 2;
+ options.truncate = false;
+ expect(skyNumeric.formatNumber(value, options)).toBe('1,000,000,000.00');
+ });
+
it('formats 1000000000000 with 0 digits as 1T', () => {
const value = 1000000000000;
let options = new NumericOptions();
@@ -61,6 +93,14 @@ describe('Numeric service', () => {
expect(skyNumeric.formatNumber(value, options)).toBe('1T');
});
+ it('does not truncate 1000000000000 with 2 digits as 1T when truncate is false', () => {
+ const value = 1000000000000;
+ let options = new NumericOptions();
+ options.digits = 2;
+ options.truncate = false;
+ expect(skyNumeric.formatNumber(value, options)).toBe('1,000,000,000,000.00');
+ });
+
it('formats 999000000 as 999M', () => {
const value = 999000000;
let options = new NumericOptions();
@@ -124,6 +164,25 @@ describe('Numeric service', () => {
options.format = 'currency';
expect(skyNumeric.formatNumber(value, options)).toBe('£15.50');
});
+
+ // Testing ability only after a certain value is specified
+ // using the truncateAfter configuration property
+ it('does not truncate 5000 to 5K when truncateAfter set to 10000', () => {
+ const value = 5000;
+ let options = new NumericOptions();
+ options.digits = 0;
+ options.truncateAfter = 10000;
+ expect(skyNumeric.formatNumber(value, options)).toBe('5,000');
+ });
+
+ it('formats 10001 to 10K when truncateAfter set to 10000', () => {
+ const value = 10001;
+ let options = new NumericOptions();
+ options.digits = 0;
+ options.truncateAfter = 10000;
+ expect(skyNumeric.formatNumber(value, options)).toBe('10K');
+ });
+
// Adjusting test to expect either format of a negative. MS browsers use system's Region
// setting for Currency formatting. For Negative currency, the windows default is parentheses
// around the number. All other browsers use a preceeding negative sign (-).