diff --git a/ui/advancedFilter/FilterByColumn.pw.tsx b/ui/advancedFilter/FilterByColumn.pw.tsx index 55942b7b18..04dd3d057e 100644 --- a/ui/advancedFilter/FilterByColumn.pw.tsx +++ b/ui/advancedFilter/FilterByColumn.pw.tsx @@ -22,8 +22,8 @@ const filters = { methods: [ '0xa9059cbb' ], age: '7d' as const, address_relation: 'or' as const, - from_address_hashes_to_include: [ '0x123' ], - to_address_hashes_to_include: [ '0x456' ], + from_address_hashes_to_include: [ '0x1230000000000000000000000000000000000000' ], + to_address_hashes_to_include: [ '0x4560000000000000000000000000000000000000' ], amount_from: '100', token_contract_symbols_to_include: [ 'ETH' ], token_contract_address_hashes_to_include: [ 'native' ], diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_from-filter-dark-mode-1.png b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_from-filter-dark-mode-1.png index 8049106c8c..02f426450a 100644 Binary files a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_from-filter-dark-mode-1.png and b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_from-filter-dark-mode-1.png differ diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_to-filter-dark-mode-1.png b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_to-filter-dark-mode-1.png index 0549b966d3..aaa7c6002f 100644 Binary files a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_to-filter-dark-mode-1.png and b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_dark-color-mode_to-filter-dark-mode-1.png differ diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_from-filter-dark-mode-1.png b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_from-filter-dark-mode-1.png index 9778ae0bb6..71dc696f32 100644 Binary files a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_from-filter-dark-mode-1.png and b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_from-filter-dark-mode-1.png differ diff --git a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_to-filter-dark-mode-1.png b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_to-filter-dark-mode-1.png index 6cb64976c9..79e0bf7b30 100644 Binary files a/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_to-filter-dark-mode-1.png and b/ui/advancedFilter/__screenshots__/FilterByColumn.pw.tsx_default_to-filter-dark-mode-1.png differ diff --git a/ui/advancedFilter/filters/AddressFilter.tsx b/ui/advancedFilter/filters/AddressFilter.tsx index 5f0542794a..ad1ceb853d 100644 --- a/ui/advancedFilter/filters/AddressFilter.tsx +++ b/ui/advancedFilter/filters/AddressFilter.tsx @@ -5,11 +5,13 @@ import React from 'react'; import type { AdvancedFilterParams } from 'types/api/advancedFilter'; +import { Field } from 'toolkit/chakra/field'; import { Input } from 'toolkit/chakra/input'; import { InputGroup } from 'toolkit/chakra/input-group'; import { Select } from 'toolkit/chakra/select'; import AddButton from 'toolkit/components/buttons/AddButton'; import { ClearButton } from 'toolkit/components/buttons/ClearButton'; +import { ADDRESS_REGEXP } from 'toolkit/utils/regexp'; import TableColumnFilter from 'ui/shared/filters/TableColumnFilter'; const FILTER_PARAM_TO_INCLUDE = 'to_address_hashes_to_include'; @@ -42,8 +44,10 @@ type InputProps = { isLast: boolean; onModeChange: ({ value }: { value: Array }) => void; onChange: (event: ChangeEvent) => void; + onBlur: () => void; onClear: () => void; onAddFieldClick: () => void; + isInvalid: boolean; }; type AddressFilter = { @@ -55,9 +59,9 @@ function addressFilterToKey(filter: AddressFilter) { return `${ filter.address.toLowerCase() }-${ filter.mode }`; } -const AddressFilterInput = ({ address, mode, onModeChange, onChange, onClear, isLast, onAddFieldClick }: InputProps) => { +const AddressFilterInput = ({ address, mode, onModeChange, onChange, onBlur, onClear, isLast, onAddFieldClick, isInvalid }: InputProps) => { return ( - + - + } + > + + + { isLast && ( { const [ currentValue, setCurrentValue ] = React.useState>([ ...value, emptyItem ]); + const [ touched, setTouched ] = React.useState>(value.map(() => true).concat(false)); const handleModeSelectChange = React.useCallback((index: number) => ({ value }: { value: Array }) => { setCurrentValue(prev => { @@ -103,6 +113,11 @@ const AddressFilter = ({ type, value = [], handleFilterChange }: Props) => { newVal[index] = { ...newVal[index], address: '' }; return newVal; }); + setTouched(prev => { + const newTouched = [ ...prev ]; + newTouched[index] = false; + return newTouched; + }); }, []); const handleAddressChange = React.useCallback((index: number) => (event: React.ChangeEvent) => { @@ -115,11 +130,23 @@ const AddressFilter = ({ type, value = [], handleFilterChange }: Props) => { }); }, []); + const handleAddressBlur = React.useCallback((index: number) => () => { + setTouched(prev => { + const newTouched = [ ...prev ]; + newTouched[index] = true; + return newTouched; + }); + }, []); + const onAddFieldClick = React.useCallback(() => { setCurrentValue(prev => [ ...prev, emptyItem ]); + setTouched(prev => [ ...prev, false ]); }, []); - const onReset = React.useCallback(() => setCurrentValue([ emptyItem ]), []); + const onReset = React.useCallback(() => { + setCurrentValue([ emptyItem ]); + setTouched([ false ]); + }, []); const onFilter = React.useCallback(() => { const includeFilterParam = type === 'from' ? FILTER_PARAM_FROM_INCLUDE : FILTER_PARAM_TO_INCLUDE; @@ -131,11 +158,14 @@ const AddressFilter = ({ type, value = [], handleFilterChange }: Props) => { handleFilterChange(excludeFilterParam, excludeValue.length ? excludeValue : undefined); }, [ handleFilterChange, currentValue, type ]); + const hasErrors = currentValue.some(i => Boolean(i.address) && !ADDRESS_REGEXP.test(i.address)); + const isTouched = !isEqual(currentValue.filter(i => i.address).map(addressFilterToKey).sort(), value.map(addressFilterToKey).sort()); + return ( i.address).map(addressFilterToKey).sort(), value.map(addressFilterToKey).sort()) } + isTouched={ isTouched && !hasErrors } onFilter={ onFilter } onReset={ onReset } hasReset @@ -149,8 +179,10 @@ const AddressFilter = ({ type, value = [], handleFilterChange }: Props) => { isLast={ index === currentValue.length - 1 } onModeChange={ handleModeSelectChange(index) } onChange={ handleAddressChange(index) } + onBlur={ handleAddressBlur(index) } onClear={ handleAddressClear(index) } onAddFieldClick={ onAddFieldClick } + isInvalid={ Boolean(touched[index]) && Boolean(item.address) && !ADDRESS_REGEXP.test(item.address) } /> )) }