From 5baadb15f279f707ba8697a3e089f28fb5a4c160 Mon Sep 17 00:00:00 2001 From: herkoss Date: Fri, 2 Jun 2023 18:08:14 +0300 Subject: [PATCH 01/21] Added new select to settings --- package.json | 4 + src/app/templates/AssetSelect/AssetSelect.tsx | 6 +- src/app/templates/IconifiedSelect/Field2.tsx | 148 ++++++++++ src/app/templates/IconifiedSelect/Menu.tsx | 53 +++- src/app/templates/IconifiedSelect/types.ts | 13 +- .../templates/InputGeneral/InputGeneral.tsx | 20 ++ .../templates/InputGeneral/SelectGeneral.tsx | 187 ++++++++++++ .../Components/LocaleSelect.tsx | 51 ++-- .../SwapForm/SwapFormInput/SwapFormInput.tsx | 277 ++++++++++++------ src/lib/ui/Popper.tsx | 4 +- yarn.lock | 10 + 11 files changed, 655 insertions(+), 118 deletions(-) create mode 100644 src/app/templates/IconifiedSelect/Field2.tsx create mode 100644 src/app/templates/InputGeneral/InputGeneral.tsx create mode 100644 src/app/templates/InputGeneral/SelectGeneral.tsx diff --git a/package.json b/package.json index 7434eacfe..01a8c6842 100644 --- a/package.json +++ b/package.json @@ -182,6 +182,7 @@ "use-force-update": "1.0.7", "use-onclickoutside": "0.4.1", "util": "0.11.1", + "uuid": "^9.0.0", "wasm-themis": "0.14.6", "webextension-polyfill": "^0.10.0", "webpack": "^5.74.0", @@ -216,5 +217,8 @@ "nanoid": "3.1.31", "tslib": "2.4.0", "@types/react": "18.0.15" + }, + "devDependencies": { + "@types/uuid": "^9.0.1" } } diff --git a/src/app/templates/AssetSelect/AssetSelect.tsx b/src/app/templates/AssetSelect/AssetSelect.tsx index f07c7e37c..c7176bfc9 100644 --- a/src/app/templates/AssetSelect/AssetSelect.tsx +++ b/src/app/templates/AssetSelect/AssetSelect.tsx @@ -66,7 +66,7 @@ const AssetSelect: FC = ({ value, assets, onChange, className, export default AssetSelect; -const AssetSelectTitle: FC = () => ( +export const AssetSelectTitle: FC = () => (

@@ -80,7 +80,7 @@ const AssetSelectTitle: FC = () => ( type AssetSelectOptionRenderProps = IconifiedSelectOptionRenderProps; -const AssetFieldContent: FC = ({ option }) => { +export const AssetFieldContent: FC = ({ option }) => { const account = useAccount(); const assetSlug = getSlug(option); const metadata = useAssetMetadata(assetSlug); @@ -115,7 +115,7 @@ const AssetFieldContent: FC = ({ option }) => { ); }; -const AssetOptionContent: FC = ({ option }) => { +export const AssetOptionContent: FC = ({ option }) => { const slug = getSlug(option); return ( diff --git a/src/app/templates/IconifiedSelect/Field2.tsx b/src/app/templates/IconifiedSelect/Field2.tsx new file mode 100644 index 000000000..d594cdba6 --- /dev/null +++ b/src/app/templates/IconifiedSelect/Field2.tsx @@ -0,0 +1,148 @@ +import React, { forwardRef, HTMLAttributes, PropsWithChildren, ReactNode } from 'react'; + +import classNames from 'clsx'; + +import { ReactComponent as ChevronDownIcon } from 'app/icons/chevron-down.svg'; +import { ReactComponent as SearchIcon } from 'app/icons/search.svg'; +import { setTestID, TestIDProps } from 'lib/analytics'; +import { useFocusOnElement } from 'lib/ui/hooks'; + +import { IconifiedSelectProps2, IconifiedSelectPropsBase2 } from './types'; + +type FieldBaseProps2 = HTMLAttributes & + IconifiedSelectPropsBase2 & { + Content: ReactNode; + dropdown?: boolean; + }; + +interface SearchProps { + value?: string; + placeholder?: string; + onChange: (value: string) => void; + inputTestID?: string; +} + +interface IconifiedSelectFieldProps2 extends FieldBaseProps2, TestIDProps { + opened: boolean; + search?: SearchProps; + BeforeContent: ReactNode; +} + +export const IconifiedSelectField2 = forwardRef((props, ref) => { + const { search, ...rest } = props; + + if (search) return ; + + return ; +}); + +interface FieldWithNoSearchProps extends FieldBaseProps2, TestIDProps { + opened: boolean; + BeforeContent: IconifiedSelectProps2['BeforeContent']; +} +interface FieldWithNoSearchProps extends FieldBaseProps2, TestIDProps { + opened: boolean; + BeforeContent: ReactNode; +} +interface FieldWithNoSearchProps2 extends FieldBaseProps2, TestIDProps { + opened: boolean; + BeforeContent: ReactNode; +} + +const FieldWithNoSearch2 = forwardRef((props, ref) => { + const { opened, className, style, testID, BeforeContent, ...rest } = props; + + return ( + + + + ); +}); + +interface FieldWithSearchProps extends FieldWithNoSearchProps { + search: SearchProps; +} + +const FieldWithSearch2 = forwardRef((props, ref) => { + const { opened, search, className, style, testID, BeforeContent, ...rest } = props; + + const searchInputRef = useFocusOnElement(opened); + + return ( + + + ); +}); + +interface FieldContainerProps { + active?: boolean; + opened?: boolean; + className?: string; + style?: React.CSSProperties; + BeforeContent: ReactNode; +} + +export const FieldContainer2 = forwardRef>( + ({ opened = false, className, style, BeforeContent, children }, ref) => ( + <> + {BeforeContent} +
} + className={classNames( + 'w-full flex items-stretch transition ease-in-out duration-200 w-full border rounded-md', + opened ? 'border-orange-500 bg-gray-100' : 'border-gray-300', + className + )} + style={style} + > + {children} +
+ + ) +); + +export const FieldInnerComponent2 = forwardRef( + ({ Content, hidden, dropdown, className, ...rest }, ref) => ( + + ) +); diff --git a/src/app/templates/IconifiedSelect/Menu.tsx b/src/app/templates/IconifiedSelect/Menu.tsx index ba2ca2ea2..d7203f9b9 100644 --- a/src/app/templates/IconifiedSelect/Menu.tsx +++ b/src/app/templates/IconifiedSelect/Menu.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect } from 'react'; +import React, { FC, useCallback, useEffect } from 'react'; import classNames from 'clsx'; @@ -7,7 +7,7 @@ import { ReactComponent as SearchIcon } from 'app/icons/search.svg'; import { AnalyticsEventCategory, TestIDProperty, useAnalytics } from 'lib/analytics'; import { PopperRenderProps } from 'lib/ui/Popper'; -import { IconifiedSelectProps, IconifiedSelectPropsBase } from './types'; +import { IconifiedSelectProps, IconifiedSelectPropsBase, IconifiedSelectPropsBase2 } from './types'; interface Props extends PopperRenderProps, TestIDProperty, IconifiedSelectPropsBase { search?: { @@ -15,6 +15,55 @@ interface Props extends PopperRenderProps, TestIDProperty, IconifiedSelectPro }; } +interface Props2 extends PopperRenderProps, TestIDProperty, IconifiedSelectPropsBase2 { + search?: { + value?: string; + }; +} + +export const IconifiedSelectMenu2: FC = ({ + opened, + search, + testID, + padded, + noItemsText, + OptionsContent, + toggleOpened +}) => { + const { trackEvent } = useAnalytics(); + + useEffect(() => { + if (testID && opened) trackEvent(testID, AnalyticsEventCategory.DropdownOpened); + }, [opened, trackEvent]); + + return ( + + {OptionsContent.length ? ( + OptionsContent.map(OptionContent => ) + ) : ( +

+ {search?.value ? : null} + + {noItemsText} +

+ )} +
+ ); +}; + export const IconifiedSelectMenu = (props: Props) => { const { opened, diff --git a/src/app/templates/IconifiedSelect/types.ts b/src/app/templates/IconifiedSelect/types.ts index 2cc577c68..e68d94982 100644 --- a/src/app/templates/IconifiedSelect/types.ts +++ b/src/app/templates/IconifiedSelect/types.ts @@ -1,4 +1,4 @@ -import { ComponentType } from 'react'; +import { ComponentType, ReactNode } from 'react'; import { TestIDProperty } from 'lib/analytics'; @@ -19,6 +19,11 @@ export interface IconifiedSelectPropsBase { noItemsText: string; padded?: boolean; } +export interface IconifiedSelectPropsBase2 { + OptionsContent: Array; + noItemsText: string; + padded?: boolean; +} export interface IconifiedSelectProps extends IconifiedSelectPropsBase, TestIDProperty { FieldContent: OptionContentComponent; @@ -31,3 +36,9 @@ export interface IconifiedSelectProps extends IconifiedSelectPropsBase, Te inputTestID?: string; }; } +export interface IconifiedSelectProps2 extends IconifiedSelectPropsBase2, TestIDProperty { + FieldContent: ReactNode; + BeforeContent?: ReactNode; + className?: string; + fieldStyle?: React.CSSProperties; +} diff --git a/src/app/templates/InputGeneral/InputGeneral.tsx b/src/app/templates/InputGeneral/InputGeneral.tsx new file mode 100644 index 000000000..5952017ac --- /dev/null +++ b/src/app/templates/InputGeneral/InputGeneral.tsx @@ -0,0 +1,20 @@ +import React, { ReactNode, type FC } from 'react'; + +import classNames from 'clsx'; + +interface Props { + className?: string; + header?: ReactNode; + mainContent: ReactNode; + footer?: ReactNode; +} + +export const InputGeneral: FC = ({ className, header, mainContent, footer }) => { + return ( +
+ {header} + {mainContent} + {footer} +
+ ); +}; diff --git a/src/app/templates/InputGeneral/SelectGeneral.tsx b/src/app/templates/InputGeneral/SelectGeneral.tsx new file mode 100644 index 000000000..b68eec7d4 --- /dev/null +++ b/src/app/templates/InputGeneral/SelectGeneral.tsx @@ -0,0 +1,187 @@ +import React, { ChangeEventHandler, ReactNode, FC, Dispatch, SetStateAction } from 'react'; + +import classNames from 'clsx'; +import { v4 } from 'uuid'; + +import AssetField from 'app/atoms/AssetField'; +import DropdownWrapper from 'app/atoms/DropdownWrapper'; +import { ReactComponent as ChevronDownIcon } from 'app/icons/chevron-down.svg'; +import { ReactComponent as SearchIcon } from 'app/icons/search.svg'; +import { t } from 'lib/i18n'; +import Popper from 'lib/ui/Popper'; +import { sameWidthModifiers } from 'lib/ui/same-width-modifiers'; +import { isDefined } from 'lib/utils/is-defined'; + +interface Props { + DropdownFaceContent: ReactNode; + Input?: ReactNode; + className?: string; + searchProps: SelectSearchProps; + optionsProps: SelectOptionsPropsBase; +} + +export const SelectGeneral = ({ + DropdownFaceContent, + Input, + searchProps, + optionsProps +}: Props) => { + const isInputDefined = isDefined(Input); + return ( + } + > + {({ opened, toggleOpened }) => + opened ? ( + + ) : ( +
+ + {Input} +
+ ) + } +
+ ); +}; +interface SelectOptionsPropsBase { + options: Array; + noItemsText: ReactNode; + onOptionChange: (newValue: T) => void; + renderOptionContent: (option: T) => ReactNode; +} +interface SelectOptionsProps extends SelectOptionsPropsBase { + opened: boolean; + setOpened: Dispatch>; +} + +const SelectOptions = ({ + opened, + options, + + noItemsText, + onOptionChange, + setOpened, + renderOptionContent +}: SelectOptionsProps) => { + return ( + + {options.length ? ( + ( + + )} + /> + ) : ( +

+ + {noItemsText} +

+ )} +
+ ); +}; + +interface SelectSearchProps { + searchValue: string; + tokenIdValue?: string; + showTokenIdInput?: boolean; + onSearchChange: ChangeEventHandler; + onTokenIdChange?: (newValue: number | string | undefined) => void; +} +const SelectSearch: FC = ({ + searchValue, + tokenIdValue, + showTokenIdInput = false, + onSearchChange, + onTokenIdChange +}) => { + return ( +
+
+ +
+ +
+
+ +
+ + {showTokenIdInput && ( +
+ +
+ )} +
+
+ ); +}; + +interface FlatListProps { + data: Array; + renderItem: (props: T) => ReactNode; +} + +const FlatList = ({ data, renderItem }: FlatListProps) => { + return ( +
    + {data.map(prop => ( +
  • {renderItem(prop)}
  • + ))} +
+ ); +}; diff --git a/src/app/templates/SettingsGeneral/Components/LocaleSelect.tsx b/src/app/templates/SettingsGeneral/Components/LocaleSelect.tsx index 3c0a5941c..3e2f65ff7 100644 --- a/src/app/templates/SettingsGeneral/Components/LocaleSelect.tsx +++ b/src/app/templates/SettingsGeneral/Components/LocaleSelect.tsx @@ -3,6 +3,8 @@ import React, { useMemo, useCallback, FC } from 'react'; import browser from 'webextension-polyfill'; import Flag from 'app/atoms/Flag'; +import { InputGeneral } from 'app/templates/InputGeneral/InputGeneral'; +import { SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; import { AnalyticsEventCategory, AnalyticsEventEnum, useAnalytics, setTestID } from 'lib/analytics'; import { getCurrentLocale, updateLocale, T, t } from 'lib/i18n'; import { searchAndFilterItems } from 'lib/utils/search-items'; @@ -119,21 +121,26 @@ const LocaleSelect: FC = ({ className }) => { ); return ( - } + mainContent={ + <> + } + optionsProps={{ + options: LOCALE_OPTIONS, + noItemsText: 'No items', + renderOptionContent, + onOptionChange: handleLocaleChange + }} + searchProps={{ + searchValue: 'qwe', + onSearchChange: () => console.log('qweqwe') + }} + /> + + } + footer={undefined} /> ); }; @@ -141,7 +148,7 @@ const LocaleSelect: FC = ({ className }) => { export default LocaleSelect; const LocaleTitle: FC = () => ( -

+

@@ -151,16 +158,16 @@ const LocaleTitle: FC = () => ( type SelectItemProps = IconifiedSelectOptionRenderProps; const LocaleIcon: FC = ({ option: { flagName, code } }) => ( - + ); -const LocaleFieldContent: FC = ({ option }) => { +const LocaleFieldContent = (option: LocaleOption) => { return ( - <> +
{option.label} - +
); }; @@ -169,7 +176,7 @@ const LocaleOptionContent: FC = ({ option }) => { <> -
+
{option.label} {option.disabled && ( @@ -184,6 +191,8 @@ const LocaleOptionContent: FC = ({ option }) => { ); }; +const renderOptionContent = (option: LocaleOption) => ; + const searchLocale = (searchString: string) => searchAndFilterItems(LOCALE_OPTIONS, searchString, [ { name: 'code', weight: 0.75 }, diff --git a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx index 07b4f9942..47af3cecb 100644 --- a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx +++ b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx @@ -3,35 +3,32 @@ import React, { ChangeEvent, FC, useMemo } from 'react'; import BigNumber from 'bignumber.js'; import classNames from 'clsx'; +import AssetField from 'app/atoms/AssetField'; +import Money from 'app/atoms/Money'; +import { AssetIcon } from 'app/templates/AssetIcon'; +import InFiat from 'app/templates/InFiat'; +import { InputGeneral } from 'app/templates/InputGeneral/InputGeneral'; +import { SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; import { useFormAnalytics } from 'lib/analytics'; -import { t } from 'lib/i18n'; -import { toTokenSlug } from 'lib/temple/assets'; +import { T, t, toLocalFormat } from 'lib/i18n'; import { useAccount, useBalance, useAssetMetadata, useGetTokenMetadata, useOnBlock } from 'lib/temple/front'; -import { useAvailableRoute3Tokens, useFilteredSwapAssets } from 'lib/temple/front/assets'; -import { EMPTY_ASSET_METADATA } from 'lib/temple/metadata'; -import Popper from 'lib/ui/Popper'; -import { sameWidthModifiers } from 'lib/ui/same-width-modifiers'; +import { useFilteredSwapAssets } from 'lib/temple/front/assets'; +import { AssetMetadata, EMPTY_ASSET_METADATA } from 'lib/temple/metadata'; +import { isDefined } from 'lib/utils/is-defined'; -import { AssetsMenu } from './AssetsMenu/AssetsMenu'; import { PercentageButton } from './PercentageButton/PercentageButton'; import { SwapFormInputProps } from './SwapFormInput.props'; -import { SwapFormInputHeader } from './SwapFormInputHeader/SwapFormInputHeader'; -import { useSwapFormTokenIdInput } from './SwapFormTokenIdInput.hook'; const EXCHANGE_XTZ_RESERVE = new BigNumber('0.3'); const PERCENTAGE_BUTTONS = [25, 50, 75, 100]; -export const SwapFormInput: FC = ({ - value, - className, - error, - label, - name, - amountInputDisabled, - testIDs, - onChange -}) => { +const renderOptionContent = (option: string) => { + return
{option}
; +}; + +export const SwapFormInput: FC = ({ value, error, name, onChange, amountInputDisabled }) => { const { trackChange } = useFormAnalytics('SwapForm'); + const { assetSlug, amount } = value; const isTezosSlug = assetSlug === 'tez'; const assetSlugWithFallback = assetSlug ?? 'tez'; @@ -47,11 +44,7 @@ export const SwapFormInput: FC = ({ const balance = useBalance(assetSlugWithFallback, account.publicKeyHash, { suspense: false }); useOnBlock(_ => balance.mutate()); - const { isLoading } = useAvailableRoute3Tokens(); - const { filteredAssets, searchValue, setSearchValue, tokenId, setTokenId } = useFilteredSwapAssets(name); - - const showTokenIdInput = useSwapFormTokenIdInput(searchValue); - const searchAssetSlug = toTokenSlug(searchValue, tokenId); + const { filteredAssets, searchValue, setSearchValue, setTokenId } = useFilteredSwapAssets(name); const maxAmount = useMemo(() => { if (!assetSlug) { @@ -63,11 +56,6 @@ export const SwapFormInput: FC = ({ return maxSendAmount ?? new BigNumber(0); }, [assetSlug, isTezosSlug, balance.data]); - const handleSearchChange = (e: ChangeEvent) => { - setTokenId(undefined); - setSearchValue(e.target.value); - }; - const handleAmountChange = (newAmount?: BigNumber) => onChange({ assetSlug, @@ -100,6 +88,11 @@ export const SwapFormInput: FC = ({ trackChange({ [name]: assetMetadata.symbol }, { [name]: newAssetMetadata.symbol }); }; + const handleSearchChange = (e: ChangeEvent) => { + setTokenId(undefined); + setSearchValue(e.target.value); + }; + const prettyError = useMemo(() => { if (!error) { return error; @@ -113,74 +106,180 @@ export const SwapFormInput: FC = ({ }, [error]); return ( -
- - - ( - + } + mainContent={ + <> + + } + searchProps={{ + searchValue, + onSearchChange: handleSearchChange + }} + Input={ + + } + optionsProps={{ + options: filteredAssets, + noItemsText: 'No items', + renderOptionContent, + onOptionChange: handleSelectedAssetChange + }} /> - )} - > - {({ ref, opened, toggleOpened, setOpened }) => ( - } - selectedAssetSlug={assetSlug} - selectedAssetMetadata={assetMetadata} - amount={amount} - balance={assetSlug ? balance.data : undefined} - label={label} - opened={opened} - searchString={searchValue} - setOpened={setOpened} - showTokenIdInput={showTokenIdInput} - tokenId={tokenId} - toggleOpened={toggleOpened} - onTokenIdChange={setTokenId} - amountInputDisabled={amountInputDisabled} - onAmountChange={handleAmountChange} - onSearchChange={handleSearchChange} - testIDs={testIDs} + + } + footer={ +
+ {prettyError &&
{prettyError}
} + + - )} - +
+ } + /> + ); +}; + +interface SwapFieldProps { + selectedAssetSlug?: string; + selectedAssetMetadata: AssetMetadata; +} + +const SwapDropdownFace: FC = ({ selectedAssetSlug, selectedAssetMetadata }) => + selectedAssetSlug ? ( +
+ + + {selectedAssetMetadata.symbol} + +
+ ) : ( +
+
+ +
+
+ ); +interface SwapInputProps extends SwapFieldProps { + amount: BigNumber | undefined; + amountInputDisabled: boolean; + onChange: (value?: BigNumber) => void; +} +const SwapInput: FC = ({ + amount, + amountInputDisabled, + selectedAssetSlug, + selectedAssetMetadata, + onChange +}) => { + const handleAmountChange = (newAmount?: string) => + onChange(Boolean(newAmount) && isDefined(newAmount) ? new BigNumber(newAmount) : undefined); + + return ( +
- {prettyError &&
{prettyError}
} - - {!amountInputDisabled && ( -
- {PERCENTAGE_BUTTONS.map(percentage => ( - - ))} -
- )} +
+ + + + {({ balance, symbol }) => ( +
+ + {balance} + {symbol} +
+ )} +
+
); }; + +const SwapInputHeader: FC<{ label: string; selectedAssetSlug: string; selectedAssetSymbol: string }> = ({ + selectedAssetSlug, + selectedAssetSymbol, + label +}) => { + const account = useAccount(); + const balance = useBalance(selectedAssetSlug, account.publicKeyHash, { suspense: false }); + useOnBlock(_ => balance.mutate()); + + return ( +
+ {label} + + {selectedAssetSlug && ( + + + : + + {balance.data && ( + + + {balance.data} + + + )} + {selectedAssetSymbol} + + )} +
+ ); +}; + +const SwapFooter: FC<{ + amountInputDisabled: boolean; + selectedAssetSlug: string; + handlePercentageClick: (percentage: number) => void; +}> = ({ amountInputDisabled, selectedAssetSlug, handlePercentageClick }) => { + const account = useAccount(); + const balance = useBalance(selectedAssetSlug, account.publicKeyHash, { suspense: false }); + useOnBlock(_ => balance.mutate()); + + return amountInputDisabled ? null : ( +
+ {PERCENTAGE_BUTTONS.map(percentage => ( + + ))} +
+ ); +}; diff --git a/src/lib/ui/Popper.tsx b/src/lib/ui/Popper.tsx index 4d32c9c5d..1bef463b1 100644 --- a/src/lib/ui/Popper.tsx +++ b/src/lib/ui/Popper.tsx @@ -23,9 +23,9 @@ export interface PopperRenderProps { setOpened: Dispatch>; toggleOpened: () => void; } -type RenderProp

= (props: P) => ReactElement; +export type RenderProp

= (props: P) => ReactElement; -type PopperProps = Partial & { +export type PopperProps = Partial & { popup: RenderProp; children: RenderProp< PopperRenderProps & { diff --git a/yarn.lock b/yarn.lock index 895190ef0..c8cb39ea5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2641,6 +2641,11 @@ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== +"@types/uuid@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.1.tgz#98586dc36aee8dacc98cc396dbca8d0429647aa6" + integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== + "@types/w3c-web-hid@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@types/w3c-web-hid/-/w3c-web-hid-1.0.3.tgz#e08587a7d737f8654ea6bc0a88689ce5d3ce2d19" @@ -10412,6 +10417,11 @@ utila@~0.4: resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" From 68def48e63fc492edad116b43257821e5368f3b8 Mon Sep 17 00:00:00 2001 From: herkoss Date: Tue, 6 Jun 2023 19:33:40 +0300 Subject: [PATCH 02/21] TW-565 WIP Added padding on component --- .../templates/InputGeneral/InputGeneral.tsx | 6 +- .../templates/InputGeneral/SelectGeneral.tsx | 37 +++- .../Components/BlockExplorerSelect.tsx | 60 +++-- .../Components/FiatCurrencySelect.tsx | 60 +++-- .../Components/LocaleSelect.tsx | 18 +- .../SwapForm/SwapFormInput/SwapFormInput.tsx | 66 +++--- src/app/templates/TopUpInput/index.tsx | 205 ++++++++++++++---- 7 files changed, 316 insertions(+), 136 deletions(-) diff --git a/src/app/templates/InputGeneral/InputGeneral.tsx b/src/app/templates/InputGeneral/InputGeneral.tsx index 5952017ac..e70f0363e 100644 --- a/src/app/templates/InputGeneral/InputGeneral.tsx +++ b/src/app/templates/InputGeneral/InputGeneral.tsx @@ -12,9 +12,9 @@ interface Props { export const InputGeneral: FC = ({ className, header, mainContent, footer }) => { return (

- {header} - {mainContent} - {footer} +
{header}
+
{mainContent}
+
{footer}
); }; diff --git a/src/app/templates/InputGeneral/SelectGeneral.tsx b/src/app/templates/InputGeneral/SelectGeneral.tsx index b68eec7d4..7d83de76d 100644 --- a/src/app/templates/InputGeneral/SelectGeneral.tsx +++ b/src/app/templates/InputGeneral/SelectGeneral.tsx @@ -12,21 +12,28 @@ import Popper from 'lib/ui/Popper'; import { sameWidthModifiers } from 'lib/ui/same-width-modifiers'; import { isDefined } from 'lib/utils/is-defined'; +export enum DropdownSize { + Large, + Small +} interface Props { DropdownFaceContent: ReactNode; Input?: ReactNode; className?: string; searchProps: SelectSearchProps; optionsProps: SelectOptionsPropsBase; + dropdownSize?: DropdownSize; } export const SelectGeneral = ({ DropdownFaceContent, Input, searchProps, - optionsProps + optionsProps, + dropdownSize = DropdownSize.Small }: Props) => { const isInputDefined = isDefined(Input); + return ( ({ > {({ opened, toggleOpened }) => opened ? ( - + ) : (
- {Input} -
- ) - } + + {Input} +
+ )} +
+ )} ); }; @@ -77,18 +82,28 @@ interface SelectOptionsPropsBase { } interface SelectOptionsProps extends SelectOptionsPropsBase { opened: boolean; + isLoading?: boolean; setOpened: Dispatch>; } +const ROW_HEIGHT = 64; + const SelectOptions = ({ opened, options, - noItemsText, + isLoading, onOptionChange, setOpened, renderOptionContent }: SelectOptionsProps) => { + const { dropdownWidth } = useAppEnvStyle(); + + const handleOptionClick = (newValue: T) => { + onOptionChange(newValue); + setOpened(false); + }; + return ( ({ borderColor: '#e2e8f0' }} > - {options.length ? ( - ( - + {(options.length === 0 || isLoading) && ( +
+ {isLoading ? ( + + ) : ( +

+ Search +

)} - /> - ) : ( -

- - {noItemsText} -

+
)} + + 2 ? 240 : options.length * ROW_HEIGHT} + rowCount={options.length} + rowHeight={ROW_HEIGHT} + rowRenderer={({ index }) => { + const option = options[index]; + + return ( + + ); + }} + />
); }; @@ -183,18 +204,3 @@ const SelectSearch: FC = ({
); }; - -interface FlatListProps { - data: Array; - renderItem: (props: T) => ReactNode; -} - -const FlatList = ({ data, renderItem }: FlatListProps) => { - return ( -
    - {data.map(prop => ( -
  • {renderItem(prop)}
  • - ))} -
- ); -}; diff --git a/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx b/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx index fa227c307..a3762b758 100644 --- a/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx +++ b/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx @@ -110,13 +110,13 @@ const BlockExplorerFieldContent: FC> = ({ option }) => { return ( - <> +
{option.name}
- +
); }; diff --git a/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx b/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx index 0c5df00c8..95d95d7e4 100644 --- a/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx +++ b/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx @@ -46,7 +46,7 @@ const FiatCurrencySelect: FC = ({ className }) => { }} searchProps={{ searchValue: 'qwe', - onSearchChange: () => console.log('qweqwe') + onSearchChange: event => searchFiatCurrencyOptions(event.target.value) }} /> @@ -104,13 +104,13 @@ const FiatCurrencyFieldContent: FC = ({ option }) => { const FiatCurrencyOptionContent: FC = ({ option }) => { return ( - <> +
{option.name} ({option.fullname})
- +
); }; diff --git a/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetOption.tsx b/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetOption.tsx index d7baae35a..1b61d7f5a 100644 --- a/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetOption.tsx +++ b/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetOption.tsx @@ -15,30 +15,25 @@ import { AssetsMenuSelectors } from './selectors'; interface Props extends Partial> { assetSlug: string; selected?: boolean; - onClick: (newValue: string) => void; } -export const AssetOption: FC = ({ assetSlug, selected, style, onClick }) => { +export const AssetOption: FC = ({ assetSlug, selected }) => { const assetMetadata: AssetMetadata | null = useAssetMetadata(assetSlug); - const handleClick = () => onClick(assetSlug); - if (!isTruthy(assetMetadata)) return null; return ( - + ); }; diff --git a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx index 11c36bdf8..b00107520 100644 --- a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx +++ b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx @@ -23,9 +23,7 @@ import { SwapFormInputProps } from './SwapFormInput.props'; const EXCHANGE_XTZ_RESERVE = new BigNumber('0.3'); const PERCENTAGE_BUTTONS = [25, 50, 75, 100]; -const renderOptionContent = (option: string, onClick: (value: string) => void) => ( - -); +const renderOptionContent = (option: string) => ; export const SwapFormInput: FC = ({ value, label, error, name, onChange, amountInputDisabled }) => { const { trackChange } = useFormAnalytics('SwapForm'); @@ -134,7 +132,7 @@ export const SwapFormInput: FC = ({ value, label, error, nam optionsProps={{ options: filteredAssets, noItemsText: 'No items', - renderOptionContent: option => renderOptionContent(option, handleSelectedAssetChange), + renderOptionContent, onOptionChange: handleSelectedAssetChange }} /> diff --git a/src/app/templates/TopUpInput/CurrenciesMenu/CurrencyOption.tsx b/src/app/templates/TopUpInput/CurrenciesMenu/CurrencyOption.tsx index 80a9355e9..edafed16a 100644 --- a/src/app/templates/TopUpInput/CurrenciesMenu/CurrencyOption.tsx +++ b/src/app/templates/TopUpInput/CurrenciesMenu/CurrencyOption.tsx @@ -13,15 +13,12 @@ interface Props extends Partial> { currency: CurrencyBase; isSelected: boolean; fitIcons?: boolean | ((currency: CurrencyBase) => boolean); - onClick?: (newValue: CurrencyBase) => void; } -export const CurrencyOption: FC = ({ currency, isSelected, fitIcons, style, onClick }) => ( - + ); diff --git a/yarn.lock b/yarn.lock index c8cb39ea5..895190ef0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2641,11 +2641,6 @@ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== -"@types/uuid@^9.0.1": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.1.tgz#98586dc36aee8dacc98cc396dbca8d0429647aa6" - integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== - "@types/w3c-web-hid@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@types/w3c-web-hid/-/w3c-web-hid-1.0.3.tgz#e08587a7d737f8654ea6bc0a88689ce5d3ce2d19" @@ -10417,11 +10412,6 @@ utila@~0.4: resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= -uuid@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" - integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== - v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" From ca97d3832aa4494e7b3b4157c519156cdd97c722 Mon Sep 17 00:00:00 2001 From: herkoss Date: Wed, 7 Jun 2023 18:20:22 +0300 Subject: [PATCH 04/21] TW-565 WIP --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index debf17e8d..2766c1cbc 100644 --- a/package.json +++ b/package.json @@ -215,6 +215,5 @@ "nanoid": "3.1.31", "tslib": "2.4.0", "@types/react": "18.0.15" - }, - "devDependencies": {} + } } From 3ee6add669e06b99ec9e1c70445c1b126786001f Mon Sep 17 00:00:00 2001 From: herkoss Date: Wed, 7 Jun 2023 18:24:33 +0300 Subject: [PATCH 05/21] TW-565 WIP --- src/app/templates/IconifiedSelect/Field2.tsx | 148 ------------------ src/app/templates/IconifiedSelect/Menu.tsx | 53 +------ src/app/templates/IconifiedSelect/types.ts | 13 +- .../templates/InputGeneral/SelectGeneral.tsx | 4 +- 4 files changed, 5 insertions(+), 213 deletions(-) delete mode 100644 src/app/templates/IconifiedSelect/Field2.tsx diff --git a/src/app/templates/IconifiedSelect/Field2.tsx b/src/app/templates/IconifiedSelect/Field2.tsx deleted file mode 100644 index d594cdba6..000000000 --- a/src/app/templates/IconifiedSelect/Field2.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import React, { forwardRef, HTMLAttributes, PropsWithChildren, ReactNode } from 'react'; - -import classNames from 'clsx'; - -import { ReactComponent as ChevronDownIcon } from 'app/icons/chevron-down.svg'; -import { ReactComponent as SearchIcon } from 'app/icons/search.svg'; -import { setTestID, TestIDProps } from 'lib/analytics'; -import { useFocusOnElement } from 'lib/ui/hooks'; - -import { IconifiedSelectProps2, IconifiedSelectPropsBase2 } from './types'; - -type FieldBaseProps2 = HTMLAttributes & - IconifiedSelectPropsBase2 & { - Content: ReactNode; - dropdown?: boolean; - }; - -interface SearchProps { - value?: string; - placeholder?: string; - onChange: (value: string) => void; - inputTestID?: string; -} - -interface IconifiedSelectFieldProps2 extends FieldBaseProps2, TestIDProps { - opened: boolean; - search?: SearchProps; - BeforeContent: ReactNode; -} - -export const IconifiedSelectField2 = forwardRef((props, ref) => { - const { search, ...rest } = props; - - if (search) return ; - - return ; -}); - -interface FieldWithNoSearchProps extends FieldBaseProps2, TestIDProps { - opened: boolean; - BeforeContent: IconifiedSelectProps2['BeforeContent']; -} -interface FieldWithNoSearchProps extends FieldBaseProps2, TestIDProps { - opened: boolean; - BeforeContent: ReactNode; -} -interface FieldWithNoSearchProps2 extends FieldBaseProps2, TestIDProps { - opened: boolean; - BeforeContent: ReactNode; -} - -const FieldWithNoSearch2 = forwardRef((props, ref) => { - const { opened, className, style, testID, BeforeContent, ...rest } = props; - - return ( - - - - ); -}); - -interface FieldWithSearchProps extends FieldWithNoSearchProps { - search: SearchProps; -} - -const FieldWithSearch2 = forwardRef((props, ref) => { - const { opened, search, className, style, testID, BeforeContent, ...rest } = props; - - const searchInputRef = useFocusOnElement(opened); - - return ( - - - ); -}); - -interface FieldContainerProps { - active?: boolean; - opened?: boolean; - className?: string; - style?: React.CSSProperties; - BeforeContent: ReactNode; -} - -export const FieldContainer2 = forwardRef>( - ({ opened = false, className, style, BeforeContent, children }, ref) => ( - <> - {BeforeContent} -
} - className={classNames( - 'w-full flex items-stretch transition ease-in-out duration-200 w-full border rounded-md', - opened ? 'border-orange-500 bg-gray-100' : 'border-gray-300', - className - )} - style={style} - > - {children} -
- - ) -); - -export const FieldInnerComponent2 = forwardRef( - ({ Content, hidden, dropdown, className, ...rest }, ref) => ( - - ) -); diff --git a/src/app/templates/IconifiedSelect/Menu.tsx b/src/app/templates/IconifiedSelect/Menu.tsx index d7203f9b9..ba2ca2ea2 100644 --- a/src/app/templates/IconifiedSelect/Menu.tsx +++ b/src/app/templates/IconifiedSelect/Menu.tsx @@ -1,4 +1,4 @@ -import React, { FC, useCallback, useEffect } from 'react'; +import React, { useCallback, useEffect } from 'react'; import classNames from 'clsx'; @@ -7,7 +7,7 @@ import { ReactComponent as SearchIcon } from 'app/icons/search.svg'; import { AnalyticsEventCategory, TestIDProperty, useAnalytics } from 'lib/analytics'; import { PopperRenderProps } from 'lib/ui/Popper'; -import { IconifiedSelectProps, IconifiedSelectPropsBase, IconifiedSelectPropsBase2 } from './types'; +import { IconifiedSelectProps, IconifiedSelectPropsBase } from './types'; interface Props extends PopperRenderProps, TestIDProperty, IconifiedSelectPropsBase { search?: { @@ -15,55 +15,6 @@ interface Props extends PopperRenderProps, TestIDProperty, IconifiedSelectPro }; } -interface Props2 extends PopperRenderProps, TestIDProperty, IconifiedSelectPropsBase2 { - search?: { - value?: string; - }; -} - -export const IconifiedSelectMenu2: FC = ({ - opened, - search, - testID, - padded, - noItemsText, - OptionsContent, - toggleOpened -}) => { - const { trackEvent } = useAnalytics(); - - useEffect(() => { - if (testID && opened) trackEvent(testID, AnalyticsEventCategory.DropdownOpened); - }, [opened, trackEvent]); - - return ( - - {OptionsContent.length ? ( - OptionsContent.map(OptionContent => ) - ) : ( -

- {search?.value ? : null} - - {noItemsText} -

- )} -
- ); -}; - export const IconifiedSelectMenu = (props: Props) => { const { opened, diff --git a/src/app/templates/IconifiedSelect/types.ts b/src/app/templates/IconifiedSelect/types.ts index e68d94982..2cc577c68 100644 --- a/src/app/templates/IconifiedSelect/types.ts +++ b/src/app/templates/IconifiedSelect/types.ts @@ -1,4 +1,4 @@ -import { ComponentType, ReactNode } from 'react'; +import { ComponentType } from 'react'; import { TestIDProperty } from 'lib/analytics'; @@ -19,11 +19,6 @@ export interface IconifiedSelectPropsBase { noItemsText: string; padded?: boolean; } -export interface IconifiedSelectPropsBase2 { - OptionsContent: Array; - noItemsText: string; - padded?: boolean; -} export interface IconifiedSelectProps extends IconifiedSelectPropsBase, TestIDProperty { FieldContent: OptionContentComponent; @@ -36,9 +31,3 @@ export interface IconifiedSelectProps extends IconifiedSelectPropsBase, Te inputTestID?: string; }; } -export interface IconifiedSelectProps2 extends IconifiedSelectPropsBase2, TestIDProperty { - FieldContent: ReactNode; - BeforeContent?: ReactNode; - className?: string; - fieldStyle?: React.CSSProperties; -} diff --git a/src/app/templates/InputGeneral/SelectGeneral.tsx b/src/app/templates/InputGeneral/SelectGeneral.tsx index c10d48d7f..989f63029 100644 --- a/src/app/templates/InputGeneral/SelectGeneral.tsx +++ b/src/app/templates/InputGeneral/SelectGeneral.tsx @@ -1,5 +1,6 @@ -import React, { ChangeEventHandler, ReactNode, FC, Dispatch, SetStateAction, useMemo } from 'react'; +import React, { ChangeEventHandler, ReactNode, FC, Dispatch, SetStateAction } from 'react'; +import { isDefined } from '@rnw-community/shared'; import classNames from 'clsx'; import { List } from 'react-virtualized'; @@ -12,7 +13,6 @@ import { ReactComponent as SearchIcon } from 'app/icons/search.svg'; import { t } from 'lib/i18n'; import Popper from 'lib/ui/Popper'; import { sameWidthModifiers } from 'lib/ui/same-width-modifiers'; -import { isDefined } from 'lib/utils/is-defined'; export enum DropdownSize { Large, From 0c048b7d8d0ca6673c46672b742d5de755413e1f Mon Sep 17 00:00:00 2001 From: herkoss Date: Thu, 8 Jun 2023 15:22:34 +0300 Subject: [PATCH 06/21] TW-565 Fixed linter errors --- package.json | 4 + src/app/hooks/use-app-env-style.hook.ts | 7 - src/app/templates/AssetSelect/AssetSelect.tsx | 6 +- .../templates/InputGeneral/InputGeneral.tsx | 4 +- .../templates/InputGeneral/SelectGeneral.tsx | 115 +++++---- .../PaymentProviderInputHeader.tsx | 45 ---- .../PaymentProviderOption.tsx | 12 +- .../PaymentProvidersMenu/index.tsx | 80 ------ .../templates/PaymentProviderInput/index.tsx | 101 +++++--- .../Components/BlockExplorerSelect.tsx | 93 +++---- .../Components/FiatCurrencySelect.tsx | 119 ++++----- .../Components/LocaleSelect.tsx | 92 ++++--- .../SwapFormInput/AssetsMenu/AssetsMenu.tsx | 98 -------- .../SwapFormInput/SwapFormInput.props.ts | 2 +- .../SwapForm/SwapFormInput/SwapFormInput.tsx | 221 +++++++++-------- .../SwapFormInputHeader.tsx | 220 ----------------- .../SwapFormTokenIdInput.hook.ts | 36 --- .../TopUpInput/CurrenciesMenu/index.tsx | 93 ------- .../templates/TopUpInput/TopUpInputHeader.tsx | 230 ------------------ src/app/templates/TopUpInput/index.tsx | 8 +- src/app/utils/function.utils.ts | 1 - src/lib/fiat-currency/core.ts | 2 - src/lib/fiat-currency/index.ts | 1 - src/lib/ui/Popper.tsx | 4 +- yarn.lock | 10 + 25 files changed, 452 insertions(+), 1152 deletions(-) delete mode 100644 src/app/hooks/use-app-env-style.hook.ts delete mode 100644 src/app/templates/PaymentProviderInput/PaymentProviderInputHeader.tsx delete mode 100644 src/app/templates/PaymentProviderInput/PaymentProvidersMenu/index.tsx delete mode 100644 src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetsMenu.tsx delete mode 100644 src/app/templates/SwapForm/SwapFormInput/SwapFormInputHeader/SwapFormInputHeader.tsx delete mode 100644 src/app/templates/SwapForm/SwapFormInput/SwapFormTokenIdInput.hook.ts delete mode 100644 src/app/templates/TopUpInput/CurrenciesMenu/index.tsx delete mode 100644 src/app/templates/TopUpInput/TopUpInputHeader.tsx delete mode 100644 src/app/utils/function.utils.ts diff --git a/package.json b/package.json index 2766c1cbc..2784e8e36 100644 --- a/package.json +++ b/package.json @@ -181,6 +181,7 @@ "use-force-update": "1.0.7", "use-onclickoutside": "0.4.1", "util": "0.11.1", + "uuid": "^9.0.0", "wasm-themis": "0.14.6", "webextension-polyfill": "^0.10.0", "webpack": "^5.74.0", @@ -215,5 +216,8 @@ "nanoid": "3.1.31", "tslib": "2.4.0", "@types/react": "18.0.15" + }, + "devDependencies": { + "@types/uuid": "^9.0.1" } } diff --git a/src/app/hooks/use-app-env-style.hook.ts b/src/app/hooks/use-app-env-style.hook.ts deleted file mode 100644 index 2dcbcd018..000000000 --- a/src/app/hooks/use-app-env-style.hook.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { useAppEnv } from '../env'; - -export const useAppEnvStyle = () => { - const { popup } = useAppEnv(); - - return { dropdownWidth: popup ? 328 : 382 }; -}; diff --git a/src/app/templates/AssetSelect/AssetSelect.tsx b/src/app/templates/AssetSelect/AssetSelect.tsx index 6bc716d26..e217bf09d 100644 --- a/src/app/templates/AssetSelect/AssetSelect.tsx +++ b/src/app/templates/AssetSelect/AssetSelect.tsx @@ -67,7 +67,7 @@ const AssetSelect: FC = ({ value, assets, onChange, className, export default AssetSelect; -export const AssetSelectTitle: FC = () => ( +const AssetSelectTitle: FC = () => (

@@ -81,7 +81,7 @@ export const AssetSelectTitle: FC = () => ( type AssetSelectOptionRenderProps = IconifiedSelectOptionRenderProps; -export const AssetFieldContent: FC = ({ option }) => { +const AssetFieldContent: FC = ({ option }) => { const account = useAccount(); const assetSlug = getSlug(option); const metadata = useAssetMetadata(assetSlug); @@ -116,7 +116,7 @@ export const AssetFieldContent: FC = ({ option }) ); }; -export const AssetOptionContent: FC = ({ option }) => { +const AssetOptionContent: FC = ({ option }) => { const slug = getSlug(option); return ( diff --git a/src/app/templates/InputGeneral/InputGeneral.tsx b/src/app/templates/InputGeneral/InputGeneral.tsx index e70f0363e..b03a0e7b5 100644 --- a/src/app/templates/InputGeneral/InputGeneral.tsx +++ b/src/app/templates/InputGeneral/InputGeneral.tsx @@ -12,9 +12,9 @@ interface Props { export const InputGeneral: FC = ({ className, header, mainContent, footer }) => { return (
-
{header}
+ {header &&
{header}
}
{mainContent}
-
{footer}
+ {footer &&
{footer}
}
); }; diff --git a/src/app/templates/InputGeneral/SelectGeneral.tsx b/src/app/templates/InputGeneral/SelectGeneral.tsx index 989f63029..b31eeb936 100644 --- a/src/app/templates/InputGeneral/SelectGeneral.tsx +++ b/src/app/templates/InputGeneral/SelectGeneral.tsx @@ -2,39 +2,48 @@ import React, { ChangeEventHandler, ReactNode, FC, Dispatch, SetStateAction } fr import { isDefined } from '@rnw-community/shared'; import classNames from 'clsx'; -import { List } from 'react-virtualized'; +import { v4 } from 'uuid'; import AssetField from 'app/atoms/AssetField'; import DropdownWrapper from 'app/atoms/DropdownWrapper'; import Spinner from 'app/atoms/Spinner/Spinner'; -import { useAppEnvStyle } from 'app/hooks/use-app-env-style.hook'; import { ReactComponent as ChevronDownIcon } from 'app/icons/chevron-down.svg'; import { ReactComponent as SearchIcon } from 'app/icons/search.svg'; +import { AnalyticsEventCategory, setTestID, useAnalytics } from 'lib/analytics'; import { t } from 'lib/i18n'; import Popper from 'lib/ui/Popper'; import { sameWidthModifiers } from 'lib/ui/same-width-modifiers'; -export enum DropdownSize { - Large, - Small -} interface Props { DropdownFaceContent: ReactNode; Input?: ReactNode; - className?: string; + optionsListClassName?: string; + dropdownButtonClassName?: string; searchProps: SelectSearchProps; optionsProps: SelectOptionsPropsBase; - dropdownSize?: DropdownSize; + testIds?: { + dropdownTestId?: string; + searchInputTestId?: string; + }; } export const SelectGeneral = ({ - DropdownFaceContent, Input, searchProps, optionsProps, - dropdownSize = DropdownSize.Small + testIds, + DropdownFaceContent, + optionsListClassName, + dropdownButtonClassName }: Props) => { const isInputDefined = isDefined(Input); + const { trackEvent } = useAnalytics(); + + const trackDropdownClick = () => { + if (testIds?.dropdownTestId) { + trackEvent(testIds?.dropdownTestId, AnalyticsEventCategory.DropdownOpened); + } + }; return ( ({ strategy="fixed" modifiers={sameWidthModifiers} fallbackPlacementsEnabled={false} - popup={({ opened, setOpened }) => } + popup={({ opened, setOpened }) => ( + + )} > {({ ref, opened, toggleOpened }) => (
}> {opened ? ( - + ) : (
)} - 2 ? 240 : options.length * ROW_HEIGHT} - rowCount={options.length} - rowHeight={ROW_HEIGHT} - rowRenderer={({ index }) => { - const option = options[index]; - - return ( - - ); - }} - /> + + ))} + ); }; interface SelectSearchProps { - dropdownSize?: DropdownSize; + testId?: string; + className?: string; searchValue: string; tokenIdValue?: string; showTokenIdInput?: boolean; @@ -154,9 +166,10 @@ interface SelectSearchProps { onTokenIdChange?: (newValue: number | string | undefined) => void; } const SelectSearch: FC = ({ + testId, + className, searchValue, tokenIdValue, - dropdownSize = DropdownSize.Small, showTokenIdInput = false, onSearchChange, onTokenIdChange @@ -165,11 +178,10 @@ const SelectSearch: FC = ({
-
+
@@ -178,9 +190,10 @@ const SelectSearch: FC = ({
diff --git a/src/app/templates/PaymentProviderInput/PaymentProviderInputHeader.tsx b/src/app/templates/PaymentProviderInput/PaymentProviderInputHeader.tsx deleted file mode 100644 index 97b7472c9..000000000 --- a/src/app/templates/PaymentProviderInput/PaymentProviderInputHeader.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React, { forwardRef } from 'react'; - -import { isDefined } from '@rnw-community/shared'; - -import { ReactComponent as ChevronDownIcon } from 'app/icons/chevron-down.svg'; -import { setTestID, TestIDProps } from 'lib/analytics'; -import { PaymentProviderInterface } from 'lib/buy-with-credit-card/topup.interface'; -import { T } from 'lib/i18n'; -import { PopperRenderProps } from 'lib/ui/Popper'; - -import { TopUpProviderIcon } from '../TopUpProviderIcon'; -import { MoneyRange } from './MoneyRange'; - -interface Props extends PopperRenderProps, TestIDProps { - value?: PaymentProviderInterface; -} - -export const PaymentProviderInputHeader = forwardRef(({ value, toggleOpened, testID }, ref) => ( -
- -
- {isDefined(value) ? ( - <> - {value.name} - - - ) : ( - - - - )} -
- -
-)); diff --git a/src/app/templates/PaymentProviderInput/PaymentProvidersMenu/PaymentProviderOption.tsx b/src/app/templates/PaymentProviderInput/PaymentProvidersMenu/PaymentProviderOption.tsx index ec82b2c05..fd6dedd83 100644 --- a/src/app/templates/PaymentProviderInput/PaymentProvidersMenu/PaymentProviderOption.tsx +++ b/src/app/templates/PaymentProviderInput/PaymentProvidersMenu/PaymentProviderOption.tsx @@ -8,7 +8,6 @@ import { useCryptoCurrenciesSelector } from 'app/store/buy-with-credit-card/sele import { TopUpProviderIcon } from 'app/templates/TopUpProviderIcon'; import { PaymentProviderInterface } from 'lib/buy-with-credit-card/topup.interface'; import { t, toLocalFixed } from 'lib/i18n'; -import { useScrollIntoView } from 'lib/ui/use-scroll-into-view'; import { MoneyRange } from '../MoneyRange'; import { PaymentProviderTag, PaymentProviderTagProps } from './PaymentProviderTag'; @@ -17,12 +16,10 @@ interface Props extends Partial> { value: PaymentProviderInterface; isSelected: boolean; shouldShowSeparator: boolean; - onClick?: (newValue: PaymentProviderInterface) => void; } -export const PaymentProviderOption: FC = ({ value, isSelected, shouldShowSeparator, style, onClick }) => { +export const PaymentProviderOption: FC = ({ value, isSelected, shouldShowSeparator, style }) => { const cryptoCurrencies = useCryptoCurrenciesSelector(value.id); - const ref = useScrollIntoView(isSelected, { behavior: 'smooth', block: 'start' }); const tagsProps = useMemo(() => { const result: PaymentProviderTagProps[] = []; @@ -43,16 +40,13 @@ export const PaymentProviderOption: FC = ({ value, isSelected, shouldShow }, [value, cryptoCurrencies]); return ( -
- + ); }; diff --git a/src/app/templates/PaymentProviderInput/PaymentProvidersMenu/index.tsx b/src/app/templates/PaymentProviderInput/PaymentProvidersMenu/index.tsx deleted file mode 100644 index c1ff04d96..000000000 --- a/src/app/templates/PaymentProviderInput/PaymentProvidersMenu/index.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React, { FC, useEffect } from 'react'; - -import classNames from 'clsx'; - -import DropdownWrapper from 'app/atoms/DropdownWrapper'; -import Spinner from 'app/atoms/Spinner/Spinner'; -import { useAppEnvStyle } from 'app/hooks/use-app-env-style.hook'; -import { AnalyticsEventCategory, TestIDProperty, useAnalytics } from 'lib/analytics'; -import { PaymentProviderInterface } from 'lib/buy-with-credit-card/topup.interface'; -import { T } from 'lib/i18n'; - -import { PaymentProviderOption } from './PaymentProviderOption'; -import styles from './style.module.css'; - -interface Props extends TestIDProperty { - value?: PaymentProviderInterface; - options: PaymentProviderInterface[]; - isLoading?: boolean; - opened: boolean; - setOpened: (newValue: boolean) => void; - onChange?: (newValue: PaymentProviderInterface) => void; -} - -export const PaymentProvidersMenu: FC = ({ - value, - options, - isLoading = false, - opened, - testID, - setOpened, - onChange -}) => { - const { dropdownWidth } = useAppEnvStyle(); - - const { trackEvent } = useAnalytics(); - - useEffect(() => { - if (testID && opened) trackEvent(testID, AnalyticsEventCategory.DropdownOpened); - }, [opened, trackEvent]); - - const handleOptionClick = onChange - ? (newValue: PaymentProviderInterface) => { - if (value?.id !== newValue.id) { - onChange(newValue); - } - setOpened(false); - } - : undefined; - - return ( - - {(options.length === 0 || isLoading) && ( -
- {isLoading ? ( - - ) : ( -

- -

- )} -
- )} -
- {options.map((option, index) => ( - - ))} -
-
- ); -}; diff --git a/src/app/templates/PaymentProviderInput/index.tsx b/src/app/templates/PaymentProviderInput/index.tsx index 56b540cdc..e57fa625a 100644 --- a/src/app/templates/PaymentProviderInput/index.tsx +++ b/src/app/templates/PaymentProviderInput/index.tsx @@ -1,15 +1,24 @@ -import React, { FC } from 'react'; +import React, { FC, useState } from 'react'; +import { isDefined } from '@rnw-community/shared'; import classNames from 'clsx'; -import Popper from 'lib/ui/Popper'; -import { sameWidthModifiers } from 'lib/ui/same-width-modifiers'; +import { setTestID } from 'lib/analytics'; +import { PaymentProviderInterface } from 'lib/buy-with-credit-card/topup.interface'; +import { T } from 'lib/i18n'; import { isTruthy } from 'lib/utils'; -import { PaymentProviderInputHeader } from './PaymentProviderInputHeader'; -import { PaymentProvidersMenu } from './PaymentProvidersMenu'; +import { InputGeneral } from '../InputGeneral/InputGeneral'; +import { SelectGeneral } from '../InputGeneral/SelectGeneral'; +import { TopUpProviderIcon } from '../TopUpProviderIcon'; +import { MoneyRange } from './MoneyRange'; +import { PaymentProviderOption } from './PaymentProvidersMenu/PaymentProviderOption'; import { PaymentProviderInputProps } from './types'; +const renderOptionContent = (option: PaymentProviderInterface) => ( + +); + export const PaymentProviderInput: FC = ({ className, error, @@ -19,36 +28,58 @@ export const PaymentProviderInput: FC = ({ onChange, headerTestID, testID -}) => ( -
- ( - - )} - > - {({ ref, opened, toggleOpened, setOpened }) => ( - } - value={value} - opened={opened} - setOpened={setOpened} - toggleOpened={toggleOpened} - testID={headerTestID} - /> +}) => { + const [searchValue, setSearchValue] = useState(''); + return ( +
+ } + searchProps={{ searchValue, onSearchChange: event => setSearchValue(event?.target.value) }} + optionsProps={{ + options, + isLoading, + noItemsText: 'No Items', + renderOptionContent, + onOptionChange: onChange + }} + /> + } + footer={isTruthy(error) && {error}} + /> +
+ ); +}; + +interface PaymentProviderDropdownFaceContentProps { + value?: PaymentProviderInterface; + testId?: string; +} + +const PaymentProviderDropdownFaceContent: FC = ({ value, testId }) => ( +
+ +
+ {isDefined(value) ? ( + <> + {value.name} + + + ) : ( + + + )} - - {isTruthy(error) && {error}} +
); diff --git a/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx b/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx index a3762b758..5cfe3b388 100644 --- a/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx +++ b/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx @@ -1,40 +1,42 @@ -import React, { useMemo, useCallback, FC } from 'react'; +import React, { useMemo, useCallback, FC, useState } from 'react'; +import classNames from 'clsx'; import browser from 'webextension-polyfill'; import Flag from 'app/atoms/Flag'; import { InputGeneral } from 'app/templates/InputGeneral/InputGeneral'; import { SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; import { setTestID } from 'lib/analytics'; -import { T, t } from 'lib/i18n'; +import { T } from 'lib/i18n'; import { BlockExplorer, useChainId, BLOCK_EXPLORERS, useBlockExplorer } from 'lib/temple/front'; import { isKnownChainId } from 'lib/temple/types'; import { searchAndFilterItems } from 'lib/utils/search-items'; -import IconifiedSelect, { IconifiedSelectOptionRenderProps } from '../../IconifiedSelect'; +import { IconifiedSelectOptionRenderProps } from '../../IconifiedSelect'; import { SettingsGeneralSelectors } from '../selectors'; type BlockExplorerSelectProps = { className?: string; }; -const getBlockExplorerId = ({ id }: BlockExplorer) => id; - -const renderOptionContent = (option: BlockExplorer) => ; +const renderOptionContent = (option: BlockExplorer, isSelected: boolean) => ( + +); -const BlockExplorerSelect: FC = ({ className }) => { +const BlockExplorerSelect: FC = () => { const { explorer, setExplorerId } = useBlockExplorer(); const chainId = useChainId(true)!; + const [searchValue, setSearchValue] = useState(''); const options = useMemo(() => { if (chainId && isKnownChainId(chainId)) { - return BLOCK_EXPLORERS.filter(explorer => explorer.baseUrls.get(chainId)); + const knownExplorers = BLOCK_EXPLORERS.filter(explorer => explorer.baseUrls.get(chainId)); + + return searchBlockExplorer(searchValue, knownExplorers); } return []; - }, [chainId]); - - const searchItems = useCallback((searchString: string) => searchBlockExplorer(searchString, options), [options]); + }, [chainId, searchValue]); const handleBlockExplorerChange = useCallback( (option: BlockExplorer) => { @@ -44,50 +46,39 @@ const BlockExplorerSelect: FC = ({ className }) => { ); return ( - <> +
} mainContent={ - <> - } - optionsProps={{ - options: options, - noItemsText: 'No items', - renderOptionContent, - onOptionChange: handleBlockExplorerChange - }} - searchProps={{ - searchValue: 'qwe', - onSearchChange: () => console.log('qweqwe') - }} - /> - + } + optionsProps={{ + options, + noItemsText: 'No items', + renderOptionContent: option => + renderOptionContent(option, JSON.stringify(option) === JSON.stringify(explorer)), + onOptionChange: handleBlockExplorerChange + }} + searchProps={{ + searchValue, + onSearchChange: event => setSearchValue(event?.target.value) + }} + /> } /> - - +
); }; export default BlockExplorerSelect; const BlockExplorerTitle: FC = () => ( -

+

@@ -108,9 +99,19 @@ const BlockExplorerFieldContent: FC> = ({ option }) => { +interface BlockExplorerOptionContentProps { + option: BlockExplorer; + isSelected?: boolean; +} + +const BlockExplorerOptionContent: FC = ({ option, isSelected }) => { return ( -
+
diff --git a/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx b/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx index 95d95d7e4..26773e11e 100644 --- a/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx +++ b/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx @@ -1,27 +1,47 @@ -import React, { useCallback, FC } from 'react'; +import React, { useCallback, FC, useState, useMemo } from 'react'; + +import classNames from 'clsx'; import { InputGeneral } from 'app/templates/InputGeneral/InputGeneral'; import { SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; import { AnalyticsEventCategory, AnalyticsEventEnum, setTestID, useAnalytics } from 'lib/analytics'; -import { FIAT_CURRENCIES, FiatCurrencyOption, getFiatCurrencyKey, useFiatCurrency } from 'lib/fiat-currency'; -import { T, t } from 'lib/i18n'; +import { FIAT_CURRENCIES, FiatCurrencyOption, useFiatCurrency } from 'lib/fiat-currency'; +import { T } from 'lib/i18n'; import { searchAndFilterItems } from 'lib/utils/search-items'; -import IconifiedSelect, { IconifiedSelectOptionRenderProps } from '../../IconifiedSelect'; import { SettingsGeneralSelectors } from '../selectors'; type FiatCurrencySelectProps = { className?: string; }; -const renderOptionContent = (option: FiatCurrencyOption) => ; +const renderOptionContent = (option: FiatCurrencyOption, isSelected: boolean) => ( + +); -const FiatCurrencySelect: FC = ({ className }) => { +const FiatCurrencySelect: FC = () => { const { trackEvent } = useAnalytics(); const { selectedFiatCurrency, setSelectedFiatCurrency } = useFiatCurrency(); const value = selectedFiatCurrency; + const [searchValue, setSearchValue] = useState(''); + + const options = useMemo( + () => + searchAndFilterItems( + FIAT_CURRENCIES, + searchValue, + [ + { name: 'name', weight: 1 }, + { name: 'fullname', weight: 0.75 } + ], + null, + 0.25 + ), + [searchValue] + ); + const handleFiatCurrencyChange = useCallback( (fiatOption: FiatCurrencyOption) => { trackEvent(AnalyticsEventEnum.FiatCurrencyChanged, AnalyticsEventCategory.ButtonPress, { name: fiatOption.name }); @@ -31,59 +51,51 @@ const FiatCurrencySelect: FC = ({ className }) => { ); return ( - <> +
} mainContent={ - <> - } - optionsProps={{ - options: FIAT_CURRENCIES, - noItemsText: 'No items', - renderOptionContent, - onOptionChange: handleFiatCurrencyChange - }} - searchProps={{ - searchValue: 'qwe', - onSearchChange: event => searchFiatCurrencyOptions(event.target.value) - }} - /> - + } + optionsProps={{ + options, + noItemsText: 'No items', + renderOptionContent: option => + renderOptionContent(option, option.fullname === selectedFiatCurrency.fullname), + onOptionChange: handleFiatCurrencyChange + }} + searchProps={{ + searchValue, + onSearchChange: event => setSearchValue(event.target.value) + }} + /> } /> - - +
); }; export default FiatCurrencySelect; const FiatCurrencyTitle: FC = () => ( -

+

); -type SelectItemProps = IconifiedSelectOptionRenderProps; +interface FiatCurrencyOptionContentProps { + option: FiatCurrencyOption; + isSelected?: boolean; +} -const FiatCurrencyIcon: FC = ({ option: { symbol } }) => ( +const FiatCurrencyIcon: FC = ({ option: { symbol } }) => (
= ({ option: { symbol } }) => (
); -const FiatCurrencyFieldContent: FC = ({ option }) => { +const FiatCurrencyFieldContent: FC = ({ option }) => { return ( -
+
{option.name} @@ -102,9 +114,14 @@ const FiatCurrencyFieldContent: FC = ({ option }) => { ); }; -const FiatCurrencyOptionContent: FC = ({ option }) => { +const FiatCurrencyOptionContent: FC = ({ option, isSelected }) => { return ( -
+
@@ -113,15 +130,3 @@ const FiatCurrencyOptionContent: FC = ({ option }) => {
); }; - -const searchFiatCurrencyOptions = (searchString: string) => - searchAndFilterItems( - FIAT_CURRENCIES, - searchString, - [ - { name: 'name', weight: 1 }, - { name: 'fullname', weight: 0.75 } - ], - null, - 0.25 - ); diff --git a/src/app/templates/SettingsGeneral/Components/LocaleSelect.tsx b/src/app/templates/SettingsGeneral/Components/LocaleSelect.tsx index 7f9403d66..05027f55b 100644 --- a/src/app/templates/SettingsGeneral/Components/LocaleSelect.tsx +++ b/src/app/templates/SettingsGeneral/Components/LocaleSelect.tsx @@ -1,15 +1,16 @@ -import React, { useMemo, useCallback, FC } from 'react'; +import React, { useMemo, useCallback, FC, useState } from 'react'; +import classNames from 'clsx'; import browser from 'webextension-polyfill'; import Flag from 'app/atoms/Flag'; import { InputGeneral } from 'app/templates/InputGeneral/InputGeneral'; import { SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; -import { AnalyticsEventCategory, AnalyticsEventEnum, useAnalytics } from 'lib/analytics'; +import { AnalyticsEventCategory, AnalyticsEventEnum, setTestID, useAnalytics } from 'lib/analytics'; import { getCurrentLocale, updateLocale, T } from 'lib/i18n'; import { searchAndFilterItems } from 'lib/utils/search-items'; -import { IconifiedSelectOptionRenderProps } from '../../IconifiedSelect'; +import { SettingsGeneralSelectors } from '../selectors'; type LocaleSelectProps = { className?: string; @@ -101,9 +102,20 @@ const LOCALE_OPTIONS: LocaleOption[] = [ const LocaleSelect: FC = () => { const selectedLocale = getCurrentLocale(); const { trackEvent } = useAnalytics(); + const [searchValue, setSearchValue] = useState(''); + + const options = useMemo( + () => + searchAndFilterItems(LOCALE_OPTIONS, searchValue, [ + { name: 'code', weight: 0.75 }, + { name: 'flagName', weight: 1 }, + { name: 'label', weight: 0.5 } + ]), + [searchValue] + ); const value = useMemo( - () => LOCALE_OPTIONS.find(({ code }) => code === selectedLocale) ?? LOCALE_OPTIONS[0], + () => options.find(({ code }) => code === selectedLocale) ?? LOCALE_OPTIONS[0], [selectedLocale] ); @@ -116,26 +128,31 @@ const LocaleSelect: FC = () => { ); return ( - } - mainContent={ - <> +
+ } + mainContent={ } optionsProps={{ - options: LOCALE_OPTIONS, + options, noItemsText: 'No items', - renderOptionContent, + renderOptionContent: option => renderOptionContent(option, option.code === value.code), onOptionChange: handleLocaleChange }} searchProps={{ - searchValue: 'qwe', - onSearchChange: () => console.log('qweqwe') + searchValue, + onSearchChange: event => setSearchValue(event?.target.value) }} /> - - } - /> + } + /> +
); }; @@ -149,25 +166,33 @@ const LocaleTitle: FC = () => (

); -type SelectItemProps = IconifiedSelectOptionRenderProps; +interface LocaleOptionContentProps { + option: LocaleOption; + isSelected?: boolean; +} -const LocaleIcon: FC = ({ option: { flagName, code } }) => ( +const LocaleIcon: FC = ({ option: { flagName, code } }) => ( ); -const LocaleFieldContent = (option: LocaleOption) => { - return ( -
- +const LocaleFieldContent = (option: LocaleOption) => ( +
+ - {option.label} -
- ); -}; + {option.label} +
+); -const LocaleOptionContent: FC = ({ option }) => { +const LocaleOptionContent: FC = ({ option, isSelected }) => { return ( - <> +
@@ -181,15 +206,10 @@ const LocaleOptionContent: FC = ({ option }) => {
)}
- +
); }; -const renderOptionContent = (option: LocaleOption) => ; - -const searchLocale = (searchString: string) => - searchAndFilterItems(LOCALE_OPTIONS, searchString, [ - { name: 'code', weight: 0.75 }, - { name: 'flagName', weight: 1 }, - { name: 'label', weight: 0.5 } - ]); +const renderOptionContent = (option: LocaleOption, isSelected: boolean) => ( + +); diff --git a/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetsMenu.tsx b/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetsMenu.tsx deleted file mode 100644 index f6066dc0a..000000000 --- a/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetsMenu.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, { FC, useEffect } from 'react'; - -import { List } from 'react-virtualized'; - -import DropdownWrapper from 'app/atoms/DropdownWrapper'; -import Spinner from 'app/atoms/Spinner/Spinner'; -import { useAppEnvStyle } from 'app/hooks/use-app-env-style.hook'; -import { ReactComponent as SearchIcon } from 'app/icons/search.svg'; -import { AnalyticsEventCategory, TestIDProperty, useAnalytics } from 'lib/analytics'; -import { T } from 'lib/i18n'; - -import { AssetOption } from './AssetOption'; - -interface Props extends TestIDProperty { - value?: string; - options: string[]; - isLoading: boolean; - searchString?: string; - showTokenIdInput: boolean; - opened: boolean; - setOpened: (newValue: boolean) => void; - onChange: (newValue: string) => void; -} - -export const AssetsMenu: FC = ({ - value, - options, - isLoading, - searchString, - showTokenIdInput, - opened, - testID, - setOpened, - onChange -}) => { - const { dropdownWidth } = useAppEnvStyle(); - - const { trackEvent } = useAnalytics(); - - useEffect(() => { - if (testID && opened) trackEvent(testID, AnalyticsEventCategory.DropdownOpened); - }, [opened, trackEvent]); - - const handleOptionClick = (newValue: string) => { - if (value !== newValue) { - onChange(newValue); - } - setOpened(false); - }; - - return ( - - {(options.length === 0 || isLoading) && ( -
- {isLoading ? ( - - ) : ( -

- {searchString ? : null} - - - - -

- )} -
- )} - - { - const option = options[index]; - - return ( - - ); - }} - /> -
- ); -}; diff --git a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.props.ts b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.props.ts index f03439853..19d96444b 100644 --- a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.props.ts +++ b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.props.ts @@ -15,7 +15,7 @@ export interface SwapFormInputProps extends TestIDProps { testIDs?: SwapFormTestIDs; } -export interface SwapFormTestIDs { +interface SwapFormTestIDs { dropdown: string; input?: string; searchInput?: string; diff --git a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx index d213d070b..2177f7338 100644 --- a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx +++ b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx @@ -1,21 +1,20 @@ import React, { ChangeEvent, FC, ReactNode, useMemo } from 'react'; +import { isDefined } from '@rnw-community/shared'; import BigNumber from 'bignumber.js'; import classNames from 'clsx'; -import { AssetMetadata } from 'lib/temple/metadata'; -import { isDefined } from 'lib/utils/is-defined'; import AssetField from 'app/atoms/AssetField'; import Money from 'app/atoms/Money'; import { AssetIcon } from 'app/templates/AssetIcon'; import InFiat from 'app/templates/InFiat'; import { InputGeneral } from 'app/templates/InputGeneral/InputGeneral'; -import { DropdownSize, SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; -import { useFormAnalytics } from 'lib/analytics'; +import { SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; +import { setTestID, useFormAnalytics } from 'lib/analytics'; import { T, t, toLocalFormat } from 'lib/i18n'; -import { EMPTY_BASE_METADATA, useAssetMetadata } from 'lib/metadata'; +import { EMPTY_BASE_METADATA, useAssetMetadata, AssetMetadataBase } from 'lib/metadata'; import { useAccount, useBalance, useGetTokenMetadata, useOnBlock } from 'lib/temple/front'; -import { useFilteredSwapAssets } from 'lib/temple/front/assets'; +import { useAvailableRoute3Tokens, useFilteredSwapAssets } from 'lib/temple/front/assets'; import { AssetOption } from './AssetsMenu/AssetOption'; import { PercentageButton } from './PercentageButton/PercentageButton'; @@ -24,9 +23,20 @@ import { SwapFormInputProps } from './SwapFormInput.props'; const EXCHANGE_XTZ_RESERVE = new BigNumber('0.3'); const PERCENTAGE_BUTTONS = [25, 50, 75, 100]; -const renderOptionContent = (option: string) => ; +const renderOptionContent = (option: string, isSelected: boolean) => ( + +); -export const SwapFormInput: FC = ({ value, label, error, name, onChange, amountInputDisabled }) => { +export const SwapFormInput: FC = ({ + className, + value, + label, + error, + name, + amountInputDisabled, + testIDs, + onChange +}) => { const { trackChange } = useFormAnalytics('SwapForm'); const { assetSlug, amount } = value; @@ -44,6 +54,7 @@ export const SwapFormInput: FC = ({ value, label, error, nam const balance = useBalance(assetSlugWithFallback, account.publicKeyHash, { suspense: false }); useOnBlock(_ => balance.mutate()); + const { isLoading } = useAvailableRoute3Tokens(); const { filteredAssets, searchValue, setSearchValue, setTokenId } = useFilteredSwapAssets(name); const maxAmount = useMemo(() => { @@ -106,74 +117,93 @@ export const SwapFormInput: FC = ({ value, label, error, nam }, [error]); return ( - - } - mainContent={ - } - searchProps={{ - searchValue, - onSearchChange: handleSearchChange - }} - Input={ - - } - optionsProps={{ - options: filteredAssets, - noItemsText: 'No items', - renderOptionContent, - onOptionChange: handleSelectedAssetChange - }} - /> - } - footer={ -
- {prettyError &&
{prettyError}
} - + -
- } - /> + } + mainContent={ + + } + searchProps={{ + searchValue, + onSearchChange: handleSearchChange + }} + Input={ + + } + optionsProps={{ + isLoading, + options: filteredAssets, + noItemsText: 'No items', + renderOptionContent: option => renderOptionContent(option, value.assetSlug === option), + onOptionChange: handleSelectedAssetChange + }} + /> + } + footer={ +
+ {prettyError &&
{prettyError}
} + +
+ } + /> + ); }; interface SwapFieldProps { + testId?: string; selectedAssetSlug?: string; - selectedAssetMetadata: AssetMetadata; + selectedAssetMetadata: AssetMetadataBase; } -const SwapDropdownFace: FC = ({ selectedAssetSlug, selectedAssetMetadata }) => - selectedAssetSlug ? ( -
- - - {selectedAssetMetadata.symbol} - -
- ) : ( -
-
- +const SwapDropdownFace: FC = ({ testId, selectedAssetSlug, selectedAssetMetadata }) => ( +
+ {selectedAssetSlug ? ( +
+ + + {selectedAssetMetadata.symbol} +
-
- ); + ) : ( +
+
+ +
+
+ )} +
+); interface SwapInputProps extends SwapFieldProps { + testId?: string; amount: BigNumber | undefined; amountInputDisabled: boolean; onChange: (value?: BigNumber) => void; @@ -183,43 +213,44 @@ const SwapInput: FC = ({ amountInputDisabled, selectedAssetSlug, selectedAssetMetadata, + testId, onChange }) => { const handleAmountChange = (newAmount?: string) => onChange(Boolean(newAmount) && isDefined(newAmount) ? new BigNumber(newAmount) : undefined); return ( -
-
-
- +
+
+ - - {({ balance, symbol }) => ( -
- - {balance} - {symbol} -
- )} -
-
+ + {({ balance, symbol }) => ( +
+ + {balance} + {symbol} +
+ )} +
); @@ -235,7 +266,7 @@ const SwapInputHeader: FC<{ label: ReactNode; selectedAssetSlug: string; selecte useOnBlock(_ => balance.mutate()); return ( -
+
{label} {selectedAssetSlug && ( diff --git a/src/app/templates/SwapForm/SwapFormInput/SwapFormInputHeader/SwapFormInputHeader.tsx b/src/app/templates/SwapForm/SwapFormInput/SwapFormInputHeader/SwapFormInputHeader.tsx deleted file mode 100644 index 7808f94a6..000000000 --- a/src/app/templates/SwapForm/SwapFormInput/SwapFormInputHeader/SwapFormInputHeader.tsx +++ /dev/null @@ -1,220 +0,0 @@ -import React, { ChangeEvent, forwardRef, FocusEvent, useRef, useState } from 'react'; - -import BigNumber from 'bignumber.js'; -import classNames from 'clsx'; - -import AssetField from 'app/atoms/AssetField'; -import Money from 'app/atoms/Money'; -import { ReactComponent as ChevronDownIcon } from 'app/icons/chevron-down.svg'; -import { ReactComponent as SearchIcon } from 'app/icons/search.svg'; -import { AssetIcon } from 'app/templates/AssetIcon'; -import InFiat from 'app/templates/InFiat'; -import { setTestID } from 'lib/analytics'; -import { toLocalFormat, T, t } from 'lib/i18n'; -import { AssetMetadataBase } from 'lib/metadata'; -import { useFocusOnElement } from 'lib/ui/hooks'; -import { PopperRenderProps } from 'lib/ui/Popper'; - -import { SwapFormInputProps, SwapFormTestIDs } from '../SwapFormInput.props'; - -interface Props extends PopperRenderProps, Pick { - amount?: BigNumber; - balance?: BigNumber; - searchString: string; - selectedAssetSlug?: string; - selectedAssetMetadata: AssetMetadataBase; - showTokenIdInput: boolean; - tokenId?: number; - amountInputDisabled?: boolean; - testIDs?: SwapFormTestIDs; - onTokenIdChange: (value?: number) => void; - onAmountChange: (amount?: BigNumber) => void; - onSearchChange: (e: ChangeEvent) => void; -} - -export const SwapFormInputHeader = forwardRef( - ( - { - amount, - balance, - label, - opened, - searchString, - selectedAssetSlug, - selectedAssetMetadata, - showTokenIdInput, - tokenId, - toggleOpened, - amountInputDisabled, - onTokenIdChange, - onAmountChange, - onSearchChange, - testIDs - }, - ref - ) => { - const amountFieldRef = useRef(null); - const searchInputRef = useFocusOnElement(opened); - const [isActive, setIsActive] = useState(false); - - const handleFocus = () => setIsActive(true); - const handleBlur = () => setIsActive(false); - - const handleAmountFieldFocus = (event: FocusEvent | FocusEvent) => { - event.preventDefault(); - setIsActive(true); - amountFieldRef.current?.focus({ preventScroll: true }); - }; - - const handleAmountChange = (newInputValue?: string) => { - const newValue = newInputValue ? new BigNumber(newInputValue) : undefined; - onAmountChange(newValue); - }; - - const handleTokenIdChange = (newValue?: string) => { - const newValueNum = newValue ? Number(newValue) : undefined; - onTokenIdChange(newValueNum); - }; - - return ( -
-
- {label} - - {selectedAssetSlug && ( - - - : - - {balance && ( - - - {balance} - - - )} - {selectedAssetMetadata.symbol} - - )} -
- -
-
-
- -
- -
-
- -
- - {showTokenIdInput && ( -
- -
- )} -
-
- -
-
- {selectedAssetSlug ? ( - <> - - - {selectedAssetMetadata.symbol} - - - ) : ( -
-
- -
-
- )} - - -
- -
-
- - - - {({ balance, symbol }) => ( -
- - {balance} - {symbol} -
- )} -
-
-
-
-
-
- ); - } -); diff --git a/src/app/templates/SwapForm/SwapFormInput/SwapFormTokenIdInput.hook.ts b/src/app/templates/SwapForm/SwapFormInput/SwapFormTokenIdInput.hook.ts deleted file mode 100644 index f854e1aa4..000000000 --- a/src/app/templates/SwapForm/SwapFormInput/SwapFormTokenIdInput.hook.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useEffect, useState } from 'react'; - -import { TezosToolkit } from '@taquito/taquito'; - -import { isFA2Token } from 'lib/assets'; -import { fromAssetSlug } from 'lib/assets/utils'; -import { useTezos } from 'lib/temple/front'; - -const getIsFa2AssetSlug = async (assetSlug: string | undefined, tezos: TezosToolkit) => { - if (assetSlug) { - const asset = await fromAssetSlug(tezos, assetSlug).catch(() => undefined); - - if (asset === 'tez') { - return false; - } else if (asset) { - return isFA2Token(asset); - } - } - - return false; -}; - -export const useSwapFormTokenIdInput = (assetSlug?: string) => { - const tezos = useTezos(); - const [showTokenIdInput, setShowTokenIdInput] = useState(false); - - useEffect(() => { - (async () => { - const isFa2AssetSlug = await getIsFa2AssetSlug(assetSlug, tezos); - - setShowTokenIdInput(isFa2AssetSlug); - })(); - }, [assetSlug, tezos]); - - return showTokenIdInput; -}; diff --git a/src/app/templates/TopUpInput/CurrenciesMenu/index.tsx b/src/app/templates/TopUpInput/CurrenciesMenu/index.tsx deleted file mode 100644 index d82ac5296..000000000 --- a/src/app/templates/TopUpInput/CurrenciesMenu/index.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, { FC, useEffect } from 'react'; - -import { List } from 'react-virtualized'; - -import DropdownWrapper from 'app/atoms/DropdownWrapper'; -import Spinner from 'app/atoms/Spinner/Spinner'; -import { useAppEnvStyle } from 'app/hooks/use-app-env-style.hook'; -import { AnalyticsEventCategory, TestIDProperty, useAnalytics } from 'lib/analytics'; -import { t } from 'lib/i18n'; - -import { CurrencyBase } from '../types'; -import { CurrencyOption } from './CurrencyOption'; - -interface Props extends TestIDProperty { - value: CurrencyBase; - options: CurrencyBase[]; - isLoading?: boolean; - opened: boolean; - fitIcons?: boolean | ((currency: CurrencyBase) => boolean); - emptyListPlaceholder?: string; - setOpened: (newValue: boolean) => void; - onChange?: (newValue: CurrencyBase) => void; -} - -const ROW_HEIGHT = 65; - -export const CurrenciesMenu: FC = ({ - value, - options, - isLoading = false, - opened, - fitIcons, - emptyListPlaceholder = t('tokenNotFound'), - testID, - setOpened, - onChange -}) => { - const { dropdownWidth } = useAppEnvStyle(); - - const { trackEvent } = useAnalytics(); - - useEffect(() => { - if (testID && opened) trackEvent(testID, AnalyticsEventCategory.DropdownOpened); - }, [opened, trackEvent]); - - const handleOptionClick = onChange - ? (newValue: CurrencyBase) => { - if (value.code !== newValue.code || value.network !== newValue.network) { - onChange(newValue); - } - setOpened(false); - } - : undefined; - - return ( - - {(options.length === 0 || isLoading) && ( -
- {isLoading ? ( - - ) : ( -

{emptyListPlaceholder}

- )} -
- )} - 2 ? 240 : options.length * ROW_HEIGHT} - rowCount={options.length} - rowHeight={ROW_HEIGHT} - rowRenderer={({ key, index, style }) => ( - - )} - /> -
- ); -}; diff --git a/src/app/templates/TopUpInput/TopUpInputHeader.tsx b/src/app/templates/TopUpInput/TopUpInputHeader.tsx deleted file mode 100644 index ffef961e1..000000000 --- a/src/app/templates/TopUpInput/TopUpInputHeader.tsx +++ /dev/null @@ -1,230 +0,0 @@ -import React, { ChangeEvent, forwardRef, FocusEvent, useEffect, useRef, useState } from 'react'; - -import classNames from 'clsx'; - -import AssetField from 'app/atoms/AssetField'; -import { ReactComponent as ChevronDownIcon } from 'app/icons/chevron-down.svg'; -import { ReactComponent as SearchIcon } from 'app/icons/search.svg'; -import { getBigErrorText, getSmallErrorText } from 'app/pages/Buy/utils/errorText.utils'; -import { emptyFn } from 'app/utils/function.utils'; -import { getAssetSymbolToDisplay } from 'lib/buy-with-credit-card/get-asset-symbol-to-display'; -import { toLocalFormat, T, t } from 'lib/i18n'; -import { PopperRenderProps } from 'lib/ui/Popper'; - -import { StaticCurrencyImage } from './StaticCurrencyImage'; -import { TopUpInputPropsBase } from './types'; -import { getProperNetworkFullName } from './utils'; - -interface Props extends PopperRenderProps, Omit { - fitIcons?: boolean; - searchString: string; - onSearchChange: (e: ChangeEvent) => void; -} - -export const TopUpInputHeader = forwardRef( - ( - { - currenciesList, - currency, - amount, - decimals = 2, - label, - readOnly, - opened, - searchString, - toggleOpened, - amountInputDisabled, - minAmount, - maxAmount, - isMinAmountError, - isMaxAmountError, - isInsufficientTezBalanceError, - isSearchable = false, - fitIcons, - onAmountChange = emptyFn, - onSearchChange - }, - ref - ) => { - const amountFieldRef = useRef(null); - const searchInputRef = useRef(null); - const prevOpenedRef = useRef(opened); - - const [isActive, setIsActive] = useState(false); - - const minAmountErrorClassName = getBigErrorText(isMinAmountError); - - useEffect(() => { - if (!prevOpenedRef.current && opened) { - searchInputRef.current?.focus(); - } - prevOpenedRef.current = opened; - }, [opened]); - - const handleFocus = () => setIsActive(true); - const handleBlur = () => setIsActive(false); - - const handleAmountFieldFocus = (event: FocusEvent | FocusEvent) => { - event.preventDefault(); - setIsActive(true); - amountFieldRef.current?.focus({ preventScroll: true }); - }; - - const handleAmountChange = (newInputValue?: string) => { - const newValue = newInputValue ? Number(newInputValue) : undefined; - onAmountChange(newValue); - }; - - const singleToken = currenciesList.length < 2; - - return ( -
-
- {label} - {minAmount && !opened && ( -

- {' ' + minAmount}{' '} - {currency.code} -

- )} -
-
- {isSearchable && ( -
-
- -
-
-
- -
-
-
- )} -
-
- -
- - {getAssetSymbolToDisplay(currency)} - - - {currency.network?.shortName ?? getProperNetworkFullName(currency)} - -
- - {singleToken ? ( - - ) : ( - - )} -
-
-
- -
-
-
-
- {maxAmount && !opened && ( - - )} -
- ); - } -); - -interface ErrorsComponentProps { - isInsufficientTezBalanceError?: boolean; - isMaxAmountError?: boolean; - maxAmount?: string; - coin: string; -} - -const ErrorsComponent: React.FC = ({ - isInsufficientTezBalanceError, - isMaxAmountError, - maxAmount, - coin -}) => ( -
-

- -

-

- -

-
-); - -const CurrencyText: React.FC = ({ className, coin, maxAmount }) => ( - <> - - {':'} - {maxAmount !== 'Infinity' ? maxAmount : '0'}{' '} - {coin} - -); diff --git a/src/app/templates/TopUpInput/index.tsx b/src/app/templates/TopUpInput/index.tsx index c14f03edc..83acbb389 100644 --- a/src/app/templates/TopUpInput/index.tsx +++ b/src/app/templates/TopUpInput/index.tsx @@ -8,7 +8,7 @@ import { getAssetSymbolToDisplay } from 'lib/buy-with-credit-card/get-asset-symb import { T, toLocalFormat } from 'lib/i18n'; import { InputGeneral } from '../InputGeneral/InputGeneral'; -import { DropdownSize, SelectGeneral } from '../InputGeneral/SelectGeneral'; +import { SelectGeneral } from '../InputGeneral/SelectGeneral'; import { CurrencyOption } from './CurrenciesMenu/CurrencyOption'; import { StaticCurrencyImage } from './StaticCurrencyImage'; import { TopUpInputPropsGeneric, CurrencyBase, TopUpInputPropsBase } from './types'; @@ -69,7 +69,10 @@ export const TopUpInput = (_props: TopUpInputPropsGeneri } mainContent={ - dropdownSize={DropdownSize.Large} + testIds={{ + dropdownTestId: testID + }} + dropdownButtonClassName="pl-4 pr-3 py-5" DropdownFaceContent={ } @@ -105,6 +108,7 @@ export const TopUpInput = (_props: TopUpInputPropsGeneri
} optionsProps={{ + isLoading: isCurrenciesLoading, options: filteredCurrencies, noItemsText: emptyListPlaceholder, renderOptionContent: currCurrency => diff --git a/src/app/utils/function.utils.ts b/src/app/utils/function.utils.ts deleted file mode 100644 index d1dbff532..000000000 --- a/src/app/utils/function.utils.ts +++ /dev/null @@ -1 +0,0 @@ -export const emptyFn = () => void 0; diff --git a/src/lib/fiat-currency/core.ts b/src/lib/fiat-currency/core.ts index 2b922d63d..e220199e0 100644 --- a/src/lib/fiat-currency/core.ts +++ b/src/lib/fiat-currency/core.ts @@ -83,5 +83,3 @@ export const fetchFiatToTezosRates = () => return mappedRates; }); - -export const getFiatCurrencyKey = ({ name }: FiatCurrencyOption) => name; diff --git a/src/lib/fiat-currency/index.ts b/src/lib/fiat-currency/index.ts index 1b0887a27..915986a8f 100644 --- a/src/lib/fiat-currency/index.ts +++ b/src/lib/fiat-currency/index.ts @@ -1,7 +1,6 @@ export type { FiatCurrencyOption } from './types'; export { FIAT_CURRENCIES } from './consts'; export { - getFiatCurrencyKey, useFiatCurrency, useFiatToUsdRate, useAssetUSDPrice, diff --git a/src/lib/ui/Popper.tsx b/src/lib/ui/Popper.tsx index 1bef463b1..4d32c9c5d 100644 --- a/src/lib/ui/Popper.tsx +++ b/src/lib/ui/Popper.tsx @@ -23,9 +23,9 @@ export interface PopperRenderProps { setOpened: Dispatch>; toggleOpened: () => void; } -export type RenderProp

= (props: P) => ReactElement; +type RenderProp

= (props: P) => ReactElement; -export type PopperProps = Partial & { +type PopperProps = Partial & { popup: RenderProp; children: RenderProp< PopperRenderProps & { diff --git a/yarn.lock b/yarn.lock index d8675917a..e850550f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2641,6 +2641,11 @@ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== +"@types/uuid@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.1.tgz#98586dc36aee8dacc98cc396dbca8d0429647aa6" + integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== + "@types/w3c-web-hid@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@types/w3c-web-hid/-/w3c-web-hid-1.0.3.tgz#e08587a7d737f8654ea6bc0a88689ce5d3ce2d19" @@ -10420,6 +10425,11 @@ utila@~0.4: resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" From 9c548b102ab2e6b3fec9a4ad2526f0668755189f Mon Sep 17 00:00:00 2001 From: herkoss Date: Tue, 13 Jun 2023 16:37:17 +0300 Subject: [PATCH 07/21] TW-565 Fixed comments --- src/app/pages/Home/Home.tsx | 5 +---- .../templates/InputGeneral/SelectGeneral.tsx | 11 ++++------- .../Components/FiatCurrencySelect.tsx | 18 ++++++++++-------- .../SwapFormInput/AssetsMenu/AssetOption.tsx | 3 +-- .../SwapForm/SwapFormInput/SwapFormInput.tsx | 3 +-- .../CurrenciesMenu/CurrencyOption.tsx | 6 ++++-- src/app/templates/TopUpInput/index.tsx | 9 +++++---- tailwind.config.js | 5 ++++- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/app/pages/Home/Home.tsx b/src/app/pages/Home/Home.tsx index d592c7c69..22844a511 100644 --- a/src/app/pages/Home/Home.tsx +++ b/src/app/pages/Home/Home.tsx @@ -188,10 +188,7 @@ const ActionButton: FC = ({ >

- + {label} diff --git a/src/app/templates/InputGeneral/SelectGeneral.tsx b/src/app/templates/InputGeneral/SelectGeneral.tsx index b31eeb936..fb2823287 100644 --- a/src/app/templates/InputGeneral/SelectGeneral.tsx +++ b/src/app/templates/InputGeneral/SelectGeneral.tsx @@ -65,10 +65,7 @@ export const SelectGeneral = ({ {opened ? ( ) : ( -
+
{Input}
@@ -134,7 +131,7 @@ const SelectOptions = ({ {(options.length === 0 || isLoading) && (
{isLoading ? ( - + ) : (

{noItemsText} @@ -146,7 +143,7 @@ const SelectOptions = ({

    {options.map(option => (
  • -
  • diff --git a/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx b/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx index 26773e11e..858d44dc2 100644 --- a/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx +++ b/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx @@ -3,7 +3,7 @@ import React, { useCallback, FC, useState, useMemo } from 'react'; import classNames from 'clsx'; import { InputGeneral } from 'app/templates/InputGeneral/InputGeneral'; -import { SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; +import { GeneralOption, SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; import { AnalyticsEventCategory, AnalyticsEventEnum, setTestID, useAnalytics } from 'lib/analytics'; import { FIAT_CURRENCIES, FiatCurrencyOption, useFiatCurrency } from 'lib/fiat-currency'; import { T } from 'lib/i18n'; @@ -27,7 +27,7 @@ const FiatCurrencySelect: FC = () => { const [searchValue, setSearchValue] = useState(''); - const options = useMemo( + const options = useMemo>>( () => searchAndFilterItems( FIAT_CURRENCIES, @@ -38,14 +38,16 @@ const FiatCurrencySelect: FC = () => { ], null, 0.25 - ), + ).map(value => ({ value, disabled: false })), [searchValue] ); const handleFiatCurrencyChange = useCallback( - (fiatOption: FiatCurrencyOption) => { - trackEvent(AnalyticsEventEnum.FiatCurrencyChanged, AnalyticsEventCategory.ButtonPress, { name: fiatOption.name }); - setSelectedFiatCurrency(fiatOption); + (fiatOption: GeneralOption) => { + trackEvent(AnalyticsEventEnum.FiatCurrencyChanged, AnalyticsEventCategory.ButtonPress, { + name: fiatOption.value.name + }); + setSelectedFiatCurrency(fiatOption.value); }, [setSelectedFiatCurrency, trackEvent] ); @@ -66,7 +68,7 @@ const FiatCurrencySelect: FC = () => { options, noItemsText: 'No items', renderOptionContent: option => - renderOptionContent(option, option.fullname === selectedFiatCurrency.fullname), + renderOptionContent(option.value, option.value.fullname === selectedFiatCurrency.fullname), onOptionChange: handleFiatCurrencyChange }} searchProps={{ @@ -106,7 +108,7 @@ const FiatCurrencyIcon: FC = ({ option: { symbol const FiatCurrencyFieldContent: FC = ({ option }) => { return ( -
    +
    {option.name} diff --git a/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetOption.tsx b/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetOption.tsx index 53f20f294..2f83f10ee 100644 --- a/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetOption.tsx +++ b/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetOption.tsx @@ -24,10 +24,9 @@ export const AssetOption: FC = ({ assetSlug, selected }) => { return (
    diff --git a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx index 2177f7338..7aede7f5d 100644 --- a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx +++ b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx @@ -222,10 +222,9 @@ const SwapInput: FC = ({ return (
    > { export const CurrencyOption: FC = ({ currency, isSelected, fitIcons }) => (
    ( ); @@ -45,7 +47,7 @@ export const TopUpInput = (_props: TopUpInputPropsGeneri const fitIconsValue = typeof fitIcons === 'function' ? fitIcons(currency) : Boolean(fitIcons); const { filteredCurrencies, searchValue, setSearchValue } = useFilteredCurrencies(currenciesList); - const singleToken = currenciesList.length < 2; + const singleToken = currenciesList.length < TWO_TOKENS_LENGTH; const minAmountErrorClassName = getBigErrorText(isMinAmountError); const handleAmountChange = (newInputValue?: string) => { @@ -79,10 +81,9 @@ export const TopUpInput = (_props: TopUpInputPropsGeneri Input={
    = ({ singleToken, currency, fi {getAssetSymbolToDisplay(currency)} - + {currency.network?.shortName ?? getProperNetworkFullName(currency)}
    diff --git a/tailwind.config.js b/tailwind.config.js index 65bcc8971..1ce776551 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -111,7 +111,8 @@ module.exports = { 700: '#2b6cb0', 750: '#4a5568', 800: '#2c5282', - 900: '#2a4365' + 900: '#2a4365', + 910: '#212e36' }, indigo: { 100: '#ebf4ff', @@ -306,6 +307,7 @@ module.exports = { }; })(), fontSize: { + xxs: '0.6875rem', xs: '0.75rem', sm: '0.875rem', base: '1rem', @@ -333,6 +335,7 @@ module.exports = { auto: 'auto', ...theme('spacing'), 12: '3rem', + 18: '4.5rem', full: '100%', screen: '100vh' }), From 6054e32b1415be9b72a7cfd56b1bdb10071eb36e Mon Sep 17 00:00:00 2001 From: herkoss Date: Tue, 13 Jun 2023 16:38:06 +0300 Subject: [PATCH 08/21] TW-565 Fixed errors --- .../Components/FiatCurrencySelect.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx b/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx index 858d44dc2..568691096 100644 --- a/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx +++ b/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx @@ -3,7 +3,7 @@ import React, { useCallback, FC, useState, useMemo } from 'react'; import classNames from 'clsx'; import { InputGeneral } from 'app/templates/InputGeneral/InputGeneral'; -import { GeneralOption, SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; +import { SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; import { AnalyticsEventCategory, AnalyticsEventEnum, setTestID, useAnalytics } from 'lib/analytics'; import { FIAT_CURRENCIES, FiatCurrencyOption, useFiatCurrency } from 'lib/fiat-currency'; import { T } from 'lib/i18n'; @@ -27,7 +27,7 @@ const FiatCurrencySelect: FC = () => { const [searchValue, setSearchValue] = useState(''); - const options = useMemo>>( + const options = useMemo>( () => searchAndFilterItems( FIAT_CURRENCIES, @@ -38,16 +38,16 @@ const FiatCurrencySelect: FC = () => { ], null, 0.25 - ).map(value => ({ value, disabled: false })), + ), [searchValue] ); const handleFiatCurrencyChange = useCallback( - (fiatOption: GeneralOption) => { + (fiatOption: FiatCurrencyOption) => { trackEvent(AnalyticsEventEnum.FiatCurrencyChanged, AnalyticsEventCategory.ButtonPress, { - name: fiatOption.value.name + name: fiatOption.name }); - setSelectedFiatCurrency(fiatOption.value); + setSelectedFiatCurrency(fiatOption); }, [setSelectedFiatCurrency, trackEvent] ); @@ -68,7 +68,7 @@ const FiatCurrencySelect: FC = () => { options, noItemsText: 'No items', renderOptionContent: option => - renderOptionContent(option.value, option.value.fullname === selectedFiatCurrency.fullname), + renderOptionContent(option, option.fullname === selectedFiatCurrency.fullname), onOptionChange: handleFiatCurrencyChange }} searchProps={{ From d6315ecb3db4a6041ace8748407ccfff1cf0d29d Mon Sep 17 00:00:00 2001 From: herkoss Date: Fri, 23 Jun 2023 13:42:44 +0300 Subject: [PATCH 09/21] TW-565 Finished select --- src/app/templates/AssetSelect/AssetSelect.tsx | 77 ++++++---- .../DropdownSelect.tsx} | 14 +- src/app/templates/IconifiedSelect/Field.tsx | 145 ------------------ src/app/templates/IconifiedSelect/Menu.tsx | 123 --------------- src/app/templates/IconifiedSelect/index.tsx | 102 ------------ src/app/templates/IconifiedSelect/types.ts | 33 ---- .../InputContainer.tsx} | 6 +- .../templates/PaymentProviderInput/index.tsx | 42 +++-- .../Components/BlockExplorerSelect.tsx | 67 ++++---- .../Components/FiatCurrencySelect.tsx | 55 +++---- .../Components/LocaleSelect.tsx | 54 +++---- src/app/templates/SettingsGeneral/index.tsx | 6 +- .../SwapForm/SwapFormInput/SwapFormInput.tsx | 82 +++++----- src/app/templates/TopUpInput/index.tsx | 114 +++++++------- src/lib/ui/hooks/index.ts | 2 - src/lib/ui/hooks/useFocusOnElement.ts | 15 -- tailwind.config.js | 1 + 17 files changed, 258 insertions(+), 680 deletions(-) rename src/app/templates/{InputGeneral/SelectGeneral.tsx => DropdownSelect/DropdownSelect.tsx} (93%) delete mode 100644 src/app/templates/IconifiedSelect/Field.tsx delete mode 100644 src/app/templates/IconifiedSelect/Menu.tsx delete mode 100644 src/app/templates/IconifiedSelect/index.tsx delete mode 100644 src/app/templates/IconifiedSelect/types.ts rename src/app/templates/{InputGeneral/InputGeneral.tsx => InputContainer/InputContainer.tsx} (70%) delete mode 100644 src/lib/ui/hooks/useFocusOnElement.ts diff --git a/src/app/templates/AssetSelect/AssetSelect.tsx b/src/app/templates/AssetSelect/AssetSelect.tsx index e217bf09d..6440b1693 100644 --- a/src/app/templates/AssetSelect/AssetSelect.tsx +++ b/src/app/templates/AssetSelect/AssetSelect.tsx @@ -1,10 +1,12 @@ -import React, { FC, useCallback } from 'react'; +import React, { FC, useCallback, useMemo, useState } from 'react'; + +import classNames from 'clsx'; +import { useDebounce } from 'use-debounce'; import Money from 'app/atoms/Money'; import { useTokensMetadataSelector } from 'app/store/tokens-metadata/selectors'; import { AssetIcon } from 'app/templates/AssetIcon'; import Balance from 'app/templates/Balance'; -import IconifiedSelect, { IconifiedSelectOptionRenderProps } from 'app/templates/IconifiedSelect'; import InFiat from 'app/templates/InFiat'; import { setTestID, setAnotherSelector } from 'lib/analytics'; import { T, t } from 'lib/i18n'; @@ -13,6 +15,8 @@ import { useAccount } from 'lib/temple/front'; import { searchAssetsWithNoMeta } from 'lib/temple/front/assets'; import { AssetItemContent } from '../AssetItemContent'; +import { DropdownSelect } from '../DropdownSelect/DropdownSelect'; +import { InputContainer } from '../InputContainer/InputContainer'; import { SendFormSelectors } from '../SendForm/selectors'; import { IAsset } from './interfaces'; import { getSlug } from './utils'; @@ -28,13 +32,25 @@ interface AssetSelectProps { }; } +const renderOptionContent = (asset: IAsset, selected: boolean) => ( + +); + const AssetSelect: FC = ({ value, assets, onChange, className, testIDs }) => { const allTokensMetadata = useTokensMetadataSelector(); + const [searchString, setSearchString] = useState(''); + + const [searchStringDebounced] = useDebounce(searchString, 300); + const searchItems = useCallback( (searchString: string) => searchAssetsWithNoMeta(searchString, assets, allTokensMetadata, getSlug), [assets, allTokensMetadata] ); + const searchedOptions = useMemo( + () => (searchStringDebounced ? searchItems(searchStringDebounced) : assets), + [searchItems, searchStringDebounced, assets] + ); const handleChange = useCallback( (asset: IAsset) => { @@ -44,31 +60,31 @@ const AssetSelect: FC = ({ value, assets, onChange, className, ); return ( - + }> + } + searchProps={{ + searchValue: searchStringDebounced, + onSearchChange: event => setSearchString(event.target.value) + }} + testIds={{ searchInputTestId: testIDs?.searchInput }} + dropdownButtonClassName="p-2 h-18" + optionsProps={{ + options: searchedOptions, + noItemsText: t('noAssetsFound'), + getKey: option => getSlug(option), + onOptionChange: handleChange, + renderOptionContent: asset => renderOptionContent(asset, JSON.stringify(asset) === JSON.stringify(value)) + }} + /> + ); }; export default AssetSelect; const AssetSelectTitle: FC = () => ( -

    +

    @@ -79,15 +95,13 @@ const AssetSelectTitle: FC = () => (

    ); -type AssetSelectOptionRenderProps = IconifiedSelectOptionRenderProps; - -const AssetFieldContent: FC = ({ option }) => { +const AssetFieldContent: FC<{ asset: IAsset }> = ({ asset }) => { const account = useAccount(); - const assetSlug = getSlug(option); + const assetSlug = getSlug(asset); const metadata = useAssetMetadata(assetSlug); return ( - <> +
    @@ -112,16 +126,19 @@ const AssetFieldContent: FC = ({ option }) => {
    )} - +
    ); }; -const AssetOptionContent: FC = ({ option }) => { - const slug = getSlug(option); +const AssetOptionContent: FC<{ asset: IAsset; selected: boolean }> = ({ asset, selected }) => { + const slug = getSlug(asset); return (
    diff --git a/src/app/templates/InputGeneral/SelectGeneral.tsx b/src/app/templates/DropdownSelect/DropdownSelect.tsx similarity index 93% rename from src/app/templates/InputGeneral/SelectGeneral.tsx rename to src/app/templates/DropdownSelect/DropdownSelect.tsx index fb2823287..88641d893 100644 --- a/src/app/templates/InputGeneral/SelectGeneral.tsx +++ b/src/app/templates/DropdownSelect/DropdownSelect.tsx @@ -2,7 +2,6 @@ import React, { ChangeEventHandler, ReactNode, FC, Dispatch, SetStateAction } fr import { isDefined } from '@rnw-community/shared'; import classNames from 'clsx'; -import { v4 } from 'uuid'; import AssetField from 'app/atoms/AssetField'; import DropdownWrapper from 'app/atoms/DropdownWrapper'; @@ -27,7 +26,7 @@ interface Props { }; } -export const SelectGeneral = ({ +export const DropdownSelect = ({ Input, searchProps, optionsProps, @@ -65,7 +64,10 @@ export const SelectGeneral = ({ {opened ? ( ) : ( -
    +
    @@ -177,6 +182,7 @@ const SelectSearch: FC = ({ 'w-full flex items-center transition ease-in-out duration-200 w-full border rounded-md border-orange-500 bg-gray-100', className )} + style={{ maxHeight: '4.5rem' }} >
    diff --git a/src/app/templates/IconifiedSelect/Field.tsx b/src/app/templates/IconifiedSelect/Field.tsx deleted file mode 100644 index dcdf76f7c..000000000 --- a/src/app/templates/IconifiedSelect/Field.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import React, { forwardRef, HTMLAttributes, PropsWithChildren } from 'react'; - -import classNames from 'clsx'; - -import { ReactComponent as ChevronDownIcon } from 'app/icons/chevron-down.svg'; -import { ReactComponent as SearchIcon } from 'app/icons/search.svg'; -import { setTestID, TestIDProps } from 'lib/analytics'; -import { useFocusOnElement } from 'lib/ui/hooks'; - -import { IconifiedSelectProps, IconifiedSelectPropsBase } from './types'; - -type FieldBaseProps = HTMLAttributes & - Pick, 'value'> & { - Content: IconifiedSelectProps['FieldContent']; - dropdown?: boolean; - }; - -interface SearchProps { - value?: string; - placeholder?: string; - onChange: (value: string) => void; - inputTestID?: string; -} - -interface IconifiedSelectFieldProps extends FieldBaseProps, TestIDProps { - opened: boolean; - search?: SearchProps; - BeforeContent: IconifiedSelectProps['BeforeContent']; -} - -export const IconifiedSelectField = forwardRef((props, ref) => { - const { search, ...rest } = props; - - if (search) return ; - - return ; -}); - -interface FieldWithNoSearchProps extends FieldBaseProps, TestIDProps { - opened: boolean; - BeforeContent: IconifiedSelectProps['BeforeContent']; -} - -const FieldWithNoSearch = forwardRef((props, ref) => { - const { opened, className, style, testID, BeforeContent, ...rest } = props; - - return ( - - - - ); -}); - -interface FieldWithSearchProps extends FieldWithNoSearchProps { - search: SearchProps; -} - -const FieldWithSearch = forwardRef((props, ref) => { - const { opened, search, className, style, testID, BeforeContent, ...rest } = props; - - const searchInputRef = useFocusOnElement(opened); - - return ( - - - ); -}); - -interface FieldContainerProps { - active?: boolean; - opened?: boolean; - className?: string; - style?: React.CSSProperties; - BeforeContent: IconifiedSelectProps['BeforeContent']; -} - -export const FieldContainer = forwardRef>( - ({ opened = false, className, style, BeforeContent, children }, ref) => ( - <> - {BeforeContent && } -
    } - className={classNames( - 'w-full flex items-stretch transition ease-in-out duration-200 w-full border rounded-md', - opened ? 'border-orange-500 bg-gray-100' : 'border-gray-300', - className - )} - style={style} - > - {children} -
    - - ) -); - -interface FieldInnerComponentProps extends FieldBaseProps, TestIDProps { - hidden?: boolean; -} - -export const FieldInnerComponent = forwardRef( - ({ Content, value, hidden, dropdown, className, testID, ...rest }, ref) => ( - - ) -); diff --git a/src/app/templates/IconifiedSelect/Menu.tsx b/src/app/templates/IconifiedSelect/Menu.tsx deleted file mode 100644 index ba2ca2ea2..000000000 --- a/src/app/templates/IconifiedSelect/Menu.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import React, { useCallback, useEffect } from 'react'; - -import classNames from 'clsx'; - -import DropdownWrapper from 'app/atoms/DropdownWrapper'; -import { ReactComponent as SearchIcon } from 'app/icons/search.svg'; -import { AnalyticsEventCategory, TestIDProperty, useAnalytics } from 'lib/analytics'; -import { PopperRenderProps } from 'lib/ui/Popper'; - -import { IconifiedSelectProps, IconifiedSelectPropsBase } from './types'; - -interface Props extends PopperRenderProps, TestIDProperty, IconifiedSelectPropsBase { - search?: { - value?: string; - }; -} - -export const IconifiedSelectMenu = (props: Props) => { - const { - opened, - options, - value, - padded, - noItemsText, - search, - testID, - isDisabled, - setOpened, - onChange, - getKey, - OptionContent - } = props; - - const { trackEvent } = useAnalytics(); - - useEffect(() => { - if (testID && opened) trackEvent(testID, AnalyticsEventCategory.DropdownOpened); - }, [opened, trackEvent]); - - const withSearch = Boolean(search); - - const handleOptionClick = useCallback( - (newValue: T) => { - if (getKey(newValue) !== getKey(value)) { - onChange?.(newValue); - } - setOpened(false); - }, - [onChange, setOpened, value, getKey] - ); - - return ( - - {options.length ? ( - options.map(option => ( - - )) - ) : ( -

    - {search?.value ? : null} - - {noItemsText} -

    - )} -
    - ); -}; - -type IconifiedSelectOptionProps = Pick, 'OptionContent' | 'value'> & { - disabled?: boolean; - value: T; - selected: boolean; - padded?: boolean; - withSearch: boolean; - onClick?: IconifiedSelectProps['onChange']; -}; - -const IconifiedSelectOption = (props: IconifiedSelectOptionProps) => { - const { disabled, value, selected, padded, withSearch, onClick, OptionContent } = props; - - const handleClick = useCallback(() => { - onClick?.(value); - }, [onClick, value]); - - return ( - - ); -}; diff --git a/src/app/templates/IconifiedSelect/index.tsx b/src/app/templates/IconifiedSelect/index.tsx deleted file mode 100644 index 5c096d7aa..000000000 --- a/src/app/templates/IconifiedSelect/index.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import React, { useMemo, useState } from 'react'; - -import { useDebounce } from 'use-debounce'; - -import Popper from 'lib/ui/Popper'; -import { sameWidthModifiers } from 'lib/ui/same-width-modifiers'; - -import { FieldContainer, FieldInnerComponent, IconifiedSelectField } from './Field'; -import { IconifiedSelectMenu } from './Menu'; -import { IconifiedSelectProps } from './types'; - -export type { IconifiedSelectOptionRenderProps } from './types'; - -const IconifiedSelect = ({ - FieldContent, - OptionContent, - BeforeContent, - getKey, - isDisabled, - onChange, - options, - value, - noItemsText, - className, - padded, - fieldStyle, - search, - testID -}: IconifiedSelectProps) => { - const [searchString, setSearchString] = useState(); - - const [searchStringDebounced] = useDebounce(searchString, 300); - - const searchedOptions = useMemo( - () => (search && searchStringDebounced ? search.filterItems(searchStringDebounced) : options), - [search?.filterItems, searchStringDebounced, options] - ); - - if (options.length < 2) - return ( -
    - - - -
    - ); - - return ( -
    - ( - - )} - > - {({ ref, opened, toggleOpened }) => ( - } - Content={FieldContent} - BeforeContent={BeforeContent} - opened={opened} - value={value} - dropdown - style={fieldStyle} - onClick={toggleOpened} - testID={testID} - search={ - search && { - value: searchString, - placeholder: search?.placeholder, - onChange: setSearchString, - inputTestID: search.inputTestID - } - } - /> - )} - -
    - ); -}; - -export default IconifiedSelect; diff --git a/src/app/templates/IconifiedSelect/types.ts b/src/app/templates/IconifiedSelect/types.ts deleted file mode 100644 index 2cc577c68..000000000 --- a/src/app/templates/IconifiedSelect/types.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ComponentType } from 'react'; - -import { TestIDProperty } from 'lib/analytics'; - -export type IconifiedSelectOptionRenderProps = { - option: T; - index?: number; -}; - -type OptionContentComponent = ComponentType>; - -export interface IconifiedSelectPropsBase { - OptionContent: OptionContentComponent; - getKey: (option: T) => string | number | undefined; - isDisabled?: (option: T) => boolean; - onChange?: (a: T) => void; - options: T[]; - value: T; - noItemsText: string; - padded?: boolean; -} - -export interface IconifiedSelectProps extends IconifiedSelectPropsBase, TestIDProperty { - FieldContent: OptionContentComponent; - BeforeContent?: ComponentType<{ opened: boolean }>; - className?: string; - fieldStyle?: React.CSSProperties; - search?: { - placeholder?: string; - filterItems(searchString: string): T[]; - inputTestID?: string; - }; -} diff --git a/src/app/templates/InputGeneral/InputGeneral.tsx b/src/app/templates/InputContainer/InputContainer.tsx similarity index 70% rename from src/app/templates/InputGeneral/InputGeneral.tsx rename to src/app/templates/InputContainer/InputContainer.tsx index b03a0e7b5..8e6827e81 100644 --- a/src/app/templates/InputGeneral/InputGeneral.tsx +++ b/src/app/templates/InputContainer/InputContainer.tsx @@ -5,15 +5,15 @@ import classNames from 'clsx'; interface Props { className?: string; header?: ReactNode; - mainContent: ReactNode; + children: ReactNode; footer?: ReactNode; } -export const InputGeneral: FC = ({ className, header, mainContent, footer }) => { +export const InputContainer: FC = ({ className, header, children, footer }) => { return (
    {header &&
    {header}
    } -
    {mainContent}
    +
    {children}
    {footer &&
    {footer}
    }
    ); diff --git a/src/app/templates/PaymentProviderInput/index.tsx b/src/app/templates/PaymentProviderInput/index.tsx index e57fa625a..4a0011911 100644 --- a/src/app/templates/PaymentProviderInput/index.tsx +++ b/src/app/templates/PaymentProviderInput/index.tsx @@ -8,8 +8,8 @@ import { PaymentProviderInterface } from 'lib/buy-with-credit-card/topup.interfa import { T } from 'lib/i18n'; import { isTruthy } from 'lib/utils'; -import { InputGeneral } from '../InputGeneral/InputGeneral'; -import { SelectGeneral } from '../InputGeneral/SelectGeneral'; +import { DropdownSelect } from '../DropdownSelect/DropdownSelect'; +import { InputContainer } from '../InputContainer/InputContainer'; import { TopUpProviderIcon } from '../TopUpProviderIcon'; import { MoneyRange } from './MoneyRange'; import { PaymentProviderOption } from './PaymentProvidersMenu/PaymentProviderOption'; @@ -32,26 +32,24 @@ export const PaymentProviderInput: FC = ({ const [searchValue, setSearchValue] = useState(''); return (
    - } - searchProps={{ searchValue, onSearchChange: event => setSearchValue(event?.target.value) }} - optionsProps={{ - options, - isLoading, - noItemsText: 'No Items', - renderOptionContent, - onOptionChange: onChange - }} - /> - } - footer={isTruthy(error) && {error}} - /> + {error}}> + } + searchProps={{ searchValue, onSearchChange: event => setSearchValue(event?.target.value) }} + optionsProps={{ + options, + isLoading, + noItemsText: 'No Items', + getKey: option => option.id, + renderOptionContent, + onOptionChange: onChange + }} + /> +
    ); }; diff --git a/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx b/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx index 5cfe3b388..6d4352885 100644 --- a/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx +++ b/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx @@ -4,26 +4,21 @@ import classNames from 'clsx'; import browser from 'webextension-polyfill'; import Flag from 'app/atoms/Flag'; -import { InputGeneral } from 'app/templates/InputGeneral/InputGeneral'; -import { SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; +import { DropdownSelect } from 'app/templates/DropdownSelect/DropdownSelect'; +import { InputContainer } from 'app/templates/InputContainer/InputContainer'; import { setTestID } from 'lib/analytics'; import { T } from 'lib/i18n'; import { BlockExplorer, useChainId, BLOCK_EXPLORERS, useBlockExplorer } from 'lib/temple/front'; import { isKnownChainId } from 'lib/temple/types'; import { searchAndFilterItems } from 'lib/utils/search-items'; -import { IconifiedSelectOptionRenderProps } from '../../IconifiedSelect'; import { SettingsGeneralSelectors } from '../selectors'; -type BlockExplorerSelectProps = { - className?: string; -}; - const renderOptionContent = (option: BlockExplorer, isSelected: boolean) => ( ); -const BlockExplorerSelect: FC = () => { +const BlockExplorerSelect = () => { const { explorer, setExplorerId } = useBlockExplorer(); const chainId = useChainId(true)!; const [searchValue, setSearchValue] = useState(''); @@ -47,30 +42,28 @@ const BlockExplorerSelect: FC = () => { return (
    - } - mainContent={ - } - optionsProps={{ - options, - noItemsText: 'No items', - renderOptionContent: option => - renderOptionContent(option, JSON.stringify(option) === JSON.stringify(explorer)), - onOptionChange: handleBlockExplorerChange - }} - searchProps={{ - searchValue, - onSearchChange: event => setSearchValue(event?.target.value) - }} - /> - } - /> + }> + } + optionsProps={{ + options, + noItemsText: 'No items', + getKey: option => option.id, + renderOptionContent: option => + renderOptionContent(option, JSON.stringify(option) === JSON.stringify(explorer)), + onOptionChange: handleBlockExplorerChange + }} + searchProps={{ + searchValue, + onSearchChange: event => setSearchValue(event?.target.value) + }} + /> +
    ); }; @@ -85,16 +78,16 @@ const BlockExplorerTitle: FC = () => (

); -const BlockExplorerIcon: FC> = ({ option: { id, name } }) => ( +const BlockExplorerIcon: FC> = ({ id, name }) => ( ); -const BlockExplorerFieldContent: FC> = ({ option }) => { +const BlockExplorerFieldContent: FC = ({ id, name }) => { return (
- + - {option.name} + {name}
); }; @@ -112,7 +105,7 @@ const BlockExplorerOptionContent: FC = ({ optio isSelected ? 'bg-gray-200' : 'hover:bg-gray-100' )} > - +
{option.name} diff --git a/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx b/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx index 568691096..1bfc5c6ec 100644 --- a/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx +++ b/src/app/templates/SettingsGeneral/Components/FiatCurrencySelect.tsx @@ -2,8 +2,8 @@ import React, { useCallback, FC, useState, useMemo } from 'react'; import classNames from 'clsx'; -import { InputGeneral } from 'app/templates/InputGeneral/InputGeneral'; -import { SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; +import { DropdownSelect } from 'app/templates/DropdownSelect/DropdownSelect'; +import { InputContainer } from 'app/templates/InputContainer/InputContainer'; import { AnalyticsEventCategory, AnalyticsEventEnum, setTestID, useAnalytics } from 'lib/analytics'; import { FIAT_CURRENCIES, FiatCurrencyOption, useFiatCurrency } from 'lib/fiat-currency'; import { T } from 'lib/i18n'; @@ -11,15 +11,11 @@ import { searchAndFilterItems } from 'lib/utils/search-items'; import { SettingsGeneralSelectors } from '../selectors'; -type FiatCurrencySelectProps = { - className?: string; -}; - const renderOptionContent = (option: FiatCurrencyOption, isSelected: boolean) => ( ); -const FiatCurrencySelect: FC = () => { +const FiatCurrencySelect: FC = () => { const { trackEvent } = useAnalytics(); const { selectedFiatCurrency, setSelectedFiatCurrency } = useFiatCurrency(); @@ -53,32 +49,27 @@ const FiatCurrencySelect: FC = () => { ); return ( -
- } - mainContent={ - } - optionsProps={{ - options, - noItemsText: 'No items', - renderOptionContent: option => - renderOptionContent(option, option.fullname === selectedFiatCurrency.fullname), - onOptionChange: handleFiatCurrencyChange - }} - searchProps={{ - searchValue, - onSearchChange: event => setSearchValue(event.target.value) - }} - /> - } + }> + } + optionsProps={{ + options, + noItemsText: 'No items', + getKey: option => option.fullname, + renderOptionContent: option => renderOptionContent(option, option.fullname === selectedFiatCurrency.fullname), + onOptionChange: handleFiatCurrencyChange + }} + searchProps={{ + searchValue, + onSearchChange: event => setSearchValue(event.target.value) + }} /> -
+ ); }; diff --git a/src/app/templates/SettingsGeneral/Components/LocaleSelect.tsx b/src/app/templates/SettingsGeneral/Components/LocaleSelect.tsx index 05027f55b..75778c8b4 100644 --- a/src/app/templates/SettingsGeneral/Components/LocaleSelect.tsx +++ b/src/app/templates/SettingsGeneral/Components/LocaleSelect.tsx @@ -4,18 +4,14 @@ import classNames from 'clsx'; import browser from 'webextension-polyfill'; import Flag from 'app/atoms/Flag'; -import { InputGeneral } from 'app/templates/InputGeneral/InputGeneral'; -import { SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; +import { DropdownSelect } from 'app/templates/DropdownSelect/DropdownSelect'; +import { InputContainer } from 'app/templates/InputContainer/InputContainer'; import { AnalyticsEventCategory, AnalyticsEventEnum, setTestID, useAnalytics } from 'lib/analytics'; import { getCurrentLocale, updateLocale, T } from 'lib/i18n'; import { searchAndFilterItems } from 'lib/utils/search-items'; import { SettingsGeneralSelectors } from '../selectors'; -type LocaleSelectProps = { - className?: string; -}; - type LocaleOption = { code: string; disabled: boolean; @@ -99,7 +95,7 @@ const LOCALE_OPTIONS: LocaleOption[] = [ } ]; -const LocaleSelect: FC = () => { +const LocaleSelect: FC = () => { const selectedLocale = getCurrentLocale(); const { trackEvent } = useAnalytics(); const [searchValue, setSearchValue] = useState(''); @@ -128,31 +124,27 @@ const LocaleSelect: FC = () => { ); return ( -
- } - mainContent={ - } - optionsProps={{ - options, - noItemsText: 'No items', - renderOptionContent: option => renderOptionContent(option, option.code === value.code), - onOptionChange: handleLocaleChange - }} - searchProps={{ - searchValue, - onSearchChange: event => setSearchValue(event?.target.value) - }} - /> - } + }> + } + optionsProps={{ + options, + noItemsText: 'No items', + getKey: option => option.code, + renderOptionContent: option => renderOptionContent(option, option.code === value.code), + onOptionChange: handleLocaleChange + }} + searchProps={{ + searchValue, + onSearchChange: event => setSearchValue(event?.target.value) + }} /> -
+ ); }; diff --git a/src/app/templates/SettingsGeneral/index.tsx b/src/app/templates/SettingsGeneral/index.tsx index ed6db63c3..9406cf021 100644 --- a/src/app/templates/SettingsGeneral/index.tsx +++ b/src/app/templates/SettingsGeneral/index.tsx @@ -14,11 +14,11 @@ import { PartnersPromotionSettings } from './Components/partners-promotion-setti const GeneralSettings: FC = () => { return (
- + - + - + diff --git a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx index 7aede7f5d..46f1fbe56 100644 --- a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx +++ b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx @@ -7,9 +7,9 @@ import classNames from 'clsx'; import AssetField from 'app/atoms/AssetField'; import Money from 'app/atoms/Money'; import { AssetIcon } from 'app/templates/AssetIcon'; +import { DropdownSelect } from 'app/templates/DropdownSelect/DropdownSelect'; import InFiat from 'app/templates/InFiat'; -import { InputGeneral } from 'app/templates/InputGeneral/InputGeneral'; -import { SelectGeneral } from 'app/templates/InputGeneral/SelectGeneral'; +import { InputContainer } from 'app/templates/InputContainer/InputContainer'; import { setTestID, useFormAnalytics } from 'lib/analytics'; import { T, t, toLocalFormat } from 'lib/i18n'; import { EMPTY_BASE_METADATA, useAssetMetadata, AssetMetadataBase } from 'lib/metadata'; @@ -118,7 +118,7 @@ export const SwapFormInput: FC = ({ return (
- = ({ selectedAssetSymbol={assetMetadataWithFallback.symbol} /> } - mainContent={ - - } - searchProps={{ - searchValue, - onSearchChange: handleSearchChange - }} - Input={ - - } - optionsProps={{ - isLoading, - options: filteredAssets, - noItemsText: 'No items', - renderOptionContent: option => renderOptionContent(option, value.assetSlug === option), - onOptionChange: handleSelectedAssetChange - }} - /> - } footer={
{prettyError &&
{prettyError}
} @@ -172,7 +136,43 @@ export const SwapFormInput: FC = ({ />
} - /> + > + + } + searchProps={{ + searchValue, + onSearchChange: handleSearchChange + }} + Input={ + + } + optionsProps={{ + isLoading, + options: filteredAssets, + noItemsText: 'No items', + getKey: option => option, + renderOptionContent: option => renderOptionContent(option, value.assetSlug === option), + onOptionChange: handleSelectedAssetChange + }} + /> +
); }; @@ -184,7 +184,7 @@ interface SwapFieldProps { } const SwapDropdownFace: FC = ({ testId, selectedAssetSlug, selectedAssetMetadata }) => ( -
+
{selectedAssetSlug ? (
diff --git a/src/app/templates/TopUpInput/index.tsx b/src/app/templates/TopUpInput/index.tsx index 1572934c4..4c3724c9e 100644 --- a/src/app/templates/TopUpInput/index.tsx +++ b/src/app/templates/TopUpInput/index.tsx @@ -7,8 +7,8 @@ import { getBigErrorText, getSmallErrorText } from 'app/pages/Buy/utils/errorTex import { getAssetSymbolToDisplay } from 'lib/buy-with-credit-card/get-asset-symbol-to-display'; import { T, toLocalFormat } from 'lib/i18n'; -import { InputGeneral } from '../InputGeneral/InputGeneral'; -import { SelectGeneral } from '../InputGeneral/SelectGeneral'; +import { DropdownSelect } from '../DropdownSelect/DropdownSelect'; +import { InputContainer } from '../InputContainer/InputContainer'; import { CurrencyOption } from './CurrenciesMenu/CurrencyOption'; import { StaticCurrencyImage } from './StaticCurrencyImage'; import { TopUpInputPropsGeneric, CurrencyBase, TopUpInputPropsBase } from './types'; @@ -57,7 +57,7 @@ export const TopUpInput = (_props: TopUpInputPropsGeneri return (
- {label} @@ -69,59 +69,6 @@ export const TopUpInput = (_props: TopUpInputPropsGeneri )}
} - mainContent={ - - testIds={{ - dropdownTestId: testID - }} - dropdownButtonClassName="pl-4 pr-3 py-5" - DropdownFaceContent={ - - } - Input={ -
-
- -
-
- } - optionsProps={{ - isLoading: isCurrenciesLoading, - options: filteredCurrencies, - noItemsText: emptyListPlaceholder, - renderOptionContent: currCurrency => - renderOptionContent(currCurrency, JSON.stringify(currCurrency) === JSON.stringify(currency)), - onOptionChange: newValue => onCurrencySelect?.(newValue) - }} - searchProps={{ - searchValue, - onSearchChange: event => setSearchValue(event.target.value) - }} - /> - } footer={ (_props: TopUpInputPropsGeneri maxAmount={maxAmount} /> } - /> + > + + testIds={{ + dropdownTestId: testID + }} + dropdownButtonClassName="pl-4 pr-3 py-5" + DropdownFaceContent={ + + } + Input={ +
+
+ +
+
+ } + optionsProps={{ + isLoading: isCurrenciesLoading, + options: filteredCurrencies, + noItemsText: emptyListPlaceholder, + getKey: option => option.code, + renderOptionContent: currCurrency => + renderOptionContent(currCurrency, JSON.stringify(currCurrency) === JSON.stringify(currency)), + onOptionChange: newValue => onCurrencySelect?.(newValue) + }} + searchProps={{ + searchValue, + onSearchChange: event => setSearchValue(event.target.value) + }} + /> +
); }; diff --git a/src/lib/ui/hooks/index.ts b/src/lib/ui/hooks/index.ts index eed1196b4..c97d29217 100644 --- a/src/lib/ui/hooks/index.ts +++ b/src/lib/ui/hooks/index.ts @@ -9,5 +9,3 @@ export { useSafeState } from './useSafeState'; export { useInterval } from './useInterval'; export { useStopper } from './useStopper'; - -export { useFocusOnElement } from './useFocusOnElement'; diff --git a/src/lib/ui/hooks/useFocusOnElement.ts b/src/lib/ui/hooks/useFocusOnElement.ts deleted file mode 100644 index 4bdbecfe4..000000000 --- a/src/lib/ui/hooks/useFocusOnElement.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useEffect, useRef } from 'react'; - -export function useFocusOnElement(condition = true) { - const ref = useRef(null); - const prevConditionRef = useRef(condition); - - useEffect(() => { - if (!prevConditionRef.current && condition) { - ref.current?.focus(); - } - prevConditionRef.current = condition; - }, [condition]); - - return ref; -} diff --git a/tailwind.config.js b/tailwind.config.js index 1ce776551..b585de794 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -408,6 +408,7 @@ module.exports = { minHeight: { 0: '0', full: '100%', + 18: '4.5rem', screen: '100vh' }, minWidth: { From e6928039c7488b985be7e61d9e126323f26708e4 Mon Sep 17 00:00:00 2001 From: herkoss Date: Fri, 23 Jun 2023 13:44:27 +0300 Subject: [PATCH 10/21] TW-565 Removed uuid --- package.json | 4 ---- yarn.lock | 10 ---------- 2 files changed, 14 deletions(-) diff --git a/package.json b/package.json index 2784e8e36..2766c1cbc 100644 --- a/package.json +++ b/package.json @@ -181,7 +181,6 @@ "use-force-update": "1.0.7", "use-onclickoutside": "0.4.1", "util": "0.11.1", - "uuid": "^9.0.0", "wasm-themis": "0.14.6", "webextension-polyfill": "^0.10.0", "webpack": "^5.74.0", @@ -216,8 +215,5 @@ "nanoid": "3.1.31", "tslib": "2.4.0", "@types/react": "18.0.15" - }, - "devDependencies": { - "@types/uuid": "^9.0.1" } } diff --git a/yarn.lock b/yarn.lock index e850550f3..d8675917a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2641,11 +2641,6 @@ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== -"@types/uuid@^9.0.1": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.1.tgz#98586dc36aee8dacc98cc396dbca8d0429647aa6" - integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== - "@types/w3c-web-hid@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@types/w3c-web-hid/-/w3c-web-hid-1.0.3.tgz#e08587a7d737f8654ea6bc0a88689ce5d3ce2d19" @@ -10425,11 +10420,6 @@ utila@~0.4: resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= -uuid@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" - integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== - v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" From 293086a44fc503593b355ff9a6dbae5444045cab Mon Sep 17 00:00:00 2001 From: herkoss Date: Fri, 23 Jun 2023 17:29:33 +0300 Subject: [PATCH 11/21] TW-565 Fixed testid --- src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx index 46f1fbe56..90cdb116c 100644 --- a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx +++ b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.tsx @@ -145,7 +145,7 @@ export const SwapFormInput: FC = ({ dropdownButtonClassName="pl-4 pr-3 py-5" DropdownFaceContent={ From c9dde249e51d541948b373b7e4d3d4c8326947a8 Mon Sep 17 00:00:00 2001 From: herkoss Date: Mon, 26 Jun 2023 19:25:46 +0300 Subject: [PATCH 12/21] TW-565 Fixed comments --- src/app/templates/AssetSelect/AssetSelect.tsx | 3 ++- .../SettingsGeneral/Components/BlockExplorerSelect.tsx | 4 ++-- src/app/templates/TopUpInput/index.tsx | 4 ++-- src/lib/assets/known-tokens.ts | 8 +++++--- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/app/templates/AssetSelect/AssetSelect.tsx b/src/app/templates/AssetSelect/AssetSelect.tsx index 6440b1693..1913b0c3f 100644 --- a/src/app/templates/AssetSelect/AssetSelect.tsx +++ b/src/app/templates/AssetSelect/AssetSelect.tsx @@ -1,6 +1,7 @@ import React, { FC, useCallback, useMemo, useState } from 'react'; import classNames from 'clsx'; +import { isEqual } from 'lodash'; import { useDebounce } from 'use-debounce'; import Money from 'app/atoms/Money'; @@ -74,7 +75,7 @@ const AssetSelect: FC = ({ value, assets, onChange, className, noItemsText: t('noAssetsFound'), getKey: option => getSlug(option), onOptionChange: handleChange, - renderOptionContent: asset => renderOptionContent(asset, JSON.stringify(asset) === JSON.stringify(value)) + renderOptionContent: asset => renderOptionContent(asset, isEqual(asset, value)) }} /> diff --git a/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx b/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx index 6d4352885..c96e5d593 100644 --- a/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx +++ b/src/app/templates/SettingsGeneral/Components/BlockExplorerSelect.tsx @@ -1,6 +1,7 @@ import React, { useMemo, useCallback, FC, useState } from 'react'; import classNames from 'clsx'; +import { isEqual } from 'lodash'; import browser from 'webextension-polyfill'; import Flag from 'app/atoms/Flag'; @@ -54,8 +55,7 @@ const BlockExplorerSelect = () => { options, noItemsText: 'No items', getKey: option => option.id, - renderOptionContent: option => - renderOptionContent(option, JSON.stringify(option) === JSON.stringify(explorer)), + renderOptionContent: option => renderOptionContent(option, isEqual(option, explorer)), onOptionChange: handleBlockExplorerChange }} searchProps={{ diff --git a/src/app/templates/TopUpInput/index.tsx b/src/app/templates/TopUpInput/index.tsx index 4c3724c9e..0a8e0dc50 100644 --- a/src/app/templates/TopUpInput/index.tsx +++ b/src/app/templates/TopUpInput/index.tsx @@ -1,6 +1,7 @@ import React, { FC } from 'react'; import classNames from 'clsx'; +import { isEqual } from 'lodash'; import AssetField from 'app/atoms/AssetField'; import { getBigErrorText, getSmallErrorText } from 'app/pages/Buy/utils/errorText.utils'; @@ -121,8 +122,7 @@ export const TopUpInput = (_props: TopUpInputPropsGeneri options: filteredCurrencies, noItemsText: emptyListPlaceholder, getKey: option => option.code, - renderOptionContent: currCurrency => - renderOptionContent(currCurrency, JSON.stringify(currCurrency) === JSON.stringify(currency)), + renderOptionContent: currCurrency => renderOptionContent(currCurrency, isEqual(currCurrency, currency)), onOptionChange: newValue => onCurrencySelect?.(newValue) }} searchProps={{ diff --git a/src/lib/assets/known-tokens.ts b/src/lib/assets/known-tokens.ts index 7900ea2d5..910e0dfcd 100644 --- a/src/lib/assets/known-tokens.ts +++ b/src/lib/assets/known-tokens.ts @@ -37,13 +37,15 @@ const PREDEFINED_TOKENS_BY_CHAIN_ID: Record = { export const getPredefinedTokensSlugs = (chainId: string) => PREDEFINED_TOKENS_BY_CHAIN_ID[chainId] ?? []; +const YOUVES_COLORS = { bg: '#143A3A', bgHover: '#4F6B6B' }; + export const TOKENS_BRAND_COLORS: Record = { [KNOWN_TOKENS_SLUGS.KUSD]: { bg: '#3EBD93', bgHover: '#65CAA9' }, [KNOWN_TOKENS_SLUGS.TZBTC]: { bg: '#1373E4', bgHover: '#428FE9' }, [KNOWN_TOKENS_SLUGS.USDT]: { bg: '#009393', bgHover: '#52AF95' }, - [KNOWN_TOKENS_SLUGS.UUSD]: { bg: '#143A3A', bgHover: '#4F6B6B' }, - [KNOWN_TOKENS_SLUGS.UBTC]: { bg: '#143A3A', bgHover: '#4F6B6B' }, - [KNOWN_TOKENS_SLUGS.YOU]: { bg: '#143A3A', bgHover: '#4F6B6B' } + [KNOWN_TOKENS_SLUGS.UUSD]: YOUVES_COLORS, + [KNOWN_TOKENS_SLUGS.UBTC]: YOUVES_COLORS, + [KNOWN_TOKENS_SLUGS.YOU]: YOUVES_COLORS }; export const DEPRECATED_TKEY_METADATA: TokenMetadata = { From b6ea0e5e270097ab20fbbc79c8c085d1773969dc Mon Sep 17 00:00:00 2001 From: herkoss Date: Tue, 27 Jun 2023 11:36:57 +0300 Subject: [PATCH 13/21] TW-565 Fixed inline styles --- src/app/templates/DropdownSelect/DropdownSelect.tsx | 11 +++-------- .../SwapForm/SwapFormInput/SwapFormInput.tsx | 2 +- tailwind.config.js | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/app/templates/DropdownSelect/DropdownSelect.tsx b/src/app/templates/DropdownSelect/DropdownSelect.tsx index 88641d893..33a9ec8af 100644 --- a/src/app/templates/DropdownSelect/DropdownSelect.tsx +++ b/src/app/templates/DropdownSelect/DropdownSelect.tsx @@ -64,13 +64,10 @@ export const DropdownSelect = ({ {opened ? ( ) : ( -
+