From 869bc6c81ec077f4af33cad324593182a6e271cf Mon Sep 17 00:00:00 2001 From: Adam Havel Date: Thu, 16 Jan 2025 09:04:14 +0100 Subject: [PATCH] feat(suite): refactor advanced coin settings modal --- .../src/components/List/ListItem.tsx | 9 +- .../src/components/NewModal/NewModal.tsx | 2 +- .../src/components/typography/Text/Text.tsx | 1 + .../src/components/typography/utils.tsx | 30 ++- .../AdvancedCoinSettingsModal.tsx | 236 +++++++++++++----- .../CustomBackends/BackendInput.tsx | 47 ---- .../CustomBackends/BackendTypeSelect.tsx | 1 + .../CustomBackends/ConnectionInfo.tsx | 20 +- .../CustomBackends/CustomBackends.tsx | 182 -------------- .../CustomBackends/TorModal.tsx | 43 ++-- packages/suite/src/support/messages.ts | 4 + submodules/trezor-common | 2 +- 12 files changed, 244 insertions(+), 333 deletions(-) delete mode 100644 packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/BackendInput.tsx delete mode 100644 packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/CustomBackends.tsx diff --git a/packages/components/src/components/List/ListItem.tsx b/packages/components/src/components/List/ListItem.tsx index fa45a418e62..6ec92fa1e58 100644 --- a/packages/components/src/components/List/ListItem.tsx +++ b/packages/components/src/components/List/ListItem.tsx @@ -42,13 +42,18 @@ const ContentWrapper = styled.div` export type ListItemProps = { children: React.ReactNode; bulletComponent?: React.ReactNode; + 'data-testid'?: string; }; -export const ListItem = ({ bulletComponent, children }: ListItemProps) => { +export const ListItem = ({ + bulletComponent, + 'data-testid': dataTestId, + children, +}: ListItemProps) => { const { bulletGap, bulletAlignment, bulletComponent: listBulletComponent } = useList(); return ( - + {bulletComponent ?? listBulletComponent} {children} diff --git a/packages/components/src/components/NewModal/NewModal.tsx b/packages/components/src/components/NewModal/NewModal.tsx index 4e488e9fead..e3bc6b58e09 100644 --- a/packages/components/src/components/NewModal/NewModal.tsx +++ b/packages/components/src/components/NewModal/NewModal.tsx @@ -104,7 +104,7 @@ const InnerNewModalBase = ({ {hasHeader && ( diff --git a/packages/components/src/components/typography/Text/Text.tsx b/packages/components/src/components/typography/Text/Text.tsx index b2e54c96e8f..aaa03964aa4 100644 --- a/packages/components/src/components/typography/Text/Text.tsx +++ b/packages/components/src/components/typography/Text/Text.tsx @@ -24,6 +24,7 @@ export const allowedTextTextProps = [ 'textWrap', 'align', 'ellipsisLineCount', + 'case', ] as const satisfies TextPropsKeys[]; type AllowedTextTextProps = Pick; diff --git a/packages/components/src/components/typography/utils.tsx b/packages/components/src/components/typography/utils.tsx index 2e72840cec6..b0e4b9199c0 100644 --- a/packages/components/src/components/typography/utils.tsx +++ b/packages/components/src/components/typography/utils.tsx @@ -8,11 +8,15 @@ import { TransientProps, makePropsTransient } from '../../utils/transientProps'; export const textWraps = ['balance', 'break-word', 'pretty', 'nowrap']; export type TextWrap = (typeof textWraps)[number]; +export const textCase = ['uppercase', 'lowercase', 'titlecase'] as const; +export type TextCase = (typeof textCase)[number]; + export type TextProps = { typographyStyle?: TypographyStyle; textWrap?: TextWrap; align?: UIHorizontalAlignment; ellipsisLineCount?: number; + case?: TextCase; }; export type TextPropsKeys = keyof TextProps; @@ -32,6 +36,7 @@ export const withTextProps = ({ $typographyStyle, $align, $ellipsisLineCount = 0, + $case, }: TransientTextProps) => css` ${$textWrap && css` @@ -42,23 +47,34 @@ export const withTextProps = ({ ${typography[$typographyStyle]} ` : ''} - ${$align && + ${$align && css` text-align: ${$align}; `} - ${$ellipsisLineCount > 0 && + ${$ellipsisLineCount > 0 && css` text-overflow: ellipsis; overflow: hidden; white-space: nowrap; `} - ${$ellipsisLineCount > 1 && + ${$ellipsisLineCount > 1 && css` white-space: initial; -webkit-line-clamp: ${$ellipsisLineCount}; display: -webkit-box; -webkit-box-orient: vertical; `} + ${$case && $case === 'titlecase' + ? css` + text-transform: lowercase; + + &::first-letter { + text-transform: uppercase; + } + ` + : css` + text-transform: ${$case}; + `} `; const getStorybookType = (key: TextPropsKeys) => { @@ -91,6 +107,13 @@ const getStorybookType = (key: TextPropsKeys) => { min: 0, }, }; + case 'case': + return { + options: [undefined, ...textCase], + control: { + type: 'select', + }, + }; default: return { control: { @@ -120,6 +143,7 @@ export const getTextPropsStory = (allowedTextProps: Array) => { ...(allowedTextProps.includes('typographyStyle') ? { typographyStyle: undefined } : {}), ...(allowedTextProps.includes('align') ? { align: undefined } : {}), ...(allowedTextProps.includes('ellipsisLineCount') ? { hasEllipsis: undefined } : {}), + ...(allowedTextProps.includes('case') ? { case: undefined } : {}), }, argTypes, }; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/AdvancedCoinSettingsModal.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/AdvancedCoinSettingsModal.tsx index 3f2e4273bf4..ec679924028 100644 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/AdvancedCoinSettingsModal.tsx +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/AdvancedCoinSettingsModal.tsx @@ -1,77 +1,197 @@ -import styled from 'styled-components'; +import { useState } from 'react'; import { type NetworkSymbol, getNetwork } from '@suite-common/wallet-config'; -import { variables } from '@trezor/components'; -import { CoinLogo } from '@trezor/product-components'; +import { + Button, + Card, + CollapsibleBox, + Column, + DotIndicator, + Input, + List, + NewModal, + Row, + Text, +} from '@trezor/components'; +import { spacings } from '@trezor/theme'; -import { Modal, Translation } from 'src/components/suite'; -import { useSelector } from 'src/hooks/suite'; -import { getCoinLabel } from 'src/utils/suite/getCoinLabel'; +import { toggleTor } from 'src/actions/suite/suiteActions'; +import { Translation } from 'src/components/suite'; +import { useBackendsForm, useDefaultUrls } from 'src/hooks/settings/backends'; +import { useDispatch, useSelector } from 'src/hooks/suite'; +import { selectTorState } from 'src/reducers/suite/suiteReducer'; -import { CustomBackends } from './CustomBackends/CustomBackends'; +import { BackendTypeSelect } from './CustomBackends/BackendTypeSelect'; +import ConnectionInfo from './CustomBackends/ConnectionInfo'; +import { TorModal, TorResult } from './CustomBackends/TorModal'; -const Section = styled.div` - display: flex; - flex-direction: column; -`; +type AdvancedCoinSettingsModalProps = { + symbol: NetworkSymbol; + onCancel: () => void; +}; -const Heading = styled.div` - display: flex; - align-items: center; - line-height: initial; +export const AdvancedCoinSettingsModal = ({ symbol, onCancel }: AdvancedCoinSettingsModalProps) => { + const network = getNetwork(symbol); + const { isTorEnabled } = useSelector(selectTorState); + const blockchain = useSelector(state => state.wallet.blockchain); + const dispatch = useDispatch(); + const [torModalOpen, setTorModalOpen] = useState(false); - > * + * { - margin-left: 16px; - } -`; + const { + type, + urls, + input: { error, name, placeholder, register, reset, validate, value }, + changeType, + addUrl, + removeUrl, + save, + hasOnlyOnions, + } = useBackendsForm(symbol); -const Header = styled.div` - display: flex; - flex-direction: column; -`; + const onSaveClick = () => { + if (!isTorEnabled && hasOnlyOnions()) { + setTorModalOpen(true); + } else { + save(); + onCancel(); + } + }; -const Subheader = styled.span` - font-size: ${variables.FONT_SIZE.NORMAL}; - font-weight: ${variables.FONT_WEIGHT.MEDIUM}; - color: ${({ theme }) => theme.legacy.TYPE_LIGHT_GREY}; -`; + const onTorResult = async (result: TorResult) => { + switch (result) { + case 'enable-tor': + await dispatch(toggleTor(true)); -interface AdvancedCoinSettingsModalProps { - symbol: NetworkSymbol; - onCancel: () => void; -} + setTorModalOpen(false); + save(); + onCancel(); -export const AdvancedCoinSettingsModal = ({ symbol, onCancel }: AdvancedCoinSettingsModalProps) => { - const blockchain = useSelector(state => state.wallet.blockchain); - const network = getNetwork(symbol); + break; + case 'use-defaults': + changeType('default'); + setTorModalOpen(false); + + // no default + } + }; - const { name, features, testnet: isTestnet } = network; - const hasCustomBackend = !!blockchain[symbol].backends.selected; - const label = getCoinLabel(features, isTestnet, hasCustomBackend); + const { defaultUrls } = useDefaultUrls(symbol); + const { ref: inputRef, ...inputField } = register(name, { validate }); + const isEditable = type !== 'default'; + const isSubmitButtonDisabled = isEditable && !!error; - return ( - + ) : ( + - - -
- {name} - - {label && ( - - - - )} -
- + + {network.name} + + } + description={ + + } + size="small" + bottomContent={ + <> + + + + + + + } > -
- -
-
+ + + } + > + + {(urls.length || (!isEditable && defaultUrls.length)) && ( + } gap={spacings.sm}> + {(isEditable ? urls : defaultUrls).map(url => ( + + ) : undefined + } + > + + + {url} + + {isEditable && ( + + )} + + + ))} + + )} + {isEditable && ( + + { + addUrl(value); + reset(); + }} + isDisabled={!!error || value === ''} + > + + + } + {...inputField} + /> + + )} + + + }> + + + + ); }; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/BackendInput.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/BackendInput.tsx deleted file mode 100644 index c9b15cf48b7..00000000000 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/BackendInput.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { Input, Spinner, Tooltip } from '@trezor/components'; - -import { StatusLight, Translation } from 'src/components/suite'; - -const ActiveStatus = ( - }> - - -); - -type BackendInputProps = { - url: string; - isActive: boolean; - isLoading?: boolean; - onRemove?: () => void; - 'data-testid'?: string; -}; - -export const BackendInput = ({ - url, - isActive, - isLoading, - onRemove, - 'data-testid': dataTest, -}: BackendInputProps) => { - const getInnerAddon = () => { - if (isLoading) { - return ; - } - if (isActive) { - return ActiveStatus; - } - - return undefined; - }; - - return ( - - ); -}; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/BackendTypeSelect.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/BackendTypeSelect.tsx index bdae13d781f..549e95853be 100644 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/BackendTypeSelect.tsx +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/BackendTypeSelect.tsx @@ -65,6 +65,7 @@ export const BackendTypeSelect = ({ network, value, onChange }: BackendTypeSelec onChange={changeType} options={backendOptions} data-testid="@settings/advance/select-type" + size="small" /> ) : null; }; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/ConnectionInfo.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/ConnectionInfo.tsx index aa122a931a9..94137a4da37 100644 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/ConnectionInfo.tsx +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/ConnectionInfo.tsx @@ -1,21 +1,12 @@ -import styled from 'styled-components'; - import { type NetworkSymbol } from '@suite-common/wallet-config'; import { Paragraph } from '@trezor/components'; import { Translation } from 'src/components/suite/Translation'; import { useSelector } from 'src/hooks/suite'; -// eslint-disable-next-line local-rules/no-override-ds-component -const Wrapper = styled(Paragraph)` - display: flex; - flex-direction: column; - text-align: left; -`; - -interface ConnectionInfoProps { +type ConnectionInfoProps = { symbol: NetworkSymbol; -} +}; const ConnectionInfo = ({ symbol }: ConnectionInfoProps) => { const blockchain = useSelector(state => state.wallet.blockchain); @@ -23,21 +14,24 @@ const ConnectionInfo = ({ symbol }: ConnectionInfoProps) => { const { connected, url, blockHash: hash, blockHeight: height, version } = blockchain[symbol]; return ( - + {connected ? ( <> +
+
+
) : ( )} -
+ ); }; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/CustomBackends.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/CustomBackends.tsx deleted file mode 100644 index 3eb92cff6f6..00000000000 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/CustomBackends.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import { useState } from 'react'; - -import styled from 'styled-components'; - -import { Network } from '@suite-common/wallet-config'; -import { Button, CollapsibleBox, Column, H3, Input } from '@trezor/components'; -import { spacings } from '@trezor/theme'; - -import { toggleTor } from 'src/actions/suite/suiteActions'; -import { TooltipSymbol, Translation } from 'src/components/suite'; -import { useBackendsForm, useDefaultUrls } from 'src/hooks/settings/backends'; -import { useDispatch, useSelector } from 'src/hooks/suite'; -import { selectTorState } from 'src/reducers/suite/suiteReducer'; - -import { BackendInput } from './BackendInput'; -import { BackendTypeSelect } from './BackendTypeSelect'; -import ConnectionInfo from './ConnectionInfo'; -import { TorModal, TorResult } from './TorModal'; - -// eslint-disable-next-line local-rules/no-override-ds-component -const AddUrlButton = styled(Button)` - align-self: end; -`; - -// eslint-disable-next-line local-rules/no-override-ds-component -const Heading = styled(H3)` - display: flex; - align-items: center; - color: ${({ theme }) => theme.legacy.TYPE_DARK_GREY}; - margin-bottom: 6px; -`; - -// eslint-disable-next-line local-rules/no-override-ds-component -const SaveButton = styled(Button)` - width: 200px; - margin-top: 30px; - align-self: center; -`; - -interface CustomBackendsProps { - network: Network; - onCancel: () => void; -} - -export const CustomBackends = ({ network, onCancel }: CustomBackendsProps) => { - const { isTorEnabled } = useSelector(selectTorState); - const blockchain = useSelector(state => state.wallet.blockchain); - const dispatch = useDispatch(); - const [torModalOpen, setTorModalOpen] = useState(false); - - const { symbol } = network; - - const { - type, - urls, - input: { error, name, placeholder, register, reset, validate, value }, - changeType, - addUrl, - removeUrl, - save, - hasOnlyOnions, - } = useBackendsForm(symbol); - - const onSaveClick = () => { - if (!isTorEnabled && hasOnlyOnions()) { - setTorModalOpen(true); - } else { - save(); - onCancel(); - } - }; - - const onTorResult = async (result: TorResult) => { - switch (result) { - case 'enable-tor': - await dispatch(toggleTor(true)); - - setTorModalOpen(false); - save(); - onCancel(); - - break; - case 'use-defaults': - changeType('default'); - setTorModalOpen(false); - - // no default - } - }; - - const { defaultUrls, isLoading } = useDefaultUrls(symbol); - const { ref: inputRef, ...inputField } = register(name, { validate }); - const isEditable = type !== 'default'; - const isSubmitButtonDisabled = isEditable && !!error; - - return ( - <> - - - - - - - - } - /> - - - - - {(isEditable ? urls : defaultUrls).map(url => ( - removeUrl(url) : undefined} - /> - ))} - - {isEditable && ( - - )} - - {isEditable && ( - { - addUrl(value); - reset(); - }} - isDisabled={!!error || value === ''} - > - - - )} - - } - margin={{ top: spacings.md }} - > - - - - - - - - - {torModalOpen && } - - ); -}; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/TorModal.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/TorModal.tsx index 416052bb980..b0c9047abb1 100644 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/TorModal.tsx +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/AdvancedCoinSettingsModal/CustomBackends/TorModal.tsx @@ -1,21 +1,10 @@ -import styled from 'styled-components'; - -import { Button, Paragraph } from '@trezor/components'; +import { H3, NewModal, Paragraph } from '@trezor/components'; import { isDesktop } from '@trezor/env-utils'; -import { Modal, Translation } from 'src/components/suite'; +import { Translation } from 'src/components/suite'; import { useSelector } from 'src/hooks/suite/useSelector'; import { getIsTorLoading } from 'src/utils/suite/tor'; -const StyledModal = styled(Modal)` - width: 550px; -`; - -// eslint-disable-next-line local-rules/no-override-ds-component -const Content = styled(Paragraph)` - margin: 16px 0; -`; - export type TorResult = 'use-defaults' | 'enable-tor'; type TorModalProps = { @@ -26,29 +15,31 @@ export const TorModal = ({ onResult }: TorModalProps) => { const isTorLoading = useSelector(state => getIsTorLoading(state.suite.torStatus)); return ( - } - bottomBarComponents={ + - - {isDesktop() && ( - + )} + onResult('use-defaults')}> + + } + size="small" + iconName="tor" > - +

+ +

+ -
-
+ + ); }; diff --git a/packages/suite/src/support/messages.ts b/packages/suite/src/support/messages.ts index c4a604a5206..fd2def6fba2 100644 --- a/packages/suite/src/support/messages.ts +++ b/packages/suite/src/support/messages.ts @@ -4511,6 +4511,10 @@ export default defineMessages({ id: 'TR_BACKUP_CREATED', defaultMessage: 'Wallet backup complete', }, + TR_REMOVE: { + id: 'TR_REMOVE', + defaultMessage: 'Remove', + }, TR_FIRMWARE_STATUS_INSTALLATION_COMPLETED: { id: 'TR_FIRMWARE_STATUS_INSTALLATION_COMPLETED', defaultMessage: 'Completed', diff --git a/submodules/trezor-common b/submodules/trezor-common index e8792ecc265..739207fa93b 160000 --- a/submodules/trezor-common +++ b/submodules/trezor-common @@ -1 +1 @@ -Subproject commit e8792ecc2659d756798f38c85482238c8a9da942 +Subproject commit 739207fa93b09597c1cb815cd0669c46de2bbb52