Skip to content

Commit

Permalink
feat: show user email & name in user menu dropdown (#558)
Browse files Browse the repository at this point in the history
VAN-1851
  • Loading branch information
mubbsharanwar authored Mar 20, 2024
1 parent 2bba7f5 commit 2e76a03
Show file tree
Hide file tree
Showing 18 changed files with 184 additions and 85 deletions.
6 changes: 4 additions & 2 deletions example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ subscribe(APP_READY, () => {
authenticatedUser: {
userId: '123abc',
username: 'testuser',
name: 'test user',
name: 'Test User',
email: '[email protected]',
roles: [],
administrator: false,
},
Expand All @@ -38,8 +39,9 @@ subscribe(APP_READY, () => {
<AppContext.Provider value={{
authenticatedUser: {
userId: '123abc',
name: 'test name',
username: 'testuser',
name: 'Test User',
email: '[email protected]',
roles: [],
administrator: false,
},
Expand Down
24 changes: 13 additions & 11 deletions src/DesktopHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
import { AvatarButton, Dropdown } from '@openedx/paragon';

// Local Components
import { AvatarButton, Dropdown } from '@openedx/paragon';
import UserMenuItem from './common/UserMenuItem';
import { Menu, MenuTrigger, MenuContent } from './Menu';
import { LinkedLogo, Logo } from './Logo';

Expand Down Expand Up @@ -58,12 +59,10 @@ class DesktopHeader extends React.Component {
const {
userMenu,
avatar,
username,
name,
email,
intl,
} = this.props;
const hideUsername = getConfig().HIDE_USERNAME_FROM_HEADER;
const usernameOrName = hideUsername ? name : username;

return (
<Dropdown>
Expand All @@ -72,13 +71,16 @@ class DesktopHeader extends React.Component {
as={AvatarButton}
src={avatar}
alt=""
aria-label={intl.formatMessage(messages['header.label.account.menu.for'], { username: usernameOrName })}
aria-label={intl.formatMessage(messages['header.label.account.menu.for'], { name })}
data-hj-suppress
>
{!hideUsername && username}
</Dropdown.Toggle>

/>
<Dropdown.Menu alignRight>
<Dropdown.Item key="user-info">

This comment has been minimized.

Copy link
@KristinAoki

KristinAoki Mar 20, 2024

Member

@mubbsharanwar is this supposed to appear in every dropdown in the header navigation? I am seeing it in the Content, Settings, and Tools dropdowns as well as the User dropdown (the expected location)
Screenshot 2024-03-20 at 12 08 16 PM

<UserMenuItem
name={name}
email={email}
/>
</Dropdown.Item>
{userMenu.map(({ type, href, content }) => (
<Dropdown.Item key={`${type}-${content}`} href={href}>
{content}
Expand Down Expand Up @@ -158,8 +160,8 @@ DesktopHeader.propTypes = {
logoAltText: PropTypes.string,
logoDestination: PropTypes.string,
avatar: PropTypes.string,
username: PropTypes.string,
name: PropTypes.string,
email: PropTypes.string,
loggedIn: PropTypes.bool,

// i18n
Expand All @@ -174,8 +176,8 @@ DesktopHeader.defaultProps = {
logoAltText: null,
logoDestination: null,
avatar: null,
username: null,
name: '',
email: '',
loggedIn: false,
};

Expand Down
7 changes: 2 additions & 5 deletions src/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ subscribe(APP_CONFIG_INITIALIZED, () => {
MINIMAL_HEADER: !!process.env.MINIMAL_HEADER,
ENTERPRISE_LEARNER_PORTAL_HOSTNAME: process.env.ENTERPRISE_LEARNER_PORTAL_HOSTNAME,
AUTHN_MINIMAL_HEADER: !!process.env.AUTHN_MINIMAL_HEADER,
// this flag is introduced to unblock for new release.
// the flag would be removed in follow up PR
HIDE_USERNAME_FROM_HEADER: !!process.env.HIDE_USERNAME_FROM_HEADER,
}, 'Header additional config');
});

Expand Down Expand Up @@ -157,8 +154,8 @@ const Header = ({ intl }) => {
siteName: 'edX',
logoDestination: getConfig().MINIMAL_HEADER ? null : `${config.LMS_BASE_URL}/dashboard`,
loggedIn: authenticatedUser !== null,
username: authenticatedUser !== null ? authenticatedUser.username : null,
name: authenticatedUser !== null ? authenticatedUser.name : null,
name: authenticatedUser !== null ? authenticatedUser.name : '',
email: authenticatedUser !== null ? authenticatedUser.email : '',
avatar: authenticatedUser !== null ? authenticatedUser.avatar : null,
mainMenu: getConfig().MINIMAL_HEADER || getConfig().AUTHN_MINIMAL_HEADER ? [] : mainMenu,
userMenu: getConfig().AUTHN_MINIMAL_HEADER ? [] : userMenu,
Expand Down
4 changes: 2 additions & 2 deletions src/Header.messages.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ const messages = defineMessages({
},
'header.label.account.menu.for': {
id: 'header.label.account.menu.for',
defaultMessage: 'Account menu for {username}',
description: 'The aria label for the account menu trigger when the username is displayed in it',
defaultMessage: 'Account menu for {name}',
description: 'The aria label for the account menu trigger',
},
'header.label.main.nav': {
id: 'header.label.main.nav',
Expand Down
23 changes: 23 additions & 0 deletions src/Header.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,27 @@ describe('<Header />', () => {
expect(wrapper.toJSON()).toMatchSnapshot();
});

it('displays user menu in dropdown', () => {
const authenticatedUser = {
userId: 'abc123',
username: 'edX',
name: 'edX',
email: '[email protected]',
roles: [],
administrator: false,
};
const contextValue = {
authenticatedUser,
config: APP_CONTEXT_CONFIG,
};
const component = <HeaderContext width={{ width: 1280 }} contextValue={contextValue} />;
const wrapper = render(component);
fireEvent.click(wrapper.container.querySelector('#menu-dropdown'));

expect(screen.getByText(authenticatedUser.name)).toBeInTheDocument();
expect(screen.getByText(authenticatedUser.email)).toBeInTheDocument();
});

it('renders correctly for authenticated users on desktop with or without learner portal links', async () => {
const contextValue = {
authenticatedUser: {
Expand Down Expand Up @@ -143,6 +164,7 @@ describe('<Header />', () => {
authenticatedUser: {
userId: 'abc123',
username: 'edX',
name: 'edX Test',
roles: [],
administrator: false,
},
Expand Down Expand Up @@ -185,6 +207,7 @@ describe('<Header />', () => {
authenticatedUser: {
userId: 'abc123',
username: 'edX',
name: 'edX Test',
roles: [],
administrator: false,
},
Expand Down
22 changes: 15 additions & 7 deletions src/MobileHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { getConfig } from '@edx/frontend-platform';
import { AvatarButton } from '@openedx/paragon';
import { Menu, MenuTrigger, MenuContent } from './Menu';
import { LinkedLogo, Logo } from './Logo';
import UserMenuItem from './common/UserMenuItem';

// i18n
import messages from './Header.messages';
Expand Down Expand Up @@ -57,13 +58,18 @@ class MobileHeader extends React.Component {
}

renderUserMenuItems() {
const { userMenu } = this.props;

return userMenu.map(({ type, href, content }) => (
const { userMenu, name, email } = this.props;
const userInfoItem = (
<li className="nav-item" key="user-info">
<UserMenuItem name={name} email={email} />
</li>
);
const userMenuItems = userMenu.map(({ type, href, content }) => (
<li className="nav-item" key={`${type}-${content}`}>
<a className="nav-link" href={href}>{content}</a>
</li>
));
return [userInfoItem, ...userMenuItems];
}

renderLoggedOutItems() {
Expand All @@ -88,7 +94,7 @@ class MobileHeader extends React.Component {
logoDestination,
loggedIn,
avatar,
username,
name,
stickyOnMobile,
intl,
mainMenu,
Expand Down Expand Up @@ -139,7 +145,7 @@ class MobileHeader extends React.Component {
src={avatar}
showLabel={false}
>
{username}
{name}
</MenuTrigger>
<MenuContent tag="ul" className="nav flex-column pin-left pin-right border-top shadow py-2">
{loggedIn ? this.renderUserMenuItems() : this.renderLoggedOutItems()}
Expand Down Expand Up @@ -172,7 +178,8 @@ MobileHeader.propTypes = {
logoAltText: PropTypes.string,
logoDestination: PropTypes.string,
avatar: PropTypes.string,
username: PropTypes.string,
name: PropTypes.string,
email: PropTypes.string,
loggedIn: PropTypes.bool,
stickyOnMobile: PropTypes.bool,

Expand All @@ -188,7 +195,8 @@ MobileHeader.defaultProps = {
logoAltText: null,
logoDestination: null,
avatar: null,
username: null,
name: '',
email: '',
loggedIn: false,
stickyOnMobile: true,

Expand Down
8 changes: 3 additions & 5 deletions src/__snapshots__/Header.test.jsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ exports[`<Header /> minimal renders correctly for authenticated users when minim
alt=""
aria-expanded={false}
aria-haspopup={true}
aria-label="Account menu for edX"
aria-label="Account menu for edX Test"
className="btn-avatar pgn__avatar-button-avatar pgn__avatar-button-avatar-md dropdown-toggle btn btn-tertiary btn-md"
data-hj-suppress={true}
disabled={false}
Expand All @@ -50,7 +50,6 @@ exports[`<Header /> minimal renders correctly for authenticated users when minim
className="pgn__avatar pgn__avatar-sm"
src="icon/mock/path"
/>
edX
</button>
</div>
</nav>
Expand Down Expand Up @@ -180,7 +179,6 @@ exports[`<Header /> renders correctly for authenticated users on desktop 1`] = `
className="pgn__avatar pgn__avatar-sm"
src="icon/mock/path"
/>
edX
</button>
</div>
</nav>
Expand Down Expand Up @@ -293,7 +291,7 @@ exports[`<Header /> renders correctly for authenticated users on mobile 1`] = `
type="button"
>
<img
alt="edX"
alt="edX Test"
className="pgn__avatar pgn__avatar-sm"
src="icon/mock/path"
/>
Expand Down Expand Up @@ -478,7 +476,7 @@ exports[`<Header /> renders correctly for unauthenticated users on mobile 1`] =
type="button"
>
<img
alt={null}
alt=""
className="pgn__avatar pgn__avatar-sm"
src="icon/mock/path"
/>
Expand Down
34 changes: 34 additions & 0 deletions src/common/UserMenuItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import PropTypes from 'prop-types';

import { Avatar } from '@openedx/paragon';
import { injectIntl } from '@edx/frontend-platform/i18n';

import './style.scss';

const UserMenuItem = ({ name, email }) => (
<>
<Avatar
size="sm"
className="mr-2"
alt={name}
data-testid="avatar-icon"
/>
<div className="text-left">
{name && <span className="h5 d-block">{name}</span>}
{email && <span className="small d-block">{email}</span>}
</div>
</>
);

UserMenuItem.propTypes = {
name: PropTypes.string,
email: PropTypes.string,
};

UserMenuItem.defaultProps = {
name: '',
email: '',
};

export default injectIntl(UserMenuItem);
18 changes: 18 additions & 0 deletions src/common/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.dropdown-menu a:first-child {
pointer-events: none;
}

@media screen and (max-width: 768px) {
.menu-content li:first-child {
display: flex;
align-items: center;
padding: 0 16px;
border-bottom: 1px solid #70828E;

a {
display: flex;
align-items: center;
border-bottom: 1px solid #70828E !important;
}
}
}
5 changes: 5 additions & 0 deletions src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,8 @@ $white: #fff;
border-radius: $rounded-pill;
}
}

// bottom boarder of first child of user dropdown menu
.dropdown-menu a:first-child {
border-bottom: 1px solid #70828E !important;
}
Loading

0 comments on commit 2e76a03

Please sign in to comment.