diff --git a/example/index.js b/example/index.js index cfec3546..d89e7344 100644 --- a/example/index.js +++ b/example/index.js @@ -26,6 +26,7 @@ subscribe(APP_READY, () => { authenticatedUser: { userId: '123abc', username: 'testuser', + name: 'test user', roles: [], administrator: false, }, @@ -37,6 +38,7 @@ subscribe(APP_READY, () => { @@ -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} @@ -156,6 +159,7 @@ DesktopHeader.propTypes = { logoDestination: PropTypes.string, avatar: PropTypes.string, username: PropTypes.string, + name: PropTypes.string, loggedIn: PropTypes.bool, // i18n @@ -171,6 +175,7 @@ DesktopHeader.defaultProps = { logoDestination: null, avatar: null, username: null, + name: '', loggedIn: false, }; diff --git a/src/Header.jsx b/src/Header.jsx index d8a05d43..760e6940 100644 --- a/src/Header.jsx +++ b/src/Header.jsx @@ -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'); }); @@ -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, diff --git a/src/Header.test.jsx b/src/Header.test.jsx index ba6ea1b5..9a8dce5c 100644 --- a/src/Header.test.jsx +++ b/src/Header.test.jsx @@ -74,6 +74,7 @@ describe('
', () => { authenticatedUser: { userId: 'abc123', username: 'edX', + name: 'edX', roles: [], administrator: false, }, diff --git a/src/learning-header/AuthenticatedUserDropdown.jsx b/src/learning-header/AuthenticatedUserDropdown.jsx index 73499e8c..4062c8cf 100644 --- a/src/learning-header/AuthenticatedUserDropdown.jsx +++ b/src/learning-header/AuthenticatedUserDropdown.jsx @@ -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); @@ -47,17 +53,25 @@ const AuthenticatedUserDropdown = ({ enterpriseLearnerPortalLink, intl, username careersMenuItem = ''; } + const dropdownToggle = ( + + + {getConfig().HIDE_USERNAME_FROM_HEADER ? ( + + ) : ( + + {username} + + )} + + ); + return ( <> {intl.formatMessage(messages.help)} {showNotificationsTray && } - - - - {username} - - + {dropdownToggle} {dashboardMenuItem} {careersMenuItem} @@ -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); diff --git a/src/learning-header/LearningHeader.jsx b/src/learning-header/LearningHeader.jsx index 2eeb6c04..9b8fa96a 100644 --- a/src/learning-header/LearningHeader.jsx +++ b/src/learning-header/LearningHeader.jsx @@ -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'); }); @@ -92,6 +93,7 @@ const LearningHeader = ({ )} {showUserDropdown && !authenticatedUser && ( diff --git a/src/learning-header/LearningHeader.test.jsx b/src/learning-header/LearningHeader.test.jsx index 3d80888e..c48082a3 100644 --- a/src/learning-header/LearningHeader.test.jsx +++ b/src/learning-header/LearningHeader.test.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import { getConfig, mergeConfig } from '@edx/frontend-platform'; import { authenticatedUser, initializeMockApp, render, screen, } from '../setupTest'; @@ -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(
); + const username = queryByTestId('username'); + expect(username).not.toBeInTheDocument(); + }); + it('displays course data', () => { const courseData = { courseOrg: 'course-org', diff --git a/src/studio-header/HeaderBody.jsx b/src/studio-header/HeaderBody.jsx index af369061..130d0170 100644 --- a/src/studio-header/HeaderBody.jsx +++ b/src/studio-header/HeaderBody.jsx @@ -21,6 +21,7 @@ const HeaderBody = ({ org, title, username, + name, isAdmin, studioBaseUrl, logoutUrl, @@ -100,6 +101,7 @@ const HeaderBody = ({ { expect(avatarIcon).toBeVisible(); }); + it('user menu should not contain username', async () => { + const config = getConfig(); + mergeConfig({ + ...config, + HIDE_USERNAME_FROM_HEADER: true, + }); + const { container } = render(); + 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(); diff --git a/src/studio-header/UserMenu.jsx b/src/studio-header/UserMenu.jsx index 03695c16..28351172 100644 --- a/src/studio-header/UserMenu.jsx +++ b/src/studio-header/UserMenu.jsx @@ -1,6 +1,7 @@ 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'; @@ -8,6 +9,7 @@ import NavDropdownMenu from './NavDropdownMenu'; import getUserMenuItems from './utils'; const UserMenu = ({ + name, username, studioBaseUrl, logoutUrl, @@ -17,22 +19,25 @@ const UserMenu = ({ // injected intl, }) => { + const hideUsername = getConfig().HIDE_USERNAME_FROM_HEADER; + const avatarAlt = hideUsername ? name : username; const avatar = authenticatedUserAvatar ? ( {username} ) : ( ); - const title = isMobile ? avatar : <>{avatar}{username}; + + const title = (isMobile || hideUsername) ? avatar : <>{avatar}{username}; return (