diff --git a/.changelog/689.trivial.md b/.changelog/689.trivial.md new file mode 100644 index 000000000..85bd19452 --- /dev/null +++ b/.changelog/689.trivial.md @@ -0,0 +1 @@ +Tweak the search field design and behavior diff --git a/src/app/components/Search/index.tsx b/src/app/components/Search/index.tsx index a5606627f..46bf7edb6 100644 --- a/src/app/components/Search/index.tsx +++ b/src/app/components/Search/index.tsx @@ -11,6 +11,7 @@ import { RouteUtils } from '../../utils/route-utils' import { useScreenSize } from '../../hooks/useScreensize' import HighlightOffIcon from '@mui/icons-material/HighlightOff' import ErrorIcon from '@mui/icons-material/Error' +import WarningIcon from '@mui/icons-material/Warning' import IconButton from '@mui/material/IconButton' import { SearchSuggestionsButtons } from './SearchSuggestionsButtons' import { formHelperTextClasses } from '@mui/material/FormHelperText' @@ -19,6 +20,7 @@ import { SearchScope } from '../../../types/searchScope' import { textSearchMininumLength } from './search-utils' import Typography from '@mui/material/Typography' import { isValidBlockHeight } from '../../utils/helpers' +import { typingDelay } from '../../../styles/theme' import { isValidMnemonic } from '../../utils/helpers' export type SearchVariant = 'button' | 'icon' | 'expandable' @@ -105,6 +107,7 @@ const SearchCmp: FC = ({ scope, variant, disabled, onFocusChange: o const [value, setValue] = useState('') const [isFocused, setIsFocused] = useState(false) const valueInSearchParams = useSearchParams()[0].get('q') ?? '' + const [isTyping, setIsTyping] = useState(false) const wordsOfPower = t('search.wordsOfPower') const hasWordsOfPower = value.trim().toLowerCase().startsWith(wordsOfPower.toLowerCase()) @@ -124,6 +127,7 @@ const SearchCmp: FC = ({ scope, variant, disabled, onFocusChange: o const onChange = (newValue: string) => { setValue(newValue) + setIsTyping(true) } const onFocusChange = (value: boolean) => { @@ -154,13 +158,22 @@ const SearchCmp: FC = ({ scope, variant, disabled, onFocusChange: o const searchButtonContent = variant !== 'button' ? : t('search.searchBtnText') - const errorMessage = isTooShort - ? t('search.error.tooShort') - : hasPrivacyProblem + const errorMessage = isTooShort ? t('search.error.tooShort') : undefined + const hasError = !!errorMessage + + const warningMessage = hasPrivacyProblem ? t('search.error.privacy', { appName: t('appName'), wordsOfPower }) : undefined + const hasWarning = !!warningMessage - const hasError = !!errorMessage + const hasProblem = hasError || hasWarning + + useEffect(() => { + const timeout = setTimeout(() => { + setIsTyping(false) + }, typingDelay) + return () => clearTimeout(timeout) + }, [value]) return ( = ({ scope, variant, disabled, onFocusChange: o aria-label={searchPlaceholderTranslated} > onChange(e.target.value)} - error={hasError} + error={!isTyping && hasProblem} onFocus={() => onFocusChange(true)} onBlur={() => onFocusChange(false)} InputProps={{ @@ -183,12 +205,12 @@ const SearchCmp: FC = ({ scope, variant, disabled, onFocusChange: o endAdornment: ( <> - {variant === 'icon' && value && ( + {value && ( )} - + {searchButtonContent} @@ -208,35 +230,56 @@ const SearchCmp: FC = ({ scope, variant, disabled, onFocusChange: o helperText={ value && value !== valueInSearchParams && ( -
- {hasError && ( + <> + {!isTyping && hasError && ( <> - -   + {errorMessage}
)} + {!isTyping && hasWarning && ( + <> + + + {warningMessage} + +
+ + )} { setValue(suggestion) }} /> -
+ ) } /> diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index d0bea5ae9..dfda3214e 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -374,10 +374,10 @@ "soon": "soon" }, "search": { - "placeholder": "Address, Block, Contract, Txn Hash, Transaction ID, Token name, etc", + "placeholder": "Address, Block, Contract, Transaction hash, Token name, etc.", "error": { "tooShort": "Please enter either at least 3 characters or a number in order to perform a search.", - "privacy": "It seems like you might accidentally entered a keyphrase for a wallet. Please note that your mnemonic is a secret key that should never be shared, even not with our {{ pagetitle }}.\nExecuting this search is highly unlikely to return any results. If you want to proceed nonetheless, please add “{{ wordsOfPower }}” in front of your search query to perform the search at your own risk." + "privacy": "It seems like you might accidentally entered a keyphrase for a wallet. Please note that your mnemonic is a secret key that should never be shared, even not with our {{ appName }}.\nExecuting this search is highly unlikely to return any results. If you want to proceed nonetheless, please add “{{ wordsOfPower }}” in front of your search query to perform the search at your own risk." }, "mobilePlaceholder": "Search Address, Block, Txn, Token, etc", "noResults": { @@ -413,7 +413,7 @@ "moreCount_other": "{{ count }} more results" }, "searchBtnText": "Search", - "searchSuggestions": "Not sure what to look for? Try out a search: Block, Transaction, Address, Token ", + "searchSuggestions": "Not sure what to look for? Try out a search: Block, Transaction, Address, Token .", "sectionHeader": "Results on {{ scope }}", "wordsOfPower": "I COMMAND THEE TO SEARCH FOR" } diff --git a/src/styles/theme/defaultTheme.ts b/src/styles/theme/defaultTheme.ts index 29c6b0b59..a050b7b66 100644 --- a/src/styles/theme/defaultTheme.ts +++ b/src/styles/theme/defaultTheme.ts @@ -654,9 +654,6 @@ export const defaultTheme = createTheme({ [`&.${outlinedInputClasses.focused} .${outlinedInputClasses.notchedOutline}`]: { borderWidth: '3px', }, - [`&.${outlinedInputClasses.error} .${outlinedInputClasses.notchedOutline}`]: { - borderWidth: '1px !important', - }, }, notchedOutline: { borderColor: 'transparent', diff --git a/src/styles/theme/index.ts b/src/styles/theme/index.ts index d3dae5a8f..ff2cec728 100644 --- a/src/styles/theme/index.ts +++ b/src/styles/theme/index.ts @@ -7,6 +7,7 @@ export { defaultTheme } from './defaultTheme' export { testnetTheme } from './testnet/theme' export const tooltipDelay = 500 +export const typingDelay = 1000 export const getThemesForNetworks: () => Record = () => ({ [Network.mainnet]: defaultTheme,