Merge branch 'main' of github.com:blockscout/frontend into network-customization
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 48 KiB |
@@ -4,6 +4,7 @@ import {
|
||||
HStack,
|
||||
Icon,
|
||||
Text,
|
||||
Grid,
|
||||
} from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
@@ -32,54 +33,141 @@ const LatestTxsItem = ({ tx }: Props) => {
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
if (isMobile) {
|
||||
return (
|
||||
<Box
|
||||
width="100%"
|
||||
borderTop="1px solid"
|
||||
borderColor="divider"
|
||||
py={ 4 }
|
||||
_last={{ borderBottom: '1px solid', borderColor: 'divider' }}
|
||||
>
|
||||
<Flex justifyContent="space-between">
|
||||
<HStack>
|
||||
<TxType types={ tx.tx_types }/>
|
||||
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined }/>
|
||||
</HStack>
|
||||
<TxAdditionalInfo tx={ tx } isMobile/>
|
||||
</Flex>
|
||||
<Flex
|
||||
mt={ 2 }
|
||||
alignItems="center"
|
||||
width="100%"
|
||||
justifyContent="space-between"
|
||||
mb={ 6 }
|
||||
>
|
||||
<Flex mr={ 3 }>
|
||||
<Icon
|
||||
as={ transactionIcon }
|
||||
boxSize="30px"
|
||||
mr={ 2 }
|
||||
color="link"
|
||||
/>
|
||||
<Address width="100%">
|
||||
<AddressLink
|
||||
hash={ tx.hash }
|
||||
type="transaction"
|
||||
fontWeight="700"
|
||||
truncation="constant"
|
||||
/>
|
||||
</Address>
|
||||
</Flex>
|
||||
{ tx.timestamp && <Text variant="secondary" fontWeight="400" fontSize="sm">{ timeAgo }</Text> }
|
||||
</Flex>
|
||||
<Flex alignItems="center" mb={ 3 }>
|
||||
<Address>
|
||||
<AddressIcon address={ tx.from }/>
|
||||
<AddressLink
|
||||
type="address"
|
||||
hash={ tx.from.hash }
|
||||
alias={ tx.from.name }
|
||||
fontWeight="500"
|
||||
ml={ 2 }
|
||||
truncation="constant"
|
||||
fontSize="sm"
|
||||
/>
|
||||
</Address>
|
||||
<Icon
|
||||
as={ rightArrowIcon }
|
||||
boxSize={ 6 }
|
||||
mx={ 2 }
|
||||
color="gray.500"
|
||||
/>
|
||||
{ dataTo && (
|
||||
<Address>
|
||||
<AddressIcon address={ dataTo }/>
|
||||
<AddressLink
|
||||
type="address"
|
||||
hash={ dataTo.hash }
|
||||
alias={ dataTo.name }
|
||||
fontWeight="500"
|
||||
ml={ 2 }
|
||||
truncation="constant"
|
||||
fontSize="sm"
|
||||
/>
|
||||
</Address>
|
||||
) }
|
||||
</Flex>
|
||||
<Box mb={ 2 } fontSize="sm">
|
||||
<Text as="span">Value { appConfig.network.currency.symbol } </Text>
|
||||
<Text as="span" variant="secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() }</Text>
|
||||
</Box>
|
||||
<Box fontSize="sm">
|
||||
<Text as="span">Fee { appConfig.network.currency.symbol } </Text>
|
||||
<Text as="span" variant="secondary">{ getValueWithUnit(tx.fee.value).dp(5).toFormat() }</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
width="100%"
|
||||
minW="700px"
|
||||
borderTop="1px solid"
|
||||
borderColor="divider"
|
||||
py={ 4 }
|
||||
px={{ base: 0, lg: 4 }}
|
||||
p={ 4 }
|
||||
_last={{ borderBottom: '1px solid', borderColor: 'divider' }}
|
||||
>
|
||||
<Flex justifyContent="space-between" width="100%" alignItems="start" flexDirection={{ base: 'column', lg: 'row' }}>
|
||||
{ !isMobile && <Flex mr={ 3 }><TxAdditionalInfo tx={ tx }/></Flex> }
|
||||
<Box width={{ base: '100%', lg: 'calc(50% - 32px)' }}>
|
||||
<Flex justifyContent="space-between">
|
||||
<Grid width="100%" gridTemplateColumns="3fr 2fr 150px" gridGap={ 8 }>
|
||||
<Flex overflow="hidden" w="100%">
|
||||
<TxAdditionalInfo tx={ tx }/>
|
||||
<Box ml={ 3 } w="calc(100% - 40px)">
|
||||
<HStack>
|
||||
<TxType types={ tx.tx_types }/>
|
||||
<TxStatus status={ tx.status } errorText={ tx.status === 'error' ? tx.result : undefined }/>
|
||||
</HStack>
|
||||
{ isMobile && <TxAdditionalInfo tx={ tx } isMobile/> }
|
||||
</Flex>
|
||||
<Flex
|
||||
mt={ 2 }
|
||||
alignItems="center"
|
||||
width="100%"
|
||||
justifyContent={{ base: 'space-between', lg: 'start' }}
|
||||
mb={{ base: 6, lg: 0 }}
|
||||
>
|
||||
<Flex mr={ 3 }>
|
||||
<Flex
|
||||
mt={ 2 }
|
||||
alignItems="center"
|
||||
>
|
||||
<Icon
|
||||
as={ transactionIcon }
|
||||
boxSize="30px"
|
||||
mr={ 2 }
|
||||
color="link"
|
||||
display="inline"
|
||||
mr={ 2 }
|
||||
/>
|
||||
<Address width="100%">
|
||||
<Address overflow="hidden" w="calc(100% - 130px)" maxW="calc(100% - 130px)" mr={ 2 }>
|
||||
<AddressLink
|
||||
hash={ tx.hash }
|
||||
type="transaction"
|
||||
fontWeight="700"
|
||||
truncation="constant"
|
||||
/>
|
||||
</Address>
|
||||
{ tx.timestamp && <Text variant="secondary" fontWeight="400" fontSize="sm">{ timeAgo }</Text> }
|
||||
</Flex>
|
||||
{ tx.timestamp && <Text variant="secondary" fontWeight="400" fontSize="sm">{ timeAgo }</Text> }
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box width={{ base: '100%', lg: '50%' }}>
|
||||
<Flex alignItems="center" mb={ 3 } justifyContent={{ base: 'start', lg: 'end' }}>
|
||||
<Address>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Grid alignItems="center" templateColumns="24px auto">
|
||||
<Icon
|
||||
as={ rightArrowIcon }
|
||||
boxSize={ 6 }
|
||||
color="gray.500"
|
||||
transform="rotate(90deg)"
|
||||
/>
|
||||
<Box overflow="hidden" ml={ 1 }>
|
||||
<Address mb={ 2 }>
|
||||
<AddressIcon address={ tx.from }/>
|
||||
<AddressLink
|
||||
type="address"
|
||||
@@ -87,16 +175,9 @@ const LatestTxsItem = ({ tx }: Props) => {
|
||||
alias={ tx.from.name }
|
||||
fontWeight="500"
|
||||
ml={ 2 }
|
||||
truncation="constant"
|
||||
fontSize="sm"
|
||||
/>
|
||||
</Address>
|
||||
<Icon
|
||||
as={ rightArrowIcon }
|
||||
boxSize={ 6 }
|
||||
mx={ 2 }
|
||||
color="gray.500"
|
||||
/>
|
||||
{ dataTo && (
|
||||
<Address>
|
||||
<AddressIcon address={ dataTo }/>
|
||||
@@ -106,24 +187,23 @@ const LatestTxsItem = ({ tx }: Props) => {
|
||||
alias={ dataTo.name }
|
||||
fontWeight="500"
|
||||
ml={ 2 }
|
||||
truncation="constant"
|
||||
fontSize="sm"
|
||||
/>
|
||||
</Address>
|
||||
) }
|
||||
</Flex>
|
||||
<Flex fontSize="sm" justifyContent="end" flexDirection={{ base: 'column', lg: 'row' }}>
|
||||
<Box mr={{ base: 0, lg: 3 }} mb={{ base: 2, lg: 0 }}>
|
||||
<Text as="span">Value { appConfig.network.currency.symbol } </Text>
|
||||
<Text as="span" variant="secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() }</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text as="span">Fee { appConfig.network.currency.symbol } </Text>
|
||||
<Text as="span" variant="secondary">{ getValueWithUnit(tx.fee.value).dp(5).toFormat() }</Text>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Box>
|
||||
<Box mb={ 2 }>
|
||||
<Text as="span" whiteSpace="pre">{ appConfig.network.currency.symbol } </Text>
|
||||
<Text as="span" variant="secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() }</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text as="span">Fee </Text>
|
||||
<Text as="span" variant="secondary">{ getValueWithUnit(tx.fee.value).dp(5).toFormat() }</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,39 +17,63 @@ const LatestTxsItemSkeleton = () => {
|
||||
px={{ base: 0, lg: 4 }}
|
||||
_last={{ borderBottom: '1px solid', borderColor: 'divider' }}
|
||||
>
|
||||
<Flex justifyContent="space-between" width="100%" alignItems="start" flexDirection={{ base: 'column', lg: 'row' }}>
|
||||
<Box width="100%">
|
||||
<HStack spacing={ 2 }>
|
||||
<Skeleton w="101px" h="24px"/>
|
||||
<Skeleton w="101px" h="24px"/>
|
||||
</HStack>
|
||||
<Flex
|
||||
mt={ 2 }
|
||||
alignItems="center"
|
||||
width="100%"
|
||||
justifyContent={{ base: 'space-between', lg: 'start' }}
|
||||
mb={{ base: 6, lg: 0 }}
|
||||
>
|
||||
<Flex mr={ 3 } alignItems="center">
|
||||
<Box width="100%" display={{ base: 'block', lg: 'none' }}>
|
||||
<HStack spacing={ 2 }>
|
||||
<Skeleton w="101px" h="24px"/>
|
||||
<Skeleton w="101px" h="24px"/>
|
||||
</HStack>
|
||||
<Flex
|
||||
mt={ 2 }
|
||||
alignItems="center"
|
||||
width="100%"
|
||||
justifyContent="space-between"
|
||||
mb={ 6 }
|
||||
>
|
||||
<Flex mr={ 3 } alignItems="center">
|
||||
<Skeleton w="30px" h="30px" mr={ 2 }/>
|
||||
<Skeleton w="101px" h="12px"/>
|
||||
</Flex>
|
||||
<Skeleton w="40px" h="12px"/>
|
||||
</Flex>
|
||||
<Flex alignItems="center" mb={ 3 }>
|
||||
<SkeletonCircle w="30px" h="30px" mr={ 2 }/>
|
||||
<Skeleton w="101px" h="12px" mr={ 5 }/>
|
||||
<SkeletonCircle w="30px" h="30px" mr={ 2 }/>
|
||||
<Skeleton w="101px" h="12px"/>
|
||||
</Flex>
|
||||
<Skeleton w="123px" h="12px" mb={ 2 } mt={ 3 }/>
|
||||
<Skeleton w="123px" h="12px"/>
|
||||
</Box>
|
||||
<Box display={{ base: 'none', lg: 'grid' }} width="100%" gridTemplateColumns="3fr 2fr 150px" gridGap={ 8 }>
|
||||
<Flex w="100%">
|
||||
<Skeleton w={ 5 } h={ 5 } mr={ 3 }/>
|
||||
<Box w="100%">
|
||||
<HStack>
|
||||
<Skeleton w="101px" h="24px"/>
|
||||
<Skeleton w="101px" h="24px"/>
|
||||
</HStack>
|
||||
<Flex alignItems="center" mt={ 2 }>
|
||||
<Skeleton w="30px" h="30px" mr={ 2 }/>
|
||||
<Skeleton w="101px" h="12px"/>
|
||||
<Skeleton w="calc(100% - 100px)" h="20px" mr={ 5 }/>
|
||||
<Skeleton w="40px" h="16px"/>
|
||||
</Flex>
|
||||
<Skeleton w="40px" h="12px"/>
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box>
|
||||
<Flex alignItems="center" mb={ 2 } mt={ 1 }>
|
||||
<SkeletonCircle w="24px" h="24px" mr={ 2 }/>
|
||||
<Skeleton w="100%" h="16px"/>
|
||||
</Flex>
|
||||
<Flex alignItems="center">
|
||||
<SkeletonCircle w="24px" h="24px" mr={ 2 }/>
|
||||
<Skeleton w="100%" h="16px"/>
|
||||
</Flex>
|
||||
</Box>
|
||||
<Box>
|
||||
<Flex alignItems="center" mb={ 3 }>
|
||||
<SkeletonCircle w="30px" h="30px" mr={ 2 }/>
|
||||
<Skeleton w="101px" h="12px" mr={ 5 }/>
|
||||
<SkeletonCircle w="30px" h="30px" mr={ 2 }/>
|
||||
<Skeleton w="101px" h="12px"/>
|
||||
</Flex>
|
||||
<Flex fontSize="sm" mt={ 3 } justifyContent="end" flexDirection={{ base: 'column', lg: 'row' }}>
|
||||
<Skeleton w="123px" h="12px" mr={{ base: 0, lg: 9 }} mb={{ base: 2, lg: 0 }}/>
|
||||
<Skeleton w="123px" h="12px"/>
|
||||
</Flex>
|
||||
<Skeleton w="123px" h="16px" mb={ 4 } mt={ 2 }/>
|
||||
<Skeleton w="123px" h="16px"/>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 65 KiB |
@@ -1,4 +1,4 @@
|
||||
import { test, expect } from '@playwright/experimental-ct-react';
|
||||
import { test, expect, devices } from '@playwright/experimental-ct-react';
|
||||
import React from 'react';
|
||||
|
||||
import * as blockMock from 'mocks/blocks/block';
|
||||
@@ -11,7 +11,7 @@ import insertAdPlaceholder from 'playwright/utils/insertAdPlaceholder';
|
||||
|
||||
import Home from './Home';
|
||||
|
||||
test('default view -@default +@desktop-xl +@mobile +@dark-mode', async({ mount, page }) => {
|
||||
test('default view -@default +@desktop-xl +@dark-mode', async({ mount, page }) => {
|
||||
await page.route(buildApiUrl('homepage_stats'), (route) => route.fulfill({
|
||||
status: 200,
|
||||
body: JSON.stringify(statsMock.base),
|
||||
@@ -46,3 +46,44 @@ test('default view -@default +@desktop-xl +@mobile +@dark-mode', async({ mount,
|
||||
|
||||
await expect(component.locator('main')).toHaveScreenshot();
|
||||
});
|
||||
|
||||
// had to separate mobile test, otherwise all the tests fell on CI
|
||||
test.describe('mobile', () => {
|
||||
test.use({ viewport: devices['iPhone 13 Pro'].viewport });
|
||||
|
||||
test('base view', async({ mount, page }) => {
|
||||
await page.route(buildApiUrl('homepage_stats'), (route) => route.fulfill({
|
||||
status: 200,
|
||||
body: JSON.stringify(statsMock.base),
|
||||
}));
|
||||
await page.route(buildApiUrl('homepage_blocks'), (route) => route.fulfill({
|
||||
status: 200,
|
||||
body: JSON.stringify([
|
||||
blockMock.base,
|
||||
blockMock.base2,
|
||||
]),
|
||||
}));
|
||||
await page.route(buildApiUrl('homepage_txs'), (route) => route.fulfill({
|
||||
status: 200,
|
||||
body: JSON.stringify([
|
||||
txMock.base,
|
||||
txMock.withContractCreation,
|
||||
txMock.withTokenTransfer,
|
||||
]),
|
||||
}));
|
||||
await page.route(buildApiUrl('homepage_chart_txs'), (route) => route.fulfill({
|
||||
status: 200,
|
||||
body: JSON.stringify(dailyTxsMock.base),
|
||||
}));
|
||||
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
<Home/>
|
||||
</TestApp>,
|
||||
);
|
||||
|
||||
await insertAdPlaceholder(page);
|
||||
|
||||
await expect(component.locator('main')).toHaveScreenshot();
|
||||
});
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 292 KiB |
|
Before Width: | Height: | Size: 221 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 122 KiB |
@@ -47,7 +47,7 @@ const TxsTable = ({
|
||||
<Th width="20%">Method</Th>
|
||||
{ showBlockInfo && <Th width="18%">Block</Th> }
|
||||
<Th width={{ xl: '132px', base: '66px' }}>From</Th>
|
||||
<Th width={{ xl: currentAddress ? '48px' : '36px', base: '0' }}></Th>
|
||||
<Th width={{ xl: currentAddress ? '48px' : '36px', base: currentAddress ? '52px' : '28px' }}></Th>
|
||||
<Th width={{ xl: '132px', base: '66px' }}>To</Th>
|
||||
<Th width="20%" isNumeric>
|
||||
<Link onClick={ sort('val') } display="flex" justifyContent="end">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
Box,
|
||||
Tr,
|
||||
Td,
|
||||
Tag,
|
||||
@@ -8,6 +7,7 @@ import {
|
||||
Text,
|
||||
Show,
|
||||
Hide,
|
||||
Flex,
|
||||
} from '@chakra-ui/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { route } from 'nextjs-routes';
|
||||
@@ -44,14 +44,14 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement }
|
||||
const timeAgo = useTimeAgoIncrement(tx.timestamp, enableTimeIncrement);
|
||||
|
||||
const addressFrom = (
|
||||
<Address>
|
||||
<Address w="100%">
|
||||
<AddressIcon address={ tx.from }/>
|
||||
<AddressLink type="address" hash={ tx.from.hash } alias={ tx.from.name } fontWeight="500" ml={ 2 } truncation="constant" isDisabled={ isOut }/>
|
||||
</Address>
|
||||
);
|
||||
|
||||
const addressTo = dataTo ? (
|
||||
<Address>
|
||||
<Address w="100%">
|
||||
<AddressIcon address={ dataTo }/>
|
||||
<AddressLink type="address" hash={ dataTo.hash } alias={ dataTo.name } fontWeight="500" ml={ 2 } truncation="constant" isDisabled={ isIn }/>
|
||||
</Address>
|
||||
@@ -117,21 +117,23 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement }
|
||||
</Show>
|
||||
<Hide above="xl" ssr={ false }>
|
||||
<Td colSpan={ 3 }>
|
||||
<Box>
|
||||
{ addressFrom }
|
||||
<Flex alignItems="center">
|
||||
{ (isIn || isOut) ?
|
||||
<InOutTag isIn={ isIn } isOut={ isOut } width="48px" my={ 2 }/> : (
|
||||
<InOutTag isIn={ isIn } isOut={ isOut } width="48px"/> :
|
||||
(
|
||||
<Icon
|
||||
as={ rightArrowIcon }
|
||||
boxSize={ 6 }
|
||||
mt={ 2 }
|
||||
mb={ 1 }
|
||||
color="gray.500"
|
||||
transform="rotate(90deg)"
|
||||
/>
|
||||
) }
|
||||
{ addressTo }
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
<VStack alignItems="start" overflow="hidden" ml={ 1 }>
|
||||
{ addressFrom }
|
||||
{ addressTo }
|
||||
</VStack>
|
||||
</Flex>
|
||||
</Td>
|
||||
</Hide>
|
||||
<Td isNumeric>
|
||||
|
||||
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 39 KiB |