From 5b6bbfc301cfe501bcf5dd49ac519a17e1f2754b Mon Sep 17 00:00:00 2001 From: kyranjamie Date: Fri, 23 Feb 2024 13:26:27 +0100 Subject: [PATCH] refactor(send-form): remove custom dropdown type --- .../send-inscription-form.tsx | 4 +- ...d.tsx => recipient-address-type-field.tsx} | 9 +-- .../generic-recipient-field.tsx | 62 -------------- .../hooks/use-recipient-select-fields.tsx | 26 +++--- ....tsx => recipient-bns-name-type-field.tsx} | 14 ++-- .../recipient-fields/recipient-field.tsx | 55 +++++++++++++ .../components/recipient-dropdown-item.tsx | 43 ---------- .../components/recipient-dropdown-overlay.tsx | 23 ------ .../components/recipient-dropdown.layout.tsx | 49 ----------- .../recipient-type-dropdown.tsx | 81 ++++++++++++------- .../components/bitcoin-recipient-field.tsx | 4 +- .../components/stacks-recipient-field.tsx | 4 +- .../dowpdown-menu/dropdown-menu.tsx | 2 +- src/app/ui/components/flag/flag.tsx | 15 +++- src/shared/utils/type-utils.ts | 2 + tests/selectors/send.selectors.ts | 2 +- 16 files changed, 153 insertions(+), 242 deletions(-) rename src/app/pages/send/send-crypto-asset-form/components/{recipient-field.tsx => recipient-address-type-field.tsx} (89%) delete mode 100644 src/app/pages/send/send-crypto-asset-form/components/recipient-fields/generic-recipient-field.tsx rename src/app/pages/send/send-crypto-asset-form/components/recipient-fields/{recipient-field-bns-name.tsx => recipient-bns-name-type-field.tsx} (84%) create mode 100644 src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-field.tsx delete mode 100644 src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/components/recipient-dropdown-item.tsx delete mode 100644 src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/components/recipient-dropdown-overlay.tsx delete mode 100644 src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/components/recipient-dropdown.layout.tsx diff --git a/src/app/pages/send/ordinal-inscription/send-inscription-form.tsx b/src/app/pages/send/ordinal-inscription/send-inscription-form.tsx index 56d371dcd01..23d5d05e9f6 100644 --- a/src/app/pages/send/ordinal-inscription/send-inscription-form.tsx +++ b/src/app/pages/send/ordinal-inscription/send-inscription-form.tsx @@ -12,7 +12,7 @@ import { InscriptionPreviewCard } from '@app/components/inscription-preview-card import { OrdinalIcon } from '@app/ui/components/avatar-icon/ordinal-icon'; import { Button } from '@app/ui/components/button/button'; -import { RecipientField } from '../send-crypto-asset-form/components/recipient-field'; +import { RecipientAddressTypeField } from '../send-crypto-asset-form/components/recipient-address-type-field'; import { CollectibleAsset } from './components/collectible-asset'; import { useSendInscriptionState } from './components/send-inscription-container'; import { useSendInscriptionForm } from './hooks/use-send-inscription-form'; @@ -48,7 +48,7 @@ export function SendInscriptionForm() { } name="Ordinal inscription" /> - (); @@ -41,7 +39,6 @@ export function RecipientField({ { field.onBlur(e); diff --git a/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/generic-recipient-field.tsx b/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/generic-recipient-field.tsx deleted file mode 100644 index 72012b49efb..00000000000 --- a/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/generic-recipient-field.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { TextInputFieldError } from '@app/components/field-error'; -import { RecipientFieldType } from '@app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/recipient-type-dropdown'; - -import { SelectAccountButton } from '../recipient-accounts-drawer/select-account-button'; -import { RecipientField } from '../recipient-field'; -import { RecipientDropdownOverlay } from '../recipient-type-dropdown/components/recipient-dropdown-overlay'; -import { useRecipientSelectFields } from './hooks/use-recipient-select-fields'; -import { RecipientFieldBnsName } from './recipient-field-bns-name'; - -interface GenericRecipientFieldProps { - bnsFn(client: any, name: string, isTestnet?: boolean): Promise; -} -export function GenericRecipientField({ bnsFn }: GenericRecipientFieldProps) { - const { - isSelectVisible, - showRecipientAccountsModal, - onSelectRecipientFieldType, - onSetIsSelectVisible, - selectedRecipientField, - } = useRecipientSelectFields(); - - const recipientDropdown = ( - - ); - - const selectAccountButton = ; - - switch (selectedRecipientField) { - case RecipientFieldType.BnsName: - return ( - <> - - - - ); - case RecipientFieldType.Address: - default: - return ( - <> - - - - ); - } -} diff --git a/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/hooks/use-recipient-select-fields.tsx b/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/hooks/use-recipient-select-fields.tsx index 7be5e1fe8a3..0607daecd73 100644 --- a/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/hooks/use-recipient-select-fields.tsx +++ b/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/hooks/use-recipient-select-fields.tsx @@ -3,23 +3,24 @@ import { useNavigate } from 'react-router-dom'; import { useFormikContext } from 'formik'; -import { BitcoinSendFormValues, StacksSendFormValues } from '@shared/models/form.model'; import { RouteUrls } from '@shared/route-urls'; -import { RecipientFieldType } from '@app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/recipient-type-dropdown'; - import { useRecipientBnsName } from './use-recipient-bns-name'; +export const recipientIdentifierTypesMap = { bnsName: 'BNS Name', address: 'Address' } as const; + +export type RecipientIdentifierType = keyof typeof recipientIdentifierTypesMap; + export function useRecipientSelectFields() { - const { setFieldError, setFieldTouched, setFieldValue } = useFormikContext< - BitcoinSendFormValues | StacksSendFormValues - >(); - const [selectedRecipientField, setSelectedRecipientField] = useState(RecipientFieldType.Address); + const { setFieldError, setFieldTouched, setFieldValue } = useFormikContext(); + + const [selectedRecipientField, setSelectedRecipientField] = + useState('address'); + const [isSelectVisible, setIsSelectVisible] = useState(false); const { setBnsAddress } = useRecipientBnsName(); const navigate = useNavigate(); - // Formik does not provide a field reset const resetAllRecipientFields = useCallback(async () => { void setFieldValue('recipient', ''); setFieldError('recipient', undefined); @@ -32,17 +33,20 @@ export function useRecipientSelectFields() { return useMemo( () => ({ selectedRecipientField, + + selectedRecipientFieldName: recipientIdentifierTypesMap[selectedRecipientField], + isSelectVisible, showRecipientAccountsModal() { - setSelectedRecipientField(RecipientFieldType.Address); + setSelectedRecipientField('address'); navigate(RouteUrls.SendCryptoAssetFormRecipientAccounts, { replace: true }); }, - async onSelectRecipientFieldType(index: number) { + async onSelectRecipientFieldType(recipientType: RecipientIdentifierType) { await resetAllRecipientFields(); setBnsAddress(''); - setSelectedRecipientField(index); + setSelectedRecipientField(recipientType); setIsSelectVisible(false); }, diff --git a/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-field-bns-name.tsx b/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-bns-name-type-field.tsx similarity index 84% rename from src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-field-bns-name.tsx rename to src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-bns-name-type-field.tsx index f84f75843d3..80ae47f32c9 100644 --- a/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-field-bns-name.tsx +++ b/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-bns-name-type-field.tsx @@ -4,25 +4,22 @@ import { useFormikContext } from 'formik'; import { BitcoinSendFormValues, StacksSendFormValues } from '@shared/models/form.model'; -import { RecipientField } from '@app/pages/send/send-crypto-asset-form/components/recipient-field'; +import { RecipientAddressTypeField } from '@app/pages/send/send-crypto-asset-form/components/recipient-address-type-field'; import { StacksClient } from '@app/query/stacks/stacks-client'; import { RecipientAddressDisplayer } from './components/recipient-address-displayer'; import { useRecipientBnsName } from './hooks/use-recipient-bns-name'; -interface RecipientFieldBnsNameProps { +interface RecipientBnsNameTypeFieldProps { fetchFn(client: StacksClient, name: string, isTestnet?: boolean): Promise; - isSelectVisible: boolean; - selectedRecipientField: number; topInputOverlay: React.JSX.Element; rightLabel: React.JSX.Element; } -export function RecipientFieldBnsName({ +export function RecipientBnsNameTypeField({ fetchFn, - isSelectVisible, topInputOverlay, rightLabel, -}: RecipientFieldBnsNameProps) { +}: RecipientBnsNameTypeFieldProps) { const [showBnsAddress, setShowBnsAddress] = useState(false); const { errors, setFieldError, values } = useFormikContext< BitcoinSendFormValues | StacksSendFormValues @@ -47,8 +44,7 @@ export function RecipientFieldBnsName({ return ( <> - getBnsAddressAndValidate(fetchFn)} placeholder="Enter recipient BNS name" diff --git a/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-field.tsx b/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-field.tsx new file mode 100644 index 00000000000..0c9dc2a79ce --- /dev/null +++ b/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-field.tsx @@ -0,0 +1,55 @@ +import { TextInputFieldError } from '@app/components/field-error'; + +import { SelectAccountButton } from '../recipient-accounts-drawer/select-account-button'; +import { RecipientAddressTypeField } from '../recipient-address-type-field'; +import { RecipientIdentifierTypeDropdown } from '../recipient-type-dropdown/recipient-type-dropdown'; +import { useRecipientSelectFields } from './hooks/use-recipient-select-fields'; +import { RecipientBnsNameTypeField } from './recipient-bns-name-type-field'; + +interface RecipientFieldProps { + bnsLookupFn(client: any, name: string, isTestnet?: boolean): Promise; +} +export function RecipientField({ bnsLookupFn }: RecipientFieldProps) { + const { + showRecipientAccountsModal, + onSelectRecipientFieldType, + selectedRecipientFieldName, + selectedRecipientField, + } = useRecipientSelectFields(); + + const recipientDropdown = ( + onSelectRecipientFieldType(recipientType)} + /> + ); + + const selectAccountButton = ; + + switch (selectedRecipientField) { + case 'bnsName': + return ( + <> + + + + ); + case 'address': + default: + return ( + <> + + + + ); + } +} diff --git a/src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/components/recipient-dropdown-item.tsx b/src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/components/recipient-dropdown-item.tsx deleted file mode 100644 index ab8d7922e28..00000000000 --- a/src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/components/recipient-dropdown-item.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import type { ComponentProps } from 'react'; - -import { HStack, styled } from 'leather-styles/jsx'; - -import { ChevronDownIcon } from '@app/ui/icons/chevron-down-icon'; - -const labels = ['Address', 'BNS Name']; -const testLabels = ['address', 'bns-name']; - -interface RecipientDropdownItemProps extends ComponentProps { - index: number; - isVisible?: boolean; - onSelectItem(index: number): void; -} -export function RecipientDropdownItem({ - index, - isVisible, - onSelectItem, -}: RecipientDropdownItemProps) { - return ( - onSelectItem(index)} - zIndex={20} - pl={isVisible ? 'space.02' : 'unset'} - > - - {labels[index]} - {isVisible ? <> : } - - - ); -} diff --git a/src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/components/recipient-dropdown-overlay.tsx b/src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/components/recipient-dropdown-overlay.tsx deleted file mode 100644 index 548fa11a293..00000000000 --- a/src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/components/recipient-dropdown-overlay.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { RecipientTypeDropdown } from '@app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/recipient-type-dropdown'; - -interface RecipientDropdownOverlayProps { - isSelectVisible: boolean; - onSelectRecipientFieldType(index: number): void; - onSetIsSelectVisible(value: boolean): void; - selectedRecipientField: number; -} -export function RecipientDropdownOverlay({ - isSelectVisible, - onSelectRecipientFieldType, - onSetIsSelectVisible, - selectedRecipientField, -}: RecipientDropdownOverlayProps) { - return ( - - ); -} diff --git a/src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/components/recipient-dropdown.layout.tsx b/src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/components/recipient-dropdown.layout.tsx deleted file mode 100644 index 9de3e0b5f3c..00000000000 --- a/src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/components/recipient-dropdown.layout.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { ReactNode, useRef } from 'react'; - -import { Box, Stack } from 'leather-styles/jsx'; - -import { useOnClickOutside } from '@app/common/hooks/use-onclickoutside'; - -import { RecipientDropdownItem } from './recipient-dropdown-item'; - -interface RecipientDropdownLayoutProps { - children: ReactNode; - isVisible: boolean; - onSetIsSelectVisible(value: boolean): void; - selectedItem: number; -} -export function RecipientDropdownLayout({ - children, - isVisible, - onSetIsSelectVisible, - selectedItem, -}: RecipientDropdownLayoutProps) { - const ref = useRef(null); - - useOnClickOutside(ref, () => onSetIsSelectVisible(false)); - - return ( - - onSetIsSelectVisible(true)} /> - {isVisible ? ( - - {children} - - ) : null} - - ); -} diff --git a/src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/recipient-type-dropdown.tsx b/src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/recipient-type-dropdown.tsx index 5327cb71462..f95b16f9252 100644 --- a/src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/recipient-type-dropdown.tsx +++ b/src/app/pages/send/send-crypto-asset-form/components/recipient-type-dropdown/recipient-type-dropdown.tsx @@ -1,36 +1,59 @@ -import { RecipientDropdownItem } from './components/recipient-dropdown-item'; -import { RecipientDropdownLayout } from './components/recipient-dropdown.layout'; +import type { Entries } from '@shared/utils/type-utils'; -export enum RecipientFieldType { - Address, - BnsName, -} +import { DropdownMenu } from '@app/ui/components/dowpdown-menu/dropdown-menu'; +import { Flag } from '@app/ui/components/flag/flag'; +import { ChevronDownIcon } from '@app/ui/icons'; + +import { + type RecipientIdentifierType, + recipientIdentifierTypesMap, +} from '../recipient-fields/hooks/use-recipient-select-fields'; -interface RecipientTypeDropdownProps { - isVisible: boolean; - onSelectItem(index: number): void; - onSetIsSelectVisible(value: boolean): void; - selectedItem: number; +function makeIteratbleListOfRecipientIdentifierTypes( + recipientTypeMap: typeof recipientIdentifierTypesMap +) { + return (Object.entries(recipientTypeMap) as Entries).map( + ([key, value]) => ({ key, label: value }) + ); } -export function RecipientTypeDropdown(props: RecipientTypeDropdownProps) { - const { isVisible, onSelectItem, onSetIsSelectVisible, selectedItem } = props; +const recipientIdentifierTypes = makeIteratbleListOfRecipientIdentifierTypes( + recipientIdentifierTypesMap +); +interface RecipientIdentifierTypeDropdownProps { + activeRecipientIdentifierType: string; + onSelectRecipientIdentifierType(recipientType: RecipientIdentifierType): void; +} +export function RecipientIdentifierTypeDropdown(props: RecipientIdentifierTypeDropdownProps) { + const { activeRecipientIdentifierType, onSelectRecipientIdentifierType } = props; return ( - - - - + + + } + spacing="space.01" + color="accent.text-primary" + _hover={{ color: 'accent.action-primary-hover' }} + > + {activeRecipientIdentifierType} + + + + + + {recipientIdentifierTypes.map(type => ( + onSelectRecipientIdentifierType(type.key)} + data-testid={`recipient-select-field-${type.key}`} + > + {type.label} + + ))} + + + + ); } diff --git a/src/app/pages/send/send-crypto-asset-form/family/bitcoin/components/bitcoin-recipient-field.tsx b/src/app/pages/send/send-crypto-asset-form/family/bitcoin/components/bitcoin-recipient-field.tsx index 7131c7f7b90..589ab44ed8f 100644 --- a/src/app/pages/send/send-crypto-asset-form/family/bitcoin/components/bitcoin-recipient-field.tsx +++ b/src/app/pages/send/send-crypto-asset-form/family/bitcoin/components/bitcoin-recipient-field.tsx @@ -1,7 +1,7 @@ import { fetchBtcNameOwner } from '@app/query/stacks/bns/bns.utils'; -import { GenericRecipientField } from '../../../components/recipient-fields/generic-recipient-field'; +import { RecipientField } from '../../../components/recipient-fields/recipient-field'; export function BitcoinRecipientField() { - return ; + return ; } diff --git a/src/app/pages/send/send-crypto-asset-form/family/stacks/components/stacks-recipient-field.tsx b/src/app/pages/send/send-crypto-asset-form/family/stacks/components/stacks-recipient-field.tsx index 22ee903512a..9c93ffe8755 100644 --- a/src/app/pages/send/send-crypto-asset-form/family/stacks/components/stacks-recipient-field.tsx +++ b/src/app/pages/send/send-crypto-asset-form/family/stacks/components/stacks-recipient-field.tsx @@ -1,7 +1,7 @@ import { fetchNameOwner } from '@app/query/stacks/bns/bns.utils'; -import { GenericRecipientField } from '../../../components/recipient-fields/generic-recipient-field'; +import { RecipientField } from '../../../components/recipient-fields/recipient-field'; export function StacksRecipientField() { - return ; + return ; } diff --git a/src/app/ui/components/dowpdown-menu/dropdown-menu.tsx b/src/app/ui/components/dowpdown-menu/dropdown-menu.tsx index 688f31378f7..83343e3e8fb 100644 --- a/src/app/ui/components/dowpdown-menu/dropdown-menu.tsx +++ b/src/app/ui/components/dowpdown-menu/dropdown-menu.tsx @@ -22,7 +22,6 @@ const dropdownTriggerStyles = css({ px: 'space.04', py: 'space.03', textStyle: 'label.02', - '&[data-state=open]': { bg: 'ink.component-background-pressed', }, @@ -90,6 +89,7 @@ const Separator: typeof RadixDropdownMenu.Separator = forwardRef((props, ref) => export const DropdownMenu = { Root, Trigger, + UnstyledTrigger: RadixDropdownMenu.Trigger, Portal, Content, Group, diff --git a/src/app/ui/components/flag/flag.tsx b/src/app/ui/components/flag/flag.tsx index 6b2e471f3c1..7739a07bcec 100644 --- a/src/app/ui/components/flag/flag.tsx +++ b/src/app/ui/components/flag/flag.tsx @@ -21,6 +21,7 @@ export interface FlagProps extends FlexProps { align?: FlagAlignment; children: React.ReactNode; img?: React.ReactNode; + reverse?: boolean; } /** @@ -33,13 +34,23 @@ export interface FlagProps extends FlexProps { export function Flag({ spacing = 'space.03', align = 'middle', + reverse = false, img, children, ...props }: FlagProps) { return ( - - {img} + + + {img} + {children} ); diff --git a/src/shared/utils/type-utils.ts b/src/shared/utils/type-utils.ts index d426dc3a336..a42eba5bcd3 100644 --- a/src/shared/utils/type-utils.ts +++ b/src/shared/utils/type-utils.ts @@ -5,3 +5,5 @@ type Primitive = null | undefined | string | number | boolean | symbol | bigint; export type LiteralUnion = | LiteralType | (BaseType & Record); + +export type Entries = { [K in keyof T]: [K, T[K]] }[keyof T][]; diff --git a/tests/selectors/send.selectors.ts b/tests/selectors/send.selectors.ts index 579c3b64405..6daf0aea9f7 100644 --- a/tests/selectors/send.selectors.ts +++ b/tests/selectors/send.selectors.ts @@ -14,7 +14,7 @@ export enum SendCryptoAssetSelectors { PreviewSendTxBtn = 'preview-send-tx-btn', RecipientChooseAccountButton = 'recipient-choose-account-button', RecipientSelectFieldAddress = 'recipient-select-field-address', - RecipientSelectFieldBnsName = 'recipient-select-field-bns-name', + RecipientSelectFieldBnsName = 'recipient-select-field-bnsName', RecipientFieldInput = 'recipient-field-input', RecipientBnsAddressLabel = 'recipient-bns-address-label', RecipientBnsAddressCopyToClipboard = 'recipient-bns-address-copy-to-clipboard',