mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
Merge branch 'main' of github.com:blockscout/frontend into tom2drum/issue-1826
This commit is contained in:
@@ -24,6 +24,11 @@ const hiddenLinks = (() => {
|
||||
return result;
|
||||
})();
|
||||
|
||||
const highlightedRoutes = (() => {
|
||||
const parsedValue = parseEnvJson<Array<NavigationLinkId>>(getEnvValue('NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES'));
|
||||
return Array.isArray(parsedValue) ? parsedValue : [];
|
||||
})();
|
||||
|
||||
const defaultColorTheme = (() => {
|
||||
const envValue = getEnvValue('NEXT_PUBLIC_COLOR_THEME_DEFAULT') as ColorThemeId | undefined;
|
||||
return COLOR_THEMES.find((theme) => theme.id === envValue);
|
||||
@@ -43,6 +48,7 @@ const UI = Object.freeze({
|
||||
dark: getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON_DARK'),
|
||||
},
|
||||
hiddenLinks,
|
||||
highlightedRoutes,
|
||||
otherLinks: parseEnvJson<Array<NavItemExternal>>(getEnvValue('NEXT_PUBLIC_OTHER_LINKS')) || [],
|
||||
featuredNetworks: getExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS'),
|
||||
},
|
||||
|
||||
@@ -56,4 +56,4 @@ NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.blocks
|
||||
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
|
||||
|
||||
#meta
|
||||
NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/eth-goerli.png?raw=true
|
||||
NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/eth-goerli.png?raw=true
|
||||
|
||||
@@ -33,6 +33,7 @@ NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/front
|
||||
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/sepolia.png
|
||||
NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/sepolia.png
|
||||
NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}]
|
||||
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/blocks','/apps']
|
||||
## footer
|
||||
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/sepolia.json
|
||||
##views
|
||||
|
||||
@@ -28,6 +28,7 @@ NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
|
||||
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/featured-networks/eth-goerli.json
|
||||
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-logos/goerli.svg
|
||||
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/network-icons/goerli.svg
|
||||
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/accounts','/apps']
|
||||
## footer
|
||||
##views
|
||||
NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'LooksRare','collection_url':'https://goerli.looksrare.org/collections/{hash}','instance_url':'https://goerli.looksrare.org/collections/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/looks-rare.png'}]
|
||||
|
||||
@@ -514,6 +514,11 @@ const schema = yup
|
||||
.transform(replaceQuotes)
|
||||
.json()
|
||||
.of(yup.string<NavigationLinkId>().oneOf(NAVIGATION_LINK_IDS)),
|
||||
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES: yup
|
||||
.array()
|
||||
.transform(replaceQuotes)
|
||||
.json()
|
||||
.of(yup.string()),
|
||||
NEXT_PUBLIC_NETWORK_LOGO: yup.string().test(urlTest),
|
||||
NEXT_PUBLIC_NETWORK_LOGO_DARK: yup.string().test(urlTest),
|
||||
NEXT_PUBLIC_NETWORK_ICON: yup.string().test(urlTest),
|
||||
|
||||
@@ -26,6 +26,7 @@ NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.blocks
|
||||
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://example.com
|
||||
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true
|
||||
NEXT_PUBLIC_FEATURED_NETWORKS=https://example.com
|
||||
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/accounts','/apps']
|
||||
NEXT_PUBLIC_FOOTER_LINKS=https://example.com
|
||||
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
|
||||
NEXT_PUBLIC_HIDE_INDEXING_ALERT_BLOCKS=false
|
||||
|
||||
@@ -129,6 +129,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
|
||||
| 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_NAVIGATION_HIDDEN_LINKS | `Array<LinkId>` | List of external links hidden in the navigation. Supported ids are `eth_rpc_api`, `rpc_api` | - | - | `['eth_rpc_api']` |
|
||||
| NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES | `Array<string>` | List of menu item routes that should have a lightning label | - | - | `['/accounts']` |
|
||||
|
||||
#### Featured network configuration properties
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.375 16v-5.006H.676L7.697 0h7.9v6l.027 10H5.374Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.72 6.01V0L2.63 10.15h4.02V16l6.66-9.99H8.72Z" fill="#ED8936"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 282 B |
@@ -63,6 +63,9 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = {
|
||||
noNftMarketplaces: [
|
||||
[ 'NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES', '' ],
|
||||
],
|
||||
navigationHighlightedRoutes: [
|
||||
[ 'NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES', '["/blocks", "/apps"]' ],
|
||||
],
|
||||
dataAvailability: [
|
||||
[ 'NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED', 'true' ],
|
||||
],
|
||||
|
||||
Vendored
+1
@@ -72,6 +72,7 @@
|
||||
| "integration/full"
|
||||
| "integration/partial"
|
||||
| "key"
|
||||
| "lightning_navbar"
|
||||
| "lightning"
|
||||
| "link"
|
||||
| "lock"
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { useColorModeValue, useBreakpointValue } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import getDefaultTransitionProps from 'theme/utils/getDefaultTransitionProps';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
|
||||
export const LIGHTNING_LABEL_CLASS_NAME = 'lightning-label';
|
||||
|
||||
const LightningLabel = ({ bgColor, isCollapsed }: { bgColor?: string; isCollapsed?: boolean }) => {
|
||||
const isLgScreen = useBreakpointValue({ base: false, lg: true, xl: false });
|
||||
const themeBgColor = useColorModeValue('white', 'black');
|
||||
const defaultTransitionProps = getDefaultTransitionProps({ transitionProperty: 'color' });
|
||||
|
||||
const isExpanded = isCollapsed === false;
|
||||
|
||||
const color = React.useMemo(() => {
|
||||
if (isCollapsed || (!isExpanded && isLgScreen)) {
|
||||
return (bgColor && bgColor !== 'transparent') ? bgColor : themeBgColor;
|
||||
}
|
||||
return 'transparent';
|
||||
}, [ bgColor, themeBgColor, isCollapsed, isExpanded, isLgScreen ]);
|
||||
|
||||
return (
|
||||
<IconSvg
|
||||
className={ LIGHTNING_LABEL_CLASS_NAME }
|
||||
name="lightning_navbar"
|
||||
boxSize={ 4 }
|
||||
ml={{ base: 1, lg: isExpanded ? 1 : 0, xl: isCollapsed ? 0 : 1 }}
|
||||
position={{ lg: isExpanded ? 'relative' : 'absolute', xl: isCollapsed ? 'absolute' : 'relative' }}
|
||||
top={{ lg: isExpanded ? '0' : '10px', xl: isCollapsed ? '10px' : '0' }}
|
||||
right={{ lg: isExpanded ? '0' : '15px', xl: isCollapsed ? '15px' : '0' }}
|
||||
color={ color }
|
||||
{ ...defaultTransitionProps }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LightningLabel;
|
||||
@@ -10,9 +10,11 @@ import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import { isInternalItem } from 'lib/hooks/useNavItems';
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
|
||||
import LightningLabel, { LIGHTNING_LABEL_CLASS_NAME } from './LightningLabel';
|
||||
import NavLinkIcon from './NavLinkIcon';
|
||||
import useColors from './useColors';
|
||||
import useNavLinkStyleProps from './useNavLinkStyleProps';
|
||||
import { checkRouteHighlight } from './utils';
|
||||
|
||||
type Props = {
|
||||
item: NavItem;
|
||||
@@ -34,6 +36,8 @@ const NavLink = ({ item, isCollapsed, px, className, onClick, disableActiveState
|
||||
const isXLScreen = useBreakpointValue({ base: false, xl: true });
|
||||
const href = isInternalLink ? route(item.nextRoute) : item.url;
|
||||
|
||||
const isHighlighted = checkRouteHighlight(item);
|
||||
|
||||
const content = (
|
||||
<Link
|
||||
href={ href }
|
||||
@@ -41,12 +45,13 @@ const NavLink = ({ item, isCollapsed, px, className, onClick, disableActiveState
|
||||
{ ...styleProps.itemProps }
|
||||
w={{ base: '100%', lg: isExpanded ? '100%' : '60px', xl: isCollapsed ? '60px' : '100%' }}
|
||||
display="flex"
|
||||
position="relative"
|
||||
px={ px || { base: 3, lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 } }
|
||||
aria-label={ `${ item.text } link` }
|
||||
whiteSpace="nowrap"
|
||||
onClick={ onClick }
|
||||
_hover={{
|
||||
'& *': {
|
||||
[`& *:not(.${ LIGHTNING_LABEL_CLASS_NAME }, .${ LIGHTNING_LABEL_CLASS_NAME } *)`]: {
|
||||
color: 'link_hovered',
|
||||
},
|
||||
}}
|
||||
@@ -60,12 +65,15 @@ const NavLink = ({ item, isCollapsed, px, className, onClick, disableActiveState
|
||||
gutter={ 20 }
|
||||
color={ isInternalLink && item.isActive ? colors.text.active : colors.text.hover }
|
||||
>
|
||||
<HStack spacing={ 3 } overflow="hidden">
|
||||
<HStack spacing={ 0 } overflow="hidden">
|
||||
<NavLinkIcon item={ item }/>
|
||||
<Text { ...styleProps.textProps } as="span">
|
||||
<Text { ...styleProps.textProps } as="span" ml={ 3 }>
|
||||
<span>{ item.text }</span>
|
||||
{ !isInternalLink && <IconSvg name="arrows/north-east" boxSize={ 4 } color="text_secondary" verticalAlign="middle"/> }
|
||||
</Text>
|
||||
{ isHighlighted && (
|
||||
<LightningLabel bgColor={ styleProps.itemProps.bgColor } isCollapsed={ isCollapsed }/>
|
||||
) }
|
||||
</HStack>
|
||||
</Tooltip>
|
||||
</Link>
|
||||
|
||||
@@ -15,9 +15,11 @@ import type { NavGroupItem } from 'types/client/navigation-items';
|
||||
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
|
||||
import LightningLabel from './LightningLabel';
|
||||
import NavLink from './NavLink';
|
||||
import NavLinkIcon from './NavLinkIcon';
|
||||
import useNavLinkStyleProps from './useNavLinkStyleProps';
|
||||
import { checkRouteHighlight } from './utils';
|
||||
|
||||
type Props = {
|
||||
item: NavGroupItem;
|
||||
@@ -29,6 +31,8 @@ const NavLinkGroupDesktop = ({ item, isCollapsed }: Props) => {
|
||||
|
||||
const styleProps = useNavLinkStyleProps({ isCollapsed, isExpanded, isActive: item.isActive });
|
||||
|
||||
const isHighlighted = checkRouteHighlight(item.subItems);
|
||||
|
||||
return (
|
||||
<Box as="li" listStyleType="none" w="100%">
|
||||
<Popover
|
||||
@@ -45,13 +49,17 @@ const NavLinkGroupDesktop = ({ item, isCollapsed }: Props) => {
|
||||
aria-label={ `${ item.text } link group` }
|
||||
position="relative"
|
||||
>
|
||||
<HStack spacing={ 3 } overflow="hidden">
|
||||
<HStack spacing={ 0 } overflow="hidden">
|
||||
<NavLinkIcon item={ item }/>
|
||||
<Text
|
||||
{ ...styleProps.textProps }
|
||||
ml={ 3 }
|
||||
>
|
||||
{ item.text }
|
||||
</Text>
|
||||
{ isHighlighted && (
|
||||
<LightningLabel bgColor={ styleProps.itemProps.bgColor } isCollapsed={ isCollapsed }/>
|
||||
) }
|
||||
<IconSvg
|
||||
name="arrows/east-mini"
|
||||
position="absolute"
|
||||
|
||||
@@ -10,8 +10,10 @@ import type { NavGroupItem } from 'types/client/navigation-items';
|
||||
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
|
||||
import LightningLabel from './LightningLabel';
|
||||
import NavLinkIcon from './NavLinkIcon';
|
||||
import useNavLinkStyleProps from './useNavLinkStyleProps';
|
||||
import { checkRouteHighlight } from './utils';
|
||||
|
||||
type Props = {
|
||||
item: NavGroupItem;
|
||||
@@ -22,6 +24,8 @@ type Props = {
|
||||
const NavLinkGroup = ({ item, onClick, isExpanded }: Props) => {
|
||||
const styleProps = useNavLinkStyleProps({ isActive: item.isActive, isExpanded });
|
||||
|
||||
const isHighlighted = checkRouteHighlight(item.subItems);
|
||||
|
||||
return (
|
||||
<Box as="li" listStyleType="none" w="100%" onClick={ onClick }>
|
||||
<Box
|
||||
@@ -31,13 +35,15 @@ const NavLinkGroup = ({ item, onClick, isExpanded }: Props) => {
|
||||
aria-label={ `${ item.text } link group` }
|
||||
>
|
||||
<Flex justifyContent="space-between" width="100%" alignItems="center" pr={ 1 }>
|
||||
<HStack spacing={ 3 } overflow="hidden">
|
||||
<HStack spacing={ 0 } overflow="hidden">
|
||||
<NavLinkIcon item={ item }/>
|
||||
<Text
|
||||
{ ...styleProps.textProps }
|
||||
ml={ 3 }
|
||||
>
|
||||
{ item.text }
|
||||
</Text>
|
||||
{ isHighlighted && (<LightningLabel bgColor={ styleProps.itemProps.bgColor }/>) }
|
||||
</HStack>
|
||||
<IconSvg name="arrows/east-mini" transform="rotate(180deg)" boxSize={ 6 }/>
|
||||
</Flex>
|
||||
|
||||
@@ -6,6 +6,7 @@ import config from 'configs/app';
|
||||
import * as cookies from 'lib/cookies';
|
||||
import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network';
|
||||
import { contextWithAuth } from 'playwright/fixtures/auth';
|
||||
import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
|
||||
import { test, expect } from 'playwright/lib';
|
||||
import * as pwConfig from 'playwright/utils/config';
|
||||
|
||||
@@ -217,3 +218,36 @@ test.describe('hover xl screen', () => {
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('with highlighted routes', () => {
|
||||
let component: Locator;
|
||||
|
||||
test.beforeEach(async({ render, mockEnvs }) => {
|
||||
await mockEnvs(ENVS_MAP.navigationHighlightedRoutes);
|
||||
|
||||
component = await render(
|
||||
<Flex w="100%" minH="100vh" alignItems="stretch">
|
||||
<NavigationDesktop/>
|
||||
<Box bgColor="lightpink" w="100%"/>
|
||||
</Flex>,
|
||||
{ hooksConfig },
|
||||
);
|
||||
});
|
||||
|
||||
test('+@dark-mode', async() => {
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('with submenu', async({ page }) => {
|
||||
await page.locator('a[aria-label="Blockchain link group"]').hover();
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test.describe('xl screen', () => {
|
||||
test.use({ viewport: pwConfig.viewport.xl });
|
||||
|
||||
test('+@dark-mode', async() => {
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,11 @@
|
||||
import type { NavItem } from 'types/client/navigation-items';
|
||||
|
||||
import config from 'configs/app';
|
||||
import { isInternalItem } from 'lib/hooks/useNavItems';
|
||||
|
||||
export function checkRouteHighlight(item: NavItem | Array<NavItem> | Array<Array<NavItem>>): boolean {
|
||||
if (Array.isArray(item)) {
|
||||
return item.some((subItem) => checkRouteHighlight(subItem));
|
||||
}
|
||||
return isInternalItem(item) && (config.UI.sidebar.highlightedRoutes.includes(item.nextRoute.pathname));
|
||||
}
|
||||
Reference in New Issue
Block a user