Commit 963aaf40 authored by tom's avatar tom

change PageTitle layout

parent 2dcddbd0
......@@ -15,7 +15,7 @@ const APIDocsPage: NextPage = () => {
return (
<Page>
<PageTitle text="API Documentation"/>
<PageTitle title="API Documentation"/>
<Head><title>{ `API for the ${ networkTitle }` }</title></Head>
<SwaggerUI/>
</Page>
......
......@@ -9,7 +9,7 @@ import PageTitle from 'ui/shared/Page/PageTitle';
const MarketplacePage: NextPage = () => {
return (
<Page>
<PageTitle text="Marketplace"/>
<PageTitle title="Marketplace"/>
<Head><title>Blockscout | Marketplace</title></Head>
<Marketplace/>
......
......@@ -16,7 +16,7 @@ const GraphiqlPage: NextPage = () => {
return (
<Page>
<Head><title>Graph Page</title></Head>
<PageTitle text="GraphQL playground"/>
<PageTitle title="GraphQL playground"/>
<GraphQL/>
</Page>
);
......
import type { Transaction } from 'types/api/transaction';
import type { AddressTag, WatchlistName } from './addressParams';
import type { UserTags } from './addressParams';
import type { Block } from './block';
import type { InternalTransaction } from './internalTransaction';
import type { TokenInfo, TokenInstance, TokenType } from './token';
import type { TokenTransfer, TokenTransferPagination } from './tokenTransfer';
export interface Address {
export interface Address extends UserTags {
block_number_balance_updated_at: number | null;
coin_balance: string | null;
creator_address_hash: string | null;
......@@ -30,11 +30,8 @@ export interface Address {
is_contract: boolean;
is_verified: boolean;
name: string | null;
private_tags: Array<AddressTag> | null;
public_tags: Array<AddressTag> | null;
token: TokenInfo | null;
watchlist_address_id: number | null;
watchlist_names: Array<WatchlistName> | null;
}
export interface AddressCounters {
......
......@@ -9,13 +9,16 @@ export interface WatchlistName {
display_name: string;
}
export interface AddressParam {
export interface UserTags {
private_tags: Array<AddressTag> | null;
watchlist_names: Array<WatchlistName> | null;
public_tags: Array<AddressTag> | null;
}
export interface AddressParam extends UserTags {
hash: string;
implementation_name: string | null;
name: string | null;
is_contract: boolean;
is_verified: boolean | null;
private_tags: Array<AddressTag> | null;
watchlist_names: Array<WatchlistName> | null;
public_tags: Array<AddressTag> | null;
}
......@@ -51,7 +51,7 @@ const Accounts = () => {
return (
<Page>
<PageTitle text="Top accounts" withTextAd/>
<PageTitle title="Top accounts" withTextAd/>
<DataListDisplay
isError={ isError }
isLoading={ isLoading }
......
import { Flex, Skeleton, Tag, Box, Icon } from '@chakra-ui/react';
import { Skeleton, Box, Icon } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -22,6 +22,7 @@ import AddressTokenTransfers from 'ui/address/AddressTokenTransfers';
import AddressTxs from 'ui/address/AddressTxs';
import AddressWithdrawals from 'ui/address/AddressWithdrawals';
import TextAd from 'ui/shared/ad/TextAd';
import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
......@@ -49,17 +50,6 @@ const AddressPageContent = () => {
queryOptions: { enabled: Boolean(hash) },
});
const tags = [
addressQuery.data?.is_contract ? { label: 'contract', display_name: 'Contract' } : { label: 'eoa', display_name: 'EOA' },
addressQuery.data?.implementation_address ? { label: 'proxy', display_name: 'Proxy' } : undefined,
addressQuery.data?.token ? { label: 'token', display_name: 'Token' } : undefined,
...(addressQuery.data?.private_tags || []),
...(addressQuery.data?.public_tags || []),
...(addressQuery.data?.watchlist_names || []),
]
.filter(Boolean)
.map((tag) => <Tag key={ tag.label }>{ tag.display_name }</Tag>);
const contractTabs = useContractTabs(addressQuery.data);
const tabs: Array<RoutedTab> = React.useMemo(() => {
......@@ -98,12 +88,19 @@ const AddressPageContent = () => {
].filter(Boolean);
}, [ addressQuery.data, contractTabs, hash ]);
const tagsNode = tags.length > 0 ? <Flex columnGap={ 2 }>{ tags }</Flex> : null;
const additionalsRight = (
<>
{ tagsNode }
<NetworkExplorers type="address" pathParam={ hash } ml="auto"/>
</>
const tags = (
<EntityTags
data={ addressQuery.data }
isLoading={ addressQuery.isPlaceholderData }
tagsBefore={ [
addressQuery.data?.is_contract ? { label: 'contract', display_name: 'Contract' } : { label: 'eoa', display_name: 'EOA' },
addressQuery.data?.implementation_address ? { label: 'proxy', display_name: 'Proxy' } : undefined,
addressQuery.data?.token ? { label: 'token', display_name: 'Token' } : undefined,
] }
contentAfter={
<NetworkExplorers type="address" pathParam={ hash } ml="auto"/>
}
/>
);
const content = addressQuery.isError ? null : <RoutedTabs tabs={ tabs } tabListProps={{ mt: 8 }}/>;
......@@ -128,9 +125,9 @@ const AddressPageContent = () => {
<Skeleton h={ 10 } w="260px" mb={ 6 }/>
) : (
<PageTitle
text={ `${ addressQuery.data?.is_contract ? 'Contract' : 'Address' } details` }
additionalsRight={ additionalsRight }
title={ `${ addressQuery.data?.is_contract ? 'Contract' : 'Address' } details` }
backLink={ backLink }
contentAfter={ tags }
/>
) }
<AddressDetails addressQuery={ addressQuery } scrollRef={ tabsScrollRef }/>
......
......@@ -132,7 +132,7 @@ const ApiKeysPage: React.FC = () => {
return (
<Page>
<Box h="100%">
<PageTitle text="API keys"/>
<PageTitle title="API keys"/>
{ content }
</Box>
</Page>
......
......@@ -103,9 +103,10 @@ const BlockPageContent = () => {
<Skeleton h={ 10 } w="300px" mb={ 6 }/>
) : (
<PageTitle
text={ `Block #${ blockQuery.data?.height }` }
title={ `Block #${ blockQuery.data?.height }` }
backLink={ backLink }
additionalsRight={ <NetworkExplorers type="block" pathParam={ height } ml={{ base: 'initial', lg: 'auto' }}/> }
contentAfter={ <NetworkExplorers type="block" pathParam={ height } ml={{ base: 'initial', lg: 'auto' }}/> }
withTextAd
/>
) }
{ blockQuery.isLoading ? <SkeletonTabs/> : (
......
......@@ -42,7 +42,7 @@ const BlocksPageContent = () => {
return (
<Page>
<PageTitle text="Blocks" withTextAd/>
<PageTitle title="Blocks" withTextAd/>
<RoutedTabs
tabs={ tabs }
tabListProps={ isMobile ? undefined : TAB_LIST_PROPS }
......
......@@ -98,7 +98,7 @@ const ContractVerification = () => {
return (
<Page>
<PageTitle
text="New smart contract verification"
title="New smart contract verification"
backLink={ backLink }
/>
{ hash && (
......
......@@ -90,7 +90,7 @@ const CsvExport = () => {
return (
<Page>
<PageTitle
text="Export data to CSV file"
title="Export data to CSV file"
backLink={ backLink }
/>
<Flex mb={ 10 } whiteSpace="pre-wrap" flexWrap="wrap">
......
......@@ -115,7 +115,7 @@ const CustomAbiPage: React.FC = () => {
return (
<Page>
<Box h="100%">
<PageTitle text="Custom ABI"/>
<PageTitle title="Custom ABI"/>
{ content }
</Box>
</Page>
......
......@@ -9,7 +9,7 @@ import PageTitle from 'ui/shared/Page/PageTitle';
const Graph = () => {
return (
<Page>
<PageTitle text="Charts"/>
<PageTitle title="Charts"/>
<Heading as="h2" size="sm" fontWeight="500" mb={ 3 }>Ethereum Daily Transactions & ERC-20 Token Transfer Chart</Heading>
<Box w="100%" h="400px">
<EthereumChart/>
......
......@@ -68,7 +68,7 @@ const L2Deposits = () => {
return (
<Page>
<PageTitle text={ `Deposits (L1${ nbsp }${ rightLineArrow }${ nbsp }L2)` } withTextAd/>
<PageTitle title={ `Deposits (L1${ nbsp }${ rightLineArrow }${ nbsp }L2)` } withTextAd/>
<DataListDisplay
isError={ isError }
isLoading={ isLoading }
......
......@@ -70,7 +70,7 @@ const L2OutputRoots = () => {
return (
<Page>
<PageTitle text="Output roots" withTextAd/>
<PageTitle title="Output roots" withTextAd/>
<DataListDisplay
isError={ isError }
isLoading={ isLoading }
......
......@@ -71,7 +71,7 @@ const L2TxnBatches = () => {
return (
<Page>
<PageTitle text={ `Tx batches (L2${ nbsp }blocks)` } withTextAd/>
<PageTitle title={ `Tx batches (L2${ nbsp }blocks)` } withTextAd/>
<DataListDisplay
isError={ isError }
isLoading={ isLoading }
......
......@@ -68,7 +68,7 @@ const L2Withdrawals = () => {
return (
<Page>
<PageTitle text={ `Withdrawals (L2${ nbsp }${ rightLineArrow }${ nbsp }L1)` } withTextAd/>
<PageTitle title={ `Withdrawals (L2${ nbsp }${ rightLineArrow }${ nbsp }L1)` } withTextAd/>
<DataListDisplay
isError={ isError }
isLoading={ isLoading }
......
......@@ -56,7 +56,7 @@ const Login = () => {
return (
<Page>
<VStack gap={ 4 } alignItems="flex-start" maxW="1000px">
<PageTitle text="Login page 😂"/>
<PageTitle title="Login page 😂"/>
{ isFormVisible && (
<>
<Alert status="error" flexDirection="column" alignItems="flex-start">
......
......@@ -55,7 +55,7 @@ const MyProfile = () => {
return (
<Page>
<PageTitle text="My profile"/>
<PageTitle title="My profile"/>
{ content }
</Page>
);
......
......@@ -19,7 +19,7 @@ const PrivateTags = () => {
return (
<Page>
<PageTitle text="Private tags"/>
<PageTitle title="Private tags"/>
<RoutedTabs tabs={ TABS }/>
</Page>
);
......
import { Link, Text, Icon } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React, { useCallback, useState } from 'react';
import { animateScroll } from 'react-scroll';
import type { PublicTag } from 'types/api/account';
import eastArrowIcon from 'icons/arrows/east.svg';
import useIsMobile from 'lib/hooks/useIsMobile';
import useRedirectForInvalidAuthToken from 'lib/hooks/useRedirectForInvalidAuthToken';
import useToast from 'lib/hooks/useToast';
import getQueryParamString from 'lib/router/getQueryParamString';
......@@ -32,7 +29,6 @@ const PublicTagsComponent: React.FC = () => {
const [ formData, setFormData ] = useState<Partial<PublicTag> | undefined>(addressHash ? { addresses: [ addressHash ] } : undefined);
const toast = useToast();
const isMobile = useIsMobile();
useRedirectForInvalidAuthToken();
React.useEffect(() => {
......@@ -88,15 +84,18 @@ const PublicTagsComponent: React.FC = () => {
header = formData ? 'Request to edit a public tag/label' : 'Request a public tag/label';
}
const backLink = {
label: 'Public tags',
onClick: onGoBack,
};
return (
<Page>
{ screen === 'form' && (
<Link display="inline-flex" alignItems="center" mb={ 6 } onClick={ onGoBack }>
<Icon as={ eastArrowIcon } boxSize={ 6 } transform="rotate(180deg)"/>
{ isMobile && <Text variant="inherit" fontSize="sm" ml={ 2 }>Public tags</Text> }
</Link>
) }
<PageTitle text={ header } display={{ base: 'block', lg: 'inline-flex' }} ml={{ base: 0, lg: 3 }}/>
<PageTitle
title={ header }
backLink={ screen === 'form' ? backLink : undefined }
display={{ base: 'block', lg: 'inline-flex' }}
/>
{ content }
</Page>
);
......
......@@ -152,7 +152,7 @@ const SearchResultsPageContent = () => {
<Page renderHeader={ renderHeader }>
{ isLoading || redirectCheckQuery.isLoading ?
<Skeleton h={ 10 } mb={ 6 } w="100%" maxW="222px"/> :
<PageTitle text="Search results"/>
<PageTitle title="Search results"/>
}
{ bar }
{ content }
......
......@@ -34,7 +34,7 @@ const Sol2Uml = () => {
return (
<Page>
<PageTitle
text="Solidity UML diagram"
title="Solidity UML diagram"
backLink={ backLink }
/>
<Flex mb={ 10 }>
......
......@@ -26,7 +26,7 @@ const Stats = () => {
return (
<Page>
<PageTitle text={ `${ appConfig.network.name } stats` }/>
<PageTitle title={ `${ appConfig.network.name } stats` }/>
<Box mb={{ base: 6, sm: 8 }}>
<NumberWidgetsList/>
......
import { Box, Icon, Flex } from '@chakra-ui/react';
import { Box, Icon } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import React, { useEffect } from 'react';
......@@ -22,7 +22,7 @@ import * as addressStubs from 'stubs/address';
import * as tokenStubs from 'stubs/token';
import AddressContract from 'ui/address/AddressContract';
import TextAd from 'ui/shared/ad/TextAd';
import Tag from 'ui/shared/chakra/Tag';
import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import PageTitle from 'ui/shared/Page/PageTitle';
import type { Props as PaginationProps } from 'ui/shared/Pagination';
......@@ -228,38 +228,36 @@ const TokenPageContent = () => {
};
}, [ appProps.referrer ]);
const tags = [
{ label: tokenQuery.data?.type, display_name: tokenQuery.data?.type },
...(contractQuery.data?.private_tags || []),
...(contractQuery.data?.public_tags || []),
...(contractQuery.data?.watchlist_names || []),
]
.filter(Boolean)
.map((tag) => <Tag key={ tag.label } isLoading={ tokenQuery.isPlaceholderData }>{ tag.display_name }</Tag>);
const tagsNode = tags.length > 0 ? <Flex columnGap={ 2 }>{ tags }</Flex> : null;
const additionalsRight = (
<>
{ tagsNode }
<NetworkExplorers type="token" pathParam={ hashString } ml="auto"/>
</>
const tags = (
<EntityTags
data={ contractQuery.data }
isLoading={ tokenQuery.isPlaceholderData || contractQuery.isPlaceholderData }
tagsBefore={ [
tokenQuery.data ? { label: tokenQuery.data?.type, display_name: tokenQuery.data?.type } : undefined,
] }
contentAfter={
<NetworkExplorers type="token" pathParam={ hashString } ml="auto"/>
}
flexGrow={ 1 }
/>
);
return (
<>
<TextAd mb={ 6 }/>
<PageTitle
title={ `${ tokenQuery.data?.name || 'Unnamed' }${ tokenSymbolText } token` }
isLoading={ tokenQuery.isPlaceholderData }
text={ `${ tokenQuery.data?.name || 'Unnamed' }${ tokenSymbolText } token` }
backLink={ backLink }
additionalsLeft={ (
<TokenLogo data={ tokenQuery.data } boxSize={ 6 } isLoading={ tokenQuery.isPlaceholderData }/>
beforeTitle={ (
<TokenLogo data={ tokenQuery.data } boxSize={ 6 } isLoading={ tokenQuery.isPlaceholderData } display="inline-block" mr={ 2 }/>
) }
additionalsRight={ additionalsRight }
afterTitle={
verifiedInfoQuery.data?.tokenAddress ?
<Icon as={ iconSuccess } color="green.500" boxSize={ 4 } verticalAlign="top"/> :
<Box boxSize={ 4 } display="inline-block"/>
}
contentAfter={ tags }
/>
<TokenContractInfo tokenQuery={ tokenQuery } contractQuery={ contractQuery }/>
<TokenVerifiedInfo verifiedInfoQuery={ verifiedInfoQuery } isVerifiedInfoEnabled={ isVerifiedInfoEnabled }/>
......
......@@ -7,7 +7,7 @@ import TokensList from 'ui/tokens/Tokens';
const Tokens = () => {
return (
<Page>
<PageTitle text="Tokens" withTextAd/>
<PageTitle title="Tokens" withTextAd/>
<TokensList/>
</Page>
);
......
import { Flex, Tag } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import React from 'react';
......@@ -8,6 +7,7 @@ import useApiQuery from 'lib/api/useApiQuery';
import { useAppContext } from 'lib/appContext';
import getQueryParamString from 'lib/router/getQueryParamString';
import TextAd from 'ui/shared/ad/TextAd';
import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
......@@ -34,16 +34,19 @@ const TransactionPageContent = () => {
const hash = getQueryParamString(router.query.hash);
const { data } = useApiQuery('tx', {
const { data, isPlaceholderData } = useApiQuery('tx', {
pathParams: { hash },
queryOptions: { enabled: Boolean(hash) },
});
const additionals = (
<Flex justifyContent="space-between" alignItems="center" flexGrow={ 1 } flexWrap="wrap">
{ data?.tx_tag && <Tag my={ 2 }>{ data.tx_tag }</Tag> }
<NetworkExplorers type="tx" pathParam={ hash } ml={{ base: 'initial', lg: 'auto' }}/>
</Flex>
const tags = (
<EntityTags
isLoading={ isPlaceholderData }
tagsBefore={ [ data?.tx_tag ? { label: data.tx_tag, display_name: data.tx_tag } : undefined ] }
contentAfter={
<NetworkExplorers type="tx" pathParam={ hash } ml={{ base: 'initial', lg: 'auto' }}/>
}
/>
);
const backLink = React.useMemo(() => {
......@@ -63,9 +66,9 @@ const TransactionPageContent = () => {
<Page>
<TextAd mb={ 6 }/>
<PageTitle
text="Transaction details"
additionalsRight={ additionals }
title="Transaction details"
backLink={ backLink }
contentAfter={ tags }
/>
<RoutedTabs tabs={ TABS }/>
</Page>
......
......@@ -73,7 +73,7 @@ const Transactions = () => {
return (
<Page>
<Box h="100%">
<PageTitle text="Transactions" withTextAd/>
<PageTitle title="Transactions" withTextAd/>
<RoutedTabs
tabs={ tabs }
tabListProps={ isMobile ? undefined : TAB_LIST_PROPS }
......
......@@ -130,7 +130,7 @@ const VerifiedAddresses = () => {
const tokenName = addressInfo ? `${ addressInfo.metadata.tokenName } (${ addressInfo.metadata.tokenSymbol })` : '';
return (
<>
<PageTitle text="Token info application form" backLink={ backLink }/>
<PageTitle title="Token info application form" backLink={ backLink }/>
<TokenInfoForm
address={ selectedAddress }
tokenName={ tokenName }
......@@ -167,7 +167,7 @@ const VerifiedAddresses = () => {
return (
<>
<PageTitle text="My verified addresses"/>
<PageTitle title="My verified addresses"/>
<AccountPageDescription allowCut={ false }>
<span>
Verify ownership of a smart contract address to easily update information in Blockscout.
......
......@@ -117,7 +117,7 @@ const VerifiedContracts = () => {
return (
<Box>
<PageTitle text="Verified contracts" withTextAd/>
<PageTitle title="Verified contracts" withTextAd/>
<VerifiedContractsCounters/>
<DataListDisplay
isError={ isError }
......
......@@ -167,7 +167,7 @@ const WatchList: React.FC = () => {
return (
<Page>
<Box h="100%">
<PageTitle text="Watch list"/>
<PageTitle title="Watch list"/>
{ content }
</Box>
</Page>
......
......@@ -70,7 +70,7 @@ const Withdrawals = () => {
return (
<Page>
<PageTitle text="Withdrawals" withTextAd/>
<PageTitle title="Withdrawals" withTextAd/>
<DataListDisplay
isError={ isError }
isLoading={ isLoading }
......
import { Flex, chakra } from '@chakra-ui/react';
import React from 'react';
import type { UserTags } from 'types/api/addressParams';
import Tag from 'ui/shared/chakra/Tag';
interface TagData {
label: string;
display_name: string;
}
interface Props {
className?: string;
data?: UserTags;
isLoading?: boolean;
tagsBefore?: Array<TagData | undefined>;
tagsAfter?: Array<TagData | undefined>;
contentAfter?: React.ReactNode;
}
const EntityTags = ({ className, data, tagsBefore = [], tagsAfter = [], isLoading, contentAfter }: Props) => {
const tags = [
...tagsBefore,
...(data?.private_tags || []),
...(data?.public_tags || []),
...(data?.watchlist_names || []),
...tagsAfter,
]
.filter(Boolean)
.map((tag) => <Tag key={ tag.label } isLoading={ isLoading }>{ tag.display_name }</Tag>);
if (tags.length === 0) {
return null;
}
return (
<Flex className={ className } columnGap={ 2 } rowGap={ 2 } flexWrap="wrap" alignItems="center">
{ tags }
{ contentAfter }
</Flex>
);
};
export default React.memo(chakra(EntityTags));
// import { Icon } from '@chakra-ui/react';
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
// import plusIcon from 'icons/plus.svg';
import * as textAdMock from 'mocks/ad/textAd';
import TestApp from 'playwright/TestApp';
import PageTitle from './PageTitle';
import DefaultView from './specs/DefaultView';
import LongNameAndManyTags from './specs/LongNameAndManyTags';
import WithTextAd from './specs/WithTextAd';
test.beforeEach(async({ page }) => {
await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
......@@ -19,49 +19,38 @@ test.beforeEach(async({ page }) => {
path: './playwright/image_s.jpg',
});
});
await page.route('https://example.com/logo.png', (route) => {
return route.fulfill({
status: 200,
path: './playwright/image_s.jpg',
});
});
});
test('default view +@mobile', async({ mount }) => {
const component = await mount(
<TestApp>
<PageTitle
text="Title"
/>
<DefaultView/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('with text ad, back link and addons +@mobile +@dark-mode', async({ mount }) => {
// https://github.com/microsoft/playwright/issues/15620
// not possible to pass component as a prop in tests
// const left = <Icon as={ plusIcon }/>;
test('with text ad +@mobile', async({ mount }) => {
const component = await mount(
<TestApp>
<PageTitle
text="Title"
withTextAd
backLink={{ label: 'Back', url: 'back' }}
// additionalsLeft={ left }
additionalsRight="Privet"
/>
<WithTextAd/>
</TestApp>,
);
await expect(component).toHaveScreenshot();
});
test('long title with text ad, back link and addons +@mobile', async({ mount }) => {
test('with long name and many tags +@mobile', async({ mount }) => {
const component = await mount(
<TestApp>
<PageTitle
text="This title is long, really long"
withTextAd
backLink={{ label: 'Back', url: 'back' }}
additionalsRight="Privet, kak dela?"
/>
<LongNameAndManyTags/>
</TestApp>,
);
......
import { Heading, Flex, Grid, Tooltip, Icon, Link, chakra, Skeleton } from '@chakra-ui/react';
import { Heading, Flex, Tooltip, Icon, Link, chakra, Box, Skeleton } from '@chakra-ui/react';
import React from 'react';
import eastArrowIcon from 'icons/arrows/east.svg';
......@@ -8,83 +8,79 @@ import LinkInternal from 'ui/shared/LinkInternal';
type BackLinkProp = { label: string; url: string } | { label: string; onClick: () => void };
type Props = {
text: string;
additionalsLeft?: React.ReactNode;
additionalsRight?: React.ReactNode;
withTextAd?: boolean;
title: string;
className?: string;
backLink?: BackLinkProp;
beforeTitle?: React.ReactNode;
afterTitle?: React.ReactNode;
contentAfter?: React.ReactNode;
isLoading?: boolean;
withTextAd?: boolean;
}
const PageTitle = ({ text, additionalsLeft, additionalsRight, withTextAd, backLink, className, isLoading, afterTitle }: Props) => {
const title = (
<Skeleton isLoaded={ !isLoading }>
<Heading
as="h1"
size="lg"
flex="none"
wordBreak="break-word"
>
{ text }
{ afterTitle }
</Heading>
</Skeleton>
);
const backLinkComponent = (() => {
if (!backLink) {
return null;
}
const BackLink = (props: BackLinkProp & { isLoading?: boolean }) => {
if (!props) {
return null;
}
const icon = <Icon as={ eastArrowIcon } boxSize={ 6 } transform="rotate(180deg)" margin="auto"/>;
if (props.isLoading) {
return <Skeleton boxSize={ 6 } display="inline-block" borderRadius="base" mr={ 3 } isLoaded={ !props.isLoading }/>;
}
if ('url' in backLink) {
return (
<Tooltip label={ backLink.label }>
<LinkInternal display="inline-flex" href={ backLink.url } h="40px">
{ icon }
</LinkInternal>
</Tooltip>
);
}
const icon = <Icon as={ eastArrowIcon } boxSize={ 6 } transform="rotate(180deg)" margin="auto"/>;
if ('url' in props) {
return (
<Tooltip label={ backLink.label }>
<Link display="inline-flex" onClick={ backLink.onClick } h="40px">
<Tooltip label={ props.label }>
<LinkInternal display="inline-flex" href={ props.url } h="40px" mr={ 3 }>
{ icon }
</Link>
</LinkInternal>
</Tooltip>
);
})();
}
return (
<Tooltip label={ props.label }>
<Link display="inline-flex" onClick={ props.onClick } h="40px" mr={ 3 }>
{ icon }
</Link>
</Tooltip>
);
};
const PageTitle = ({ title, contentAfter, withTextAd, backLink, className, isLoading, afterTitle, beforeTitle }: Props) => {
return (
<Flex
columnGap={ 3 }
rowGap={ 3 }
alignItems={{ base: 'start', lg: 'center' }}
flexDirection={{ base: 'column', lg: 'row' }}
mb={ 6 }
justifyContent="space-between"
className={ className }
mb={ 6 }
flexDir="row"
flexWrap="wrap"
rowGap={ 3 }
columnGap={ 3 }
alignItems="center"
>
<Flex flexWrap="wrap" columnGap={ 3 } alignItems="center" width={ withTextAd ? 'unset' : '100%' } flexShrink={ 0 }>
<Grid
templateColumns={ [ backLinkComponent && 'auto', additionalsLeft && 'auto', '1fr' ].filter(Boolean).join(' ') }
columnGap={ 3 }
<Box>
{ backLink && <BackLink { ...backLink } isLoading={ isLoading }/> }
{ beforeTitle }
<Skeleton
isLoaded={ !isLoading }
display="inline"
verticalAlign={ isLoading ? 'super' : undefined }
>
{ backLinkComponent }
{ additionalsLeft !== undefined && (
<Flex h="40px" alignItems="center">
{ additionalsLeft }
</Flex>
) }
{ title }
</Grid>
{ additionalsRight }
</Flex>
{ withTextAd && <TextAd flexShrink={ 100 }/> }
<Heading
as="h1"
size="lg"
display="inline"
wordBreak="break-word"
w="100%"
>
{ title }
</Heading>
</Skeleton>
{ afterTitle }
</Box>
{ contentAfter }
{ withTextAd && <TextAd order={{ base: -1, lg: 100 }} mb={{ base: 6, lg: 0 }} ml="auto"/> }
</Flex>
);
};
......
import { Icon } from '@chakra-ui/react';
import React from 'react';
import type { TokenInfo } from 'types/api/token';
import iconSuccess from 'icons/status/success.svg';
import Tag from 'ui/shared/chakra/Tag';
import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import TokenLogo from 'ui/shared/TokenLogo';
import PageTitle from '../PageTitle';
const DefaultView = () => {
const tokenData: TokenInfo = {
address: '0x363574E6C5C71c343d7348093D84320c76d5Dd29',
type: 'ERC-20',
symbol: 'SHAAAAAAAAAAAAA',
name: null,
decimals: '18',
holders: '1',
exchange_rate: null,
total_supply: null,
icon_url: 'https://example.com/logo.png',
};
const backLink = {
label: 'Back to tokens list',
url: 'https://localhost:3000/tokens',
};
const contentAfter = (
<EntityTags
tagsBefore={ [
{ label: 'example', display_name: 'Example label' },
] }
contentAfter={ (
<>
<Tag key="custom" colorScheme="orange" variant="solid">Awesome</Tag>
<NetworkExplorers type="token" pathParam="token-hash" ml="auto"/>
</>
) }
flexGrow={ 1 }
/>
);
return (
<PageTitle
title="Shavukha Token (SHVKH) token"
beforeTitle={ (
<TokenLogo data={ tokenData } boxSize={ 6 } display="inline-block" mr={ 2 }/>
) }
afterTitle={ <Icon as={ iconSuccess } color="green.500" boxSize={ 4 } verticalAlign="top"/> }
backLink={ backLink }
contentAfter={ contentAfter }
/>
);
};
export default DefaultView;
/* eslint-disable max-len */
import { Icon } from '@chakra-ui/react';
import React from 'react';
import type { TokenInfo } from 'types/api/token';
import iconSuccess from 'icons/status/success.svg';
import trimTokenSymbol from 'lib/token/trimTokenSymbol';
import { publicTag, privateTag, watchlistName } from 'mocks/address/tag';
import Tag from 'ui/shared/chakra/Tag';
import EntityTags from 'ui/shared/EntityTags';
import NetworkExplorers from 'ui/shared/NetworkExplorers';
import TokenLogo from 'ui/shared/TokenLogo';
import PageTitle from '../PageTitle';
const LongNameAndManyTags = () => {
const tokenData: TokenInfo = {
address: '0xa77A39CC9680B10C00af5D4ABFc92e1F07406c64',
decimals: null,
exchange_rate: null,
holders: '294',
icon_url: null,
name: 'Ring ding ding daa baa Baa aramba baa bom baa barooumba Wh-wha-what&#39;s going on-on? Ding, ding This is the Crazy Frog Ding, ding Bem, bem! Ring ding ding ding ding ding Ring ding ding ding bem bem bem Ring ding ding ding ding ding Ring ding ding ding baa b',
symbol: 'BatcoiRing ding ding daa baa Baa aramba baa bom baa barooumba Wh-wha-what&#39;s going on-on? Ding, ding This is the Crazy Frog Ding, ding Bem, bem! Ring ding ding ding ding ding Ring ding ding ding bem bem bem Ring ding ding ding ding ding Ring ding ding ding',
total_supply: '13747',
type: 'ERC-721',
};
const contentAfter = (
<EntityTags
data={{
private_tags: [ privateTag ],
public_tags: [ publicTag ],
watchlist_names: [ watchlistName ],
}}
tagsBefore={ [
{ label: 'example', display_name: 'Example with long name' },
] }
tagsAfter={ [
{ label: 'after_1', display_name: 'Another tag' },
{ label: 'after_2', display_name: 'And yet more' },
] }
contentAfter={ (
<>
<Tag key="custom" colorScheme="orange" variant="solid">Awesome</Tag>
<NetworkExplorers type="token" pathParam="token-hash" ml="auto"/>
</>
) }
flexGrow={ 1 }
/>
);
const tokenSymbolText = ` (${ trimTokenSymbol(tokenData.symbol) })`;
return (
<PageTitle
title={ `${ tokenData?.name }${ tokenSymbolText } token` }
beforeTitle={ (
<TokenLogo data={ tokenData } boxSize={ 6 } display="inline-block" mr={ 2 }/>
) }
afterTitle={ <Icon as={ iconSuccess } color="green.500" boxSize={ 4 } verticalAlign="top"/> }
contentAfter={ contentAfter }
/>
);
};
export default LongNameAndManyTags;
import React from 'react';
import Tag from 'ui/shared/chakra/Tag';
import PageTitle from '../PageTitle';
const WithTextAd = () => {
const backLink = {
label: 'Back to Home',
url: 'https://localhost:3000',
};
return (
<PageTitle
title="Block"
backLink={ backLink }
contentAfter={ <Tag key="custom" colorScheme="orange" variant="solid">Awesome</Tag> }
withTextAd
/>
);
};
export default WithTextAd;
......@@ -145,10 +145,10 @@ const TokenInstanceContent = () => {
<>
<TextAd mb={ 6 }/>
<PageTitle
text={ `${ tokenInstanceQuery.data.token.name || 'Unnamed token' } #${ tokenInstanceQuery.data.id }` }
title={ `${ tokenInstanceQuery.data.token.name || 'Unnamed token' } #${ tokenInstanceQuery.data.id }` }
backLink={ backLink }
additionalsLeft={ nftShieldIcon }
additionalsRight={ tokenTag }
beforeTitle={ nftShieldIcon }
contentAfter={ tokenTag }
/>
<AddressHeadingInfo address={ address } token={ tokenInstanceQuery.data.token }/>
......
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