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

Merge pull request #1881 from blockscout/tom2drum/issue-1863

Minor bug fixes
parents 1263ed5c e6a06778
......@@ -200,6 +200,8 @@ Settings for meta tags, OG tags and SEO
| `total_reward` | Total block reward |
| `nonce` | Block nonce |
| `miner` | Address of block's miner or validator |
| `L1_status` | Short interpretation of the batch lifecycle (applicable for Rollup chains) |
| `batch` | Batch index (applicable for Rollup chains) |
 
......@@ -234,6 +236,8 @@ Settings for meta tags, OG tags and SEO
| `tx_fee` | Total transaction fee |
| `gas_fees` | Gas fees breakdown |
| `burnt_fees` | Amount of native coin burnt for transaction |
| `L1_status` | Short interpretation of the batch lifecycle (applicable for Rollup chains) |
| `batch` | Batch index (applicable for Rollup chains) |
##### Transaction additional fields list
| Id | Description |
......
......@@ -5,6 +5,8 @@ export const BLOCK_FIELDS_IDS = [
'total_reward',
'nonce',
'miner',
'L1_status',
'batch',
] as const;
export type BlockFieldId = ArrayElement<typeof BLOCK_FIELDS_IDS>;
......@@ -7,6 +7,8 @@ export const TX_FIELDS_IDS = [
'tx_fee',
'gas_fees',
'burnt_fees',
'L1_status',
'batch',
] as const;
export type TxFieldsId = ArrayElement<typeof TX_FIELDS_IDS>;
......
......@@ -220,28 +220,28 @@ const BlockDetails = ({ query }: Props) => {
</DetailsInfoItem>
) }
{ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && data.zksync && (
<>
<DetailsInfoItem
title="Batch"
hint="Batch number"
isLoading={ isPlaceholderData }
>
{ data.zksync.batch_number ? (
<BatchEntityL2
isLoading={ isPlaceholderData }
number={ data.zksync.batch_number }
/>
) : <Skeleton isLoaded={ !isPlaceholderData }>Pending</Skeleton> }
</DetailsInfoItem>
<DetailsInfoItem
title="Status"
hint="Status is the short interpretation of the batch lifecycle"
isLoading={ isPlaceholderData }
>
<VerificationSteps steps={ ZKSYNC_L2_TX_BATCH_STATUSES } currentStep={ data.zksync.status } isLoading={ isPlaceholderData }/>
</DetailsInfoItem>
</>
{ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && data.zksync && !config.UI.views.block.hiddenFields?.batch && (
<DetailsInfoItem
title="Batch"
hint="Batch number"
isLoading={ isPlaceholderData }
>
{ data.zksync.batch_number ? (
<BatchEntityL2
isLoading={ isPlaceholderData }
number={ data.zksync.batch_number }
/>
) : <Skeleton isLoaded={ !isPlaceholderData }>Pending</Skeleton> }
</DetailsInfoItem>
) }
{ rollupFeature.isEnabled && rollupFeature.type === 'zkSync' && data.zksync && !config.UI.views.block.hiddenFields?.L1_status && (
<DetailsInfoItem
title="Status"
hint="Status is the short interpretation of the batch lifecycle"
isLoading={ isPlaceholderData }
>
<VerificationSteps steps={ ZKSYNC_L2_TX_BATCH_STATUSES } currentStep={ data.zksync.status } isLoading={ isPlaceholderData }/>
</DetailsInfoItem>
) }
{ !config.UI.views.block.hiddenFields?.miner && (
......
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import * as statsMock from 'mocks/stats/index';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import { test, expect } from 'playwright/lib';
import * as configs from 'playwright/utils/configs';
import GasTrackerPriceSnippet from './GasTrackerPriceSnippet';
......@@ -13,48 +11,57 @@ test.use({ viewport: configs.viewport.md });
const data = statsMock.base.gas_prices.fast;
const clip = { x: 0, y: 0, width: 334, height: 204 };
test('with usd as primary unit +@dark-mode', async({ mount, page }) => {
await mount(
<TestApp>
<GasTrackerPriceSnippet
data={ data }
type="fast"
isLoading={ false }
/>
</TestApp>,
test('with usd as primary unit +@dark-mode', async({ render, page }) => {
await render(
<GasTrackerPriceSnippet
data={ data }
type="fast"
isLoading={ false }
/>,
);
await expect(page).toHaveScreenshot({ clip });
});
test('loading state', async({ mount, page }) => {
await mount(
<TestApp>
<GasTrackerPriceSnippet
data={ data }
type="fast"
isLoading={ true }
/>
</TestApp>,
test('loading state', async({ render, page }) => {
await render(
<GasTrackerPriceSnippet
data={ data }
type="fast"
isLoading={ true }
/>,
);
await expect(page).toHaveScreenshot({ clip });
});
const gweiUnitsTest = test.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_GAS_TRACKER_UNITS', value: '["gwei","usd"]' },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
test('with gwei as primary unit +@dark-mode', async({ render, page, mockEnvs }) => {
await mockEnvs([
[ 'NEXT_PUBLIC_GAS_TRACKER_UNITS', '["gwei","usd"]' ],
]);
await render(
<GasTrackerPriceSnippet
data={ data }
type="slow"
isLoading={ false }
/>,
);
await expect(page).toHaveScreenshot({ clip });
});
gweiUnitsTest('with gwei as primary unit +@dark-mode', async({ mount, page }) => {
await mount(
<TestApp>
<GasTrackerPriceSnippet
data={ data }
type="slow"
isLoading={ false }
/>
</TestApp>,
test('with zero values', async({ render, page }) => {
const data = {
fiat_price: '1.74',
price: 0.0,
time: 0,
base_fee: 0,
priority_fee: 0,
};
await render(
<GasTrackerPriceSnippet
data={ data }
type="slow"
isLoading={ false }
/>,
);
await expect(page).toHaveScreenshot({ clip });
});
......@@ -50,14 +50,14 @@ const GasTrackerPriceSnippet = ({ data, type, isLoading }: Props) => {
</Skeleton>
</Flex>
<Skeleton isLoaded={ !isLoading } fontSize="sm" color="text_secondary" mt={ 3 } w="fit-content">
{ data.price && data.fiat_price && <GasPrice data={ data } prefix={ `${ asymp } ` } unitMode="secondary"/> }
{ data.price !== null && data.fiat_price !== null && <GasPrice data={ data } prefix={ `${ asymp } ` } unitMode="secondary"/> }
<span> per transaction</span>
{ typeof data.time === 'number' && data.time > 0 && <span> / { (data.time / SECOND).toLocaleString(undefined, { maximumFractionDigits: 1 }) }s</span> }
</Skeleton>
<Skeleton isLoaded={ !isLoading } fontSize="sm" color="text_secondary" mt={ 2 } w="fit-content" whiteSpace="pre">
{ data.base_fee && <span>Base { data.base_fee.toLocaleString(undefined, { maximumFractionDigits: 0 }) }</span> }
{ data.base_fee && data.priority_fee && <span> / </span> }
{ data.priority_fee && <span>Priority { data.priority_fee.toLocaleString(undefined, { maximumFractionDigits: 0 }) }</span> }
{ typeof data.base_fee === 'number' && <span>Base { data.base_fee.toLocaleString(undefined, { maximumFractionDigits: 0 }) }</span> }
{ typeof data.base_fee === 'number' && typeof data.priority_fee === 'number' && <span> / </span> }
{ typeof data.priority_fee === 'number' && <span>Priority { data.priority_fee.toLocaleString(undefined, { maximumFractionDigits: 0 }) }</span> }
</Skeleton>
</Box>
);
......
......@@ -36,7 +36,8 @@ const GasTracker = () => {
rowGap={ 1 }
flexDir={{ base: 'column', lg: 'row' }}
>
{ data?.network_utilization_percentage && <GasTrackerNetworkUtilization percentage={ data.network_utilization_percentage } isLoading={ isLoading }/> }
{ typeof data?.network_utilization_percentage === 'number' &&
<GasTrackerNetworkUtilization percentage={ data.network_utilization_percentage } isLoading={ isLoading }/> }
{ data?.gas_price_updated_at && (
<Skeleton isLoaded={ !isLoading } whiteSpace="pre" display="flex" alignItems="center">
<span>Last updated </span>
......
......@@ -11,10 +11,6 @@ import WalletMenuMobile from 'ui/snippets/walletMenu/WalletMenuMobile';
import Burger from './Burger';
const LOGO_IMAGE_PROPS = {
margin: '0 auto',
};
type Props = {
hideSearchBar?: boolean;
renderSearchBar?: () => React.ReactNode;
......@@ -45,13 +41,12 @@ const HeaderMobile = ({ hideSearchBar, renderSearchBar }: Props) => {
bgColor={ bgColor }
width="100%"
alignItems="center"
justifyContent="space-between"
transitionProperty="box-shadow"
transitionDuration="slow"
boxShadow={ !inView && scrollDirection === 'down' ? 'md' : 'none' }
>
<Burger/>
<NetworkLogo imageProps={ LOGO_IMAGE_PROPS }/>
<NetworkLogo ml={ 2 } mr="auto"/>
<Flex columnGap={ 2 }>
{ config.features.account.isEnabled ? <ProfileMenuMobile/> : <Box boxSize={ 10 }/> }
{ config.features.blockchainInteraction.isEnabled && <WalletMenuMobile/> }
......
......@@ -20,16 +20,17 @@ type Props = {
px?: string | number;
className?: string;
onClick?: () => void;
disableActiveState?: boolean;
}
const NavLink = ({ item, isCollapsed, px, className, onClick }: Props) => {
const NavLink = ({ item, isCollapsed, px, className, onClick, disableActiveState }: Props) => {
const isMobile = useIsMobile();
const colors = useColors();
const isExpanded = isCollapsed === false;
const isInternalLink = isInternalItem(item);
const styleProps = useNavLinkStyleProps({ isCollapsed, isExpanded, isActive: isInternalLink && item.isActive });
const styleProps = useNavLinkStyleProps({ isCollapsed, isExpanded, isActive: isInternalLink && item.isActive && !disableActiveState });
const isXLScreen = useBreakpointValue({ base: false, xl: true });
const href = isInternalLink ? route(item.nextRoute) : item.url;
......
import type { StyleProps } from '@chakra-ui/react';
import { Box, Image, useColorModeValue, Skeleton } from '@chakra-ui/react';
import { Box, Image, useColorModeValue, Skeleton, chakra } from '@chakra-ui/react';
import React from 'react';
import { route } from 'nextjs-routes';
......@@ -10,10 +9,10 @@ import IconSvg from 'ui/shared/IconSvg';
interface Props {
isCollapsed?: boolean;
onClick?: (event: React.SyntheticEvent) => void;
imageProps?: StyleProps;
className?: string;
}
const LogoFallback = ({ isCollapsed, isSmall, imageProps }: { isCollapsed?: boolean; isSmall?: boolean; imageProps?: StyleProps }) => {
const LogoFallback = ({ isCollapsed, isSmall }: { isCollapsed?: boolean; isSmall?: boolean }) => {
const field = isSmall ? 'icon' : 'logo';
const logoColor = useColorModeValue('blue.600', 'white');
......@@ -38,12 +37,11 @@ const LogoFallback = ({ isCollapsed, isSmall, imageProps }: { isCollapsed?: bool
height="100%"
color={ logoColor }
display={ display }
{ ...imageProps }
/>
);
};
const NetworkLogo = ({ isCollapsed, onClick, imageProps }: Props) => {
const NetworkLogo = ({ isCollapsed, onClick, className }: Props) => {
const logoSrc = useColorModeValue(config.UI.sidebar.logo.default, config.UI.sidebar.logo.dark || config.UI.sidebar.logo.default);
const iconSrc = useColorModeValue(config.UI.sidebar.icon.default, config.UI.sidebar.icon.dark || config.UI.sidebar.icon.default);
......@@ -53,6 +51,7 @@ const NetworkLogo = ({ isCollapsed, onClick, imageProps }: Props) => {
return (
<Box
className={ className }
as="a"
href={ route({ pathname: '/' }) }
width={{ base: '120px', lg: isCollapsed === false ? '120px' : '30px', xl: isCollapsed ? '30px' : '120px' }}
......@@ -69,10 +68,9 @@ const NetworkLogo = ({ isCollapsed, onClick, imageProps }: Props) => {
h="100%"
src={ logoSrc }
alt={ `${ config.chain.name } network logo` }
fallback={ <LogoFallback isCollapsed={ isCollapsed } imageProps={ imageProps }/> }
fallback={ <LogoFallback isCollapsed={ isCollapsed }/> }
display={{ base: 'block', lg: isCollapsed === false ? 'block' : 'none', xl: isCollapsed ? 'none' : 'block' }}
style={ logoStyle }
{ ...imageProps }
/>
{ /* small logo */ }
<Image
......@@ -80,13 +78,12 @@ const NetworkLogo = ({ isCollapsed, onClick, imageProps }: Props) => {
h="100%"
src={ iconSrc }
alt={ `${ config.chain.name } network logo` }
fallback={ <LogoFallback isCollapsed={ isCollapsed } imageProps={ imageProps } isSmall/> }
fallback={ <LogoFallback isCollapsed={ isCollapsed } isSmall/> }
display={{ base: 'none', lg: isCollapsed === false ? 'none' : 'block', xl: isCollapsed ? 'block' : 'none' }}
style={ iconStyle }
{ ...imageProps }
/>
</Box>
);
};
export default React.memo(NetworkLogo);
export default React.memo(chakra(NetworkLogo));
......@@ -45,10 +45,12 @@ const NetworkMenuContentMobile = ({ items, tabs }: Props) => {
</>
) : (
<>
<Select size="xs" borderRadius="base" value={ selectedTab } onChange={ handleSelectChange } focusBorderColor="none">
{ tabs.map((tab) => <option key={ tab } value={ tab }>{ capitalize(tab) }</option>) }
</Select>
<VStack as="ul" spacing={ 2 } alignItems="stretch" mt={ 6 }>
{ tabs.length > 1 && (
<Select size="xs" borderRadius="base" value={ selectedTab } onChange={ handleSelectChange } focusBorderColor="none" mb={ 6 }>
{ tabs.map((tab) => <option key={ tab } value={ tab }>{ capitalize(tab) }</option>) }
</Select>
) }
<VStack as="ul" spacing={ 2 } alignItems="stretch">
{ items
.filter(({ group }) => group === selectedTab)
.map((network) => (
......
import { Box, Button, Text, VStack, useColorModeValue } from '@chakra-ui/react';
import { Box, Button, VStack, chakra } from '@chakra-ui/react';
import React from 'react';
import type { UserInfo } from 'types/api/account';
......@@ -18,7 +18,6 @@ type Props = {
const ProfileMenuContent = ({ data, onNavLinkClick }: Props) => {
const { accountNavItems, profileItem } = useNavItems();
const primaryTextColor = useColorModeValue('blackAlpha.800', 'whiteAlpha.800');
const handleSingOutClick = React.useCallback(() => {
mixpanel.logEvent(
......@@ -32,37 +31,28 @@ const ProfileMenuContent = ({ data, onNavLinkClick }: Props) => {
return null;
}
const userName = data?.email || data?.nickname || data?.name;
return (
<Box>
{ (data?.name || data?.nickname) && (
<Text
{ userName && (
<Box
fontSize="sm"
fontWeight={ 500 }
color={ primaryTextColor }
{ ...getDefaultTransitionProps() }
>
Signed in as { data.name || data.nickname }
</Text>
) }
{ data?.email && (
<Text
fontSize="sm"
mb={ 1 }
fontWeight={ 500 }
color="gray.500"
{ ...getDefaultTransitionProps() }
>
{ data.email }
</Text>
<span>Signed in as </span>
<chakra.span color="text_secondary">{ userName }</chakra.span>
</Box>
) }
<NavLink item={ profileItem } isActive={ undefined } px="0px" isCollapsed={ false } onClick={ onNavLinkClick }/>
<NavLink item={ profileItem } disableActiveState={ true } px="0px" isCollapsed={ false } onClick={ onNavLinkClick }/>
<Box as="nav" mt={ 2 } pt={ 2 } borderTopColor="divider" borderTopWidth="1px" { ...getDefaultTransitionProps() }>
<VStack as="ul" spacing="0" alignItems="flex-start" overflow="hidden">
{ accountNavItems.map((item) => (
<NavLink
key={ item.text }
item={ item }
isActive={ undefined }
disableActiveState={ true }
isCollapsed={ false }
px="0px"
onClick={ onNavLinkClick }
......
......@@ -97,7 +97,7 @@ const ProfileMenuDesktop = ({ isHomePage, className, fallbackIconSize }: Props)
</Box>
</Tooltip>
{ hasMenu && (
<PopoverContent w="212px">
<PopoverContent maxW="400px" minW="220px" w="min-content">
<PopoverBody padding="24px 16px 16px 16px">
<ProfileMenuContent data={ data }/>
</PopoverBody>
......
......@@ -66,7 +66,7 @@ const ProfileMenuMobile = () => {
autoFocus={ false }
>
<DrawerOverlay/>
<DrawerContent maxWidth="260px">
<DrawerContent maxWidth="300px">
<DrawerBody p={ 6 }>
<ProfileMenuContent data={ data } onNavLinkClick={ onClose }/>
</DrawerBody>
......
......@@ -160,7 +160,8 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
</Tag>
) }
</DetailsInfoItem>
{ rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && data.op_withdrawals && data.op_withdrawals.length > 0 && (
{ rollupFeature.isEnabled && rollupFeature.type === 'optimistic' && data.op_withdrawals && data.op_withdrawals.length > 0 &&
!config.UI.views.tx.hiddenFields?.L1_status && (
<DetailsInfoItem
title="Withdrawal status"
hint="Detailed status progress of the transaction"
......@@ -181,7 +182,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
</Flex>
</DetailsInfoItem>
) }
{ data.zkevm_status && (
{ data.zkevm_status && !config.UI.views.tx.hiddenFields?.L1_status && (
<DetailsInfoItem
title="Confirmation status"
hint="Status of the transaction confirmation path to L1"
......@@ -198,7 +199,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
<TxRevertReason { ...data.revert_reason }/>
</DetailsInfoItem>
) }
{ data.zksync && (
{ data.zksync && !config.UI.views.tx.hiddenFields?.L1_status && (
<DetailsInfoItem
title="L1 status"
hint="Status is the short interpretation of the batch lifecycle"
......@@ -229,7 +230,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
</>
) }
</DetailsInfoItem>
{ data.zkevm_batch_number && (
{ data.zkevm_batch_number && !config.UI.views.tx.hiddenFields?.batch && (
<DetailsInfoItem
title="Tx batch"
hint="Batch index for this transaction"
......@@ -241,7 +242,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
/>
</DetailsInfoItem>
) }
{ data.zksync && (
{ data.zksync && !config.UI.views.tx.hiddenFields?.batch && (
<DetailsInfoItem
title="Batch"
hint="Batch number"
......
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