Commit 21bb0fc5 authored by Igor Stuev's avatar Igor Stuev Committed by GitHub

Merge pull request #824 from blockscout/footer

footer
parents f2e4419e 578e2a5d
......@@ -6,6 +6,10 @@ NEXT_PUBLIC_APP_HOST=__PLACEHOLDER_FOR_NEXT_PUBLIC_APP_HOST__
NEXT_PUBLIC_APP_PORT=__PLACEHOLDER_FOR_NEXT_PUBLIC_APP_PORT__
NEXT_PUBLIC_AUTH_URL=__PLACEHOLDER_FOR_NEXT_PUBLIC_AUTH_URL__
# version config
NEXT_PUBLIC_GIT_COMMIT_SHA=__PLACEHOLDER_FOR_NEXT_PUBLIC_GIT_COMMIT_SHA__
NEXT_PUBLIC_GIT_TAG = __PLACEHOLDER_FOR_NEXT_PUBLIC_GIT_TAG__
# network config
NEXT_PUBLIC_NETWORK_NAME=__PLACEHOLDER_FOR_NEXT_PUBLIC_NETWORK_NAME__
NEXT_PUBLIC_NETWORK_SHORT_NAME=__PLACEHOLDER_FOR_NEXT_PUBLIC_NETWORK_SHORT_NAME__
......@@ -26,11 +30,8 @@ NEXT_PUBLIC_IS_TESTNET=__PLACEHOLDER_FOR_NEXT_PUBLIC_IS_TESTNET__
# ui config
NEXT_PUBLIC_BLOCKSCOUT_VERSION=__PLACEHOLDER_FOR_NEXT_PUBLIC_BLOCKSCOUT_VERSION__
NEXT_PUBLIC_FOOTER_GITHUB_LINK=__PLACEHOLDER_FOR_NEXT_PUBLIC_FOOTER_GITHUB_LINK__
NEXT_PUBLIC_FOOTER_TWITTER_LINK=__PLACEHOLDER_FOR_NEXT_PUBLIC_FOOTER_TWITTER_LINK__
NEXT_PUBLIC_FOOTER_TELEGRAM_LINK=__PLACEHOLDER_FOR_NEXT_PUBLIC_FOOTER_TELEGRAM_LINK__
NEXT_PUBLIC_FOOTER_STAKING_LINK=__PLACEHOLDER_FOR_NEXT_PUBLIC_FOOTER_STAKING_LINK__
NEXT_PUBLIC_FEATURED_NETWORKS=__PLACEHOLDER_FOR_NEXT_PUBLIC_FEATURED_NETWORKS__
NEXT_PUBLIC_FOOTER_LINKS=__PLACEHOLDER_FOR_NEXT_PUBLIC_FOOTER_LINKS__
NEXT_PUBLIC_NETWORK_EXPLORERS=__PLACEHOLDER_FOR_NEXT_PUBLIC_NETWORK_EXPLORERS__
NEXT_PUBLIC_OTHER_LINKS=__PLACEHOLDER_FOR_NEXT_PUBLIC_OTHER_LINKS__
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=__PLACEHOLDER_FOR_NEXT_PUBLIC_MARKETPLACE_CONFIG_URL__
......
......@@ -35,6 +35,8 @@ WORKDIR /app
# pass commit sha to the app (uses by sentry.io as release version)
ARG GIT_COMMIT_SHA
ENV NEXT_PUBLIC_GIT_COMMIT_SHA=$GIT_COMMIT_SHA
# pass git tag to the app (for the footer link)
ARG GIT_TAG
ENV NEXT_PUBLIC_GIT_TAG=$GIT_TAG
......
......@@ -96,15 +96,12 @@ const config = Object.freeze({
rpcUrl: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_RPC_URL),
isTestnet: getEnvValue(process.env.NEXT_PUBLIC_IS_TESTNET) === 'true',
},
footerLinks: {
github: getEnvValue(process.env.NEXT_PUBLIC_FOOTER_GITHUB_LINK),
twitter: getEnvValue(process.env.NEXT_PUBLIC_FOOTER_TWITTER_LINK),
telegram: getEnvValue(process.env.NEXT_PUBLIC_FOOTER_TELEGRAM_LINK),
staking: getEnvValue(process.env.NEXT_PUBLIC_FOOTER_STAKING_LINK),
},
otherLinks: parseEnvJson<Array<NavItemExternal>>(getEnvValue(process.env.NEXT_PUBLIC_OTHER_LINKS)) || [],
featuredNetworks: getEnvValue(process.env.NEXT_PUBLIC_FEATURED_NETWORKS),
footerLinks: getEnvValue(process.env.NEXT_PUBLIC_FOOTER_LINKS),
blockScoutVersion: getEnvValue(process.env.NEXT_PUBLIC_BLOCKSCOUT_VERSION),
frontendVersion: getEnvValue(process.env.NEXT_PUBLIC_GIT_TAG),
frontendCommit: getEnvValue(process.env.NEXT_PUBLIC_GIT_COMMIT_SHA),
isAccountSupported: getEnvValue(process.env.NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED) === 'true',
marketplaceConfigUrl: getEnvValue(process.env.NEXT_PUBLIC_MARKETPLACE_CONFIG_URL),
marketplaceSubmitForm: getEnvValue(process.env.NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM),
......
......@@ -7,8 +7,6 @@ NEXT_PUBLIC_APP_ENV=development
# ui config
NEXT_PUBLIC_BLOCKSCOUT_VERSION=v4.1.7-beta
NEXT_PUBLIC_FOOTER_GITHUB_LINK=https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
# api config
......
......@@ -5,8 +5,6 @@ NEXT_PUBLIC_APP_ENV=testing
# ui config
NEXT_PUBLIC_BLOCKSCOUT_VERSION=v4.1.7-beta
NEXT_PUBLIC_FOOTER_GITHUB_LINK=https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_IS_TESTNET=true
# api config
......
......@@ -7,10 +7,6 @@ NEXT_PUBLIC_APP_ENV=development
# ui config
NEXT_PUBLIC_BLOCKSCOUT_VERSION=v4.1.7-beta
NEXT_PUBLIC_FOOTER_TELEGRAM_LINK=https://t.me/poa_network
NEXT_PUBLIC_FOOTER_STAKING_LINK=https://duneanalytics.com/maxaleks/xdai-staking
NEXT_PUBLIC_FOOTER_GITHUB_LINK=https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/transaction','address':'/ethereum/poa/core/address'}}]
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
......
# ui config
NEXT_PUBLIC_FOOTER_TELEGRAM_LINK=https://t.me/poa_network
NEXT_PUBLIC_FOOTER_STAKING_LINK=https://duneanalytics.com/maxaleks/xdai-staking
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/transaction','address':'/ethereum/poa/core/address','block':'/ethereum/poa/core/block'}}]
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
......
......@@ -7,16 +7,14 @@ NEXT_PUBLIC_APP_ENV=testing
# ui config
NEXT_PUBLIC_BLOCKSCOUT_VERSION=v4.1.7-beta
NEXT_PUBLIC_FOOTER_GITHUB_LINK=https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_FOOTER_TELEGRAM_LINK=https://t.me/poa_network
NEXT_PUBLIC_FOOTER_STAKING_LINK=https://duneanalytics.com/maxaleks/xdai-staking
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
NEXT_PUBLIC_GIT_TAG=v1.0.11
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true
NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=
NEXT_PUBLIC_FEATURED_NETWORKS=
NEXT_PUBLIC_FOOTER_LINKS=
NEXT_PUBLIC_NETWORK_LOGO=
NEXT_PUBLIC_NETWORK_LOGO_DARK=
NEXT_PUBLIC_NETWORK_ICON=
......
......@@ -352,10 +352,6 @@ frontend:
environment:
NEXT_PUBLIC_BLOCKSCOUT_VERSION:
_default: v5.1.5-beta
NEXT_PUBLIC_FOOTER_GITHUB_LINK:
_default: https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK:
_default: https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_APP_ENV:
_default: stable
NEXT_PUBLIC_APP_INSTANCE:
......
......@@ -275,10 +275,6 @@ frontend:
_default: /
NEXT_PUBLIC_BLOCKSCOUT_VERSION:
_default: v5.1.2-beta
NEXT_PUBLIC_FOOTER_GITHUB_LINK:
_default: https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK:
_default: https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_APP_ENV:
_default: stable
NEXT_PUBLIC_APP_INSTANCE:
......
......@@ -61,10 +61,6 @@ frontend:
environment:
NEXT_PUBLIC_BLOCKSCOUT_VERSION:
_default: v5.1.0-beta
NEXT_PUBLIC_FOOTER_GITHUB_LINK:
_default: https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK:
_default: https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_APP_ENV:
_default: preview
NEXT_PUBLIC_APP_INSTANCE:
......
......@@ -57,18 +57,10 @@ frontend:
environment:
NEXT_PUBLIC_BLOCKSCOUT_VERSION:
_default: v5.1.2-beta
NEXT_PUBLIC_FOOTER_GITHUB_LINK:
_default: https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK:
_default: https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_APP_ENV:
_default: preview
NEXT_PUBLIC_APP_INSTANCE:
_default: eth_goerli
NEXT_PUBLIC_FOOTER_TELEGRAM_LINK:
_default: https://t.me/poa_network
NEXT_PUBLIC_FOOTER_STAKING_LINK:
_default: https://duneanalytics.com/maxaleks/xdai-staking
NEXT_PUBLIC_NETWORK_NAME:
_default: Göerli
NEXT_PUBLIC_NETWORK_SHORT_NAME:
......
......@@ -30,11 +30,8 @@ The app instance could be customized by passing following variables to NodeJS en
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_FEATURED_NETWORKS | `string` | URL of configuration file (`.json` format only) which contains list of featured networks that will be shown in the network menu. See [below](#featured-network-configuration-properties) list of available properties for particular network | - | - | `https://example.com/featured_networks_config.json` |
| NEXT_PUBLIC_OTHER_LINKS | `Array<{url: string; text: string}>` | List of links for the "Other" navigation menu | - | - | `[{'url':'https://blockscout.com','text':'Blockscout'}]` |
| NEXT_PUBLIC_FOOTER_LINKS | `string` | URL of configuration file (`.json` format only) which contains list of link groups to be displayed in the footer. See [below](#footer-links-configuration-properties) list of available properties for particular group | - | - | `https://example.com/footer_links_config.json` |
| NEXT_PUBLIC_BLOCKSCOUT_VERSION | `string` | Current running version of Blockscout (used to display link to release in the footer) | - | - | `v.5.1.0-beta`
| NEXT_PUBLIC_FOOTER_GITHUB_LINK | `string` | Link to Github in the footer | - | - | `https://github.com/blockscout/blockscout` |
| NEXT_PUBLIC_FOOTER_TWITTER_LINK | `string` | Link to Twitter in the footer | - | - | `https://www.twitter.com/blockscoutcom` |
| NEXT_PUBLIC_FOOTER_TELEGRAM_LINK | `string` | Link to Telegram in the footer | - | - | `https://t.me/poa_network` |
| NEXT_PUBLIC_FOOTER_STAKING_LINK | `string` | Link to staking dashboard in the footer | - | - | `https://duneanalytics.com/maxaleks/xdai-staking` |
| NEXT_PUBLIC_MARKETPLACE_CONFIG_URL | `string` | URL of configuration file (`.json` format only) which contains list of apps that will be shown on the marketplace page. See [below](#marketplace-app-configuration-properties) list of available properties for an app | - | - | `https://example.com/marketplace_config.json` |
| NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM | `string` | Link to form where authors can submit their dapps to the marketplace | - | - | `https://airtable.com/shrqUAcjgGJ4jU88C` |
| NEXT_PUBLIC_NETWORK_EXPLORERS | `Array<NetworkExplorer>` where `NetworkExplorer` can have following [properties](#network-explorer-configuration-properties) | Used to build up links to transactions, blocks, addresses in other chain explorers. | - | - | `[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/poa/core/tx'}}]` |
......@@ -108,6 +105,13 @@ For each application, you need to specify the `MarketplaceCategoryId` to which i
*Note* The url of an entity will be constructed as `<baseUrl><paths[<entity-type>]><entity-id>`, e.g `https://explorer.anyblock.tools/ethereum/poa/core/tx/<tx-id>`
## Footer links configuration properties
| Variable | Type| Description | Is required | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| title | `string` | Title of link group | yes | - | `Company` |
| links | `Array<{'text':string;'url':string;}>` | list of links | yes | - | `[{'text':'Homepage','url':'https://www.blockscout.com'}]` |
## App configuration
......
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<path fill="currentColor" d="M13.76 7.444h2.88a5.76 5.76 0 0 1 0 11.52v2.52c-3.6-1.44-8.64-3.6-8.64-8.28a5.76 5.76 0 0 1 5.76-5.76Zm1.44 10.08h1.44a4.32 4.32 0 1 0 0-8.64h-2.88a4.32 4.32 0 0 0-4.32 4.32c0 2.6 1.772 4.296 5.76 6.106v-1.786Z"/>
<path fill="currentColor" d="M10.4 3.444H7.2a6.32 6.32 0 0 0-4.526 1.923A6.65 6.65 0 0 0 .8 10.008c0 1.741.674 3.41 1.874 4.642A6.32 6.32 0 0 0 7.2 16.572v2.872c4-1.64 9.6-4.102 9.6-9.436a6.65 6.65 0 0 0-1.875-4.641A6.32 6.32 0 0 0 10.4 3.444ZM8.8 14.931H7.2c-.63 0-1.255-.127-1.837-.374a4.793 4.793 0 0 1-1.557-1.068c-.446-.457-.8-1-1.04-1.597a5.033 5.033 0 0 1 0-3.768 4.933 4.933 0 0 1 1.04-1.597c.445-.457.975-.82 1.557-1.067A4.696 4.696 0 0 1 7.2 5.085h3.2a4.74 4.74 0 0 1 3.394 1.442 4.988 4.988 0 0 1 1.406 3.481c0 2.962-1.97 4.895-6.4 6.958v-2.035Z"/>
</svg>
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30">
<path d="M15 25c5.523 0 10-4.477 10-10S20.523 5 15 5 5 9.477 5 15s4.477 10 10 10Z" stroke="currentColor" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M21.667 22.333a6.666 6.666 0 1 0-13.334 0" stroke="currentColor" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M15 15.667a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z" stroke="currentColor" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
</svg>
This diff is collapsed.
import React from 'react';
import appConfig from 'configs/app/config';
import isBrowser from 'lib/isBrowser';
const base = 'https://github.com/blockscout/blockscout/issues/new/';
const bodyTemplate = `*Describe your issue here.*
### Environment
* Backend Version/branch/commit: ${ appConfig.blockScoutVersion }
* Frontend Version+commit: ${ [ appConfig.frontendVersion, appConfig.frontendCommit ].filter(Boolean).join('+') }
* User Agent: __userAgent__
### Steps to reproduce
*Tell us how to reproduce this issue. ❤️ if you can push up a branch to your fork with a regression test we can run to reproduce locally.*
### Expected behaviour
*Tell us what should happen.*
### Actual behaviour
*Tell us what happens instead.*`;
const labels = 'new UI';
const title = `${ appConfig.network.name }: <Issue Title>`;
export default function useIssueUrl() {
const [ userAgent, setUserAgent ] = React.useState('');
const isInBrowser = isBrowser();
React.useEffect(() => {
if (isInBrowser) {
setUserAgent(window.navigator.userAgent);
}
}, [ isInBrowser ]);
const params = new URLSearchParams({ labels, title, body: bodyTemplate.replace('__userAgent__', userAgent) });
return base + '?' + params.toString();
}
......@@ -15,7 +15,6 @@ import globeIcon from 'icons/globe-b.svg';
import graphQLIcon from 'icons/graphQL.svg';
import outputRootsIcon from 'icons/output_roots.svg';
import privateTagIcon from 'icons/privattags.svg';
import profileIcon from 'icons/profile.svg';
import publicTagIcon from 'icons/publictags.svg';
import apiDocsIcon from 'icons/restAPI.svg';
import rpcIcon from 'icons/RPC.svg';
......@@ -27,6 +26,7 @@ import txnBatchIcon from 'icons/txn_batches.svg';
import verifiedIcon from 'icons/verified.svg';
import watchlistIcon from 'icons/watchlist.svg';
import { rightLineArrow } from 'lib/html-entities';
import UserAvatar from 'ui/shared/UserAvatar';
interface ReturnType {
mainNavItems: Array<NavItem | NavGroupItem>;
......@@ -213,7 +213,7 @@ export default function useNavItems(): ReturnType {
const profileItem = {
text: 'My profile',
nextRoute: { pathname: '/auth/profile' as const },
icon: profileIcon,
iconComponent: UserAvatar,
isActive: pathname === '/auth/profile',
};
......
......@@ -5,13 +5,11 @@ import metamaskIcon from 'icons/wallets/metamask.svg';
export const WALLETS_INFO: Record<WalletType, WalletInfo> = {
metamask: {
add_token_text: 'Add token to MetaMask',
add_network_text: 'Add network to MetaMask',
name: 'MetaMask',
icon: metamaskIcon,
},
coinbase: {
add_token_text: 'Add token to Coinbase Wallet',
add_network_text: 'Add network to Coinbase Wallet',
name: 'Coinbase Wallet',
icon: coinbaseIcon,
},
};
import type { CustomLinksGroup } from 'types/footerLinks';
export const FOOTER_LINKS: Array<CustomLinksGroup> = [
{
title: 'Company',
links: [
{
text: 'Advertise',
url: 'https://coinzilla.com/',
},
{
text: 'Staking',
url: '',
},
{
text: 'Contact us',
url: '',
},
{
text: 'Brand assets',
url: '',
},
{
text: 'Term of service',
url: '',
},
],
},
{
title: 'Community',
links: [
{
text: 'API docs',
url: '',
},
{
text: 'Knowledge base',
url: '',
},
{
text: 'Network status',
url: '',
},
{
text: 'Learn Alphabet',
url: '',
},
],
},
{
title: 'Product',
links: [
{
text: 'Stake Alphabet',
url: '',
},
{
text: 'Build token',
url: '',
},
{
text: 'Build DAPPS',
url: '',
},
{
text: 'NFT marketplace',
url: '',
},
{
text: 'Become validator',
url: '',
},
],
},
];
......@@ -18,7 +18,7 @@ const variantPrimary = defineStyle((props) => {
const variantSecondary = defineStyle((props) => {
return {
color: mode('gray.500', 'gray.500')(props),
color: mode('gray.600', 'gray.500')(props),
_hover: {
color: mode('gray.600', 'gray.400')(props),
},
......
import type { Route } from 'nextjs-routes';
import type React from 'react';
type NavIconOrComponent = {
icon?: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
} | {
iconComponent?: React.FC<{size?: number}>;
};
type NavItemCommon = {
text: string;
icon?: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
}
} & NavIconOrComponent;
export type NavItemInternal = NavItemCommon & {
nextRoute: Route;
......
......@@ -3,8 +3,7 @@ import type { providers } from 'ethers';
export type WalletType = 'metamask' | 'coinbase';
export interface WalletInfo {
add_token_text: string;
add_network_text: string;
name: string;
icon: React.ElementType;
}
......
type CustomLink = {
text: string;
url: string;
}
export type CustomLinksGroup = {
title: string;
links: Array<CustomLink>;
}
......@@ -135,7 +135,6 @@ const AddressPageContent = () => {
{ /* should stay before tabs to scroll up with pagination */ }
<Box ref={ tabsScrollRef }></Box>
{ addressQuery.isPlaceholderData ? <SkeletonTabs tabs={ tabs }/> : content }
{ !addressQuery.isPlaceholderData && !addressQuery.isError && <Box h={{ base: 0, lg: '40vh' }}/> }
</>
);
};
......
......@@ -8,7 +8,6 @@ import Stats from 'ui/home/Stats';
import Transactions from 'ui/home/Transactions';
import AdBanner from 'ui/shared/ad/AdBanner';
import Page from 'ui/shared/Page/Page';
import ColorModeToggler from 'ui/snippets/header/ColorModeToggler';
import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop';
import SearchBar from 'ui/snippets/searchBar/SearchBar';
......@@ -33,15 +32,9 @@ const Home = () => {
>
Welcome to { appConfig.network.name } explorer
</Heading>
<Flex
alignItems="center"
display={{ base: 'none', lg: 'flex' }}
columnGap={ 12 }
pl={ 4 }
>
<ColorModeToggler trackBg="blackAlpha.900"/>
<Box display={{ base: 'none', lg: 'block' }}>
{ appConfig.isAccountSupported && <ProfileMenuDesktop/> }
</Flex>
</Box>
</Flex>
<LightMode>
<SearchBar isHomepage/>
......
......@@ -9,7 +9,7 @@ import PageTitle from 'ui/shared/Page/PageTitle';
import UserAvatar from 'ui/shared/UserAvatar';
const MyProfile = () => {
const { data, isLoading, isError, error, isFetched } = useFetchProfileInfo();
const { data, isLoading, isError, error } = useFetchProfileInfo();
useRedirectForInvalidAuthToken();
const content = (() => {
......@@ -26,7 +26,7 @@ const MyProfile = () => {
return (
<VStack maxW="412px" mt={ 8 } gap={ 5 } alignItems="stretch">
<UserAvatar size={ 64 } data={ data } isFetched={ isFetched }/>
<UserAvatar size={ 64 }/>
<FormControl variant="floating" id="name" isRequired size="lg">
<Input
required
......
......@@ -276,8 +276,6 @@ const TokenPageContent = () => {
stickyEnabled={ !isMobile }
/>
) }
{ !tokenQuery.isLoading && !tokenQuery.isError && <Box h={{ base: 0, lg: '40vh' }}/> }
</>
);
};
......
......@@ -183,8 +183,6 @@ const TokenInstanceContent = () => {
stickyEnabled={ !isMobile }
/>
) }
{ !tokenInstanceQuery.isLoading && !tokenInstanceQuery.isError && <Box h={{ base: 0, lg: '40vh' }}/> }
</>
);
};
......
import { Box, Icon, Tooltip, chakra } from '@chakra-ui/react';
import { Button, Icon, chakra } from '@chakra-ui/react';
import React from 'react';
import appConfig from 'configs/app/config';
......@@ -59,11 +59,10 @@ const NetworkAddToWallet = ({ className }: Props) => {
const defaultWallet = appConfig.web3.defaultWallet;
return (
<Tooltip label={ WALLETS_INFO[defaultWallet].add_network_text }>
<Box className={ className } display="inline-flex" cursor="pointer" onClick={ handleClick }>
<Icon as={ WALLETS_INFO[defaultWallet].icon } boxSize={ 5 }/>
</Box>
</Tooltip>
<Button variant="outline" size="sm" onClick={ handleClick } className={ className }>
<Icon as={ WALLETS_INFO[defaultWallet].icon } boxSize={ 5 } mr={ 2 }/>
Add { appConfig.network.name }
</Button>
);
};
......
import { Flex } from '@chakra-ui/react';
import { Box, Flex } from '@chakra-ui/react';
import React from 'react';
import getErrorCauseStatusCode from 'lib/errors/getErrorCauseStatusCode';
......@@ -12,6 +12,7 @@ import AppErrorInvalidTxHash from 'ui/shared/AppError/AppErrorInvalidTxHash';
import AppErrorUnverifiedEmail from 'ui/shared/AppError/AppErrorUnverifiedEmail';
import ErrorBoundary from 'ui/shared/ErrorBoundary';
import PageContent from 'ui/shared/Page/PageContent';
import Footer from 'ui/snippets/footer/Footer';
import Header from 'ui/snippets/header/Header';
import NavigationDesktop from 'ui/snippets/navigation/NavigationDesktop';
......@@ -73,6 +74,7 @@ const Page = ({
) : children;
return (
<Box minWidth={{ base: '100vw', lg: 'fit-content' }}>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Flex flexDir="column" flexGrow={ 1 } w={{ base: '100%', lg: 'auto' }}>
......@@ -85,6 +87,8 @@ const Page = ({
</ErrorBoundary>
</Flex>
</Flex>
<Footer/>
</Box>
);
};
......
......@@ -2,10 +2,9 @@ import { useColorModeValue, useToken, SkeletonCircle, Image, Box } from '@chakra
import React from 'react';
import Identicon from 'react-identicons';
import type { UserInfo } from 'types/api/account';
import { useAppContext } from 'lib/appContext';
import * as cookies from 'lib/cookies';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
const IdenticonComponent = typeof Identicon === 'object' && 'default' in Identicon ? Identicon.default : Identicon;
......@@ -34,14 +33,13 @@ const FallbackImage = ({ size, id }: { size: number; id: string }) => {
interface Props {
size: number;
data?: UserInfo;
isFetched: boolean;
}
const UserAvatar = ({ size, data, isFetched }: Props) => {
const UserAvatar = ({ size }: Props) => {
const appProps = useAppContext();
const hasAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN, appProps.cookies));
const [ isImageLoadError, setImageLoadError ] = React.useState(false);
const { data, isFetched } = useFetchProfileInfo();
const sizeString = `${ size }px`;
......
......@@ -67,7 +67,7 @@ const AddressAddToWallet = ({ className, token, isLoading }: Props) => {
const defaultWallet = appConfig.web3.defaultWallet;
return (
<Tooltip label={ WALLETS_INFO[defaultWallet].add_token_text }>
<Tooltip label={ `Add token to ${ WALLETS_INFO[defaultWallet].name }` }>
<Box className={ className } display="inline-flex" cursor="pointer" onClick={ handleClick }>
<Icon as={ WALLETS_INFO[defaultWallet].icon } boxSize={ 6 }/>
</Box>
......
import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import { FOOTER_LINKS } from 'mocks/config/footerLinks';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import Footer from './Footer';
const FOOTER_LINKS_URL = 'https://localhost:3000/footer-links.json';
base.describe('with custom links, 4 cols', () => {
const test = base.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_FOOTER_LINKS', value: FOOTER_LINKS_URL },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
});
test('base view +@dark-mode +@mobile +@desktop-xl', async({ mount, page }) => {
await page.route(FOOTER_LINKS_URL, (route) => {
return route.fulfill({
body: JSON.stringify(FOOTER_LINKS),
});
});
await mount(
<TestApp>
<Footer/>
</TestApp>,
);
await expect(page).toHaveScreenshot();
});
});
base.describe('with custom links, 2 cols', () => {
const test = base.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_FOOTER_LINKS', value: FOOTER_LINKS_URL },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
});
test('base view +@dark-mode +@mobile', async({ mount, page }) => {
await page.route(FOOTER_LINKS_URL, (route) => {
return route.fulfill({
body: JSON.stringify([ FOOTER_LINKS[0] ]),
});
});
await mount(
<TestApp>
<Footer/>
</TestApp>,
);
await expect(page).toHaveScreenshot();
});
});
base.describe('without custom links', () => {
base('base view +@dark-mode +@mobile', async({ mount, page }) => {
await page.evaluate(() => {
window.ethereum = {
providers: [ { isMetaMask: true } ],
};
});
await mount(
<TestApp>
<Footer/>
</TestApp>,
);
await expect(page).toHaveScreenshot();
});
});
import { Box, Grid, Flex, Text, Link, VStack, Skeleton } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import React from 'react';
import type { CustomLinksGroup } from 'types/footerLinks';
import appConfig from 'configs/app/config';
import discussionsIcon from 'icons/discussions.svg';
import editIcon from 'icons/edit.svg';
import discordIcon from 'icons/social/discord.svg';
import gitIcon from 'icons/social/git.svg';
import twitterIcon from 'icons/social/tweet.svg';
import type { ResourceError } from 'lib/api/resources';
import useFetch from 'lib/hooks/useFetch';
import useIssueUrl from 'lib/hooks/useIssueUrl';
import NetworkAddToWallet from 'ui/shared/NetworkAddToWallet';
import ColorModeToggler from '../header/ColorModeToggler';
import FooterLinkItem from './FooterLinkItem';
const MAX_LINKS_COLUMNS = 3;
const API_VERSION_URL = `https://github.com/blockscout/blockscout/tree/${ appConfig.blockScoutVersion }`;
// const FRONT_VERSION_URL = `https://github.com/blockscout/frontend/tree/${ appConfig.frontendVersion }`;
const Footer = () => {
const issueUrl = useIssueUrl();
const BLOCSKOUT_LINKS = [
{
icon: editIcon,
iconSize: '16px',
text: 'Submit an issue',
url: issueUrl,
},
{
icon: gitIcon,
iconSize: '18px',
text: 'Contribute',
url: 'https://github.com/blockscout/blockscout',
},
{
icon: twitterIcon,
iconSize: '18px',
text: 'Twitter',
url: 'https://www.twitter.com/blockscoutcom',
},
{
icon: discordIcon,
iconSize: '18px',
text: 'Discord',
url: 'https://discord.gg/blockscout',
},
{
icon: discussionsIcon,
iconSize: '20px',
text: 'Discussions',
url: 'https://github.com/orgs/blockscout/discussions',
},
];
const fetch = useFetch();
const { isLoading, data: linksData } = useQuery<unknown, ResourceError<unknown>, Array<CustomLinksGroup>>(
[ 'footer-links' ],
async() => fetch(appConfig.footerLinks || ''),
{
enabled: Boolean(appConfig.footerLinks),
staleTime: Infinity,
});
return (
<Flex direction={{ base: 'column', lg: 'row' }} p={{ base: 4, lg: 9 }} borderTop="1px solid" borderColor="divider">
<Box flexGrow="1" mb={{ base: 8, lg: 0 }}>
<Flex>
<ColorModeToggler/>
<NetworkAddToWallet ml={ 8 }/>
</Flex>
<Box mt={{ base: 5, lg: '44px' }}>
<Link fontSize="xs" href="https://www.blockscout.com">blockscout.com</Link>
</Box>
<Text mt={ 3 } mr={{ base: 0, lg: '114px' }} maxW={{ base: 'unset', lg: '470px' }} fontSize="xs">
Blockscout is a tool for inspecting and analyzing EVM based blockchains. Blockchain explorer for Ethereum Networks.
</Text>
<VStack spacing={ 1 } mt={ 6 } alignItems="start">
{ appConfig.blockScoutVersion && (
<Text fontSize="xs">
Backend: <Link href={ API_VERSION_URL } target="_blank">{ appConfig.blockScoutVersion }</Link>
</Text>
) }
{ (appConfig.frontendVersion || appConfig.frontendCommit) && (
<Text fontSize="xs">
{ /* Frontend: <Link href={ FRONT_VERSION_URL } target="_blank">{ appConfig.frontendVersion }</Link> */ }
Frontend: { [ appConfig.frontendVersion, appConfig.frontendCommit ].filter(Boolean).join('+') }
</Text>
) }
</VStack>
</Box>
<Grid
gap={{ base: 6, lg: 12 }}
gridTemplateColumns={ appConfig.footerLinks ?
{ base: 'repeat(auto-fill, 160px)', lg: `repeat(${ (linksData?.length || MAX_LINKS_COLUMNS) + 1 }, 160px)` } :
'auto'
}
>
<Box minW="160px" w={ appConfig.footerLinks ? '160px' : '100%' }>
{ appConfig.footerLinks && <Text fontWeight={ 500 } mb={ 3 }>Blockscout</Text> }
<Grid
gap={ 1 }
gridTemplateColumns={ appConfig.footerLinks ? '160px' : { base: 'repeat(auto-fill, 160px)', lg: 'repeat(3, 160px)' } }
gridTemplateRows={{ base: 'auto', lg: appConfig.footerLinks ? 'auto' : 'repeat(2, auto)' }}
gridAutoFlow={{ base: 'row', lg: appConfig.footerLinks ? 'row' : 'column' }}
mt={{ base: 0, lg: appConfig.footerLinks ? 0 : '100px' }}
>
{ BLOCSKOUT_LINKS.map(link => <FooterLinkItem { ...link } key={ link.text }/>) }
</Grid>
</Box>
{ appConfig.footerLinks && isLoading && (
Array.from(Array(3)).map((i, index) => (
<Box minW="160px" key={ index }>
<Skeleton w="120px" h="20px" mb={ 6 }/>
<VStack spacing={ 5 } alignItems="start" mb={ 2 }>
{ Array.from(Array(5)).map((i, index) => <Skeleton w="160px" h="14px" key={ index }/>) }
</VStack>
</Box>
))
) }
{ appConfig.footerLinks && linksData && (
linksData.slice(0, MAX_LINKS_COLUMNS).map(linkGroup => (
<Box minW="160px" key={ linkGroup.title }>
<Text fontWeight={ 500 } mb={ 3 }>{ linkGroup.title }</Text>
<VStack spacing={ 1 } alignItems="start">
{ linkGroup.links.map(link => <FooterLinkItem { ...link } key={ link.text }/>) }
</VStack>
</Box>
))
) }
</Grid>
</Flex>
);
};
export default Footer;
import { Center, Icon, Link } from '@chakra-ui/react';
import React from 'react';
type Props = {
icon?: React.FC<React.SVGAttributes<SVGElement>>;
iconSize?: string;
text: string;
url: string;
}
const FooterLinkItem = ({ icon, iconSize, text, url }: Props) => {
return (
<Link href={ url } display="flex" alignItems="center" h="30px" variant="secondary" target="_blank" fontSize="xs">
{ icon && (
<Center minW={ 6 } mr="6px">
<Icon boxSize={ iconSize || 5 } as={ icon }/>
</Center>
) }
{ text }
</Link>
);
};
export default FooterLinkItem;
......@@ -110,7 +110,7 @@ test.describe('auth', () => {
},
});
extendedTest.use({ viewport: { width: devices['iPhone 13 Pro'].viewport.width, height: 1000 } });
extendedTest.use({ viewport: { width: devices['iPhone 13 Pro'].viewport.width, height: 800 } });
extendedTest('base view', async({ mount, page }) => {
const component = await mount(
......
......@@ -10,7 +10,6 @@ import ProfileMenuMobile from 'ui/snippets/profileMenu/ProfileMenuMobile';
import SearchBar from 'ui/snippets/searchBar/SearchBar';
import Burger from './Burger';
import ColorModeToggler from './ColorModeToggler';
type Props = {
isHomePage?: boolean;
......@@ -66,7 +65,6 @@ const Header = ({ isHomePage, renderSearchBar }: Props) => {
<Box width="100%">
{ searchBar }
</Box>
<ColorModeToggler/>
{ appConfig.isAccountSupported && <ProfileMenuDesktop/> }
</HStack>
) }
......
import { Box, Text, Stack, Icon, Link, VStack } from '@chakra-ui/react';
import React from 'react';
import appConfig from 'configs/app/config';
import ghIcon from 'icons/social/git.svg';
import statsIcon from 'icons/social/stats.svg';
import tgIcon from 'icons/social/telega.svg';
import twIcon from 'icons/social/tweet.svg';
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import NetworkAddToWallet from 'ui/shared/NetworkAddToWallet';
const SOCIAL_LINKS = [
{ link: appConfig.footerLinks.github, icon: ghIcon, label: 'Github link' },
{ link: appConfig.footerLinks.twitter, icon: twIcon, label: 'Twitter link' },
{ link: appConfig.footerLinks.telegram, icon: tgIcon, label: 'Telegram link' },
{ link: appConfig.footerLinks.staking, icon: statsIcon, label: 'Staking analytic link' },
].filter(({ link }) => link);
const VERSION_URL = `https://github.com/blockscout/blockscout/tree/${ appConfig.blockScoutVersion }`;
interface Props {
isCollapsed?: boolean;
hasAccount?: boolean;
}
const NavFooter = ({ isCollapsed, hasAccount }: Props) => {
const isExpanded = isCollapsed === false;
const marginTop = (() => {
if (!hasAccount) {
return 'auto';
}
return { base: 6, lg: 20 };
})();
return (
<VStack
as="footer"
borderTop="1px solid"
borderColor="divider"
width={{ base: '100%', lg: isExpanded ? '180px' : '20px', xl: isCollapsed ? '20px' : '180px' }}
marginTop={ marginTop }
alignItems="flex-start"
alignSelf="center"
color="gray.500"
fontSize="xs"
{ ...getDefaultTransitionProps({ transitionProperty: 'width' }) }
>
<Stack
direction={{ base: 'row', lg: isExpanded ? 'row' : 'column', xl: isCollapsed ? 'column' : 'row' }}
mt={{ base: 6, lg: 8 }}
_empty={{
display: 'none',
}}
>
<NetworkAddToWallet/>
{ SOCIAL_LINKS.map(sl => {
return (
<Link href={ sl.link } key={ sl.link } variant="secondary" w={ 5 } h={ 5 } aria-label={ sl.label } target="_blank">
<Icon as={ sl.icon } boxSize={ 5 }/>
</Link>
);
}) }
</Stack>
<Box display={{ base: 'block', lg: isExpanded ? 'block' : 'none', xl: isCollapsed ? 'none' : 'block' }}>
<Text variant="secondary" mt={ 8 }>
Blockscout is a tool for inspecting and analyzing EVM based blockchains. Blockchain explorer for Ethereum Networks.
</Text>
{ appConfig.blockScoutVersion &&
<Text variant="secondary" mt={ 8 }>Version: <Link href={ VERSION_URL } target="_blank">{ appConfig.blockScoutVersion }</Link></Text> }
</Box>
</VStack>
);
};
export default NavFooter;
import { Link, Icon, Text, HStack, Tooltip, Box, useBreakpointValue, chakra, shouldForwardProp } from '@chakra-ui/react';
import { Link, Text, HStack, Tooltip, Box, useBreakpointValue, chakra, shouldForwardProp } from '@chakra-ui/react';
import NextLink from 'next/link';
import { route } from 'nextjs-routes';
import React from 'react';
......@@ -8,6 +8,7 @@ import type { NavItem } from 'types/client/navigation-items';
import useIsMobile from 'lib/hooks/useIsMobile';
import { isInternalItem } from 'lib/hooks/useNavItems';
import NavLinkIcon from './NavLinkIcon';
import useColors from './useColors';
import useNavLinkStyleProps from './useNavLinkStyleProps';
......@@ -50,7 +51,7 @@ const NavLink = ({ item, isCollapsed, px, className }: Props) => {
color={ isInternalLink && item.isActive ? colors.text.active : colors.text.hover }
>
<HStack spacing={ 3 } overflow="hidden">
{ item.icon && <Icon as={ item.icon } boxSize="30px"/> }
<NavLinkIcon item={ item }/>
<Text { ...styleProps.textProps }>
{ item.text }
</Text>
......
......@@ -17,16 +17,18 @@ import type { NavGroupItem } from 'types/client/navigation-items';
import chevronIcon from 'icons/arrows/east-mini.svg';
import NavLink from './NavLink';
import NavLinkIcon from './NavLinkIcon';
import useNavLinkStyleProps from './useNavLinkStyleProps';
type Props = NavGroupItem & {
type Props = {
item: NavGroupItem;
isCollapsed?: boolean;
}
const NavLinkGroupDesktop = ({ text, subItems, icon, isCollapsed, isActive }: Props) => {
const NavLinkGroupDesktop = ({ item, isCollapsed }: Props) => {
const isExpanded = isCollapsed === false;
const styleProps = useNavLinkStyleProps({ isCollapsed, isExpanded, isActive });
const styleProps = useNavLinkStyleProps({ isCollapsed, isExpanded, isActive: item.isActive });
return (
<Box as="li" listStyleType="none" w="100%">
......@@ -41,15 +43,15 @@ const NavLinkGroupDesktop = ({ text, subItems, icon, isCollapsed, isActive }: Pr
w={{ lg: isExpanded ? '180px' : '60px', xl: isCollapsed ? '60px' : '180px' }}
pl={{ lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 }}
pr={{ lg: isExpanded ? 0 : '15px', xl: isCollapsed ? '15px' : 0 }}
aria-label={ `${ text } link group` }
aria-label={ `${ item.text } link group` }
position="relative"
>
<HStack spacing={ 3 } overflow="hidden">
<Icon as={ icon } boxSize="30px"/>
<NavLinkIcon item={ item }/>
<Text
{ ...styleProps.textProps }
>
{ text }
{ item.text }
</Text>
<Icon
as={ chevronIcon }
......@@ -68,10 +70,10 @@ const NavLinkGroupDesktop = ({ text, subItems, icon, isCollapsed, isActive }: Pr
<PopoverContent width="252px" top={{ lg: isExpanded ? '-16px' : 0, xl: isCollapsed ? 0 : '-16px' }}>
<PopoverBody p={ 4 }>
<Text variant="secondary" fontSize="sm" mb={ 2 } display={{ lg: isExpanded ? 'none' : 'block', xl: isCollapsed ? 'block' : 'none' }}>
{ text }
{ item.text }
</Text>
<VStack spacing={ 1 } alignItems="start">
{ subItems.map((item, index) => Array.isArray(item) ? (
{ item.subItems.map((subItem, index) => Array.isArray(subItem) ? (
<Box
key={ index }
w="100%"
......@@ -83,10 +85,10 @@ const NavLinkGroupDesktop = ({ text, subItems, icon, isCollapsed, isActive }: Pr
borderColor: 'divider',
}}
>
{ item.map(subItem => <NavLink key={ subItem.text } item={ subItem } isCollapsed={ false }/>) }
{ subItem.map(subSubItem => <NavLink key={ subSubItem.text } item={ subSubItem } isCollapsed={ false }/>) }
</Box>
) :
<NavLink key={ item.text } item={ item } isCollapsed={ false }/>,
<NavLink key={ item.text } item={ subItem } isCollapsed={ false }/>,
) }
</VStack>
</PopoverBody>
......
......@@ -11,15 +11,16 @@ import type { NavGroupItem } from 'types/client/navigation-items';
import chevronIcon from 'icons/arrows/east-mini.svg';
import NavLinkIcon from './NavLinkIcon';
import useNavLinkStyleProps from './useNavLinkStyleProps';
type Props = NavGroupItem & {
isCollapsed?: boolean;
type Props = {
item: NavGroupItem;
onClick: () => void;
}
const NavLinkGroup = ({ text, icon, isActive, onClick }: Props) => {
const styleProps = useNavLinkStyleProps({ isActive });
const NavLinkGroup = ({ item, onClick }: Props) => {
const styleProps = useNavLinkStyleProps({ isActive: item.isActive });
return (
<Box as="li" listStyleType="none" w="100%" onClick={ onClick }>
......@@ -27,15 +28,15 @@ const NavLinkGroup = ({ text, icon, isActive, onClick }: Props) => {
{ ...styleProps.itemProps }
w="100%"
px={ 3 }
aria-label={ `${ text } link group` }
aria-label={ `${ item.text } link group` }
>
<Flex justifyContent="space-between" width="100%" alignItems="center" pr={ 1 }>
<HStack spacing={ 3 } overflow="hidden">
<Icon as={ icon } boxSize="30px"/>
<NavLinkIcon item={ item }/>
<Text
{ ...styleProps.textProps }
>
{ text }
{ item.text }
</Text>
</HStack>
<Icon as={ chevronIcon } transform="rotate(180deg)" boxSize={ 6 }/>
......
import { Icon } from '@chakra-ui/react';
import React from 'react';
import type { NavItem, NavGroupItem } from 'types/client/navigation-items';
const NavLinkIcon = ({ item }: { item: NavItem | NavGroupItem}) => {
if ('icon' in item) {
return <Icon as={ item.icon } boxSize="30px"/>;
}
if ('iconComponent' in item && item.iconComponent) {
const IconComponent = item.iconComponent;
return <IconComponent size={ 30 }/>;
}
return null;
};
export default NavLinkIcon;
......@@ -26,13 +26,7 @@ const test = base.extend({
]) as any,
});
test('no auth +@desktop-xl +@dark-mode-xl', async({ page, mount }) => {
await page.evaluate(() => {
window.ethereum = {
providers: [ { isMetaMask: true } ],
};
});
test('no auth +@desktop-xl +@dark-mode-xl', async({ mount }) => {
const component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
......
......@@ -12,7 +12,6 @@ import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo';
import NetworkMenu from 'ui/snippets/networkMenu/NetworkMenu';
import NavFooter from './NavFooter';
import NavLink from './NavLink';
import NavLinkGroupDesktop from './NavLinkGroupDesktop';
......@@ -83,7 +82,7 @@ const NavigationDesktop = () => {
<VStack as="ul" spacing="1" alignItems="flex-start">
{ mainNavItems.map((item) => {
if (isGroupItem(item)) {
return <NavLinkGroupDesktop key={ item.text } { ...item } isCollapsed={ isCollapsed }/>;
return <NavLinkGroupDesktop key={ item.text } item={ item } isCollapsed={ isCollapsed }/>;
} else {
return <NavLink key={ item.text } item={ item } isCollapsed={ isCollapsed }/>;
}
......@@ -97,7 +96,6 @@ const NavigationDesktop = () => {
</VStack>
</Box>
) }
<NavFooter isCollapsed={ isCollapsed } hasAccount={ hasAccount }/>
<Icon
as={ chevronIcon }
width={ 6 }
......
......@@ -5,7 +5,6 @@ import React, { useCallback } from 'react';
import chevronIcon from 'icons/arrows/east-mini.svg';
import useHasAccount from 'lib/hooks/useHasAccount';
import useNavItems, { isGroupItem } from 'lib/hooks/useNavItems';
import NavFooter from 'ui/snippets/navigation/NavFooter';
import NavLink from 'ui/snippets/navigation/NavLink';
import NavLinkGroupMobile from './NavLinkGroupMobile';
......@@ -58,7 +57,7 @@ const NavigationMobile = () => {
>
{ mainNavItems.map((item, index) => {
if (isGroupItem(item)) {
return <NavLinkGroupMobile key={ item.text } { ...item } onClick={ onGroupItemOpen(index) }/>;
return <NavLinkGroupMobile key={ item.text } item={ item } onClick={ onGroupItemOpen(index) }/>;
} else {
return <NavLink key={ item.text } item={ item }/>;
}
......@@ -78,7 +77,6 @@ const NavigationMobile = () => {
</VStack>
</Box>
) }
<NavFooter hasAccount={ hasAccount }/>
</Box>
{ openedGroupIndex >= 0 && (
<Box
......
......@@ -53,6 +53,6 @@ test.describe('auth', () => {
);
await component.getByAltText(/Profile picture/i).click();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 250, height: 550 } });
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 250, height: 600 } });
});
});
......@@ -7,7 +7,7 @@ import UserAvatar from 'ui/shared/UserAvatar';
import ProfileMenuContent from 'ui/snippets/profileMenu/ProfileMenuContent';
const ProfileMenuDesktop = () => {
const { data, isFetched } = useFetchProfileInfo();
const { data } = useFetchProfileInfo();
const loginUrl = useLoginUrl();
return (
......@@ -21,7 +21,7 @@ const ProfileMenuDesktop = () => {
as={ data ? undefined : 'a' }
href={ data ? undefined : loginUrl }
>
<UserAvatar size={ 50 } data={ data } isFetched={ isFetched }/>
<UserAvatar size={ 50 }/>
</Button>
</PopoverTrigger>
{ data && (
......
......@@ -8,19 +8,26 @@ import buildApiUrl from 'playwright/utils/buildApiUrl';
import ProfileMenuMobile from './ProfileMenuMobile';
test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test('no auth', async({ mount, page }) => {
const hooksConfig = {
router: {
asPath: '/',
pathname: '/',
},
};
const component = await mount(
<TestApp>
<ProfileMenuMobile/>
</TestApp>,
{ hooksConfig },
);
await component.locator('.identicon').click();
await expect(page).toHaveScreenshot();
expect(page.url()).toBe('http://localhost:3100/auth/auth0?path=%2F');
});
test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test.describe('auth', () => {
const extendedTest = test.extend({
context: ({ context }, use) => {
......@@ -49,8 +56,5 @@ test.describe('auth', () => {
await component.getByAltText(/Profile picture/i).click();
await expect(page).toHaveScreenshot();
await page.locator('div[aria-label="Toggle color mode"]').click();
await expect(page).toHaveScreenshot();
});
});
import { Flex, Box, Drawer, DrawerOverlay, DrawerContent, DrawerBody, useDisclosure, Button } from '@chakra-ui/react';
import { Box, Drawer, DrawerOverlay, DrawerContent, DrawerBody, useDisclosure, Button } from '@chakra-ui/react';
import React from 'react';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
import useLoginUrl from 'lib/hooks/useLoginUrl';
import UserAvatar from 'ui/shared/UserAvatar';
import ColorModeToggler from 'ui/snippets/header/ColorModeToggler';
import ProfileMenuContent from 'ui/snippets/profileMenu/ProfileMenuContent';
const ProfileMenuMobile = () => {
const { isOpen, onOpen, onClose } = useDisclosure();
const { data, isFetched } = useFetchProfileInfo();
const { data } = useFetchProfileInfo();
const loginUrl = useLoginUrl();
return (
<>
<Box padding={ 2 } onClick={ onOpen }>
<UserAvatar size={ 24 } data={ data } isFetched={ isFetched }/>
<Box padding={ 2 } onClick={ data ? onOpen : undefined }>
<Button
variant="unstyled"
height="auto"
as={ data ? undefined : 'a' }
href={ data ? undefined : loginUrl }
>
<UserAvatar size={ 24 }/>
</Button>
</Box>
{ data && (
<Drawer
isOpen={ isOpen }
placement="right"
......@@ -27,22 +34,11 @@ const ProfileMenuMobile = () => {
<DrawerOverlay/>
<DrawerContent maxWidth="260px">
<DrawerBody p={ 6 }>
<Flex
justifyContent="space-between"
alignItems="center"
mb={ 6 }
>
<ColorModeToggler/>
<Box onClick={ onClose }>
<UserAvatar size={ 24 } data={ data } isFetched={ isFetched }/>
</Box>
</Flex>
{ data ? <ProfileMenuContent { ...data }/> : (
<Button size="sm" width="full" variant="outline" as="a" href={ loginUrl }>Sign In</Button>
) }
<ProfileMenuContent { ...data }/>
</DrawerBody>
</DrawerContent>
</Drawer>
) }
</>
);
};
......
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