Skip to content

Commit fcaa2b6

Browse files
committed
feat: set username behind flag
username would be show in header bt enabling ENABLE_HEADER_WITHOUT_USERNAME flag. VAN-1804 fix: address reviewer comments
1 parent 3b2a2bf commit fcaa2b6

14 files changed

+215
-21
lines changed

README.rst

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Environment Variables
4949
* ``ACCOUNT_PROFILE_URL`` - The URL of the account profile page.
5050
* ``ACCOUNT_SETTINGS_URL`` - The URL of the account settings page.
5151
* ``AUTHN_MINIMAL_HEADER`` - A boolean flag which hides the main menu, user menu, and logged-out
52+
* ``HIDE_USERNAME_FROM_HEADER`` - A boolean flag which hides the username from the header
5253
menu items when truthy. This is intended to be used in micro-frontends like
5354
frontend-app-authentication in which these menus are considered distractions from the user's task.
5455

example/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ subscribe(APP_READY, () => {
2626
authenticatedUser: {
2727
userId: '123abc',
2828
username: 'testuser',
29+
name: 'test user',
2930
roles: [],
3031
administrator: false,
3132
},

src/DesktopHeader.jsx

+9-3
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,25 @@ class DesktopHeader extends React.Component {
7474

7575
renderUserMenu() {
7676
const {
77+
intl,
7778
userMenu,
7879
avatar,
80+
name,
7981
username,
80-
intl,
8182
} = this.props;
83+
const hideUsername = getConfig().HIDE_USERNAME_FROM_HEADER;
84+
const usernameOrName = hideUsername ? name : username;
8285

8386
return (
8487
<Menu transitionClassName="menu-dropdown" transitionTimeout={250}>
8588
<MenuTrigger
8689
tag="button"
87-
aria-label={intl.formatMessage(messages['header.label.account.menu.for'], { username })}
90+
aria-label={intl.formatMessage(messages['header.label.account.menu.using.name.or.username'], { usernameOrName })}
8891
className="btn btn-outline-primary d-inline-flex align-items-center pl-2 pr-3"
8992
>
9093
<Avatar size="1.5em" src={avatar} alt="" className="mr-2" />
91-
{username} <CaretIcon role="img" aria-hidden focusable="false" />
94+
{!hideUsername && username}
95+
<CaretIcon role="img" aria-hidden focusable="false" />
9296
</MenuTrigger>
9397
<MenuContent className="mb-0 dropdown-menu show dropdown-menu-right pin-right shadow py-2">
9498
{userMenu.map(({ type, href, content }) => (
@@ -178,6 +182,7 @@ DesktopHeader.propTypes = {
178182
logoDestination: PropTypes.string,
179183
avatar: PropTypes.string,
180184
username: PropTypes.string,
185+
name: PropTypes.string,
181186
loggedIn: PropTypes.bool,
182187

183188
// i18n
@@ -207,6 +212,7 @@ DesktopHeader.defaultProps = {
207212
logoDestination: null,
208213
avatar: null,
209214
username: null,
215+
name: null,
210216
loggedIn: false,
211217
appMenu: null,
212218
};

src/Header.jsx

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ ensureConfig([
2727
subscribe(APP_CONFIG_INITIALIZED, () => {
2828
mergeConfig({
2929
AUTHN_MINIMAL_HEADER: !!process.env.AUTHN_MINIMAL_HEADER,
30+
HIDE_USERNAME_FROM_HEADER: !!process.env.HIDE_USERNAME_FROM_HEADER,
3031
}, 'Header additional config');
3132
});
3233

@@ -94,6 +95,7 @@ const Header = ({ intl }) => {
9495
logoDestination: `${config.LMS_BASE_URL}/dashboard`,
9596
loggedIn: authenticatedUser !== null,
9697
username: authenticatedUser !== null ? authenticatedUser.username : null,
98+
name: authenticatedUser !== null ? authenticatedUser.name : null,
9799
avatar: authenticatedUser !== null ? authenticatedUser.avatar : null,
98100
mainMenu: getConfig().AUTHN_MINIMAL_HEADER ? [] : mainMenu,
99101
userMenu: getConfig().AUTHN_MINIMAL_HEADER ? [] : userMenu,

src/Header.messages.jsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ const messages = defineMessages({
7676
defaultMessage: 'Account Menu',
7777
description: 'The aria label for the account menu trigger',
7878
},
79-
'header.label.account.menu.for': {
80-
id: 'header.label.account.menu.for',
81-
defaultMessage: 'Account menu for {username}',
82-
description: 'The aria label for the account menu trigger when the username is displayed in it',
79+
'header.label.account.menu.using.name.or.username': {
80+
id: 'header.label.account.menu.using.name.or.username',
81+
defaultMessage: 'Account menu for {usernameOrName}',
82+
description: 'The aria label for the account menu trigger when the username is displayed or hide in it',
8383
},
8484
'header.label.main.nav': {
8585
id: 'header.label.main.nav',

src/Header.test.jsx

+27-1
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ describe('<Header />', () => {
3838
expect(wrapper.toJSON()).toMatchSnapshot();
3939
});
4040

41-
it('renders correctly for authenticated desktop', () => {
41+
it('renders correctly for authenticated desktop with username', () => {
4242
const contextValue = {
4343
authenticatedUser: {
4444
userId: 'abc123',
4545
username: 'edX',
46+
name: 'edX',
4647
roles: [],
4748
administrator: false,
4849
},
@@ -61,6 +62,30 @@ describe('<Header />', () => {
6162
expect(wrapper.toJSON()).toMatchSnapshot();
6263
});
6364

65+
it('renders correctly for authenticated desktop without username', () => {
66+
const contextValue = {
67+
authenticatedUser: {
68+
userId: 'abc123',
69+
name: 'edX',
70+
roles: [],
71+
administrator: false,
72+
},
73+
config: {
74+
LMS_BASE_URL: process.env.LMS_BASE_URL,
75+
SITE_NAME: process.env.SITE_NAME,
76+
LOGIN_URL: process.env.LOGIN_URL,
77+
LOGOUT_URL: process.env.LOGOUT_URL,
78+
LOGO_URL: process.env.LOGO_URL,
79+
HIDE_USERNAME_FROM_HEADER: true,
80+
},
81+
};
82+
const component = <HeaderComponent width={{ width: 1280 }} contextValue={contextValue} />;
83+
84+
const wrapper = TestRenderer.create(component);
85+
86+
expect(wrapper.toJSON()).toMatchSnapshot();
87+
});
88+
6489
it('renders correctly for anonymous mobile', () => {
6590
const contextValue = {
6691
authenticatedUser: null,
@@ -84,6 +109,7 @@ describe('<Header />', () => {
84109
authenticatedUser: {
85110
userId: 'abc123',
86111
username: 'edX',
112+
name: 'edX',
87113
roles: [],
88114
administrator: false,
89115
},

src/__snapshots__/Header.test.jsx.snap

+108-2
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ exports[`<Header /> renders correctly for anonymous mobile 1`] = `
196196
</header>
197197
`;
198198

199-
exports[`<Header /> renders correctly for authenticated desktop 1`] = `
199+
exports[`<Header /> renders correctly for authenticated desktop with username 1`] = `
200200
<header
201201
className="site-header-desktop"
202202
>
@@ -281,7 +281,113 @@ exports[`<Header /> renders correctly for authenticated desktop 1`] = `
281281
</svg>
282282
</span>
283283
edX
284-
284+
<svg
285+
aria-hidden={true}
286+
focusable="false"
287+
height="16px"
288+
role="img"
289+
version="1.1"
290+
viewBox="0 0 16 16"
291+
width="16px"
292+
>
293+
<path
294+
d="M7,4 L7,8 L11,8 L11,10 L5,10 L5,4 L7,4 Z"
295+
fill="currentColor"
296+
transform="translate(8.000000, 7.000000) rotate(-45.000000) translate(-8.000000, -7.000000) "
297+
/>
298+
</svg>
299+
</button>
300+
</div>
301+
</nav>
302+
</div>
303+
</div>
304+
</header>
305+
`;
306+
307+
exports[`<Header /> renders correctly for authenticated desktop without username 1`] = `
308+
<header
309+
className="site-header-desktop"
310+
>
311+
<a
312+
className="nav-skip sr-only sr-only-focusable"
313+
href="#main"
314+
>
315+
Skip to main content
316+
</a>
317+
<div
318+
className="container-fluid null"
319+
>
320+
<div
321+
className="nav-container position-relative d-flex align-items-center"
322+
>
323+
<a
324+
className="logo"
325+
href="http://localhost:18000/dashboard"
326+
>
327+
<img
328+
alt="edX"
329+
className="d-block"
330+
src="https://edx-cdn.org/v3/default/logo.svg"
331+
/>
332+
</a>
333+
<nav
334+
aria-label="Main"
335+
className="nav main-nav"
336+
>
337+
<a
338+
className="nav-link"
339+
href="http://localhost:18000/dashboard"
340+
>
341+
Courses
342+
</a>
343+
</nav>
344+
<nav
345+
aria-label="Secondary"
346+
className="nav secondary-menu-container align-items-center ml-auto"
347+
>
348+
<div
349+
className="menu null"
350+
onKeyDown={[Function]}
351+
onMouseEnter={[Function]}
352+
onMouseLeave={[Function]}
353+
>
354+
<button
355+
aria-expanded={false}
356+
aria-haspopup="menu"
357+
aria-label="Account menu for "
358+
className="menu-trigger btn btn-outline-primary d-inline-flex align-items-center pl-2 pr-3"
359+
onClick={[Function]}
360+
>
361+
<span
362+
className="avatar overflow-hidden d-inline-flex rounded-circle mr-2"
363+
style={
364+
Object {
365+
"height": "1.5em",
366+
"width": "1.5em",
367+
}
368+
}
369+
>
370+
<svg
371+
aria-hidden={true}
372+
focusable="false"
373+
height="24px"
374+
role="img"
375+
style={
376+
Object {
377+
"height": "1.5em",
378+
"width": "1.5em",
379+
}
380+
}
381+
version="1.1"
382+
viewBox="0 0 24 24"
383+
width="24px"
384+
>
385+
<path
386+
d="M4.10255106,18.1351061 C4.7170266,16.0581859 8.01891846,14.4720277 12,14.4720277 C15.9810815,14.4720277 19.2829734,16.0581859 19.8974489,18.1351061 C21.215206,16.4412566 22,14.3122775 22,12 C22,6.4771525 17.5228475,2 12,2 C6.4771525,2 2,6.4771525 2,12 C2,14.3122775 2.78479405,16.4412566 4.10255106,18.1351061 Z M12,24 C5.372583,24 0,18.627417 0,12 C0,5.372583 5.372583,0 12,0 C18.627417,0 24,5.372583 24,12 C24,18.627417 18.627417,24 12,24 Z M12,13 C9.790861,13 8,11.209139 8,9 C8,6.790861 9.790861,5 12,5 C14.209139,5 16,6.790861 16,9 C16,11.209139 14.209139,13 12,13 Z"
387+
fill="currentColor"
388+
/>
389+
</svg>
390+
</span>
285391
<svg
286392
aria-hidden={true}
287393
focusable="false"

src/learning-header/AuthenticatedUserDropdown.jsx

+24-8
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,38 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
55
import { faUserCircle } from '@fortawesome/free-solid-svg-icons';
66
import { getConfig } from '@edx/frontend-platform';
77
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
8-
import { Dropdown } from '@openedx/paragon';
8+
import { Avatar, Dropdown } from '@openedx/paragon';
99

1010
import messages from './messages';
1111

12-
const AuthenticatedUserDropdown = ({ intl, username }) => {
12+
const AuthenticatedUserDropdown = ({
13+
intl, username, name,
14+
}) => {
1315
const dashboardMenuItem = (
1416
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/dashboard`}>
1517
{intl.formatMessage(messages.dashboard)}
1618
</Dropdown.Item>
1719
);
1820

21+
// show avatar instead username if HIDE_USERNAME_FROM_HEADER flag is enabled
22+
const dropdownToggle = (
23+
<Dropdown.Toggle variant="outline-primary">
24+
<FontAwesomeIcon icon={faUserCircle} className="d-md-none" size="lg" />
25+
{getConfig().HIDE_USERNAME_FROM_HEADER ? (
26+
<Avatar size="sm" alt={name} className="mr-2" />
27+
) : (
28+
<span data-hj-suppress className="d-none d-md-inline" data-testid="username">
29+
{username}
30+
</span>
31+
)}
32+
</Dropdown.Toggle>
33+
);
34+
1935
return (
2036
<>
2137
<a className="text-gray-700" href={`${getConfig().SUPPORT_URL}`}>{intl.formatMessage(messages.help)}</a>
2238
<Dropdown className="user-dropdown ml-3">
23-
<Dropdown.Toggle variant="outline-primary">
24-
<FontAwesomeIcon icon={faUserCircle} className="d-md-none" size="lg" />
25-
<span data-hj-suppress className="d-none d-md-inline">
26-
{username}
27-
</span>
28-
</Dropdown.Toggle>
39+
{dropdownToggle}
2940
<Dropdown.Menu className="dropdown-menu-right">
3041
{dashboardMenuItem}
3142
<Dropdown.Item href={`${getConfig().ACCOUNT_PROFILE_URL}/u/${username}`}>
@@ -51,6 +62,11 @@ const AuthenticatedUserDropdown = ({ intl, username }) => {
5162
AuthenticatedUserDropdown.propTypes = {
5263
intl: intlShape.isRequired,
5364
username: PropTypes.string.isRequired,
65+
name: PropTypes.string,
66+
};
67+
68+
AuthenticatedUserDropdown.defaultProps = {
69+
name: null,
5470
};
5571

5672
export default injectIntl(AuthenticatedUserDropdown);

src/learning-header/LearningHeader.jsx

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const LearningHeader = ({
5151
{showUserDropdown && authenticatedUser && (
5252
<AuthenticatedUserDropdown
5353
username={authenticatedUser.username}
54+
name={authenticatedUser.name}
5455
/>
5556
)}
5657
{showUserDropdown && !authenticatedUser && (

src/learning-header/LearningHeader.test.jsx

+12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import { getConfig, mergeConfig } from '@edx/frontend-platform';
23
import {
34
authenticatedUser, initializeMockApp, render, screen,
45
} from '../setupTest';
@@ -15,6 +16,17 @@ describe('Header', () => {
1516
expect(screen.getByText(authenticatedUser.username)).toBeInTheDocument();
1617
});
1718

19+
it('displays user button without username', () => {
20+
const config = getConfig();
21+
mergeConfig({
22+
...config,
23+
HIDE_USERNAME_FROM_HEADER: true,
24+
});
25+
const { queryByTestId } = render(<Header />);
26+
const userName = queryByTestId('username');
27+
expect(userName).not.toBeInTheDocument();
28+
});
29+
1830
it('displays course data', () => {
1931
const courseData = {
2032
courseOrg: 'course-org',

src/studio-header/HeaderBody.jsx

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const HeaderBody = ({
2020
number,
2121
org,
2222
title,
23+
name,
2324
username,
2425
isAdmin,
2526
studioBaseUrl,
@@ -99,6 +100,7 @@ const HeaderBody = ({
99100
<Nav>
100101
<UserMenu
101102
{...{
103+
name,
102104
username,
103105
studioBaseUrl,
104106
logoutUrl,
@@ -124,6 +126,7 @@ HeaderBody.propTypes = {
124126
logo: PropTypes.string,
125127
logoAltText: PropTypes.string,
126128
authenticatedUserAvatar: PropTypes.string,
129+
name: PropTypes.string,
127130
username: PropTypes.string,
128131
isAdmin: PropTypes.bool,
129132
isMobile: PropTypes.bool,
@@ -149,6 +152,7 @@ HeaderBody.defaultProps = {
149152
org: '',
150153
title: '',
151154
authenticatedUserAvatar: null,
155+
name: null,
152156
username: null,
153157
isAdmin: false,
154158
isMobile: false,

src/studio-header/StudioHeader.jsx

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const StudioHeader = ({
2525
number,
2626
org,
2727
title,
28+
name: authenticatedUser?.name,
2829
username: authenticatedUser?.username,
2930
isAdmin: authenticatedUser?.administrator,
3031
authenticatedUserAvatar: authenticatedUser?.avatar,

0 commit comments

Comments
 (0)