From 2cb4f7e0fde7df89c6448ec176c03c5d02d21ec0 Mon Sep 17 00:00:00 2001 From: Evgeniia Vakarina <27793901+EvgeniiaVak@users.noreply.github.com> Date: Fri, 10 Nov 2023 21:45:07 +0400 Subject: [PATCH 1/4] fixes separators logic disparities --- __tests__/utils.test.ts | 46 +++---------- src/app/components/OrderBook.tsx | 4 ++ .../components/order_input/AmountInput.tsx | 11 +-- src/app/utils.ts | 68 +++++++------------ 4 files changed, 39 insertions(+), 90 deletions(-) diff --git a/__tests__/utils.test.ts b/__tests__/utils.test.ts index c7ad7469..e4d68718 100644 --- a/__tests__/utils.test.ts +++ b/__tests__/utils.test.ts @@ -1,5 +1,7 @@ -import { displayAmount, displayNumber } from "../src/app/utils"; +import { displayAmount } from "../src/app/utils"; +// the separators are set to "." and " " for testing purposes +// inside jest.setup.js describe("displayAmount", () => { it("sends error message if noDigits is less than 4", () => { let digits = 3; @@ -90,7 +92,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits, -1, ".", "")).toBe(expected); + expect(displayAmount(input, digits, -1)).toBe(expected); }); }); @@ -127,7 +129,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits, -1, ".", " ")).toBe(expected); + expect(displayAmount(input, digits, -1)).toBe(expected); }); }); @@ -165,7 +167,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits, -1, ".", " ")).toBe(expected); + expect(displayAmount(input, digits, -1)).toBe(expected); }); }); @@ -203,7 +205,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits, 3, ".", " ")).toBe(expected); + expect(displayAmount(input, digits, 3)).toBe(expected); }); }); @@ -241,39 +243,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits, 3, ".", " ")).toBe(expected); - }); - }); -}); - -describe("displayNumber", () => { - it("formats all numbers to fixed decimal places", () => { - const inputs = [ - 1002, 1248, 99.95, 88, 77.7, 1000000.1, 0.03, 0.000004, 0.1, - ]; - const expected = [ - "1K", - "1.25K", - "99.95000", - "88.00000", - "77.70000", - "1M", - "0.03000", - "0.00000", - "0.10000", - ]; - - inputs.forEach((input, index) => { - expect(displayNumber(input, 5)).toBe(expected[index]); - }); - }); - - it("rounds decimals correctly", () => { - const inputs = [0.233, 0.235]; - const expected = ["0.23", "0.24"]; - - inputs.forEach((input, index) => { - expect(displayNumber(input, 2)).toBe(expected[index]); + expect(displayAmount(input, digits, 3)).toBe(expected); }); }); }); diff --git a/src/app/components/OrderBook.tsx b/src/app/components/OrderBook.tsx index 0de976e4..f4693f14 100644 --- a/src/app/components/OrderBook.tsx +++ b/src/app/components/OrderBook.tsx @@ -5,6 +5,8 @@ import * as utils from "../utils"; import { OrderBookRowProps, orderBookSlice } from "../redux/orderBookSlice"; import { useAppDispatch, useAppSelector } from "../hooks"; +const N_DIGITS = 8; + function OrderBookRow(props: OrderBookRowProps) { const { barColor, orderCount, price, size, total, maxTotal } = props; if ( @@ -70,10 +72,12 @@ function CurrentPriceRow() { if (orderBook.spreadPercent !== null && orderBook.spread !== null) { const spread = utils.displayPositiveNumber( orderBook.spread, + N_DIGITS, priceMaxDecimals ); const spreadPercent = utils.displayPositiveNumber( orderBook.spreadPercent, + N_DIGITS, 2 ); diff --git a/src/app/components/order_input/AmountInput.tsx b/src/app/components/order_input/AmountInput.tsx index 6b61ea1d..839431bc 100644 --- a/src/app/components/order_input/AmountInput.tsx +++ b/src/app/components/order_input/AmountInput.tsx @@ -10,6 +10,7 @@ import { selectValidationByAddress, } from "redux/orderInputSlice"; import { BottomRightErrorLabel } from "components/BottomRightErrorLabel"; +import { getLocaleSeparators } from "utils"; export const enum PayReceive { PAY = "YOU PAY:", @@ -43,14 +44,8 @@ export function AmountInput(props: TokenInputFiledProps) { onChange, } = props; - // TODO: after https://github.com/DeXter-on-Radix/website/pull/159 - // read this from the state instead - const getDecimalSeparator = () => { - const numberWithDecimal = 1.1; - return numberWithDecimal.toLocaleString().substring(1, 2); - }; - - const placeholder = `0${getDecimalSeparator()}0`; + const { decimalSeparator } = getLocaleSeparators(); + const placeholder = `0${decimalSeparator}0`; return (
diff --git a/src/app/utils.ts b/src/app/utils.ts index 4b462ecb..ab8ae985 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -1,46 +1,47 @@ -export function displayPositiveNumber(x: number, decimals: number): string { +export function displayPositiveNumber( + x: number, + noDigits: number = 6, + fixedDecimals: number = -1 +): string { // the same as with displayNumber, but if the number is negative, it will return empty string if (x < 0) { return ""; } else { - return displayNumber(x, decimals); + return displayAmount(x, noDigits, fixedDecimals); } } export function getLocaleSeparators(): { - localeDecimalSeparator: string; - localeThousandsSeparator: string; + decimalSeparator: string; + thousandsSeparator: string; } { - let localeDecimalSeparator = Number(1.1).toLocaleString().substring(1, 2); - let localeThousandsSeparator = Number(10000).toLocaleString().substring(2, 3); - if (localeThousandsSeparator == "0") { - localeThousandsSeparator = ""; + // we don't want users locale settings but + // the actual settings for number formatting + // (which may be different from the default than their locale default + // and can be edited manually by users in OS settings) => + // not toLocaleString but toString + let decimalSeparator = Number(1.1).toString().substring(1, 2); + let thousandsSeparator = Number(10000).toString().substring(2, 3); + + if (thousandsSeparator == "0") { + thousandsSeparator = " "; } return { - localeDecimalSeparator, - localeThousandsSeparator, + decimalSeparator, + thousandsSeparator, }; } export function displayAmount( x: number, noDigits: number = 6, - fixedDecimals: number = -1, - decimalSeparator: string | undefined = undefined, - thousandsSeparator: string | undefined = undefined + fixedDecimals: number = -1 ): string { if (noDigits < 4) { return "ERROR: displayAmount cannot work with noDigits less than 4"; } - const { localeDecimalSeparator, localeThousandsSeparator } = - getLocaleSeparators(); - if (decimalSeparator == undefined) { - decimalSeparator = localeDecimalSeparator; - } - if (thousandsSeparator == undefined) { - thousandsSeparator = localeThousandsSeparator; - } + const { decimalSeparator, thousandsSeparator } = getLocaleSeparators(); if (x < 1) { let roundedNumber = roundTo(x, noDigits - 2, RoundType.DOWN); @@ -50,7 +51,7 @@ export function displayAmount( return roundedNumber.toString(); } } - let numberStr = x.toString(); + let numberStr = x.toString(); // FIXME: toString() is platform dependent it may return , for decimal separator let wholeNumber = Math.trunc(x); let wholeNumberStr = wholeNumber.toString(); let numberOfSeparators = Math.trunc((wholeNumberStr.length - 1) / 3); @@ -77,6 +78,7 @@ export function displayAmount( if (wholeNumberStr.length < noDigits) { const noDecimals = noDigits - wholeNumberStr.length; + // FIXME: see fixme above let decimalsStr = numberStr.split(".")[1]; decimalsStr = decimalsStr ? decimalsStr.substring(0, noDecimals - 1).replace(/0+$/, "") @@ -158,28 +160,6 @@ export function displayAmount( } } -export function displayNumber( - x: number, // the number to display - decimals: number // the number of decimal places to display, mu -): string { - let result = ""; - if (x >= 1000000) { - result = roundTo(x / 1000000, 2).toString() + "M"; - } else if (x >= 1000) { - result = roundTo(x / 1000, 2).toString() + "K"; - } else { - // toFixed() digits argument must be between 0 and 100 - if (decimals > 100) { - decimals = 100; - } - if (decimals < 0) { - decimals = 0; - } - result = roundTo(x, decimals).toFixed(decimals); - } - return result; -} - export enum RoundType { UP = "UP", // rounds away from zero DOWN = "DOWN", // rounds towards zero From b7f5fc85d0e461af58ff5ae20ee4e48236cd69ee Mon Sep 17 00:00:00 2001 From: Evgeniia Vakarina <27793901+EvgeniiaVak@users.noreply.github.com> Date: Mon, 13 Nov 2023 19:02:52 +0400 Subject: [PATCH 2/4] fixes decimal separator split --- src/app/utils.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/utils.ts b/src/app/utils.ts index ab8ae985..23074cf6 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -51,7 +51,7 @@ export function displayAmount( return roundedNumber.toString(); } } - let numberStr = x.toString(); // FIXME: toString() is platform dependent it may return , for decimal separator + let numberStr = x.toString(); let wholeNumber = Math.trunc(x); let wholeNumberStr = wholeNumber.toString(); let numberOfSeparators = Math.trunc((wholeNumberStr.length - 1) / 3); @@ -78,8 +78,7 @@ export function displayAmount( if (wholeNumberStr.length < noDigits) { const noDecimals = noDigits - wholeNumberStr.length; - // FIXME: see fixme above - let decimalsStr = numberStr.split(".")[1]; + let decimalsStr = numberStr.split(decimalSeparator)[1]; decimalsStr = decimalsStr ? decimalsStr.substring(0, noDecimals - 1).replace(/0+$/, "") : ""; From ce88c72fa4ac086545a190c48245a67b632b60ab Mon Sep 17 00:00:00 2001 From: Evgeniia Vakarina <27793901+EvgeniiaVak@users.noreply.github.com> Date: Mon, 13 Nov 2023 19:53:38 +0400 Subject: [PATCH 3/4] always have a thousands separator --- __tests__/utils.test.ts | 46 +++++------------------------------------ src/app/utils.ts | 35 +++++++++++-------------------- 2 files changed, 17 insertions(+), 64 deletions(-) diff --git a/__tests__/utils.test.ts b/__tests__/utils.test.ts index e4d68718..191e2c61 100644 --- a/__tests__/utils.test.ts +++ b/__tests__/utils.test.ts @@ -26,7 +26,7 @@ describe("displayAmount", () => { }); }); - it("displays amounts in 4 digits with thousands_separator", () => { + it("displays amounts in 4 digits", () => { const digits = 4; const inputs: [number, string][] = [ [0, "0"], @@ -60,43 +60,7 @@ describe("displayAmount", () => { }); }); - it("displays amounts in 4 digits without thousands_separator", () => { - const digits = 4; - const inputs: [number, string][] = [ - [0, "0"], - [0.1, "0.1"], - [0.101, "0.1"], - [0.12, "0.12"], - [0.123, "0.12"], - [0.1234, "0.12"], - [0.123456, "0.12"], - [0.1234567, "0.12"], - [0.12345678, "0.12"], - [0.123456789, "0.12"], - [1.101, "1.1"], - [1.1234567, "1.12"], - [12.1234567, "12.1"], - [123.1234567, "123"], - [1234.1234567, "1234"], - [1, "1"], - [12, "12"], - [123, "123"], - [1234, "1234"], - [1356, "1356"], - [34567, "34K"], - [123456, "123K"], - [1234567, "1.2M"], - [12345678, "12M"], - [123456789, "123M"], - [1234567890, "1.2B"], - ]; - - inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits, -1)).toBe(expected); - }); - }); - - it("displays amounts in 6 digits with thousands_separator", () => { + it("displays amounts in 6 digits", () => { const digits = 6; const inputs: [number, string][] = [ [0, "0"], @@ -133,7 +97,7 @@ describe("displayAmount", () => { }); }); - it("displays amounts in 10 digits with thousands_separator", () => { + it("displays amounts in 10 digits", () => { const digits = 10; const inputs: [number, string][] = [ [0, "0"], @@ -171,7 +135,7 @@ describe("displayAmount", () => { }); }); - it("displays amounts in 6 digits with thousands_separator and fixed decimals = 3", () => { + it("displays amounts in 6 digits with fixed decimals = 3", () => { const digits = 6; const inputs: [number, string][] = [ [0, "0.000"], @@ -209,7 +173,7 @@ describe("displayAmount", () => { }); }); - it("displays amounts in 10 digits with thousands_separator and fixed decimals = 3", () => { + it("displays amounts in 10 digits with fixed decimals = 3", () => { const digits = 10; const inputs: [number, string][] = [ [0, "0.000"], diff --git a/src/app/utils.ts b/src/app/utils.ts index 23074cf6..0825d3e9 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -23,9 +23,15 @@ export function getLocaleSeparators(): { let decimalSeparator = Number(1.1).toString().substring(1, 2); let thousandsSeparator = Number(10000).toString().substring(2, 3); + // we always have a thousands separator + // if the platform doesn't have one we add space if (thousandsSeparator == "0") { thousandsSeparator = " "; } + if (thousandsSeparator == "") { + thousandsSeparator = " "; + } + return { decimalSeparator, thousandsSeparator, @@ -99,15 +105,9 @@ export function displayAmount( return wholeNumberStr + decimalsStr; } else { let excessLength = wholeNumberStr.length - noDigits + 1; - let excessRemainder = - thousandsSeparator != "" ? excessLength % 4 : excessLength % 3; - // console.log("Excess Remainder: " + excessRemainder); - let excessMultiple = - thousandsSeparator != "" - ? Math.trunc(excessLength / 4) - : Math.trunc(excessLength / 3); + let excessRemainder = excessLength % 4; + let excessMultiple = Math.trunc(excessLength / 4); let displayStr = wholeNumberStr.slice(0, noDigits - 1); - // console.log("DisplayStr: " + displayStr); switch (excessRemainder) { case 0: if (excessMultiple > 0) { @@ -115,23 +115,12 @@ export function displayAmount( } break; case 1: - if (thousandsSeparator != "") { - displayStr = - displayStr.slice(0, -3) + decimalSeparator + displayStr.slice(-2); - } else { - displayStr = - displayStr.slice(0, -2) + - decimalSeparator + - displayStr.slice(-2, -1); - } + displayStr = + displayStr.slice(0, -3) + decimalSeparator + displayStr.slice(-2); break; case 2: - if (thousandsSeparator != "") { - displayStr = - displayStr.slice(0, -2) + decimalSeparator + displayStr.slice(-1); - } else { - displayStr = displayStr.slice(0, -1); - } + displayStr = + displayStr.slice(0, -2) + decimalSeparator + displayStr.slice(-1); break; case 3: displayStr = displayStr.slice(0, -1); From c5c2a41972429b6439066a814aa1fe6ca3773ea7 Mon Sep 17 00:00:00 2001 From: Evgeniia Vakarina <27793901+EvgeniiaVak@users.noreply.github.com> Date: Mon, 13 Nov 2023 20:01:36 +0400 Subject: [PATCH 4/4] refactors to more general displayNumber --- __tests__/utils.test.ts | 20 ++++++++++---------- src/app/components/OrderBook.tsx | 6 +++--- src/app/components/PriceChart.tsx | 16 ++++++++-------- src/app/components/PriceInfo.tsx | 12 ++++++------ src/app/redux/orderInputSlice.ts | 6 +++--- src/app/utils.ts | 4 ++-- 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/__tests__/utils.test.ts b/__tests__/utils.test.ts index 191e2c61..637a76d8 100644 --- a/__tests__/utils.test.ts +++ b/__tests__/utils.test.ts @@ -1,4 +1,4 @@ -import { displayAmount } from "../src/app/utils"; +import { displayNumber } from "../src/app/utils"; // the separators are set to "." and " " for testing purposes // inside jest.setup.js @@ -10,19 +10,19 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits)).toBe(expected); + expect(displayNumber(input, digits)).toBe(expected); }); digits = 0; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits)).toBe(expected); + expect(displayNumber(input, digits)).toBe(expected); }); digits = -3; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits)).toBe(expected); + expect(displayNumber(input, digits)).toBe(expected); }); digits = -10; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits)).toBe(expected); + expect(displayNumber(input, digits)).toBe(expected); }); }); @@ -56,7 +56,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits)).toBe(expected); + expect(displayNumber(input, digits)).toBe(expected); }); }); @@ -93,7 +93,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits, -1)).toBe(expected); + expect(displayNumber(input, digits, -1)).toBe(expected); }); }); @@ -131,7 +131,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits, -1)).toBe(expected); + expect(displayNumber(input, digits, -1)).toBe(expected); }); }); @@ -169,7 +169,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits, 3)).toBe(expected); + expect(displayNumber(input, digits, 3)).toBe(expected); }); }); @@ -207,7 +207,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits, 3)).toBe(expected); + expect(displayNumber(input, digits, 3)).toBe(expected); }); }); }); diff --git a/src/app/components/OrderBook.tsx b/src/app/components/OrderBook.tsx index f4693f14..b5a9efea 100644 --- a/src/app/components/OrderBook.tsx +++ b/src/app/components/OrderBook.tsx @@ -18,9 +18,9 @@ function OrderBookRow(props: OrderBookRowProps) { typeof maxTotal !== "undefined" ) { const charactersToDisplay = 6; - const priceString = utils.displayAmount(price, charactersToDisplay); - const sizeString = utils.displayAmount(size, charactersToDisplay); - const totalString = utils.displayAmount(total, charactersToDisplay); + const priceString = utils.displayNumber(price, charactersToDisplay); + const sizeString = utils.displayNumber(size, charactersToDisplay); + const totalString = utils.displayNumber(total, charactersToDisplay); const barWidth = `${(total / maxTotal) * 100}%`; const barStyle = { diff --git a/src/app/components/PriceChart.tsx b/src/app/components/PriceChart.tsx index d0aa93ca..ad195645 100644 --- a/src/app/components/PriceChart.tsx +++ b/src/app/components/PriceChart.tsx @@ -9,7 +9,7 @@ import { initializeLegend, } from "../redux/priceChartSlice"; import { useAppDispatch, useAppSelector } from "../hooks"; -import { displayAmount } from "../utils"; +import { displayNumber } from "../utils"; import * as tailwindConfig from "../../../tailwind.config"; interface PriceChartProps { @@ -37,29 +37,29 @@ function PriceChartCanvas(props: PriceChartProps) { const noDigits = 8; const fixedDecimals = 6; - const volume = displayAmount(props.volume || 0, noDigits, 2); - const percChange = displayAmount(props.percChange || 0, noDigits, 2); - const change = displayAmount(props.change || 0, noDigits, 2); + const volume = displayNumber(props.volume || 0, noDigits, 2); + const percChange = displayNumber(props.percChange || 0, noDigits, 2); + const change = displayNumber(props.change || 0, noDigits, 2); - const candleOpen = displayAmount( + const candleOpen = displayNumber( candlePrice?.open || 0, noDigits, fixedDecimals ); - const candleHigh = displayAmount( + const candleHigh = displayNumber( candlePrice?.high || 0, noDigits, fixedDecimals ); - const candleLow = displayAmount( + const candleLow = displayNumber( candlePrice?.low || 0, noDigits, fixedDecimals ); - const candleClose = displayAmount( + const candleClose = displayNumber( candlePrice?.close || 0, noDigits, fixedDecimals diff --git a/src/app/components/PriceInfo.tsx b/src/app/components/PriceInfo.tsx index 49880f7e..50649ef2 100644 --- a/src/app/components/PriceInfo.tsx +++ b/src/app/components/PriceInfo.tsx @@ -1,16 +1,16 @@ import React from "react"; import { useAppSelector } from "../hooks"; -import { displayAmount } from "../utils"; +import { displayNumber } from "../utils"; export function PriceInfo() { const priceInfo = useAppSelector((state) => state.priceInfo); const noDigits = 4; const fixedDecimals = 3; - const lastPrice = displayAmount(priceInfo.lastPrice, noDigits, fixedDecimals); - const change = displayAmount(priceInfo.change24h, noDigits, fixedDecimals); - const high = displayAmount(priceInfo.high24h, noDigits, fixedDecimals); - const low = displayAmount(priceInfo.low24h, noDigits, fixedDecimals); - const volume = displayAmount(priceInfo.value24h, noDigits, fixedDecimals); + const lastPrice = displayNumber(priceInfo.lastPrice, noDigits, fixedDecimals); + const change = displayNumber(priceInfo.change24h, noDigits, fixedDecimals); + const high = displayNumber(priceInfo.high24h, noDigits, fixedDecimals); + const low = displayNumber(priceInfo.low24h, noDigits, fixedDecimals); + const volume = displayNumber(priceInfo.value24h, noDigits, fixedDecimals); const isNegativeOrZero = priceInfo.isNegativeOrZero; const basePair = "XRD"; diff --git a/src/app/redux/orderInputSlice.ts b/src/app/redux/orderInputSlice.ts index 7eab0f6a..a0e03303 100644 --- a/src/app/redux/orderInputSlice.ts +++ b/src/app/redux/orderInputSlice.ts @@ -7,7 +7,7 @@ import { import * as adex from "alphadex-sdk-js"; import { SdkResult } from "alphadex-sdk-js/lib/models/sdk-result"; import { RDT, getRdt } from "../subscriptions"; -import { RoundType, displayAmount, roundTo } from "../utils"; +import { RoundType, displayNumber, roundTo } from "../utils"; import { fetchAccountHistory } from "./accountHistorySlice"; import { selectBestBuy, selectBestSell } from "./orderBookSlice"; import { fetchBalances } from "./pairSelectorSlice"; @@ -438,10 +438,10 @@ function toDescription(quote: Quote): string { if (quote.fromAmount > 0 && quote.toAmount > 0) { description += - `Sending ${displayAmount(quote.fromAmount, 8)} ${ + `Sending ${displayNumber(quote.fromAmount, 8)} ${ quote.fromToken.symbol } ` + - `to receive ${displayAmount(quote.toAmount, 8)} ${ + `to receive ${displayNumber(quote.toAmount, 8)} ${ quote.toToken.symbol }.\n`; } diff --git a/src/app/utils.ts b/src/app/utils.ts index 0825d3e9..7f6023b9 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -7,7 +7,7 @@ export function displayPositiveNumber( if (x < 0) { return ""; } else { - return displayAmount(x, noDigits, fixedDecimals); + return displayNumber(x, noDigits, fixedDecimals); } } @@ -38,7 +38,7 @@ export function getLocaleSeparators(): { }; } -export function displayAmount( +export function displayNumber( x: number, noDigits: number = 6, fixedDecimals: number = -1