Skip to content
This repository has been archived by the owner on May 12, 2023. It is now read-only.

Commit

Permalink
feat: created my profile side panel (#29)
Browse files Browse the repository at this point in the history
* feat: created my profile side panel

* fix: adjusting code smells

* fix: fixing lint error

* fix: adjust user info, no mock data necessary
  • Loading branch information
mgaseta authored Jan 11, 2023
1 parent 08c76cf commit a412c60
Show file tree
Hide file tree
Showing 12 changed files with 311 additions and 19 deletions.
18 changes: 18 additions & 0 deletions src/components/AvatarImage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';

import './style.css';
import emptyUser from '../../mock-data/img/EmptyUser.jpg';

type Size = 'small' | 'large';

interface AvatarImageProps {
userName: string;
source?: string;
size: Size;
}

const AvatarImage = ({ userName, source, size }: AvatarImageProps) => (
<img src={source || emptyUser} alt={`${userName}`} className={`profile-image ${size}`} />
);

export default AvatarImage;
13 changes: 13 additions & 0 deletions src/components/AvatarImage/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.profile-image {
border-radius: 50%;
}

.small {
width: 1.5rem;
height: 1.5rem;
}

.large {
width: 4rem;
height: 4rem;
}
83 changes: 70 additions & 13 deletions src/components/BCHeader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import React from 'react';
import { Link, useNavigate } from 'react-router-dom';

import {
Button,
HeaderContainer,
Header,
SkipToContent,
HeaderMenuButton,
HeaderGlobalBar,
HeaderGlobalAction,
HeaderPanel,
SideNav,
SideNavItems,
SideNavLink
Expand All @@ -17,6 +17,9 @@ import * as Icons from '@carbon/icons-react';

import { env } from '../../env';

import RightPanelTitle from '../RightPanelTitle';
import MyProfile from '../MyProfile';
import NotificationsCentral from '../NotificationsCentral';
import PanelSectionName from '../PanelSectionName';

import LeftPanelItems from '../../mock-data/LeftPanelItems';
Expand All @@ -38,12 +41,43 @@ const listItems = LeftPanelItems;

const BCHeader = () => {
const version: string = `Version: ${env.REACT_APP_NRSPARWEBAPP_VERSION}`;
const [myProfile, setMyProfile] = React.useState<boolean>(false);
const [notifications, setNotifications] = React.useState<boolean>(false);
const [overlay, setOverlay] = React.useState<boolean>(false);

const navigate = useNavigate();
const handleNotificationsPanel = React.useCallback((): void => {
if (notifications) {
setOverlay(false);
setNotifications(false);
} else {
setOverlay(true);
setNotifications(true);
}
setMyProfile(false);
}, [notifications]);

const handleMyProfilePanel = React.useCallback((): void => {
if (myProfile) {
setOverlay(false);
setMyProfile(false);
} else {
setOverlay(true);
setMyProfile(true);
}
setNotifications(false);
}, [myProfile]);

const goOut = () => {
navigate('/logout');
};
const closeNotificationsPanel = React.useCallback((): void => {
setOverlay(false);
setNotifications(false);
}, []);

const closeMyProfilePanel = React.useCallback((): void => {
setOverlay(false);
setMyProfile(false);
}, []);

const navigate = useNavigate();

return (
<HeaderContainer
Expand All @@ -64,22 +98,45 @@ const BCHeader = () => {
<span className="header-full-name"> Seed Planning and Registry System</span>
</Link>
<HeaderGlobalBar>
<Button
onClick={() => goOut()}
size="sm"
<HeaderGlobalAction
aria-label="Search"
data-testid="header-button__search"
>
Logout
</Button>
<HeaderGlobalAction aria-label="Search" data-testid="header-button__search">
<Icons.Search size={20} />
</HeaderGlobalAction>
<HeaderGlobalAction aria-label="Notifications" data-testid="header-button__notifications">
<HeaderGlobalAction
aria-label="Notifications"
data-testid="header-button__notifications"
onClick={handleNotificationsPanel}
isActive={notifications}
>
<Icons.Notification size={20} />
</HeaderGlobalAction>
<HeaderGlobalAction aria-label="User Settings" tooltipAlignment="end" data-testid="header-button__user">
<HeaderGlobalAction
aria-label="User Settings"
tooltipAlignment="end"
data-testid="header-button__user"
onClick={handleMyProfilePanel}
isActive={myProfile}
>
<Icons.UserAvatar size={20} />
</HeaderGlobalAction>
</HeaderGlobalBar>
<HeaderPanel expanded={notifications} className="notifications-panel">
<RightPanelTitle
title="Notifications"
closeFn={closeNotificationsPanel}
/>
<NotificationsCentral />
</HeaderPanel>
<HeaderPanel expanded={myProfile} className="profile-panel">
<RightPanelTitle
title="My Profile"
closeFn={closeMyProfilePanel}
/>
<MyProfile />
</HeaderPanel>
<div className={overlay ? 'overlay-element active' : 'overlay-element'} />
<SideNav isChildOfHeader expanded={isSideNavExpanded} aria-label="Side menu">
<SideNavItems>
{listItems.map((item: ListItems) => (
Expand Down
54 changes: 50 additions & 4 deletions src/components/BCHeader/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

.spar-header {
overflow: hidden;
border: 0;
}

.header-link {
Expand All @@ -28,21 +29,58 @@ a.header-link:link {
margin-top: auto;
}

button.#{vars.$bcgov-prefix}--header__action {
border-radius: 0;
}

button.#{vars.$bcgov-prefix}--header__action--active {
background: colors.$white;
border: 0;
}

.#{vars.$bcgov-prefix}--header__global .#{vars.$bcgov-prefix}--btn--icon-only.#{vars.$bcgov-prefix}--header__action svg,
button.#{vars.$bcgov-prefix}--header__action svg {
fill: colors.$white;
}

button.#{vars.$bcgov-prefix}--header__action.#{vars.$bcgov-prefix}--header__menu-trigger {
border-radius: 4px;
button.#{vars.$bcgov-prefix}--header__action.#{vars.$bcgov-prefix}--header__action--active > svg,
.#{vars.$bcgov-prefix}--popover-container button.#{vars.$bcgov-prefix}--header__action.#{vars.$bcgov-prefix}--header__action--active > svg {
fill: colors.$gray-100;
}

button.#{vars.$bcgov-prefix}--header__action.#{vars.$bcgov-prefix}--header__menu-trigger:focus {
border-color: colors.$white;
}

button.#{vars.$bcgov-prefix}--header__action.#{vars.$bcgov-prefix}--header__action--active svg {
fill: colors.$gray-100;
.#{vars.$bcgov-prefix}--header-panel--expanded.notifications-panel,
.#{vars.$bcgov-prefix}--header-panel--expanded.profile-panel {
z-index: 10000;
width: 25rem;
height: 100vh;
background-color: colors.$white;
color: colors.$gray-100;
transition: width 0.11s cubic-bezier(0.2, 0, 1, 0.9);
will-change: width;
}

.overlay-element {
position: fixed;
top: 3rem;
left: 0;
width: 0;
height: 0;
background-color: transparent;
opacity: 0;
transition: opacity 300ms cubic-bezier(0.5, 0, 0.1, 1), background-color 300ms cubic-bezier(0.5, 0, 0.1, 1);
}

.overlay-element.active {
z-index: 9000;
width: 100vw;
height: 100vh;
background-color: var(--#{vars.$bcgov-prefix}-overlay);
opacity: 1;
transition: opacity 300ms cubic-bezier(0.5, 0, 0.1, 1), background-color 300ms cubic-bezier(0.5, 0, 0.1, 1);
}

/* Medium - Up to 672px */
Expand All @@ -51,3 +89,11 @@ button.#{vars.$bcgov-prefix}--header__action.#{vars.$bcgov-prefix}--header__acti
display: none;
}
}
/* This rule is specific for the right panel only, it should fill the whole viewport
when the screen hits the panel size or smaller */
@media only screen and (max-width: 400px) {
.#{vars.$bcgov-prefix}--header-panel--expanded.notifications-panel,
.#{vars.$bcgov-prefix}--header-panel--expanded.profile-panel {
width: 100vw;
}
}
72 changes: 72 additions & 0 deletions src/components/MyProfile/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';

import {
SideNavLink
} from '@carbon/react';
import * as Icons from '@carbon/icons-react';

import KeycloakService from '../../service/KeycloakService';

import AvatarImage from '../AvatarImage';
import PanelSectionName from '../PanelSectionName';

import AccountOptions from '../../mock-data/AccountOptions';

import './style.scss';

const MyProfile = () => {
const userData = KeycloakService.getUser();

const navigate = useNavigate();

const goTo = React.useCallback((url: string) => {
navigate(url);
}, []);

const goOut = React.useCallback(() => {
navigate('/logout');
}, []);

return (
<>
<div className="user-info-section">
<div className="user-image">
<AvatarImage userName={`${userData.firstName} ${userData.lastName}`} size="large" />
</div>
<div className="user-data">
<p className="user-name">{`${userData.firstName} ${userData.lastName}`}</p>
<p>{`IDIR: ${userData.idirUsername}`}</p>
<p>{userData.email}</p>
</div>
</div>
<hr className="divisory" />
<nav className="account-nav">
<ul>
<PanelSectionName title="Change account" />
{AccountOptions.map((option) => {
const IconComponent = Icons[option.icon];
return (
<SideNavLink
key={option.header}
renderIcon={IconComponent || ''}
onClick={goTo(option.url)}
>
{option.header}
</SideNavLink>
);
})}
<PanelSectionName title="Options" />
<SideNavLink
renderIcon={Icons.UserFollow}
onClick={goOut}
>
Sign out
</SideNavLink>
</ul>
</nav>
</>
);
};

export default MyProfile;
25 changes: 25 additions & 0 deletions src/components/MyProfile/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@use '../../theme/variables.scss' as vars;

.user-info-section {
padding: 1rem;
display: flex;
}

.user-data {
margin-left: 2rem;
}

.user-name {
font-weight: bold;
}

.divisory {
background-color: var(--#{vars.$bcgov-prefix}-border-subtle-01);
border: 0;
height: 1px;
}

.account-nav a {
width: 100%;
font-weight: bold;
}
7 changes: 7 additions & 0 deletions src/components/NotificationsCentral/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from 'react';

const NotificationsCentral = () => (
<span> Placeholder </span>
);

export default NotificationsCentral;
29 changes: 29 additions & 0 deletions src/components/RightPanelTitle/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';

import { IconButton } from '@carbon/react';
import { Settings, Close } from '@carbon/icons-react';

import './styles.css';

interface RightPanelTitleProps {
title: string;
closeFn: Function;
}

const RightPanelTitle = ({ title, closeFn }: RightPanelTitleProps) => (
<div className="title-section">
<h4>
{title}
</h4>
<div className="title-buttons">
<IconButton kind="ghost" label="Settings" align="bottom">
<Settings />
</IconButton>
<IconButton kind="ghost" label="Close" onClick={closeFn} align="bottom">
<Close />
</IconButton>
</div>
</div>
);

export default RightPanelTitle;
6 changes: 6 additions & 0 deletions src/components/RightPanelTitle/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.title-section {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
}
Loading

0 comments on commit a412c60

Please sign in to comment.