Commit abf8a445 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Update styles of form inputs (#1914)

* Update styles of form inputs

* update token info form

* update demo values

* fix preset for sepolia

* update screenshots
parent 8796abbf
......@@ -24,8 +24,8 @@ NEXT_PUBLIC_API_BASE_PATH=/
# ui config
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND='rgba(51, 53, 67, 1)'
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR='rgba(165, 252, 122, 1)'
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=rgba(51,53,67,1)
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=rgba(165,252,122,1)
## sidebar
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth-sepolia.json
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/sepolia.svg
......
......@@ -94,7 +94,6 @@ frontend:
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE: "{ \"id\": \"632018\", \"width\": \"320\", \"height\": \"100\" }"
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED: true
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED: true
NEXT_PUBLIC_COLOR_THEME_DEFAULT: "dim"
envFromSecret:
NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN
SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI
......
import { formAnatomy as parts } from '@chakra-ui/anatomy';
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { getColor, mode } from '@chakra-ui/theme-tools';
import getDefaultFormColors from '../utils/getDefaultFormColors';
import getFormStyles from '../utils/getFormStyles';
import FancySelect from './FancySelect';
import FormLabel from './FormLabel';
import Input from './Input';
......@@ -13,8 +12,7 @@ const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(parts.keys);
function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunctionProps) {
const { theme } = props;
const { focusPlaceholderColor, errorColor } = getDefaultFormColors(props);
const formStyles = getFormStyles(props);
const activeLabelStyles = {
...FormLabel.variants?.floating?.(props)._focusWithin,
......@@ -63,12 +61,29 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction
// label styles
label: FormLabel.sizes?.[size](props) || {},
'input:not(:placeholder-shown) + label, textarea:not(:placeholder-shown) + label': activeLabelStyles,
'textarea:not(:placeholder-shown) + label': {
bgColor: formStyles.input.filled.bgColor,
},
[`
input[readonly] + label,
textarea[readonly] + label,
&[aria-readonly=true] label
`]: {
bgColor: formStyles.input.readOnly.bgColor,
},
[`
input[aria-invalid=true] + label,
textarea[aria-invalid=true] + label,
&[aria-invalid=true] label
`]: {
color: getColor(theme, errorColor),
color: formStyles.placeholder.error.color,
},
[`
input[disabled] + label,
textarea[disabled] + label,
&[aria-disabled=true] label
`]: {
color: formStyles.placeholder.disabled.color,
},
// input styles
......@@ -79,31 +94,24 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction
padding: inputPx,
},
'input:not(:placeholder-shown), textarea:not(:placeholder-shown)': activeInputStyles,
[`
input[disabled] + label,
&[aria-disabled=true] label
`]: {
backgroundColor: 'transparent',
},
// in textarea bg of label could not be transparent; it should match the background color of input but without alpha
// so we have to use non-standard colors here
'textarea[disabled] + label': {
backgroundColor: mode('#ececec', '#232425')(props),
},
'textarea[disabled] + label[data-in-modal=true]': {
backgroundColor: mode('#ececec', '#292b34')(props),
},
// indicator styles
'input:not(:placeholder-shown) + label .chakra-form__required-indicator, textarea:not(:placeholder-shown) + label .chakra-form__required-indicator': {
color: getColor(theme, focusPlaceholderColor),
color: formStyles.placeholder.default.color,
},
[`
input[aria-invalid=true] + label .chakra-form__required-indicator,
textarea[aria-invalid=true] + label .chakra-form__required-indicator,
&[aria-invalid=true] .chakra-form__required-indicator
`]: {
color: getColor(theme, errorColor),
color: formStyles.placeholder.error.color,
},
[`
input[disabled] + label .chakra-form__required-indicator,
textarea[disabled] + label .chakra-form__required-indicator,
&[aria-disabled=true] .chakra-form__required-indicator
`]: {
color: formStyles.placeholder.disabled.color,
},
},
};
......
......@@ -65,6 +65,19 @@ test.describe('floating label size md +@dark-mode', () => {
await expect(component).toHaveScreenshot();
});
test('filled read-only', async({ mount }) => {
const component = await mount(
<TestApp>
<FormControl variant="floating" id="name" isRequired size="md">
<Input required value="foo" isReadOnly/>
<FormLabel>Smart contract / Address (0x...)</FormLabel>
</FormControl>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('filled error', async({ mount }) => {
const component = await mount(
<TestApp>
......
import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
import { getColor } from '@chakra-ui/theme-tools';
import getDefaultFormColors from '../utils/getDefaultFormColors';
import getFormStyles from '../utils/getFormStyles';
const baseStyle = defineStyle({
display: 'flex',
......@@ -13,14 +12,12 @@ const baseStyle = defineStyle({
transitionDuration: 'normal',
opacity: 1,
_disabled: {
opacity: 0.4,
opacity: 0.2,
},
});
const variantFloating = defineStyle((props) => {
const { theme, backgroundColor } = props;
const { focusPlaceholderColor } = getDefaultFormColors(props);
const bc = backgroundColor || 'transparent';
const formStyles = getFormStyles(props);
return {
left: '2px',
......@@ -29,8 +26,8 @@ const variantFloating = defineStyle((props) => {
position: 'absolute',
borderRadius: 'base',
boxSizing: 'border-box',
color: 'gray.500',
backgroundColor: 'transparent',
color: formStyles.placeholder.default.color,
backgroundColor: props.bgColor || props.backgroundColor || 'transparent',
pointerEvents: 'none',
margin: 0,
transformOrigin: 'top left',
......@@ -39,8 +36,8 @@ const variantFloating = defineStyle((props) => {
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
_focusWithin: {
backgroundColor: bc,
color: getColor(theme, focusPlaceholderColor),
backgroundColor: props.bgColor || props.backgroundColor || 'transparent',
color: formStyles.placeholder.default.color,
fontSize: 'xs',
lineHeight: '16px',
borderTopRightRadius: 'none',
......@@ -70,7 +67,7 @@ const sizes = {
return {
fontSize: 'md',
lineHeight: '24px',
padding: '28px 4px 28px 24px',
padding: '26px 4px 26px 24px',
right: '22px',
_focusWithin: {
padding: '16px 0 2px 24px',
......
......@@ -10,11 +10,11 @@ import { runIfFn } from '@chakra-ui/utils';
const { defineMultiStyleConfig, definePartsStyle } =
createMultiStyleConfigHelpers(parts.keys);
const baseStyleDialog = defineStyle((props) => {
const baseStyleDialog = defineStyle(() => {
return {
padding: 8,
borderRadius: 'lg',
bg: mode('white', 'gray.900')(props),
bg: 'dialog_bg',
margin: 'auto',
};
});
......@@ -61,7 +61,7 @@ const baseStyleOverlay = defineStyle({
});
const baseStyle = definePartsStyle((props) => ({
dialog: runIfFn(baseStyleDialog, props),
dialog: runIfFn(baseStyleDialog),
dialogContainer: baseStyleDialogContainer,
header: runIfFn(baseStyleHeader, props),
......
import { Textarea as TextareaComponent } from '@chakra-ui/react';
import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
import { mode } from '@chakra-ui/theme-tools';
import getOutlinedFieldStyles from '../utils/getOutlinedFieldStyles';
const variantFilledInactive = defineStyle((props) => {
return {
// https://bugs.chromium.org/p/chromium/issues/detail?id=1362573
// there is a problem with scrollbar color in chromium
// so blackAlpha.50 here is replaced with #f5f5f6
// and whiteAlpha.50 is replaced with #1a1b1b
// bgColor: mode('blackAlpha.50', 'whiteAlpha.50')(props),
bgColor: mode('#f5f5f6', '#1a1b1b')(props),
};
});
const sizes = {
md: defineStyle({
fontSize: 'md',
......@@ -38,7 +24,6 @@ const Textarea = defineStyleConfig({
sizes,
variants: {
outline: defineStyle(getOutlinedFieldStyles),
filledInactive: variantFilledInactive,
},
defaultProps: {
variant: 'outline',
......
......@@ -23,6 +23,10 @@ const semanticTokens = {
'default': 'red.400',
_dark: 'red.300',
},
dialog_bg: {
'default': 'white',
_dark: 'gray.900',
},
},
shadows: {
action_bar: '0 4px 4px -4px rgb(0 0 0 / 10%), 0 2px 4px -4px rgb(0 0 0 / 6%)',
......
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { mode } from '@chakra-ui/theme-tools';
export default function getDefaultFormColors(props: StyleFunctionProps) {
const { focusBorderColor: fc, errorBorderColor: ec, filledBorderColor: flc } = props;
return {
focusBorderColor: fc || mode('blue.500', 'blue.300')(props),
focusPlaceholderColor: fc || 'gray.500',
errorColor: ec || mode('red.400', 'red.300')(props),
filledColor: flc || mode('gray.300', 'gray.600')(props),
};
}
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { mode, transparentize } from '@chakra-ui/theme-tools';
export default function getFormStyles(props: StyleFunctionProps) {
return {
input: {
empty: {
// there is no text in the empty input
// color: ???,
bgColor: props.bgColor || mode('white', 'black')(props),
borderColor: mode('gray.100', 'gray.700')(props),
},
hover: {
color: mode('gray.800', 'gray.50')(props),
bgColor: props.bgColor || mode('white', 'black')(props),
borderColor: mode('gray.200', 'gray.500')(props),
},
focus: {
color: mode('gray.800', 'gray.50')(props),
bgColor: props.bgColor || mode('white', 'black')(props),
borderColor: mode('blue.400', 'blue.400')(props),
},
filled: {
color: mode('gray.800', 'gray.50')(props),
bgColor: props.bgColor || mode('white', 'black')(props),
borderColor: mode('gray.300', 'gray.600')(props),
},
readOnly: {
color: mode('gray.800', 'gray.50')(props),
bgColor: mode('gray.200', 'gray.800')(props),
borderColor: mode('gray.200', 'gray.800')(props),
},
// we use opacity to show the disabled state
disabled: {
opacity: 0.2,
},
error: {
color: mode('gray.800', 'gray.50')(props),
bgColor: props.bgColor || mode('white', 'black')(props),
borderColor: mode('red.500', 'red.500')(props),
},
},
placeholder: {
'default': {
color: mode('gray.500', 'gray.500')(props),
},
disabled: {
color: transparentize('gray.500', 0.2)(props.theme),
},
error: {
color: mode('red.500', 'red.500')(props),
},
},
};
}
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { mode, getColor } from '@chakra-ui/theme-tools';
import { mode } from '@chakra-ui/theme-tools';
import getDefaultFormColors from './getDefaultFormColors';
import getDefaultTransitionProps from './getDefaultTransitionProps';
import getFormStyles from './getFormStyles';
export default function getOutlinedFieldStyles(props: StyleFunctionProps) {
const { theme, borderColor } = props;
const { focusBorderColor, errorColor } = getDefaultFormColors(props);
const formStyles = getFormStyles(props);
const transitionProps = getDefaultTransitionProps();
return {
border: '2px solid',
// filled input
backgroundColor: 'transparent',
borderColor: mode('gray.300', 'gray.600')(props),
...formStyles.input.filled,
...transitionProps,
_hover: {
borderColor: mode('gray.200', 'gray.500')(props),
...formStyles.input.hover,
},
_readOnly: {
boxShadow: 'none !important',
userSelect: 'all',
pointerEvents: 'none',
...formStyles.input.readOnly,
_hover: {
...formStyles.input.readOnly,
},
_focus: {
...formStyles.input.readOnly,
},
},
_disabled: {
opacity: 1,
backgroundColor: mode('blackAlpha.200', 'whiteAlpha.200')(props),
borderColor: 'transparent',
...formStyles.input.disabled,
cursor: 'not-allowed',
_hover: {
borderColor: 'transparent',
},
':-webkit-autofill': {
// background color for disabled input which value was selected from browser autocomplete popup
'-webkit-box-shadow': `0 0 0px 1000px ${ mode('rgba(16, 17, 18, 0.08)', 'rgba(255, 255, 255, 0.08)')(props) } inset`,
},
},
_invalid: {
borderColor: getColor(theme, errorColor),
...formStyles.input.error,
boxShadow: `none`,
_placeholder: {
color: formStyles.placeholder.error.color,
},
},
_focusVisible: {
...formStyles.input.focus,
zIndex: 1,
borderColor: getColor(theme, focusBorderColor),
boxShadow: 'md',
},
_placeholder: {
color: mode('blackAlpha.600', 'whiteAlpha.600')(props),
color: formStyles.placeholder.default.color,
},
// not filled input
':placeholder-shown:not(:focus-visible):not(:hover):not([aria-invalid=true])': { borderColor: borderColor || mode('gray.100', 'gray.700')(props) },
':placeholder-shown:not(:focus-visible):not(:hover):not([aria-invalid=true]):not([aria-readonly=true])': {
...formStyles.input.empty,
},
// not filled input with type="date"
':not(:placeholder-shown)[value=""]:not(:focus-visible):not(:hover):not([aria-invalid=true])': {
borderColor: borderColor || mode('gray.100', 'gray.700')(props),
color: 'gray.500',
':not(:placeholder-shown)[value=""]:not(:focus-visible):not(:hover):not([aria-invalid=true]):not([aria-readonly=true])': {
...formStyles.input.empty,
},
':-webkit-autofill': { transition: 'background-color 5000s ease-in-out 0s' },
......
......@@ -128,7 +128,6 @@ const ContractSourceCode = ({ address, implementationAddress }: Props) => {
size="xs"
value={ sourceType }
onChange={ handleSelectChange }
focusBorderColor="none"
w="auto"
fontWeight={ 600 }
borderRadius="base"
......
......@@ -24,7 +24,6 @@ interface Props {
const TokenSelectMenu = ({ erc20sort, erc1155sort, erc404sort, filteredData, onInputChange, onSortClick, searchTerm }: Props) => {
const searchIconColor = useColorModeValue('blackAlpha.600', 'whiteAlpha.600');
const inputBorderColor = useColorModeValue('blackAlpha.100', 'whiteAlpha.200');
const hasFilteredResult = _sumBy(Object.values(filteredData), ({ items }) => items.length) > 0;
......@@ -39,7 +38,7 @@ const TokenSelectMenu = ({ erc20sort, erc1155sort, erc404sort, filteredData, onI
placeholder="Search by token name"
ml="1px"
onChange={ onInputChange }
borderColor={ inputBorderColor }
bgColor="dialog_bg"
/>
</InputGroup>
<Flex flexDir="column" rowGap={ 6 }>
......
import { FormControl, Input, useColorModeValue } from '@chakra-ui/react';
import { FormControl, Input } from '@chakra-ui/react';
import React from 'react';
import type { Control, ControllerRenderProps, FormState } from 'react-hook-form';
import { Controller } from 'react-hook-form';
......@@ -15,13 +15,11 @@ interface Props {
}
const AddressVerificationFieldAddress = ({ formState, control }: Props) => {
const backgroundColor = useColorModeValue('white', 'gray.900');
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<Fields, 'address'>}) => {
const error = 'address' in formState.errors ? formState.errors.address : undefined;
return (
<FormControl variant="floating" id={ field.name } isRequired size="md" backgroundColor={ backgroundColor } mt={ 8 }>
<FormControl variant="floating" id={ field.name } isRequired size="md" bgColor="dialog_bg" mt={ 8 }>
<Input
{ ...field }
required
......@@ -29,11 +27,12 @@ const AddressVerificationFieldAddress = ({ formState, control }: Props) => {
maxLength={ ADDRESS_LENGTH }
isDisabled={ formState.isSubmitting }
autoComplete="off"
bgColor="dialog_bg"
/>
<InputPlaceholder text="Smart contract address (0x...)" error={ error }/>
</FormControl>
);
}, [ formState.errors, formState.isSubmitting, backgroundColor ]);
}, [ formState.errors, formState.isSubmitting ]);
return (
<Controller
......
import { FormControl, Textarea, useColorModeValue } from '@chakra-ui/react';
import { FormControl, Textarea } from '@chakra-ui/react';
import React from 'react';
import type { Control, ControllerRenderProps, FormState } from 'react-hook-form';
import { Controller } from 'react-hook-form';
......@@ -15,25 +15,24 @@ interface Props {
}
const AddressVerificationFieldMessage = ({ formState, control }: Props) => {
const backgroundColor = useColorModeValue('white', 'gray.900');
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<Fields, 'message'>}) => {
const error = 'message' in formState.errors ? formState.errors.message : undefined;
return (
<FormControl variant="floating" id={ field.name } isRequired size="md" backgroundColor={ backgroundColor }>
<FormControl variant="floating" id={ field.name } isRequired size="md" bgColor="dialog_bg">
<Textarea
{ ...field }
required
isInvalid={ Boolean(error) }
isDisabled
isReadOnly
autoComplete="off"
maxH={{ base: '140px', lg: '80px' }}
bgColor="dialog_bg"
/>
<InputPlaceholder text="Message to sign" error={ error } isInModal/>
<InputPlaceholder text="Message to sign" error={ error }/>
</FormControl>
);
}, [ formState.errors, backgroundColor ]);
}, [ formState.errors ]);
return (
<Controller
......
import { FormControl, Input, useColorModeValue } from '@chakra-ui/react';
import { FormControl, Input } from '@chakra-ui/react';
import React from 'react';
import type { Control, ControllerRenderProps, FormState } from 'react-hook-form';
import { Controller } from 'react-hook-form';
......@@ -16,24 +16,23 @@ interface Props {
}
const AddressVerificationFieldSignature = ({ formState, control }: Props) => {
const backgroundColor = useColorModeValue('white', 'gray.900');
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<Fields, 'signature'>}) => {
const error = 'signature' in formState.errors ? formState.errors.signature : undefined;
return (
<FormControl variant="floating" id={ field.name } isRequired size="md" backgroundColor={ backgroundColor }>
<FormControl variant="floating" id={ field.name } isRequired size="md" bgColor="dialog_bg">
<Input
{ ...field }
required
isInvalid={ Boolean(error) }
isDisabled={ formState.isSubmitting }
autoComplete="off"
bgColor="dialog_bg"
/>
<InputPlaceholder text="Signature hash" error={ error }/>
</FormControl>
);
}, [ formState.errors, formState.isSubmitting, backgroundColor ]);
}, [ formState.errors, formState.isSubmitting ]);
return (
<Controller
......
......@@ -105,7 +105,7 @@ const AddressVerificationStepAddress = ({ defaultAddress, onContinue }: Props) =
{ rootError && <Alert status="warning" mt={ 3 }>{ rootError }</Alert> }
<AddressVerificationFieldAddress formState={ formState } control={ control }/>
<Flex alignItems={{ base: 'flex-start', lg: 'center' }} mt={ 8 } columnGap={ 5 } rowGap={ 2 } flexDir={{ base: 'column', lg: 'row' }}>
<Button size="lg" type="submit" isDisabled={ formState.isSubmitting } flexShrink={ 0 }>
<Button size="lg" type="submit" isLoading={ formState.isSubmitting } loadingText="Continue" flexShrink={ 0 }>
Continue
</Button>
<AdminSupportText/>
......
......@@ -4,7 +4,6 @@ import {
FormControl,
FormLabel,
Input,
useColorModeValue,
} from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback } from 'react';
......@@ -42,7 +41,6 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
});
const apiFetch = useApiFetch();
const queryClient = useQueryClient();
const formBackgroundColor = useColorModeValue('white', 'gray.900');
const updateApiKey = (data: Inputs) => {
const body = { name: data.name };
......@@ -102,25 +100,27 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
<FormControl variant="floating" id="address">
<Input
{ ...field }
isDisabled={ true }
bgColor="dialog_bg"
isReadOnly
/>
<FormLabel data-in-modal="true">Auto-generated API key token</FormLabel>
<FormLabel>Auto-generated API key token</FormLabel>
</FormControl>
);
}, []);
const renderNameInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'name'>}) => {
return (
<FormControl variant="floating" id="name" isRequired backgroundColor={ formBackgroundColor }>
<FormControl variant="floating" id="name" isRequired bgColor="dialog_bg">
<Input
{ ...field }
isInvalid={ Boolean(errors.name) }
maxLength={ NAME_MAX_LENGTH }
bgColor="dialog_bg"
/>
<InputPlaceholder text="Application name for API key (e.g Web3 project)" error={ errors.name }/>
</FormControl>
);
}, [ errors, formBackgroundColor ]);
}, [ errors ]);
return (
<form noValidate onSubmit={ handleSubmit(onSubmit) }>
......
......@@ -108,7 +108,6 @@ const BlobData = ({ data, isLoading, hash }: Props) => {
borderRadius="base"
value={ format }
onChange={ handleSelectChange }
focusBorderColor="none"
w="auto"
>
{ formats.map((format) => (
......
......@@ -4,7 +4,6 @@ import {
FormControl,
Input,
Textarea,
useColorModeValue,
} from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback } from 'react';
......@@ -61,8 +60,6 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
});
};
const formBackgroundColor = useColorModeValue('white', 'gray.900');
const mutation = useMutation({
mutationFn: customAbiKey,
onSuccess: (data) => {
......@@ -109,38 +106,40 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose, setAlertVisible }) => {
<AddressInput<Inputs, 'contract_address_hash'>
field={ field }
error={ errors.contract_address_hash }
backgroundColor={ formBackgroundColor }
bgColor="dialog_bg"
placeholder="Smart contract address (0x...)"
/>
);
}, [ errors, formBackgroundColor ]);
}, [ errors ]);
const renderNameInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'name'>}) => {
return (
<FormControl variant="floating" id="name" isRequired backgroundColor={ formBackgroundColor }>
<FormControl variant="floating" id="name" isRequired bgColor="dialog_bg">
<Input
{ ...field }
isInvalid={ Boolean(errors.name) }
maxLength={ NAME_MAX_LENGTH }
bgColor="dialog_bg"
/>
<InputPlaceholder text="Project name" error={ errors.name }/>
</FormControl>
);
}, [ errors, formBackgroundColor ]);
}, [ errors ]);
const renderAbiInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'abi'>}) => {
return (
<FormControl variant="floating" id="abi" isRequired backgroundColor={ formBackgroundColor }>
<FormControl variant="floating" id="abi" isRequired bgColor="dialog_bg">
<Textarea
{ ...field }
size="lg"
minH="300px"
isInvalid={ Boolean(errors.abi) }
bgColor="dialog_bg"
/>
<InputPlaceholder text="Custom ABI [{...}] (JSON format)" error={ errors.abi }/>
</FormControl>
);
}, [ errors, formBackgroundColor ]);
}, [ errors ]);
return (
<form noValidate onSubmit={ handleSubmit(onSubmit) }>
......
......@@ -27,7 +27,7 @@ const MyProfile = () => {
<FormControl variant="floating" id="name" isRequired size="lg">
<Input
required
disabled
readOnly
value={ data.name || '' }
/>
<FormLabel>Name</FormLabel>
......@@ -35,7 +35,7 @@ const MyProfile = () => {
<FormControl variant="floating" id="nickname" isRequired size="lg">
<Input
required
disabled
readOnly
value={ data.nickname || '' }
/>
<FormLabel>Nickname</FormLabel>
......@@ -43,7 +43,7 @@ const MyProfile = () => {
<FormControl variant="floating" id="email" isRequired size="lg">
<Input
required
disabled
readOnly
value={ data.email || '' }
/>
<FormLabel>Email</FormLabel>
......
import {
Box,
Button,
useColorModeValue,
} from '@chakra-ui/react';
import { useMutation } from '@tanstack/react-query';
import React, { useCallback, useState } from 'react';
......@@ -42,8 +41,6 @@ const AddressForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVisibl
},
});
const formBackgroundColor = useColorModeValue('white', 'gray.900');
const { mutate } = useMutation({
mutationFn: (formData: Inputs) => {
const body = {
......@@ -87,12 +84,12 @@ const AddressForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVisibl
};
const renderAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'address'>}) => {
return <AddressInput<Inputs, 'address'> field={ field } error={ errors.address } backgroundColor={ formBackgroundColor }/>;
}, [ errors, formBackgroundColor ]);
return <AddressInput<Inputs, 'address'> field={ field } error={ errors.address } bgColor="dialog_bg"/>;
}, [ errors ]);
const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => {
return <TagInput<Inputs, 'tag'> field={ field } error={ errors.tag } backgroundColor={ formBackgroundColor }/>;
}, [ errors, formBackgroundColor ]);
return <TagInput<Inputs, 'tag'> field={ field } error={ errors.tag } bgColor="dialog_bg"/>;
}, [ errors ]);
return (
<form noValidate onSubmit={ handleSubmit(onSubmit) }>
......
import {
Box,
Button,
useColorModeValue,
} from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback, useState } from 'react';
......@@ -34,7 +33,6 @@ type Inputs = {
const TransactionForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVisible }) => {
const [ pending, setPending ] = useState(false);
const formBackgroundColor = useColorModeValue('white', 'gray.900');
const { control, handleSubmit, formState: { errors, isDirty }, setError } = useForm<Inputs>({
mode: 'onTouched',
......@@ -90,12 +88,12 @@ const TransactionForm: React.FC<Props> = ({ data, onClose, onSuccess, setAlertVi
};
const renderTransactionInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'transaction'>}) => {
return <TransactionInput field={ field } error={ errors.transaction } backgroundColor={ formBackgroundColor }/>;
}, [ errors, formBackgroundColor ]);
return <TransactionInput field={ field } error={ errors.transaction } bgColor="dialog_bg"/>;
}, [ errors ]);
const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => {
return <TagInput<Inputs, 'tag'> field={ field } error={ errors.tag } backgroundColor={ formBackgroundColor }/>;
}, [ errors, formBackgroundColor ]);
return <TagInput<Inputs, 'tag'> field={ field } error={ errors.tag } bgColor="dialog_bg"/>;
}, [ errors ]);
return (
<form noValidate onSubmit={ handleSubmit(onSubmit) }>
......
......@@ -13,7 +13,7 @@ type Props<TInputs extends FieldValues, TInputName extends Path<TInputs>> = {
field: ControllerRenderProps<TInputs, TInputName>;
size?: InputProps['size'];
placeholder?: string;
backgroundColor?: string;
bgColor?: string;
error?: FieldError;
}
......@@ -23,14 +23,15 @@ export default function AddressInput<Inputs extends FieldValues, Name extends Pa
field,
size,
placeholder = 'Address (0x...)',
backgroundColor,
bgColor,
}: Props<Inputs, Name>) {
return (
<FormControl variant="floating" id="address" isRequired backgroundColor={ backgroundColor } size={ size }>
<FormControl variant="floating" id="address" isRequired size={ size } bgColor={ bgColor }>
<Input
{ ...field }
isInvalid={ Boolean(error) }
maxLength={ ADDRESS_LENGTH }
bgColor={ bgColor }
/>
<InputPlaceholder text={ placeholder } error={ error }/>
</FormControl>
......
......@@ -92,5 +92,20 @@ const defaultProps = {
await expect(component).toHaveScreenshot();
});
test('read-only', async({ mount }) => {
const component = await mount(
<TestApp>
<FancySelect
{ ...defaultProps }
size={ size }
value={ OPTIONS[0] }
isReadOnly
/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
});
});
......@@ -3,6 +3,9 @@ import type { Size, ChakraStylesConfig } from 'chakra-react-select';
import type { Option } from './types';
import theme from 'theme';
import getFormStyles from 'theme/utils/getFormStyles';
function getValueContainerStyles(size?: Size) {
switch (size) {
case 'sm':
......@@ -42,13 +45,12 @@ function getSingleValueStyles(size?: Size) {
}
const getChakraStyles: (colorMode: ColorMode) => ChakraStylesConfig<Option> = (colorMode) => {
const emptyInputBorderColor = colorMode === 'dark' ? 'gray.700' : 'gray.100';
const filledInputBorderColor = colorMode === 'dark' ? 'gray.600' : 'gray.300';
const formColor = getFormStyles({ colorMode, colorScheme: 'blue', theme });
return {
control: (provided, state) => ({
...provided,
borderColor: state.hasValue ? filledInputBorderColor : emptyInputBorderColor,
borderColor: state.hasValue ? formColor.input.filled.borderColor : formColor.input.empty.borderColor,
}),
inputContainer: (provided) => ({
...provided,
......
......@@ -6,12 +6,10 @@ interface Props {
text: string;
icon?: React.ReactNode;
error?: Partial<FieldError>;
className?: string;
isFancy?: boolean;
isInModal?: boolean;
}
const InputPlaceholder = ({ text, icon, error, className, isFancy, isInModal }: Props) => {
const InputPlaceholder = ({ text, icon, error, isFancy }: Props) => {
let errorMessage = error?.message;
if (!errorMessage && error?.type === 'pattern') {
......@@ -20,10 +18,10 @@ const InputPlaceholder = ({ text, icon, error, className, isFancy, isInModal }:
return (
<FormLabel
className={ className }
alignItems="center"
{ ...(isFancy ? { 'data-fancy': true } : {}) }
{ ...(isInModal ? { 'data-in-modal': true } : {}) }
variant="floating"
bgColor="deeppink"
>
{ icon }
<chakra.span>{ text }</chakra.span>
......@@ -32,4 +30,4 @@ const InputPlaceholder = ({ text, icon, error, className, isFancy, isInModal }:
);
};
export default chakra(InputPlaceholder);
export default React.memo(InputPlaceholder);
......@@ -11,13 +11,29 @@ interface Props {
rightSlot?: React.ReactNode;
beforeSlot?: React.ReactNode;
textareaMaxHeight?: string;
textareaMinHeight?: string;
showCopy?: boolean;
isLoading?: boolean;
contentProps?: ChakraProps;
}
const RawDataSnippet = ({ data, className, title, rightSlot, beforeSlot, textareaMaxHeight, showCopy = true, isLoading, contentProps }: Props) => {
// see issue in theme/components/Textarea.ts
const RawDataSnippet = ({
data,
className,
title,
rightSlot,
beforeSlot,
textareaMaxHeight,
textareaMinHeight,
showCopy = true,
isLoading,
contentProps,
}: Props) => {
// https://bugs.chromium.org/p/chromium/issues/detail?id=1362573
// there is a problem with scrollbar color in chromium
// so blackAlpha.50 here is replaced with #f5f5f6
// and whiteAlpha.50 is replaced with #1a1b1b
// const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
const bgColor = useColorModeValue('#f5f5f6', '#1a1b1b');
return (
<Box className={ className } as="section" title={ title }>
......@@ -33,7 +49,7 @@ const RawDataSnippet = ({ data, className, title, rightSlot, beforeSlot, textare
p={ 4 }
bgColor={ isLoading ? 'inherit' : bgColor }
maxH={ textareaMaxHeight || '400px' }
minH={ isLoading ? '200px' : undefined }
minH={ textareaMinHeight || (isLoading ? '200px' : undefined) }
fontSize="sm"
borderRadius="md"
wordBreak="break-all"
......
import { Box, Flex, Select, Textarea } from '@chakra-ui/react';
import { Select } from '@chakra-ui/react';
import React from 'react';
import hexToUtf8 from 'lib/hexToUtf8';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import RawDataSnippet from 'ui/shared/RawDataSnippet';
type DataType = 'Hex' | 'UTF-8'
const OPTIONS: Array<DataType> = [ 'Hex', 'UTF-8' ];
......@@ -18,24 +18,20 @@ const RawInputData = ({ hex }: Props) => {
setSelectedDataType(event.target.value as DataType);
}, []);
const select = (
<Select size="xs" borderRadius="base" value={ selectedDataType } onChange={ handleSelectChange } w="auto" mr="auto">
{ OPTIONS.map((option) => <option key={ option } value={ option }>{ option }</option>) }
</Select>
);
return (
<Box w="100%">
<Flex justifyContent="space-between" alignItems="center">
<Select size="xs" borderRadius="base" value={ selectedDataType } onChange={ handleSelectChange } focusBorderColor="none" w="auto">
{ OPTIONS.map((option) => <option key={ option } value={ option }>{ option }</option>) }
</Select>
<CopyToClipboard text={ hex }/>
</Flex>
<Textarea
value={ selectedDataType === 'Hex' ? hex : hexToUtf8(hex) }
w="100%"
maxH="220px"
mt={ 2 }
p={ 4 }
variant="filledInactive"
fontSize="sm"
/>
</Box>
<RawDataSnippet
data={ selectedDataType === 'Hex' ? hex : hexToUtf8(hex) }
rightSlot={ select }
textareaMaxHeight="220px"
textareaMinHeight="160px"
w="100%"
/>
);
};
......
......@@ -12,16 +12,17 @@ const TAG_MAX_LENGTH = 35;
type Props<TInputs extends FieldValues, TInputName extends Path<TInputs>> = {
field: ControllerRenderProps<TInputs, TInputName>;
error?: FieldError;
backgroundColor?: string;
bgColor?: string;
}
function TagInput<Inputs extends FieldValues, Name extends Path<Inputs>>({ field, error, backgroundColor }: Props<Inputs, Name>) {
function TagInput<Inputs extends FieldValues, Name extends Path<Inputs>>({ field, error, bgColor }: Props<Inputs, Name>) {
return (
<FormControl variant="floating" id="tag" isRequired backgroundColor={ backgroundColor }>
<FormControl variant="floating" id="tag" isRequired bgColor={ bgColor }>
<Input
{ ...field }
isInvalid={ Boolean(error) }
maxLength={ TAG_MAX_LENGTH }
bgColor={ bgColor }
/>
<InputPlaceholder text="Private tag (max 35 characters)" error={ error }/>
</FormControl>
......
......@@ -11,16 +11,17 @@ import InputPlaceholder from 'ui/shared/InputPlaceholder';
type Props<Field> = {
field: Field;
error?: FieldError;
backgroundColor?: string;
bgColor?: string;
}
function TransactionInput<Field extends Partial<ControllerRenderProps<FieldValues, 'transaction'>>>({ field, error, backgroundColor }: Props<Field>) {
function TransactionInput<Field extends Partial<ControllerRenderProps<FieldValues, 'transaction'>>>({ field, error, bgColor }: Props<Field>) {
return (
<FormControl variant="floating" id="transaction" isRequired backgroundColor={ backgroundColor }>
<FormControl variant="floating" id="transaction" isRequired bgColor={ bgColor }>
<Input
{ ...field }
isInvalid={ Boolean(error) }
maxLength={ TRANSACTION_HASH_LENGTH }
bgColor={ bgColor }
/>
<InputPlaceholder text="Transaction hash (0x...)" error={ error }/>
</FormControl>
......
......@@ -46,7 +46,7 @@ const NetworkMenuContentMobile = ({ items, tabs }: Props) => {
) : (
<>
{ tabs.length > 1 && (
<Select size="xs" borderRadius="base" value={ selectedTab } onChange={ handleSelectChange } focusBorderColor="none" mb={ 6 }>
<Select size="xs" borderRadius="base" value={ selectedTab } onChange={ handleSelectChange } mb={ 6 }>
{ tabs.map((tab) => <option key={ tab } value={ tab }>{ capitalize(tab) }</option>) }
</Select>
) }
......
......@@ -18,7 +18,7 @@ const TokenInfoFieldAddress = ({ control }: Props) => {
<Input
{ ...field }
required
isDisabled
isReadOnly
/>
<InputPlaceholder text="Token contract address"/>
</FormControl>
......
......@@ -19,7 +19,8 @@ const TokenInfoFieldComment = ({ control, isReadOnly }: Props) => {
<Textarea
{ ...field }
isInvalid={ Boolean(fieldState.error) }
isDisabled={ formState.isSubmitting || isReadOnly }
isDisabled={ formState.isSubmitting }
isReadOnly={ isReadOnly }
autoComplete="off"
maxH="160px"
maxLength={ 300 }
......
......@@ -20,7 +20,8 @@ const TokenInfoFieldDocs = ({ control, isReadOnly }: Props) => {
<Input
{ ...field }
isInvalid={ Boolean(fieldState.error) }
isDisabled={ formState.isSubmitting || isReadOnly }
isDisabled={ formState.isSubmitting }
isReadOnly={ isReadOnly }
autoComplete="off"
/>
<InputPlaceholder text="Docs" error={ fieldState.error }/>
......
......@@ -58,7 +58,8 @@ const TokenInfoFieldIconUrl = ({ control, isReadOnly, trigger }: Props) => {
{ ...field }
onBlur={ handleBlur }
isInvalid={ Boolean(fieldState.error) }
isDisabled={ formState.isSubmitting || isReadOnly }
isDisabled={ formState.isSubmitting }
isReadOnly={ isReadOnly }
autoComplete="off"
required
/>
......
......@@ -22,7 +22,8 @@ const TokenInfoFieldPriceTicker = ({ control, isReadOnly, name, label }: Props)
<Input
{ ...field }
isInvalid={ Boolean(fieldState.error) }
isDisabled={ formState.isSubmitting || isReadOnly }
isDisabled={ formState.isSubmitting }
isReadOnly={ isReadOnly }
autoComplete="off"
/>
<InputPlaceholder text={ label } error={ fieldState.error }/>
......
......@@ -20,7 +20,8 @@ const TokenInfoFieldProjectDescription = ({ control, isReadOnly }: Props) => {
{ ...field }
required
isInvalid={ Boolean(fieldState.error) }
isDisabled={ formState.isSubmitting || isReadOnly }
isDisabled={ formState.isSubmitting }
isReadOnly={ isReadOnly }
autoComplete="off"
maxH="160px"
maxLength={ 300 }
......
......@@ -21,7 +21,8 @@ const TokenInfoFieldProjectEmail = ({ control, isReadOnly }: Props) => {
{ ...field }
required
isInvalid={ Boolean(fieldState.error) }
isDisabled={ formState.isSubmitting || isReadOnly }
isDisabled={ formState.isSubmitting }
isReadOnly={ isReadOnly }
autoComplete="off"
/>
<InputPlaceholder text="Official project email address" error={ fieldState.error }/>
......
......@@ -20,7 +20,8 @@ const TokenInfoFieldProjectName = ({ control, isReadOnly }: Props) => {
<Input
{ ...field }
isInvalid={ Boolean(fieldState.error) }
isDisabled={ formState.isSubmitting || isReadOnly }
isDisabled={ formState.isSubmitting }
isReadOnly={ isReadOnly }
autoComplete="off"
/>
<InputPlaceholder text="Project name" error={ fieldState.error }/>
......
......@@ -29,7 +29,8 @@ const TokenInfoFieldProjectSector = ({ control, isReadOnly, config }: Props) =>
options={ options }
size={ isMobile ? 'md' : 'lg' }
placeholder="Project industry"
isDisabled={ formState.isSubmitting || isReadOnly }
isDisabled={ formState.isSubmitting }
isReadOnly={ isReadOnly }
error={ fieldState.error }
/>
);
......
......@@ -20,7 +20,8 @@ const TokenInfoFieldProjectWebsite = ({ control, isReadOnly }: Props) => {
<Input
{ ...field }
isInvalid={ Boolean(fieldState.error) }
isDisabled={ formState.isSubmitting || isReadOnly }
isDisabled={ formState.isSubmitting }
isReadOnly={ isReadOnly }
autoComplete="off"
required
/>
......
......@@ -21,7 +21,8 @@ const TokenInfoFieldRequesterEmail = ({ control, isReadOnly }: Props) => {
{ ...field }
required
isInvalid={ Boolean(fieldState.error) }
isDisabled={ formState.isSubmitting || isReadOnly }
isDisabled={ formState.isSubmitting }
isReadOnly={ isReadOnly }
autoComplete="off"
/>
<InputPlaceholder text="Requester email" error={ fieldState.error }/>
......
......@@ -20,7 +20,8 @@ const TokenInfoFieldRequesterName = ({ control, isReadOnly }: Props) => {
{ ...field }
required
isInvalid={ Boolean(fieldState.error) }
isDisabled={ formState.isSubmitting || isReadOnly }
isDisabled={ formState.isSubmitting }
isReadOnly={ isReadOnly }
autoComplete="off"
/>
<InputPlaceholder text="Requester name" error={ fieldState.error }/>
......
......@@ -42,7 +42,8 @@ const TokenInfoFieldSocialLink = ({ control, isReadOnly, name }: Props) => {
<Input
{ ...field }
isInvalid={ Boolean(fieldState.error) }
isDisabled={ formState.isSubmitting || isReadOnly }
isDisabled={ formState.isSubmitting }
isReadOnly={ isReadOnly }
autoComplete="off"
/>
<InputPlaceholder text={ SETTINGS[name].label } error={ fieldState.error }/>
......
......@@ -22,7 +22,8 @@ const TokenInfoFieldSupport = ({ control, isReadOnly }: Props) => {
<Input
{ ...field }
isInvalid={ Boolean(fieldState.error) }
isDisabled={ formState.isSubmitting || isReadOnly }
isDisabled={ formState.isSubmitting }
isReadOnly={ isReadOnly }
autoComplete="off"
/>
<InputPlaceholder text="Support URL or email" error={ fieldState.error }/>
......
......@@ -18,7 +18,7 @@ const TokenInfoFieldTokenName = ({ control }: Props) => {
<Input
{ ...field }
required
isDisabled
isReadOnly
/>
<InputPlaceholder text="Token name"/>
</FormControl>
......
......@@ -39,7 +39,7 @@ const TokenInstanceMetadata = ({ data, isPlaceholderData }: Props) => {
<Box>
<Flex alignItems="center" mb={ 6 }>
<chakra.span fontWeight={ 500 }>Metadata</chakra.span>
<Select size="xs" borderRadius="base" value={ format } onChange={ handleSelectChange } focusBorderColor="none" w="auto" ml={ 5 }>
<Select size="xs" borderRadius="base" value={ format } onChange={ handleSelectChange } w="auto" ml={ 5 }>
<option value="Table">Table</option>
<option value="JSON">JSON</option>
</Select>
......
......@@ -2,7 +2,6 @@ import {
Box,
Button,
Text,
useColorModeValue,
} from '@chakra-ui/react';
import { useMutation } from '@tanstack/react-query';
import React, { useCallback, useState } from 'react';
......@@ -69,7 +68,6 @@ type Checkboxes = 'notification' |
const AddressForm: React.FC<Props> = ({ data, onSuccess, setAlertVisible, isAdd }) => {
const [ pending, setPending ] = useState(false);
const formBackgroundColor = useColorModeValue('white', 'gray.900');
let notificationsDefault = {} as Inputs['notification_settings'];
if (!data?.notification_settings) {
......@@ -142,15 +140,15 @@ const AddressForm: React.FC<Props> = ({ data, onSuccess, setAlertVisible, isAdd
return (
<AddressInput<Inputs, 'address'>
field={ field }
backgroundColor={ formBackgroundColor }
bgColor="dialog_bg"
error={ errors.address }
/>
);
}, [ errors, formBackgroundColor ]);
}, [ errors ]);
const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => {
return <TagInput<Inputs, 'tag'> field={ field } error={ errors.tag } backgroundColor={ formBackgroundColor }/>;
}, [ errors, formBackgroundColor ]);
return <TagInput<Inputs, 'tag'> field={ field } error={ errors.tag } bgColor="dialog_bg"/>;
}, [ errors ]);
// eslint-disable-next-line react/display-name
const renderCheckbox = useCallback((text: string) => ({ field }: {field: ControllerRenderProps<Inputs, Checkboxes>}) => (
......
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