Skip to content

Commit 4cc31ca

Browse files
committed
feat: set username behind flag
username would be show in header bt enabling ENABLE_HEADER_WITHOUT_USERNAME flag. VAN-1804
1 parent 3b2a2bf commit 4cc31ca

14 files changed

+282
-59
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+
* ``ENABLE_HEADER_WITHOUT_USERNAME`` - 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

+78-45
Original file line numberDiff line numberDiff line change
@@ -19,76 +19,50 @@ class DesktopHeader extends React.Component {
1919
super(props);
2020
}
2121

22-
renderMainMenu() {
23-
const { mainMenu } = this.props;
24-
25-
// Nodes are accepted as a prop
26-
if (!Array.isArray(mainMenu)) {
27-
return mainMenu;
28-
}
29-
30-
return mainMenu.map((menuItem) => {
31-
const {
32-
type,
33-
href,
34-
content,
35-
submenuContent,
36-
} = menuItem;
37-
38-
if (type === 'item') {
39-
return (
40-
<a key={`${type}-${content}`} className="nav-link" href={href}>{content}</a>
41-
);
42-
}
43-
44-
return (
45-
<Menu key={`${type}-${content}`} tag="div" className="nav-item" respondToPointerEvents>
46-
<MenuTrigger tag="a" className="nav-link d-inline-flex align-items-center" href={href}>
47-
{content} <CaretIcon role="img" aria-hidden focusable="false" />
48-
</MenuTrigger>
49-
<MenuContent className="pin-left pin-right shadow py-2">
50-
{submenuContent}
51-
</MenuContent>
52-
</Menu>
53-
);
54-
});
55-
}
22+
userMenuWithUsername() {
23+
const {
24+
userMenu,
25+
avatar,
26+
username,
27+
intl,
28+
} = this.props;
5629

57-
// Renders an optional App Menu for
58-
renderAppMenu() {
59-
const { appMenu } = this.props;
60-
const { content: appMenuContent, menuItems } = appMenu;
6130
return (
6231
<Menu transitionClassName="menu-dropdown" transitionTimeout={250}>
63-
<MenuTrigger tag="a" className="nav-link d-inline-flex align-items-center">
64-
{appMenuContent} <CaretIcon role="img" aria-hidden focusable="false" />
32+
<MenuTrigger
33+
tag="button"
34+
aria-label={intl.formatMessage(messages['header.label.account.menu.with.username'], { username })}
35+
className="btn btn-outline-primary d-inline-flex align-items-center pl-2 pr-3"
36+
>
37+
<Avatar size="1.5em" src={avatar} alt="" className="mr-2" />
38+
{username} <CaretIcon role="img" aria-hidden focusable="false" />
6539
</MenuTrigger>
6640
<MenuContent className="mb-0 dropdown-menu show dropdown-menu-right pin-right shadow py-2">
67-
{menuItems.map(({ type, href, content }) => (
41+
{userMenu.map(({ type, href, content }) => (
6842
<a className={`dropdown-${type}`} key={`${type}-${content}`} href={href}>{content}</a>
6943
))}
7044
</MenuContent>
7145
</Menu>
7246
);
7347
}
7448

75-
renderUserMenu() {
49+
userMenuWithoutUsername() {
7650
const {
7751
userMenu,
7852
avatar,
79-
username,
53+
name,
8054
intl,
8155
} = this.props;
8256

8357
return (
8458
<Menu transitionClassName="menu-dropdown" transitionTimeout={250}>
8559
<MenuTrigger
8660
tag="button"
87-
aria-label={intl.formatMessage(messages['header.label.account.menu.for'], { username })}
61+
aria-label={intl.formatMessage(messages['header.label.account.menu.without.username'], { name })}
8862
className="btn btn-outline-primary d-inline-flex align-items-center pl-2 pr-3"
8963
>
9064
<Avatar size="1.5em" src={avatar} alt="" className="mr-2" />
91-
{username} <CaretIcon role="img" aria-hidden focusable="false" />
65+
<CaretIcon role="img" aria-hidden focusable="false" />
9266
</MenuTrigger>
9367
<MenuContent className="mb-0 dropdown-menu show dropdown-menu-right pin-right shadow py-2">
9468
{userMenu.map(({ type, href, content }) => (
@@ -99,6 +73,63 @@ class DesktopHeader extends React.Component {
9973
);
10074
}
10175

76+
renderUserMenu() {
77+
return (getConfig().ENABLE_HEADER_WITHOUT_USERNAME ? this.userMenuWithoutUsername() : this.userMenuWithUsername());
78+
}
79+
80+
// Renders an optional App Menu for
81+
renderAppMenu() {
82+
const { appMenu } = this.props;
83+
const { content: appMenuContent, menuItems } = appMenu;
84+
return (
85+
<Menu transitionClassName="menu-dropdown" transitionTimeout={250}>
86+
<MenuTrigger tag="a" className="nav-link d-inline-flex align-items-center">
87+
{appMenuContent} <CaretIcon role="img" aria-hidden focusable="false" />
88+
</MenuTrigger>
89+
<MenuContent className="mb-0 dropdown-menu show dropdown-menu-right pin-right shadow py-2">
90+
{menuItems.map(({ type, href, content }) => (
91+
<a className={`dropdown-${type}`} key={`${type}-${content}`} href={href}>{content}</a>
92+
))}
93+
</MenuContent>
94+
</Menu>
95+
);
96+
}
97+
98+
renderMainMenu() {
99+
const { mainMenu } = this.props;
100+
101+
// Nodes are accepted as a prop
102+
if (!Array.isArray(mainMenu)) {
103+
return mainMenu;
104+
}
105+
106+
return mainMenu.map((menuItem) => {
107+
const {
108+
type,
109+
href,
110+
content,
111+
submenuContent,
112+
} = menuItem;
113+
114+
if (type === 'item') {
115+
return (
116+
<a key={`${type}-${content}`} className="nav-link" href={href}>{content}</a>
117+
);
118+
}
119+
120+
return (
121+
<Menu key={`${type}-${content}`} tag="div" className="nav-item" respondToPointerEvents>
122+
<MenuTrigger tag="a" className="nav-link d-inline-flex align-items-center" href={href}>
123+
{content} <CaretIcon role="img" aria-hidden focusable="false" />
124+
</MenuTrigger>
125+
<MenuContent className="pin-left pin-right shadow py-2">
126+
{submenuContent}
127+
</MenuContent>
128+
</Menu>
129+
);
130+
});
131+
}
132+
102133
renderLoggedOutItems() {
103134
const { loggedOutItems } = this.props;
104135

@@ -178,6 +209,7 @@ DesktopHeader.propTypes = {
178209
logoDestination: PropTypes.string,
179210
avatar: PropTypes.string,
180211
username: PropTypes.string,
212+
name: PropTypes.string,
181213
loggedIn: PropTypes.bool,
182214

183215
// i18n
@@ -207,6 +239,7 @@ DesktopHeader.defaultProps = {
207239
logoDestination: null,
208240
avatar: null,
209241
username: null,
242+
name: null,
210243
loggedIn: false,
211244
appMenu: null,
212245
};

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+
ENABLE_HEADER_WITHOUT_USERNAME: !!process.env.ENABLE_HEADER_WITHOUT_USERNAME,
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

+7-2
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,16 @@ 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',
79+
'header.label.account.menu.with.username': {
80+
id: 'header.label.account.menu.with.username',
8181
defaultMessage: 'Account menu for {username}',
8282
description: 'The aria label for the account menu trigger when the username is displayed in it',
8383
},
84+
'header.label.account.menu.without.username': {
85+
id: 'header.label.account.without.username',
86+
defaultMessage: 'Account menu for {name}',
87+
description: 'The aria label for the account menu trigger when ENABLE_HEADER_WITHOUT_USERNAME is enabled',
88+
},
8489
'header.label.main.nav': {
8590
id: 'header.label.main.nav',
8691
defaultMessage: 'Main',

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+
ENABLE_HEADER_WITHOUT_USERNAME: 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
},

0 commit comments

Comments
 (0)