Commit 80f584a7 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '201999-add-iec-digital-formats' into 'master'

Add IEC format for digital units

See merge request gitlab-org/gitlab!26045
parents bb93702a dd3307ff
...@@ -117,3 +117,23 @@ export const scaledSIFormatter = (unit = '', prefixOffset = 0) => { ...@@ -117,3 +117,23 @@ export const scaledSIFormatter = (unit = '', prefixOffset = 0) => {
return scaledFormatter(units); return scaledFormatter(units);
}; };
/**
* Returns a function that formats a number scaled using SI units notation.
*/
export const scaledBinaryFormatter = (unit = '', prefixOffset = 0) => {
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
const multiplicative = ['Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
const symbols = ['', ...multiplicative];
const units = symbols.slice(prefixOffset).map(prefix => {
return `${prefix}${unit}`;
});
if (!units.length) {
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
throw new RangeError('The unit cannot be converted, please try a different scale');
}
return scaledFormatter(units, 1024);
};
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { suffixFormatter, scaledSIFormatter, numberFormatter } from './formatter_factory'; import {
suffixFormatter,
scaledSIFormatter,
scaledBinaryFormatter,
numberFormatter,
} from './formatter_factory';
/** /**
* Supported formats * Supported formats
*
* Based on:
*
* https://tc39.es/proposal-unified-intl-numberformat/section6/locales-currencies-tz_proposed_out.html#sec-issanctionedsimpleunitidentifier
*/ */
export const SUPPORTED_FORMATS = { export const SUPPORTED_FORMATS = {
// Number // Number
...@@ -13,15 +22,23 @@ export const SUPPORTED_FORMATS = { ...@@ -13,15 +22,23 @@ export const SUPPORTED_FORMATS = {
// Duration // Duration
seconds: 'seconds', seconds: 'seconds',
miliseconds: 'miliseconds', milliseconds: 'milliseconds',
// Digital // Digital (Metric)
bytes: 'bytes', decimalBytes: 'decimalBytes',
kilobytes: 'kilobytes', kilobytes: 'kilobytes',
megabytes: 'megabytes', megabytes: 'megabytes',
gigabytes: 'gigabytes', gigabytes: 'gigabytes',
terabytes: 'terabytes', terabytes: 'terabytes',
petabytes: 'petabytes', petabytes: 'petabytes',
// Digital (IEC)
bytes: 'bytes',
kibibytes: 'kibibytes',
mebibytes: 'mebibytes',
gibibytes: 'gibibytes',
tebibytes: 'tebibytes',
pebibytes: 'pebibytes',
}; };
/** /**
...@@ -32,6 +49,7 @@ export const SUPPORTED_FORMATS = { ...@@ -32,6 +49,7 @@ export const SUPPORTED_FORMATS = {
*/ */
export const getFormatter = (format = SUPPORTED_FORMATS.number) => { export const getFormatter = (format = SUPPORTED_FORMATS.number) => {
// Number // Number
if (format === SUPPORTED_FORMATS.number) { if (format === SUPPORTED_FORMATS.number) {
/** /**
* Formats a number * Formats a number
...@@ -70,6 +88,7 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => { ...@@ -70,6 +88,7 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => {
} }
// Durations // Durations
if (format === SUPPORTED_FORMATS.seconds) { if (format === SUPPORTED_FORMATS.seconds) {
/** /**
* Formats a number of seconds * Formats a number of seconds
...@@ -82,9 +101,9 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => { ...@@ -82,9 +101,9 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => {
*/ */
return suffixFormatter(s__('Units|s')); return suffixFormatter(s__('Units|s'));
} }
if (format === SUPPORTED_FORMATS.miliseconds) { if (format === SUPPORTED_FORMATS.milliseconds) {
/** /**
* Formats a number of miliseconds with ms as units * Formats a number of milliseconds with ms as units
* *
* @function * @function
* @param {Number} value - Number to format, `1` is formatted as `1ms` * @param {Number} value - Number to format, `1` is formatted as `1ms`
...@@ -95,8 +114,9 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => { ...@@ -95,8 +114,9 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => {
return suffixFormatter(s__('Units|ms')); return suffixFormatter(s__('Units|ms'));
} }
// Digital // Digital (Metric)
if (format === SUPPORTED_FORMATS.bytes) {
if (format === SUPPORTED_FORMATS.decimalBytes) {
/** /**
* Formats a number of bytes scaled up to larger digital * Formats a number of bytes scaled up to larger digital
* units for larger numbers. * units for larger numbers.
...@@ -162,6 +182,76 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => { ...@@ -162,6 +182,76 @@ export const getFormatter = (format = SUPPORTED_FORMATS.number) => {
*/ */
return scaledSIFormatter('B', 5); return scaledSIFormatter('B', 5);
} }
// Digital (IEC)
if (format === SUPPORTED_FORMATS.bytes) {
/**
* Formats a number of bytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1B`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B');
}
if (format === SUPPORTED_FORMATS.kibibytes) {
/**
* Formats a number of kilobytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1kB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 1);
}
if (format === SUPPORTED_FORMATS.mebibytes) {
/**
* Formats a number of megabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1MB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 2);
}
if (format === SUPPORTED_FORMATS.gibibytes) {
/**
* Formats a number of gigabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 3);
}
if (format === SUPPORTED_FORMATS.tebibytes) {
/**
* Formats a number of terabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1GB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 4);
}
if (format === SUPPORTED_FORMATS.pebibytes) {
/**
* Formats a number of petabytes scaled up to larger digital
* units for larger numbers.
*
* @function
* @param {Number} value - Number to format, `1` is formatted as `1PB`
* @param {Number} fractionDigits - number of precision decimals
*/
return scaledBinaryFormatter('B', 5);
}
// Fail so client library addresses issue // Fail so client library addresses issue
throw TypeError(`${format} is not a valid number format`); throw TypeError(`${format} is not a valid number format`);
}; };
...@@ -3,109 +3,149 @@ import { getFormatter, SUPPORTED_FORMATS } from '~/lib/utils/unit_format'; ...@@ -3,109 +3,149 @@ import { getFormatter, SUPPORTED_FORMATS } from '~/lib/utils/unit_format';
describe('unit_format', () => { describe('unit_format', () => {
describe('when a supported format is provided, the returned function formats', () => { describe('when a supported format is provided, the returned function formats', () => {
it('numbers, by default', () => { it('numbers, by default', () => {
expect(getFormatter()(1)).toEqual('1'); expect(getFormatter()(1)).toBe('1');
}); });
it('numbers', () => { it('numbers', () => {
const formatNumber = getFormatter(SUPPORTED_FORMATS.number); const formatNumber = getFormatter(SUPPORTED_FORMATS.number);
expect(formatNumber(1)).toEqual('1'); expect(formatNumber(1)).toBe('1');
expect(formatNumber(100)).toEqual('100'); expect(formatNumber(100)).toBe('100');
expect(formatNumber(1000)).toEqual('1,000'); expect(formatNumber(1000)).toBe('1,000');
expect(formatNumber(10000)).toEqual('10,000'); expect(formatNumber(10000)).toBe('10,000');
expect(formatNumber(1000000)).toEqual('1,000,000'); expect(formatNumber(1000000)).toBe('1,000,000');
}); });
it('percent', () => { it('percent', () => {
const formatPercent = getFormatter(SUPPORTED_FORMATS.percent); const formatPercent = getFormatter(SUPPORTED_FORMATS.percent);
expect(formatPercent(1)).toEqual('100%'); expect(formatPercent(1)).toBe('100%');
expect(formatPercent(1, 2)).toEqual('100.00%'); expect(formatPercent(1, 2)).toBe('100.00%');
expect(formatPercent(0.1)).toEqual('10%'); expect(formatPercent(0.1)).toBe('10%');
expect(formatPercent(0.5)).toEqual('50%'); expect(formatPercent(0.5)).toBe('50%');
expect(formatPercent(0.888888)).toEqual('89%'); expect(formatPercent(0.888888)).toBe('89%');
expect(formatPercent(0.888888, 2)).toEqual('88.89%'); expect(formatPercent(0.888888, 2)).toBe('88.89%');
expect(formatPercent(0.888888, 5)).toEqual('88.88880%'); expect(formatPercent(0.888888, 5)).toBe('88.88880%');
expect(formatPercent(2)).toEqual('200%'); expect(formatPercent(2)).toBe('200%');
expect(formatPercent(10)).toEqual('1,000%'); expect(formatPercent(10)).toBe('1,000%');
}); });
it('percentunit', () => { it('percentunit', () => {
const formatPercentHundred = getFormatter(SUPPORTED_FORMATS.percentHundred); const formatPercentHundred = getFormatter(SUPPORTED_FORMATS.percentHundred);
expect(formatPercentHundred(1)).toEqual('1%'); expect(formatPercentHundred(1)).toBe('1%');
expect(formatPercentHundred(1, 2)).toEqual('1.00%'); expect(formatPercentHundred(1, 2)).toBe('1.00%');
expect(formatPercentHundred(88.8888)).toEqual('89%'); expect(formatPercentHundred(88.8888)).toBe('89%');
expect(formatPercentHundred(88.8888, 2)).toEqual('88.89%'); expect(formatPercentHundred(88.8888, 2)).toBe('88.89%');
expect(formatPercentHundred(88.8888, 5)).toEqual('88.88880%'); expect(formatPercentHundred(88.8888, 5)).toBe('88.88880%');
expect(formatPercentHundred(100)).toEqual('100%'); expect(formatPercentHundred(100)).toBe('100%');
expect(formatPercentHundred(100, 2)).toEqual('100.00%'); expect(formatPercentHundred(100, 2)).toBe('100.00%');
expect(formatPercentHundred(200)).toEqual('200%'); expect(formatPercentHundred(200)).toBe('200%');
expect(formatPercentHundred(1000)).toEqual('1,000%'); expect(formatPercentHundred(1000)).toBe('1,000%');
}); });
it('seconds', () => { it('seconds', () => {
expect(getFormatter(SUPPORTED_FORMATS.seconds)(1)).toEqual('1s'); expect(getFormatter(SUPPORTED_FORMATS.seconds)(1)).toBe('1s');
}); });
it('miliseconds', () => { it('milliseconds', () => {
const formatMiliseconds = getFormatter(SUPPORTED_FORMATS.miliseconds); const formatMilliseconds = getFormatter(SUPPORTED_FORMATS.milliseconds);
expect(formatMiliseconds(1)).toEqual('1ms'); expect(formatMilliseconds(1)).toBe('1ms');
expect(formatMiliseconds(100)).toEqual('100ms'); expect(formatMilliseconds(100)).toBe('100ms');
expect(formatMiliseconds(1000)).toEqual('1,000ms'); expect(formatMilliseconds(1000)).toBe('1,000ms');
expect(formatMiliseconds(10000)).toEqual('10,000ms'); expect(formatMilliseconds(10000)).toBe('10,000ms');
expect(formatMiliseconds(1000000)).toEqual('1,000,000ms'); expect(formatMilliseconds(1000000)).toBe('1,000,000ms');
}); });
it('bytes', () => { it('decimalBytes', () => {
const formatBytes = getFormatter(SUPPORTED_FORMATS.bytes); const formatDecimalBytes = getFormatter(SUPPORTED_FORMATS.decimalBytes);
expect(formatBytes(1)).toEqual('1B'); expect(formatDecimalBytes(1)).toBe('1B');
expect(formatBytes(1, 1)).toEqual('1.0B'); expect(formatDecimalBytes(1, 1)).toBe('1.0B');
expect(formatBytes(10)).toEqual('10B'); expect(formatDecimalBytes(10)).toBe('10B');
expect(formatBytes(10 ** 2)).toEqual('100B'); expect(formatDecimalBytes(10 ** 2)).toBe('100B');
expect(formatBytes(10 ** 3)).toEqual('1kB'); expect(formatDecimalBytes(10 ** 3)).toBe('1kB');
expect(formatBytes(10 ** 4)).toEqual('10kB'); expect(formatDecimalBytes(10 ** 4)).toBe('10kB');
expect(formatBytes(10 ** 5)).toEqual('100kB'); expect(formatDecimalBytes(10 ** 5)).toBe('100kB');
expect(formatBytes(10 ** 6)).toEqual('1MB'); expect(formatDecimalBytes(10 ** 6)).toBe('1MB');
expect(formatBytes(10 ** 7)).toEqual('10MB'); expect(formatDecimalBytes(10 ** 7)).toBe('10MB');
expect(formatBytes(10 ** 8)).toEqual('100MB'); expect(formatDecimalBytes(10 ** 8)).toBe('100MB');
expect(formatBytes(10 ** 9)).toEqual('1GB'); expect(formatDecimalBytes(10 ** 9)).toBe('1GB');
expect(formatBytes(10 ** 10)).toEqual('10GB'); expect(formatDecimalBytes(10 ** 10)).toBe('10GB');
expect(formatBytes(10 ** 11)).toEqual('100GB'); expect(formatDecimalBytes(10 ** 11)).toBe('100GB');
}); });
it('kilobytes', () => { it('kilobytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.kilobytes)(1)).toEqual('1kB'); expect(getFormatter(SUPPORTED_FORMATS.kilobytes)(1)).toBe('1kB');
expect(getFormatter(SUPPORTED_FORMATS.kilobytes)(1, 1)).toEqual('1.0kB'); expect(getFormatter(SUPPORTED_FORMATS.kilobytes)(1, 1)).toBe('1.0kB');
}); });
it('megabytes', () => { it('megabytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.megabytes)(1)).toEqual('1MB'); expect(getFormatter(SUPPORTED_FORMATS.megabytes)(1)).toBe('1MB');
expect(getFormatter(SUPPORTED_FORMATS.megabytes)(1, 1)).toEqual('1.0MB'); expect(getFormatter(SUPPORTED_FORMATS.megabytes)(1, 1)).toBe('1.0MB');
}); });
it('gigabytes', () => { it('gigabytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.gigabytes)(1)).toEqual('1GB'); expect(getFormatter(SUPPORTED_FORMATS.gigabytes)(1)).toBe('1GB');
expect(getFormatter(SUPPORTED_FORMATS.gigabytes)(1, 1)).toEqual('1.0GB'); expect(getFormatter(SUPPORTED_FORMATS.gigabytes)(1, 1)).toBe('1.0GB');
}); });
it('terabytes', () => { it('terabytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.terabytes)(1)).toEqual('1TB'); expect(getFormatter(SUPPORTED_FORMATS.terabytes)(1)).toBe('1TB');
expect(getFormatter(SUPPORTED_FORMATS.terabytes)(1, 1)).toEqual('1.0TB'); expect(getFormatter(SUPPORTED_FORMATS.terabytes)(1, 1)).toBe('1.0TB');
}); });
it('petabytes', () => { it('petabytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.petabytes)(1)).toEqual('1PB'); expect(getFormatter(SUPPORTED_FORMATS.petabytes)(1)).toBe('1PB');
expect(getFormatter(SUPPORTED_FORMATS.petabytes)(1, 1)).toEqual('1.0PB'); expect(getFormatter(SUPPORTED_FORMATS.petabytes)(1, 1)).toBe('1.0PB');
});
it('bytes', () => {
const formatBytes = getFormatter(SUPPORTED_FORMATS.bytes);
expect(formatBytes(1)).toBe('1B');
expect(formatBytes(1, 1)).toBe('1.0B');
expect(formatBytes(10)).toBe('10B');
expect(formatBytes(100)).toBe('100B');
expect(formatBytes(1000)).toBe('1,000B');
expect(formatBytes(1 * 1024)).toBe('1KiB');
expect(formatBytes(1 * 1024 ** 2)).toBe('1MiB');
expect(formatBytes(1 * 1024 ** 3)).toBe('1GiB');
});
it('kibibytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.kibibytes)(1)).toBe('1KiB');
expect(getFormatter(SUPPORTED_FORMATS.kibibytes)(1, 1)).toBe('1.0KiB');
});
it('mebibytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.mebibytes)(1)).toBe('1MiB');
expect(getFormatter(SUPPORTED_FORMATS.mebibytes)(1, 1)).toBe('1.0MiB');
});
it('gibibytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.gibibytes)(1)).toBe('1GiB');
expect(getFormatter(SUPPORTED_FORMATS.gibibytes)(1, 1)).toBe('1.0GiB');
});
it('tebibytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.tebibytes)(1)).toBe('1TiB');
expect(getFormatter(SUPPORTED_FORMATS.tebibytes)(1, 1)).toBe('1.0TiB');
});
it('pebibytes', () => {
expect(getFormatter(SUPPORTED_FORMATS.pebibytes)(1)).toBe('1PiB');
expect(getFormatter(SUPPORTED_FORMATS.pebibytes)(1, 1)).toBe('1.0PiB');
}); });
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment