Skip to content
This repository has been archived by the owner on Dec 8, 2022. It is now read-only.

Added truncate and truncateAfter options to numeric pipe #1823

Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/demos/numeric/numeric-demo.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,22 @@
{{1234567 | skyNumeric}}
</sky-definition-list-value>
</p>
<p>
<sky-definition-list-label>
1234567 without truncation
</sky-definition-list-label>
<sky-definition-list-value>
{{1234567 | skyNumeric:{truncate: false} }}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This produced the following:

screen shot 2018-07-13 at 12 27 58 pm

Is that expected, or should it just be 1,234,567?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn’t mess with the default for the digits option which is 1 in the NumericOptions class. When not truncating a decimal, the min and max fraction value are both set to that digits option so as to not mess with the output.

</sky-definition-list-value>
</p>
<p>
<sky-definition-list-label>
15501 with truncation after 10000 and 1 digit
</sky-definition-list-label>
<sky-definition-list-value>
{{15501 | skyNumeric:{digits: 1, truncateAfter: 10000} }}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Blackbaud-JacobWood If I change truncateAfter to 1000, the result doesn't change. Is that expected?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, original affects of the this component would only occur when the value is greater than the truncateAfter, with 15501 being greater than both 1000 and 10000 they would be the same. When set to 10000, the value 9999 should not be truncated.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gotcha, thanks for the explanation!

</sky-definition-list-value>
</p>
<p>
<sky-definition-list-label>
15.50 as US dollar currency with 2 digits:
Expand Down
2 changes: 2 additions & 0 deletions src/modules/numeric/numeric.options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ export class NumericOptions {
public digits = 1;
public format = 'number';
public iso = 'USD';
public truncate: boolean = true;
public truncateAfter: number = 0;
}
55 changes: 55 additions & 0 deletions src/modules/numeric/numeric.pipe.spec.ts
Original file line number Diff line number Diff line change
@@ -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');
});
});
21 changes: 19 additions & 2 deletions src/modules/numeric/numeric.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,32 @@ 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]]]
*
* options is an object to be passed in with the following parameters:
* 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.
*/
Expand All @@ -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);
}
Expand Down
26 changes: 18 additions & 8 deletions src/modules/numeric/numeric.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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()) {

Expand All @@ -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}`;
Expand All @@ -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;
}

Expand Down
59 changes: 59 additions & 0 deletions src/modules/numeric/numeric.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -40,27 +48,59 @@ 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();
options.digits = 0;
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();
options.digits = 0;
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();
options.digits = 0;
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();
Expand Down Expand Up @@ -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 (-).
Expand Down