Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(suite): refactor advanced coin settings modal #17434

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions packages/components/src/components/List/ListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Item $gap={bulletGap} $bulletAlignment={bulletAlignment}>
<Item $gap={bulletGap} $bulletAlignment={bulletAlignment} data-testid={dataTestId}>
<BulletWrapper>{bulletComponent ?? listBulletComponent}</BulletWrapper>
<ContentWrapper>{children}</ContentWrapper>
</Item>
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/components/NewModal/NewModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ const InnerNewModalBase = ({
{hasHeader && (
<Row
padding={{ horizontal: spacings.md, top: spacings.md }}
alignItems="center"
alignItems={description ? 'flex-start' : 'center'}
gap={spacings.md}
as="header"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const allowedTextTextProps = [
'textWrap',
'align',
'ellipsisLineCount',
'case',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prop name seems to me very confusing. Why not keep textTransform?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I know why you did it. It's because titlecase is non-standard. OK then...

] as const satisfies TextPropsKeys[];
type AllowedTextTextProps = Pick<TextPropsCommon, (typeof allowedTextTextProps)[number]>;

Expand Down
30 changes: 27 additions & 3 deletions packages/components/src/components/typography/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -32,6 +36,7 @@ export const withTextProps = ({
$typographyStyle,
$align,
$ellipsisLineCount = 0,
$case,
}: TransientTextProps) => css`
${$textWrap &&
css`
Expand All @@ -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) => {
Expand Down Expand Up @@ -91,6 +107,13 @@ const getStorybookType = (key: TextPropsKeys) => {
min: 0,
},
};
case 'case':
return {
options: [undefined, ...textCase],
control: {
type: 'select',
},
};
default:
return {
control: {
Expand Down Expand Up @@ -120,6 +143,7 @@ export const getTextPropsStory = (allowedTextProps: Array<TextPropsKeys>) => {
...(allowedTextProps.includes('typographyStyle') ? { typographyStyle: undefined } : {}),
...(allowedTextProps.includes('align') ? { align: undefined } : {}),
...(allowedTextProps.includes('ellipsisLineCount') ? { hasEllipsis: undefined } : {}),
...(allowedTextProps.includes('case') ? { case: undefined } : {}),
},
argTypes,
};
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: maybe torModalOpened or isTorModalOpened?


> * + * {
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 (
<Modal
isCancelable
return torModalOpen ? (
<TorModal onResult={onTorResult} />
) : (
<NewModal
onCancel={onCancel}
heading={
<Heading>
<CoinLogo symbol={symbol} />

<Header>
<span>{name}</span>

{label && (
<Subheader>
<Translation id={label} />
</Subheader>
)}
</Header>
</Heading>
<Text case="titlecase" as="p">
{network.name} <Translation id="TR_BACKENDS" />
</Text>
}
description={
<Translation
id={
network?.networkType === 'cardano'
? 'SETTINGS_ADV_COIN_BLOCKFROST_DESCRIPTION'
: 'SETTINGS_ADV_COIN_BLOCKBOOK_DESCRIPTION'
}
/>
}
size="small"
bottomContent={
<>
<NewModal.Button
onClick={onSaveClick}
isDisabled={isSubmitButtonDisabled}
data-testid="@settings/advance/button/save"
>
<Translation id="TR_CONFIRM" />
</NewModal.Button>
<NewModal.Button onClick={onCancel} variant="tertiary">
<Translation id="TR_CANCEL" />
</NewModal.Button>
</>
}
>
<Section>
<CustomBackends network={network} onCancel={onCancel} />
</Section>
</Modal>
<Column gap={spacings.lg}>
<Card
heading={
<BackendTypeSelect network={network} value={type} onChange={changeType} />
}
>
<Column gap={spacings.xxl}>
{(urls.length || (!isEditable && defaultUrls.length)) && (
<List bulletComponent={<DotIndicator />} gap={spacings.sm}>
{(isEditable ? urls : defaultUrls).map(url => (
<List.Item
data-testid="@settings/advance/url"
key={url}
bulletComponent={
url === blockchain[symbol]?.url ? (
<DotIndicator isActive />
) : undefined
}
>
<Row gap={spacings.sm}>
<Text
variant={
url === blockchain[symbol]?.url
? 'default'
: 'tertiary'
}
>
{url}
</Text>
{isEditable && (
<Button
variant="tertiary"
size="tiny"
icon="trash"
onClick={() => removeUrl(url)}
>
<Translation id="TR_REMOVE" />
</Button>
)}
</Row>
</List.Item>
))}
</List>
)}
{isEditable && (
<Column gap={spacings.sm}>
<Input
data-testid={`@settings/advance/${name}`}
placeholder={placeholder}
inputState={error ? 'error' : undefined}
bottomText={error?.message || null}
innerRef={inputRef}
innerAddon={
<Button
variant="primary"
size="tiny"
icon="plus"
data-testid="@settings/advance/button/add"
onClick={() => {
addUrl(value);
reset();
}}
isDisabled={!!error || value === ''}
>
<Translation id="TR_ADD_NEW_BLOCKBOOK_BACKEND" />
</Button>
}
{...inputField}
/>
</Column>
)}
</Column>
</Card>
<CollapsibleBox heading={<Translation id="SETTINGS_ADV_COIN_CONN_INFO_TITLE" />}>
<ConnectionInfo symbol={symbol} />
</CollapsibleBox>
</Column>
</NewModal>
);
};
Loading