diff --git a/src/components/Balance.module.css b/src/components/Balance.module.css index ced0b163..013532ec 100644 --- a/src/components/Balance.module.css +++ b/src/components/Balance.module.css @@ -22,23 +22,35 @@ color: var(--jam-balance-color); } -.frozenSymbol { - order: -2; +.hideSymbol { + padding-left: 0.1em; + color: var(--jam-balance-deemphasize-color); } .bitcoinSymbol { - padding-right: 0.1em; order: -1; + width: 1em; + padding-right: 0.1em; } .satsSymbol { - padding-right: 0.1em; - order: 6; + order: 5; } -.hideSymbol { - padding-left: 0.1em; - color: var(--jam-balance-deemphasize-color); +.frozenSymbol { + order: 5; +} +.bitcoinAmount + .frozenSymbol { + order: -2; + width: 1em; + height: 1em; +} + +.frozenSymbol, +.bitcoinSymbol, +.satsSymbol { + display: flex; + justify-content: center; } .bitcoinAmount .fractionalPart :nth-child(3)::before, diff --git a/src/components/Balance.test.tsx b/src/components/Balance.test.tsx index 9429387c..68377b10 100644 --- a/src/components/Balance.test.tsx +++ b/src/components/Balance.test.tsx @@ -11,31 +11,46 @@ describe('', () => { expect(screen.getByText(`NaN`)).toBeInTheDocument() }) - it('should render BTC using satscomma formatting', () => { + it('should render balance in BTC', () => { render() expect(screen.getByTestId('bitcoin-amount').dataset.formattedValue).toBe(`123.45600000`) + expect(screen.getByTestId('bitcoin-symbol')).toBeVisible() + expect(screen.queryByTestId('sats-symbol')).not.toBeInTheDocument() + expect(screen.queryByTestId('frozen-symbol')).not.toBeInTheDocument() + }) + + it('should render balance in SATS', () => { + render() + expect(screen.getByTestId('sats-amount')).toHaveTextContent(`12,345,600,000`) + expect(screen.getByTestId('sats-symbol')).toBeVisible() + expect(screen.queryByTestId('bitcoin-symbol')).not.toBeInTheDocument() + expect(screen.queryByTestId('frozen-symbol')).not.toBeInTheDocument() }) it('should hide balance for BTC by default', () => { render() expect(screen.getByText(`*****`)).toBeInTheDocument() expect(screen.queryByTestId('bitcoin-amount')).not.toBeInTheDocument() + expect(screen.queryByTestId('bitcoin-symbol')).not.toBeInTheDocument() }) it('should hide balance for SATS by default', () => { render() expect(screen.getByText(`*****`)).toBeInTheDocument() expect(screen.queryByTestId(`sats-amount`)).not.toBeInTheDocument() + expect(screen.queryByTestId('sats-symbol')).not.toBeInTheDocument() }) it('should render a string BTC value correctly as BTC', () => { render() expect(screen.getByTestId('bitcoin-amount').dataset.formattedValue).toBe(`123.03224961`) + expect(screen.getByTestId('bitcoin-symbol')).toBeVisible() }) it('should render a string BTC value correctly as SATS', () => { render() expect(screen.getByTestId(`sats-amount`)).toHaveTextContent(`12,303,224,961`) + expect(screen.getByTestId('sats-symbol')).toBeVisible() }) it('should render a zero string BTC value correctly as BTC', () => { @@ -108,6 +123,27 @@ describe('', () => { expect(screen.getByTestId(`sats-amount`)).toHaveTextContent(`2,100,000,000,000,000`) }) + it('should render frozen balance in BTC', () => { + render() + expect(screen.getByTestId('bitcoin-amount').dataset.formattedValue).toBe(`123.45600000`) + expect(screen.getByTestId('bitcoin-symbol')).toBeVisible() + expect(screen.getByTestId('frozen-symbol')).toBeVisible() + }) + + it('should render frozen balance in SATS', () => { + render() + expect(screen.getByTestId('sats-amount')).toHaveTextContent(`12,345,600,000`) + expect(screen.getByTestId('sats-symbol')).toBeVisible() + expect(screen.getByTestId('frozen-symbol')).toBeVisible() + }) + + it('should render balance without symbol', () => { + render() + expect(screen.getByTestId('sats-amount')).toBeVisible() + expect(screen.getByTestId('frozen-symbol')).toBeVisible() + expect(screen.queryByTestId('sats-symbol')).not.toBeInTheDocument() + }) + it('should toggle visibility of initially hidden balance on click by default', () => { render() expect(screen.queryByTestId(`sats-amount`)).not.toBeInTheDocument() diff --git a/src/components/Balance.tsx b/src/components/Balance.tsx index 9d494375..880d0f67 100644 --- a/src/components/Balance.tsx +++ b/src/components/Balance.tsx @@ -1,4 +1,4 @@ -import { MouseEventHandler, useCallback, useEffect, useMemo, useState } from 'react' +import { PropsWithChildren, MouseEventHandler, useEffect, useMemo, useState } from 'react' import classNames from 'classnames' import Sprite from './Sprite' import { SATS, BTC, btcToSats, satsToBtc, isValidNumber, formatBtc, formatSats } from '../utils' @@ -15,74 +15,99 @@ const getDisplayMode = (unit: Unit, showBalance: boolean) => { return DISPLAY_MODE_HIDDEN } -const DECIMAL_POINT_CHAR = '.' - -const BitcoinAmountComponent = ({ value }: { value: number }) => { - const numberString = formatBtc(value) - const [integerPart, fractionalPart] = numberString.split(DECIMAL_POINT_CHAR) - - const fractionPartArray = fractionalPart.split('') - const integerPartIsZero = integerPart === '0' - const fractionalPartStartsWithZero = fractionPartArray[0] === '0' - - return ( - - {integerPart} - {DECIMAL_POINT_CHAR} - - {fractionPartArray.map((digit, index) => ( - - {digit} - - ))} - - - ) -} - -const SatsAmountComponent = ({ value }: { value: number }) => { - return ( - - {formatSats(value)} - - ) -} - -const BTC_SYMBOL = {'\u20BF'} +const BTC_SYMBOL = ( + + {'\u20BF'} + +) -const SAT_SYMBOL = +const SAT_SYMBOL = ( + +) const FROZEN_SYMBOL = ( - + ) interface BalanceComponentProps { - symbol: JSX.Element - value: JSX.Element + symbol?: JSX.Element + showSymbol?: boolean frozen?: boolean } -const BalanceComponent = ({ symbol, value, frozen = false }: BalanceComponentProps) => { +const BalanceComponent = ({ + symbol, + showSymbol = true, + frozen = false, + children, +}: PropsWithChildren) => { return ( + {children} + {showSymbol && symbol} {frozen && FROZEN_SYMBOL} - {value} - {symbol} ) } +const DECIMAL_POINT_CHAR = '.' + +type BitcoinBalanceProps = Omit & { value: number } + +const BitcoinBalance = ({ value, ...props }: BitcoinBalanceProps) => { + const numberString = formatBtc(value) + const [integerPart, fractionalPart] = numberString.split(DECIMAL_POINT_CHAR) + + const fractionPartArray = fractionalPart.split('') + const integerPartIsZero = integerPart === '0' + const fractionalPartStartsWithZero = fractionPartArray[0] === '0' + + return ( + + + {integerPart} + {DECIMAL_POINT_CHAR} + + {fractionPartArray.map((digit, index) => ( + + {digit} + + ))} + + + + ) +} + +type SatsBalanceProps = Omit & { value: number } + +const SatsBalance = ({ value, ...props }: SatsBalanceProps) => { + return ( + + + {formatSats(value)} + + + ) +} + /** * Options argument for Balance component. * @@ -97,12 +122,11 @@ const BalanceComponent = ({ symbol, value, frozen = false }: BalanceComponentPro * @param {loading}: A loading flag that renders a placeholder while true. * @param {enableVisibilityToggle}: A flag that controls whether the balance can mask/unmask when clicked */ -interface BalanceProps { +type BalanceProps = Omit & { valueString: string convertToUnit: Unit showBalance?: boolean enableVisibilityToggle?: boolean - frozen?: boolean } /** @@ -113,7 +137,7 @@ export default function Balance({ convertToUnit, showBalance = false, enableVisibilityToggle = !showBalance, - frozen = false, + ...props }: BalanceProps) { const [isBalanceVisible, setIsBalanceVisible] = useState(showBalance) const displayMode = useMemo(() => getDisplayMode(convertToUnit, isBalanceVisible), [convertToUnit, isBalanceVisible]) @@ -122,28 +146,29 @@ export default function Balance({ setIsBalanceVisible(showBalance) }, [showBalance]) - const toggleVisibility: MouseEventHandler = useCallback((e) => { + const toggleVisibility: MouseEventHandler = (e) => { e.preventDefault() e.stopPropagation() setIsBalanceVisible((current) => !current) - }, []) + } const balanceComponent = useMemo(() => { if (displayMode === DISPLAY_MODE_HIDDEN) { return ( } - value={{'*****'}} - frozen={frozen} - /> + {...props} + > + {'*****'} + ) } const valueNumber = parseFloat(valueString) if (!isValidNumber(valueNumber)) { console.warn(' component expects number input as string') - return } value={<>{valueString}} frozen={frozen} /> + return {valueString} } // Treat integers as sats. @@ -151,35 +176,17 @@ export default function Balance({ // Treat decimal numbers as btc. const valueIsBtc = !valueIsSats && valueString.indexOf('.') > -1 - if (valueIsBtc && displayMode === DISPLAY_MODE_BTC) - return ( - } frozen={frozen} /> - ) - if (valueIsSats && displayMode === DISPLAY_MODE_SATS) - return ( - } frozen={frozen} /> - ) + if (valueIsBtc && displayMode === DISPLAY_MODE_BTC) return + if (valueIsSats && displayMode === DISPLAY_MODE_SATS) return if (valueIsBtc && displayMode === DISPLAY_MODE_SATS) - return ( - } - frozen={frozen} - /> - ) + return if (valueIsSats && displayMode === DISPLAY_MODE_BTC) - return ( - } - frozen={frozen} - /> - ) + return console.warn(' component cannot determine balance format') - return } value={<>{valueString}} frozen={frozen} /> - }, [valueString, displayMode, frozen]) + return {valueString} + }, [valueString, displayMode, props]) if (!enableVisibilityToggle) { return <>{balanceComponent} diff --git a/src/components/jars/Jar.module.css b/src/components/jars/Jar.module.css index b24d8b0a..9cb838df 100644 --- a/src/components/jars/Jar.module.css +++ b/src/components/jars/Jar.module.css @@ -58,10 +58,6 @@ font-size: 0.8rem; } -.frozen.jarBalance { - font-size: 0.7rem; -} - .selectableJarContainer { display: flex; flex-direction: column; diff --git a/src/components/jars/Jar.tsx b/src/components/jars/Jar.tsx index 4def150e..5b2fae67 100644 --- a/src/components/jars/Jar.tsx +++ b/src/components/jars/Jar.tsx @@ -135,6 +135,7 @@ const Jar = ({ index, balance, frozenBalance, fillLevel, isOpen = false }: JarPr convertToUnit={settings.unit} showBalance={settings.showBalance} frozen={true} + showSymbol={false} /> )}