Commit 99cb9b03 authored by tom's avatar tom

sortable table column header

parent 1bf8899f
'use client';
import type { HTMLChakraProps, RecipeProps } from '@chakra-ui/react';
import { createRecipeContext } from '@chakra-ui/react';
export interface LinkButtonProps
extends HTMLChakraProps<'a', RecipeProps<'button'>> {}
const { withContext } = createRecipeContext({ key: 'button' });
// TODO @tom2drum style and use this component
// Replace "a" with your framework's link component
export const LinkButton = withContext<HTMLAnchorElement, LinkButtonProps>('a');
......@@ -2,6 +2,10 @@ import { Table as ChakraTable } from '@chakra-ui/react';
import { throttle } from 'es-toolkit';
import * as React from 'react';
import IconSvg from 'ui/shared/IconSvg';
import { Link } from './link';
export const TableRoot = ChakraTable.Root;
export const TableBody = ChakraTable.Body;
export const TableHeader = ChakraTable.Header;
......@@ -27,6 +31,40 @@ export const TableColumnHeader = (props: TableColumnHeaderProps) => {
return <ChakraTable.ColumnHeader textAlign={ isNumeric ? 'right' : undefined } { ...rest }/>;
};
export interface TableColumnHeaderSortableProps<F extends string> extends TableColumnHeaderProps {
sortField: F;
sortValue: string;
onSortToggle: (sortField: F) => void;
disabled?: boolean;
}
export const TableColumnHeaderSortable = <F extends string>(props: TableColumnHeaderSortableProps<F>) => {
const { sortField, sortValue, onSortToggle, children, disabled, ...rest } = props;
const handleSortToggle = React.useCallback(() => {
onSortToggle(sortField);
}, [ onSortToggle, sortField ]);
return (
<TableColumnHeader { ...rest }>
<Link onClick={ disabled ? undefined : handleSortToggle } position="relative">
{ sortValue.includes(sortField) && (
<IconSvg
name="arrows/east"
w={ 4 }
h="100%"
transform={ sortValue.toLowerCase().includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)' }
position="absolute"
left={ -5 }
top={ 0 }
/>
) }
{ children }
</Link>
</TableColumnHeader>
);
};
export interface TableHeaderProps extends ChakraTable.HeaderProps {
top?: number;
}
......
......@@ -210,6 +210,9 @@ export const recipe = defineRecipe({
bg: 'transparent',
color: 'link.primary.hover',
},
_disabled: {
color: 'text.secondary',
},
},
},
size: {
......
......@@ -15,9 +15,6 @@ export const recipe = defineRecipe({
textDecoration: 'none',
color: 'link.primary.hover',
},
_disabled: {
color: 'text.secondary',
},
},
secondary: {
color: 'link.secondary',
......
......@@ -2,11 +2,11 @@ import {
chakra,
Flex,
Text,
Link,
Button,
} from '@chakra-ui/react';
import React from 'react';
import { Button } from 'toolkit/chakra/button';
import ColumnFilterWrapper from './ColumnFilterWrapper';
type Props = {
......@@ -40,20 +40,17 @@ const ColumnFilterContent = ({ title, isFilled, onFilter, onReset, onClose, chil
<>
<Flex alignItems="center" justifyContent="space-between" mb={ 3 }>
<Text color="text_secondary" fontWeight="600">{ title }</Text>
<Link
<Button
variant="link"
onClick={ onReset }
cursor={ isFilled ? 'pointer' : 'unset' }
opacity={ isFilled ? 1 : 0.2 }
_hover={{
color: isFilled ? 'link_hovered' : 'none',
}}
disabled={ !isFilled }
>
Reset
</Link>
</Button>
</Flex>
{ children }
<Button
isDisabled={ !isFilled }
disabled={ !isFilled }
mt={ 4 }
onClick={ onFilterClick }
w="fit-content"
......
......@@ -31,11 +31,10 @@ const NameDomainHistory = ({ domain }: Props) => {
},
});
const handleSortToggle = React.useCallback((event: React.MouseEvent) => {
const handleSortToggle = React.useCallback((field: SortField) => {
if (isPlaceholderData) {
return;
}
const field = (event.currentTarget as HTMLDivElement).getAttribute('data-field') as SortField | undefined;
if (field) {
setSort(getNextSortValue(field));
......
......@@ -2,46 +2,35 @@ import React from 'react';
import type * as bens from '@blockscout/bens-types';
import { Link } from 'toolkit/chakra/link';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import IconSvg from 'ui/shared/IconSvg';
import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import NameDomainHistoryTableItem from './NameDomainHistoryTableItem';
import type { Sort } from './utils';
import type { SortField, Sort } from './utils';
import { sortFn } from './utils';
interface Props {
history: bens.ListDomainEventsResponse | undefined;
domain: bens.DetailedDomain | undefined;
isLoading?: boolean;
sort: Sort | undefined;
onSortToggle: (event: React.MouseEvent) => void;
sort: Sort;
onSortToggle: (field: SortField) => void;
}
const NameDomainHistoryTable = ({ history, domain, isLoading, sort, onSortToggle }: Props) => {
const sortIconTransform = sort?.includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)';
return (
<TableRoot>
<TableHeaderSticky top={ 0 }>
<TableRow>
<TableColumnHeader width="25%">Txn hash</TableColumnHeader>
<TableColumnHeader width="25%" pl={ 9 }>
<Link display="flex" alignItems="center" justifyContent="flex-start" position="relative" data-field="timestamp" onClick={ onSortToggle }>
{ sort?.includes('timestamp') && (
<IconSvg
name="arrows/east"
boxSize={ 4 }
transform={ sortIconTransform }
color="link.primary"
position="absolute"
left={ -5 }
top={ 0 }
/>
) }
<span>Age</span>
</Link>
</TableColumnHeader>
<TableColumnHeaderSortable
width="25%"
pl={ 9 }
sortField="timestamp"
sortValue={ sort }
onSortToggle={ onSortToggle }
>
Age
</TableColumnHeaderSortable>
<TableColumnHeader width="25%">From</TableColumnHeader>
<TableColumnHeader width="25%">Method</TableColumnHeader>
</TableRow>
......
......@@ -6,9 +6,9 @@ import type { EnsDomainLookupFiltersOptions } from 'types/api/ens';
import type { PaginationParams } from 'ui/shared/pagination/types';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import { Button } from 'toolkit/chakra/button';
import { Checkbox } from 'toolkit/chakra/checkbox';
import { Image } from 'toolkit/chakra/image';
import { Link } from 'toolkit/chakra/link';
import ActionBar from 'ui/shared/ActionBar';
import FilterInput from 'ui/shared/filters/FilterInput';
import PopoverFilter from 'ui/shared/filters/PopoverFilter';
......@@ -87,12 +87,13 @@ const NameDomainsActionBar = ({
<>
<Flex justifyContent="space-between" textStyle="sm" mb={ 3 }>
<Text fontWeight={ 600 } color="text.secondary">Protocol</Text>
<Link
<Button
variant="link"
onClick={ handleProtocolReset }
disabled={ protocolsFilterValue.length === 0 }
>
Reset
</Link>
</Button>
</Flex>
<Fieldset.Root>
<CheckboxGroup defaultValue={ protocolsFilterValue } onValueChange={ onProtocolsFilterChange } value={ protocolsFilterValue } name="token_type">
......
......@@ -2,46 +2,35 @@ import React from 'react';
import type * as bens from '@blockscout/bens-types';
import { Link } from 'toolkit/chakra/link';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import IconSvg from 'ui/shared/IconSvg';
import NameDomainsTableItem from './NameDomainsTableItem';
import { type Sort } from './utils';
import type { SortField, Sort } from './utils';
interface Props {
data: bens.LookupDomainNameResponse | undefined;
isLoading?: boolean;
sort: Sort;
onSortToggle: (event: React.MouseEvent) => void;
onSortToggle: (field: SortField) => void;
}
const NameDomainsTable = ({ data, isLoading, sort, onSortToggle }: Props) => {
const sortIconTransform = sort?.toLowerCase().includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)';
return (
<TableRoot>
<TableHeaderSticky top={ ACTION_BAR_HEIGHT_DESKTOP }>
<TableRow>
<TableColumnHeader width="25%">Domain</TableColumnHeader>
<TableColumnHeader width="25%">Address</TableColumnHeader>
<TableColumnHeader width="25%" pl={ 9 }>
<Link display="flex" alignItems="center" justifyContent="flex-start" position="relative" data-field="registration_date" onClick={ onSortToggle }>
{ sort?.includes('registration_date') && (
<IconSvg
name="arrows/east"
boxSize={ 4 }
transform={ sortIconTransform }
color="link.primary"
position="absolute"
left={ -5 }
top={ 0 }
/>
) }
<span>Registered on</span>
</Link>
</TableColumnHeader>
<TableColumnHeaderSortable
width="25%"
pl={ 9 }
sortField="registration_date"
sortValue={ sort }
onSortToggle={ onSortToggle }
>
Registered on
</TableColumnHeaderSortable>
<TableColumnHeader width="25%">Expiration date</TableColumnHeader>
</TableRow>
</TableHeaderSticky>
......
......@@ -21,12 +21,22 @@ import PinInputShowcase from 'ui/showcases/PinInput';
import ProgressCircleShowcase from 'ui/showcases/ProgressCircle';
import RadioShowcase from 'ui/showcases/Radio';
import SelectShowcase from 'ui/showcases/Select';
import TableShowcase from 'ui/showcases/Table';
import TabsShowcase from 'ui/showcases/Tabs';
import TagShowcase from 'ui/showcases/Tag';
import TextareaShowcase from 'ui/showcases/Textarea';
import ToastShowcase from 'ui/showcases/Toast';
import TooltipShowcase from 'ui/showcases/Tooltip';
// Drawer
// CloseButton
// IconButton
// EmptyState ?
// Rating
// Switch
// ToggleTip
// Popover
const tabs = [
{ label: 'Accordion', value: 'accordion', component: <AccordionsShowcase/> },
{ label: 'Alert', value: 'alert', component: <AlertShowcase/> },
......@@ -44,6 +54,7 @@ const tabs = [
{ label: 'Radio', value: 'radio', component: <RadioShowcase/> },
{ label: 'Pin input', value: 'pin-input', component: <PinInputShowcase/> },
{ label: 'Select', value: 'select', component: <SelectShowcase/> },
{ label: 'Table', value: 'table', component: <TableShowcase/> },
{ label: 'Tabs', value: 'tabs', component: <TabsShowcase/> },
{ label: 'Tag', value: 'tag', component: <TagShowcase/> },
{ label: 'Textarea', value: 'textarea', component: <TextareaShowcase/> },
......
......@@ -110,11 +110,10 @@ const NameDomains = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ isAddressSearch ]);
const handleSortToggle = React.useCallback((event: React.MouseEvent) => {
const handleSortToggle = React.useCallback((field: SortField) => {
if (isLoading) {
return;
}
const field = (event.currentTarget as HTMLDivElement).getAttribute('data-field') as SortField | undefined;
if (field) {
setSort((prevValue) => {
......
......@@ -6,7 +6,6 @@ import {
import React from 'react';
import { Button } from 'toolkit/chakra/button';
import { Link } from 'toolkit/chakra/link';
import { PopoverCloseTriggerWrapper } from 'toolkit/chakra/popover';
type Props = {
......@@ -28,16 +27,13 @@ const TableColumnFilter = ({ title, isFilled, isTouched, hasReset, onFilter, onR
<Flex alignItems="center" justifyContent="space-between">
<Text color="text.secondary" fontWeight="600">{ title }</Text>
{ hasReset && (
<Link
<Button
variant="link"
onClick={ onReset }
cursor={ isFilled ? 'pointer' : 'unset' }
opacity={ isFilled ? 1 : 0.2 }
_hover={{
color: isFilled ? 'link_hovered' : 'none',
}}
disabled={ !isFilled }
>
Reset
</Link>
</Button>
) }
</Flex>
{ children }
......
......@@ -4,8 +4,8 @@ import React from 'react';
import type { NFTTokenType, TokenType } from 'types/api/token';
import { TOKEN_TYPES, TOKEN_TYPE_IDS, NFT_TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
import { Button } from 'toolkit/chakra/button';
import { Checkbox } from 'toolkit/chakra/checkbox';
import { Link } from 'toolkit/chakra/link';
type Props<T extends TokenType | NFTTokenType> = {
onChange: (nextValue: Array<T>) => void;
......@@ -32,12 +32,13 @@ const TokenTypeFilter = <T extends TokenType | NFTTokenType>({ nftOnly, onChange
<>
<Flex justifyContent="space-between" fontSize="sm">
<Text fontWeight={ 600 } color="text.secondary">Type</Text>
<Link
<Button
variant="link"
onClick={ handleReset }
disabled={ value.length === 0 }
>
Reset
</Link>
</Button>
</Flex>
<Fieldset.Root>
<CheckboxGroup defaultValue={ defaultValue } onValueChange={ handleChange } value={ value } name="token_type">
......
......@@ -26,9 +26,8 @@ const TxWatchListTags = ({ tx, isLoading }: Props) => {
<Badge
key={ tag.label }
loading={ isLoading }
truncate
// TODO @tom2drum check these styles
// maxW={{ base: '115px', lg: 'initial' }}
truncated
maxW={{ base: '115px', lg: 'initial' }}
colorPalette="gray"
>
{ tag.display_name }
......
......@@ -4,7 +4,7 @@ import { IconButton } from 'toolkit/chakra/icon-button';
import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from 'toolkit/chakra/menu';
import IconSvg from 'ui/shared/IconSvg';
import { Section, Container, SectionHeader, SamplesStack, Sample, SectionSubHeader } from './parts';
import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts';
const MenuShowcase = () => {
......@@ -44,11 +44,6 @@ const MenuShowcase = () => {
</Sample>
</SamplesStack>
</Section>
<Section>
<SectionHeader>Examples</SectionHeader>
<SectionSubHeader>Example 1</SectionSubHeader>
</Section>
</Container>
);
};
......
......@@ -23,8 +23,6 @@ const txSortingOptions = createListCollection({
items: SORT_OPTIONS,
});
// TODO @tom2drum + tanya: select with search
const SelectShowcase = () => {
const [ hasActiveFilter, setHasActiveFilter ] = React.useState(false);
......@@ -52,8 +50,8 @@ const SelectShowcase = () => {
</SelectRoot>
</Sample>
<Sample label="variant: filter">
<SelectRoot collection={ frameworks } variant="filter" multiple>
<SelectControl w="200px">
<SelectRoot collection={ frameworks } variant="filter">
<SelectControl w="200px" noIndicator>
<SelectValueText placeholder="Select framework"/>
</SelectControl>
<SelectContent>
......
import { Box } from '@chakra-ui/react';
import React from 'react';
import { TableColumnHeader, TableHeaderSticky, TableRoot, TableRow, TableCell, TableBody, TableColumnHeaderSortable } from 'toolkit/chakra/table';
import getNextSortValue from 'ui/shared/sort/getNextSortValue';
import { Section, Container, SectionHeader, SamplesStack, Sample } from './parts';
const ITEMS = [
{ id: 1, name: 'Laptop', category: 'Electronics', price: 999.99 },
{ id: 2, name: 'Coffee Maker', category: 'Home Appliances', price: 49.99 },
{ id: 3, name: 'Desk Chair', category: 'Furniture', price: 150.0 },
{ id: 4, name: 'Smartphone', category: 'Electronics', price: 799.99 },
{ id: 5, name: 'Headphones', category: 'Accessories', price: 199.99 },
];
type Item = typeof ITEMS[number];
type SortField = 'category' | 'price';
type SortValue = 'category-asc' | 'category-desc' | 'price-asc' | 'price-desc' | 'default';
const SORT_SEQUENCE: Record<SortField, Array<SortValue>> = {
category: [ 'category-desc', 'category-asc', 'default' ],
price: [ 'price-desc', 'price-asc', 'default' ],
};
const TableShowcase = () => {
const [ sort, setSort ] = React.useState<SortValue>('default');
const handleSortToggle = React.useCallback((sortField: string) => {
const value = getNextSortValue<SortField, SortValue>(SORT_SEQUENCE, sortField as SortField)(sort);
setSort(value);
}, [ sort, setSort ]);
const sortFn = (a: Item, b: Item) => {
if (sort === 'category-asc') {
return a.category.localeCompare(b.category);
}
if (sort === 'category-desc') {
return b.category.localeCompare(a.category);
}
if (sort === 'price-asc') {
return a.price - b.price;
}
if (sort === 'price-desc') {
return b.price - a.price;
}
return 0;
};
return (
<Container value="table">
<Section>
<SectionHeader>Variant</SectionHeader>
<SamplesStack >
<Sample label="variant: line">
<TableRoot>
<TableHeaderSticky>
<TableRow>
<TableColumnHeader>Product</TableColumnHeader>
<TableColumnHeaderSortable
sortField="category"
sortValue={ sort }
onSortToggle={ handleSortToggle }
>
Category
</TableColumnHeaderSortable>
<TableColumnHeaderSortable
sortField="price"
sortValue={ sort }
onSortToggle={ handleSortToggle }
isNumeric
>
Price
</TableColumnHeaderSortable>
</TableRow>
</TableHeaderSticky>
<TableBody>
{ ITEMS.slice().sort(sortFn).map((item) => (
<TableRow key={ item.id }>
<TableCell>{ item.name }</TableCell>
<TableCell>{ item.category }</TableCell>
<TableCell isNumeric>{ item.price }</TableCell>
</TableRow>
)) }
</TableBody>
</TableRoot>
<Box h="1000px"/>
</Sample>
</SamplesStack>
</Section>
</Container>
);
};
export default React.memo(TableShowcase);
......@@ -2,8 +2,8 @@ import { CheckboxGroup, Text, Flex, useCheckboxGroup, chakra, Fieldset } from '@
import React from 'react';
import config from 'configs/app';
import { Button } from 'toolkit/chakra/button';
import { Checkbox } from 'toolkit/chakra/checkbox';
import { Link } from 'toolkit/chakra/link';
const feature = config.features.bridgedTokens;
......@@ -36,12 +36,13 @@ const TokensBridgedChainsFilter = ({ onChange, defaultValue }: Props) => {
<>
<Flex justifyContent="space-between" fontSize="sm">
<Text fontWeight={ 600 } color="text.secondary">Show bridged tokens from</Text>
<Link
<Button
variant="link"
onClick={ handleReset }
disabled={ value.length === 0 }
>
Reset
</Link>
</Button>
</Flex>
<Fieldset.Root>
<CheckboxGroup defaultValue={ defaultValue } onValueChange={ handleChange } value={ value } name="bridged_token_chain">
......
......@@ -83,12 +83,11 @@ const TxInternals = ({ txQuery }: Props) => {
// }, []);
const handleSortToggle = React.useCallback((field: SortField) => {
return () => {
if (isPlaceholderData) {
return;
}
setSort(getNextSortValue(field));
};
}, [ isPlaceholderData ]);
if (!txQuery.isPlaceholderData && !txQuery.isError && !txQuery.data?.status) {
......
......@@ -12,7 +12,7 @@ interface Props {
isLoading?: boolean;
}
const TxInternalsTable = ({ data, top, isLoading }: Props) => {
const TxBlobsTable = ({ data, top, isLoading }: Props) => {
return (
<TableRoot>
......@@ -32,4 +32,4 @@ const TxInternalsTable = ({ data, top, isLoading }: Props) => {
);
};
export default TxInternalsTable;
export default TxBlobsTable;
......@@ -4,23 +4,19 @@ import type { InternalTransaction } from 'types/api/internalTransaction';
import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import { currencyUnits } from 'lib/units';
import { Link } from 'toolkit/chakra/link';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import IconSvg from 'ui/shared/IconSvg';
import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import TxInternalsTableItem from 'ui/tx/internals/TxInternalsTableItem';
import type { Sort, SortField } from 'ui/tx/internals/utils';
interface Props {
data: Array<InternalTransaction>;
sort: Sort | undefined;
onSortToggle: (field: SortField) => () => void;
sort: Sort;
onSortToggle: (field: SortField) => void;
top: number;
isLoading?: boolean;
}
const TxInternalsTable = ({ data, sort, onSortToggle, top, isLoading }: Props) => {
const sortIconTransform = sort?.includes('asc') ? 'rotate(-90deg)' : 'rotate(90deg)';
return (
<AddressHighlightProvider>
<TableRoot>
......@@ -28,18 +24,24 @@ const TxInternalsTable = ({ data, sort, onSortToggle, top, isLoading }: Props) =
<TableRow>
<TableColumnHeader width="28%">Type</TableColumnHeader>
<TableColumnHeader width="40%">From/To</TableColumnHeader>
<TableColumnHeader width="16%" isNumeric>
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('value') } columnGap={ 1 }>
{ sort?.includes('value') && <IconSvg name="arrows/east" boxSize={ 4 } transform={ sortIconTransform }/> }
<TableColumnHeaderSortable
width="16%"
isNumeric
sortField="value"
sortValue={ sort }
onSortToggle={ onSortToggle }
>
Value { currencyUnits.ether }
</Link>
</TableColumnHeader>
<TableColumnHeader width="16%" isNumeric>
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ onSortToggle('gas-limit') } columnGap={ 1 }>
{ sort?.includes('gas-limit') && <IconSvg name="arrows/east" boxSize={ 4 } transform={ sortIconTransform }/> }
</TableColumnHeaderSortable>
<TableColumnHeaderSortable
width="16%"
isNumeric
sortField="gas-limit"
sortValue={ sort }
onSortToggle={ onSortToggle }
>
Gas limit { currencyUnits.ether }
</Link>
</TableColumnHeader>
</TableColumnHeaderSortable>
</TableRow>
</TableHeaderSticky>
<TableBody>
......
......@@ -25,7 +25,6 @@ type Props =
className?: string;
};
// TODO @tom2drum fix other popovers
const TxAdditionalInfo = ({ hash, tx, isMobile, isLoading, className }: Props) => {
const content = hash !== undefined ? <TxAdditionalInfoContainer hash={ hash }/> : <TxAdditionalInfoContent tx={ tx }/>;
......
......@@ -60,7 +60,7 @@ const TxsContent = ({
}: Props) => {
const isMobile = useIsMobile();
const onSortToggle = React.useCallback((field: TransactionsSortingField) => () => {
const onSortToggle = React.useCallback((field: TransactionsSortingField) => {
const value = getNextSortValue<TransactionsSortingField, TransactionsSortingValue>(SORT_SEQUENCE, field)(sort);
setSorting(value);
}, [ sort, setSorting ]);
......@@ -84,8 +84,8 @@ const TxsContent = ({
<Box hideBelow="lg">
<TxsTable
txs={ itemsWithTranslation }
sort={ onSortToggle }
sorting={ sort }
sort={ sort }
onSortToggle={ onSortToggle }
showBlockInfo={ showBlockInfo }
showSocketInfo={ showSocketInfo }
socketInfoAlert={ socketInfoAlert }
......
......@@ -10,8 +10,9 @@ test('base view +@dark-mode', async({ render }) => {
const component = await render(
<TxsTable
txs={ [ txMock.base, txMock.withWatchListNames ] }
sort="default"
// eslint-disable-next-line react/jsx-no-bind
sort={ () => () => {} }
onSortToggle={ () => {} }
top={ 0 }
showBlockInfo
showSocketInfo={ false }
......@@ -30,8 +31,9 @@ test.describe('screen xl', () => {
const component = await render(
<TxsTable
txs={ [ txMock.base, txMock.withWatchListNames ] }
sort="default"
// eslint-disable-next-line react/jsx-no-bind
sort={ () => () => {} }
onSortToggle={ () => {} }
top={ 0 }
showBlockInfo
showSocketInfo={ false }
......
......@@ -7,17 +7,15 @@ import { AddressHighlightProvider } from 'lib/contexts/addressHighlight';
import useInitialList from 'lib/hooks/useInitialList';
import useLazyRenderedList from 'lib/hooks/useLazyRenderedList';
import { currencyUnits } from 'lib/units';
import { Link } from 'toolkit/chakra/link';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import IconSvg from 'ui/shared/IconSvg';
import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import * as SocketNewItemsNotice from 'ui/shared/SocketNewItemsNotice';
import TxsTableItem from './TxsTableItem';
type Props = {
txs: Array<Transaction>;
sort: (field: TransactionsSortingField) => () => void;
sorting?: TransactionsSortingValue;
sort: TransactionsSortingValue;
onSortToggle: (field: TransactionsSortingField) => void;
top: number;
showBlockInfo: boolean;
showSocketInfo: boolean;
......@@ -31,7 +29,7 @@ type Props = {
const TxsTable = ({
txs,
sort,
sorting,
onSortToggle,
top,
showBlockInfo,
showSocketInfo,
......@@ -62,32 +60,38 @@ const TxsTable = ({
<TableColumnHeader width="160px">Type</TableColumnHeader>
<TableColumnHeader width="20%">Method</TableColumnHeader>
{ showBlockInfo && (
<TableColumnHeader width="18%">
<Link onClick={ isLoading ? undefined : sort('block_number') } display="flex" alignItems="center">
{ sorting === 'block_number-asc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(-90deg)"/> }
{ sorting === 'block_number-desc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(90deg)"/> }
<TableColumnHeaderSortable
width="18%"
sortField="block_number"
sortValue={ sort }
onSortToggle={ onSortToggle }
>
Block
</Link>
</TableColumnHeader>
</TableColumnHeaderSortable>
) }
<TableColumnHeader width="224px">From/To</TableColumnHeader>
{ !config.UI.views.tx.hiddenFields?.value && (
<TableColumnHeader width="20%" isNumeric>
<Link onClick={ isLoading ? undefined : sort('value') } display="flex" alignItems="center" justifyContent="end">
{ sorting === 'value-asc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(-90deg)"/> }
{ sorting === 'value-desc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(90deg)"/> }
<TableColumnHeaderSortable
width="20%"
isNumeric
sortField="value"
sortValue={ sort }
onSortToggle={ onSortToggle }
>
{ `Value ${ currencyUnits.ether }` }
</Link>
</TableColumnHeader>
</TableColumnHeaderSortable>
) }
{ !config.UI.views.tx.hiddenFields?.tx_fee && (
<TableColumnHeader width="20%" isNumeric pr={ 5 }>
<Link onClick={ isLoading ? undefined : sort('fee') } display="flex" alignItems="center" justifyContent="end">
{ sorting === 'fee-asc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(-90deg)"/> }
{ sorting === 'fee-desc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(90deg)"/> }
<TableColumnHeaderSortable
width="20%"
isNumeric
pr={ 5 }
sortField="fee"
sortValue={ sort }
onSortToggle={ onSortToggle }
>
{ `Fee${ feeCurrency }` }
</Link>
</TableColumnHeader>
</TableColumnHeaderSortable>
) }
</TableRow>
</TableHeaderSticky>
......
import React from 'react';
import type { VerifiedContract } from 'types/api/contracts';
import type { VerifiedContractsSorting, VerifiedContractsSortingField, VerifiedContractsSortingValue } from 'types/api/verifiedContracts';
import type { VerifiedContractsSortingField, VerifiedContractsSortingValue } from 'types/api/verifiedContracts';
import { currencyUnits } from 'lib/units';
import { Link } from 'toolkit/chakra/link';
import { TableBody, TableColumnHeader, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import { TableBody, TableColumnHeader, TableColumnHeaderSortable, TableHeaderSticky, TableRoot, TableRow } from 'toolkit/chakra/table';
import { ACTION_BAR_HEIGHT_DESKTOP } from 'ui/shared/ActionBar';
import IconSvg from 'ui/shared/IconSvg';
import getNextSortValue from 'ui/shared/sort/getNextSortValue';
import { SORT_SEQUENCE } from 'ui/verifiedContracts/utils';
......@@ -21,9 +19,7 @@ interface Props {
}
const VerifiedContractsTable = ({ data, sort, setSorting, isLoading }: Props) => {
const sortIconTransform = sort?.includes('asc' as VerifiedContractsSorting['order']) ? 'rotate(-90deg)' : 'rotate(90deg)';
const onSortToggle = React.useCallback((field: VerifiedContractsSortingField) => () => {
const onSortToggle = React.useCallback((field: VerifiedContractsSortingField) => {
const value = getNextSortValue<VerifiedContractsSortingField, VerifiedContractsSortingValue>(SORT_SEQUENCE, field)(sort);
setSorting({ value: [ value ] });
}, [ sort, setSorting ]);
......@@ -33,24 +29,26 @@ const VerifiedContractsTable = ({ data, sort, setSorting, isLoading }: Props) =>
<TableHeaderSticky top={ ACTION_BAR_HEIGHT_DESKTOP }>
<TableRow>
<TableColumnHeader width="50%">Contract</TableColumnHeader>
<TableColumnHeader width="130px" isNumeric>
<Link display="flex" alignItems="center" justifyContent="flex-end" onClick={ isLoading ? undefined : onSortToggle('balance') } columnGap={ 1 }>
{ sort?.includes('balance') && <IconSvg name="arrows/east" boxSize={ 4 } transform={ sortIconTransform }/> }
<TableColumnHeaderSortable
width="130px"
isNumeric
sortField="balance"
sortValue={ sort }
onSortToggle={ onSortToggle }
disabled={ isLoading }
>
Balance { currencyUnits.ether }
</Link>
</TableColumnHeader>
<TableColumnHeader width="130px" isNumeric>
<Link
display="flex"
alignItems="center"
justifyContent="flex-end"
onClick={ isLoading ? undefined : onSortToggle('transactions_count') }
columnGap={ 1 }
</TableColumnHeaderSortable>
<TableColumnHeaderSortable
width="130px"
isNumeric
sortField="transactions_count"
sortValue={ sort }
onSortToggle={ onSortToggle }
disabled={ isLoading }
>
{ sort?.includes('transactions_count') && <IconSvg name="arrows/east" boxSize={ 4 } transform={ sortIconTransform }/> }
Txs
</Link>
</TableColumnHeader>
</TableColumnHeaderSortable>
<TableColumnHeader width="50%">Language / Compiler version</TableColumnHeader>
<TableColumnHeader width="80px">Settings</TableColumnHeader>
<TableColumnHeader width="150px">Verified</TableColumnHeader>
......
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