From 1a3a6c17038c8f3ac249ce38b7bc213dc4677683 Mon Sep 17 00:00:00 2001 From: tomasklim Date: Wed, 10 Apr 2024 16:28:53 +0200 Subject: [PATCH 1/3] fix(suite): jumping in sendform when fee is calculated --- .../suite/src/components/wallet/Fees/Fees.tsx | 2 +- .../views/wallet/send/components/TotalSent.tsx | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/suite/src/components/wallet/Fees/Fees.tsx b/packages/suite/src/components/wallet/Fees/Fees.tsx index 3c163af7b58..391514896f5 100644 --- a/packages/suite/src/components/wallet/Fees/Fees.tsx +++ b/packages/suite/src/components/wallet/Fees/Fees.tsx @@ -38,7 +38,7 @@ const FeeInfoRow = styled.div` justify-content: space-between; align-items: start; width: 100%; - min-height: 50px; /* reserve space for fiat/crypto amounts */ + min-height: 52px; /* reserve space for fiat/crypto amounts */ `; const FeeAmount = styled.div` diff --git a/packages/suite/src/views/wallet/send/components/TotalSent.tsx b/packages/suite/src/views/wallet/send/components/TotalSent.tsx index 6469429b4da..8a00e1a3675 100644 --- a/packages/suite/src/views/wallet/send/components/TotalSent.tsx +++ b/packages/suite/src/views/wallet/send/components/TotalSent.tsx @@ -20,7 +20,11 @@ const StyledCard = styled(Card)` `; const Left = styled.div` + display: flex; font-weight: ${variables.FONT_WEIGHT.MEDIUM}; + gap: 6px; + justify-content: center; + flex-direction: column; `; const Label = styled.div` @@ -31,7 +35,6 @@ const Label = styled.div` `; const SecondaryLabel = styled.div` - padding-top: 2px; font-size: ${variables.FONT_SIZE.SMALL}; color: ${({ theme }) => theme.TYPE_LIGHT_GREY}; `; @@ -41,6 +44,7 @@ const Right = styled.div` justify-content: center; flex-direction: column; align-items: flex-end; + gap: 6px; `; const TotalSentCoin = styled.div` @@ -48,7 +52,6 @@ const TotalSentCoin = styled.div` font-weight: ${variables.FONT_WEIGHT.MEDIUM}; font-size: ${variables.FONT_SIZE.NORMAL}; color: ${({ theme }) => theme.TYPE_DARK_GREY}; - padding-bottom: 6px; `; const TotalSentFiat = styled.div` @@ -78,11 +81,9 @@ export const TotalSent = () => { - {!isTokenTransfer && ( - - - - )} + + {!isTokenTransfer ? : ' '} + {transactionInfo && transactionInfo.type !== 'error' && ( From f859f335c97cf86c8e1a0ba02cf0c1c548ff3893 Mon Sep 17 00:00:00 2001 From: tomasklim Date: Wed, 10 Apr 2024 16:34:28 +0200 Subject: [PATCH 2/3] fix(suite): format long token symbols --- packages/suite/src/utils/wallet/tokenUtils.ts | 7 +++++++ .../components/Amount/components/TokenSelect.tsx | 12 ++++++++---- .../components/Outputs/components/Amount/index.tsx | 7 ++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/suite/src/utils/wallet/tokenUtils.ts b/packages/suite/src/utils/wallet/tokenUtils.ts index 4684d494d25..6ba38b0709f 100644 --- a/packages/suite/src/utils/wallet/tokenUtils.ts +++ b/packages/suite/src/utils/wallet/tokenUtils.ts @@ -53,3 +53,10 @@ export const enhanceTokensWithRates = ( return tokensWithRates; }; + +export const formatTokenSymbol = (symbol?: string) => { + const tokenSymbol = symbol?.toUpperCase() || 'N/A'; + const isTokenSymbolLong = tokenSymbol.length > 7; + + return isTokenSymbolLong ? `${tokenSymbol.slice(0, 7)}...` : tokenSymbol; +}; diff --git a/packages/suite/src/views/wallet/send/components/Outputs/components/Amount/components/TokenSelect.tsx b/packages/suite/src/views/wallet/send/components/Outputs/components/Amount/components/TokenSelect.tsx index d20ab8246d6..63c9d435e05 100644 --- a/packages/suite/src/views/wallet/send/components/Outputs/components/Amount/components/TokenSelect.tsx +++ b/packages/suite/src/views/wallet/send/components/Outputs/components/Amount/components/TokenSelect.tsx @@ -16,7 +16,11 @@ import BigNumber from 'bignumber.js'; import { Timestamp, TokenAddress, TokenDefinitions } from '@suite-common/wallet-types'; import { TooltipSymbol, Translation } from 'src/components/suite'; import { NetworkSymbol, getNetworkFeatures } from '@suite-common/wallet-config'; -import { enhanceTokensWithRates, sortTokensWithRates } from 'src/utils/wallet/tokenUtils'; +import { + enhanceTokensWithRates, + formatTokenSymbol, + sortTokensWithRates, +} from 'src/utils/wallet/tokenUtils'; import { getShortFingerprint } from '@suite-common/wallet-utils'; import { selectLocalCurrency } from 'src/reducers/wallet/settingsReducer'; import { FiatCurrencyCode } from '@suite-common/suite-config'; @@ -57,7 +61,7 @@ export const buildTokenOptions = ( return; } - const tokenName = token.symbol || 'N/A'; + const tokenSymbol = formatTokenSymbol(token.symbol); if ( !hasCoinDefinitions || @@ -65,13 +69,13 @@ export const buildTokenOptions = ( ) { result[0].options.push({ value: token.contract, - label: tokenName.toUpperCase(), + label: tokenSymbol, fingerprint: token.name, }); } else { unknownTokens.push({ value: token.contract, - label: `${tokenName.toUpperCase().slice(0, 7)}…`, + label: tokenSymbol, fingerprint: token.name, }); } diff --git a/packages/suite/src/views/wallet/send/components/Outputs/components/Amount/index.tsx b/packages/suite/src/views/wallet/send/components/Outputs/components/Amount/index.tsx index c02640893e3..2ed8bb02df1 100644 --- a/packages/suite/src/views/wallet/send/components/Outputs/components/Amount/index.tsx +++ b/packages/suite/src/views/wallet/send/components/Outputs/components/Amount/index.tsx @@ -31,6 +31,7 @@ import { breakpointMediaQueries } from '@trezor/styles'; import { selectFiatRatesByFiatRateKey } from '@suite-common/wallet-core'; import { TokenAddress } from '@suite-common/wallet-types'; import { FiatCurrencyCode } from '@suite-common/suite-config'; +import { formatTokenSymbol } from 'src/utils/wallet/tokenUtils'; const Row = styled.div` position: relative; @@ -136,11 +137,11 @@ export const Amount = ({ output, outputId }: AmountProps) => { const values = getValues(); const fiatCurrency = values?.outputs?.[0]?.currency; + const tokenSymbol = formatTokenSymbol(token?.symbol); + const tokenBalance = token ? ( - {`${ - token.balance - } ${token.symbol!.toUpperCase()}`} + {`${token.balance} ${tokenSymbol}`} ) : undefined; From c30cba60c71ed2f7881e2e6ce02461fe181e5c2a Mon Sep 17 00:00:00 2001 From: tomasklim Date: Fri, 12 Apr 2024 14:26:40 +0200 Subject: [PATCH 3/3] fix(suite): send max coliding with token balance --- .../src/components/form/Input/Input.tsx | 15 ++- .../form/Textarea/Textarea.stories.tsx | 2 +- .../src/components/form/Textarea/Textarea.tsx | 10 +- .../src/components/form/TopAddons.tsx | 45 +++++++-- .../src/components/form/form.stories.tsx | 2 +- .../Outputs/components/Address/index.tsx | 25 ++--- .../components/Amount/components/Fiat.tsx | 6 +- .../Amount/components/SendMaxSwitch.tsx | 56 +++++++++++ .../Outputs/components/Amount/index.tsx | 92 ++++++++++++------- 9 files changed, 186 insertions(+), 67 deletions(-) create mode 100644 packages/suite/src/views/wallet/send/components/Outputs/components/Amount/components/SendMaxSwitch.tsx diff --git a/packages/components/src/components/form/Input/Input.tsx b/packages/components/src/components/form/Input/Input.tsx index 019452b9c76..74215717e5e 100644 --- a/packages/components/src/components/form/Input/Input.tsx +++ b/packages/components/src/components/form/Input/Input.tsx @@ -77,8 +77,9 @@ export interface InputProps extends Omit, value?: string; innerRef?: Ref; label?: ReactElement | string; - labelHoverAddon?: ReactElement; - labelRight?: ReactElement; + labelHoverRight?: React.ReactNode; + labelLeft?: React.ReactNode; + labelRight?: React.ReactNode; innerAddon?: ReactElement; /** * @description pass `null` if bottom text can be `undefined` @@ -103,8 +104,9 @@ const Input = ({ innerRef, inputState, label, - labelHoverAddon, + labelLeft, labelRight, + labelHoverRight, innerAddon, innerAddonAlign = 'right', bottomText, @@ -138,7 +140,12 @@ const Input = ({ $hasBottomPadding={hasBottomPadding === true && bottomText === null} className={className} > - + {innerAddon && innerAddonAlign === 'left' && ( diff --git a/packages/components/src/components/form/Textarea/Textarea.stories.tsx b/packages/components/src/components/form/Textarea/Textarea.stories.tsx index ac53262cdf2..5164221c1a2 100644 --- a/packages/components/src/components/form/Textarea/Textarea.stories.tsx +++ b/packages/components/src/components/form/Textarea/Textarea.stories.tsx @@ -41,7 +41,7 @@ export const Textarea: StoryObj = { type: 'range', }, }, - labelHoverAddon: { + labelHoverRight: { control: 'text', }, labelRight: { diff --git a/packages/components/src/components/form/Textarea/Textarea.tsx b/packages/components/src/components/form/Textarea/Textarea.tsx index ce7fcfb2542..b6d0470bb51 100644 --- a/packages/components/src/components/form/Textarea/Textarea.tsx +++ b/packages/components/src/components/form/Textarea/Textarea.tsx @@ -72,7 +72,7 @@ const TextareaLabel = styled(Label)` export interface TextareaProps extends TextareaHTMLAttributes { isDisabled?: boolean; label?: ReactNode; - labelHoverAddon?: ReactNode; + labelHoverRight?: ReactNode; labelRight?: ReactNode; innerRef?: Ref; /** @@ -90,7 +90,7 @@ export const Textarea = ({ className, value, maxLength, - labelHoverAddon, + labelHoverRight, isDisabled, innerRef, label, @@ -114,7 +114,11 @@ export const Textarea = ({ onMouseLeave={() => setIsHovered(false)} $hasBottomPadding={hasBottomPadding === true && bottomText === null} > - + ` display: flex; - justify-content: end; + justify-content: ${({ hasLeftAddon }) => (hasLeftAddon ? 'space-between' : 'flex-end')}; align-items: flex-end; gap: ${spacingsPx.xs}; min-height: 30px; padding-bottom: 6px; `; -export const HoverAddon = styled.div<{ $isVisible?: boolean }>` +export const RightAddonWrapper = styled.div` + display: flex; + gap: 6px; +`; + +export const RightAddon = styled.div` + display: flex; + align-items: center; +`; + +export const HoverAddonRight = styled.div<{ $isVisible?: boolean }>` opacity: ${({ $isVisible }) => ($isVisible ? 1 : 0)}; transition: opacity 0.1s ease-out; `; -export const RightAddon = styled.div` +export const LeftAddon = styled.div` display: flex; align-items: center; `; interface TopAddonsProps { isHovered?: boolean; + addonLeft?: React.ReactNode; addonRight?: React.ReactNode; - hoverAddon?: React.ReactNode; + hoverAddonRight?: React.ReactNode; } -export const TopAddons = ({ isHovered, addonRight, hoverAddon }: TopAddonsProps) => { - const isWithTopLabel = hoverAddon || addonRight; +export const TopAddons = ({ + isHovered, + addonLeft, + addonRight, + hoverAddonRight, +}: TopAddonsProps) => { + const isWithTopLabel = addonLeft || addonRight || hoverAddonRight; + + const isWithRightLabel = addonRight || hoverAddonRight; if (!isWithTopLabel) { return null; } return ( - - {hoverAddon && {hoverAddon}} - {addonRight && {addonRight}} + + {addonLeft && {addonLeft}} + {isWithRightLabel && ( + + {hoverAddonRight && ( + {hoverAddonRight} + )} + {addonRight && {addonRight}} + + )} ); }; diff --git a/packages/components/src/components/form/form.stories.tsx b/packages/components/src/components/form/form.stories.tsx index 5c3745011f5..902acdf500b 100644 --- a/packages/components/src/components/form/form.stories.tsx +++ b/packages/components/src/components/form/form.stories.tsx @@ -92,7 +92,7 @@ export const All: StoryObj = { value="Input label with warning" dataTest="input-warning-label" bottomText="bottom text" - labelHoverAddon={ + labelHoverRight={ diff --git a/packages/suite/src/views/wallet/send/components/Outputs/components/Address/index.tsx b/packages/suite/src/views/wallet/send/components/Outputs/components/Address/index.tsx index dad6e8952e7..9ac94571b8b 100644 --- a/packages/suite/src/views/wallet/send/components/Outputs/components/Address/index.tsx +++ b/packages/suite/src/views/wallet/send/components/Outputs/components/Address/index.tsx @@ -26,10 +26,6 @@ const Container = styled.div` position: relative; `; -const Heading = styled.p` - position: absolute; -`; - const Text = styled.span` display: flex; align-items: center; @@ -228,13 +224,6 @@ export const Address = ({ output, outputId, outputsCount }: AddressProps) => { return ( - - 1 ? 'TR_SEND_RECIPIENT_ADDRESS' : 'TR_SEND_ADDRESS_SECTION'} - values={{ index: recipientId }} - /> - - { } - labelHoverAddon={ + labelHoverRight={ } + labelLeft={ +

+ 1 + ? 'TR_SEND_RECIPIENT_ADDRESS' + : 'TR_SEND_ADDRESS_SECTION' + } + values={{ index: recipientId }} + /> +

+ } labelRight={ outputsCount > 1 ? ( ; outputId: number; + labelHoverRight?: React.ReactNode; + labelRight?: React.ReactNode; } -export const Fiat = ({ output, outputId }: FiatProps) => { +export const Fiat = ({ output, outputId, labelHoverRight, labelRight }: FiatProps) => { const { account, network, @@ -213,6 +215,8 @@ export const Fiat = ({ output, outputId }: FiatProps) => { return ( ` + ${({ $hideOnLargeScreens }) => + $hideOnLargeScreens && + css` + ${breakpointMediaQueries.lg} { + display: none; + } + `} + ${({ $hideOnSmallScreens }) => + $hideOnSmallScreens && + css` + ${breakpointMediaQueries.below_lg} { + display: none; + } + `} + + ${breakpointMediaQueries.below_md} { + gap: ${spacingsPx.xs}; + } +`; + +interface SendMaxSwitchProps { + hideOnLargeScreens?: boolean; + hideOnSmallScreens?: boolean; + isSetMaxActive: boolean; + dataTest: string; + onChange: () => void; +} + +export const SendMaxSwitch = ({ + isSetMaxActive, + dataTest, + onChange, + hideOnLargeScreens, + hideOnSmallScreens, +}: SendMaxSwitchProps) => ( + } + /> +); diff --git a/packages/suite/src/views/wallet/send/components/Outputs/components/Amount/index.tsx b/packages/suite/src/views/wallet/send/components/Outputs/components/Amount/index.tsx index 2ed8bb02df1..f7a36775d25 100644 --- a/packages/suite/src/views/wallet/send/components/Outputs/components/Amount/index.tsx +++ b/packages/suite/src/views/wallet/send/components/Outputs/components/Amount/index.tsx @@ -2,7 +2,7 @@ import { useCallback } from 'react'; import BigNumber from 'bignumber.js'; import styled, { useTheme } from 'styled-components'; -import { Icon, Switch, Warning, variables } from '@trezor/components'; +import { Icon, Warning, variables } from '@trezor/components'; import { FiatValue, Translation, NumberInput, HiddenPlaceholder } from 'src/components/suite'; import { amountToSatoshi, @@ -32,6 +32,7 @@ import { selectFiatRatesByFiatRateKey } from '@suite-common/wallet-core'; import { TokenAddress } from '@suite-common/wallet-types'; import { FiatCurrencyCode } from '@suite-common/suite-config'; import { formatTokenSymbol } from 'src/utils/wallet/tokenUtils'; +import { SendMaxSwitch } from './components/SendMaxSwitch'; const Row = styled.div` position: relative; @@ -45,9 +46,9 @@ const Row = styled.div` `; const Heading = styled.p` - position: absolute; display: flex; - flex-direction: row; + flex-direction: column; + white-space: nowrap; `; const AmountInput = styled(NumberInput)` @@ -62,9 +63,9 @@ const Left = styled.div` `; const TokenBalance = styled.div` - padding: 0 6px; font-size: ${variables.FONT_SIZE.TINY}; color: ${({ theme }) => theme.TYPE_LIGHT_GREY}; + height: 18px; `; const TokenBalanceValue = styled.span` @@ -72,9 +73,10 @@ const TokenBalanceValue = styled.span` `; const StyledTransferIcon = styled(Icon)` - margin: 50px 20px 0; + margin: 0 20px 46px; + align-self: end; - @media all and (max-width: ${variables.SCREEN_SIZE.LG}) { + ${breakpointMediaQueries.below_lg} { align-self: center; margin: 0; transform: rotate(90deg); @@ -127,6 +129,7 @@ export const Amount = ({ output, outputId }: AmountProps) => { const isSetMaxActive = getDefaultValue('setMaxOutputId') === outputId; const outputError = errors.outputs ? errors.outputs[outputId] : undefined; const error = outputError ? outputError.amount : undefined; + // corner-case: do not display "setMax" button if FormState got ANY error (setMax probably cannot be calculated) const isSetMaxVisible = isSetMaxActive && !error && !Object.keys(errors).length; const maxSwitchId = `outputs.${outputId}.setMax`; @@ -139,12 +142,6 @@ export const Amount = ({ output, outputId }: AmountProps) => { const tokenSymbol = formatTokenSymbol(token?.symbol); - const tokenBalance = token ? ( - - {`${token.balance} ${tokenSymbol}`} - - ) : undefined; - const currentRate = useSelector(state => selectFiatRatesByFiatRateKey( state, @@ -222,37 +219,58 @@ export const Amount = ({ output, outputId }: AmountProps) => { }, }; - const SendMaxSwitch = () => ( - { - setMax(outputId, isSetMaxActive); - composeTransaction(inputName); - }} - label={} + const onSwitchChange = () => { + setMax(outputId, isSetMaxActive); + composeTransaction(inputName); + }; + + const isTokenSelected = !!token; + const tokenBalance = isTokenSelected ? ( + + {`${token.balance} ${tokenSymbol}`} + + ) : undefined; + + const getSendMaxSwitchComponent = ( + hideOnLargeScreens: boolean | undefined, + hideOnSmallScreens: boolean | undefined, + ) => ( + ); return ( <> - - - {tokenBalance && ( - - () - - )} - - : undefined} - labelRight={isSetMaxVisible ? : undefined} + labelHoverRight={ + !isSetMaxVisible && getSendMaxSwitchComponent(isWithRate, undefined) + } + labelRight={ + isSetMaxVisible && getSendMaxSwitchComponent(isWithRate, undefined) + } + labelLeft={ + + + {isTokenSelected && ( + + ( + + ) + + )} + + } bottomText={bottomText || null} onChange={handleInputChange} name={inputName} @@ -283,7 +301,11 @@ export const Amount = ({ output, outputId }: AmountProps) => { /> - + )