mirror of
https://github.com/pezkuwichain/pezkuwi-ui.git
synced 2026-04-29 12:37:58 +00:00
Add ui-util formatting & util (#76)
* Add ui-util formatting & util * Update README
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
// Copyright 2017-2019 @polkadot/ui-util authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
import formatBalance from './formatBalance';
|
||||
|
||||
describe('formatBalance', () => {
|
||||
const TESTVAL = new BN('123456789000');
|
||||
|
||||
it('formats empty to 0', () => {
|
||||
expect(formatBalance()).toEqual('0');
|
||||
expect(formatBalance('0')).toEqual('0');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=15)', () => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, true, 15)
|
||||
).toEqual('123.456µ Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=12)', () => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, true, 12)
|
||||
).toEqual('123.456m Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=12, no SI)', () => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, false, 12)
|
||||
).toEqual('123.456');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=9)', () => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, true, 9)
|
||||
).toEqual('123.456 Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (decimals=6)', () => {
|
||||
expect(
|
||||
formatBalance(TESTVAL, true, 6)
|
||||
).toEqual('123.456k Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 * 10 (decimals=12)', () => {
|
||||
expect(
|
||||
formatBalance(TESTVAL.muln(10), true, 12)
|
||||
).toEqual('1.234 Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 * 100 (decimals=12)', () => {
|
||||
expect(
|
||||
formatBalance(TESTVAL.muln(100), true, 12)
|
||||
).toEqual('12.345 Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 * 1000 (decimals=12)', () => {
|
||||
expect(
|
||||
formatBalance(TESTVAL.muln(1000), true, 12)
|
||||
).toEqual('123.456 Unit');
|
||||
});
|
||||
|
||||
describe('findSi', () => {
|
||||
it('finds the SI value', () => {
|
||||
expect(
|
||||
formatBalance.findSi('k')
|
||||
).toEqual({ power: 3, value: 'k', text: 'Kilo' });
|
||||
});
|
||||
|
||||
it('returns default on not found', () => {
|
||||
expect(
|
||||
formatBalance.findSi('blah')
|
||||
).toEqual({ power: 0, value: '-', text: 'Unit' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('defaults', () => {
|
||||
it('returns defaults', () => {
|
||||
expect(formatBalance.getDefaults()).toEqual({
|
||||
decimals: 0,
|
||||
unit: 'Unit'
|
||||
});
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (defaultDecimals=12)', () => {
|
||||
formatBalance.setDefaults({ decimals: 12 });
|
||||
|
||||
expect(
|
||||
formatBalance(TESTVAL)
|
||||
).toEqual('123.456m Unit');
|
||||
});
|
||||
|
||||
it('formats 123,456,789,000 (defaultUnit=TEST)', () => {
|
||||
formatBalance.setDefaults({ unit: 'TEST' });
|
||||
|
||||
expect(
|
||||
formatBalance(TESTVAL)
|
||||
).toEqual('123.456m TEST');
|
||||
});
|
||||
});
|
||||
|
||||
it('returns options for dropdown', () => {
|
||||
formatBalance.setDefaults({ decimals: 0, unit: 'TEST' });
|
||||
|
||||
expect(
|
||||
formatBalance.getOptions()
|
||||
).toEqual([
|
||||
{ power: 0, value: '-', text: 'TEST' },
|
||||
{ power: 3, value: 'k', text: 'Kilo' },
|
||||
{ power: 6, value: 'M', text: 'Mega' },
|
||||
{ power: 9, value: 'G', text: 'Giga' },
|
||||
{ power: 12, value: 'T', text: 'Tera' },
|
||||
{ power: 15, value: 'P', text: 'Peta' },
|
||||
{ power: 18, value: 'E', text: 'Exa' },
|
||||
{ power: 21, value: 'Z', text: 'Zeta' },
|
||||
{ power: 24, value: 'Y', text: 'Yotta' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright 2017-2019 @polkadot/ui-util authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { isUndefined } from '@polkadot/util';
|
||||
|
||||
import formatDecimal from './formatDecimal';
|
||||
|
||||
type SiDef = {
|
||||
power: number,
|
||||
text: string,
|
||||
value: string
|
||||
};
|
||||
|
||||
type Defaults = {
|
||||
decimals?: number,
|
||||
unit?: string
|
||||
};
|
||||
|
||||
interface BalanceFormatter {
|
||||
(input?: number | string | BN, withSi?: boolean, decimals?: number): string;
|
||||
findSi (type: string): SiDef;
|
||||
getDefaults (): Defaults;
|
||||
getOptions (decimals?: number): Array<SiDef>;
|
||||
setDefaults (defaults: Defaults): void;
|
||||
}
|
||||
|
||||
const SI_MID = 8;
|
||||
const DEFAULT_DECIMALS = 0;
|
||||
const DEFAULT_UNIT = 'Unit';
|
||||
|
||||
let defaultDecimals = DEFAULT_DECIMALS;
|
||||
let defaultUnit = DEFAULT_UNIT;
|
||||
|
||||
const SI: Array<SiDef> = [
|
||||
{ power: -24, value: 'y', text: 'yocto' },
|
||||
{ power: -21, value: 'z', text: 'zepto' },
|
||||
{ power: -18, value: 'a', text: 'atto' },
|
||||
{ power: -15, value: 'f', text: 'femto' },
|
||||
{ power: -12, value: 'p', text: 'pico' },
|
||||
{ power: -9, value: 'n', text: 'nano' },
|
||||
{ power: -6, value: 'µ', text: 'micro' },
|
||||
{ power: -3, value: 'm', text: 'milli' },
|
||||
{ power: 0, value: '-', text: defaultUnit }, // position 8
|
||||
{ power: 3, value: 'k', text: 'Kilo' },
|
||||
{ power: 6, value: 'M', text: 'Mega' },
|
||||
{ power: 9, value: 'G', text: 'Giga' },
|
||||
{ power: 12, value: 'T', text: 'Tera' },
|
||||
{ power: 15, value: 'P', text: 'Peta' },
|
||||
{ power: 18, value: 'E', text: 'Exa' },
|
||||
{ power: 21, value: 'Z', text: 'Zeta' },
|
||||
{ power: 24, value: 'Y', text: 'Yotta' }
|
||||
];
|
||||
|
||||
export function calcSi (text: string, decimals: number = defaultDecimals): SiDef {
|
||||
return SI[(SI_MID - 1) + Math.ceil((text.length - decimals) / 3)] || SI[SI.length - 1];
|
||||
}
|
||||
|
||||
// Formats a string/number with <prefix>.<postfix><type> notation
|
||||
function _formatBalance (input?: number | string | BN, withSi: boolean = true, decimals: number = defaultDecimals): string {
|
||||
const text = (input || '').toString();
|
||||
|
||||
if (text.length === 0 || text === '0') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
// NOTE We start at midpoint (8) minus 1 - this means that values display as
|
||||
// 123.456 instead of 0.123k (so always 6 relevant). Additionally we us ceil
|
||||
// so there are at most 3 decimal before the decimal seperator
|
||||
const si = calcSi(text, decimals);
|
||||
const mid = text.length - (decimals + si.power);
|
||||
const prefix = text.substr(0, mid);
|
||||
const postfix = `${text.substr(mid)}000`.substr(0, 3);
|
||||
const units = withSi
|
||||
? (
|
||||
si.value === '-'
|
||||
? ` ${si.text}`
|
||||
: `${si.value} ${SI[SI_MID].text}`
|
||||
)
|
||||
: '';
|
||||
|
||||
return `${formatDecimal(prefix || '0')}.${postfix}${units}`;
|
||||
}
|
||||
|
||||
const formatBalance = _formatBalance as BalanceFormatter;
|
||||
|
||||
// Given a SI type (e.g. k, m, Y) find the SI definition
|
||||
formatBalance.findSi = (type: string): SiDef => {
|
||||
return SI.find(({ value }) => value === type) || SI[SI_MID];
|
||||
};
|
||||
|
||||
formatBalance.getDefaults = (): Defaults => {
|
||||
return {
|
||||
decimals: defaultDecimals,
|
||||
unit: defaultUnit
|
||||
};
|
||||
};
|
||||
|
||||
// get allowable options to display in a dropdown
|
||||
formatBalance.getOptions = (decimals: number = defaultDecimals): Array<SiDef> => {
|
||||
return SI.filter(({ power }) =>
|
||||
power < 0
|
||||
? (decimals + power) >= 0
|
||||
: true
|
||||
);
|
||||
};
|
||||
|
||||
// Sets the default decimals to use for formatting (ui-wide)
|
||||
formatBalance.setDefaults = ({ decimals, unit }: Defaults): void => {
|
||||
defaultDecimals = isUndefined(decimals) ? defaultDecimals : decimals;
|
||||
defaultUnit = isUndefined(unit) ? defaultUnit : unit;
|
||||
|
||||
SI[SI_MID].text = defaultUnit;
|
||||
};
|
||||
|
||||
export default formatBalance;
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2017-2019 @polkadot/ui-util authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import formatDecimal from './formatDecimal';
|
||||
|
||||
describe('formatDecimal', () => {
|
||||
it('formats decimals in number groupings', () => {
|
||||
expect(formatDecimal('12345')).toEqual('12,345');
|
||||
});
|
||||
|
||||
it('formats decimal-only in number groupings', () => {
|
||||
expect(formatDecimal('test6789')).toEqual('6,789');
|
||||
});
|
||||
|
||||
it('returns input for non-decimal', () => {
|
||||
expect(formatDecimal('test')).toEqual('test');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017-2019 @polkadot/ui-util authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
const NUMBER_REGEX = new RegExp('(\\d+?)(?=(\\d{3})+(?!\\d)|$)', 'g');
|
||||
|
||||
export default function formatDecimal (value: string): string {
|
||||
const matched = value.match(NUMBER_REGEX);
|
||||
|
||||
return matched
|
||||
? matched.join(',')
|
||||
: value;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2017-2019 @polkadot/ui-util authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Compact, UInt } from '@polkadot/types';
|
||||
|
||||
import formatElapsed from './formatElapsed';
|
||||
|
||||
describe('formatElapsed', () => {
|
||||
const start = 12345678;
|
||||
const now = new Date(12345678);
|
||||
|
||||
it('formats a Date', () => {
|
||||
expect(
|
||||
formatElapsed(now, new Date(start + 9700))
|
||||
).toEqual('9.7s');
|
||||
});
|
||||
|
||||
it('formats a BN', () => {
|
||||
expect(
|
||||
formatElapsed(now, new BN(start + 42700))
|
||||
).toEqual('42s');
|
||||
});
|
||||
|
||||
it('formats a Compact', () => {
|
||||
const C = Compact.with(UInt);
|
||||
|
||||
expect(
|
||||
formatElapsed(now, new C(start + (5.3 * 60000)))
|
||||
).toEqual('5m');
|
||||
});
|
||||
|
||||
it('formats a number', () => {
|
||||
expect(
|
||||
formatElapsed(now, start + (42 * 60 * 60000))
|
||||
).toEqual('42h');
|
||||
});
|
||||
|
||||
it('formats defaults', () => {
|
||||
expect(
|
||||
formatElapsed()
|
||||
).toEqual('0.0s');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2017-2019 @polkadot/ui-util authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Compact } from '@polkadot/types';
|
||||
|
||||
function getValue (value?: BN | Compact | Date | number | null): number {
|
||||
if (value instanceof Compact) {
|
||||
return getValue(value.unwrap());
|
||||
} else if (value instanceof Date) {
|
||||
return getValue(value.getTime());
|
||||
} else if (value instanceof BN) {
|
||||
return getValue(value.toNumber());
|
||||
}
|
||||
|
||||
return value || 0;
|
||||
}
|
||||
|
||||
export default function formatElapsed (now?: Date | null, value?: BN | Compact | Date | number | null): string {
|
||||
const tsNow = (now && now.getTime()) || 0;
|
||||
const tsValue = getValue(value);
|
||||
let display = '0.0s';
|
||||
|
||||
if (tsNow && tsValue) {
|
||||
const elapsed = Math.max(Math.abs(tsNow - tsValue), 0) / 1000;
|
||||
|
||||
if (elapsed < 15) {
|
||||
display = `${elapsed.toFixed(1)}s`;
|
||||
} else if (elapsed < 60) {
|
||||
display = `${elapsed | 0}s`;
|
||||
} else if (elapsed < 3600) {
|
||||
display = `${elapsed / 60 | 0}m`;
|
||||
} else {
|
||||
display = `${elapsed / 3600 | 0}h`;
|
||||
}
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2017-2019 @polkadot/ui-util authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Compact } from '@polkadot/types';
|
||||
import { bnToBn } from '@polkadot/util';
|
||||
|
||||
import formatDecimal from './formatDecimal';
|
||||
|
||||
export default function formatNumber (_value?: Compact | BN | number | null): string {
|
||||
if (!_value) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
const value = _value instanceof Compact
|
||||
? _value.toBn()
|
||||
: bnToBn(_value);
|
||||
|
||||
return formatDecimal(value.toString());
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright 2017-2019 @polkadot/ui-util authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
export { default as formatBalance } from './formatBalance';
|
||||
export { default as formatDecimal } from './formatDecimal';
|
||||
export { default as formatElapsed } from './formatElapsed';
|
||||
export { default as formatNumber } from './formatNumber';
|
||||
export { default as isTestChain } from './isTestChain';
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2017-2019 @polkadot/ui-util authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import isTestChain from './isTestChain';
|
||||
|
||||
describe('isTestChain', () => {
|
||||
it('enables test environment when chain specification matches text of dev or loc(al)', () => {
|
||||
const validTestModeChainSpecsWithDev = ['Development'];
|
||||
const validTestModeChainSpecsWithLoc = ['Local Testnet'];
|
||||
|
||||
validTestModeChainSpecsWithDev.concat(validTestModeChainSpecsWithLoc).forEach((s) =>
|
||||
expect(isTestChain(s)).toEqual(true)
|
||||
);
|
||||
});
|
||||
|
||||
it('disables keyring test mode when chain specification is not a test mode or undefined or number type', () => {
|
||||
const invalidTestModeChainSpecs = ['dev', 'local', 'development', 'PoC-1 Testnet', 'Staging Testnet', 'future PoC-2 Testnet', 'a pocadot?', undefined];
|
||||
|
||||
invalidTestModeChainSpecs.forEach((s) =>
|
||||
expect(isTestChain(s)).toEqual(false)
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017-2019 @polkadot/ui-util authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
const re = new RegExp('^(Development|Local Testnet)$');
|
||||
|
||||
export default function isTestChain (chain?: string | null): boolean {
|
||||
if (!chain) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!re.test(chain.toString());
|
||||
}
|
||||
Reference in New Issue
Block a user