Skip to content

Commit

Permalink
Update value view component w/ shortened amount
Browse files Browse the repository at this point in the history
  • Loading branch information
grod220 committed Nov 19, 2024
1 parent 5640878 commit 955e09a
Show file tree
Hide file tree
Showing 8 changed files with 517 additions and 27 deletions.
2 changes: 1 addition & 1 deletion apps/minifront/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"devDependencies": {
"@chain-registry/types": "^0.45.38",
"@eslint/compat": "^1.1.0",
"@types/lodash": "^4.17.4",
"@types/lodash": "^4.17.7",
"@types/react": "^18.3.2",
"@types/react-dom": "^18.3.0",
"@types/react-helmet": "^6.1.11",
Expand Down
4 changes: 3 additions & 1 deletion packages/types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@
"dependencies": {
"bignumber.js": "^9.1.2",
"idb": "^8.0.0",
"lodash": "^4.17.21",
"zod": "^3.23.8"
},
"devDependencies": {
"@bufbuild/protobuf": "^1.10.0",
"@penumbra-zone/bech32m": "workspace:*",
"@penumbra-zone/getters": "workspace:*",
"@penumbra-zone/protobuf": "workspace:*"
"@penumbra-zone/protobuf": "workspace:*",
"@types/lodash": "^4.17.7"
},
"peerDependencies": {
"@bufbuild/protobuf": "^1.10.0",
Expand Down
124 changes: 124 additions & 0 deletions packages/types/src/round.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { describe, expect, it } from 'vitest';
import { round, RoundOptions } from './round.js';

describe('round function', () => {
const testCases: {
description: string;
options: RoundOptions;
expected: string;
}[] = [
// Default rounding mode ('round')
{
description: 'should round up using default rounding (round)',
options: { value: 1.2345, decimals: 3 },
expected: '1.235',
},
{
description: 'should round down using default rounding (round)',
options: { value: 1.2341, decimals: 3 },
expected: '1.234',
},
{
description: 'should round a negative number using default rounding (round)',
options: { value: -1.2345, decimals: 2 },
expected: '-1.23',
},
{
description: 'should round zero',
options: { value: 0, decimals: 2 },
expected: '0.00',
},
{
description: 'should round an integer with decimals',
options: { value: 5, decimals: 2 },
expected: '5.00',
},
// Rounding mode: 'ceil'
{
description: 'should ceil to 2 decimals',
options: { value: 1.2345, decimals: 2, roundingMode: 'ceil' },
expected: '1.24',
},
{
description: 'should ceil a negative number',
options: { value: -1.2345, decimals: 2, roundingMode: 'ceil' },
expected: '-1.23',
},
{
description: 'should ceil with zero decimals',
options: { value: 1.5, decimals: 0, roundingMode: 'ceil' },
expected: '2',
},
// Rounding mode: 'floor'
{
description: 'should floor to 2 decimals',
options: { value: 1.2399, decimals: 2, roundingMode: 'floor' },
expected: '1.23',
},
{
description: 'should floor a negative number',
options: { value: -1.2345, decimals: 2, roundingMode: 'floor' },
expected: '-1.24',
},
{
description: 'should floor with zero decimals',
options: { value: 1.9, decimals: 0, roundingMode: 'floor' },
expected: '1',
},
// Edge Cases
{
description: 'should handle very large numbers',
options: { value: 1.23456789e10, decimals: 2, roundingMode: 'round' },
expected: '12345678900.00',
},
{
description: 'should handle very small numbers',
options: { value: 0.000123456, decimals: 8, roundingMode: 'round' },
expected: '0.00012346',
},
{
description: 'should handle Infinity',
options: { value: Infinity, decimals: 2, roundingMode: 'round' },
expected: 'Infinity',
},
{
description: 'should handle -Infinity',
options: { value: -Infinity, decimals: 2, roundingMode: 'floor' },
expected: '-Infinity',
},
{
description: 'should handle NaN',
options: { value: NaN, decimals: 2, roundingMode: 'ceil' },
expected: 'NaN',
},
{
description: 'should handle decimals greater than available decimal places',
options: { value: 1.2, decimals: 5, roundingMode: 'floor' },
expected: '1.20000',
},
// Rounding to integer
{
description: 'should round to integer using round mode',
options: { value: 2.5, decimals: 0, roundingMode: 'round' },
expected: '3',
},
{
description: 'should ceil to integer',
options: { value: 2.1, decimals: 0, roundingMode: 'ceil' },
expected: '3',
},
{
description: 'should floor to integer',
options: { value: 2.9, decimals: 0, roundingMode: 'floor' },
expected: '2',
},
];

testCases.forEach(({ description, options, expected }) => {
// eslint-disable-next-line vitest/valid-title
it(description, () => {
const result = round(options);
expect(result).toBe(expected);
});
});
});
39 changes: 39 additions & 0 deletions packages/types/src/round.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ceil as lodashCeil, floor as lodashFloor, round as lodashRound } from 'lodash';

export type RoundingMode = 'round' | 'ceil' | 'floor';

export interface RoundOptions {
value: number;
decimals: number;
roundingMode?: RoundingMode;
}

const roundingStrategies = {
ceil: lodashCeil,
floor: lodashFloor,
round: lodashRound,
} as const;

/**
* Rounds a number based on the specified options.
*
* @param options - An object containing the properties:
* - value: The number to round.
* - decimals: The number of decimal places to round to.
* - roundingMode: The mode of rounding ('round', 'ceil', 'floor'). Defaults to 'round'.
*
* @returns A string representation of the rounded number.
*
* @example
*
* ```typescript
* round({ value: 1.2345, decimals: 2, roundingMode: 'ceil' }); // "1.24"
* round({ value: 1.2345, decimals: 2, roundingMode: 'floor' }); // "1.23"
* round({ value: 1.2345, decimals: 2 }); // "1.23" (default rounding)
* ```
*/
export function round({ value, decimals, roundingMode = 'round' }: RoundOptions): string {
const roundingFn = roundingStrategies[roundingMode];
const roundedNumber = roundingFn(value, decimals);
return roundedNumber.toFixed(decimals);
}
208 changes: 208 additions & 0 deletions packages/types/src/shortify.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import { describe, expect, it } from 'vitest';
import { removeTrailingZeros, shortify } from './shortify.js';

describe('shortify Function', () => {
describe('No suffix needed (|value| < 1,000)', () => {
it('should return the integer part for positive numbers', () => {
expect(shortify(0)).toBe('0');
expect(shortify(999)).toBe('999');
expect(shortify(123.456)).toBe('123'); // floor rounding
});

it('should return the integer part for negative numbers', () => {
expect(shortify(-999)).toBe('-999');
expect(shortify(-123.456)).toBe('-123'); // ceil rounding
});
});

describe('Thousands suffix (K)', () => {
it('should format positive numbers correctly', () => {
expect(shortify(1000)).toBe('1K');
expect(shortify(1500)).toBe('1.5K');
expect(shortify(999999)).toBe('999K');
expect(shortify(1234)).toBe('1.23K');
expect(shortify(12345)).toBe('12.3K');
expect(shortify(123456)).toBe('123K');
});

it('should format negative numbers correctly', () => {
expect(shortify(-1000)).toBe('-1K');
expect(shortify(-1500)).toBe('-1.5K');
expect(shortify(-999999)).toBe('-999K');
expect(shortify(-1234)).toBe('-1.23K');
expect(shortify(-12345)).toBe('-12.3K');
expect(shortify(-123456)).toBe('-123K');
});
});

describe('Millions suffix (M)', () => {
it('should format positive numbers correctly', () => {
expect(shortify(1_000_000)).toBe('1M');
expect(shortify(1_500_000)).toBe('1.5M');
expect(shortify(999_999_999)).toBe('999M');
expect(shortify(1234567)).toBe('1.23M');
expect(shortify(12345678)).toBe('12.3M');
expect(shortify(123456789)).toBe('123M');
});

it('should format negative numbers correctly', () => {
expect(shortify(-1_000_000)).toBe('-1M');
expect(shortify(-1_500_000)).toBe('-1.5M');
expect(shortify(-999_999_999)).toBe('-999M');
expect(shortify(-1234567)).toBe('-1.23M');
expect(shortify(-12345678)).toBe('-12.3M');
expect(shortify(-123456789)).toBe('-123M');
});
});

describe('Billions suffix (B)', () => {
it('should format positive numbers correctly', () => {
expect(shortify(1_000_000_000)).toBe('1B');
expect(shortify(1_500_000_000)).toBe('1.5B');
expect(shortify(999_999_999_999)).toBe('999B');
expect(shortify(1234567890)).toBe('1.23B');
expect(shortify(12345678901)).toBe('12.3B');
expect(shortify(123456789012)).toBe('123B');
});

it('should format negative numbers correctly', () => {
expect(shortify(-1_000_000_000)).toBe('-1B');
expect(shortify(-1_500_000_000)).toBe('-1.5B');
expect(shortify(-999_999_999_999)).toBe('-999B');
expect(shortify(-1234567890)).toBe('-1.23B');
expect(shortify(-12345678901)).toBe('-12.3B');
expect(shortify(-123456789012)).toBe('-123B');
});
});

describe('Trillions suffix (T)', () => {
it('should format positive numbers correctly', () => {
expect(shortify(1_000_000_000_000)).toBe('1T');
expect(shortify(1_500_000_000_000)).toBe('1.5T');
expect(shortify(1234567890123)).toBe('1.23T');
expect(shortify(12345678901234)).toBe('12.3T');
expect(shortify(123456789012345)).toBe('123T');
expect(shortify(1e15)).toBe('1000T'); // Edge case: beyond trillion
});

it('should format negative numbers correctly', () => {
expect(shortify(-1_000_000_000_000)).toBe('-1T');
expect(shortify(-1_500_000_000_000)).toBe('-1.5T');
expect(shortify(-1234567890123)).toBe('-1.23T');
expect(shortify(-12345678901234)).toBe('-12.3T');
expect(shortify(-123456789012345)).toBe('-123T');
expect(shortify(-1e15)).toBe('-1000T'); // Edge case: beyond trillion
});
});

describe('Trailing zeros removal', () => {
it('should remove trailing zeros after decimal', () => {
expect(shortify(1000)).toBe('1K');
expect(shortify(1200)).toBe('1.2K');
expect(shortify(1230)).toBe('1.23K');
expect(shortify(1000000)).toBe('1M');
expect(shortify(1200000)).toBe('1.2M');
expect(shortify(1230000)).toBe('1.23M');
expect(shortify(1000000000)).toBe('1B');
expect(shortify(1200000000)).toBe('1.2B');
expect(shortify(1230000000)).toBe('1.23B');
expect(shortify(1000000000000)).toBe('1T');
expect(shortify(1200000000000)).toBe('1.2T');
expect(shortify(1230000000000)).toBe('1.23T');

expect(shortify(-1000)).toBe('-1K');
expect(shortify(-1200)).toBe('-1.2K');
expect(shortify(-1230)).toBe('-1.23K');
expect(shortify(-1000000)).toBe('-1M');
expect(shortify(-1200000)).toBe('-1.2M');
expect(shortify(-1230000)).toBe('-1.23M');
expect(shortify(-1000000000)).toBe('-1B');
expect(shortify(-1200000000)).toBe('-1.2B');
expect(shortify(-1230000000)).toBe('-1.23B');
expect(shortify(-1000000000000)).toBe('-1T');
expect(shortify(-1200000000000)).toBe('-1.2T');
expect(shortify(-1230000000000)).toBe('-1.23T');
});
});

describe('Very large numbers beyond trillion', () => {
it('should handle numbers larger than 1 trillion', () => {
expect(shortify(1_500_000_000_000)).toBe('1.5T');
expect(shortify(12_345_678_901_234)).toBe('12.3T');
expect(shortify(123_456_789_012_345)).toBe('123T');
expect(shortify(999_999_999_999_999)).toBe('999T');
expect(shortify(1e15)).toBe('1000T');
expect(shortify(-1_500_000_000_000)).toBe('-1.5T');
expect(shortify(-12_345_678_901_234)).toBe('-12.3T');
expect(shortify(-123_456_789_012_345)).toBe('-123T');
expect(shortify(-999_999_999_999_999)).toBe('-999T');
expect(shortify(-1e15)).toBe('-1000T');
});
});

describe('Rounding behavior', () => {
it('should floor positive numbers and ceil negative numbers', () => {
// Positive numbers
expect(shortify(1234)).toBe('1.23K'); // floor to 2 decimals
expect(shortify(12345)).toBe('12.3K'); // floor to 1 decimal
expect(shortify(123456)).toBe('123K'); // floor to 0 decimals

// Negative numbers
expect(shortify(-1234)).toBe('-1.23K'); // ceil to 2 decimals
expect(shortify(-12345)).toBe('-12.3K'); // ceil to 1 decimal
expect(shortify(-123456)).toBe('-123K'); // ceil to 0 decimals
});
});
});

describe('removeTrailingZeros', () => {
it('should remove trailing zeros after decimal point', () => {
expect(removeTrailingZeros('123.45000')).toBe('123.45');
expect(removeTrailingZeros('0.5000')).toBe('0.5');
expect(removeTrailingZeros('3.14159000')).toBe('3.14159');
});

it('should remove decimal point if all decimal digits are zeros', () => {
expect(removeTrailingZeros('123.000')).toBe('123');
expect(removeTrailingZeros('0.0000')).toBe('0');
expect(removeTrailingZeros('456.0')).toBe('456');
});

it('should handle numbers without decimal points', () => {
expect(removeTrailingZeros('123')).toBe('123');
expect(removeTrailingZeros('0')).toBe('0');
expect(removeTrailingZeros('456789')).toBe('456789');
});

it('should handle numbers with no trailing zeros', () => {
expect(removeTrailingZeros('123.45')).toBe('123.45');
expect(removeTrailingZeros('0.5')).toBe('0.5');
expect(removeTrailingZeros('789.123456')).toBe('789.123456');
});

it('should handle negative numbers', () => {
expect(removeTrailingZeros('-123.45000')).toBe('-123.45');
expect(removeTrailingZeros('-0.000')).toBe('-0');
expect(removeTrailingZeros('-456')).toBe('-456');
});

it('should handle empty string gracefully', () => {
expect(removeTrailingZeros('')).toBe('');
});

it('should handle very large numbers', () => {
expect(removeTrailingZeros('12345678901234567890.000000')).toBe('12345678901234567890');
expect(removeTrailingZeros('9876543210.09876543210000')).toBe('9876543210.0987654321');
});

it('should handle numbers with leading zeros', () => {
expect(removeTrailingZeros('000123.45000')).toBe('000123.45');
expect(removeTrailingZeros('000.000')).toBe('000');
expect(removeTrailingZeros('000456')).toBe('000456');
});

it('should handle numbers with no digits after decimal point', () => {
expect(removeTrailingZeros('123.')).toBe('123.');
expect(removeTrailingZeros('-456.')).toBe('-456.');
});
});
Loading

0 comments on commit 955e09a

Please sign in to comment.