diff --git a/packages/components/src/components/Collapsible/Collapsible.tsx b/packages/components/src/components/Collapsible/Collapsible.tsx index 55848b3aa7..54beec551d 100644 --- a/packages/components/src/components/Collapsible/Collapsible.tsx +++ b/packages/components/src/components/Collapsible/Collapsible.tsx @@ -38,7 +38,9 @@ export const Collapsible = ({ gap, }} > - {children} + + {children} + ); }; diff --git a/packages/components/src/components/Collapsible/CollapsibleContent.tsx b/packages/components/src/components/Collapsible/CollapsibleContent.tsx index a0cca332ab..e0ead87c20 100644 --- a/packages/components/src/components/Collapsible/CollapsibleContent.tsx +++ b/packages/components/src/components/Collapsible/CollapsibleContent.tsx @@ -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, '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} > {children} diff --git a/packages/suite/src/components/wallet/Fees/CollapsibleFees/CollapsibleFees.tsx b/packages/suite/src/components/wallet/Fees/CollapsibleFees/CollapsibleFees.tsx index b9ad96cc6d..bcc2ca2932 100644 --- a/packages/suite/src/components/wallet/Fees/CollapsibleFees/CollapsibleFees.tsx +++ b/packages/suite/src/components/wallet/Fees/CollapsibleFees/CollapsibleFees.tsx @@ -19,8 +19,9 @@ export type CollapsibleFeesProps = { networkSymbol: NetworkSymbol; networkType: NetworkType; rbfForm?: boolean; + isOpen?: boolean; } & Pick & - Omit; + Omit; export function CollapsibleFees({ label, @@ -31,7 +32,7 @@ export function CollapsibleFees({ changeFeeLevel, rbfForm, headerTypographyStyle = 'body', - isHeaderRowLayout, + isOpen, }: CollapsibleFeesProps) { const selectedFee = useWatch({ name: 'selectedFee', @@ -68,17 +69,17 @@ export function CollapsibleFees({ composedLevels, }} > - + {supportsAdjustableFees && ( - + ev.stopPropagation()}> {!isCustomFee && } diff --git a/packages/suite/src/components/wallet/Fees/CollapsibleFees/CollapsibleFeesHeader.tsx b/packages/suite/src/components/wallet/Fees/CollapsibleFees/CollapsibleFeesHeader.tsx index 64078401e0..b2ef6e9310 100644 --- a/packages/suite/src/components/wallet/Fees/CollapsibleFees/CollapsibleFeesHeader.tsx +++ b/packages/suite/src/components/wallet/Fees/CollapsibleFees/CollapsibleFeesHeader.tsx @@ -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 ( - + ; + isOpen?: boolean; }; export const CollapsibleFeesHeaderContent = ({ label, headerTypographyStyle = 'body', supportsAdjustableFees, - isHeaderRowLayout, txMaxFee, + isOpen, }: CollapsibleFeesHeaderContentProps) => { const content = ( - + @@ -35,20 +39,5 @@ export const CollapsibleFeesHeaderContent = ({ ); - return ( - - {isHeaderRowLayout ? ( - - {content} - - ) : ( - content - )} - - ); + return isOpen !== undefined ? content : {content}; }; diff --git a/packages/suite/src/components/wallet/Fees/Fees.tsx b/packages/suite/src/components/wallet/Fees/Fees.tsx index 108baf6193..ad55941b31 100644 --- a/packages/suite/src/components/wallet/Fees/Fees.tsx +++ b/packages/suite/src/components/wallet/Fees/Fees.tsx @@ -9,10 +9,15 @@ import { FieldErrorBanner } from './FieldErrorBanner'; export type FeesProps = { account: Pick; - 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} /> diff --git a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingBuyFormInputs.tsx b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingBuyFormInputs.tsx index 3c345592a9..4a96fcc50c 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingBuyFormInputs.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingBuyFormInputs.tsx @@ -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(); @@ -61,76 +62,56 @@ export const TradingBuyFormInputs = () => { const buySupportedCryptoIds = useSelector(selectTradingBuySupportedCryptoIds); return ( - - - - - - - - {amountInCrypto && ( - - - - )} - - - + + + + + + {amountInCrypto && ( + + + + )} - {cryptoSelect && !isLoading && } - - + + + {cryptoSelect && !isLoading && } + - - - - - - - + + + + + - - + - - + + ); }; diff --git a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingExchangeFormInputs.tsx b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingExchangeFormInputs.tsx index 1ca2344f16..768cd1325f 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingExchangeFormInputs.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingExchangeFormInputs.tsx @@ -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(); @@ -115,8 +116,8 @@ export const TradingExchangeFormInputs = () => { const exchangeSellSupportedCryptoIds = useSelector(selectTradingExchangeSellCryptoIds); return ( - - + + { dataTestId="@trading/form/select-crypto-for-sell" onAssetSelect={handleSellAssetSelect} /> - + { /> {amountInCrypto && ( - + {generateFractionButtons(helpers).map(button => ( { onAssetSelect={handleReceiveAssetSelect} dataTestId="@trading/form/select-crypto-for-buy" /> - + {receiveCryptoSelect && !isLoading && } - - - - - - + - - + + ); }; diff --git a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormCard.tsx b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormCard.tsx new file mode 100644 index 0000000000..feff6a762f --- /dev/null +++ b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormCard.tsx @@ -0,0 +1,7 @@ +import { Card, Column } from '@trezor/components'; + +export const TradingFormCard = ({ children }: { children: React.ReactNode }) => ( + + {children} + +); diff --git a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormFees.tsx b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormFees.tsx new file mode 100644 index 0000000000..1b4bb7f7fe --- /dev/null +++ b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormFees.tsx @@ -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 ( + { + setIsOpen(!isOpen); + if (document.activeElement instanceof HTMLElement) { + document.activeElement.blur(); + } + }} + cursor="pointer" + isActive={isOpen} + > + + + ); +}; diff --git a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormSection.tsx b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormSection.tsx new file mode 100644 index 0000000000..a3f8c7571d --- /dev/null +++ b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormSection.tsx @@ -0,0 +1,7 @@ +import { Column } from '@trezor/components'; + +export const TradingFormSection = ({ children }: { children: React.ReactNode }) => ( + + {children} + +); diff --git a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingSellFormInputs.tsx b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingSellFormInputs.tsx index a6576fc8b2..fee5e0c34d 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingSellFormInputs.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingSellFormInputs.tsx @@ -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(); @@ -78,8 +79,8 @@ export const TradingSellFormInputs = () => { const sellSupportedCryptoIds = useSelector(selectTradingSellSupportedCryptoIds); return ( - - + + { dataTestId="@trading/form/select-crypto-for-sell" onAssetSelect={handleSellAssetSelect} /> - + { /> {amountInCrypto && ( - + {generateFractionButtons(helpers).map(button => ( ))} @@ -117,32 +118,27 @@ export const TradingSellFormInputs = () => { )} - {showReserveBanner && ( )} - - - + - - + - + - - + - - + + ); }; diff --git a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingReceiveAddress/TradingReceiveAddress.tsx b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingReceiveAddress/TradingReceiveAddress.tsx index b5374eb414..649bd460ed 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingReceiveAddress/TradingReceiveAddress.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingReceiveAddress/TradingReceiveAddress.tsx @@ -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) => ( - + {title} {text} @@ -34,17 +33,18 @@ export const TradingReceiveAddress = () => { }; return ( - - - + @@ -60,12 +60,17 @@ export const TradingReceiveAddress = () => { {selectedAccountOption?.account && receiveAddress ? ( <> - + + +
{ {receiveAddress ? (
) : ( @@ -94,6 +99,6 @@ export const TradingReceiveAddress = () => { - + ); }; diff --git a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingSelectedOfferProvider.tsx b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingSelectedOfferProvider.tsx index 0a937935e3..c1474b499d 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingSelectedOfferProvider.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingSelectedOffer/TradingSelectedOfferProvider.tsx @@ -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) => ( - + {title} {text} @@ -49,15 +48,13 @@ export const TradingSelectedOfferProvider = () => { } return ( - - - + + @@ -66,17 +63,17 @@ export const TradingSelectedOfferProvider = () => { ) : ( <> - + - + )} - + ); }; diff --git a/suite/e2e/support/pageObjects/feeSection.ts b/suite/e2e/support/pageObjects/feeSection.ts index d8f2b5c171..e6b33dd8c4 100644 --- a/suite/e2e/support/pageObjects/feeSection.ts +++ b/suite/e2e/support/pageObjects/feeSection.ts @@ -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()