mirror of
https://github.com/trezor/trezor-suite.git
synced 2026-02-20 00:33:07 +01:00
refactor(suite): refactor trading form components
This commit is contained in:
@@ -38,7 +38,9 @@ export const Collapsible = ({
|
||||
gap,
|
||||
}}
|
||||
>
|
||||
<Container data-testid={dataTest}>{children}</Container>
|
||||
<Container data-testid={dataTest} aria-expanded={isOpen ?? uncontrolledIsOpen}>
|
||||
{children}
|
||||
</Container>
|
||||
</CollapsibleContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { HTMLProps, ReactNode } from 'react';
|
||||
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import styled, { CSSProperties } from 'styled-components';
|
||||
@@ -13,7 +13,7 @@ const Container = styled(motion.div)<{ $overflow?: CSSProperties['overflow'] }>`
|
||||
overflow: ${({ $overflow = 'hidden' }) => $overflow};
|
||||
`;
|
||||
|
||||
type CollapsibleContentProps = {
|
||||
type CollapsibleContentProps = Pick<HTMLProps<HTMLDivElement>, 'onClick'> & {
|
||||
children: ReactNode;
|
||||
'data-testid'?: string;
|
||||
onAnimationComplete?: (isOpen: boolean) => void;
|
||||
@@ -25,6 +25,7 @@ export const CollapsibleContent = ({
|
||||
onAnimationComplete,
|
||||
'data-testid': dataTestId,
|
||||
overflow,
|
||||
onClick,
|
||||
}: CollapsibleContentProps) => {
|
||||
const { isOpen, contentId, gap } = useCollapsible();
|
||||
|
||||
@@ -47,6 +48,7 @@ export const CollapsibleContent = ({
|
||||
aria-expanded={isOpen}
|
||||
id={contentId}
|
||||
$overflow={overflow}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Box padding={{ top: gap }}>{children}</Box>
|
||||
</Container>
|
||||
|
||||
@@ -19,8 +19,9 @@ export type CollapsibleFeesProps = {
|
||||
networkSymbol: NetworkSymbol;
|
||||
networkType: NetworkType;
|
||||
rbfForm?: boolean;
|
||||
isOpen?: boolean;
|
||||
} & Pick<FeesContextType, 'feeInfo' | 'composedLevels' | 'changeFeeLevel'> &
|
||||
Omit<CollapsibleFeesHeaderContentProps, 'supportsAdjustableFees' | 'txMaxFee'>;
|
||||
Omit<CollapsibleFeesHeaderContentProps, 'supportsAdjustableFees' | 'txMaxFee' | 'isOpen'>;
|
||||
|
||||
export function CollapsibleFees({
|
||||
label,
|
||||
@@ -31,7 +32,7 @@ export function CollapsibleFees({
|
||||
changeFeeLevel,
|
||||
rbfForm,
|
||||
headerTypographyStyle = 'body',
|
||||
isHeaderRowLayout,
|
||||
isOpen,
|
||||
}: CollapsibleFeesProps) {
|
||||
const selectedFee = useWatch<FormState, 'selectedFee'>({
|
||||
name: 'selectedFee',
|
||||
@@ -68,17 +69,17 @@ export function CollapsibleFees({
|
||||
composedLevels,
|
||||
}}
|
||||
>
|
||||
<Collapsible gap={12}>
|
||||
<Collapsible gap={12} isOpen={isOpen} data-testid="@wallet/fees/collapsible-fees">
|
||||
<CollapsibleFeesHeaderContent
|
||||
label={label}
|
||||
headerTypographyStyle={headerTypographyStyle}
|
||||
supportsAdjustableFees={supportsAdjustableFees}
|
||||
isHeaderRowLayout={isHeaderRowLayout}
|
||||
txMaxFee={txMaxFee}
|
||||
isOpen={isOpen}
|
||||
/>
|
||||
|
||||
{supportsAdjustableFees && (
|
||||
<Collapsible.Content overflow="unset">
|
||||
<Collapsible.Content overflow="unset" onClick={ev => ev.stopPropagation()}>
|
||||
<Column gap={16}>
|
||||
<Column gap={16}>
|
||||
{!isCustomFee && <StandardFee />}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useTheme } from 'styled-components';
|
||||
|
||||
import { Translation, TranslationKey } from '@suite/intl';
|
||||
import { Icon, Link, Row, Text, Tooltip } from '@trezor/components';
|
||||
import { TypographyStyle, spacings } from '@trezor/theme';
|
||||
import { TypographyStyle } from '@trezor/theme';
|
||||
import { HELP_CENTER_TRANSACTION_FEES_URL } from '@trezor/urls';
|
||||
|
||||
import { useFeesContext } from '../context/FeesContext';
|
||||
@@ -43,7 +43,7 @@ export function CollapsibleFeesHeader({ label, typographyStyle }: CollapsibleFee
|
||||
}, [networkType]);
|
||||
|
||||
return (
|
||||
<Row flexWrap="wrap" justifyContent="space-between" gap={spacings.sm} minHeight={44}>
|
||||
<Row flexWrap="wrap" justifyContent="space-between" gap={12} minHeight={44}>
|
||||
<Tooltip
|
||||
addon={
|
||||
networkType === 'ethereum' && (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TranslationKey } from '@suite/intl';
|
||||
import { Box, Collapsible, Row } from '@trezor/components';
|
||||
import { Collapsible, Row } from '@trezor/components';
|
||||
import { TypographyStyle } from '@trezor/theme';
|
||||
|
||||
import { ContentFlex } from 'src/support/suite/ContentFlex';
|
||||
@@ -11,20 +11,24 @@ import { useTransactionMaxFee } from './hooks/useTransactionMaxFee';
|
||||
export type CollapsibleFeesHeaderContentProps = {
|
||||
label?: TranslationKey;
|
||||
headerTypographyStyle?: TypographyStyle;
|
||||
isHeaderRowLayout?: boolean;
|
||||
supportsAdjustableFees: boolean;
|
||||
txMaxFee: ReturnType<typeof useTransactionMaxFee>;
|
||||
isOpen?: boolean;
|
||||
};
|
||||
|
||||
export const CollapsibleFeesHeaderContent = ({
|
||||
label,
|
||||
headerTypographyStyle = 'body',
|
||||
supportsAdjustableFees,
|
||||
isHeaderRowLayout,
|
||||
txMaxFee,
|
||||
isOpen,
|
||||
}: CollapsibleFeesHeaderContentProps) => {
|
||||
const content = (
|
||||
<ContentFlex justifyContent="space-between" gap={12}>
|
||||
<ContentFlex
|
||||
justifyContent="space-between"
|
||||
gap={12}
|
||||
data-testid="@wallet/fees/collapsible-fees-toggle"
|
||||
>
|
||||
<CollapsibleFeesHeader label={label} typographyStyle={headerTypographyStyle} />
|
||||
<Row gap={12}>
|
||||
<MaximumFee typographyStyle={headerTypographyStyle} txMaxFee={txMaxFee} />
|
||||
@@ -35,20 +39,5 @@ export const CollapsibleFeesHeaderContent = ({
|
||||
</ContentFlex>
|
||||
);
|
||||
|
||||
return (
|
||||
<Collapsible.Toggle data-testid="@wallet/fees/collapsible-fees-toggle">
|
||||
{isHeaderRowLayout ? (
|
||||
<Box
|
||||
backgroundColorOnInteraction={
|
||||
supportsAdjustableFees ? 'backgroundSurfaceElevation2' : undefined
|
||||
}
|
||||
padding={{ vertical: 12, horizontal: 16 }}
|
||||
>
|
||||
{content}
|
||||
</Box>
|
||||
) : (
|
||||
content
|
||||
)}
|
||||
</Collapsible.Toggle>
|
||||
);
|
||||
return isOpen !== undefined ? content : <Collapsible.Toggle>{content}</Collapsible.Toggle>;
|
||||
};
|
||||
|
||||
@@ -9,10 +9,15 @@ import { FieldErrorBanner } from './FieldErrorBanner';
|
||||
|
||||
export type FeesProps = {
|
||||
account: Pick<Account, 'symbol' | 'networkType'>;
|
||||
isHeaderRowLayout?: boolean;
|
||||
} & Pick<
|
||||
CollapsibleFeesProps,
|
||||
'label' | 'rbfForm' | 'feeInfo' | 'changeFeeLevel' | 'composedLevels' | 'headerTypographyStyle'
|
||||
| 'label'
|
||||
| 'rbfForm'
|
||||
| 'feeInfo'
|
||||
| 'changeFeeLevel'
|
||||
| 'composedLevels'
|
||||
| 'headerTypographyStyle'
|
||||
| 'isOpen'
|
||||
>;
|
||||
|
||||
export const Fees = ({
|
||||
@@ -23,7 +28,7 @@ export const Fees = ({
|
||||
label,
|
||||
rbfForm,
|
||||
headerTypographyStyle,
|
||||
isHeaderRowLayout,
|
||||
isOpen,
|
||||
}: FeesProps) => {
|
||||
useFetchFees({ networkSymbol });
|
||||
|
||||
@@ -38,7 +43,7 @@ export const Fees = ({
|
||||
changeFeeLevel={changeFeeLevel}
|
||||
rbfForm={rbfForm}
|
||||
headerTypographyStyle={headerTypographyStyle}
|
||||
isHeaderRowLayout={isHeaderRowLayout}
|
||||
isOpen={isOpen}
|
||||
/>
|
||||
|
||||
<FieldErrorBanner fieldName="selectedFee" />
|
||||
|
||||
@@ -10,10 +10,9 @@ import {
|
||||
tradingActions,
|
||||
} from '@suite-common/trading';
|
||||
import { TokenAddress } from '@suite-common/wallet-types';
|
||||
import { Card, Column, Divider, Row } from '@trezor/components';
|
||||
import { Column, Row } from '@trezor/components';
|
||||
import { hasBitcoinOnlyFirmware } from '@trezor/device-utils/src/firmwareUtils';
|
||||
import { useCurrentRef } from '@trezor/react-utils';
|
||||
import { spacings } from '@trezor/theme';
|
||||
|
||||
import { useDispatch, useSelector } from 'src/hooks/suite';
|
||||
import { useTradingFormContext } from 'src/hooks/wallet/trading/form/useTradingCommonForm';
|
||||
@@ -22,13 +21,15 @@ import { TradingFormInputCountry } from 'src/views/wallet/trading/common/Trading
|
||||
import { TradingFormInputFiatCrypto } from 'src/views/wallet/trading/common/TradingForm/TradingFormInput/TradingFormInputFiatCrypto/TradingFormInputFiatCrypto';
|
||||
import { TradingFormInputPaymentMethod } from 'src/views/wallet/trading/common/TradingForm/TradingFormInput/TradingFormInputPaymentMethod';
|
||||
|
||||
import { TradingFormCard } from './TradingFormCard';
|
||||
import { TradingFormFeesDisclamer } from './TradingFormFeeDisclamer';
|
||||
import { TradingReceiveAddress } from '../TradingSelectedOffer/TradingReceiveAddress/TradingReceiveAddress';
|
||||
import { TradingFormSection } from './TradingFormSection';
|
||||
import { TradingSelectedOfferProvider } from '../TradingSelectedOffer/TradingSelectedOfferProvider';
|
||||
import {
|
||||
TradingFormInputBuyAsset,
|
||||
TradingFormInputBuyAssetProps,
|
||||
} from './TradingFormInput/TradingFormInputBuyAsset/TradingFormInputBuyAsset';
|
||||
import { TradingReceiveAddress } from '../TradingSelectedOffer/TradingReceiveAddress/TradingReceiveAddress';
|
||||
|
||||
export const TradingBuyFormInputs = () => {
|
||||
const context = useTradingFormContext<TradingBuyType>();
|
||||
@@ -61,76 +62,56 @@ export const TradingBuyFormInputs = () => {
|
||||
const buySupportedCryptoIds = useSelector(selectTradingBuySupportedCryptoIds);
|
||||
|
||||
return (
|
||||
<Column gap={spacings.lg}>
|
||||
<Card paddingType="none">
|
||||
<Column gap={spacings.lg}>
|
||||
<Column
|
||||
gap={spacings.lg}
|
||||
padding={{
|
||||
vertical: spacings.md,
|
||||
horizontal: spacings.lg,
|
||||
bottom: cryptoSelect.id && !isLoading ? 0 : spacings.md,
|
||||
}}
|
||||
>
|
||||
<Column gap={spacings.xs}>
|
||||
<TradingFormInputFiatCrypto
|
||||
cryptoInputName={TRADING_FORM_CRYPTO_INPUT}
|
||||
fiatInputName={TRADING_FORM_FIAT_INPUT}
|
||||
cryptoSelectName={TRADING_FORM_CRYPTO_CURRENCY_SELECT}
|
||||
currencySelectLabel={currencySelect.label}
|
||||
cryptoCurrencyLabel={cryptoSelect.id}
|
||||
/>
|
||||
|
||||
{amountInCrypto && (
|
||||
<Row justifyContent="end">
|
||||
<TradingBalance
|
||||
balance={cryptoInput}
|
||||
displaySymbol={cryptoSelect.displaySymbol}
|
||||
symbol={cryptoSelect.networkSymbol}
|
||||
tokenAddress={
|
||||
(cryptoSelect.contractAddress as TokenAddress) ??
|
||||
undefined
|
||||
}
|
||||
showOnlyAmount
|
||||
amountInCrypto={amountInCrypto}
|
||||
/>
|
||||
</Row>
|
||||
)}
|
||||
</Column>
|
||||
|
||||
<TradingFormInputBuyAsset
|
||||
inputLabel="TR_TRADING_YOU_BUY"
|
||||
inputName={TRADING_FORM_CRYPTO_CURRENCY_SELECT}
|
||||
inputDisabled={hasBitcoinOnlyFirmware(device)}
|
||||
onAssetSelect={handleCryptoSelect}
|
||||
includedCryptoIds={buySupportedCryptoIds}
|
||||
dataTestId="@trading/form/select-crypto-for-buy"
|
||||
<Column gap={20}>
|
||||
<TradingFormCard>
|
||||
<TradingFormSection>
|
||||
<Column gap={8}>
|
||||
<TradingFormInputFiatCrypto
|
||||
cryptoInputName={TRADING_FORM_CRYPTO_INPUT}
|
||||
fiatInputName={TRADING_FORM_FIAT_INPUT}
|
||||
cryptoSelectName={TRADING_FORM_CRYPTO_CURRENCY_SELECT}
|
||||
currencySelectLabel={currencySelect.label}
|
||||
cryptoCurrencyLabel={cryptoSelect.id}
|
||||
/>
|
||||
|
||||
{amountInCrypto && (
|
||||
<Row justifyContent="end">
|
||||
<TradingBalance
|
||||
balance={cryptoInput}
|
||||
displaySymbol={cryptoSelect.displaySymbol}
|
||||
symbol={cryptoSelect.networkSymbol}
|
||||
tokenAddress={
|
||||
(cryptoSelect.contractAddress as TokenAddress) ?? undefined
|
||||
}
|
||||
showOnlyAmount
|
||||
amountInCrypto={amountInCrypto}
|
||||
/>
|
||||
</Row>
|
||||
)}
|
||||
</Column>
|
||||
|
||||
{cryptoSelect && !isLoading && <TradingReceiveAddress />}
|
||||
</Column>
|
||||
</Card>
|
||||
<TradingFormInputBuyAsset
|
||||
inputLabel="TR_TRADING_YOU_BUY"
|
||||
inputName={TRADING_FORM_CRYPTO_CURRENCY_SELECT}
|
||||
inputDisabled={hasBitcoinOnlyFirmware(device)}
|
||||
onAssetSelect={handleCryptoSelect}
|
||||
includedCryptoIds={buySupportedCryptoIds}
|
||||
dataTestId="@trading/form/select-crypto-for-buy"
|
||||
/>
|
||||
</TradingFormSection>
|
||||
{cryptoSelect && !isLoading && <TradingReceiveAddress />}
|
||||
</TradingFormCard>
|
||||
|
||||
<Card paddingType="none">
|
||||
<Column gap={spacings.lg}>
|
||||
<Column
|
||||
gap={spacings.lg}
|
||||
padding={{ vertical: spacings.md, horizontal: spacings.lg }}
|
||||
>
|
||||
<TradingFormInputPaymentMethod label="TR_TRADING_PAYMENT_METHOD" />
|
||||
<TradingFormInputCountry label="TR_TRADING_COUNTRY" />
|
||||
</Column>
|
||||
</Column>
|
||||
<TradingFormCard>
|
||||
<TradingFormSection>
|
||||
<TradingFormInputPaymentMethod label="TR_TRADING_PAYMENT_METHOD" />
|
||||
<TradingFormInputCountry label="TR_TRADING_COUNTRY" />
|
||||
</TradingFormSection>
|
||||
<TradingSelectedOfferProvider />
|
||||
<Divider margin={0} />
|
||||
<Column
|
||||
gap={spacings.lg}
|
||||
padding={{ vertical: spacings.md, horizontal: spacings.lg }}
|
||||
>
|
||||
<TradingFormSection>
|
||||
<TradingFormFeesDisclamer />
|
||||
</Column>
|
||||
</Card>
|
||||
</TradingFormSection>
|
||||
</TradingFormCard>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -15,31 +15,32 @@ import {
|
||||
} from '@suite-common/trading';
|
||||
import { TokenAddress } from '@suite-common/wallet-types';
|
||||
import { convertAmountSubunitsToUnits } from '@suite-common/wallet-utils';
|
||||
import { Card, Column, Divider, FractionButton, Row } from '@trezor/components';
|
||||
import { Column, FractionButton, Row } from '@trezor/components';
|
||||
import { useCurrentRef } from '@trezor/react-utils';
|
||||
import { spacings } from '@trezor/theme';
|
||||
|
||||
import { Fees } from 'src/components/wallet/Fees/Fees';
|
||||
import { useDispatch, useSelector } from 'src/hooks/suite';
|
||||
import { useTradingAssetDecimals } from 'src/hooks/wallet/trading/form/common/useTradingAssetDecimals';
|
||||
import { useTradingFormContext } from 'src/hooks/wallet/trading/form/useTradingCommonForm';
|
||||
import { TradingBalance } from 'src/views/wallet/trading/common/TradingBalance';
|
||||
import { TradingFormInputFiatCrypto } from 'src/views/wallet/trading/common/TradingForm/TradingFormInput/TradingFormInputFiatCrypto/TradingFormInputFiatCrypto';
|
||||
|
||||
import { TradingFormCard } from './TradingFormCard';
|
||||
import { TradingFormFeesDisclamer } from './TradingFormFeeDisclamer';
|
||||
import { TradingFormFees } from './TradingFormFees';
|
||||
import { AssetPickerInputBalance } from './TradingFormInput/TradingFormInputAssetPicker';
|
||||
import {
|
||||
TradingFormInputBuyAsset,
|
||||
TradingFormInputBuyAssetProps,
|
||||
} from './TradingFormInput/TradingFormInputBuyAsset/TradingFormInputBuyAsset';
|
||||
import { TradingNetworkReserveBanner } from './TradingNetworkReserveBanner';
|
||||
import { generateFractionButtons } from './tradingFormInputsUtils';
|
||||
import { TradingReceiveAddress } from '../TradingSelectedOffer/TradingReceiveAddress/TradingReceiveAddress';
|
||||
import { TradingSelectedOfferProvider } from '../TradingSelectedOffer/TradingSelectedOfferProvider';
|
||||
import {
|
||||
TradingFormInputSellAsset,
|
||||
TradingFormInputSellAssetProps,
|
||||
} from './TradingFormInput/TradingFormInputSellAsset/TradingFormInputSellAsset';
|
||||
import { TradingFormSection } from './TradingFormSection';
|
||||
import { TradingNetworkReserveBanner } from './TradingNetworkReserveBanner';
|
||||
import { generateFractionButtons } from './tradingFormInputsUtils';
|
||||
import { TradingReceiveAddress } from '../TradingSelectedOffer/TradingReceiveAddress/TradingReceiveAddress';
|
||||
import { TradingSelectedOfferProvider } from '../TradingSelectedOffer/TradingSelectedOfferProvider';
|
||||
|
||||
export const TradingExchangeFormInputs = () => {
|
||||
const context = useTradingFormContext<TradingExchangeType>();
|
||||
@@ -115,8 +116,8 @@ export const TradingExchangeFormInputs = () => {
|
||||
const exchangeSellSupportedCryptoIds = useSelector(selectTradingExchangeSellCryptoIds);
|
||||
|
||||
return (
|
||||
<Card paddingType="none">
|
||||
<Column gap={spacings.lg} padding={spacings.lg}>
|
||||
<TradingFormCard>
|
||||
<TradingFormSection>
|
||||
<TradingFormInputSellAsset
|
||||
inputName={TRADING_FORM_SEND_CRYPTO_CURRENCY_SELECT}
|
||||
inputLabel="TR_FROM"
|
||||
@@ -128,7 +129,7 @@ export const TradingExchangeFormInputs = () => {
|
||||
dataTestId="@trading/form/select-crypto-for-sell"
|
||||
onAssetSelect={handleSellAssetSelect}
|
||||
/>
|
||||
<Column gap={spacings.xs}>
|
||||
<Column gap={8}>
|
||||
<TradingFormInputFiatCrypto
|
||||
cryptoInputName={TRADING_FORM_OUTPUT_AMOUNT}
|
||||
fiatInputName={TRADING_FORM_OUTPUT_FIAT}
|
||||
@@ -138,7 +139,7 @@ export const TradingExchangeFormInputs = () => {
|
||||
/>
|
||||
{amountInCrypto && (
|
||||
<Row justifyContent="space-between" alignItems="flex-start">
|
||||
<Row gap={spacings.xs} data-testid="@trading/form/fraction-buttons">
|
||||
<Row gap={8} data-testid="@trading/form/fraction-buttons">
|
||||
{generateFractionButtons(helpers).map(button => (
|
||||
<FractionButton
|
||||
key={button.id}
|
||||
@@ -179,24 +180,19 @@ export const TradingExchangeFormInputs = () => {
|
||||
onAssetSelect={handleReceiveAssetSelect}
|
||||
dataTestId="@trading/form/select-crypto-for-buy"
|
||||
/>
|
||||
</Column>
|
||||
</TradingFormSection>
|
||||
|
||||
{receiveCryptoSelect && !isLoading && <TradingReceiveAddress />}
|
||||
|
||||
<Divider margin={0} />
|
||||
<Fees
|
||||
<TradingFormFees
|
||||
feeInfo={feeInfo}
|
||||
account={account}
|
||||
composedLevels={composedLevels}
|
||||
changeFeeLevel={changeFeeLevel}
|
||||
isHeaderRowLayout
|
||||
/>
|
||||
<TradingSelectedOfferProvider />
|
||||
<Divider margin={0} />
|
||||
|
||||
<Column gap={spacings.lg} padding={{ vertical: spacings.lg, horizontal: spacings.lg }}>
|
||||
<TradingFormSection>
|
||||
<TradingFormFeesDisclamer />
|
||||
</Column>
|
||||
</Card>
|
||||
</TradingFormSection>
|
||||
</TradingFormCard>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Card, Column } from '@trezor/components';
|
||||
|
||||
export const TradingFormCard = ({ children }: { children: React.ReactNode }) => (
|
||||
<Card paddingType="none">
|
||||
<Column hasDivider>{children}</Column>
|
||||
</Card>
|
||||
);
|
||||
@@ -0,0 +1,42 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { GhostContainer } from '@trezor/components';
|
||||
|
||||
import { Fees, FeesProps } from 'src/components/wallet/Fees/Fees';
|
||||
|
||||
export type TradingFormFeesProps = Pick<
|
||||
FeesProps,
|
||||
'feeInfo' | 'account' | 'composedLevels' | 'changeFeeLevel'
|
||||
>;
|
||||
|
||||
export const TradingFormFees = ({
|
||||
feeInfo,
|
||||
account,
|
||||
composedLevels,
|
||||
changeFeeLevel,
|
||||
}: TradingFormFeesProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<GhostContainer
|
||||
padding={{ horizontal: 20, vertical: 12 }}
|
||||
borderRadius={0}
|
||||
onClick={() => {
|
||||
setIsOpen(!isOpen);
|
||||
if (document.activeElement instanceof HTMLElement) {
|
||||
document.activeElement.blur();
|
||||
}
|
||||
}}
|
||||
cursor="pointer"
|
||||
isActive={isOpen}
|
||||
>
|
||||
<Fees
|
||||
feeInfo={feeInfo}
|
||||
account={account}
|
||||
composedLevels={composedLevels}
|
||||
changeFeeLevel={changeFeeLevel}
|
||||
isOpen={isOpen}
|
||||
/>
|
||||
</GhostContainer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Column } from '@trezor/components';
|
||||
|
||||
export const TradingFormSection = ({ children }: { children: React.ReactNode }) => (
|
||||
<Column gap={20} padding={20}>
|
||||
{children}
|
||||
</Column>
|
||||
);
|
||||
@@ -11,11 +11,9 @@ import {
|
||||
} from '@suite-common/trading';
|
||||
import { TokenAddress } from '@suite-common/wallet-types';
|
||||
import { convertAmountSubunitsToUnits } from '@suite-common/wallet-utils';
|
||||
import { Card, Column, Divider, FractionButton, Row } from '@trezor/components';
|
||||
import { Column, FractionButton, Row } from '@trezor/components';
|
||||
import { useCurrentRef } from '@trezor/react-utils';
|
||||
import { spacings } from '@trezor/theme';
|
||||
|
||||
import { Fees } from 'src/components/wallet/Fees/Fees';
|
||||
import { useSelector } from 'src/hooks/suite';
|
||||
import { useTradingAssetDecimals } from 'src/hooks/wallet/trading/form/common/useTradingAssetDecimals';
|
||||
import { useTradingFormContext } from 'src/hooks/wallet/trading/form/useTradingCommonForm';
|
||||
@@ -24,15 +22,18 @@ import { TradingFormInputCountry } from 'src/views/wallet/trading/common/Trading
|
||||
import { TradingFormInputFiatCrypto } from 'src/views/wallet/trading/common/TradingForm/TradingFormInput/TradingFormInputFiatCrypto/TradingFormInputFiatCrypto';
|
||||
import { TradingFormInputPaymentMethod } from 'src/views/wallet/trading/common/TradingForm/TradingFormInput/TradingFormInputPaymentMethod';
|
||||
|
||||
import { TradingFormCard } from './TradingFormCard';
|
||||
import { TradingFormFeesDisclamer } from './TradingFormFeeDisclamer';
|
||||
import { TradingFormFees } from './TradingFormFees';
|
||||
import { AssetPickerInputBalance } from './TradingFormInput/TradingFormInputAssetPicker';
|
||||
import { TradingNetworkReserveBanner } from './TradingNetworkReserveBanner';
|
||||
import { generateFractionButtons } from './tradingFormInputsUtils';
|
||||
import { TradingSelectedOfferProvider } from '../TradingSelectedOffer/TradingSelectedOfferProvider';
|
||||
import {
|
||||
TradingFormInputSellAsset,
|
||||
TradingFormInputSellAssetProps,
|
||||
} from './TradingFormInput/TradingFormInputSellAsset/TradingFormInputSellAsset';
|
||||
import { TradingFormSection } from './TradingFormSection';
|
||||
import { TradingNetworkReserveBanner } from './TradingNetworkReserveBanner';
|
||||
import { generateFractionButtons } from './tradingFormInputsUtils';
|
||||
import { TradingSelectedOfferProvider } from '../TradingSelectedOffer/TradingSelectedOfferProvider';
|
||||
|
||||
export const TradingSellFormInputs = () => {
|
||||
const context = useTradingFormContext<TradingSellType>();
|
||||
@@ -78,8 +79,8 @@ export const TradingSellFormInputs = () => {
|
||||
const sellSupportedCryptoIds = useSelector(selectTradingSellSupportedCryptoIds);
|
||||
|
||||
return (
|
||||
<Card paddingType="none">
|
||||
<Column gap={spacings.lg} padding={{ vertical: spacings.md, horizontal: spacings.lg }}>
|
||||
<TradingFormCard>
|
||||
<TradingFormSection>
|
||||
<TradingFormInputSellAsset
|
||||
inputName={TRADING_FORM_SEND_CRYPTO_CURRENCY_SELECT}
|
||||
inputLabel="TR_TRADING_YOU_SELL"
|
||||
@@ -90,7 +91,7 @@ export const TradingSellFormInputs = () => {
|
||||
dataTestId="@trading/form/select-crypto-for-sell"
|
||||
onAssetSelect={handleSellAssetSelect}
|
||||
/>
|
||||
<Column gap={spacings.xs}>
|
||||
<Column gap={8}>
|
||||
<TradingFormInputFiatCrypto
|
||||
cryptoInputName={TRADING_FORM_OUTPUT_AMOUNT}
|
||||
fiatInputName={TRADING_FORM_OUTPUT_FIAT}
|
||||
@@ -100,7 +101,7 @@ export const TradingSellFormInputs = () => {
|
||||
/>
|
||||
{amountInCrypto && (
|
||||
<Row justifyContent="space-between" alignItems="flex-start">
|
||||
<Row gap={spacings.xs} data-testid="@trading/form/fraction-buttons">
|
||||
<Row gap={8} data-testid="@trading/form/fraction-buttons">
|
||||
{generateFractionButtons(helpers).map(button => (
|
||||
<FractionButton key={button.id} {...button} />
|
||||
))}
|
||||
@@ -117,32 +118,27 @@ export const TradingSellFormInputs = () => {
|
||||
</Row>
|
||||
)}
|
||||
</Column>
|
||||
|
||||
{showReserveBanner && (
|
||||
<TradingNetworkReserveBanner
|
||||
symbol={account.symbol}
|
||||
contractAddress={tokenAddress}
|
||||
/>
|
||||
)}
|
||||
</Column>
|
||||
<Divider margin={0} />
|
||||
<Fees
|
||||
</TradingFormSection>
|
||||
<TradingFormFees
|
||||
feeInfo={feeInfo}
|
||||
account={account}
|
||||
composedLevels={composedLevels}
|
||||
changeFeeLevel={changeFeeLevel}
|
||||
isHeaderRowLayout
|
||||
/>
|
||||
<Divider margin={0} />
|
||||
<Column gap={spacings.lg} padding={{ vertical: spacings.md, horizontal: spacings.lg }}>
|
||||
<TradingFormSection>
|
||||
<TradingFormInputPaymentMethod label="TR_TRADING_RECEIVE_METHOD" />
|
||||
<TradingFormInputCountry label="TR_TRADING_COUNTRY" />
|
||||
</Column>
|
||||
</TradingFormSection>
|
||||
<TradingSelectedOfferProvider />
|
||||
<Divider margin={0} />
|
||||
<Column gap={spacings.lg} padding={{ vertical: spacings.md, horizontal: spacings.lg }}>
|
||||
<TradingFormSection>
|
||||
<TradingFormFeesDisclamer />
|
||||
</Column>
|
||||
</Card>
|
||||
</TradingFormSection>
|
||||
</TradingFormCard>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { Translation } from '@suite/intl';
|
||||
import { Box, Column, Divider, Icon, Row, Text } from '@trezor/components';
|
||||
import { spacings } from '@trezor/theme';
|
||||
import { Column, GhostContainer, Icon, Row, Text } from '@trezor/components';
|
||||
|
||||
import { AccountLabeling, Address } from 'src/components/suite';
|
||||
|
||||
@@ -15,7 +14,7 @@ interface TradingReceiveAddressEmptyProps {
|
||||
}
|
||||
|
||||
export const TradingReceiveAddressEmpty = ({ title, text }: TradingReceiveAddressEmptyProps) => (
|
||||
<Column alignItems="center" gap={spacings.xxs} padding={{ vertical: spacings.md }}>
|
||||
<Column alignItems="center" gap={4} padding={{ vertical: 16 }}>
|
||||
<Text typographyStyle="body">{title}</Text>
|
||||
<Text typographyStyle="hint" variant="tertiary">
|
||||
{text}
|
||||
@@ -34,17 +33,18 @@ export const TradingReceiveAddress = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box cursor="pointer" backgroundColorOnInteraction="backgroundSurfaceElevation2">
|
||||
<Divider margin={0} />
|
||||
|
||||
<GhostContainer
|
||||
onClick={onReceiveAccountClick}
|
||||
data-testid="@trading/receive-address-picker"
|
||||
cursor="pointer"
|
||||
borderRadius={0}
|
||||
>
|
||||
<Row
|
||||
data-testid="@trading/receive-address-picker"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
onClick={onReceiveAccountClick}
|
||||
padding={{
|
||||
vertical: !receiveAddress ? spacings.lg : spacings.sm,
|
||||
horizontal: spacings.lg,
|
||||
vertical: selectedAccountOption?.account && receiveAddress ? 12 : 16,
|
||||
horizontal: 20,
|
||||
}}
|
||||
>
|
||||
<Text typographyStyle="body">
|
||||
@@ -60,12 +60,17 @@ export const TradingReceiveAddress = () => {
|
||||
<Column alignItems="flex-end">
|
||||
{selectedAccountOption?.account && receiveAddress ? (
|
||||
<>
|
||||
<AccountLabeling
|
||||
data-test-id="@trading/selected-receive-account"
|
||||
account={selectedAccountOption.account}
|
||||
accountTypeBadgeSize="small"
|
||||
showAccountTypeBadge
|
||||
/>
|
||||
<Text
|
||||
typographyStyle="body"
|
||||
as="div"
|
||||
data-testid="@trading/selected-receive-account"
|
||||
>
|
||||
<AccountLabeling
|
||||
account={selectedAccountOption.account}
|
||||
accountTypeBadgeSize="small"
|
||||
showAccountTypeBadge
|
||||
/>
|
||||
</Text>
|
||||
<Address
|
||||
value={receiveAddress}
|
||||
typographyStyle="hint"
|
||||
@@ -78,8 +83,8 @@ export const TradingReceiveAddress = () => {
|
||||
{receiveAddress ? (
|
||||
<Address
|
||||
value={receiveAddress}
|
||||
typographyStyle="hint"
|
||||
variant="tertiary"
|
||||
typographyStyle="body"
|
||||
variant="default"
|
||||
isTruncated
|
||||
/>
|
||||
) : (
|
||||
@@ -94,6 +99,6 @@ export const TradingReceiveAddress = () => {
|
||||
<Icon name="caretRight" size={20} variant="tertiary" />
|
||||
</Row>
|
||||
</Row>
|
||||
</Box>
|
||||
</GhostContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { Translation } from '@suite/intl';
|
||||
import { Column, Divider, Icon, Row, SkeletonRectangle, Text } from '@trezor/components';
|
||||
import { spacings } from '@trezor/theme';
|
||||
import { Column, GhostContainer, Icon, Row, SkeletonRectangle, Text } from '@trezor/components';
|
||||
|
||||
import { useTradingFormContext } from 'src/hooks/wallet/trading/form/useTradingCommonForm';
|
||||
import {
|
||||
@@ -20,7 +19,7 @@ interface TradingReceiveAddressEmptyProps {
|
||||
}
|
||||
|
||||
export const TradingReceiveAddressEmpty = ({ title, text }: TradingReceiveAddressEmptyProps) => (
|
||||
<Column alignItems="center" gap={spacings.xxs} padding={{ vertical: spacings.md }}>
|
||||
<Column alignItems="center" gap={4} padding={{ vertical: 16 }}>
|
||||
<Text typographyStyle="body">{title}</Text>
|
||||
<Text typographyStyle="hint" variant="tertiary">
|
||||
{text}
|
||||
@@ -49,15 +48,13 @@ export const TradingSelectedOfferProvider = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Column cursor="pointer">
|
||||
<Divider margin={0} />
|
||||
<Row
|
||||
data-testid="@trading/selected-offer-provider"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
onClick={onGoToOffers}
|
||||
padding={spacings.lg}
|
||||
>
|
||||
<GhostContainer
|
||||
onClick={onGoToOffers}
|
||||
cursor="pointer"
|
||||
data-testid="@trading/selected-offer-provider"
|
||||
borderRadius={0}
|
||||
>
|
||||
<Row alignItems="center" justifyContent="space-between" padding={20}>
|
||||
<Text typographyStyle="body">
|
||||
<Translation id="TR_TRADING_PROVIDER" />
|
||||
</Text>
|
||||
@@ -66,17 +63,17 @@ export const TradingSelectedOfferProvider = () => {
|
||||
<SkeletonRectangle animate />
|
||||
) : (
|
||||
<>
|
||||
<Column alignItems="flex-end">
|
||||
<Text typographyStyle="body" as="div">
|
||||
<TradingUtilsProvider
|
||||
providers={providers}
|
||||
exchange={quote.exchange}
|
||||
/>
|
||||
</Column>
|
||||
</Text>
|
||||
<Icon name="caretRight" size={20} variant="tertiary" />
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
</Row>
|
||||
</Column>
|
||||
</GhostContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@ export class FeeSection {
|
||||
this.page.getByTestId(`@fee-card/${feeType}-fiat-amount`);
|
||||
readonly rateOnCard = (feeType: FeeTypes) => this.page.getByTestId(`@fee-card/${feeType}-rate`);
|
||||
readonly collapsibleFeesToggle: Locator;
|
||||
readonly collapsibleFees: Locator;
|
||||
readonly maxFeeLoading: Locator;
|
||||
readonly customInput: Locator;
|
||||
readonly maxFee: Locator;
|
||||
@@ -31,6 +32,7 @@ export class FeeSection {
|
||||
|
||||
constructor(private readonly page: Page) {
|
||||
this.collapsibleFeesToggle = this.page.getByTestId('@wallet/fees/collapsible-fees-toggle');
|
||||
this.collapsibleFees = this.page.getByTestId('@wallet/fees/collapsible-fees');
|
||||
this.maxFeeLoading = this.page.getByTestId('@trading/quote/maximum-fee-amount-loading');
|
||||
this.customInput = this.page.getByTestId('feePerUnit');
|
||||
this.maxFee = this.page.getByTestId('@trading/quote/maximum-fee-amount');
|
||||
@@ -74,7 +76,7 @@ export class FeeSection {
|
||||
|
||||
@step()
|
||||
async openCollapsibleFees() {
|
||||
const isExpanded = await this.collapsibleFeesToggle.getAttribute('aria-expanded');
|
||||
const isExpanded = await this.collapsibleFees.getAttribute('aria-expanded');
|
||||
|
||||
if (isExpanded === 'true') {
|
||||
return;
|
||||
@@ -93,9 +95,9 @@ export class FeeSection {
|
||||
);
|
||||
}
|
||||
|
||||
await expect(this.collapsibleFeesToggle).toHaveAttribute('aria-expanded', 'false');
|
||||
await expect(this.collapsibleFees).toHaveAttribute('aria-expanded', 'false');
|
||||
await this.collapsibleFeesToggle.click();
|
||||
await expect(this.collapsibleFeesToggle).toHaveAttribute('aria-expanded', 'true');
|
||||
await expect(this.collapsibleFees).toHaveAttribute('aria-expanded', 'true');
|
||||
}
|
||||
|
||||
@step()
|
||||
|
||||
Reference in New Issue
Block a user