From 92ff333cdcab7f686a75394a65bf3bf5b77a167b Mon Sep 17 00:00:00 2001 From: Fred Liebenberg Date: Mon, 16 Oct 2023 10:58:31 +0200 Subject: [PATCH 1/4] save locale separators to store --- src/app/layout.tsx | 9 +++++++++ src/app/redux/store.ts | 2 ++ src/app/redux/uiSlice.ts | 26 ++++++++++++++++++++++++++ src/app/utils.ts | 21 ++++++++++++++++++--- 4 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 src/app/redux/uiSlice.ts diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 74df0cc0..4f9095f3 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -8,6 +8,8 @@ import { Navbar } from "./components/NavBar"; import { store } from "./redux/store"; import { Provider } from "react-redux"; import { usePathname } from "next/navigation"; +import { getLocaleSeparators } from "utils"; +import { uiSlice } from "redux/uiSlice"; export default function RootLayout({ children, @@ -18,6 +20,13 @@ export default function RootLayout({ useEffect(() => { initializeSubscriptions(store); + let separators = getLocaleSeparators(); + store.dispatch( + uiSlice.actions.setDecimalSeparator(separators.decimalSeparator) + ); + store.dispatch( + uiSlice.actions.setThousandsSeparator(separators.thousandsSeparator) + ); return () => { unsubscribeAll(); }; diff --git a/src/app/redux/store.ts b/src/app/redux/store.ts index 0fa6463e..fe1cf221 100644 --- a/src/app/redux/store.ts +++ b/src/app/redux/store.ts @@ -6,9 +6,11 @@ import { priceChartSlice } from "./priceChartSlice"; import { accountHistorySlice } from "./accountHistorySlice"; import { radixSlice } from "./radixSlice"; import { priceInfoSlice } from "./priceInfoSlice"; +import { uiSlice } from "./uiSlice"; export const store = configureStore({ reducer: { + ui: uiSlice.reducer, radix: radixSlice.reducer, pairSelector: pairSelectorSlice.reducer, orderInput: orderInputSlice.reducer, diff --git a/src/app/redux/uiSlice.ts b/src/app/redux/uiSlice.ts new file mode 100644 index 00000000..f209abb4 --- /dev/null +++ b/src/app/redux/uiSlice.ts @@ -0,0 +1,26 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + +export interface UiState { + decimalSeparator: string; + thousandsSeparator: string; +} + +const initialState: UiState = { + decimalSeparator: ".", + thousandsSeparator: " ", +}; + +export const uiSlice = createSlice({ + name: "ui", + initialState, + + // synchronous reducers + reducers: { + setDecimalSeparator: (state: UiState, action: PayloadAction) => { + state.decimalSeparator = action.payload; + }, + setThousandsSeparator: (state: UiState, action: PayloadAction) => { + state.thousandsSeparator = action.payload; + }, + }, +}); diff --git a/src/app/utils.ts b/src/app/utils.ts index ee6d49e1..29154768 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -1,4 +1,4 @@ -// utiluty function to display numbers in a fixed format +import { store } from "redux/store"; export function displayPositiveNumber(x: number, decimals: number): string { // the same as with displayNumber, but if the number is negative, it will return empty string @@ -9,11 +9,26 @@ export function displayPositiveNumber(x: number, decimals: number): string { } } +export function getLocaleSeparators(): { + decimalSeparator: string; + thousandsSeparator: string; +} { + let decimalSeparator = Number(1.1).toLocaleString().substring(1, 2); + let thousandsSeparator = Number(10000).toLocaleString().substring(2, 3); + if (thousandsSeparator == "0") { + thousandsSeparator = ""; + } + return { + decimalSeparator, + thousandsSeparator, + }; +} + export function displayAmount( x: number, noDigits: number = 6, - decimalSeparator: string = ".", - thousandsSeparator: string = " ", + decimalSeparator: string = store.getState().ui.decimalSeparator, + thousandsSeparator: string = store.getState().ui.thousandsSeparator, fixedDecimals: number = -1 ): string { if (noDigits < 4) { From e787708597bcfda6ec02c307d511b828d372975d Mon Sep 17 00:00:00 2001 From: Evgeniia Vakarina <27793901+EvgeniiaVak@users.noreply.github.com> Date: Sun, 22 Oct 2023 22:21:13 +0400 Subject: [PATCH 2/4] removes redux from number separators setting fixes tests and displayAmount usage one test is still failing because displayAmount logic seems to be incorrect --- __tests__/utils.test.ts | 54 ++++++------------------------- jest.config.mjs | 2 +- jest.setup.js | 10 ++++++ src/app/components/PriceChart.tsx | 36 ++------------------- src/app/components/PriceInfo.tsx | 42 +++--------------------- src/app/layout.tsx | 9 ------ src/app/redux/uiSlice.ts | 26 --------------- src/app/utils.ts | 10 ++---- 8 files changed, 32 insertions(+), 157 deletions(-) create mode 100644 jest.setup.js delete mode 100644 src/app/redux/uiSlice.ts diff --git a/__tests__/utils.test.ts b/__tests__/utils.test.ts index e09b20ed..eae39174 100644 --- a/__tests__/utils.test.ts +++ b/__tests__/utils.test.ts @@ -3,41 +3,29 @@ import { displayAmount, displayNumber } from "../src/app/utils"; describe("displayAmount", () => { it("sends error message if noDigits is less than 4", () => { let digits = 3; - const decimalSeparator = "."; - const thousandsSeparator = " "; const inputs: [number, string][] = [ [1234, "ERROR: displayAmount cannot work with noDigits less than 4"], ]; inputs.forEach(([input, expected]) => { - expect( - displayAmount(input, digits, decimalSeparator, thousandsSeparator) - ).toBe(expected); + expect(displayAmount(input, digits)).toBe(expected); }); digits = 0; inputs.forEach(([input, expected]) => { - expect( - displayAmount(input, digits, decimalSeparator, thousandsSeparator) - ).toBe(expected); + expect(displayAmount(input, digits)).toBe(expected); }); digits = -3; inputs.forEach(([input, expected]) => { - expect( - displayAmount(input, digits, decimalSeparator, thousandsSeparator) - ).toBe(expected); + expect(displayAmount(input, digits)).toBe(expected); }); digits = -10; inputs.forEach(([input, expected]) => { - expect( - displayAmount(input, digits, decimalSeparator, thousandsSeparator) - ).toBe(expected); + expect(displayAmount(input, digits)).toBe(expected); }); }); it("displays amounts in 4 digits with thousands_separator", () => { const digits = 4; - const decimalSeparator = "."; - const thousandsSeparator = " "; const inputs: [number, string][] = [ [0, "0"], [0.1, "0.1"], @@ -66,16 +54,12 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect( - displayAmount(input, digits, decimalSeparator, thousandsSeparator) - ).toBe(expected); + expect(displayAmount(input, digits)).toBe(expected); }); }); it("displays amounts in 4 digits without thousands_separator", () => { const digits = 4; - const decimalSeparator = "."; - const thousandsSeparator = ""; const inputs: [number, string][] = [ [0, "0"], [0.1, "0.1"], @@ -106,16 +90,12 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect( - displayAmount(input, digits, decimalSeparator, thousandsSeparator) - ).toBe(expected); + expect(displayAmount(input, digits)).toBe(expected); }); }); it("displays amounts in 6 digits with thousands_separator", () => { const digits = 6; - const decimalSeparator = "."; - const thousandsSeparator = " "; const inputs: [number, string][] = [ [0, "0"], [0.1, "0.1"], @@ -147,16 +127,12 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect( - displayAmount(input, digits, decimalSeparator, thousandsSeparator) - ).toBe(expected); + expect(displayAmount(input, digits)).toBe(expected); }); }); it("displays amounts in 10 digits with thousands_separator", () => { const digits = 10; - const decimalSeparator = "."; - const thousandsSeparator = " "; const inputs: [number, string][] = [ [0, "0"], [0.1, "0.1"], @@ -189,16 +165,12 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect( - displayAmount(input, digits, decimalSeparator, thousandsSeparator) - ).toBe(expected); + expect(displayAmount(input, digits)).toBe(expected); }); }); it("displays amounts in 6 digits with thousands_separator and fixed decimals = 3", () => { const digits = 6; - const decimalSeparator = "."; - const thousandsSeparator = " "; const inputs: [number, string][] = [ [0, "0.000"], [0.1, "0.100"], @@ -231,16 +203,12 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect( - displayAmount(input, digits, decimalSeparator, thousandsSeparator, 3) - ).toBe(expected); + expect(displayAmount(input, digits, 3)).toBe(expected); }); }); it("displays amounts in 10 digits with thousands_separator and fixed decimals = 3", () => { const digits = 10; - const decimalSeparator = "."; - const thousandsSeparator = " "; const inputs: [number, string][] = [ [0, "0.000"], [0.1, "0.100"], @@ -273,9 +241,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect( - displayAmount(input, digits, decimalSeparator, thousandsSeparator, 3) - ).toBe(expected); + expect(displayAmount(input, digits, 3)).toBe(expected); }); }); }); diff --git a/jest.config.mjs b/jest.config.mjs index b80fa9f9..ef162f9f 100644 --- a/jest.config.mjs +++ b/jest.config.mjs @@ -9,7 +9,7 @@ const createJestConfig = nextJest({ /** @type {import('jest').Config} */ const config = { // Add more setup options before each test is run - // setupFilesAfterEnv: ['/jest.setup.js'], + setupFilesAfterEnv: ["/jest.setup.js"], testEnvironment: "jest-environment-jsdom", }; diff --git a/jest.setup.js b/jest.setup.js new file mode 100644 index 00000000..e93bdd96 --- /dev/null +++ b/jest.setup.js @@ -0,0 +1,10 @@ +const originalToLocaleString = Number.prototype.toLocaleString; + +Number.prototype.toLocaleString = function (locales, options) { + // Call the original method with the 'en-US' locale + // which uses a comma as the thousands separator + // and dots for decimals + const result = originalToLocaleString.call(this, "en-US", options); + // Replace comma with space for thousands separator + return result.replace(/,/g, " "); +}; diff --git a/src/app/components/PriceChart.tsx b/src/app/components/PriceChart.tsx index 963d3e0a..d0aa93ca 100644 --- a/src/app/components/PriceChart.tsx +++ b/src/app/components/PriceChart.tsx @@ -35,63 +35,33 @@ function PriceChartCanvas(props: PriceChartProps) { const theme = tailwindConfig.daisyui.themes[0].dark; const noDigits = 8; - const decimalSeparator = "."; - const thousandSeparator = ","; const fixedDecimals = 6; - const volume = displayAmount( - props.volume || 0, - noDigits, - decimalSeparator, - thousandSeparator, - 2 - ); - - const percChange = displayAmount( - props.percChange || 0, - noDigits, - decimalSeparator, - thousandSeparator, - 2 - ); - - const change = displayAmount( - props.change || 0, - noDigits, - decimalSeparator, - thousandSeparator, - 2 - ); + 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 candleOpen = displayAmount( candlePrice?.open || 0, noDigits, - decimalSeparator, - thousandSeparator, fixedDecimals ); const candleHigh = displayAmount( candlePrice?.high || 0, noDigits, - decimalSeparator, - thousandSeparator, fixedDecimals ); const candleLow = displayAmount( candlePrice?.low || 0, noDigits, - decimalSeparator, - thousandSeparator, fixedDecimals ); const candleClose = displayAmount( candlePrice?.close || 0, noDigits, - decimalSeparator, - thousandSeparator, fixedDecimals ); diff --git a/src/app/components/PriceInfo.tsx b/src/app/components/PriceInfo.tsx index 2ac02cf2..49880f7e 100644 --- a/src/app/components/PriceInfo.tsx +++ b/src/app/components/PriceInfo.tsx @@ -5,44 +5,12 @@ import { displayAmount } from "../utils"; export function PriceInfo() { const priceInfo = useAppSelector((state) => state.priceInfo); const noDigits = 4; - const decimalSeparator = "."; - const thousandSeparator = ","; const fixedDecimals = 3; - const lastPrice = displayAmount( - priceInfo.lastPrice, - noDigits, - decimalSeparator, - thousandSeparator, - fixedDecimals - ); - const change = displayAmount( - priceInfo.change24h, - noDigits, - decimalSeparator, - thousandSeparator, - fixedDecimals - ); - const high = displayAmount( - priceInfo.high24h, - noDigits, - decimalSeparator, - thousandSeparator, - fixedDecimals - ); - const low = displayAmount( - priceInfo.low24h, - noDigits, - decimalSeparator, - thousandSeparator, - fixedDecimals - ); - const volume = displayAmount( - priceInfo.value24h, - noDigits, - decimalSeparator, - thousandSeparator, - fixedDecimals - ); + 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 isNegativeOrZero = priceInfo.isNegativeOrZero; const basePair = "XRD"; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 4f9095f3..74df0cc0 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -8,8 +8,6 @@ import { Navbar } from "./components/NavBar"; import { store } from "./redux/store"; import { Provider } from "react-redux"; import { usePathname } from "next/navigation"; -import { getLocaleSeparators } from "utils"; -import { uiSlice } from "redux/uiSlice"; export default function RootLayout({ children, @@ -20,13 +18,6 @@ export default function RootLayout({ useEffect(() => { initializeSubscriptions(store); - let separators = getLocaleSeparators(); - store.dispatch( - uiSlice.actions.setDecimalSeparator(separators.decimalSeparator) - ); - store.dispatch( - uiSlice.actions.setThousandsSeparator(separators.thousandsSeparator) - ); return () => { unsubscribeAll(); }; diff --git a/src/app/redux/uiSlice.ts b/src/app/redux/uiSlice.ts deleted file mode 100644 index f209abb4..00000000 --- a/src/app/redux/uiSlice.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { createSlice, PayloadAction } from "@reduxjs/toolkit"; - -export interface UiState { - decimalSeparator: string; - thousandsSeparator: string; -} - -const initialState: UiState = { - decimalSeparator: ".", - thousandsSeparator: " ", -}; - -export const uiSlice = createSlice({ - name: "ui", - initialState, - - // synchronous reducers - reducers: { - setDecimalSeparator: (state: UiState, action: PayloadAction) => { - state.decimalSeparator = action.payload; - }, - setThousandsSeparator: (state: UiState, action: PayloadAction) => { - state.thousandsSeparator = action.payload; - }, - }, -}); diff --git a/src/app/utils.ts b/src/app/utils.ts index 29154768..4bd7bc6b 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -1,5 +1,3 @@ -import { store } from "redux/store"; - export function displayPositiveNumber(x: number, decimals: number): string { // the same as with displayNumber, but if the number is negative, it will return empty string if (x < 0) { @@ -27,16 +25,14 @@ export function getLocaleSeparators(): { export function displayAmount( x: number, noDigits: number = 6, - decimalSeparator: string = store.getState().ui.decimalSeparator, - thousandsSeparator: string = store.getState().ui.thousandsSeparator, fixedDecimals: number = -1 ): string { if (noDigits < 4) { return "ERROR: displayAmount cannot work with noDigits less than 4"; } - if (decimalSeparator == "") { - return 'ERROR: desiplayAmount decimalSeparator cannot be ""'; - } + + const { decimalSeparator, thousandsSeparator } = getLocaleSeparators(); + if (x < 1) { let roundedNumber = roundTo(x, noDigits - 2, RoundType.DOWN); if (fixedDecimals >= 0 && fixedDecimals <= noDigits - 2) { From e16199b56ea3a28a8939293bdc7a799184587d00 Mon Sep 17 00:00:00 2001 From: Fred Liebenberg Date: Mon, 23 Oct 2023 10:23:36 +0200 Subject: [PATCH 3/4] added separators as option params for displayAount --- __tests__/utils.test.ts | 10 +++++----- src/app/utils.ts | 29 +++++++++++++++++++---------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/__tests__/utils.test.ts b/__tests__/utils.test.ts index eae39174..c7ad7469 100644 --- a/__tests__/utils.test.ts +++ b/__tests__/utils.test.ts @@ -90,7 +90,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits)).toBe(expected); + expect(displayAmount(input, digits, -1, ".", "")).toBe(expected); }); }); @@ -127,7 +127,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits)).toBe(expected); + expect(displayAmount(input, digits, -1, ".", " ")).toBe(expected); }); }); @@ -165,7 +165,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits)).toBe(expected); + expect(displayAmount(input, digits, -1, ".", " ")).toBe(expected); }); }); @@ -203,7 +203,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits, 3)).toBe(expected); + expect(displayAmount(input, digits, 3, ".", " ")).toBe(expected); }); }); @@ -241,7 +241,7 @@ describe("displayAmount", () => { ]; inputs.forEach(([input, expected]) => { - expect(displayAmount(input, digits, 3)).toBe(expected); + expect(displayAmount(input, digits, 3, ".", " ")).toBe(expected); }); }); }); diff --git a/src/app/utils.ts b/src/app/utils.ts index 4bd7bc6b..5b9b273a 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -8,30 +8,39 @@ export function displayPositiveNumber(x: number, decimals: number): string { } export function getLocaleSeparators(): { - decimalSeparator: string; - thousandsSeparator: string; + localeDecimalSeparator: string; + localeThousandsSeparator: string; } { - let decimalSeparator = Number(1.1).toLocaleString().substring(1, 2); - let thousandsSeparator = Number(10000).toLocaleString().substring(2, 3); - if (thousandsSeparator == "0") { - thousandsSeparator = ""; + let localeDecimalSeparator = Number(1.1).toLocaleString().substring(1, 2); + let localeThousandsSeparator = Number(10000).toLocaleString().substring(2, 3); + if (localeThousandsSeparator == "0") { + localeThousandsSeparator = ""; } return { - decimalSeparator, - thousandsSeparator, + localeDecimalSeparator, + localeThousandsSeparator, }; } export function displayAmount( x: number, noDigits: number = 6, - fixedDecimals: number = -1 + fixedDecimals: number = -1, + decimalSeparator: string | undefined = undefined, + thousandsSeparator: string | undefined = undefined ): string { if (noDigits < 4) { return "ERROR: displayAmount cannot work with noDigits less than 4"; } - const { decimalSeparator, thousandsSeparator } = getLocaleSeparators(); + const { localeDecimalSeparator, localeThousandsSeparator } = + getLocaleSeparators(); + if (decimalSeparator == undefined) { + decimalSeparator = localeDecimalSeparator; + } + if (thousandsSeparator == undefined) { + thousandsSeparator = localeThousandsSeparator; + } if (x < 1) { let roundedNumber = roundTo(x, noDigits - 2, RoundType.DOWN); From 9bbbdb14808a380a309149ed53e7a058062d77bf Mon Sep 17 00:00:00 2001 From: Fred Liebenberg Date: Mon, 23 Oct 2023 10:30:47 +0200 Subject: [PATCH 4/4] removed uislice from store --- src/app/redux/store.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/app/redux/store.ts b/src/app/redux/store.ts index fe1cf221..0fa6463e 100644 --- a/src/app/redux/store.ts +++ b/src/app/redux/store.ts @@ -6,11 +6,9 @@ import { priceChartSlice } from "./priceChartSlice"; import { accountHistorySlice } from "./accountHistorySlice"; import { radixSlice } from "./radixSlice"; import { priceInfoSlice } from "./priceInfoSlice"; -import { uiSlice } from "./uiSlice"; export const store = configureStore({ reducer: { - ui: uiSlice.reducer, radix: radixSlice.reducer, pairSelector: pairSelectorSlice.reducer, orderInput: orderInputSlice.reducer,