Skip to content

Commit

Permalink
refactor: make studio header more flexible
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Smith committed Oct 19, 2022
1 parent d529e00 commit cf60b93
Showing 1 changed file with 129 additions and 29 deletions.
158 changes: 129 additions & 29 deletions src/StudioHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ import { AppContext } from '@edx/frontend-platform/react';
import {
APP_CONFIG_INITIALIZED,
ensureConfig,
getConfig,
mergeConfig,
subscribe,
} from '@edx/frontend-platform';
import { ActionRow } from '@edx/paragon';

import DesktopHeader from './DesktopHeader';
import { Menu, MenuTrigger, MenuContent } from './Menu';
import Avatar from './Avatar';
import { LinkedLogo, Logo } from './Logo';

import { CaretIcon } from './Icons';

import messages from './Header.messages';

Expand All @@ -28,7 +34,124 @@ subscribe(APP_CONFIG_INITIALIZED, () => {
}, 'StudioHeader additional config');
});

function StudioHeader({ intl, mainMenu, appMenu }) {
class StudioDesktopHeaderBase extends React.Component {
constructor(props) { // eslint-disable-line no-useless-constructor
super(props);
}

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

return (
<Menu transitionClassName="menu-dropdown" transitionTimeout={250}>
<MenuTrigger
tag="button"
aria-label={intl.formatMessage(messages['header.label.account.menu.for'], { 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">
{userMenu.map(({ type, href, content }) => (
<a className={`dropdown-${type}`} key={`${type}-${content}`} href={href}>{content}</a>
))}
</MenuContent>
</Menu>
);
}

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

return loggedOutItems.map((item, i, arr) => (
<a
key={`${item.type}-${item.content}`}
className={i < arr.length - 1 ? 'btn mr-2 btn-link' : 'btn mr-2 btn-outline-primary'}
href={item.href}
>
{item.content}
</a>
));
}

render() {
const {
logo,
logoAltText,
logoDestination,
loggedIn,
intl,
actionRowContent,
} = this.props;
const logoProps = { src: logo, alt: logoAltText, href: logoDestination };
const logoClasses = getConfig().AUTHN_MINIMAL_HEADER ? 'mw-100' : null;

return (
<header className="site-header-desktop">
<a className="nav-skip sr-only sr-only-focusable" href="#main">{intl.formatMessage(messages['header.label.skip.nav'])}</a>
<div className={`container-fluid ${logoClasses}`}>
<div className="nav-container position-relative d-flex align-items-center">
{logoDestination === null ? <Logo className="logo" src={logo} alt={logoAltText} /> : <LinkedLogo className="logo" {...logoProps} />}
<ActionRow>
{actionRowContent}
<nav
aria-label={intl.formatMessage(messages['header.label.secondary.nav'])}
className="nav secondary-menu-container align-items-center ml-auto"
>
{loggedIn ? this.renderUserMenu() : this.renderLoggedOutItems()}
</nav>
</ActionRow>
</div>
</div>
</header>
);
}
}

StudioDesktopHeaderBase.propTypes = {
userMenu: PropTypes.arrayOf(PropTypes.shape({
type: PropTypes.oneOf(['item', 'menu']),
href: PropTypes.string,
content: PropTypes.string,
})),
loggedOutItems: PropTypes.arrayOf(PropTypes.shape({
type: PropTypes.oneOf(['item', 'menu']),
href: PropTypes.string,
content: PropTypes.string,
})),
logo: PropTypes.string,
logoAltText: PropTypes.string,
logoDestination: PropTypes.string,
avatar: PropTypes.string,
username: PropTypes.string,
loggedIn: PropTypes.bool,
actionRowContent: PropTypes.element,

// i18n
intl: intlShape.isRequired,
};

StudioDesktopHeaderBase.defaultProps = {
userMenu: [],
loggedOutItems: [],
logo: null,
logoAltText: null,
logoDestination: null,
avatar: null,
username: null,
loggedIn: false,
actionRowContent: null,
};

const StudioDesktopHeader = injectIntl(StudioDesktopHeaderBase);

function StudioHeader({ intl, actionRowContent }) {
const { authenticatedUser, config } = useContext(AppContext);

const userMenu = authenticatedUser === null ? [] : [
Expand Down Expand Up @@ -56,44 +179,21 @@ function StudioHeader({ intl, mainMenu, appMenu }) {
loggedIn: authenticatedUser !== null,
username: authenticatedUser !== null ? authenticatedUser.username : null,
avatar: authenticatedUser !== null ? authenticatedUser.avatar : null,
mainMenu,
actionRowContent,
userMenu,
appMenu,
loggedOutItems: [],
};

return <DesktopHeader {...props} />;
return <StudioDesktopHeader {...props} />;
}

StudioHeader.propTypes = {
intl: intlShape.isRequired,
appMenu: PropTypes.shape(
{
content: PropTypes.string,
href: PropTypes.string,
menuItems: PropTypes.arrayOf(
PropTypes.shape({
type: PropTypes.string,
href: PropTypes.string,
content: PropTypes.string,
}),
),
},
),
mainMenu: PropTypes.arrayOf(
PropTypes.shape(
{
type: PropTypes.string,
href: PropTypes.string,
content: PropTypes.string,
},
),
),
actionRowContent: PropTypes.element,
};

StudioHeader.defaultProps = {
appMenu: null,
mainMenu: [],
actionRowContent: <></>,
};

export default injectIntl(StudioHeader);

0 comments on commit cf60b93

Please sign in to comment.