Skip to content

Commit

Permalink
feat: set username behind flag
Browse files Browse the repository at this point in the history
username would be show in header bt enabling ENABLE_HEADER_WITHOUT_USERNAME flag.

VAN-1804
  • Loading branch information
mubbsharanwar committed Feb 7, 2024
1 parent 3b2a2bf commit 4cc31ca
Show file tree
Hide file tree
Showing 14 changed files with 282 additions and 59 deletions.
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Environment Variables
* ``ACCOUNT_PROFILE_URL`` - The URL of the account profile page.
* ``ACCOUNT_SETTINGS_URL`` - The URL of the account settings page.
* ``AUTHN_MINIMAL_HEADER`` - A boolean flag which hides the main menu, user menu, and logged-out
* ``ENABLE_HEADER_WITHOUT_USERNAME`` - A boolean flag which hides the username from the header
menu items when truthy. This is intended to be used in micro-frontends like
frontend-app-authentication in which these menus are considered distractions from the user's task.

Expand Down
1 change: 1 addition & 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 Down
123 changes: 78 additions & 45 deletions src/DesktopHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,76 +19,50 @@ class DesktopHeader extends React.Component {
super(props);
}

renderMainMenu() {
const { mainMenu } = this.props;

// Nodes are accepted as a prop
if (!Array.isArray(mainMenu)) {
return mainMenu;
}

return mainMenu.map((menuItem) => {
const {
type,
href,
content,
submenuContent,
} = menuItem;

if (type === 'item') {
return (
<a key={`${type}-${content}`} className="nav-link" href={href}>{content}</a>
);
}

return (
<Menu key={`${type}-${content}`} tag="div" className="nav-item" respondToPointerEvents>
<MenuTrigger tag="a" className="nav-link d-inline-flex align-items-center" href={href}>
{content} <CaretIcon role="img" aria-hidden focusable="false" />
</MenuTrigger>
<MenuContent className="pin-left pin-right shadow py-2">
{submenuContent}
</MenuContent>
</Menu>
);
});
}
userMenuWithUsername() {
const {
userMenu,
avatar,
username,
intl,
} = this.props;

// Renders an optional App Menu for
renderAppMenu() {
const { appMenu } = this.props;
const { content: appMenuContent, menuItems } = appMenu;
return (
<Menu transitionClassName="menu-dropdown" transitionTimeout={250}>
<MenuTrigger tag="a" className="nav-link d-inline-flex align-items-center">
{appMenuContent} <CaretIcon role="img" aria-hidden focusable="false" />
<MenuTrigger
tag="button"
aria-label={intl.formatMessage(messages['header.label.account.menu.with.username'], { username })}
className="btn btn-outline-primary d-inline-flex align-items-center pl-2 pr-3"
>
<Avatar size="1.5em" src={avatar} alt="" className="mr-2" />
{username} <CaretIcon role="img" aria-hidden focusable="false" />
</MenuTrigger>
<MenuContent className="mb-0 dropdown-menu show dropdown-menu-right pin-right shadow py-2">
{menuItems.map(({ type, href, content }) => (
{userMenu.map(({ type, href, content }) => (
<a className={`dropdown-${type}`} key={`${type}-${content}`} href={href}>{content}</a>
))}
</MenuContent>
</Menu>
);
}

renderUserMenu() {
userMenuWithoutUsername() {
const {
userMenu,
avatar,
username,
name,
intl,
} = this.props;

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

renderUserMenu() {
return (getConfig().ENABLE_HEADER_WITHOUT_USERNAME ? this.userMenuWithoutUsername() : this.userMenuWithUsername());
}

// Renders an optional App Menu for
renderAppMenu() {
const { appMenu } = this.props;
const { content: appMenuContent, menuItems } = appMenu;
return (
<Menu transitionClassName="menu-dropdown" transitionTimeout={250}>
<MenuTrigger tag="a" className="nav-link d-inline-flex align-items-center">
{appMenuContent} <CaretIcon role="img" aria-hidden focusable="false" />
</MenuTrigger>
<MenuContent className="mb-0 dropdown-menu show dropdown-menu-right pin-right shadow py-2">
{menuItems.map(({ type, href, content }) => (
<a className={`dropdown-${type}`} key={`${type}-${content}`} href={href}>{content}</a>
))}
</MenuContent>
</Menu>
);
}

renderMainMenu() {
const { mainMenu } = this.props;

// Nodes are accepted as a prop
if (!Array.isArray(mainMenu)) {
return mainMenu;
}

return mainMenu.map((menuItem) => {
const {
type,
href,
content,
submenuContent,
} = menuItem;

if (type === 'item') {
return (
<a key={`${type}-${content}`} className="nav-link" href={href}>{content}</a>
);
}

return (
<Menu key={`${type}-${content}`} tag="div" className="nav-item" respondToPointerEvents>
<MenuTrigger tag="a" className="nav-link d-inline-flex align-items-center" href={href}>
{content} <CaretIcon role="img" aria-hidden focusable="false" />
</MenuTrigger>
<MenuContent className="pin-left pin-right shadow py-2">
{submenuContent}
</MenuContent>
</Menu>
);
});
}

renderLoggedOutItems() {
const { loggedOutItems } = this.props;

Expand Down Expand Up @@ -178,6 +209,7 @@ DesktopHeader.propTypes = {
logoDestination: PropTypes.string,
avatar: PropTypes.string,
username: PropTypes.string,
name: PropTypes.string,
loggedIn: PropTypes.bool,

// i18n
Expand Down Expand Up @@ -207,6 +239,7 @@ DesktopHeader.defaultProps = {
logoDestination: null,
avatar: null,
username: null,
name: null,
loggedIn: false,
appMenu: null,
};
Expand Down
2 changes: 2 additions & 0 deletions src/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ ensureConfig([
subscribe(APP_CONFIG_INITIALIZED, () => {
mergeConfig({
AUTHN_MINIMAL_HEADER: !!process.env.AUTHN_MINIMAL_HEADER,
ENABLE_HEADER_WITHOUT_USERNAME: !!process.env.ENABLE_HEADER_WITHOUT_USERNAME,
}, 'Header additional config');
});

Expand Down Expand Up @@ -94,6 +95,7 @@ const Header = ({ intl }) => {
logoDestination: `${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().AUTHN_MINIMAL_HEADER ? [] : mainMenu,
userMenu: getConfig().AUTHN_MINIMAL_HEADER ? [] : userMenu,
Expand Down
9 changes: 7 additions & 2 deletions src/Header.messages.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,16 @@ const messages = defineMessages({
defaultMessage: 'Account Menu',
description: 'The aria label for the account menu trigger',
},
'header.label.account.menu.for': {
id: 'header.label.account.menu.for',
'header.label.account.menu.with.username': {
id: 'header.label.account.menu.with.username',
defaultMessage: 'Account menu for {username}',
description: 'The aria label for the account menu trigger when the username is displayed in it',
},
'header.label.account.menu.without.username': {
id: 'header.label.account.without.username',
defaultMessage: 'Account menu for {name}',
description: 'The aria label for the account menu trigger when ENABLE_HEADER_WITHOUT_USERNAME is enabled',
},
'header.label.main.nav': {
id: 'header.label.main.nav',
defaultMessage: 'Main',
Expand Down
28 changes: 27 additions & 1 deletion src/Header.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ describe('<Header />', () => {
expect(wrapper.toJSON()).toMatchSnapshot();
});

it('renders correctly for authenticated desktop', () => {
it('renders correctly for authenticated desktop with username', () => {
const contextValue = {
authenticatedUser: {
userId: 'abc123',
username: 'edX',
name: 'edX',
roles: [],
administrator: false,
},
Expand All @@ -61,6 +62,30 @@ describe('<Header />', () => {
expect(wrapper.toJSON()).toMatchSnapshot();
});

it('renders correctly for authenticated desktop without username', () => {
const contextValue = {
authenticatedUser: {
userId: 'abc123',
name: 'edX',
roles: [],
administrator: false,
},
config: {
LMS_BASE_URL: process.env.LMS_BASE_URL,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
LOGO_URL: process.env.LOGO_URL,
ENABLE_HEADER_WITHOUT_USERNAME: true,
},
};
const component = <HeaderComponent width={{ width: 1280 }} contextValue={contextValue} />;

const wrapper = TestRenderer.create(component);

expect(wrapper.toJSON()).toMatchSnapshot();
});

it('renders correctly for anonymous mobile', () => {
const contextValue = {
authenticatedUser: null,
Expand All @@ -84,6 +109,7 @@ describe('<Header />', () => {
authenticatedUser: {
userId: 'abc123',
username: 'edX',
name: 'edX',
roles: [],
administrator: false,
},
Expand Down
Loading

0 comments on commit 4cc31ca

Please sign in to comment.