Skip to content

Commit

Permalink
feat: remove username from header (#551)
Browse files Browse the repository at this point in the history
Remove username from header and add name for screen reader

VAN-1804
  • Loading branch information
mubbsharanwar committed Feb 19, 2024
1 parent 011b25d commit 1e9af3c
Show file tree
Hide file tree
Showing 11 changed files with 80 additions and 13 deletions.
2 changes: 2 additions & 0 deletions example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ subscribe(APP_READY, () => {
authenticatedUser: {
userId: '123abc',
username: 'testuser',
name: 'test user',
roles: [],
administrator: false,
},
Expand All @@ -37,6 +38,7 @@ subscribe(APP_READY, () => {
<AppContext.Provider value={{
authenticatedUser: {
userId: '123abc',
name: 'test name',
username: 'testuser',
roles: [],
administrator: false,
Expand Down
9 changes: 7 additions & 2 deletions src/DesktopHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,11 @@ class DesktopHeader extends React.Component {
userMenu,
avatar,
username,
name,
intl,
} = this.props;
const hideUsername = getConfig().HIDE_USERNAME_FROM_HEADER;
const usernameOrName = hideUsername ? name : username;

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

<Dropdown.Menu alignRight>
Expand Down Expand Up @@ -156,6 +159,7 @@ DesktopHeader.propTypes = {
logoDestination: PropTypes.string,
avatar: PropTypes.string,
username: PropTypes.string,
name: PropTypes.string,
loggedIn: PropTypes.bool,

// i18n
Expand All @@ -171,6 +175,7 @@ DesktopHeader.defaultProps = {
logoDestination: null,
avatar: null,
username: null,
name: '',
loggedIn: false,
};

Expand Down
4 changes: 4 additions & 0 deletions src/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ 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 @@ -155,6 +158,7 @@ const Header = ({ intl }) => {
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,
avatar: authenticatedUser !== null ? authenticatedUser.avatar : null,
mainMenu: getConfig().MINIMAL_HEADER || getConfig().AUTHN_MINIMAL_HEADER ? [] : mainMenu,
userMenu: getConfig().AUTHN_MINIMAL_HEADER ? [] : userMenu,
Expand Down
1 change: 1 addition & 0 deletions src/Header.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ describe('<Header />', () => {
authenticatedUser: {
userId: 'abc123',
username: 'edX',
name: 'edX',
roles: [],
administrator: false,
},
Expand Down
32 changes: 24 additions & 8 deletions src/learning-header/AuthenticatedUserDropdown.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@ import { faUserCircle } from '@fortawesome/free-solid-svg-icons';

import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Dropdown, Badge } from '@openedx/paragon';
import { Avatar, Dropdown, Badge } from '@openedx/paragon';

import messages from './messages';
import Notifications from '../Notifications';
import { selectShowNotificationTray } from '../Notifications/data/selectors';
import { fetchAppsNotificationCount } from '../Notifications/data/thunks';

const AuthenticatedUserDropdown = ({ enterpriseLearnerPortalLink, intl, username }) => {
const AuthenticatedUserDropdown = (props) => {
const {
intl,
enterpriseLearnerPortalLink,
username,
name,
} = props;
const dispatch = useDispatch();
const showNotificationsTray = useSelector(selectShowNotificationTray);

Expand Down Expand Up @@ -47,17 +53,25 @@ const AuthenticatedUserDropdown = ({ enterpriseLearnerPortalLink, intl, username
careersMenuItem = '';
}

const dropdownToggle = (
<Dropdown.Toggle variant="outline-primary" id="user-dropdown">
<FontAwesomeIcon icon={faUserCircle} className="d-md-none" size="lg" />
{getConfig().HIDE_USERNAME_FROM_HEADER ? (
<Avatar size="sm" alt={name} className="mr-2" />
) : (
<span data-hj-suppress className="d-none d-md-inline" data-testid="username">
{username}
</span>
)}
</Dropdown.Toggle>
);

return (
<>
<a className="text-gray-700" href={`${getConfig().SUPPORT_URL}`}>{intl.formatMessage(messages.help)}</a>
{showNotificationsTray && <Notifications />}
<Dropdown className="user-dropdown ml-3">
<Dropdown.Toggle variant="outline-primary" id="user-dropdown">
<FontAwesomeIcon icon={faUserCircle} className="d-md-none" size="lg" />
<span data-hj-suppress className="d-none d-md-inline">
{username}
</span>
</Dropdown.Toggle>
{dropdownToggle}
<Dropdown.Menu className="dropdown-menu-right zIndex-2">
{dashboardMenuItem}
{careersMenuItem}
Expand Down Expand Up @@ -89,10 +103,12 @@ AuthenticatedUserDropdown.propTypes = {
enterpriseLearnerPortalLink: PropTypes.string,
intl: intlShape.isRequired,
username: PropTypes.string.isRequired,
name: PropTypes.string,
};

AuthenticatedUserDropdown.defaultProps = {
enterpriseLearnerPortalLink: '',
name: '',
};

export default injectIntl(AuthenticatedUserDropdown);
2 changes: 2 additions & 0 deletions src/learning-header/LearningHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ subscribe(APP_CONFIG_INITIALIZED, () => {
mergeConfig({
ACCOUNT_SETTINGS_URL: process.env.ACCOUNT_SETTINGS_URL || '',
NOTIFICATION_FEEDBACK_URL: process.env.NOTIFICATION_FEEDBACK_URL || '',
HIDE_USERNAME_FROM_HEADER: !!process.env.HIDE_USERNAME_FROM_HEADER,
}, 'Learning Header additional config');
});

Expand Down Expand Up @@ -92,6 +93,7 @@ const LearningHeader = ({
<AuthenticatedUserDropdown
enterpriseLearnerPortalLink={enterpriseLearnerPortalLink}
username={authenticatedUser.username}
name={authenticatedUser.name}
/>
)}
{showUserDropdown && !authenticatedUser && (
Expand Down
12 changes: 12 additions & 0 deletions src/learning-header/LearningHeader.test.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { getConfig, mergeConfig } from '@edx/frontend-platform';
import {
authenticatedUser, initializeMockApp, render, screen,
} from '../setupTest';
Expand All @@ -15,6 +16,17 @@ describe('Header', () => {
expect(screen.getByText(authenticatedUser.username)).toBeInTheDocument();
});

it('displays user button without username', () => {
const config = getConfig();
mergeConfig({
...config,
HIDE_USERNAME_FROM_HEADER: true,
});
const { queryByTestId } = render(<Header />);
const username = queryByTestId('username');
expect(username).not.toBeInTheDocument();
});

it('displays course data', () => {
const courseData = {
courseOrg: 'course-org',
Expand Down
4 changes: 4 additions & 0 deletions src/studio-header/HeaderBody.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const HeaderBody = ({
org,
title,
username,
name,
isAdmin,
studioBaseUrl,
logoutUrl,
Expand Down Expand Up @@ -100,6 +101,7 @@ const HeaderBody = ({
<UserMenu
{...{
username,
name,
studioBaseUrl,
logoutUrl,
authenticatedUserAvatar,
Expand All @@ -125,6 +127,7 @@ HeaderBody.propTypes = {
logoAltText: PropTypes.string,
authenticatedUserAvatar: PropTypes.string,
username: PropTypes.string,
name: PropTypes.string,
isAdmin: PropTypes.bool,
isMobile: PropTypes.bool,
isHiddenMainMenu: PropTypes.bool,
Expand All @@ -150,6 +153,7 @@ HeaderBody.defaultProps = {
title: '',
authenticatedUserAvatar: null,
username: null,
name: '',
isAdmin: false,
isMobile: false,
isHiddenMainMenu: false,
Expand Down
1 change: 1 addition & 0 deletions src/studio-header/StudioHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const StudioHeader = ({
org,
title,
username: authenticatedUser?.username,
name: authenticatedUser?.name,
isAdmin: authenticatedUser?.administrator,
authenticatedUserAvatar: authenticatedUser?.avatar,
studioBaseUrl: config.STUDIO_BASE_URL,
Expand Down
13 changes: 13 additions & 0 deletions src/studio-header/StudioHeader.test.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable react/prop-types */
import React, { useMemo } from 'react';
import { getConfig, mergeConfig } from '@edx/frontend-platform';
import {
render,
fireEvent,
Expand All @@ -16,6 +17,7 @@ import messages from './messages';
const authenticatedUser = {
userId: 3,
username: 'abc123',
name: 'test',
administrator: true,
roles: [],
avatar: '/imges/test.png',
Expand Down Expand Up @@ -128,6 +130,17 @@ describe('Header', () => {
expect(avatarIcon).toBeVisible();
});

it('user menu should not contain username', async () => {
const config = getConfig();
mergeConfig({
...config,
HIDE_USERNAME_FROM_HEADER: true,
});
const { container } = render(<RootWrapper {...props} />);
const userMenu = container.querySelector('#user-dropdown-menu');
expect(userMenu.textContent).toContain('');
});

it('should hide nav items if prop isHiddenMainMenu true', async () => {
const initialProps = { ...props, isHiddenMainMenu: true };
const { queryByTestId } = render(<RootWrapper {...initialProps} />);
Expand Down
13 changes: 10 additions & 3 deletions src/studio-header/UserMenu.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
import {
Avatar,
} from '@openedx/paragon';
import NavDropdownMenu from './NavDropdownMenu';
import getUserMenuItems from './utils';

const UserMenu = ({
name,
username,
studioBaseUrl,
logoutUrl,
Expand All @@ -17,22 +19,25 @@ const UserMenu = ({
// injected
intl,
}) => {
const hideUsername = getConfig().HIDE_USERNAME_FROM_HEADER;
const avatarAlt = hideUsername ? name : username;
const avatar = authenticatedUserAvatar ? (
<img
className="d-block w-100 h-100"
src={authenticatedUserAvatar}
alt={username}
alt={avatarAlt}
data-testid="avatar-image"
/>
) : (
<Avatar
size="sm"
className="mr-2"
alt={username}
alt={avatarAlt}
data-testid="avatar-icon"
/>
);
const title = isMobile ? avatar : <>{avatar}{username}</>;

const title = (isMobile || hideUsername) ? avatar : <>{avatar}{username}</>;

return (
<NavDropdownMenu
Expand All @@ -50,6 +55,7 @@ const UserMenu = ({

UserMenu.propTypes = {
username: PropTypes.string,
name: PropTypes.string,
studioBaseUrl: PropTypes.string.isRequired,
logoutUrl: PropTypes.string.isRequired,
authenticatedUserAvatar: PropTypes.string,
Expand All @@ -64,6 +70,7 @@ UserMenu.defaultProps = {
isAdmin: false,
authenticatedUserAvatar: null,
username: null,
name: '',
};

export default injectIntl(UserMenu);

0 comments on commit 1e9af3c

Please sign in to comment.