Commit 734546a2 authored by tom goriunov's avatar tom goriunov Committed by GitHub

Merge pull request #439 from blockscout/navbar-margins

navbar margins and a lot of tests
parents 713ca5d9 f2afe538
......@@ -11,6 +11,8 @@ NEXT_PUBLIC_FOOTER_GITHUB_LINK=https://github.com/blockscout/blockscout
NEXT_PUBLIC_FOOTER_TWITTER_LINK=https://www.twitter.com/blockscoutcom
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true
NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true
NEXT_PUBLIC_NETWORK_LOGO=
NEXT_PUBLIC_NETWORK_SMALL_LOGO=
# api config
NEXT_PUBLIC_API_HOST=blockscout.com
......
import type { Types } from 'typescript-cookie';
import { Cookies } from 'typescript-cookie';
import Cookies from 'js-cookie';
import isBrowser from './isBrowser';
......@@ -14,10 +13,13 @@ export function get(name?: NAMES | undefined | null, serverCookie?: string) {
if (!isBrowser()) {
return serverCookie ? getFromCookieString(serverCookie, name) : undefined;
}
return Cookies.get(name);
if (name) {
return Cookies.get(name);
}
}
export function set(name: string, value: string, attributes: Types.CookieAttributes = {}) {
export function set(name: string, value: string, attributes: Cookies.CookieAttributes = {}) {
attributes.path = '/';
return Cookies.set(name, value, attributes);
......
import type { FeaturedNetwork } from 'types/networks';
const FEATURED_NETWORKS: Array<FeaturedNetwork> = [
{ title: 'Gnosis Chain', url: 'https://blockscout.com/xdai/mainnet', group: 'mainnets', type: 'xdai_mainnet' },
{ title: 'Optimism on Gnosis Chain', url: 'https://blockscout.com/xdai/optimism', group: 'mainnets', type: 'xdai_optimism' },
{ title: 'Arbitrum on xDai', url: 'https://blockscout.com/xdai/aox', group: 'mainnets' },
{ title: 'Ethereum', url: 'https://blockscout.com/eth/mainnet', group: 'mainnets', type: 'eth_mainnet' },
{ title: 'Ethereum Classic', url: 'https://blockscout.com/etx/mainnet', group: 'mainnets', type: 'etc_mainnet', icon: 'https://example.com/my-logo.png' },
{ title: 'POA', url: 'https://blockscout.com/poa/core', group: 'mainnets', type: 'poa_core' },
{ title: 'RSK', url: 'https://blockscout.com/rsk/mainnet', group: 'mainnets', type: 'rsk_mainnet' },
{ title: 'Gnosis Chain Testnet', url: 'https://blockscout.com/xdai/testnet', group: 'testnets', type: 'xdai_testnet' },
{ title: 'POA Sokol', url: 'https://blockscout.com/poa/sokol', group: 'testnets', type: 'poa_sokol' },
{ title: 'ARTIS Σ1', url: 'https://blockscout.com/artis/sigma1', group: 'other', type: 'artis_sigma1' },
{ title: 'LUKSO L14', url: 'https://blockscout.com/lukso/l14', group: 'other', type: 'lukso_l14' },
{ title: 'Astar', url: 'https://blockscout.com/astar', group: 'other', type: 'astar' },
];
export const FEATURED_NETWORKS_MOCK = JSON.stringify(FEATURED_NETWORKS).replaceAll('"', '\'');
export const base = {
avatar: 'https://avatars.githubusercontent.com/u/22130104',
email: 'tom@ohhhh.me',
name: 'tom goriunov',
nickname: 'tom2drum',
};
......@@ -96,6 +96,15 @@ const config: PlaywrightTestConfig = {
colorScheme: 'dark',
},
},
{
name: 'dark color mode desktop xl',
grep: /\+@dark-mode-xl/,
use: {
...devices['Desktop Chrome'],
viewport: { width: 1600, height: 1000 },
colorScheme: 'dark',
},
},
],
};
......
......@@ -2,6 +2,8 @@ import { ChakraProvider } from '@chakra-ui/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';
import { AppContextProvider } from 'lib/appContext';
import type { Props as PageProps } from 'lib/next/getServerSideProps';
import { SocketProvider } from 'lib/socket/context';
import { PORT } from 'playwright/fixtures/socketServer';
import theme from 'theme';
......@@ -9,9 +11,19 @@ import theme from 'theme';
type Props = {
children: React.ReactNode;
withSocket?: boolean;
appContext?: {
pageProps: PageProps;
};
}
const TestApp = ({ children, withSocket }: Props) => {
const defaultAppContext = {
pageProps: {
cookies: '',
referrer: '',
},
};
const TestApp = ({ children, withSocket, appContext = defaultAppContext }: Props) => {
const [ queryClient ] = React.useState(() => new QueryClient({
defaultOptions: {
queries: {
......@@ -25,7 +37,9 @@ const TestApp = ({ children, withSocket }: Props) => {
<ChakraProvider theme={ theme }>
<QueryClientProvider client={ queryClient }>
<SocketProvider url={ withSocket ? `ws://localhost:${ PORT }` : undefined }>
{ children }
<AppContextProvider { ...appContext }>
{ children }
</AppContextProvider>
</SocketProvider>
</QueryClientProvider>
</ChakraProvider>
......
import type { BrowserContext } from '@playwright/test';
import * as cookies from 'lib/cookies';
export default function authFixture(context: BrowserContext) {
context.addCookies([ { name: cookies.NAMES.API_TOKEN, value: 'foo', domain: 'localhost', path: '/' } ]);
}
......@@ -25,10 +25,8 @@ do
configValue="$(cut -d'=' -f2- <<<"$line")";
# if there is a value, escape it and add line to target file
if [ -n "$configValue" ]; then
escapedConfigValue=$(echo $configValue | sed s/\'/\"/g);
echo "window.process.env.${configName} = localStorage.getItem('${configName}') || '${escapedConfigValue}';" >> $targetFile;
fi
escapedConfigValue=$(echo $configValue | sed s/\'/\"/g);
echo "window.process.env.${configName} = localStorage.getItem('${configName}') || '${escapedConfigValue}';" >> $targetFile;
done < $envFile
done
......
......@@ -43,5 +43,5 @@ test('default view -@default +@desktop-xl +@mobile +@dark-mode', async({ mount,
await page.evaluate(insertAdText);
await expect(component).toHaveScreenshot();
await expect(component.locator('main')).toHaveScreenshot();
});
import { test, expect, devices } from '@playwright/experimental-ct-react';
import React from 'react';
import authFixture from 'playwright/fixtures/auth';
import TestApp from 'playwright/TestApp';
import Burger from './Burger';
test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test('base view', async({ mount, page }) => {
const component = await mount(
<TestApp>
<Burger/>
</TestApp>,
);
await component.locator('svg[aria-label="Menu button"]').click();
await expect(page).toHaveScreenshot();
await page.locator('button[aria-label="Network menu"]').click();
await expect(page).toHaveScreenshot();
});
test.describe('dark mode', () => {
test.use({ colorScheme: 'dark' });
test('base view', async({ mount, page }) => {
const component = await mount(
<TestApp>
<Burger/>
</TestApp>,
);
await component.locator('svg[aria-label="Menu button"]').click();
await expect(page).toHaveScreenshot();
await page.locator('button[aria-label="Network menu"]').click();
await expect(page).toHaveScreenshot();
});
});
test.describe('auth', () => {
const extendedTest = test.extend({
context: ({ context }, use) => {
authFixture(context);
use(context);
},
});
extendedTest('base view', async({ mount, page }) => {
const component = await mount(
<TestApp>
<Burger/>
</TestApp>,
);
await component.locator('svg[aria-label="Menu button"]').click();
await expect(page).toHaveScreenshot();
});
});
......@@ -29,6 +29,7 @@ const Burger = () => {
boxSize={ 6 }
display="block"
color={ iconColor }
aria-label="Menu button"
/>
</Box>
<Drawer
......
......@@ -89,6 +89,7 @@ const ColorModeToggler = forwardRef<ColorModeTogglerProps, 'input'>((props, ref)
<chakra.div
{ ...getCheckboxProps() }
__css={ trackStyles }
aria-label="Toggle color mode"
>
<Icon
boxSize={ 4 }
......
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import TestApp from 'playwright/TestApp';
import Header from './Header';
test('no auth +@mobile +@dark-mode +@dark-mode-mobile', async({ mount, page }) => {
await mount(
<TestApp>
<Header/>
</TestApp>,
);
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1500, height: 150 } });
});
......@@ -33,6 +33,7 @@ const NavLink = ({ text, url, icon, isCollapsed, isActive, px, isNewUi }: Props)
_hover={{ color: isActive ? colors.text.active : colors.text.hover }}
borderRadius="base"
whiteSpace="nowrap"
aria-label={ `${ text } link` }
{ ...getDefaultTransitionProps({ transitionProperty: 'width, padding' }) }
>
<Tooltip
......
import { Box, Flex } from '@chakra-ui/react';
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import * as cookies from 'lib/cookies';
import authFixture from 'playwright/fixtures/auth';
import TestApp from 'playwright/TestApp';
import NavigationDesktop from './NavigationDesktop';
const hooksConfig = {
router: {
route: '/blocks',
query: { id: '0xd789a607CEac2f0E14867de4EB15b15C9FFB5859' },
},
};
test('no auth +@desktop-xl +@dark-mode-xl', async({ mount }) => {
const component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/>
</Flex>
</TestApp>,
{ hooksConfig },
);
await expect(component).toHaveScreenshot();
});
test.describe('auth', () => {
const extendedTest = test.extend({
context: ({ context }, use) => {
authFixture(context);
use(context);
},
});
extendedTest('+@desktop-xl +@dark-mode-xl', async({ mount }) => {
const component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/>
</Flex>
</TestApp>,
{ hooksConfig },
);
await expect(component).toHaveScreenshot();
});
});
test('with tooltips +@desktop-xl -@default', async({ mount, page }) => {
const component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/>
</Flex>
</TestApp>,
{ hooksConfig },
);
await page.locator('svg[aria-label="Expand/Collapse menu"]').click();
await page.locator('a[aria-label="Tokens link"]').hover();
await expect(component).toHaveScreenshot();
});
test.describe('cookie set to false', () => {
const extendedTest = test.extend({
context: ({ context }, use) => {
context.addCookies([ { name: cookies.NAMES.NAV_BAR_COLLAPSED, value: 'false', domain: 'localhost', path: '/' } ]);
use(context);
},
});
extendedTest('navbar is opened +@desktop-xl', async({ mount }) => {
const component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/>
</Flex>
</TestApp>,
{ hooksConfig },
);
const networkMenu = component.locator('button[aria-label="Network menu"]');
expect(await networkMenu.isVisible()).toBe(true);
});
});
test.describe('cookie set to true', () => {
const extendedTest = test.extend({
context: ({ context }, use) => {
context.addCookies([ { name: cookies.NAMES.NAV_BAR_COLLAPSED, value: 'true', domain: 'localhost', path: '/' } ]);
use(context);
},
});
extendedTest('navbar is collapsed', async({ mount }) => {
const component = await mount(
<TestApp>
<Flex w="100%" minH="100vh" alignItems="stretch">
<NavigationDesktop/>
<Box bgColor="lightpink" w="100%"/>
</Flex>
</TestApp>,
{ hooksConfig },
);
const networkMenu = component.locator('button[aria-label="Network menu"]');
expect(await networkMenu.isVisible()).toBe(false);
});
});
......@@ -77,13 +77,13 @@ const NavigationDesktop = () => {
<NetworkMenu isCollapsed={ isCollapsed }/>
</Box>
<Box as="nav" mt={ 8 }>
<VStack as="ul" spacing="2" alignItems="flex-start">
<VStack as="ul" spacing="1" alignItems="flex-start">
{ mainNavItems.map((item) => <NavLink key={ item.text } { ...item } isCollapsed={ isCollapsed }/>) }
</VStack>
</Box>
{ hasAccount && (
<Box as="nav" mt={ 12 }>
<VStack as="ul" spacing="2" alignItems="flex-start">
<Box as="nav" mt={ 8 }>
<VStack as="ul" spacing="1" alignItems="flex-start">
{ accountNavItems.map((item) => <NavLink key={ item.text } { ...item } isCollapsed={ isCollapsed }/>) }
</VStack>
</Box>
......@@ -105,6 +105,7 @@ const NavigationDesktop = () => {
left={{ lg: isExpanded ? '216px' : '80px', xl: isCollapsed ? '80px' : '216px' }}
cursor="pointer"
onClick={ handleTogglerClick }
aria-label="Expand/Collapse menu"
/>
</Flex>
);
......
......@@ -16,13 +16,13 @@ const NavigationMobile = () => {
return (
<>
<Box as="nav" mt={ 6 }>
<VStack as="ul" spacing="2" alignItems="flex-start">
<VStack as="ul" spacing="1" alignItems="flex-start">
{ mainNavItems.map((item) => <NavLink key={ item.text } { ...item }/>) }
</VStack>
</Box>
{ isAuth && (
<Box as="nav" mt={ 6 }>
<VStack as="ul" spacing="2" alignItems="flex-start">
<VStack as="ul" spacing="1" alignItems="flex-start">
{ accountNavItems.map((item) => <NavLink key={ item.text } { ...item }/>) }
</VStack>
</Box>
......
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import NetworkLogo from './NetworkLogo';
test('fallback logo +@desktop-xl +@dark-mode +@dark-mode-xl', async({ mount }) => {
const component = await mount(
<TestApp>
<NetworkLogo/>
</TestApp>,
);
await expect(component.locator('a')).toHaveScreenshot();
});
test.describe('placeholder logo', () => {
const extendedTest = test.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_NETWORK_TYPE', value: 'unknown' },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
});
extendedTest('+@desktop-xl +@dark-mode +@dark-mode-xl', async({ mount }) => {
const component = await mount(
<TestApp>
<NetworkLogo/>
</TestApp>,
);
await expect(component.locator('a')).toHaveScreenshot();
});
});
test.describe('custom logo', () => {
const LOGO_URL = 'https://example.com/my-logo.png';
const SMALL_LOGO_URL = 'https://example.com/my-logo-short.png';
const extendedTest = test.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_NETWORK_LOGO', value: LOGO_URL },
{ name: 'NEXT_PUBLIC_NETWORK_SMALL_LOGO', value: SMALL_LOGO_URL },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
});
extendedTest('+@desktop-xl +@dark-mode +@dark-mode-xl', async({ mount, page }) => {
await page.route(LOGO_URL, (route) => {
return route.fulfill({
status: 200,
path: './playwright/giant_duck_long.jpg',
});
});
await page.route(SMALL_LOGO_URL, (route) => {
return route.fulfill({
status: 200,
path: './playwright/image_s.jpg',
});
});
const component = await mount(
<TestApp>
<NetworkLogo/>
</TestApp>,
);
await expect(component.locator('a')).toHaveScreenshot();
});
});
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import NetworkMenu from './NetworkMenu';
const extendedTest = test.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_MOCK },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
});
extendedTest.use({ viewport: { width: 1600, height: 1000 } });
extendedTest('base view +@dark-mode', async({ mount, page }) => {
const LOGO_URL = 'https://example.com/my-logo.png';
await page.route(LOGO_URL, (route) => {
return route.fulfill({
status: 200,
path: './playwright/image_s.jpg',
});
});
const component = await mount(
<TestApp>
<NetworkMenu/>
</TestApp>,
);
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 36, height: 36 } });
await component.locator('button[aria-label="Network menu"]').hover();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 36, height: 36 } });
await component.locator('button[aria-label="Network menu"]').click();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 450, height: 550 } });
await component.getByText(/optimism/i).hover();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 450, height: 550 } });
});
import { Popover, PopoverTrigger, Box } from '@chakra-ui/react';
import { Popover, PopoverTrigger } from '@chakra-ui/react';
import React from 'react';
import NetworkMenuButton from './NetworkMenuButton';
......@@ -9,17 +9,16 @@ interface Props {
const NetworkMenu = ({ isCollapsed }: Props) => {
return (
<Popover openDelay={ 300 } placement="right-start" gutter={ 22 } isLazy>
<Popover openDelay={ 300 } placement="right-start" gutter={ 8 } isLazy>
{ ({ isOpen }) => (
<>
<PopoverTrigger>
<Box
<NetworkMenuButton
marginLeft="auto"
overflow="hidden"
width={{ base: 'auto', lg: isCollapsed === false ? 'auto' : '0px', xl: isCollapsed ? '0px' : 'auto' }}
>
<NetworkMenuButton isActive={ isOpen }/>
</Box>
isActive={ isOpen }
/>
</PopoverTrigger>
<NetworkMenuContentDesktop/>
</>
......
import { Icon, useColorModeValue, Button } from '@chakra-ui/react';
import { Icon, useColorModeValue, Button, forwardRef, chakra } from '@chakra-ui/react';
import React from 'react';
import networksIcon from 'icons/networks.svg';
......@@ -8,15 +8,17 @@ interface Props {
isMobile?: boolean;
isActive?: boolean;
onClick?: () => void;
className?: string;
}
const NetworkMenuButton = ({ isMobile, isActive, onClick }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
const NetworkMenuButton = ({ isMobile, isActive, onClick, className }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
const defaultIconColor = useColorModeValue('gray.600', 'gray.400');
const bgColorMobile = useColorModeValue('blue.50', 'gray.800');
const iconColorMobile = useColorModeValue('blue.700', 'blue.50');
return (
<Button
className={ className }
variant="unstyled"
display="inline-flex"
alignSelf="stretch"
......@@ -43,4 +45,4 @@ const NetworkMenuButton = ({ isMobile, isActive, onClick }: Props, ref: React.Fo
);
};
export default React.forwardRef(NetworkMenuButton);
export default chakra(forwardRef(NetworkMenuButton));
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import * as profileMock from 'mocks/user/profile';
import authFixture from 'playwright/fixtures/auth';
import TestApp from 'playwright/TestApp';
import ProfileMenuDesktop from './ProfileMenuDesktop';
test('no auth', async({ mount, page }) => {
const hooksConfig = {
router: {
asPath: '/',
},
};
const component = await mount(
<TestApp>
<ProfileMenuDesktop/>
</TestApp>,
{ hooksConfig },
);
await component.locator('.identicon').click();
expect(page.url()).toBe('http://localhost:3100/auth/auth0?path=%2F');
});
test.describe('auth', () => {
const extendedTest = test.extend({
context: ({ context }, use) => {
authFixture(context);
use(context);
},
});
extendedTest('+@dark-mode', async({ mount, page }) => {
await page.route('/node-api/account/profile', (route) => route.fulfill({
status: 200,
body: JSON.stringify(profileMock.base),
}));
await page.route(profileMock.base.avatar, (route) => {
return route.fulfill({
status: 200,
path: './playwright/image_s.jpg',
});
});
const component = await mount(
<TestApp>
<ProfileMenuDesktop/>
</TestApp>,
);
await component.getByAltText(/Profile picture/i).click();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 250, height: 550 } });
});
});
import { test, expect, devices } from '@playwright/experimental-ct-react';
import React from 'react';
import * as profileMock from 'mocks/user/profile';
import authFixture from 'playwright/fixtures/auth';
import TestApp from 'playwright/TestApp';
import ProfileMenuMobile from './ProfileMenuMobile';
test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test('no auth', async({ mount, page }) => {
const component = await mount(
<TestApp>
<ProfileMenuMobile/>
</TestApp>,
);
await component.locator('.identicon').click();
await expect(page).toHaveScreenshot();
});
test.describe('auth', () => {
const extendedTest = test.extend({
context: ({ context }, use) => {
authFixture(context);
use(context);
},
});
extendedTest('base view', async({ mount, page }) => {
await page.route('/node-api/account/profile', (route) => route.fulfill({
status: 200,
body: JSON.stringify(profileMock.base),
}));
await page.route(profileMock.base.avatar, (route) => {
return route.fulfill({
status: 200,
path: './playwright/image_s.jpg',
});
});
const component = await mount(
<TestApp>
<ProfileMenuMobile/>
</TestApp>,
);
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();
});
});
......@@ -3537,6 +3537,11 @@
expect "^29.0.0"
pretty-format "^29.0.0"
"@types/js-cookie@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-3.0.2.tgz#451eaeece64c6bdac8b2dde0caab23b085899e0d"
integrity sha512-6+0ekgfusHftJNYpihfkMu8BWdeHs9EOJuGcSofErjstGPfPGEu9yTu4t460lTzzAMl2cM5zngQJqPMHbbnvYA==
"@types/jsdom@^20.0.0":
version "20.0.1"
resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808"
......@@ -6975,6 +6980,11 @@ joycon@^3.1.1:
resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03"
integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==
js-cookie@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.1.tgz#9e39b4c6c2f56563708d7d31f6f5f21873a92414"
integrity sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==
js-sdsl@^4.1.4:
version "4.2.0"
resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.2.0.tgz#278e98b7bea589b8baaf048c20aeb19eb7ad09d0"
......@@ -8999,11 +9009,6 @@ type-fest@^0.21.3:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
typescript-cookie@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/typescript-cookie/-/typescript-cookie-1.0.4.tgz#22715aa394275e967fd734fa1417e24870504985"
integrity sha512-vZo252VmoEleD/dbE9Wb2lMK63V3M/8aqFbp2Pdb4Oxq8YqqADJ7iMh8THZenFXN+uZJPE8RXkztEaHkOptH4w==
typescript@4.7.2:
version "4.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.2.tgz#1f9aa2ceb9af87cca227813b4310fff0b51593c4"
......
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