Skip to content
This repository has been archived by the owner on Sep 25, 2024. It is now read-only.

Commit

Permalink
feat(UIKIT-1151): Добавлена утилита для форматирования валюты (#3)
Browse files Browse the repository at this point in the history
* feat(UIKIT-1151): Добавлена утилита для форматирования валюты

---------

Co-authored-by: Andrey Zaitsev <zaytsev_aa@astralnalog.ru>
  • Loading branch information
Cabagemage and Andrey Zaitsev authored Sep 20, 2024
1 parent bc8600f commit b17dfbd
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 3 deletions.
4 changes: 4 additions & 0 deletions .cspell-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ webp
регулярку
ресайзе
Релизнуть
Рантайм
рантайм
рантайме
Рантайме
релизятся
рендера
рендере
Expand Down
39 changes: 38 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ Table of contents
- [declensionYear](#declensionyear)
- [phone](#phone)
- [formatPhoneToView](#formatphonetoview)

- [number](#number)
- [formatNumberToCurrency](#formatNumberToCurrency)
---

# Installation
Expand Down Expand Up @@ -245,3 +246,39 @@ formatPhoneToView();
```

---

# number

Утилиты и функции для работы с числами

## formatNumberToCurrency

Форматирование чисел или строк в формат валюты

```ts
import { formatNumberToCurrency } from '@astral/utils';

// "10 000 ₽"
formatNumberToCurrency({ amount: '10000' });

// "100 ₽"
formatNumberToCurrency({ amount: 100 });

// "0 ₽"
formatNumberToCurrency({ amount: 0 });

// "Бесплатно"
formatNumberToCurrency({
amount: 0,
isTextInsteadOfZeroFormat: true,
});

// "Даром"
formatNumberToCurrency({
amount: 0,
isTextInsteadOfZeroFormat: true,
zeroSumPlaceholder: "Даром",
});
```

---
10 changes: 8 additions & 2 deletions package/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,11 @@ export { addDays, addMonths, addYears, isDate } from './date';

export { formatPhoneToView } from './phone';

export { declensionDay, declensionMonth, declensionOfWords, declensionYear } from './declension';

export {
declensionDay,
declensionMonth,
declensionOfWords,
declensionYear,
} from './declension';

export { formatNumberToCurrency } from './number';
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { describe, expect, it } from 'vitest';

import { formatNumberToCurrency } from './formatNumberToCurrency';

const consoleMock = vi.spyOn(console, 'error');

// Если использовать обычные пробелы, то тест не проходит
const numbersToFormatCases = [
{ expectedValue: '100 ₽', numberToFormat: 100 },
{ expectedValue: '1 000 ₽', numberToFormat: 1000 },
{ expectedValue: '10 000 ₽', numberToFormat: 10000 },
{ expectedValue: '565 825 ₽', numberToFormat: 565825 },
];

const notSafeInteger = '999999999999999999999999999999999';

const incorrectCases = [null, undefined, false, true, notSafeInteger];

describe('formatNumberToCurrency', () => {
it.each(numbersToFormatCases)(
'Вернётся строка $expectedValue, если будет передано число $numberToFormat',
({ expectedValue, numberToFormat }) => {
const result = formatNumberToCurrency({
amount: numberToFormat,
});

expect(expectedValue).toBe(result);
},
);

it('Вернётся кастомный текст, если переданное число - это 0', () => {
const value = 0;
const expectedPlaceholderMessage = 'Даром';

const result = formatNumberToCurrency({
amount: value,
isTextInsteadOfZeroFormat: true,
zeroSumPlaceholder: 'Даром',
});

expect(result).toEqual(expectedPlaceholderMessage);
});

it('Вернётся отформатированное значение, если amount передан в качестве строки', () => {
const value = '100';
const expectedCurrencyFormat = '100\u00A0₽';

const result = formatNumberToCurrency({
amount: value,
});

expect(result).toEqual(expectedCurrencyFormat);
});

it('Вернётся "0 ₽", если передано число 0', () => {
const zeroValue = 0;
const expectedCurrencyFormat = '0\u00A0₽';

const result = formatNumberToCurrency({
amount: zeroValue,
});

expect(result).toEqual(expectedCurrencyFormat);
});

it('Вернется текст "Бесплатно", если передан параметр для отображения текста', () => {
const zeroValue = 0;
const expectedCurrencyFormat = 'Бесплатно';

const result = formatNumberToCurrency({
amount: zeroValue,
isTextInsteadOfZeroFormat: true,
});

expect(result).toEqual(expectedCurrencyFormat);
});

it.each(incorrectCases)(
'В консоли появится сообщение о неподходящем формате переданных данных, если передано %s',
(value) => {
formatNumberToCurrency({
// @ts-expect-error Здесь используется ts-ignore для проверки того, что в случае возникновения проблем в рантайме, мы не столкнемся с неожиданным поведением
amount: value,
isTextInsteadOfZeroFormat: true,
});

expect(consoleMock).toHaveBeenLastCalledWith(
'formatNumberToCurrency: значение должно быть безопасным целым числом',
);
},
);

it.each(incorrectCases)('Вернется undefined, если передано %s', (value) => {
const result = formatNumberToCurrency({
// @ts-expect-error Здесь используется ts-ignore для проверки того, что в случае возникновения проблем в рантайме, мы не столкнемся с неожиданным поведением
amount: value,
});

expect(result).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
type FormatNumberToCurrencyParams = {
amount: number | string;
currencyCode?: 'RUB';
isTextInsteadOfZeroFormat?: boolean;
zeroSumPlaceholder?: string;
};

/**
* Форматирует число в формат валюты.
*
* @param params - Параметры для форматирования.
*
* @param params.amount - Сумма в числовом или строковом формате.
*
* @param params.currencyCode - Код валюты в формате [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217#List_of_ISO_4217_currency_codes).
*
* @param params.isTextInsteadOfZeroFormat - Флаг для замены отображения "0 ₽" на текст. По умолчанию "Бесплатно".
*
* @param params.zeroSumPlaceholder - Текст, который должен отображаться, если сумма равна "0". По умолчанию "Бесплатно".
*
* @example
* formatCurrency({ amount: 1000 }); // "1 000 ₽"
* formatCurrency({ amount: 0, zeroSumPlaceholder: "Даром", isFreeTextInsteadOfDefaultFormat: true }); // "Даром"
* formatCurrency({ amount: 0 }); // "0 ₽"
* formatCurrency({ amount: 10000, isFreeTextInsteadOfDefaultFormat: true }); // "10 000 ₽"
* formatNumberToCurrency({amount: 10000, currencyCode: "USD"}) // "10 000 $"
*/
export const formatNumberToCurrency = (
params: FormatNumberToCurrencyParams,
) => {
const {
amount,
isTextInsteadOfZeroFormat,
currencyCode = 'RUB',
zeroSumPlaceholder = 'Бесплатно',
} = params;

const preparedAmount =
// Если передана строка, то удаляем из неё пробелы
typeof amount === 'string' ? Number(amount.replace(/\s+/g, '')) : amount;

// Защита на случай, если передано небезопасное целое число, либо в amount попадает не число (например, в рантайме)
if (!Number.isSafeInteger(preparedAmount)) {
console.error(
'formatNumberToCurrency: значение должно быть безопасным целым числом',
);

return;
}

const formatter = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: currencyCode,
minimumFractionDigits: preparedAmount % 1 !== 0 ? 2 : 0,
});

if (amount === 0 && isTextInsteadOfZeroFormat) {
return zeroSumPlaceholder;
}

return formatter.format(preparedAmount);
};
1 change: 1 addition & 0 deletions package/src/number/formatNumberToCurrency/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './formatNumberToCurrency';
1 change: 1 addition & 0 deletions package/src/number/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './formatNumberToCurrency';

0 comments on commit b17dfbd

Please sign in to comment.