design fixes and tests
@@ -1,4 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 30 30">
|
||||
<path fill="#4A5568" fill-rule="evenodd" d="M7.904 22.831c.335.102.681.153 1.029.152a3.59 3.59 0 0 0 1.907-.583 4.14 4.14 0 0 0 1.17-1.155 4.666 4.666 0 0 0 .68-1.57l.165-.688h4.287l.128.704c.132.569.363 1.102.68 1.57.319.468.716.86 1.171 1.155a3.61 3.61 0 0 0 1.444.548 3.483 3.483 0 0 0 1.521-.117c1.011-.32 1.87-1.07 2.39-2.092.52-1.021.66-2.23.39-3.366L23.2 10.31a4.69 4.69 0 0 0-.682-1.569 4.16 4.16 0 0 0-1.169-1.156 3.61 3.61 0 0 0-1.444-.548 3.483 3.483 0 0 0-1.52.117 3.793 3.793 0 0 0-1.442.832 4.34 4.34 0 0 0-1.017 1.413H14.07a4.357 4.357 0 0 0-1.018-1.411 3.814 3.814 0 0 0-1.44-.834 3.483 3.483 0 0 0-1.521-.117 3.6 3.6 0 0 0-1.444.548 4.14 4.14 0 0 0-1.173 1.162 4.668 4.668 0 0 0-.678 1.58L5.132 17.39a4.944 4.944 0 0 0 .393 3.354c.518 1.017 1.372 1.767 2.379 2.088ZM9.36 8.967c.375-.24.798-.366 1.23-.368.216 0 .433.033.642.096.39.123.747.349 1.042.657.296.309.522.691.659 1.117l.171.527h3.787l.171-.527a2.88 2.88 0 0 1 .655-1.116c.294-.309.65-.534 1.039-.658a2.22 2.22 0 0 1 .963-.073 2.3 2.3 0 0 1 .916.345c.29.19.544.442.746.742.203.3.35.643.432 1.008l1.665 7.064a3.162 3.162 0 0 1-.237 2.16c-.33.657-.88 1.141-1.527 1.348-.314.095-.642.12-.964.073a2.298 2.298 0 0 1-.916-.345 2.675 2.675 0 0 1-.749-.742 3.016 3.016 0 0 1-.437-1.008l-.435-1.878h-6.43l-.444 1.878a2.982 2.982 0 0 1-.435 1.01 2.64 2.64 0 0 1-.75.74 2.28 2.28 0 0 1-.912.346 2.199 2.199 0 0 1-.96-.074c-.648-.209-1.196-.693-1.527-1.35a3.179 3.179 0 0 1-.245-2.158l1.664-7.064c.173-.736.6-1.365 1.186-1.75ZM20 12.287a.714.714 0 1 1-1.43.001.714.714 0 0 1 1.43 0ZM9.918 14.906a1.43 1.43 0 1 0 1.588-2.377 1.43 1.43 0 0 0-1.588 2.377Zm9.367.955a.714.714 0 1 0 0-1.429.714.714 0 0 0 0 1.43Zm-.714-2.143a.714.714 0 1 1-1.429 0 .714.714 0 0 1 1.429 0Zm2.143.714a.714.714 0 1 0 0-1.429.714.714 0 0 0 0 1.43Z" clip-rule="evenodd"/>
|
||||
<path fill="#000" fill-opacity=".2" fill-rule="evenodd" d="M7.904 22.831c.335.102.681.153 1.029.152a3.59 3.59 0 0 0 1.907-.583 4.14 4.14 0 0 0 1.17-1.155 4.666 4.666 0 0 0 .68-1.57l.165-.688h4.287l.128.704c.132.569.363 1.102.68 1.57.319.468.716.86 1.171 1.155a3.61 3.61 0 0 0 1.444.548 3.483 3.483 0 0 0 1.521-.117c1.011-.32 1.87-1.07 2.39-2.092.52-1.021.66-2.23.39-3.366L23.2 10.31a4.69 4.69 0 0 0-.682-1.569 4.16 4.16 0 0 0-1.169-1.156 3.61 3.61 0 0 0-1.444-.548 3.483 3.483 0 0 0-1.52.117 3.793 3.793 0 0 0-1.442.832 4.34 4.34 0 0 0-1.017 1.413H14.07a4.357 4.357 0 0 0-1.018-1.411 3.814 3.814 0 0 0-1.44-.834 3.483 3.483 0 0 0-1.521-.117 3.6 3.6 0 0 0-1.444.548 4.14 4.14 0 0 0-1.173 1.162 4.668 4.668 0 0 0-.678 1.58L5.132 17.39a4.944 4.944 0 0 0 .393 3.354c.518 1.017 1.372 1.767 2.379 2.088ZM9.36 8.967c.375-.24.798-.366 1.23-.368.216 0 .433.033.642.096.39.123.747.349 1.042.657.296.309.522.691.659 1.117l.171.527h3.787l.171-.527a2.88 2.88 0 0 1 .655-1.116c.294-.309.65-.534 1.039-.658a2.22 2.22 0 0 1 .963-.073 2.3 2.3 0 0 1 .916.345c.29.19.544.442.746.742.203.3.35.643.432 1.008l1.665 7.064a3.162 3.162 0 0 1-.237 2.16c-.33.657-.88 1.141-1.527 1.348-.314.095-.642.12-.964.073a2.298 2.298 0 0 1-.916-.345 2.675 2.675 0 0 1-.749-.742 3.016 3.016 0 0 1-.437-1.008l-.435-1.878h-6.43l-.444 1.878a2.982 2.982 0 0 1-.435 1.01 2.64 2.64 0 0 1-.75.74 2.28 2.28 0 0 1-.912.346 2.199 2.199 0 0 1-.96-.074c-.648-.209-1.196-.693-1.527-1.35a3.179 3.179 0 0 1-.245-2.158l1.664-7.064c.173-.736.6-1.365 1.186-1.75ZM20 12.287a.714.714 0 1 1-1.43.001.714.714 0 0 1 1.43 0ZM9.918 14.906a1.43 1.43 0 1 0 1.588-2.377 1.43 1.43 0 0 0-1.588 2.377Zm9.367.955a.714.714 0 1 0 0-1.429.714.714 0 0 0 0 1.43Zm-.714-2.143a.714.714 0 1 1-1.429 0 .714.714 0 0 1 1.429 0Zm2.143.714a.714.714 0 1 0 0-1.429.714.714 0 0 0 0 1.43Z" clip-rule="evenodd"/>
|
||||
<path fill="currentColor" d="M6.485 23.997a4.23 4.23 0 0 0 1.234.183 4.31 4.31 0 0 0 2.29-.7c.545-.353 1.022-.824 1.403-1.386s.66-1.202.817-1.884l.197-.825h5.144l.155.844c.157.682.435 1.323.816 1.885s.859 1.032 1.404 1.385a4.334 4.334 0 0 0 1.733.658 4.18 4.18 0 0 0 1.825-.14c1.214-.384 2.244-1.285 2.868-2.51a5.933 5.933 0 0 0 .467-4.04l-1.997-8.496c-.16-.682-.437-1.322-.818-1.883s-.858-1.033-1.403-1.387a4.333 4.333 0 0 0-1.733-.657 4.18 4.18 0 0 0-1.825.14c-.632.194-1.221.534-1.73.998s-.923 1.042-1.22 1.696h-2.228c-.298-.653-.714-1.23-1.222-1.694s-1.096-.804-1.728-1a4.18 4.18 0 0 0-1.825-.14c-.61.088-1.2.312-1.733.657A4.969 4.969 0 0 0 5.97 7.095a5.602 5.602 0 0 0-.813 1.895l-1.998 8.477a5.932 5.932 0 0 0 .472 4.025c.621 1.22 1.646 2.12 2.855 2.505zM8.234 7.36a2.752 2.752 0 0 1 1.474-.441c.26 0 .52.04.772.115.467.148.895.418 1.25.788.355.37.626.83.79 1.34l.206.634h4.544l.206-.633c.163-.51.432-.97.786-1.34s.78-.64 1.246-.789c.377-.114.77-.144 1.156-.088.387.056.76.197 1.099.414.348.228.653.53.896.891.243.36.419.772.518 1.21l1.998 8.476c.214.872.112 1.804-.285 2.592s-1.055 1.37-1.833 1.618c-.376.114-.77.144-1.156.088a2.761 2.761 0 0 1-1.098-.414c-.35-.228-.655-.531-.9-.892s-.422-.77-.524-1.208l-.523-2.254H11.14l-.532 2.254c-.1.438-.277.85-.522 1.21-.244.361-.55.663-.9.89a2.738 2.738 0 0 1-1.095.414 2.64 2.64 0 0 1-1.152-.088c-.777-.25-1.435-.832-1.832-1.62a3.815 3.815 0 0 1-.294-2.59L6.81 9.46c.208-.883.72-1.638 1.424-2.1zm12.765 3.985a.857.857 0 1 1-1.714 0 .857.857 0 0 1 1.714 0zm-12.098 3.14a1.715 1.715 0 1 0 1.906-2.85 1.715 1.715 0 0 0-1.906 2.85zm11.241 1.147a.857.857 0 1 0 0-1.714.857.857 0 0 0 0 1.714zm-.857-2.572a.857.857 0 1 1-1.714 0 .857.857 0 0 1 1.714 0zm2.572.857a.857.857 0 1 0 0-1.714.857.857 0 0 0 0 1.714z" fill-rule="evenodd" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,94 @@
|
||||
/* eslint-disable max-len */
|
||||
import type { AddressMudRecord, AddressMudRecords, AddressMudRecordsItem, AddressMudTables } from 'types/api/address';
|
||||
import type { MudWorldSchema, MudWorldTable } from 'types/api/mudWorlds';
|
||||
|
||||
export const table1: MudWorldTable = {
|
||||
table_full_name: 'tb.store.Tables',
|
||||
table_id: '0x746273746f72650000000000000000005461626c657300000000000000000000',
|
||||
table_name: 'Tables',
|
||||
table_namespace: 'store',
|
||||
table_type: 'onchain',
|
||||
};
|
||||
|
||||
export const table2: MudWorldTable = {
|
||||
table_full_name: 'ot.world.FunctionSignatur',
|
||||
table_id: '0x6f74776f726c6400000000000000000046756e6374696f6e5369676e61747572',
|
||||
table_name: 'FunctionSignatur',
|
||||
table_namespace: 'world',
|
||||
table_type: 'offchain',
|
||||
};
|
||||
|
||||
export const schema1: MudWorldSchema = {
|
||||
key_names: [ 'moduleAddress', 'argumentsHash' ],
|
||||
key_types: [ 'address', 'bytes32' ],
|
||||
value_names: [ 'fieldLayout', 'keySchema', 'valueSchema', 'abiEncodedKeyNames', 'abiEncodedFieldNames' ],
|
||||
value_types: [ 'bytes32', 'bytes32', 'bytes32', 'bytes', 'bytes' ],
|
||||
};
|
||||
|
||||
export const schema2: MudWorldSchema = {
|
||||
key_names: [],
|
||||
key_types: [],
|
||||
value_names: [ 'value' ],
|
||||
value_types: [ 'address' ],
|
||||
};
|
||||
|
||||
export const mudTables: AddressMudTables = {
|
||||
items: [
|
||||
{
|
||||
table: table1,
|
||||
schema: schema1,
|
||||
},
|
||||
{
|
||||
table: table2,
|
||||
schema: schema2,
|
||||
},
|
||||
],
|
||||
next_page_params: {
|
||||
items_count: 50,
|
||||
table_id: '1',
|
||||
},
|
||||
};
|
||||
|
||||
const record: AddressMudRecordsItem = {
|
||||
decoded: {
|
||||
abiEncodedFieldNames: '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000006706c617965720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000576616c7565000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000974696d657374616d700000000000000000000000000000000000000000000000',
|
||||
abiEncodedKeyNames: '0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000026964000000000000000000000000000000000000000000000000000000000000',
|
||||
goldCosts: [ '100000', '150000', '200000', '250000', '400000', '550000', '700000' ],
|
||||
prototypeIds: [
|
||||
'0x53776f7264736d616e0000000000000000000000000000000000000000000000',
|
||||
'0x50696b656d616e00000000000000000000000000000000000000000000000000',
|
||||
'0x50696b656d616e00000000000000000000000000000000000000000000000000',
|
||||
'0x4172636865720000000000000000000000000000000000000000000000000000',
|
||||
'0x4b6e696768740000000000000000000000000000000000000000000000000000',
|
||||
],
|
||||
keySchema: '0x002001001f000000000000000000000000000000000000000000000000000000',
|
||||
tableId: '0x6f74000000000000000000000000000044726177557064617465000000000000',
|
||||
valueSchema: '0x00540300611f1f00000000000000000000000000000000000000000000000000',
|
||||
},
|
||||
id: '0x007a651a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007',
|
||||
is_deleted: false,
|
||||
timestamp: '2024-05-09T15:14:32.000000Z',
|
||||
};
|
||||
|
||||
export const mudRecords: AddressMudRecords = {
|
||||
items: [ record, record ],
|
||||
next_page_params: {
|
||||
items_count: 50,
|
||||
key0: '1',
|
||||
key1: '2',
|
||||
key_bytes: '3',
|
||||
},
|
||||
schema: {
|
||||
key_names: [ 'tableId' ],
|
||||
key_types: [ 'bytes32' ],
|
||||
value_names: [ 'prototypeIds', 'goldCosts', 'keySchema', 'valueSchema', 'abiEncodedKeyNames', 'abiEncodedFieldNames' ],
|
||||
value_types: [ 'bytes32[]', 'int32[]', 'bytes32', 'bytes32', 'bytes32', 'bytes', 'bytes' ],
|
||||
},
|
||||
table: table1,
|
||||
};
|
||||
|
||||
export const mudRecord: AddressMudRecord = {
|
||||
record,
|
||||
schema: mudRecords.schema,
|
||||
table: table1,
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { MudWorldsResponse } from 'types/api/mudWorlds';
|
||||
|
||||
import { withName, withoutName } from 'mocks/address/address';
|
||||
|
||||
export const mudWorlds: MudWorldsResponse = {
|
||||
items: [
|
||||
{
|
||||
address: withName,
|
||||
coin_balance: '300000000000000000',
|
||||
tx_count: 3938,
|
||||
},
|
||||
{
|
||||
address: withoutName,
|
||||
coin_balance: '0',
|
||||
tx_count: 0,
|
||||
},
|
||||
{
|
||||
address: withoutName,
|
||||
coin_balance: '0',
|
||||
tx_count: 0,
|
||||
},
|
||||
],
|
||||
next_page_params: {
|
||||
items_count: 50,
|
||||
world: '0x18f01f12ca21b6fc97b917c3e32f671f8a933caa',
|
||||
},
|
||||
};
|
||||
@@ -230,7 +230,7 @@ export type AddressMudRecords = {
|
||||
}
|
||||
|
||||
export type AddressMudRecordsItem = {
|
||||
decoded: Record<string, string>;
|
||||
decoded: Record<string, string | Array<string>>;
|
||||
id: string;
|
||||
is_deleted: boolean;
|
||||
timestamp: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Box, useColorModeValue, chakra, HStack } from '@chakra-ui/react';
|
||||
import { Box, useColorModeValue, chakra, Grid } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import { route } from 'nextjs-routes';
|
||||
@@ -8,6 +8,8 @@ import CopyToClipboard from 'ui/shared/CopyToClipboard';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import LinkInternal from 'ui/shared/links/LinkInternal';
|
||||
|
||||
import useAddressQuery from '../utils/useAddressQuery';
|
||||
|
||||
type TableViewProps = {
|
||||
scrollRef?: React.RefObject<HTMLDivElement>;
|
||||
className?: string;
|
||||
@@ -40,7 +42,7 @@ const BreadcrumbItem = ({ text, href, isLast, scrollRef }: BreadcrumbItemProps)
|
||||
|
||||
if (isLast) {
|
||||
return (
|
||||
<HStack gap={ 2 } overflow="hidden">
|
||||
<Grid gap={ 2 } overflow="hidden" templateColumns="auto 24px">
|
||||
<Box
|
||||
overflow="hidden"
|
||||
whiteSpace="nowrap"
|
||||
@@ -49,12 +51,12 @@ const BreadcrumbItem = ({ text, href, isLast, scrollRef }: BreadcrumbItemProps)
|
||||
{ text }
|
||||
</Box>
|
||||
<CopyToClipboard text={ href } type="link" mx={ 0 } color="text_secondary"/>
|
||||
</HStack>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<HStack gap={ 2 } overflow="hidden">
|
||||
<Grid gap={ 2 } overflow="hidden" templateColumns="auto 24px">
|
||||
<LinkInternal
|
||||
href={ href }
|
||||
onClick={ onLinkClick }
|
||||
@@ -65,7 +67,7 @@ const BreadcrumbItem = ({ text, href, isLast, scrollRef }: BreadcrumbItemProps)
|
||||
{ text }
|
||||
</LinkInternal>
|
||||
{ !isLast && <IconSvg name="arrows/east" boxSize={ 6 } color={ iconColor }/> }
|
||||
</HStack>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -73,6 +75,8 @@ const AddressMudBreadcrumbs = (props: TableViewProps | RecordViewProps) => {
|
||||
const queryParams = { tab: 'mud', hash: props.hash };
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const addressQuery = useAddressQuery({ hash: props.hash });
|
||||
|
||||
return (
|
||||
<Box
|
||||
display={ isMobile ? 'flex' : 'grid' }
|
||||
@@ -82,8 +86,9 @@ const AddressMudBreadcrumbs = (props: TableViewProps | RecordViewProps) => {
|
||||
alignItems="center"
|
||||
className={ props.className }
|
||||
width="fit-content"
|
||||
fontSize="sm"
|
||||
>
|
||||
<IconSvg name="MUD" boxSize={ 5 } color="green.500"/>
|
||||
<IconSvg name="MUD" boxSize={ 5 } color={ addressQuery.data?.is_verified ? 'green.500' : 'text_secondary' }/>
|
||||
<BreadcrumbItem
|
||||
text="MUD World"
|
||||
href={ route({ pathname: '/address/[hash]', query: queryParams }) }
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import { mudRecord } from 'mocks/mud/mudTables';
|
||||
import { test, expect, devices } from 'playwright/lib';
|
||||
|
||||
import AddressMudRecord from './AddressMudRecord';
|
||||
|
||||
const ADDRESS_HASH = 'hash';
|
||||
const TABLE_ID = '123';
|
||||
const RECORD_ID = '234';
|
||||
const hooksConfig = {
|
||||
router: {
|
||||
query: { hash: ADDRESS_HASH },
|
||||
},
|
||||
};
|
||||
|
||||
test('base view', async({ render, mockApiResponse }) => {
|
||||
await mockApiResponse('address_mud_record', mudRecord, { pathParams: { hash: ADDRESS_HASH, table_id: TABLE_ID, record_id: RECORD_ID } });
|
||||
|
||||
const component = await render(
|
||||
<Box pt={{ base: '134px', lg: 6 }}>
|
||||
<AddressMudRecord tableId={ TABLE_ID } recordId={ RECORD_ID }/>
|
||||
</Box>,
|
||||
{ hooksConfig },
|
||||
);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test.describe('mobile', () => {
|
||||
test.use({ viewport: devices['iPhone 13 Pro'].viewport });
|
||||
|
||||
test('base view', async({ render, mockApiResponse }) => {
|
||||
await mockApiResponse('address_mud_record', mudRecord, { pathParams: { hash: ADDRESS_HASH, table_id: TABLE_ID, record_id: RECORD_ID } });
|
||||
|
||||
const component = await render(
|
||||
<Box pt={{ base: '134px', lg: 6 }}>
|
||||
<AddressMudRecord tableId={ TABLE_ID } recordId={ RECORD_ID }/>
|
||||
</Box>,
|
||||
{ hooksConfig },
|
||||
);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
});
|
||||
@@ -10,6 +10,7 @@ import TruncatedValue from 'ui/shared/TruncatedValue';
|
||||
|
||||
import AddressMudBreadcrumbs from './AddressMudBreadcrumbs';
|
||||
import AddressMudRecordValues from './AddressMudRecordValues';
|
||||
import { getValueString } from './utils';
|
||||
|
||||
type Props ={
|
||||
scrollRef?: React.RefObject<HTMLDivElement>;
|
||||
@@ -55,12 +56,12 @@ const AddressMudRecord = ({ tableId, recordId, isQueryEnabled = true, scrollRef
|
||||
<Table borderRadius="8px" style={{ tableLayout: 'auto' }} width="100%" overflow="hidden">
|
||||
{ data?.schema.key_names.length && data?.schema.key_names.map((keyName, index) => (
|
||||
<Tr key={ keyName } borderBottomStyle={ index === data.schema.key_names.length - 1 ? 'hidden' : 'solid' }>
|
||||
<Td fontWeight={ 600 } whiteSpace="nowrap">
|
||||
<Td fontWeight={ 600 } whiteSpace="nowrap" fontSize="sm">
|
||||
{ keyName } ({ data.schema.key_types[index] })
|
||||
</Td>
|
||||
<Td colSpan={ 2 }>
|
||||
<Td colSpan={ 2 } fontSize="sm">
|
||||
<Flex justifyContent="space-between">
|
||||
<TruncatedValue value={ data.record.decoded[keyName] } mr={ 2 }/>
|
||||
<TruncatedValue value={ getValueString(data.record.decoded[keyName]) } mr={ 2 }/>
|
||||
{ index === 0 && <Box color="text_secondary">{ dayjs(data.record.timestamp).format('lll') }</Box> }
|
||||
</Flex>
|
||||
</Td>
|
||||
@@ -72,12 +73,12 @@ const AddressMudRecord = ({ tableId, recordId, isQueryEnabled = true, scrollRef
|
||||
<Hide above="lg" ssr={ false }>
|
||||
<>
|
||||
{ data?.schema.key_names.length && data?.schema.key_names.map((keyName, index) => (
|
||||
<VStack gap={ 1 } key={ keyName } alignItems="start">
|
||||
<VStack gap={ 1 } key={ keyName } alignItems="start" fontSize="sm">
|
||||
<Divider/>
|
||||
<Text fontWeight={ 600 } whiteSpace="nowrap">
|
||||
{ keyName } ({ data.schema.key_types[index] })
|
||||
</Text>
|
||||
<Text wordBreak="break-all">{ data.record.decoded[keyName] }</Text>
|
||||
<Text wordBreak="break-word">{ getValueString(data.record.decoded[keyName]) }</Text>
|
||||
{ index === 0 && <Box color="text_secondary">{ dayjs(data.record.timestamp).format('lll') }</Box> }
|
||||
</VStack>
|
||||
)) }
|
||||
|
||||
@@ -3,6 +3,8 @@ import React from 'react';
|
||||
|
||||
import type { AddressMudRecord } from 'types/api/address';
|
||||
|
||||
import { getValueString } from './utils';
|
||||
|
||||
type Props ={
|
||||
data?: AddressMudRecord;
|
||||
}
|
||||
@@ -16,19 +18,19 @@ const AddressMudRecordValues = ({ data }: Props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tr backgroundColor={ valuesBgColor } borderBottomStyle="hidden">
|
||||
<Td fontWeight={ 600 }>Field</Td>
|
||||
<Td fontWeight={ 600 }>Type</Td>
|
||||
<Td fontWeight={ 600 } w="100%" wordBreak="break-all">Value</Td>
|
||||
<Tr backgroundColor={ valuesBgColor } borderBottomStyle="hidden" >
|
||||
<Td fontWeight={ 600 } w="100px" fontSize="sm">Field</Td>
|
||||
<Td fontWeight={ 600 } w="90px" fontSize="sm">Type</Td>
|
||||
<Td fontWeight={ 600 } fontSize="sm">Value</Td>
|
||||
</Tr>
|
||||
{
|
||||
data?.schema.value_names.map((valName, index) => (
|
||||
<Tr key={ valName } backgroundColor={ valuesBgColor } borderBottomStyle="hidden">
|
||||
<Td whiteSpace="nowrap" py={ 0 } pb={ 4 }>{ valName }</Td>
|
||||
<Td py={ 0 } pb={ 4 }>{ data.schema.value_types[index] }</Td>
|
||||
<Td w="100%" wordBreak="break-all" py={ 0 } pb={ 4 }>
|
||||
<Td fontSize="sm" w="100px" py={ 0 } pb={ 4 } pr={ 0 }wordBreak="break-all">{ valName }</Td>
|
||||
<Td fontSize="sm" w="90px" py={ 0 } pb={ 4 } wordBreak="break-all">{ data.schema.value_types[index] }</Td>
|
||||
<Td fontSize="sm" wordBreak="break-word" py={ 0 } pb={ 4 }>
|
||||
<Box>
|
||||
{ data.record.decoded[valName] }
|
||||
{ getValueString(data.record.decoded[valName]) }
|
||||
</Box>
|
||||
</Td>
|
||||
</Tr>
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import type { StyleProps } from '@chakra-ui/react';
|
||||
import { Box, Link, Table, Tbody, Td, Th, Tr, Flex, useColorModeValue, useBoolean } from '@chakra-ui/react';
|
||||
import { Box, Link, Table, Tbody, Td, Th, Tr, Flex, useColorModeValue, useBoolean, Tooltip } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
|
||||
import type { AddressMudRecords, AddressMudRecordsFilter, AddressMudRecordsSorting } from 'types/api/address';
|
||||
|
||||
import { route } from 'nextjs-routes';
|
||||
|
||||
import capitalizeFirstLetter from 'lib/capitalizeFirstLetter';
|
||||
import dayjs from 'lib/date/dayjs';
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import LinkInternal from 'ui/shared/links/LinkInternal';
|
||||
import { default as Thead } from 'ui/shared/TheadSticky';
|
||||
|
||||
import AddressMudRecordsKeyFilter from './AddressMudRecordsKeyFilter';
|
||||
import { getNameTypeText } from './utils';
|
||||
import { getNameTypeText, getValueString } from './utils';
|
||||
|
||||
const COL_MIN_WIDTH = 180;
|
||||
const COL_MIN_WIDTH_MOBILE = 140;
|
||||
@@ -28,6 +31,7 @@ type Props = {
|
||||
filters: AddressMudRecordsFilter;
|
||||
toggleTableHasHorisontalScroll: () => void;
|
||||
scrollRef?: React.RefObject<HTMLDivElement>;
|
||||
hash: string;
|
||||
}
|
||||
|
||||
const AddressMudRecordsTable = ({
|
||||
@@ -39,6 +43,7 @@ const AddressMudRecordsTable = ({
|
||||
setFilters,
|
||||
toggleTableHasHorisontalScroll,
|
||||
scrollRef,
|
||||
hash,
|
||||
}: Props) => {
|
||||
const totalColsCut = data.schema.key_names.length + data.schema.value_names.length;
|
||||
const isMobile = useIsMobile(false);
|
||||
@@ -46,23 +51,32 @@ const AddressMudRecordsTable = ({
|
||||
const [ isOpened, setIsOpened ] = useBoolean(false);
|
||||
const [ hasCut, setHasCut ] = useBoolean(isMobile ? totalColsCut > MIN_CUT_COUNT : true);
|
||||
|
||||
const containerRef = React.useRef<HTMLTableElement>(null);
|
||||
const tableRef = React.useRef<HTMLTableElement>(null);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const toggleIsOpen = React.useCallback(() => {
|
||||
isOpened && tableRef.current?.scroll({ left: 0 });
|
||||
setIsOpened.toggle();
|
||||
toggleTableHasHorisontalScroll();
|
||||
}, [ setIsOpened, toggleTableHasHorisontalScroll ]);
|
||||
}, [ setIsOpened, toggleTableHasHorisontalScroll, isOpened ]);
|
||||
|
||||
const onRecordClick = React.useCallback((e: React.MouseEvent) => {
|
||||
const newQuery = {
|
||||
...router.query,
|
||||
record_id: e.currentTarget.getAttribute('data-id') as string,
|
||||
};
|
||||
router.push({ pathname: router.pathname, query: newQuery }, undefined, { shallow: true });
|
||||
if (e.metaKey || e.ctrlKey) {
|
||||
// Allow opening in a new tab/window with right-click or ctrl/cmd+click
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
router.push(
|
||||
{ pathname: '/address/[hash]', query: { hash, tab: 'mud', table_id: data.table.table_id, record_id: e.currentTarget.getAttribute('data-id') as string } },
|
||||
undefined,
|
||||
{ shallow: true },
|
||||
);
|
||||
scrollRef?.current?.scrollIntoView();
|
||||
}, [ router, scrollRef ]);
|
||||
}, [ router, scrollRef, hash, data.table.table_id ]);
|
||||
|
||||
const handleFilterChange = React.useCallback((field: keyof AddressMudRecordsFilter) => (val: string) => {
|
||||
setFilters(prev => {
|
||||
@@ -80,8 +94,8 @@ const AddressMudRecordsTable = ({
|
||||
const keyBgColor = useColorModeValue('blackAlpha.50', 'whiteAlpha.50');
|
||||
|
||||
React.useEffect(() => {
|
||||
if (hasCut && !colsCutCount && tableRef.current) {
|
||||
const count = Math.floor((tableRef.current.getBoundingClientRect().width - CUT_COL_WIDTH) / COL_MIN_WIDTH);
|
||||
if (hasCut && !colsCutCount && containerRef.current) {
|
||||
const count = Math.floor((containerRef.current.getBoundingClientRect().width - CUT_COL_WIDTH) / COL_MIN_WIDTH);
|
||||
if (totalColsCut > 2 && count - 1 < totalColsCut) {
|
||||
setColsCutCount(count - 1);
|
||||
} else {
|
||||
@@ -90,22 +104,15 @@ const AddressMudRecordsTable = ({
|
||||
}
|
||||
}, [ colsCutCount, data.schema, hasCut, setHasCut, totalColsCut ]);
|
||||
|
||||
const cutWidth = `${ CUT_COL_WIDTH }px `;
|
||||
|
||||
const colW = isMobile ? COL_MIN_WIDTH_MOBILE : COL_MIN_WIDTH;
|
||||
|
||||
const tdStyles: StyleProps = {
|
||||
wordBreak: 'break-all',
|
||||
whiteSpace: 'normal',
|
||||
minW: `${ colW }px`,
|
||||
w: `${ colW }px`,
|
||||
};
|
||||
|
||||
const thStyles: StyleProps = {
|
||||
wordBreak: 'break-word',
|
||||
whiteSpace: 'normal',
|
||||
minW: `${ colW }px`,
|
||||
w: `${ colW }px`,
|
||||
verticalAlign: 'baseline',
|
||||
lineHeight: '20px',
|
||||
};
|
||||
|
||||
const keys = (isOpened || !hasCut) ? data.schema.key_names : data.schema.key_names.slice(0, colsCutCount);
|
||||
@@ -113,16 +120,28 @@ const AddressMudRecordsTable = ({
|
||||
|
||||
const hasHorizontalScroll = isMobile || isOpened;
|
||||
|
||||
if (hasCut && !colsCutCount) {
|
||||
return <Box w="100%" ref={ containerRef }></Box>;
|
||||
}
|
||||
|
||||
const cutButton = (
|
||||
<Th width={ `${ CUT_COL_WIDTH }px ` } verticalAlign="baseline">
|
||||
<Tooltip label={ isOpened ? 'Hide columns' : 'Show all columns' }>
|
||||
<Link onClick={ toggleIsOpen } aria-label="show/hide columns">...</Link>
|
||||
</Tooltip>
|
||||
</Th>
|
||||
);
|
||||
|
||||
return (
|
||||
// can't implement both horisontal table scroll and sticky header
|
||||
<Box maxW="100%" overflowX={ hasHorizontalScroll ? 'scroll' : 'unset' } whiteSpace="nowrap">
|
||||
<Table variant="simple" size="sm" style={{ tableLayout: 'fixed' }} ref={ tableRef }>
|
||||
<Box maxW="100%" overflowX={ hasHorizontalScroll ? 'scroll' : 'unset' } whiteSpace="nowrap" ref={ tableRef }>
|
||||
<Table variant="simple" size="sm" style={{ tableLayout: 'fixed' }}>
|
||||
<Thead top={ hasHorizontalScroll ? 0 : top } display={ hasHorizontalScroll ? 'table' : 'table-header-group' } w="100%">
|
||||
<Tr >
|
||||
{ keys.map((keyName, index) => {
|
||||
const text = getNameTypeText(keyName, data.schema.key_types[index]);
|
||||
return (
|
||||
<Th key={ keyName } { ...thStyles }>
|
||||
<Th key={ keyName } { ...tdStyles }>
|
||||
{ index < 2 ? (
|
||||
<Flex>
|
||||
<Link onClick={ onKeySortClick } data-id={ index } display="flex" alignItems="start" lineHeight="20px" mr={ 2 }>
|
||||
@@ -143,13 +162,13 @@ const AddressMudRecordsTable = ({
|
||||
);
|
||||
}) }
|
||||
{ values.map((valName, index) => (
|
||||
<Th key={ valName } { ...thStyles }>
|
||||
<Th key={ valName } { ...tdStyles }>
|
||||
{ capitalizeFirstLetter(valName) } ({ data.schema.value_types[index] })
|
||||
</Th>
|
||||
)) }
|
||||
{ hasCut && !isOpened && <Th width={ cutWidth }><Link onClick={ toggleIsOpen }>...</Link></Th> }
|
||||
<Th { ...thStyles }>Modified</Th>
|
||||
{ hasCut && isOpened && <Th width={ cutWidth }><Link onClick={ toggleIsOpen }>...</Link></Th> }
|
||||
{ hasCut && !isOpened && cutButton }
|
||||
<Th { ...tdStyles }>Modified</Th>
|
||||
{ hasCut && isOpened && cutButton }
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody display={ hasHorizontalScroll ? 'table' : 'table-row-group' } w="100%">
|
||||
@@ -157,17 +176,23 @@ const AddressMudRecordsTable = ({
|
||||
<Tr key={ item.id }>
|
||||
{ keys.map((keyName, index) => (
|
||||
<Td key={ keyName } backgroundColor={ keyBgColor } { ...tdStyles }>
|
||||
{ index === 0 ?
|
||||
<Link onClick={ onRecordClick } data-id={ item.id } fontWeight={ 700 }>{ item.decoded[keyName].toString() }</Link> :
|
||||
item.decoded[keyName].toString()
|
||||
}
|
||||
{ index === 0 ? (
|
||||
<LinkInternal
|
||||
onClick={ onRecordClick }
|
||||
data-id={ item.id }
|
||||
fontWeight={ 700 }
|
||||
href={ route({ pathname: '/address/[hash]', query: { hash, tab: 'mud', table_id: data.table.table_id, record_id: item.id } }) }
|
||||
>
|
||||
{ getValueString(item.decoded[keyName]) }
|
||||
</LinkInternal>
|
||||
) : getValueString(item.decoded[keyName]) }
|
||||
</Td>
|
||||
)) }
|
||||
{ values.map((valName) =>
|
||||
<Td key={ valName } { ...tdStyles }>{ item.decoded[valName].toString() }</Td>) }
|
||||
{ hasCut && !isOpened && <Td width={ cutWidth }></Td> }
|
||||
<Td key={ valName } { ...tdStyles }>{ getValueString(item.decoded[valName]) }</Td>) }
|
||||
{ hasCut && !isOpened && <Td width={ `${ CUT_COL_WIDTH }px ` }></Td> }
|
||||
<Td { ...tdStyles } color="text_secondary">{ dayjs(item.timestamp).format('lll') }</Td>
|
||||
{ hasCut && isOpened && <Td width={ cutWidth }></Td> }
|
||||
{ hasCut && isOpened && <Td width={ `${ CUT_COL_WIDTH }px ` }></Td> }
|
||||
</Tr>
|
||||
)) }
|
||||
</Tbody>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import { mudRecords } from 'mocks/mud/mudTables';
|
||||
import { test, expect } from 'playwright/lib';
|
||||
|
||||
import AddressMudTable from './AddressMudTable';
|
||||
|
||||
const ADDRESS_HASH = 'hash';
|
||||
const TABLE_ID = '123';
|
||||
const hooksConfig = {
|
||||
router: {
|
||||
query: { hash: ADDRESS_HASH },
|
||||
},
|
||||
};
|
||||
|
||||
test('base view +@mobile', async({ render, mockApiResponse }) => {
|
||||
await mockApiResponse('address_mud_records', mudRecords, { pathParams: { hash: ADDRESS_HASH, table_id: TABLE_ID } });
|
||||
|
||||
const component = await render(
|
||||
<Box pt={{ base: '134px', lg: 6 }}>
|
||||
<AddressMudTable tableId={ TABLE_ID }/>
|
||||
</Box>,
|
||||
{ hooksConfig },
|
||||
);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('expanded view +@mobile', async({ render, mockApiResponse }) => {
|
||||
await mockApiResponse('address_mud_records', mudRecords, { pathParams: { hash: ADDRESS_HASH, table_id: TABLE_ID } });
|
||||
|
||||
const component = await render(
|
||||
<Box pt={{ base: '134px', lg: 6 }}>
|
||||
<AddressMudTable tableId={ TABLE_ID }/>
|
||||
</Box>,
|
||||
{ hooksConfig },
|
||||
);
|
||||
|
||||
await component.locator('a[aria-label="show/hide columns"]').first().click();
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
@@ -125,6 +125,7 @@ const AddressMudTable = ({ scrollRef, tableId, isQueryEnabled = true }: Props) =
|
||||
filters={ filters }
|
||||
toggleTableHasHorisontalScroll={ setTableHasHorisontalScroll.toggle }
|
||||
scrollRef={ scrollRef }
|
||||
hash={ hash }
|
||||
/>
|
||||
) : null;
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import { mudTables } from 'mocks/mud/mudTables';
|
||||
import { test, expect } from 'playwright/lib';
|
||||
|
||||
import AddressMudTables from './AddressMudTables';
|
||||
|
||||
const ADDRESS_HASH = 'hash';
|
||||
const hooksConfig = {
|
||||
router: {
|
||||
query: { hash: ADDRESS_HASH, q: 'o' },
|
||||
},
|
||||
};
|
||||
|
||||
test('base view +@mobile', async({ render, mockApiResponse }) => {
|
||||
await mockApiResponse('address_mud_tables', mudTables, { pathParams: { hash: ADDRESS_HASH }, queryParams: { q: 'o' } });
|
||||
|
||||
const component = await render(
|
||||
<Box pt={{ base: '134px', lg: 6 }}>
|
||||
<AddressMudTables/>
|
||||
</Box>,
|
||||
{ hooksConfig },
|
||||
);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('with schema opened +@mobile', async({ render, mockApiResponse }) => {
|
||||
await mockApiResponse('address_mud_tables', mudTables, { pathParams: { hash: ADDRESS_HASH }, queryParams: { q: 'o' } });
|
||||
|
||||
const component = await render(
|
||||
<Box pt={{ base: '134px', lg: 6 }}>
|
||||
<AddressMudTables/>
|
||||
</Box>,
|
||||
{ hooksConfig },
|
||||
);
|
||||
|
||||
await component.locator('div[aria-label="View schema"]').first().click();
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import { Hide, Show } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
|
||||
import useDebounce from 'lib/hooks/useDebounce';
|
||||
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
|
||||
import { apos } from 'lib/html-entities';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
@@ -27,11 +28,12 @@ const AddressMudTables = ({ scrollRef, isQueryEnabled = true }: Props) => {
|
||||
const hash = getQueryParamString(router.query.hash);
|
||||
const q = getQueryParamString(router.query.q);
|
||||
const [ searchTerm, setSearchTerm ] = React.useState<string>(q || '');
|
||||
const debouncedSearchTerm = useDebounce(searchTerm, 300);
|
||||
|
||||
const { data, isPlaceholderData, isError, pagination } = useQueryWithPages({
|
||||
resourceName: 'address_mud_tables',
|
||||
pathParams: { hash },
|
||||
filters: { q: searchTerm },
|
||||
filters: { q: debouncedSearchTerm },
|
||||
scrollRef,
|
||||
options: {
|
||||
enabled: isQueryEnabled,
|
||||
@@ -71,6 +73,7 @@ const AddressMudTables = ({ scrollRef, isQueryEnabled = true }: Props) => {
|
||||
isLoading={ isPlaceholderData }
|
||||
top={ ACTION_BAR_HEIGHT_DESKTOP }
|
||||
scrollRef={ scrollRef }
|
||||
hash={ hash }
|
||||
/>
|
||||
</Hide>
|
||||
<Show below="lg" ssr={ false }>
|
||||
@@ -79,6 +82,7 @@ const AddressMudTables = ({ scrollRef, isQueryEnabled = true }: Props) => {
|
||||
key={ item.table.table_id + (isPlaceholderData ? String(index) : '') }
|
||||
item={ item }
|
||||
isLoading={ isPlaceholderData }
|
||||
hash={ hash }
|
||||
/>
|
||||
)) }
|
||||
</Show>
|
||||
|
||||
@@ -4,30 +4,41 @@ import React from 'react';
|
||||
|
||||
import type { AddressMudTableItem } from 'types/api/address';
|
||||
|
||||
import { route } from 'nextjs-routes';
|
||||
|
||||
import Tag from 'ui/shared/chakra/Tag';
|
||||
import HashStringShorten from 'ui/shared/HashStringShorten';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import LinkInternal from 'ui/shared/links/LinkInternal';
|
||||
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
|
||||
|
||||
type Props = {
|
||||
item: AddressMudTableItem;
|
||||
isLoading: boolean;
|
||||
scrollRef?: React.RefObject<HTMLDivElement>;
|
||||
hash: string;
|
||||
};
|
||||
|
||||
const AddressMudTablesListItem = ({ item, isLoading, scrollRef }: Props) => {
|
||||
const AddressMudTablesListItem = ({ item, isLoading, scrollRef, hash }: Props) => {
|
||||
const [ isOpened, setIsOpened ] = useBoolean(false);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const onTableClick = React.useCallback((e: React.MouseEvent) => {
|
||||
const newQuery = {
|
||||
...router.query,
|
||||
table_id: e.currentTarget.getAttribute('data-id') as string,
|
||||
};
|
||||
router.push({ pathname: router.pathname, query: newQuery }, undefined, { shallow: true });
|
||||
if (e.metaKey || e.ctrlKey) {
|
||||
// Allow opening in a new tab/window with right-click or ctrl/cmd+click
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
router.push(
|
||||
{ pathname: '/address/[hash]', query: { hash, tab: 'mud', table_id: e.currentTarget.getAttribute('data-id') as string } },
|
||||
undefined,
|
||||
{ shallow: true },
|
||||
);
|
||||
scrollRef?.current?.scrollIntoView();
|
||||
}, [ router, scrollRef ]);
|
||||
}, [ router, scrollRef, hash ]);
|
||||
|
||||
return (
|
||||
<ListItemMobile rowGap={ 3 } fontSize="sm" py={ 3 }>
|
||||
@@ -41,15 +52,21 @@ const AddressMudTablesListItem = ({ item, isLoading, scrollRef }: Props) => {
|
||||
cursor="pointer"
|
||||
onClick={ setIsOpened.toggle }
|
||||
transitionDuration="faster"
|
||||
aria-label="View schema"
|
||||
/>
|
||||
</Link>
|
||||
</Skeleton>
|
||||
<Box flexGrow="1">
|
||||
<Flex justifyContent="space-between" height={ 6 } alignItems="center" mb={ 3 }>
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Link onClick={ onTableClick } data-id={ item.table.table_id } fontWeight={ 500 }>
|
||||
<LinkInternal
|
||||
onClick={ onTableClick }
|
||||
data-id={ item.table.table_id }
|
||||
fontWeight={ 500 }
|
||||
href={ route({ pathname: '/address/[hash]', query: { hash, tab: 'mud', table_id: item.table.table_id } }) }
|
||||
>
|
||||
{ item.table.table_full_name }
|
||||
</Link>
|
||||
</LinkInternal>
|
||||
</Skeleton>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary">
|
||||
{ item.table.table_type }
|
||||
|
||||
@@ -12,10 +12,11 @@ type Props = {
|
||||
isLoading: boolean;
|
||||
top: number;
|
||||
scrollRef?: React.RefObject<HTMLDivElement>;
|
||||
hash: string;
|
||||
}
|
||||
|
||||
//sorry for the naming
|
||||
const AddressMudTablesTable = ({ items, isLoading, top, scrollRef }: Props) => {
|
||||
const AddressMudTablesTable = ({ items, isLoading, top, scrollRef, hash }: Props) => {
|
||||
return (
|
||||
<Table variant="simple" size="sm" style={{ tableLayout: 'auto' }}>
|
||||
<Thead top={ top }>
|
||||
@@ -33,6 +34,7 @@ const AddressMudTablesTable = ({ items, isLoading, top, scrollRef }: Props) => {
|
||||
item={ item }
|
||||
isLoading={ isLoading }
|
||||
scrollRef={ scrollRef }
|
||||
hash={ hash }
|
||||
/>
|
||||
)) }
|
||||
</Tbody>
|
||||
|
||||
@@ -4,28 +4,39 @@ import React from 'react';
|
||||
|
||||
import type { AddressMudTableItem } from 'types/api/address';
|
||||
|
||||
import { route } from 'nextjs-routes';
|
||||
|
||||
import Tag from 'ui/shared/chakra/Tag';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
import LinkInternal from 'ui/shared/links/LinkInternal';
|
||||
|
||||
type Props = {
|
||||
item: AddressMudTableItem;
|
||||
isLoading: boolean;
|
||||
scrollRef?: React.RefObject<HTMLDivElement>;
|
||||
hash: string;
|
||||
};
|
||||
|
||||
const AddressMudTablesTableItem = ({ item, isLoading, scrollRef }: Props) => {
|
||||
const AddressMudTablesTableItem = ({ item, isLoading, scrollRef, hash }: Props) => {
|
||||
const [ isOpened, setIsOpened ] = useBoolean(false);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const onTableClick = React.useCallback((e: React.MouseEvent) => {
|
||||
const newQuery = {
|
||||
...router.query,
|
||||
table_id: e.currentTarget.getAttribute('data-id') as string,
|
||||
};
|
||||
router.push({ pathname: router.pathname, query: newQuery }, undefined, { shallow: true });
|
||||
if (e.metaKey || e.ctrlKey) {
|
||||
// Allow opening in a new tab/window with right-click or ctrl/cmd+click
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
router.push(
|
||||
{ pathname: '/address/[hash]', query: { hash, tab: 'mud', table_id: e.currentTarget.getAttribute('data-id') as string } },
|
||||
undefined,
|
||||
{ shallow: true },
|
||||
);
|
||||
scrollRef?.current?.scrollIntoView();
|
||||
}, [ router, scrollRef ]);
|
||||
}, [ router, scrollRef, hash ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -40,15 +51,21 @@ const AddressMudTablesTableItem = ({ item, isLoading, scrollRef }: Props) => {
|
||||
cursor="pointer"
|
||||
onClick={ setIsOpened.toggle }
|
||||
transitionDuration="faster"
|
||||
aria-label="View schema"
|
||||
/>
|
||||
</Link>
|
||||
</Skeleton>
|
||||
</Td>
|
||||
<Td verticalAlign="middle">
|
||||
<Skeleton isLoaded={ !isLoading }>
|
||||
<Link onClick={ onTableClick } data-id={ item.table.table_id }>
|
||||
<LinkInternal
|
||||
href={ route({ pathname: '/address/[hash]', query: { hash, tab: 'mud', table_id: item.table.table_id } }) }
|
||||
data-id={ item.table.table_id }
|
||||
onClick={ onTableClick }
|
||||
fontWeight={ 700 }
|
||||
>
|
||||
{ item.table.table_full_name }
|
||||
</Link>
|
||||
</LinkInternal>
|
||||
</Skeleton>
|
||||
</Td>
|
||||
<Td verticalAlign="middle">
|
||||
@@ -69,7 +86,7 @@ const AddressMudTablesTableItem = ({ item, isLoading, scrollRef }: Props) => {
|
||||
<Table>
|
||||
{ Boolean(item.schema.key_names.length) && (
|
||||
<Tr>
|
||||
<Td width="80px" fontSize="sm" fontWeight={ 600 } py={ 2 }>Key</Td>
|
||||
<Td width="80px" fontSize="sm" fontWeight={ 600 } py={ 2 } verticalAlign="middle">Key</Td>
|
||||
<Td py={ 2 }>
|
||||
<VStack gap={ 1 } alignItems="start">
|
||||
{ item.schema.key_names.map((name, index) => (
|
||||
|
||||
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 27 KiB |
@@ -8,3 +8,11 @@ export const SORT_SEQUENCE: Record<'key0' | 'key1', Array<'desc' | 'asc' | undef
|
||||
export const getNameTypeText = (name: string, type: string) => {
|
||||
return capitalizeFirstLetter(name) + ' (' + type + ')';
|
||||
};
|
||||
|
||||
export const getValueString = (value: string | Array<string>) => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.join(', ');
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
@@ -7,15 +7,9 @@ import type { MudWorldItem } from 'types/api/mudWorlds';
|
||||
import config from 'configs/app';
|
||||
import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
|
||||
const mudFrameworkFeature = config.features.mudFramework;
|
||||
|
||||
type Props = { item: MudWorldItem; isLoading?: boolean };
|
||||
|
||||
const MudWorldsTableItem = ({ item, isLoading }: Props) => {
|
||||
if (!mudFrameworkFeature.isEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const addressBalance = BigNumber(item.coin_balance).div(BigNumber(10 ** config.chain.currency.decimals));
|
||||
const addressBalanceChunks = addressBalance.dp(8).toFormat().split('.');
|
||||
|
||||
|
||||
@@ -272,16 +272,21 @@ const AddressPageContent = () => {
|
||||
<RoutedTabs tabs={ tabs } tabListProps={{ mt: 6 }} isLoading={ isTabsLoading }/>;
|
||||
|
||||
const backLink = React.useMemo(() => {
|
||||
const hasGoBackLink = appProps.referrer && appProps.referrer.includes('/accounts');
|
||||
|
||||
if (!hasGoBackLink) {
|
||||
return;
|
||||
if (appProps.referrer && appProps.referrer.includes('/accounts')) {
|
||||
return {
|
||||
label: 'Back to top accounts list',
|
||||
url: appProps.referrer,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
label: 'Back to top accounts list',
|
||||
url: appProps.referrer,
|
||||
};
|
||||
if (appProps.referrer && appProps.referrer.includes('/mud-worlds')) {
|
||||
return {
|
||||
label: 'Back to MUD worlds list',
|
||||
url: appProps.referrer,
|
||||
};
|
||||
}
|
||||
|
||||
return;
|
||||
}, [ appProps.referrer ]);
|
||||
|
||||
const titleSecondRow = (
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
import { mudWorlds } from 'mocks/mud/mudWorlds';
|
||||
import { test, expect } from 'playwright/lib';
|
||||
|
||||
import MudWorlds from './MudWorlds';
|
||||
|
||||
test('default view +@mobile', async({ mockTextAd, mockApiResponse, render }) => {
|
||||
await mockTextAd();
|
||||
await mockApiResponse('mud_worlds', mudWorlds);
|
||||
const component = await render(<MudWorlds/>);
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 31 KiB |
@@ -40,7 +40,7 @@ const TableColumnFilterWrapper = ({ columnName, isActive, className, children, i
|
||||
variant="ghost"
|
||||
w="20px"
|
||||
h="20px"
|
||||
icon={ <IconSvg name="filter" w="20px" h="20px"/> }
|
||||
icon={ <IconSvg name="filter" w="19px" h="19px"/> }
|
||||
isActive={ isActive }
|
||||
isDisabled={ isLoading }
|
||||
borderRadius="4px"
|
||||
|
||||