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

Update styles of outlined buttons and more (#1978)

* selected state for Button

* tests

* fix menu filter button hover issue

* update screenshots

* try to fix SearchBar recent keywords test
parent cf1caf7f
import { Button } from '@chakra-ui/react'; import { Box, Button, Flex } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { test, expect } from 'playwright/lib'; import { test, expect } from 'playwright/lib';
test.use({ viewport: { width: 150, height: 350 } });
[ [
{ variant: 'solid' }, { variant: 'solid', states: [ 'default', 'disabled', 'hovered', 'active' ] },
{ variant: 'solid', colorScheme: 'gray', withDarkMode: true }, { variant: 'outline', colorScheme: 'gray', withDarkMode: true, states: [ 'default', 'disabled', 'hovered', 'active', 'selected' ] },
{ variant: 'outline', colorScheme: 'gray', withDarkMode: true }, { variant: 'outline', colorScheme: 'blue', withDarkMode: true, states: [ 'default', 'disabled', 'hovered', 'active', 'selected' ] },
{ variant: 'outline', colorScheme: 'gray-dark', withDarkMode: true }, { variant: 'simple', withDarkMode: true, states: [ 'default', 'hovered' ] },
{ variant: 'outline', colorScheme: 'blue', withDarkMode: true }, { variant: 'ghost', withDarkMode: true, states: [ 'default', 'hovered', 'active' ] },
{ variant: 'simple', withDarkMode: true }, { variant: 'subtle', states: [ 'default', 'hovered' ] },
{ variant: 'ghost', withDarkMode: true }, { variant: 'subtle', colorScheme: 'gray', states: [ 'default', 'hovered' ], withDarkMode: true },
{ variant: 'subtle' }, ].forEach(({ variant, colorScheme, withDarkMode, states }) => {
{ variant: 'subtle', colorScheme: 'gray', withDarkMode: true },
].forEach(({ variant, colorScheme, withDarkMode }) => {
test.describe(`variant ${ variant }${ colorScheme ? ` with ${ colorScheme } color scheme` : '' }${ withDarkMode ? ' +@dark-mode' : '' }`, () => { test.describe(`variant ${ variant }${ colorScheme ? ` with ${ colorScheme } color scheme` : '' }${ withDarkMode ? ' +@dark-mode' : '' }`, () => {
test('base', async({ render }) => { test('', async({ render }) => {
const component = await render(<Button variant={ variant } colorScheme={ colorScheme }>Click me</Button>); const component = await render(
await expect(component.locator('button')).toHaveScreenshot(); <Flex p={ 2 } flexDir="column" rowGap={ 3 }>
}); { states?.map((state) => {
switch (state) {
test('disabled', async({ render }) => { case 'default': {
const component = await render(<Button variant={ variant } colorScheme={ colorScheme } isDisabled>Click me</Button>); return (
await expect(component.locator('button')).toHaveScreenshot(); <Box>
}); <Box color="text_secondary" fontSize="sm">Default:</Box>
<Button variant={ variant } colorScheme={ colorScheme }>Click me</Button>
test('hovered', async({ render }) => { </Box>
const component = await render(<Button variant={ variant } colorScheme={ colorScheme }>Click me</Button>); );
await component.getByText(/click/i).hover(); }
await expect(component.locator('button')).toHaveScreenshot(); case 'disabled': {
}); return (
<Box>
<Box color="text_secondary" fontSize="sm">Disabled:</Box>
<Button variant={ variant } colorScheme={ colorScheme } isDisabled>Click me</Button>
</Box>
);
}
case 'active': {
return (
<Box>
<Box color="text_secondary" fontSize="sm">Active:</Box>
<Button variant={ variant } colorScheme={ colorScheme } isActive>Click me</Button>
</Box>
);
}
case 'hovered': {
return (
<Box>
<Box color="text_secondary" fontSize="sm">Hovered:</Box>
<Button variant={ variant } colorScheme={ colorScheme }>Hover me</Button>
</Box>
);
}
case 'selected': {
return (
<Box>
<Box color="text_secondary" fontSize="sm">Selected:</Box>
<Button variant={ variant } colorScheme={ colorScheme } data-selected="true">Click me</Button>
</Box>
);
}
test('active', async({ render }) => { default: {
const component = await render(<Button variant={ variant } colorScheme={ colorScheme } isActive>Click me</Button>); return null;
await expect(component.locator('button')).toHaveScreenshot(); }
}
}) }
</Flex>,
);
await component.getByText('Hover me').hover();
await expect(component).toHaveScreenshot();
}); });
}); });
}); });
...@@ -5,25 +5,10 @@ import { runIfFn } from '@chakra-ui/utils'; ...@@ -5,25 +5,10 @@ import { runIfFn } from '@chakra-ui/utils';
const variantSolid = defineStyle((props) => { const variantSolid = defineStyle((props) => {
const { colorScheme: c } = props; const { colorScheme: c } = props;
if (c === 'gray') {
const bg = mode(`gray.100`, `whiteAlpha.200`)(props);
return {
bg,
_hover: {
bg: mode(`gray.200`, `whiteAlpha.300`)(props),
_disabled: {
bg,
},
},
_active: { bg: mode(`gray.300`, `whiteAlpha.400`)(props) },
};
}
const bg = `${ c }.600`; const bg = `${ c }.600`;
const color = 'white'; const color = 'white';
const hoverBg = `${ c }.400`; const hoverBg = `${ c }.400`;
const activeBg = `${ c }.700`; const activeBg = hoverBg;
return { return {
bg, bg,
...@@ -37,6 +22,8 @@ const variantSolid = defineStyle((props) => { ...@@ -37,6 +22,8 @@ const variantSolid = defineStyle((props) => {
_disabled: { _disabled: {
opacity: 0.2, opacity: 0.2,
}, },
// According to design there is no "active" or "pressed" state
// It is simply should be the same as the "hover" state
_active: { bg: activeBg }, _active: { bg: activeBg },
fontWeight: 600, fontWeight: 600,
}; };
...@@ -45,22 +32,15 @@ const variantSolid = defineStyle((props) => { ...@@ -45,22 +32,15 @@ const variantSolid = defineStyle((props) => {
const variantOutline = defineStyle((props) => { const variantOutline = defineStyle((props) => {
const { colorScheme: c } = props; const { colorScheme: c } = props;
const isGrayTheme = c === 'gray' || c === 'gray-dark'; const isGrayTheme = c === 'gray';
const bg = 'transparent';
const color = isGrayTheme ? mode('blackAlpha.800', 'whiteAlpha.800')(props) : mode(`${ c }.600`, `${ c }.300`)(props); const color = isGrayTheme ? mode('blackAlpha.800', 'whiteAlpha.800')(props) : mode(`${ c }.600`, `${ c }.300`)(props);
const borderColor = isGrayTheme ? mode('gray.200', 'gray.600')(props) : mode(`${ c }.600`, `${ c }.300`)(props); const borderColor = isGrayTheme ? mode('gray.200', 'gray.600')(props) : mode(`${ c }.600`, `${ c }.300`)(props);
const activeBg = isGrayTheme ? mode('blue.50', 'gray.600')(props) : mode(`${ c }.50`, 'gray.600')(props);
const activeColor = (() => { const selectedBg = isGrayTheme ? mode('blue.50', 'gray.600')(props) : mode(`${ c }.50`, 'gray.600')(props);
if (c === 'gray') { const selectedColor = mode('blue.600', 'gray.50')(props);
return mode('blue.600', 'gray.50')(props);
}
if (c === 'gray-dark') {
return mode('blue.600', 'gray.50')(props);
}
if (c === 'blue') {
return mode('blue.600', 'gray.50')(props);
}
return 'blue.600';
})();
return { return {
color, color,
...@@ -68,42 +48,47 @@ const variantOutline = defineStyle((props) => { ...@@ -68,42 +48,47 @@ const variantOutline = defineStyle((props) => {
borderWidth: props.borderWidth || '2px', borderWidth: props.borderWidth || '2px',
borderStyle: 'solid', borderStyle: 'solid',
borderColor, borderColor,
bg: 'transparent', bg,
_hover: { _hover: {
color: 'link_hovered', color: 'link_hovered',
borderColor: 'link_hovered', borderColor: 'link_hovered',
bg: 'transparent', bg,
_active: { span: {
bg: props.isActive ? activeBg : 'transparent', color: 'link_hovered',
borderColor: props.isActive ? activeBg : 'link_hovered',
color: props.isActive ? activeColor : 'link_hovered',
p: {
color: 'link_hovered',
},
}, },
_disabled: { _disabled: {
color, color,
borderColor, borderColor,
}, },
p: {
color: 'link_hovered',
},
}, },
_disabled: { _disabled: {
opacity: 0.2, opacity: 0.2,
}, },
// According to design there is no "active" or "pressed" state
// It is simply should be the same as the "hover" state
_active: { _active: {
bg: activeBg, color: 'link_hovered',
borderColor: activeBg, borderColor: 'link_hovered',
color: activeColor, bg,
_disabled: { span: {
color, color: 'link_hovered',
borderColor,
}, },
p: { _disabled: {
color: activeColor, color: 'link_hovered',
borderColor: 'link_hovered',
}, },
}, },
// We have a special state for this button variant that serves as a popover trigger.
// When any items (filters) are selected in the popover, the button should change its background and text color.
// The last CSS selector is for redefining styles for the TabList component.
[`
&[data-selected=true],
&[data-selected=true][aria-selected=true]
`]: {
bg: selectedBg,
color: selectedColor,
borderColor: selectedBg,
},
}; };
}); });
......
...@@ -61,7 +61,7 @@ const AddressAccountHistory = ({ scrollRef, shouldRender = true }: Props) => { ...@@ -61,7 +61,7 @@ const AddressAccountHistory = ({ scrollRef, shouldRender = true }: Props) => {
<AccountHistoryFilter <AccountHistoryFilter
defaultFilter={ filterValue } defaultFilter={ filterValue }
onFilterChange={ handleFilterChange } onFilterChange={ handleFilterChange }
isActive={ Boolean(filterValue) } hasActiveFilter={ Boolean(filterValue) }
isLoading={ pagination.isLoading } isLoading={ pagination.isLoading }
/> />
......
import {
Menu,
MenuButton,
MenuList,
MenuOptionGroup,
MenuItemOption,
useDisclosure,
} from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { NovesHistoryFilterValue } from 'types/api/noves'; import type { NovesHistoryFilterValue } from 'types/api/noves';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import FilterButton from 'ui/shared/filters/FilterButton'; import PopoverFilterRadio from 'ui/shared/filters/PopoverFilterRadio';
const OPTIONS = [
{ value: 'all', label: 'All' },
{ value: 'received', label: 'Received from' },
{ value: 'sent', label: 'Sent to' },
];
interface Props { interface Props {
isActive: boolean; hasActiveFilter: boolean;
defaultFilter: NovesHistoryFilterValue; defaultFilter: NovesHistoryFilterValue;
onFilterChange: (nextValue: string | Array<string>) => void; onFilterChange: (nextValue: string | Array<string>) => void;
isLoading?: boolean; isLoading?: boolean;
} }
const AccountHistoryFilter = ({ onFilterChange, defaultFilter, isActive, isLoading }: Props) => { const AccountHistoryFilter = ({ onFilterChange, defaultFilter, hasActiveFilter, isLoading }: Props) => {
const { isOpen, onToggle } = useDisclosure();
const isInitialLoading = useIsInitialLoading(isLoading); const isInitialLoading = useIsInitialLoading(isLoading);
const onCloseMenu = React.useCallback(() => {
if (isOpen) {
onToggle();
}
}, [ isOpen, onToggle ]);
return ( return (
<Menu isOpen={ isOpen } onClose={ onCloseMenu }> <PopoverFilterRadio
<MenuButton onClick={ onToggle }> name="account_history_filter"
<FilterButton options={ OPTIONS }
isActive={ isOpen || isActive } onChange={ onFilterChange }
isLoading={ isInitialLoading } hasActiveFilter={ hasActiveFilter }
onClick={ onToggle } isLoading={ isInitialLoading }
appliedFiltersNum={ isActive ? 1 : 0 } defaultValue={ defaultFilter || OPTIONS[0].value }
as="div" />
/>
</MenuButton>
<MenuList zIndex={ 2 }>
<MenuOptionGroup defaultValue={ defaultFilter || 'all' } type="radio" onChange={ onFilterChange }>
<MenuItemOption value="all">All</MenuItemOption>
<MenuItemOption value="received">Received from</MenuItemOption>
<MenuItemOption value="sent">Sent to</MenuItemOption>
</MenuOptionGroup>
</MenuList>
</Menu>
); );
}; };
......
...@@ -82,7 +82,7 @@ const AddressInternalTxs = ({ scrollRef, shouldRender = true }: Props) => { ...@@ -82,7 +82,7 @@ const AddressInternalTxs = ({ scrollRef, shouldRender = true }: Props) => {
<AddressTxsFilter <AddressTxsFilter
defaultFilter={ filterValue } defaultFilter={ filterValue }
onFilterChange={ handleFilterChange } onFilterChange={ handleFilterChange }
isActive={ Boolean(filterValue) } hasActiveFilter={ Boolean(filterValue) }
isLoading={ pagination.isLoading } isLoading={ pagination.isLoading }
/> />
<AddressCsvExportLink <AddressCsvExportLink
......
...@@ -111,7 +111,7 @@ const AddressTokens = ({ shouldRender = true }: Props) => { ...@@ -111,7 +111,7 @@ const AddressTokens = ({ shouldRender = true }: Props) => {
} }
const nftTypeFilter = ( const nftTypeFilter = (
<PopoverFilter isActive={ tokenTypes && tokenTypes.length > 0 } contentProps={{ w: '200px' }} appliedFiltersNum={ tokenTypes?.length }> <PopoverFilter contentProps={{ w: '200px' }} appliedFiltersNum={ tokenTypes?.length }>
<TokenTypeFilter<NFTTokenType> nftOnly onChange={ handleTokenTypesChange } defaultValue={ tokenTypes }/> <TokenTypeFilter<NFTTokenType> nftOnly onChange={ handleTokenTypesChange } defaultValue={ tokenTypes }/>
</PopoverFilter> </PopoverFilter>
); );
......
...@@ -167,7 +167,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shouldRender = ...@@ -167,7 +167,7 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shouldRender =
<AddressTxsFilter <AddressTxsFilter
defaultFilter={ filterValue } defaultFilter={ filterValue }
onFilterChange={ handleFilterChange } onFilterChange={ handleFilterChange }
isActive={ Boolean(filterValue) } hasActiveFilter={ Boolean(filterValue) }
isLoading={ addressTxsQuery.pagination.isLoading } isLoading={ addressTxsQuery.pagination.isLoading }
/> />
); );
......
import {
Menu,
MenuButton,
MenuList,
MenuOptionGroup,
MenuItemOption,
useDisclosure,
} from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { AddressFromToFilter } from 'types/api/address'; import type { AddressFromToFilter } from 'types/api/address';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading'; import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import FilterButton from 'ui/shared/filters/FilterButton'; import PopoverFilterRadio from 'ui/shared/filters/PopoverFilterRadio';
const OPTIONS = [
{ value: 'all', label: 'All' },
{ value: 'from', label: 'Outgoing transactions' },
{ value: 'to', label: 'Incoming transactions' },
];
interface Props { interface Props {
isActive: boolean; hasActiveFilter: boolean;
defaultFilter: AddressFromToFilter; defaultFilter: AddressFromToFilter;
onFilterChange: (nextValue: string | Array<string>) => void; onFilterChange: (nextValue: string | Array<string>) => void;
isLoading?: boolean; isLoading?: boolean;
} }
const AddressTxsFilter = ({ onFilterChange, defaultFilter, isActive, isLoading }: Props) => { const AddressTxsFilter = ({ onFilterChange, defaultFilter, hasActiveFilter, isLoading }: Props) => {
const { isOpen, onToggle } = useDisclosure();
const isInitialLoading = useIsInitialLoading(isLoading); const isInitialLoading = useIsInitialLoading(isLoading);
return ( return (
<Menu> <PopoverFilterRadio
<MenuButton> name="txs_filter"
<FilterButton options={ OPTIONS }
isActive={ isOpen || isActive } onChange={ onFilterChange }
isLoading={ isInitialLoading } hasActiveFilter={ hasActiveFilter }
onClick={ onToggle } isLoading={ isInitialLoading }
appliedFiltersNum={ isActive ? 1 : 0 } defaultValue={ defaultFilter || OPTIONS[0].value }
as="div" />
/>
</MenuButton>
<MenuList zIndex={ 2 }>
<MenuOptionGroup defaultValue={ defaultFilter || 'all' } type="radio" onChange={ onFilterChange }>
<MenuItemOption value="all">All</MenuItemOption>
<MenuItemOption value="from">Outgoing transactions</MenuItemOption>
<MenuItemOption value="to">Incoming transactions</MenuItemOption>
</MenuOptionGroup>
</MenuList>
</Menu>
); );
}; };
......
...@@ -44,6 +44,7 @@ const SolidityscanReport = ({ hash }: Props) => { ...@@ -44,6 +44,7 @@ const SolidityscanReport = ({ hash }: Props) => {
score={ score } score={ score }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
onClick={ onToggle } onClick={ onToggle }
isActive={ isOpen }
/> />
</PopoverTrigger> </PopoverTrigger>
<PopoverContent w={{ base: '100vw', lg: '328px' }}> <PopoverContent w={{ base: '100vw', lg: '328px' }}>
......
...@@ -76,6 +76,7 @@ const ContractMethodMultiplyButton = ({ onClick, isDisabled }: Props) => { ...@@ -76,6 +76,7 @@ const ContractMethodMultiplyButton = ({ onClick, isDisabled }: Props) => {
ml={ 1 } ml={ 1 }
p={ 0 } p={ 0 }
onClick={ onToggle } onClick={ onToggle }
isActive={ isOpen }
isDisabled={ isDisabled } isDisabled={ isDisabled }
> >
<IconSvg <IconSvg
......
...@@ -61,6 +61,7 @@ const ContractCodeIde = ({ className, hash, isLoading }: Props) => { ...@@ -61,6 +61,7 @@ const ContractCodeIde = ({ className, hash, isLoading }: Props) => {
variant="outline" variant="outline"
colorScheme="gray" colorScheme="gray"
onClick={ onToggle } onClick={ onToggle }
isActive={ isOpen }
aria-label="Open source code in IDE" aria-label="Open source code in IDE"
fontWeight={ 500 } fontWeight={ 500 }
px={ 2 } px={ 2 }
......
...@@ -65,6 +65,7 @@ const ContractExternalLibraries = ({ className, data, isLoading }: Props) => { ...@@ -65,6 +65,7 @@ const ContractExternalLibraries = ({ className, data, isLoading }: Props) => {
variant="outline" variant="outline"
colorScheme="gray" colorScheme="gray"
onClick={ onToggle } onClick={ onToggle }
isActive={ isOpen }
fontWeight={ 600 } fontWeight={ 600 }
px={ 2 } px={ 2 }
aria-label="View external libraries" aria-label="View external libraries"
......
...@@ -111,6 +111,7 @@ const AddressEnsDomains = ({ addressHash, mainDomainName }: Props) => { ...@@ -111,6 +111,7 @@ const AddressEnsDomains = ({ addressHash, mainDomainName }: Props) => {
variant="outline" variant="outline"
colorScheme="gray" colorScheme="gray"
onClick={ onToggle } onClick={ onToggle }
isActive={ isOpen }
aria-label="Address domains" aria-label="Address domains"
fontWeight={ 500 } fontWeight={ 500 }
px={ 2 } px={ 2 }
......
import { Box, Button, Skeleton, Text, useColorModeValue } from '@chakra-ui/react'; import { Box, Button, Skeleton, chakra, useColorModeValue } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { FormattedData } from './types'; import type { FormattedData } from './types';
...@@ -39,20 +39,21 @@ const TokenSelectButton = ({ isOpen, isLoading, onClick, data }: Props, ref: Rea ...@@ -39,20 +39,21 @@ const TokenSelectButton = ({ isOpen, isLoading, onClick, data }: Props, ref: Rea
variant="outline" variant="outline"
colorScheme="gray" colorScheme="gray"
onClick={ handleClick } onClick={ handleClick }
isActive={ isOpen }
aria-label="Token select" aria-label="Token select"
> >
<IconSvg name="tokens" boxSize={ 4 } mr={ 2 }/> <IconSvg name="tokens" boxSize={ 4 } mr={ 2 }/>
<Text fontWeight={ 600 }>{ prefix }{ num }</Text> <chakra.span fontWeight={ 600 }>{ prefix }{ num }</chakra.span>
<Text <chakra.span
whiteSpace="pre" whiteSpace="pre"
variant="secondary" color="text_secondary"
fontWeight={ 400 } fontWeight={ 400 }
maxW={{ base: 'calc(100vw - 230px)', lg: '500px' }} maxW={{ base: 'calc(100vw - 230px)', lg: '500px' }}
overflow="hidden" overflow="hidden"
textOverflow="ellipsis" textOverflow="ellipsis"
> >
{ space }({ prefix }${ usd.toFormat(2) }) { space }({ prefix }${ usd.toFormat(2) })
</Text> </chakra.span>
<IconSvg name="arrows/east-mini" transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } transitionDuration="faster" boxSize={ 5 } ml={ 3 }/> <IconSvg name="arrows/east-mini" transform={ isOpen ? 'rotate(90deg)' : 'rotate(-90deg)' } transitionDuration="faster" boxSize={ 5 } ml={ 3 }/>
</Button> </Button>
{ isLoading && !isOpen && <Skeleton h="100%" w="100%" position="absolute" top={ 0 } left={ 0 } bgColor={ skeletonBgColor } borderRadius="base"/> } { isLoading && !isOpen && <Skeleton h="100%" w="100%" position="absolute" top={ 0 } left={ 0 } bgColor={ skeletonBgColor } borderRadius="base"/> }
......
...@@ -51,6 +51,7 @@ const AppSecurityReport = ({ id, securityReport, showContractList, isLoading, on ...@@ -51,6 +51,7 @@ const AppSecurityReport = ({ id, securityReport, showContractList, isLoading, on
score={ securityScore } score={ securityScore }
isLoading={ isLoading } isLoading={ isLoading }
onClick={ handleButtonClick } onClick={ handleButtonClick }
isActive={ isOpen }
onlyIcon={ onlyIcon } onlyIcon={ onlyIcon }
label="The security score is based on analysis of a DApp's smart contracts." label="The security score is based on analysis of a DApp's smart contracts."
/> />
......
...@@ -40,6 +40,7 @@ const ContractSecurityReport = ({ securityReport }: Props) => { ...@@ -40,6 +40,7 @@ const ContractSecurityReport = ({ securityReport }: Props) => {
<SolidityscanReportButton <SolidityscanReportButton
score={ parseFloat(securityScore) } score={ parseFloat(securityScore) }
onClick={ handleClick } onClick={ handleClick }
isActive={ isOpen }
/> />
</PopoverTrigger> </PopoverTrigger>
<PopoverContent w={{ base: '100vw', lg: '328px' }}> <PopoverContent w={{ base: '100vw', lg: '328px' }}>
......
...@@ -36,7 +36,7 @@ const MarketplaceAppInfo = ({ data }: Props) => { ...@@ -36,7 +36,7 @@ const MarketplaceAppInfo = ({ data }: Props) => {
return ( return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy> <Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger> <PopoverTrigger>
<TriggerButton onClick={ onToggle }/> <TriggerButton onClick={ onToggle } isActive={ isOpen }/>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent w="500px"> <PopoverContent w="500px">
<PopoverBody px={ 6 } py={ 5 }> <PopoverBody px={ 6 } py={ 5 }>
......
...@@ -6,9 +6,10 @@ import IconSvg from 'ui/shared/IconSvg'; ...@@ -6,9 +6,10 @@ import IconSvg from 'ui/shared/IconSvg';
interface Props { interface Props {
onClick: () => void; onClick: () => void;
onlyIcon?: boolean; onlyIcon?: boolean;
isActive?: boolean;
} }
const TriggerButton = ({ onClick, onlyIcon }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => { const TriggerButton = ({ onClick, onlyIcon, isActive }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
return ( return (
<Button <Button
ref={ ref } ref={ ref }
...@@ -16,6 +17,7 @@ const TriggerButton = ({ onClick, onlyIcon }: Props, ref: React.ForwardedRef<HTM ...@@ -16,6 +17,7 @@ const TriggerButton = ({ onClick, onlyIcon }: Props, ref: React.ForwardedRef<HTM
variant="outline" variant="outline"
colorScheme="gray" colorScheme="gray"
onClick={ onClick } onClick={ onClick }
isActive={ isActive }
aria-label="Show project info" aria-label="Show project info"
fontWeight={ 500 } fontWeight={ 500 }
px={ onlyIcon ? 1 : 2 } px={ onlyIcon ? 1 : 2 }
......
...@@ -106,11 +106,11 @@ const Tokens = () => { ...@@ -106,11 +106,11 @@ const Tokens = () => {
const hasMultipleTabs = bridgedTokensFeature.isEnabled; const hasMultipleTabs = bridgedTokensFeature.isEnabled;
const filter = tab === 'bridged' ? ( const filter = tab === 'bridged' ? (
<PopoverFilter isActive={ bridgeChains && bridgeChains.length > 0 } contentProps={{ maxW: '350px' }} appliedFiltersNum={ bridgeChains?.length }> <PopoverFilter contentProps={{ maxW: '350px' }} appliedFiltersNum={ bridgeChains?.length }>
<TokensBridgedChainsFilter onChange={ handleBridgeChainsChange } defaultValue={ bridgeChains }/> <TokensBridgedChainsFilter onChange={ handleBridgeChainsChange } defaultValue={ bridgeChains }/>
</PopoverFilter> </PopoverFilter>
) : ( ) : (
<PopoverFilter isActive={ tokenTypes && tokenTypes.length > 0 } contentProps={{ w: '200px' }} appliedFiltersNum={ tokenTypes?.length }> <PopoverFilter contentProps={{ w: '200px' }} appliedFiltersNum={ tokenTypes?.length }>
<TokenTypeFilter<TokenType> onChange={ handleTokenTypesChange } defaultValue={ tokenTypes } nftOnly={ false }/> <TokenTypeFilter<TokenType> onChange={ handleTokenTypesChange } defaultValue={ tokenTypes } nftOnly={ false }/>
</PopoverFilter> </PopoverFilter>
); );
......
...@@ -83,7 +83,7 @@ const Validators = () => { ...@@ -83,7 +83,7 @@ const Validators = () => {
onSortingChange(getSortParamsFromValue(value)); onSortingChange(getSortParamsFromValue(value));
}, [ onSortingChange ]); }, [ onSortingChange ]);
const filterMenu = <ValidatorsFilter onChange={ handleStateFilterChange } defaultValue={ statusFilter } isActive={ Boolean(statusFilter) }/>; const filterMenu = <ValidatorsFilter onChange={ handleStateFilterChange } defaultValue={ statusFilter } hasActiveFilter={ Boolean(statusFilter) }/>;
// const filterInput = ( // const filterInput = (
// <FilterInput // <FilterInput
......
...@@ -77,7 +77,13 @@ const VerifiedContracts = () => { ...@@ -77,7 +77,13 @@ const VerifiedContracts = () => {
onSortingChange(getSortParamsFromValue(value)); onSortingChange(getSortParamsFromValue(value));
}, [ onSortingChange ]); }, [ onSortingChange ]);
const typeFilter = <VerifiedContractsFilter onChange={ handleTypeChange } defaultValue={ type } isActive={ Boolean(type) }/>; const typeFilter = (
<VerifiedContractsFilter
onChange={ handleTypeChange }
defaultValue={ type }
hasActiveFilter={ Boolean(type) }
/>
);
const filterInput = ( const filterInput = (
<FilterInput <FilterInput
......
...@@ -62,6 +62,7 @@ const NetworkExplorers = ({ className, type, pathParam }: Props) => { ...@@ -62,6 +62,7 @@ const NetworkExplorers = ({ className, type, pathParam }: Props) => {
variant="outline" variant="outline"
colorScheme="gray" colorScheme="gray"
onClick={ onToggle } onClick={ onToggle }
isActive={ isOpen }
aria-label="Verify in other explorers" aria-label="Verify in other explorers"
fontWeight={ 500 } fontWeight={ 500 }
px={ 2 } px={ 2 }
......
...@@ -119,6 +119,7 @@ const AdaptiveTabsList = (props: Props) => { ...@@ -119,6 +119,7 @@ const AdaptiveTabsList = (props: Props) => {
color: 'inherit', color: 'inherit',
}, },
}} }}
{ ...(index === props.activeTabIndex ? { 'data-selected': true } : {}) }
> >
<Skeleton isLoaded={ !props.isLoading }> <Skeleton isLoaded={ !props.isLoading }>
{ typeof tab.title === 'function' ? tab.title() : tab.title } { typeof tab.title === 'function' ? tab.title() : tab.title }
......
...@@ -22,20 +22,38 @@ const FilterButton = ({ isActive, isLoading, appliedFiltersNum, onClick, as }: P ...@@ -22,20 +22,38 @@ const FilterButton = ({ isActive, isLoading, appliedFiltersNum, onClick, as }: P
return <Skeleton w={{ base: 9, lg: '78px' }} h={ 8 } borderRadius="base" flexShrink={ 0 }/>; return <Skeleton w={{ base: 9, lg: '78px' }} h={ 8 } borderRadius="base" flexShrink={ 0 }/>;
} }
const num = (
<Circle
className="AppliedFiltersNum"
bg={ isActive ? 'link_hovered' : badgeBgColor }
size={ 5 }
color={ badgeColor }
>
{ appliedFiltersNum }
</Circle>
);
return ( return (
<Button <Button
ref={ ref } ref={ ref }
rightIcon={ appliedFiltersNum ? <Circle bg={ badgeBgColor } size={ 5 } color={ badgeColor }>{ appliedFiltersNum }</Circle> : undefined } rightIcon={ appliedFiltersNum ? num : undefined }
size="sm" size="sm"
fontWeight="500" fontWeight="500"
variant="outline" variant="outline"
colorScheme="gray-dark" colorScheme="gray"
onClick={ onClick } onClick={ onClick }
isActive={ isActive } isActive={ isActive }
data-selected={ Boolean(appliedFiltersNum) }
px={ 1.5 } px={ 1.5 }
flexShrink={ 0 } flexShrink={ 0 }
as={ as } as={ as }
pointerEvents="all" pointerEvents="all"
_hover={ isActive ? {
color: 'link_hovered',
'.AppliedFiltersNum': {
bg: 'link_hovered',
},
} : undefined }
> >
{ FilterIcon } { FilterIcon }
<Box display={{ base: 'none', lg: 'block' }}>Filter</Box> <Box display={{ base: 'none', lg: 'block' }}>Filter</Box>
......
...@@ -12,20 +12,19 @@ import FilterButton from 'ui/shared/filters/FilterButton'; ...@@ -12,20 +12,19 @@ import FilterButton from 'ui/shared/filters/FilterButton';
interface Props { interface Props {
appliedFiltersNum?: number; appliedFiltersNum?: number;
isActive?: boolean;
children: React.ReactNode; children: React.ReactNode;
contentProps?: PopoverContentProps; contentProps?: PopoverContentProps;
isLoading?: boolean; isLoading?: boolean;
} }
const PopoverFilter = ({ appliedFiltersNum, children, contentProps, isActive, isLoading }: Props) => { const PopoverFilter = ({ appliedFiltersNum, children, contentProps, isLoading }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure(); const { isOpen, onToggle, onClose } = useDisclosure();
return ( return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy> <Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger> <PopoverTrigger>
<FilterButton <FilterButton
isActive={ isOpen || isActive || Number(appliedFiltersNum) > 0 } isActive={ isOpen }
onClick={ onToggle } onClick={ onToggle }
appliedFiltersNum={ appliedFiltersNum } appliedFiltersNum={ appliedFiltersNum }
isLoading={ isLoading } isLoading={ isLoading }
......
import {
Popover,
PopoverTrigger,
PopoverContent,
PopoverBody,
useDisclosure,
useRadio,
Box,
useRadioGroup,
useColorModeValue,
} from '@chakra-ui/react';
import React from 'react';
import FilterButton from 'ui/shared/filters/FilterButton';
import IconSvg from 'ui/shared/IconSvg';
// OPTION
export interface TOption {
value: string;
label: string;
}
type OptionProps = ReturnType<ReturnType<typeof useRadioGroup>['getRadioProps']>;
const Option = (props: OptionProps) => {
const { getInputProps, getRadioProps } = useRadio(props);
const input = getInputProps();
const checkbox = getRadioProps();
const bgColorHover = useColorModeValue('blue.50', 'whiteAlpha.100');
return (
<Box
as="label"
px={ 4 }
py={ 2 }
cursor="pointer"
display="flex"
columnGap={ 3 }
alignItems="center"
_hover={{
bgColor: bgColorHover,
}}
>
<input { ...input }/>
<Box { ...checkbox }>
{ props.children }
</Box>
{ props.isChecked && <IconSvg name="check" boxSize={ 4 }/> }
</Box>
);
};
// FILTER
interface Props {
name: string;
options: Array<TOption>;
hasActiveFilter: boolean;
defaultValue?: string;
isLoading?: boolean;
onChange: (nextValue: string) => void;
}
const PopoverFilterRadio = ({ name, hasActiveFilter, options, isLoading, onChange, defaultValue }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();
const { getRootProps, getRadioProps } = useRadioGroup({
name,
defaultValue,
onChange,
});
const root = getRootProps();
return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger>
<FilterButton
isActive={ isOpen }
onClick={ onToggle }
appliedFiltersNum={ hasActiveFilter ? 1 : 0 }
isLoading={ isLoading }
/>
</PopoverTrigger>
<PopoverContent w="fit-content" minW="150px">
<PopoverBody { ...root } py={ 2 } px={ 0 } display="flex" flexDir="column">
{ options.map((option) => {
const radio = getRadioProps({ value: option.value });
return (
<Option key={ option.value } { ...radio }>
{ option.label }
</Option>
);
}) }
</PopoverBody>
</PopoverContent>
</Popover>
);
};
export default React.memo(PopoverFilterRadio);
...@@ -74,7 +74,7 @@ const LogItem = ({ address, index, topics, data, decoded, type, tx_hash: txHash, ...@@ -74,7 +74,7 @@ const LogItem = ({ address, index, topics, data, decoded, type, tx_hash: txHash,
</Tooltip> */ } </Tooltip> */ }
<Skeleton isLoaded={ !isLoading } ml="auto" borderRadius="base"> <Skeleton isLoaded={ !isLoading } ml="auto" borderRadius="base">
<Tooltip label="Log index"> <Tooltip label="Log index">
<Button variant="outline" colorScheme="gray" isActive size="sm" fontWeight={ 400 }> <Button variant="outline" colorScheme="gray" data-selected="true" size="sm" fontWeight={ 400 }>
{ index } { index }
</Button> </Button>
</Tooltip> </Tooltip>
......
...@@ -62,7 +62,7 @@ const LogTopic = ({ hex, index, isLoading }: Props) => { ...@@ -62,7 +62,7 @@ const LogTopic = ({ hex, index, isLoading }: Props) => {
return ( return (
<Flex alignItems="center" px={{ base: 0, lg: 3 }} _notFirst={{ mt: 3 }} overflow="hidden" maxW="100%"> <Flex alignItems="center" px={{ base: 0, lg: 3 }} _notFirst={{ mt: 3 }} overflow="hidden" maxW="100%">
<Skeleton isLoaded={ !isLoading } mr={ 3 } borderRadius="base"> <Skeleton isLoaded={ !isLoading } mr={ 3 } borderRadius="base">
<Button variant="outline" colorScheme="gray" isActive size="xs" fontWeight={ 400 } w={ 6 }> <Button variant="outline" colorScheme="gray" data-selected size="xs" fontWeight={ 400 } w={ 6 }>
{ index } { index }
</Button> </Button>
</Skeleton> </Skeleton>
......
...@@ -48,7 +48,7 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasPage ...@@ -48,7 +48,7 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasPage
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
isActive data-selected={ true }
borderWidth="1px" borderWidth="1px"
fontWeight={ 400 } fontWeight={ 400 }
h={ 8 } h={ 8 }
......
...@@ -12,10 +12,11 @@ interface Props { ...@@ -12,10 +12,11 @@ interface Props {
onlyIcon?: boolean; onlyIcon?: boolean;
onClick?: () => void; onClick?: () => void;
label?: string; label?: string;
isActive: boolean;
} }
const SolidityscanReportButton = ( const SolidityscanReportButton = (
{ score, isLoading, onlyIcon, onClick, label = 'Security score' }: Props, { score, isLoading, onlyIcon, onClick, label = 'Security score', isActive }: Props,
ref: React.ForwardedRef<HTMLButtonElement>, ref: React.ForwardedRef<HTMLButtonElement>,
) => { ) => {
const { scoreColor } = useScoreLevelAndColor(score); const { scoreColor } = useScoreLevelAndColor(score);
...@@ -31,6 +32,7 @@ const SolidityscanReportButton = ( ...@@ -31,6 +32,7 @@ const SolidityscanReportButton = (
variant="outline" variant="outline"
colorScheme="gray" colorScheme="gray"
onClick={ onClick } onClick={ onClick }
isActive={ isActive }
aria-label="SolidityScan score" aria-label="SolidityScan score"
fontWeight={ 500 } fontWeight={ 500 }
px="6px" px="6px"
......
...@@ -21,7 +21,7 @@ const SortButton = ({ onClick, isActive, className, isLoading }: Props) => { ...@@ -21,7 +21,7 @@ const SortButton = ({ onClick, isActive, className, isLoading }: Props) => {
aria-label="sort" aria-label="sort"
size="sm" size="sm"
variant="outline" variant="outline"
colorScheme="gray-dark" colorScheme="gray"
minWidth="36px" minWidth="36px"
onClick={ onClick } onClick={ onClick }
isActive={ isActive } isActive={ isActive }
......
...@@ -12,9 +12,16 @@ interface Props { ...@@ -12,9 +12,16 @@ interface Props {
const NetworkMenuPopup = ({ items, tabs }: Props) => { const NetworkMenuPopup = ({ items, tabs }: Props) => {
const selectedNetwork = items?.find(({ isActive }) => isActive); const selectedNetwork = items?.find(({ isActive }) => isActive);
const selectedTab = tabs.findIndex((tab) => selectedNetwork?.group === tab); const defaultTab = tabs.findIndex((tab) => selectedNetwork?.group === tab);
const [ tabIndex, setTabIndex ] = React.useState(defaultTab > -1 ? defaultTab : 0);
const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50'); const bgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
const handleTabChange = React.useCallback((index: number) => {
setTabIndex(index);
}, []);
const content = !items || items.length === 0 ? ( const content = !items || items.length === 0 ? (
<> <>
<Flex alignItems="center"> <Flex alignItems="center">
...@@ -45,11 +52,16 @@ const NetworkMenuPopup = ({ items, tabs }: Props) => { ...@@ -45,11 +52,16 @@ const NetworkMenuPopup = ({ items, tabs }: Props) => {
colorScheme="gray" colorScheme="gray"
size="sm" size="sm"
isLazy isLazy
defaultIndex={ selectedTab !== -1 ? selectedTab : undefined } index={ tabIndex }
onChange={ handleTabChange }
> >
{ tabs.length > 1 && ( { tabs.length > 1 && (
<TabList columnGap={ 2 }> <TabList columnGap={ 2 }>
{ tabs.map((tab) => <Tab key={ tab } textTransform="capitalize">{ tab }</Tab>) } { tabs.map((tab, index) => (
<Tab key={ tab } textTransform="capitalize" { ...(tabIndex === index ? { 'data-selected': 'true' } : {}) }>
{ tab }
</Tab>
)) }
</TabList> </TabList>
) } ) }
<TabPanels mt={ 3 }> <TabPanels mt={ 3 }>
......
...@@ -186,12 +186,12 @@ test('scroll suggest to category', async({ render, page, mockApiResponse }) => { ...@@ -186,12 +186,12 @@ test('scroll suggest to category', async({ render, page, mockApiResponse }) => {
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1200, height: 500 } }); await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1200, height: 500 } });
}); });
test('recent keywords suggest +@mobile', async({ render, page }) => { test('recent keywords suggest +@mobile', async({ render, page }, { project }) => {
await render(<SearchBar/>); await render(<SearchBar/>);
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
await page.evaluate(() => window.localStorage.setItem('recent_search_keywords', '["10x2d311959270e0bbdc1fc7bc6dbd8ad645c4dd8d6aa32f5f89d54629a924f112b","0x1d311959270e0bbdc1fc7bc6dbd8ad645c4dd8d6aa32f5f89d54629a924f112b","usd","bob"]')); await page.evaluate(() => window.localStorage.setItem('recent_search_keywords', '["10x2d311959270e0bbdc1fc7bc6dbd8ad645c4dd8d6aa32f5f89d54629a924f112b","0x1d311959270e0bbdc1fc7bc6dbd8ad645c4dd8d6aa32f5f89d54629a924f112b","usd","bob"]'));
await page.getByPlaceholder(/search/i).click(); await page.getByPlaceholder(/search/i).click();
await page.getByText('0x1d311959270e0bbdc1fc7bc6db').isVisible(); await page.getByText(project.name === 'mobile' ? '0x1d311959270e0bbdc1fc7bc6dbd...112b' : '0x1d311959270e0bbdc1fc7bc6dbd').isVisible();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1200, height: 500 } }); await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1200, height: 500 } });
}); });
......
...@@ -123,7 +123,11 @@ const SearchBarSuggest = ({ query, searchTerm, onItemClick, containerId }: Props ...@@ -123,7 +123,11 @@ const SearchBarSuggest = ({ query, searchTerm, onItemClick, containerId }: Props
<Box position="sticky" top="0" width="100%" background={ bgColor } py={ 5 } my={ -5 } ref={ tabsRef }> <Box position="sticky" top="0" width="100%" background={ bgColor } py={ 5 } my={ -5 } ref={ tabsRef }>
<Tabs variant="outline" colorScheme="gray" size="sm" index={ tabIndex }> <Tabs variant="outline" colorScheme="gray" size="sm" index={ tabIndex }>
<TabList columnGap={ 3 } rowGap={ 2 } flexWrap="wrap"> <TabList columnGap={ 3 } rowGap={ 2 } flexWrap="wrap">
{ resultCategories.map((cat, index) => <Tab key={ cat.id } onClick={ scrollToCategory(index) }>{ cat.title }</Tab>) } { resultCategories.map((cat, index) => (
<Tab key={ cat.id } onClick={ scrollToCategory(index) } { ...(tabIndex === index ? { 'data-selected': 'true' } : {}) }>
{ cat.title }
</Tab>
)) }
</TabList> </TabList>
</Tabs> </Tabs>
</Box> </Box>
......
...@@ -45,7 +45,7 @@ const DeFiDropdown = () => { ...@@ -45,7 +45,7 @@ const DeFiDropdown = () => {
<PopoverTrigger> <PopoverTrigger>
<Button <Button
onClick={ onToggle } onClick={ onToggle }
bgColor={ isOpen ? 'blue.400' : undefined } isActive={ isOpen }
{ ...buttonStyles } { ...buttonStyles }
> >
<chakra.span display={{ base: 'none', lg: 'inline' }} mr={ 1 }> <chakra.span display={{ base: 'none', lg: 'inline' }} mr={ 1 }>
......
import { Box, Button, Menu, MenuButton, MenuItemOption, MenuList, MenuOptionGroup, Text } from '@chakra-ui/react'; import { Box, Button, Menu, MenuButton, MenuItemOption, MenuList, MenuOptionGroup, chakra } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
...@@ -32,13 +32,13 @@ export function StatsDropdownMenu<T extends string>({ items, selectedId, onSelec ...@@ -32,13 +32,13 @@ export function StatsDropdownMenu<T extends string>({ items, selectedId, onSelec
display="flex" display="flex"
alignItems="center" alignItems="center"
> >
<Text <chakra.span
whiteSpace="nowrap" whiteSpace="nowrap"
overflow="hidden" overflow="hidden"
textOverflow="ellipsis" textOverflow="ellipsis"
> >
{ selectedCategory?.title } { selectedCategory?.title }
</Text> </chakra.span>
<IconSvg transform="rotate(-90deg)" ml="auto" name="arrows/east-mini" w={ 5 } h={ 5 }/> <IconSvg transform="rotate(-90deg)" ml="auto" name="arrows/east-mini" w={ 5 } h={ 5 }/>
</Box> </Box>
</MenuButton> </MenuButton>
......
...@@ -27,7 +27,7 @@ const TokenProjectInfo = ({ data }: Props) => { ...@@ -27,7 +27,7 @@ const TokenProjectInfo = ({ data }: Props) => {
if (isMobile) { if (isMobile) {
return ( return (
<> <>
<TriggerButton onClick={ onToggle }/> <TriggerButton onClick={ onToggle } isActive={ isOpen }/>
<Modal isOpen={ isOpen } onClose={ onClose } size="full"> <Modal isOpen={ isOpen } onClose={ onClose } size="full">
<ModalContent> <ModalContent>
<ModalCloseButton/> <ModalCloseButton/>
...@@ -41,7 +41,7 @@ const TokenProjectInfo = ({ data }: Props) => { ...@@ -41,7 +41,7 @@ const TokenProjectInfo = ({ data }: Props) => {
return ( return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy> <Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger> <PopoverTrigger>
<TriggerButton onClick={ onToggle }/> <TriggerButton onClick={ onToggle } isActive={ isOpen }/>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent w="500px"> <PopoverContent w="500px">
<PopoverBody px={ 6 } py={ 5 }> <PopoverBody px={ 6 } py={ 5 }>
......
...@@ -5,9 +5,10 @@ import IconSvg from 'ui/shared/IconSvg'; ...@@ -5,9 +5,10 @@ import IconSvg from 'ui/shared/IconSvg';
interface Props { interface Props {
onClick: () => void; onClick: () => void;
isActive: boolean;
} }
const TriggerButton = ({ onClick }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => { const TriggerButton = ({ onClick, isActive }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
return ( return (
<Button <Button
ref={ ref } ref={ ref }
...@@ -15,6 +16,7 @@ const TriggerButton = ({ onClick }: Props, ref: React.ForwardedRef<HTMLButtonEle ...@@ -15,6 +16,7 @@ const TriggerButton = ({ onClick }: Props, ref: React.ForwardedRef<HTMLButtonEle
variant="outline" variant="outline"
colorScheme="gray" colorScheme="gray"
onClick={ onClick } onClick={ onClick }
isActive={ isActive }
aria-label="Show project info" aria-label="Show project info"
fontWeight={ 500 } fontWeight={ 500 }
lineHeight={ 6 } lineHeight={ 6 }
......
import {
Menu,
MenuButton,
MenuList,
MenuOptionGroup,
MenuItemOption,
useDisclosure,
} from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { ValidatorsFilters } from 'types/api/validators'; import type { ValidatorsFilters } from 'types/api/validators';
import FilterButton from 'ui/shared/filters/FilterButton'; import PopoverFilterRadio from 'ui/shared/filters/PopoverFilterRadio';
const OPTIONS = [
{ value: 'all', label: 'All' },
{ value: 'active', label: 'Active' },
{ value: 'probation', label: 'Probation' },
{ value: 'inactive', label: 'Inactive' },
];
interface Props { interface Props {
isActive: boolean; hasActiveFilter: boolean;
defaultValue: ValidatorsFilters['state_filter'] | undefined; defaultValue: ValidatorsFilters['state_filter'] | undefined;
onChange: (nextValue: string | Array<string>) => void; onChange: (nextValue: string | Array<string>) => void;
} }
const ValidatorsFilter = ({ onChange, defaultValue, isActive }: Props) => { const ValidatorsFilter = ({ onChange, defaultValue, hasActiveFilter }: Props) => {
const { isOpen, onToggle } = useDisclosure();
return ( return (
<Menu> <PopoverFilterRadio
<MenuButton> name="validators_filter"
<FilterButton options={ OPTIONS }
isActive={ isOpen || isActive } onChange={ onChange }
appliedFiltersNum={ isActive ? 1 : 0 } hasActiveFilter={ hasActiveFilter }
onClick={ onToggle } defaultValue={ defaultValue || OPTIONS[0].value }
as="div" />
/>
</MenuButton>
<MenuList zIndex="popover">
<MenuOptionGroup defaultValue={ defaultValue || 'all' } title="Status" type="radio" onChange={ onChange }>
<MenuItemOption value="all">All</MenuItemOption>
<MenuItemOption value="active">Active</MenuItemOption>
<MenuItemOption value="probation">Probation</MenuItemOption>
<MenuItemOption value="inactive">Inactive</MenuItemOption>
</MenuOptionGroup>
</MenuList>
</Menu>
); );
}; };
......
import {
Menu,
MenuButton,
MenuList,
MenuOptionGroup,
MenuItemOption,
useDisclosure,
} from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { VerifiedContractsFilters } from 'types/api/contracts'; import type { VerifiedContractsFilters } from 'types/api/contracts';
import FilterButton from 'ui/shared/filters/FilterButton'; import PopoverFilterRadio from 'ui/shared/filters/PopoverFilterRadio';
const OPTIONS = [
{ value: 'all', label: 'All' },
{ value: 'solidity', label: 'Solidity' },
{ value: 'vyper', label: 'Vyper' },
{ value: 'yul', label: 'Yul' },
];
interface Props { interface Props {
isActive: boolean; hasActiveFilter: boolean;
defaultValue: VerifiedContractsFilters['filter'] | undefined; defaultValue: VerifiedContractsFilters['filter'] | undefined;
onChange: (nextValue: string | Array<string>) => void; onChange: (nextValue: string | Array<string>) => void;
} }
const VerifiedContractsFilter = ({ onChange, defaultValue, isActive }: Props) => { const VerifiedContractsFilter = ({ onChange, defaultValue, hasActiveFilter }: Props) => {
const { isOpen, onToggle } = useDisclosure();
return ( return (
<Menu> <PopoverFilterRadio
<MenuButton> name="verified_contracts_filter"
<FilterButton options={ OPTIONS }
isActive={ isOpen || isActive } onChange={ onChange }
appliedFiltersNum={ isActive ? 1 : 0 } hasActiveFilter={ hasActiveFilter }
onClick={ onToggle } defaultValue={ defaultValue || OPTIONS[0].value }
as="div" />
/>
</MenuButton>
<MenuList zIndex="popover">
<MenuOptionGroup defaultValue={ defaultValue || 'all' } title="Filter" type="radio" onChange={ onChange }>
<MenuItemOption value="all">All</MenuItemOption>
<MenuItemOption value="solidity">Solidity</MenuItemOption>
<MenuItemOption value="vyper">Vyper</MenuItemOption>
<MenuItemOption value="yul">Yul</MenuItemOption>
</MenuOptionGroup>
</MenuList>
</Menu>
); );
}; };
......
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