Commit 7252d107 authored by isstuev's avatar isstuev

profile menu change icon

parent 3c931880
<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>
......@@ -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',
};
......
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;
......
......@@ -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
......
......@@ -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`;
......
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;
......@@ -82,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 }/>;
}
......
......@@ -57,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 }/>;
}
......
......@@ -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) => {
......
import { Box, Drawer, DrawerOverlay, DrawerContent, DrawerBody, useDisclosure, Button, Flex } 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';
......@@ -9,34 +9,36 @@ 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>
<Drawer
isOpen={ isOpen }
placement="right"
onClose={ onClose }
autoFocus={ false }
>
<DrawerOverlay/>
<DrawerContent maxWidth="260px">
<DrawerBody p={ 6 }>
<Flex justifyContent="end" mb={ 6 }>
<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>
) }
</DrawerBody>
</DrawerContent>
</Drawer>
{ data && (
<Drawer
isOpen={ isOpen }
placement="right"
onClose={ onClose }
autoFocus={ false }
>
<DrawerOverlay/>
<DrawerContent maxWidth="260px">
<DrawerBody p={ 6 }>
<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