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

New styles for pagination and "Add / Remove" buttons (#2791)

* update styles for add / remove buttons

* update pagination styles

* change border color for filled inputs

* change row gap for alerts

* update icons and screenshots

* fix tests

* update buttons for contract method form

* update screenshot
parent 7f10c98e
<svg viewBox="0 0 16 2" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<path d="M.5 1a.937.937 0 0 1 .938-.938h13.124a.938.938 0 0 1 0 1.875H1.438A.937.937 0 0 1 .5 1Z" fill="currentColor"/> <g clip-path="url(#a)">
<path fill="currentColor" d="M8.857 9H17a1 1 0 0 1 1 1v.286a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V10a1 1 0 0 1 1-1h5.857Z"/>
</g>
<defs>
<clipPath id="a">
<path fill="currentColor" d="M0 0h20v20H0z"/>
</clipPath>
</defs>
</svg> </svg>
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<path d="M8 .5a.937.937 0 0 1 .938.938v5.625h5.624a.937.937 0 0 1 0 1.875H8.939v5.624a.937.937 0 0 1-1.876 0V8.939H1.438a.937.937 0 1 1 0-1.876h5.625V1.438A.937.937 0 0 1 8 .5Z" fill="currentColor"/> <g clip-path="url(#a)">
<path fill="currentColor" d="M7.857 9.052a1 1 0 0 0 1-1V3.195a1 1 0 0 1 1-1h.286a1 1 0 0 1 1 1v4.857a1 1 0 0 0 1 1H17a1 1 0 0 1 1 1v.286a1 1 0 0 1-1 1h-4.857a1 1 0 0 0-1 1v4.857a1 1 0 0 1-1 1h-.286a1 1 0 0 1-1-1v-4.857a1 1 0 0 0-1-1H3a1 1 0 0 1-1-1v-.286a1 1 0 0 1 1-1h4.857Z"/>
</g>
<defs>
<clipPath id="a">
<path fill="currentColor" d="M0 0h20v20H0z"/>
</clipPath>
</defs>
</svg> </svg>
...@@ -3,7 +3,7 @@ import React from 'react'; ...@@ -3,7 +3,7 @@ import React from 'react';
import { Button, type ButtonProps } from './button'; import { Button, type ButtonProps } from './button';
export interface IconButtonProps extends Omit<ButtonProps, 'size'> { export interface IconButtonProps extends Omit<ButtonProps, 'size'> {
size?: '2xs' | 'md'; size?: '2xs' | '2xs_alt' | 'md';
} }
export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>( export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
...@@ -13,7 +13,7 @@ export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>( ...@@ -13,7 +13,7 @@ export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
// FIXME: I have to clone the children instead of using _icon props because of style overrides // FIXME: I have to clone the children instead of using _icon props because of style overrides
// in some pw tests for some reason the _icon style will be applied before the style of child (IconSvg component) // in some pw tests for some reason the _icon style will be applied before the style of child (IconSvg component)
const child = React.Children.only<React.ReactElement>(children as React.ReactElement); const child = React.Children.only<React.ReactElement>(children as React.ReactElement);
const clonedChildren = size ? React.cloneElement(child, { boxSize: 5 } as React.HTMLAttributes<HTMLElement>) : child; const clonedChildren = size ? React.cloneElement(child, { boxSize: size === '2xs_alt' ? 3 : 5 } as React.HTMLAttributes<HTMLElement>) : child;
const sizeStyle = (() => { const sizeStyle = (() => {
switch (size) { switch (size) {
...@@ -24,6 +24,13 @@ export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>( ...@@ -24,6 +24,13 @@ export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
borderRadius: 'sm', borderRadius: 'sm',
}; };
} }
case '2xs_alt': {
return {
_icon: { boxSize: 3 },
boxSize: 5,
borderRadius: 'sm',
};
}
case 'md': { case 'md': {
return { return {
_icon: { boxSize: 5 }, _icon: { boxSize: 5 },
......
import React from 'react';
import type { IconButtonProps } from 'toolkit/chakra/icon-button';
import { IconButton } from 'toolkit/chakra/icon-button';
import IconSvg from 'ui/shared/IconSvg';
interface Props extends IconButtonProps {}
const AddButton = (props: Props) => {
return (
<IconButton
aria-label="Add item"
variant="icon_secondary"
size="md"
{ ...props }
>
<IconSvg name="plus"/>
</IconButton>
);
};
export default React.memo(AddButton);
import React from 'react';
import type { IconButtonProps } from 'toolkit/chakra/icon-button';
import { IconButton } from 'toolkit/chakra/icon-button';
import IconSvg from 'ui/shared/IconSvg';
interface Props extends IconButtonProps {}
const RemoveButton = (props: Props) => {
return (
<IconButton
aria-label="Remove item"
variant="icon_secondary"
size="md"
{ ...props }
>
<IconSvg name="minus"/>
</IconButton>
);
};
export default React.memo(RemoveButton);
...@@ -64,6 +64,18 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -64,6 +64,18 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
selected: { value: { _light: '{colors.blue.50}', _dark: '{colors.whiteAlpha.100}' } }, selected: { value: { _light: '{colors.blue.50}', _dark: '{colors.whiteAlpha.100}' } },
}, },
}, },
pagination: {
fg: {
DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.gray.50}' } },
selected: { value: { _light: '{colors.blue.700}', _dark: '{colors.gray.50}' } },
},
bg: {
selected: { value: { _light: '{colors.blue.50}', _dark: '{colors.whiteAlpha.100}' } },
},
border: {
DEFAULT: { value: { _light: '{colors.gray.100}', _dark: '{colors.whiteAlpha.100}' } },
},
},
hero: { hero: {
bg: { bg: {
DEFAULT: { DEFAULT: {
...@@ -251,7 +263,7 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -251,7 +263,7 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
DEFAULT: { value: { _light: '{colors.gray.100}', _dark: '{colors.gray.700}' } }, DEFAULT: { value: { _light: '{colors.gray.100}', _dark: '{colors.gray.700}' } },
hover: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.500}' } }, hover: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.500}' } },
focus: { value: '{colors.blue.400}' }, focus: { value: '{colors.blue.400}' },
filled: { value: { _light: '{colors.gray.300}', _dark: '{colors.gray.600}' } }, filled: { value: { _light: '{colors.gray.100}', _dark: '{colors.gray.700}' } },
readOnly: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.800}' } }, readOnly: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.800}' } },
error: { value: '{colors.red.500}' }, error: { value: '{colors.red.500}' },
}, },
......
...@@ -253,6 +253,28 @@ export const recipe = defineRecipe({ ...@@ -253,6 +253,28 @@ export const recipe = defineRecipe({
color: 'link.primary.hover', color: 'link.primary.hover',
}, },
}, },
pagination: {
borderWidth: '2px',
borderStyle: 'solid',
bg: 'transparent',
color: 'button.pagination.fg',
borderColor: 'button.pagination.border',
_hover: {
bg: 'transparent',
color: 'link.primary.hover',
borderColor: 'link.primary.hover',
},
_selected: {
bg: 'button.pagination.bg.selected',
color: 'button.pagination.fg.selected',
borderColor: 'transparent',
_hover: {
bg: 'button.pagination.bg.selected',
color: 'button.pagination.fg.selected',
borderColor: 'transparent',
},
},
},
}, },
size: { size: {
'2xs': { '2xs': {
......
...@@ -21,7 +21,7 @@ test.describe.configure({ mode: 'serial' }); ...@@ -21,7 +21,7 @@ test.describe.configure({ mode: 'serial' });
let addressApiUrl: string; let addressApiUrl: string;
test.beforeEach(async({ mockApiResponse, page }) => { test.beforeEach(async({ mockApiResponse, page }) => {
await page.route('https://cdn.jsdelivr.net/npm/monaco-editor@0.33.0/**', (route) => { await page.route('https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/**', (route) => {
route.abort(); route.abort();
}); });
addressApiUrl = await mockApiResponse('general:address', addressMock.contract, { pathParams: { hash: addressMock.contract.hash } }); addressApiUrl = await mockApiResponse('general:address', addressMock.contract, { pathParams: { hash: addressMock.contract.hash } });
......
...@@ -38,7 +38,7 @@ const ContractDetailsAlerts = ({ data, isLoading, addressData, channel }: Props) ...@@ -38,7 +38,7 @@ const ContractDetailsAlerts = ({ data, isLoading, addressData, channel }: Props)
}); });
return ( return (
<Flex flexDir="column" rowGap={ 2 } mb={ 6 } _empty={{ display: 'none' }}> <Flex flexDir="column" rowGap={ 1 } mb={ 6 } _empty={{ display: 'none' }}>
{ data?.is_blueprint && ( { data?.is_blueprint && (
<Box> <Box>
<span>This is an </span> <span>This is an </span>
......
import { chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import { IconButton } from 'toolkit/chakra/icon-button'; import type { IconButtonProps } from 'toolkit/chakra/icon-button';
import IconSvg from 'ui/shared/IconSvg'; import AddButton from 'toolkit/components/buttons/AddButton';
import RemoveButton from 'toolkit/components/buttons/RemoveButton';
interface Props { interface Props extends Omit<IconButtonProps, 'type'> {
index: number; index: number;
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
isDisabled?: boolean;
type: 'add' | 'remove'; type: 'add' | 'remove';
className?: string;
} }
const ContractMethodArrayButton = ({ className, type, index, onClick, isDisabled }: Props) => { const ContractMethodArrayButton = ({ type, index, onClick, ...props }: Props) => {
const handleClick = React.useCallback((event: React.MouseEvent<HTMLButtonElement>) => { const handleClick = React.useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation(); event.stopPropagation();
onClick(event); onClick?.(event);
}, [ onClick ]); }, [ onClick ]);
const Button = type === 'add' ? AddButton : RemoveButton;
return ( return (
<IconButton <Button
as="div"
className={ className }
aria-label={ type }
data-index={ index } data-index={ index }
variant="outline" size="2xs_alt"
boxSize={ 5 }
onClick={ handleClick } onClick={ handleClick }
disabled={ isDisabled } { ...props }
> />
<IconSvg name={ type === 'remove' ? 'minus' : 'plus' } boxSize={ 3 }/>
</IconButton>
); );
}; };
export default React.memo(chakra(ContractMethodArrayButton)); export default React.memo(ContractMethodArrayButton);
...@@ -34,7 +34,7 @@ const ContractMethodFieldAccordion = ({ label, level, children, onAddClick, onRe ...@@ -34,7 +34,7 @@ const ContractMethodFieldAccordion = ({ label, level, children, onAddClick, onRe
{ label } { label }
</Box> </Box>
{ onRemoveClick && index !== undefined && <ContractMethodArrayButton index={ index } onClick={ onRemoveClick } type="remove"/> } { onRemoveClick && index !== undefined && <ContractMethodArrayButton index={ index } onClick={ onRemoveClick } type="remove"/> }
{ onAddClick && index !== undefined && <ContractMethodArrayButton index={ index } onClick={ onAddClick } type="add" ml={ 2 }/> } { onAddClick && index !== undefined && <ContractMethodArrayButton index={ index } onClick={ onAddClick } type="add" ml={ 1 }/> }
</AccordionItemTrigger> </AccordionItemTrigger>
<AccordionItemContent display="flex" flexDir="column" rowGap={ 1 } pl="18px" pr="6px"> <AccordionItemContent display="flex" flexDir="column" rowGap={ 1 } pl="18px" pr="6px">
{ children } { children }
......
...@@ -140,7 +140,7 @@ const ContractMethodFieldInputArray = ({ ...@@ -140,7 +140,7 @@ const ContractMethodFieldInputArray = ({
const itemData = transformDataForArrayItem(data, index); const itemData = transformDataForArrayItem(data, index);
return ( return (
<Flex key={ registeredIndex } alignItems="flex-start" columnGap={ 3 }> <Flex key={ registeredIndex } alignItems="flex-start" columnGap={ 2 }>
<ContractMethodFieldInput <ContractMethodFieldInput
data={ itemData } data={ itemData }
hideLabel hideLabel
......
...@@ -5,13 +5,12 @@ import React from 'react'; ...@@ -5,13 +5,12 @@ import React from 'react';
import type { AdvancedFilterParams } from 'types/api/advancedFilter'; import type { AdvancedFilterParams } from 'types/api/advancedFilter';
import { IconButton } from 'toolkit/chakra/icon-button';
import { Input } from 'toolkit/chakra/input'; import { Input } from 'toolkit/chakra/input';
import { InputGroup } from 'toolkit/chakra/input-group'; import { InputGroup } from 'toolkit/chakra/input-group';
import { Select } from 'toolkit/chakra/select'; import { Select } from 'toolkit/chakra/select';
import AddButton from 'toolkit/components/buttons/AddButton';
import { ClearButton } from 'toolkit/components/buttons/ClearButton'; import { ClearButton } from 'toolkit/components/buttons/ClearButton';
import TableColumnFilter from 'ui/shared/filters/TableColumnFilter'; import TableColumnFilter from 'ui/shared/filters/TableColumnFilter';
import IconSvg from 'ui/shared/IconSvg';
const FILTER_PARAM_TO_INCLUDE = 'to_address_hashes_to_include'; const FILTER_PARAM_TO_INCLUDE = 'to_address_hashes_to_include';
const FILTER_PARAM_FROM_INCLUDE = 'from_address_hashes_to_include'; const FILTER_PARAM_FROM_INCLUDE = 'from_address_hashes_to_include';
...@@ -76,15 +75,10 @@ const AddressFilterInput = ({ address, mode, onModeChange, onChange, onClear, is ...@@ -76,15 +75,10 @@ const AddressFilterInput = ({ address, mode, onModeChange, onChange, onClear, is
<Input value={ address } onChange={ onChange } placeholder="Smart contract / Address (0x...)*" size="sm" autoComplete="off"/> <Input value={ address } onChange={ onChange } placeholder="Smart contract / Address (0x...)*" size="sm" autoComplete="off"/>
</InputGroup> </InputGroup>
{ isLast && ( { isLast && (
<IconButton <AddButton
aria-label="add"
variant="outline"
size="md"
ml={ 2 } ml={ 2 }
onClick={ onAddFieldClick } onClick={ onAddFieldClick }
> />
<IconSvg name="plus"/>
</IconButton>
) } ) }
</Flex> </Flex>
); );
......
...@@ -87,7 +87,7 @@ test('flatten source code method +@dark-mode +@mobile', async({ render, page }) ...@@ -87,7 +87,7 @@ test('flatten source code method +@dark-mode +@mobile', async({ render, page })
await page.getByRole('option', { name: 'Solidity (Single file)' }).click(); await page.getByRole('option', { name: 'Solidity (Single file)' }).click();
await page.getByText(/add contract libraries/i).click(); await page.getByText(/add contract libraries/i).click();
await page.locator('button[aria-label="add"]').click(); await page.locator('button[aria-label="Add item"]').click();
await expect(component).toHaveScreenshot({ timeout: 10_000 }); await expect(component).toHaveScreenshot({ timeout: 10_000 });
}); });
......
...@@ -3,10 +3,10 @@ import React from 'react'; ...@@ -3,10 +3,10 @@ import React from 'react';
import type { FormFields } from '../types'; import type { FormFields } from '../types';
import { IconButton } from 'toolkit/chakra/icon-button'; import AddButton from 'toolkit/components/buttons/AddButton';
import RemoveButton from 'toolkit/components/buttons/RemoveButton';
import { FormFieldAddress } from 'toolkit/components/forms/fields/FormFieldAddress'; import { FormFieldAddress } from 'toolkit/components/forms/fields/FormFieldAddress';
import { FormFieldText } from 'toolkit/components/forms/fields/FormFieldText'; import { FormFieldText } from 'toolkit/components/forms/fields/FormFieldText';
import IconSvg from 'ui/shared/IconSvg';
import ContractVerificationFormRow from '../ContractVerificationFormRow'; import ContractVerificationFormRow from '../ContractVerificationFormRow';
...@@ -42,26 +42,16 @@ const ContractVerificationFieldLibraryItem = ({ index, fieldsLength, onAddFieldC ...@@ -42,26 +42,16 @@ const ContractVerificationFieldLibraryItem = ({ index, fieldsLength, onAddFieldC
<Text color="text.secondary" fontSize="sm">Contract library { index + 1 }</Text> <Text color="text.secondary" fontSize="sm">Contract library { index + 1 }</Text>
<Flex columnGap={ 5 }> <Flex columnGap={ 5 }>
{ fieldsLength > 1 && ( { fieldsLength > 1 && (
<IconButton <RemoveButton
aria-label="delete"
variant="outline"
size="md"
onClick={ handleRemoveButtonClick } onClick={ handleRemoveButtonClick }
disabled={ isDisabled } disabled={ isDisabled }
> />
<IconSvg name="minus"/>
</IconButton>
) } ) }
{ fieldsLength < LIMIT && ( { fieldsLength < LIMIT && (
<IconButton <AddButton
aria-label="add"
variant="outline"
size="md"
onClick={ handleAddButtonClick } onClick={ handleAddButtonClick }
disabled={ isDisabled } disabled={ isDisabled }
> />
<IconSvg name="plus"/>
</IconButton>
) } ) }
</Flex> </Flex>
</Flex> </Flex>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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