Commit d8ffa02d authored by Max Alekseenko's avatar Max Alekseenko Committed by GitHub

Promo banner in the navigation menu (#2859)

parent bf74f517
import type { ContractCodeIde } from 'types/client/contract';
import { type NavItemExternal, type NavigationLayout } from 'types/client/navigation';
import { type NavItemExternal, type NavigationLayout, type NavigationPromoBannerConfig } from 'types/client/navigation';
import { HOME_STATS_WIDGET_IDS, type ChainIndicatorId, type HeroBannerConfig, type HomeStatsWidgetId } from 'types/homepage';
import type { NetworkExplorer } from 'types/networks';
import type { ColorThemeId } from 'types/settings';
......@@ -37,6 +37,11 @@ const defaultColorTheme = (() => {
return COLOR_THEMES.find((theme) => theme.id === envValue) as ColorTheme | undefined;
})();
const navigationPromoBanner = (() => {
const envValue = parseEnvJson<NavigationPromoBannerConfig>(getEnvValue('NEXT_PUBLIC_NAVIGATION_PROMO_BANNER_CONFIG'));
return envValue || undefined;
})();
const UI = Object.freeze({
navigation: {
logo: {
......@@ -51,6 +56,7 @@ const UI = Object.freeze({
otherLinks: parseEnvJson<Array<NavItemExternal>>(getEnvValue('NEXT_PUBLIC_OTHER_LINKS')) || [],
featuredNetworks: getExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS'),
layout: (getEnvValue('NEXT_PUBLIC_NAVIGATION_LAYOUT') || 'vertical') as NavigationLayout,
promoBanner: navigationPromoBanner,
},
footer: {
links: getExternalAssetFilePath('NEXT_PUBLIC_FOOTER_LINKS'),
......
......@@ -73,3 +73,4 @@ NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
NEXT_PUBLIC_ADDRESS_3RD_PARTY_WIDGETS=['talentprotocol', 'efp', 'webacy', 'deepdao', 'humanpassport', 'trustblock', 'bankless']
NEXT_PUBLIC_ADDRESS_3RD_PARTY_WIDGETS_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/widgets/config.json
NEXT_PUBLIC_NAVIGATION_PROMO_BANNER_CONFIG={'img_url': 'https://gist.githubusercontent.com/maxaleks/0a02a0f15d018674171dc03e13f85cdd/raw/7ef0a5a3f7ec48bc202634d7aa6dd6fd71c8d0c5/promo.svg', 'text': 'Try Multichain!', 'bg_color': {'light': 'rgb(250, 245, 255)', 'dark': 'rgb(68, 51, 122)'}, 'text_color': {'light': 'rgb(107, 70, 193)', 'dark': 'rgb(233, 216, 253)'}, 'link_url': 'https://www.blockscout.com?utm_source=1&utm_medium=side-menu-banner'}
......@@ -22,7 +22,7 @@ import type { MarketplaceAppOverview, MarketplaceAppSecurityReportRaw, Marketpla
import type { MultichainProviderConfig } from '../../../types/client/multichainProviderConfig';
import type { ApiDocsTabId } from '../../../types/views/apiDocs';
import { API_DOCS_TABS } from '../../../types/views/apiDocs';
import type { NavItemExternal, NavigationLayout } from '../../../types/client/navigation';
import type { NavItemExternal, NavigationLayout, NavigationPromoBannerConfig } from '../../../types/client/navigation';
import { ROLLUP_TYPES } from '../../../types/client/rollup';
import type { BridgedTokenChain, TokenBridge } from '../../../types/client/token';
import { PROVIDERS as TX_INTERPRETATION_PROVIDERS } from '../../../types/client/txInterpretation';
......@@ -906,6 +906,36 @@ const schema = yup
.json()
.of(yup.string()),
NEXT_PUBLIC_NAVIGATION_LAYOUT: yup.string<NavigationLayout>().oneOf([ 'horizontal', 'vertical' ]),
NEXT_PUBLIC_NAVIGATION_PROMO_BANNER_CONFIG: yup
.mixed()
.test('shape', 'Invalid schema were provided for NEXT_PUBLIC_NAVIGATION_PROMO_BANNER_CONFIG, it should be either object with img_url, text, bg_color, text_color, link_url or object with img_url and link_url', (data) => {
const isUndefined = data === undefined;
const jsonSchema = yup.object<NavigationPromoBannerConfig>().transform(replaceQuotes).json();
const valueSchema1 = jsonSchema.shape({
img_url: yup.string().required(),
text: yup.string().required(),
bg_color: yup.object().shape({
light: yup.string().required(),
dark: yup.string().required(),
}).required(),
text_color: yup.object().shape({
light: yup.string().required(),
dark: yup.string().required(),
}).required(),
link_url: yup.string().required(),
});
const valueSchema2 = jsonSchema.shape({
img_url: yup.object().shape({
small: yup.string().required(),
large: yup.string().required(),
}).required(),
link_url: yup.string().required(),
});
return isUndefined || valueSchema1.isValidSync(data) || valueSchema2.isValidSync(data);
}),
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),
......
......@@ -10,3 +10,4 @@ NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://example.com
NEXT_PUBLIC_METADATA_ADDRESS_TAGS_UPDATE_ENABLED=false
NEXT_PUBLIC_HAS_USER_OPS=true
NEXT_PUBLIC_USER_OPS_INDEXER_API_HOST=https://example.com
NEXT_PUBLIC_NAVIGATION_PROMO_BANNER_CONFIG={'img_url': {'small': 'https://example.com/promo-sm.png', 'large': 'https://example.com/promo-lg.png'}, 'link_url': 'https://example.com'}
......@@ -90,3 +90,4 @@ NEXT_PUBLIC_SAVE_ON_GAS_ENABLED=true
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://example.com
NEXT_PUBLIC_ADDRESS_3RD_PARTY_WIDGETS=['widget-1', 'widget-2']
NEXT_PUBLIC_ADDRESS_3RD_PARTY_WIDGETS_CONFIG_URL=https://example.com
NEXT_PUBLIC_NAVIGATION_PROMO_BANNER_CONFIG={'img_url': 'https://example.com/promo.svg', 'text': 'Promo text', 'bg_color': {'light': 'rgb(250, 245, 255)', 'dark': 'rgb(68, 51, 122)'}, 'text_color': {'light': 'rgb(107, 70, 193)', 'dark': 'rgb(233, 216, 253)'}, 'link_url': 'https://example.com'}
......@@ -165,6 +165,7 @@ _Note_ Here, all values are arrays of up to two strings. The first string repres
| NEXT_PUBLIC_OTHER_LINKS | `Array<{url: string; text: string}>` | List of links for the "Other" navigation menu | - | - | `[{'url':'https://blockscout.com','text':'Blockscout'}]` | v1.0.x+ |
| NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES | `Array<string>` | List of menu item routes that should have a lightning label | - | - | `['/accounts']` | v1.31.0+ |
| NEXT_PUBLIC_NAVIGATION_LAYOUT | `vertical \| horizontal` | Navigation menu layout type | - | `vertical` | `horizontal` | v1.32.0+ |
| NEXT_PUBLIC_NAVIGATION_PROMO_BANNER_CONFIG | `string` | Configuration of promo banner in the navigation menu. See [below](#navigation-promo-banner-configuration-properties) list of available properties for particular banner type | - | - | `{'img_url': 'https://example.com/promo.svg', 'text': 'Promo text', 'bg_color': {'light': 'rgb(250, 245, 255)', 'dark': 'rgb(68, 51, 122)'}, 'text_color': {'light': 'rgb(107, 70, 193)', 'dark': 'rgb(233, 216, 253)'}, 'link_url': 'https://example.com'}` | v2.3.0+ |
#### Featured network configuration properties
......@@ -177,6 +178,25 @@ _Note_ Here, all values are arrays of up to two strings. The first string repres
| isActive | `boolean` | Pass `true` if item should be shown as active in the menu | - | - | `true` |
| invertIconInDarkMode | `boolean` | Pass `true` if icon colors should be inverted in dark mode | - | - | `true` |
#### Navigation promo banner configuration properties
##### Text promo banner:
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| img_url | `string` | Displayed icon url. The recommended minimum image size is 60x60 pixels (1:1 aspect ratio). | Required | - | `https://example.com/promo.svg` |
| text | `string` | Displayed text | Required | - | `Promo text` |
| bg_color | `{'light': string, 'dark': string}` | Background color | Required | - | `{'light': 'rgb(250, 245, 255)', 'dark': 'rgb(68, 51, 122)'}` |
| text_color | `{'light': string, 'dark': string}` | Text color | Required | - | `{'light': 'rgb(107, 70, 193)', 'dark': 'rgb(233, 216, 253)'}` |
| link_url | `string` | Redirect link url | Required | - | `https://example.com` |
##### Image promo banner:
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| img_url | `{'small': string, 'large': string}` | Displayed image urls. Small image is used in the collapsed navigation menu and in horizontal navigation, large image is used in the expanded navigation menu and in tooltip. The recommended minimum image sizes are 120x120 pixels (1:1 aspect ratio) for small image and 500x250 pixels (2:1 aspect ratio) for large image. | Required | - | `{'small': 'https://example.com/promo-sm.svg', 'large': 'https://example.com/promo-lg.svg'}` |
| link_url | `string` | Redirect link url | Required | - | `https://example.com` |
&nbsp;
### Footer
......
......@@ -113,4 +113,10 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = {
celo: [
[ 'NEXT_PUBLIC_CELO_ENABLED', 'true' ],
],
navigationPromoBannerText: [
[ 'NEXT_PUBLIC_NAVIGATION_PROMO_BANNER_CONFIG', '{"img_url": "http://localhost:3000/image.svg", "text": "Try the DUCK!", "bg_color": {"light": "rgb(150, 211, 255)", "dark": "rgb(68, 51, 122)"}, "text_color": {"light": "rgb(69, 69, 69)", "dark": "rgb(233, 216, 253)"}, "link_url": "https://example.com"}' ],
],
navigationPromoBannerImage: [
[ 'NEXT_PUBLIC_NAVIGATION_PROMO_BANNER_CONFIG', '{"img_url": {"small": "http://localhost:3000/image_s.jpg", "large": "http://localhost:3000/image_md.jpg"}, "link_url": "https://example.com"}' ],
],
};
......@@ -32,3 +32,23 @@ export type NavGroupItem = NavItemCommon & {
};
export type NavigationLayout = 'vertical' | 'horizontal';
export type NavigationPromoBannerConfig = {
img_url: string;
text: string;
bg_color: {
light: string;
dark: string;
};
text_color: {
light: string;
dark: string;
};
link_url: string;
} | {
img_url: {
small: string;
large: string;
};
link_url: string;
};
......@@ -3,6 +3,7 @@ import React from 'react';
import { FEATURED_NETWORKS } from 'mocks/config/network';
import { contextWithAuth } from 'playwright/fixtures/auth';
import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
import { test, expect, devices } from 'playwright/lib';
import Burger from './Burger';
......@@ -76,3 +77,25 @@ authTest.describe('auth', () => {
await expect(page).toHaveScreenshot();
});
});
const promoBannerTest = (type: 'text' | 'image') => {
test.describe(`with promo banner (${ type })`, () => {
const darkModeRule = type === 'text' ? '+@dark-mode' : '';
test.beforeEach(async({ mockEnvs, mockAssetResponse }) => {
await mockEnvs(type === 'text' ? ENVS_MAP.navigationPromoBannerText : ENVS_MAP.navigationPromoBannerImage);
await mockAssetResponse('http://localhost:3000/image.svg', './playwright/mocks/image_svg.svg');
await mockAssetResponse('http://localhost:3000/image_s.jpg', './playwright/mocks/image_s.jpg');
await mockAssetResponse('http://localhost:3000/image_md.jpg', './playwright/mocks/image_md.jpg');
});
test(`${ darkModeRule }`, async({ render, page }) => {
const component = await render(<Burger/>);
await component.getByRole('button', { name: 'Menu button' }).click();
await expect(page).toHaveScreenshot();
});
});
};
promoBannerTest('text');
promoBannerTest('image');
import type { BrowserContext } from '@playwright/test';
import type { BrowserContext, Locator } from '@playwright/test';
import React from 'react';
import * as rewardsBalanceMock from 'mocks/rewards/balance';
......@@ -63,3 +63,36 @@ test('with groped items', async({ render, mockEnvs, page }) => {
await expect(page.getByText('Blocks')).toBeVisible();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1500, height: 450 } });
});
const promoBannerTest = (type: 'text' | 'image') => {
test.describe(`with promo banner (${ type })`, () => {
let component: Locator;
const darkModeRule = type === 'text' ? '+@dark-mode' : '';
const imageAltText = type === 'text' ? 'Promo banner icon' : 'Promo banner small';
test.beforeEach(async({ render, mockEnvs, mockAssetResponse }) => {
await mockEnvs([
[ 'NEXT_PUBLIC_NAVIGATION_LAYOUT', 'horizontal' ],
...(type === 'text' ? ENVS_MAP.navigationPromoBannerText : ENVS_MAP.navigationPromoBannerImage),
]);
await mockAssetResponse('http://localhost:3000/image.svg', './playwright/mocks/image_svg.svg');
await mockAssetResponse('http://localhost:3000/image_s.jpg', './playwright/mocks/image_s.jpg');
await mockAssetResponse('http://localhost:3000/image_md.jpg', './playwright/mocks/image_md.jpg');
component = await render(<NavigationDesktop/>);
await component.waitFor({ state: 'visible' });
});
test(`${ darkModeRule }`, async() => {
await expect(component).toHaveScreenshot();
});
test('with tooltip', async({ page }) => {
await page.getByAltText(imageAltText).hover();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1500, height: 450 } });
});
});
};
promoBannerTest('text');
promoBannerTest('image');
......@@ -9,6 +9,7 @@ import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo';
import UserProfileDesktop from 'ui/snippets/user/profile/UserProfileDesktop';
import UserWalletDesktop from 'ui/snippets/user/wallet/UserWalletDesktop';
import NavigationPromoBanner from '../promoBanner/NavigationPromoBanner';
import RollupStageBadge from '../RollupStageBadge';
import TestnetBadge from '../TestnetBadge';
import NavLink from './NavLink';
......@@ -42,6 +43,7 @@ const NavigationDesktop = () => {
</Flex>
</chakra.nav>
<Flex gap={ 2 }>
<NavigationPromoBanner/>
{ config.features.rewards.isEnabled && <RewardsButton size="sm"/> }
{
(config.features.account.isEnabled && <UserProfileDesktop buttonSize="sm"/>) ||
......
......@@ -6,6 +6,7 @@ import { useColorModeValue } from 'toolkit/chakra/color-mode';
import IconSvg from 'ui/shared/IconSvg';
import useIsAuth from 'ui/snippets/auth/useIsAuth';
import NavigationPromoBanner from '../promoBanner/NavigationPromoBanner';
import NavLink from '../vertical/NavLink';
import NavLinkRewards from '../vertical/NavLinkRewards';
import NavLinkGroup from './NavLinkGroup';
......@@ -98,6 +99,7 @@ const NavigationMobile = ({ onNavLinkClick, isMarketplaceAppPage }: Props) => {
</VStack>
</Box>
) }
<NavigationPromoBanner isCollapsed={ isCollapsed }/>
</Box>
<Box
key="sub"
......
import { Flex, Box, useBreakpointValue, chakra } from '@chakra-ui/react';
import React, { useCallback, useState, useEffect } from 'react';
import { keccak256, stringToBytes } from 'viem';
import config from 'configs/app';
import useIsMobile from 'lib/hooks/useIsMobile';
import { Tooltip } from 'toolkit/chakra/tooltip';
import IconSvg from 'ui/shared/IconSvg';
import NavigationPromoBannerContent from './NavigationPromoBannerContent';
const PROMO_BANNER_CLOSED_HASH_KEY = 'nav-promo-banner-closed-hash';
const promoBanner = config.UI.navigation.promoBanner;
const isHorizontalNavigation = config.UI.navigation.layout === 'horizontal';
type Props = {
isCollapsed?: boolean;
};
const NavigationPromoBanner = ({ isCollapsed }: Props) => {
const isMobile = useIsMobile();
const isXLScreen = useBreakpointValue({ base: false, xl: true });
const [ isShown, setIsShown ] = useState(false);
const [ promoBannerHash, setPromoBannerHash ] = useState('');
useEffect(() => {
try {
const promoBannerClosedHash = window.localStorage.getItem(PROMO_BANNER_CLOSED_HASH_KEY);
const promoBannerHash = keccak256(stringToBytes(JSON.stringify(promoBanner)));
setIsShown(promoBannerHash !== promoBannerClosedHash);
setPromoBannerHash(promoBannerHash);
} catch {}
}, []);
const handleClose = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
e.preventDefault();
localStorage.setItem(PROMO_BANNER_CLOSED_HASH_KEY, promoBannerHash);
setIsShown(false);
}, [ promoBannerHash ]);
const isTooltipDisabled = isMobile || (!isHorizontalNavigation && (isCollapsed === false || (isCollapsed === undefined && isXLScreen)));
if (!promoBanner || !isShown) {
return null;
}
return (
<Flex flex={ 1 } mt={ isHorizontalNavigation ? 0 : 3 } pointerEvents="none">
<chakra.a
href={ promoBanner.link_url }
target="_blank"
rel="noopener noreferrer"
pointerEvents="auto"
w="full"
minW={ isHorizontalNavigation ? 'auto' : '60px' }
mt="auto"
position={ isHorizontalNavigation ? undefined : 'sticky' }
bottom={ isHorizontalNavigation ? undefined : { base: 0, lg: 6 } }
overflow="hidden"
_hover={{
opacity: 0.8,
_icon: {
display: 'block',
},
}}
>
<Tooltip
content={ !isTooltipDisabled && (
<NavigationPromoBannerContent
isCollapsed={ false }
isHorizontalNavigation={ false }
/>
) }
showArrow={ false }
positioning={{
placement: isHorizontalNavigation ? 'bottom' : 'right-end',
offset: { crossAxis: 0, mainAxis: isHorizontalNavigation ? 8 : 5 },
}}
contentProps={{
p: 0,
borderRadius: 'base',
bgColor: 'transparent',
boxShadow: isHorizontalNavigation ? '2xl' : 'none',
cursor: 'default',
}}
interactive
>
<Box w="full" position="relative">
<NavigationPromoBannerContent
isCollapsed={ isCollapsed }
isHorizontalNavigation={ isHorizontalNavigation }
/>
<IconSvg
onClick={ handleClose }
name="close"
boxSize={ 3 }
color={{ _light: 'gray.300', _dark: 'gray.600' }}
bgColor="global.body.bg"
borderBottomLeftRadius="sm"
position="absolute"
top="0"
right="0"
display="none"
/>
</Box>
</Tooltip>
</chakra.a>
</Flex>
);
};
export default NavigationPromoBanner;
import { HStack, Text, Box } from '@chakra-ui/react';
import config from 'configs/app';
import { Image } from 'toolkit/chakra/image';
import useNavLinkStyleProps from '../useNavLinkStyleProps';
const promoBanner = config.UI.navigation.promoBanner;
type Props = {
isCollapsed?: boolean;
isHorizontalNavigation?: boolean;
};
const NavigationPromoBannerContent = ({ isCollapsed, isHorizontalNavigation }: Props) => {
const isExpanded = isCollapsed === false;
const navLinkStyleProps = useNavLinkStyleProps({ isCollapsed, isExpanded });
if (!promoBanner) {
return null;
}
return 'text' in promoBanner ? (
<HStack
{ ...navLinkStyleProps.itemProps }
minW={ isHorizontalNavigation ? 'auto' : 'full' }
maxW={ isHorizontalNavigation ? 'auto' : 'full' }
w={ isHorizontalNavigation ? 'auto' : '180px' }
gap={ 2 }
overflow="hidden"
whiteSpace="nowrap"
py={ isHorizontalNavigation ? 1.5 : 2 }
px={ isHorizontalNavigation ? 1.5 : { base: 3, lg: isExpanded ? 3 : '15px', xl: isCollapsed ? '15px' : 3 } }
bgColor={{ _light: promoBanner.bg_color.light, _dark: promoBanner.bg_color.dark }}
>
<Image
src={ promoBanner.img_url }
alt="Promo banner icon"
boxSize={ isHorizontalNavigation ? '20px' : '30px' }
/>
{ !isHorizontalNavigation && (
<Text
{ ...navLinkStyleProps.textProps }
fontWeight="medium"
color={{ _light: promoBanner.text_color.light, _dark: promoBanner.text_color.dark }}
overflow="hidden"
>
{ promoBanner.text }
</Text>
) }
</HStack>
) : (
<Box
position="relative"
minH={ isHorizontalNavigation ? 'auto' : '60px' }
>
<Image
src={ promoBanner.img_url.small }
alt="Promo banner small"
boxSize={ isHorizontalNavigation ? '32px' : '60px' }
borderRadius={ isHorizontalNavigation ? 'sm' : 'base' }
position={ isHorizontalNavigation ? undefined : 'absolute' }
top={ isHorizontalNavigation ? undefined : 'calc(50% - 30px)' }
left={ isHorizontalNavigation ? undefined : 'calc(50% - 30px)' }
opacity={ isHorizontalNavigation ? 1 : { base: 0, lg: isExpanded ? 0 : 1, xl: isCollapsed ? 1 : 0 } }
transitionProperty="opacity"
transitionDuration="normal"
transitionTimingFunction="ease"
/>
<Image
display={ isHorizontalNavigation ? 'none' : 'block' }
src={ promoBanner.img_url.large }
alt="Promo banner large"
w="full"
maxW={{ base: 'full', lg: '180px' }}
borderRadius="base"
aspectRatio={ 2 / 1 }
opacity={{ base: 1, lg: isExpanded ? 1 : 0, xl: isCollapsed ? 0 : 1 }}
transitionProperty="opacity"
transitionDuration="normal"
transitionTimingFunction="ease"
/>
</Box>
);
};
export default NavigationPromoBannerContent;
......@@ -256,3 +256,48 @@ test.describe('with highlighted routes', () => {
});
});
});
const promoBannerTest = (type: 'text' | 'image') => {
test.describe(`with promo banner (${ type })`, () => {
let component: Locator;
const darkModeRule = type === 'text' ? '+@dark-mode' : '';
const imageAltText = type === 'text' ? 'Promo banner icon' : 'Promo banner small';
test.beforeEach(async({ render, mockEnvs, mockAssetResponse }) => {
await mockEnvs(type === 'text' ? ENVS_MAP.navigationPromoBannerText : ENVS_MAP.navigationPromoBannerImage);
await mockAssetResponse('http://localhost:3000/image.svg', './playwright/mocks/image_svg.svg');
await mockAssetResponse('http://localhost:3000/image_s.jpg', './playwright/mocks/image_s.jpg');
await mockAssetResponse('http://localhost:3000/image_md.jpg', './playwright/mocks/image_md.jpg');
component = await render(
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/>
</Flex>,
{ hooksConfig },
);
await component.waitFor({ state: 'visible' });
});
test(`${ darkModeRule }`, async() => {
await expect(component).toHaveScreenshot();
});
test('with tooltip', async({ page }) => {
await page.getByAltText(imageAltText).hover();
await expect(component).toHaveScreenshot();
});
test.describe('xl screen', () => {
test.use({ viewport: pwConfig.viewport.xl });
test(`${ darkModeRule }`, async() => {
await expect(component).toHaveScreenshot();
});
});
});
};
promoBannerTest('text');
promoBannerTest('image');
......@@ -8,6 +8,7 @@ import IconSvg from 'ui/shared/IconSvg';
import useIsAuth from 'ui/snippets/auth/useIsAuth';
import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo';
import NavigationPromoBanner from '../promoBanner/NavigationPromoBanner';
import RollupStageBadge from '../RollupStageBadge';
import TestnetBadge from '../TestnetBadge';
import NavLink from './NavLink';
......@@ -56,7 +57,8 @@ const NavigationDesktop = () => {
borderRight="1px solid"
borderColor="border.divider"
px={{ lg: isExpanded ? 6 : 4, xl: isCollapsed ? 4 : 6 }}
py={ 12 }
pt={ 12 }
pb={ 6 }
width={{ lg: isExpanded ? '229px' : '92px', xl: isCollapsed ? '92px' : '229px' }}
onClick={ handleContainerClick }
transitionProperty="width, padding"
......@@ -100,6 +102,7 @@ const NavigationDesktop = () => {
</VStack>
</Box>
) }
<NavigationPromoBanner isCollapsed={ isCollapsed }/>
<IconSvg
name="arrows/east-mini"
width={ 6 }
......
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