Commit ffb25aa1 authored by N's avatar N Committed by GitHub

Merge pull request #29 from blockscout/form

react hook form
parents fa6c57ab f5db3914
......@@ -20,6 +20,7 @@
"next-react-svg": "1.1.3",
"react": "18.1.0",
"react-dom": "18.1.0",
"react-hook-form": "^7.33.1",
"react-jazzicon": "^1.0.4"
},
"devDependencies": {
......
import React, { useCallback, useEffect } from 'react';
import type { SubmitHandler, ControllerRenderProps } from 'react-hook-form';
import { useForm, Controller } from 'react-hook-form';
import {
Box,
Button,
Checkbox,
Text,
Grid,
GridItem,
} from '@chakra-ui/react';
import AddressInput from './AddressInput';
import TagInput from './TagInput';
import type { TWatchlistItem } from '../../data/watchlist';
const NOTIFICATIONS = [ 'xDAI', 'ERC-20', 'ERC-721, ERC-1155 (NFT)' ];
const ADDRESS_LENGTH = 42;
const TAG_MAX_LENGTH = 35;
type Props = {
data?: TWatchlistItem;
}
type Inputs = {
address: string;
tag: string;
notification: boolean;
}
const AddressModal: React.FC<Props> = ({ data }) => {
const { control, handleSubmit, formState: { errors }, setValue } = useForm<Inputs>();
useEffect(() => {
setValue('address', data?.address || '');
setValue('tag', data?.tag || '');
setValue('notification', Boolean(data?.notification));
}, [ setValue, data ]);
// eslint-disable-next-line no-console
const onSubmit: SubmitHandler<Inputs> = data => console.log(data);
const renderAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'address'>}) => {
return <AddressInput field={ field } isInvalid={ Boolean(errors.address) }/>
}, [ errors ]);
const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => {
return <TagInput field={ field } isInvalid={ Boolean(errors.tag) }/>
}, [ errors ]);
const renderCheckbox = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'notification'>}) => (
<Checkbox
isChecked={ field.value }
onChange={ field.onChange }
ref={ field.ref }
colorScheme="blue"
size="lg"
>
Email notifications
</Checkbox>
), []);
return (
<>
<Box marginBottom={ 5 }>
<Controller
name="address"
control={ control }
rules={{
maxLength: ADDRESS_LENGTH,
minLength: ADDRESS_LENGTH,
}}
render={ renderAddressInput }
/>
</Box>
<Controller
name="tag"
control={ control }
rules={{
maxLength: TAG_MAX_LENGTH,
}}
render={ renderTagInput }
/>
<Text color="gray.500" fontSize="sm" marginBottom={ 8 }>
Please select what types of notifications you will receive:
</Text>
<Box marginBottom={ 8 }>
{ /* add them to the form later */ }
<Grid templateColumns="repeat(3, max-content)" gap="20px 24px">
{ NOTIFICATIONS.map((notification: string) => {
return (
<>
<GridItem>{ notification }</GridItem>
<GridItem><Checkbox colorScheme="blue" size="lg">Incoming</Checkbox></GridItem>
<GridItem><Checkbox colorScheme="blue" size="lg">Outgoing</Checkbox></GridItem>
</>
)
}) }
</Grid>
</Box>
<Text color="gray.500" fontSize="sm" marginBottom={ 5 }>Notification methods:</Text>
<Controller
name="notification"
control={ control }
render={ renderCheckbox }
/>
<Box marginTop={ 8 }>
<Button
variant="primary"
onClick={ handleSubmit(onSubmit) }
disabled={ Object.keys(errors).length > 0 }
>
{ data ? 'Save changes' : 'Add address' }
</Button>
</Box>
</>
)
}
export default AddressModal;
import React from 'react'
import type { ControllerRenderProps } from 'react-hook-form';
import {
Input,
FormControl,
FormLabel,
} from '@chakra-ui/react';
const ADDRESS_LENGTH = 42;
type Props = {
field: ControllerRenderProps<any, 'address'>;
isInvalid: boolean;
}
const AddressInput: React.FC<Props> = ({ field, isInvalid }) => {
return (
<FormControl variant="floating" id="address" isRequired>
<Input
{ ...field }
placeholder=" "
isInvalid={ isInvalid }
maxLength={ ADDRESS_LENGTH }
/>
<FormLabel>Address (0x...)</FormLabel>
</FormControl>
)
}
export default AddressInput
import React, { useCallback, useEffect, useState } from 'react';
import React from 'react';
import {
Box,
Button,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
ModalCloseButton,
Input,
Checkbox,
Text,
Grid,
GridItem,
FormControl,
FormLabel,
} from '@chakra-ui/react';
import type { TWatchlistItem } from '../../data/watchlist';
const NOTIFICATIONS = [ 'xDAI', 'ERC-20', 'ERC-721, ERC-1155 (NFT)' ];
const ADDRESS_LENGTH = 42;
import AddressForm from './AddressForm';
type Props = {
isOpen: boolean;
onClose: () => void;
getDisclosureProps: () => any;
data?: TWatchlistItem;
}
const AddressModal: React.FC<Props> = ({ isOpen, onClose, data }) => {
// надо чето придумать с формой
// потом доделаем
const [ address, setAddress ] = useState<string>();
const [ tag, setTag ] = useState<string>();
const [ notification, setNotification ] = useState<boolean>();
const [ addressError, setAddressError ] = useState<boolean>(false);
const isValidAddress = (address: string) => address.length === ADDRESS_LENGTH;
useEffect(() => {
setAddress(data?.address);
setAddressError(false);
setTag(data?.tag);
setNotification(data?.notification);
}, [ data ]);
const onAddressChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
if (addressError && isValidAddress(event.target.value)) {
setAddressError(false);
}
setAddress(event.target.value);
}, [ addressError ]);
const validateAddress = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
if (!isValidAddress(event.target.value)) {
setAddressError(true);
}
}, [ ]);
const onTagChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
setTag(event.target.value)
}, [ ]);
const onNotificationChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => setNotification(event.target.checked), [ setNotification ]);
const onButtonClick = useCallback(() => {
// eslint-disable-next-line no-console
console.log(address, tag, notification);
onClose()
}, [ address, tag, notification, onClose ]);
const title = data ? 'Edit watchlist address' : 'New Address to Watchlist';
return (
......@@ -87,53 +35,8 @@ const AddressModal: React.FC<Props> = ({ isOpen, onClose, data }) => {
An Email notification can be sent to you when an address on your watch list sends or receives any transactions.
</Text>
) }
<FormControl variant="floating" id="address" marginBottom={ 5 } isRequired>
<Input
placeholder=" "
onChange={ onAddressChange }
value={ address || '' }
isInvalid={ addressError }
onBlur={ validateAddress }
maxLength={ ADDRESS_LENGTH }
/>
<FormLabel>Address (0x...)</FormLabel>
</FormControl>
<FormControl variant="floating" id="tag" marginBottom={ 8 } isRequired>
<Input placeholder=" " onChange={ onTagChange } value={ tag || '' } maxLength={ 35 }/>
<FormLabel>Private tag (max 35 characters)</FormLabel>
</FormControl>
<Text color="gray.500" fontSize="sm" marginBottom={ 8 }>
Please select what types of notifications you will receive:
</Text>
<Box marginBottom={ 8 }>
<Grid templateColumns="repeat(3, max-content)" gap="20px 24px">
{ NOTIFICATIONS.map((notification: string) => {
return (
<>
<GridItem>{ notification }</GridItem>
<GridItem><Checkbox colorScheme="blue" size="lg">Incoming</Checkbox></GridItem>
<GridItem><Checkbox colorScheme="blue" size="lg">Outgoing</Checkbox></GridItem>
</>
)
}) }
</Grid>
</Box>
<Text color="gray.500" fontSize="sm" marginBottom={ 5 }>Notification methods:</Text>
<Checkbox
isChecked={ notification }
colorScheme="blue"
onChange={ onNotificationChange }
size="lg"
>
Email notifications
</Checkbox>
<AddressForm data={ data }/>
</ModalBody>
<ModalFooter justifyContent="flex-start">
<Button variant="primary" onClick={ onButtonClick } disabled={ addressError }>
{ data ? 'Save changes' : 'Add address' }
</Button>
</ModalFooter>
</ModalContent>
</Modal>
)
......
import React from 'react';
import type { ControllerRenderProps } from 'react-hook-form';
import {
Input,
FormControl,
FormLabel,
} from '@chakra-ui/react';
const TAG_MAX_LENGTH = 35;
type Props = {
field: ControllerRenderProps<any, 'tag'>;
isInvalid: boolean;
}
const TagInput: React.FC<Props> = ({ field, isInvalid }) => {
return (
<FormControl variant="floating" id="tag" isRequired>
<Input
{ ...field }
placeholder=" "
isInvalid={ isInvalid }
maxLength={ TAG_MAX_LENGTH }
/>
<FormLabel>Private tag (max 35 characters)</FormLabel>
</FormControl>
)
}
export default TagInput;
......@@ -13,7 +13,6 @@ import {
type Props = {
isOpen: boolean;
onClose: () => void;
getDisclosureProps: () => any;
address?: string;
}
......
......@@ -2773,6 +2773,11 @@ react-focus-lock@^2.9.1:
use-callback-ref "^1.3.0"
use-sidecar "^1.1.2"
react-hook-form@^7.33.1:
version "7.33.1"
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.33.1.tgz#8c4410e3420788d3b804d62cc4c142915c2e46d0"
integrity sha512-ydTfTxEJdvgjCZBj5DDXRc58oTEfnFupEwwTAQ9FSKzykEJkX+3CiAkGtAMiZG7IPWHuzgT6AOBfogiKhUvKgg==
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment