feat: add frontend-plugin-framework slots (#545)

Add the following `frontend-plugin-framework` slots:
* `logo_slot`
* `desktop_main_menu_slot`
* `desktop_secondary_menu_slot`
* `mobile_main_menu_slot`
* `course_info_slot`
* `learning_help_slot`
* `desktop_logged_out_items_slot`
* `mobile_logged_out_items_slot`
* `mobile_user_menu_slot`
* `desktop_user_menu_slot`
* `learning_user_menu_slot`
* `learning_logged_out_items_slot`
* `desktop_header_slot`
This commit is contained in:
Brian Smith
2024-10-22 12:18:11 -04:00
committed by GitHub
parent c64a201072
commit a346dccd4c
79 changed files with 2328 additions and 401 deletions

View File

@@ -3,27 +3,25 @@ import React from 'react';
import { getConfig } from '@edx/frontend-platform';
import { getLoginRedirectUrl } from '@edx/frontend-platform/auth';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Button } from '@openedx/paragon';
import LearningLoggedOutItemsSlot from '../plugin-slots/LearningLoggedOutItemsSlot';
import genericMessages from '../generic/messages';
const AnonymousUserMenu = ({ intl }) => (
<div>
<Button
className="mr-3"
variant="outline-primary"
href={`${getConfig().LMS_BASE_URL}/register?next=${encodeURIComponent(global.location.href)}`}
>
{intl.formatMessage(genericMessages.registerSentenceCase)}
</Button>
<Button
variant="primary"
href={`${getLoginRedirectUrl(global.location.href)}`}
>
{intl.formatMessage(genericMessages.signInSentenceCase)}
</Button>
</div>
);
const AnonymousUserMenu = ({ intl }) => {
const buttonsInfo = [
{
message: intl.formatMessage(genericMessages.registerSentenceCase),
href: `${getConfig().LMS_BASE_URL}/register?next=${encodeURIComponent(global.location.href)}`,
},
{
message: intl.formatMessage(genericMessages.signInSentenceCase),
href: getLoginRedirectUrl(global.location.href),
variant: 'primary',
},
];
return <LearningLoggedOutItemsSlot buttonsInfo={buttonsInfo} />;
};
AnonymousUserMenu.propTypes = {
intl: intlShape.isRequired,

View File

@@ -7,44 +7,46 @@ import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Dropdown } from '@openedx/paragon';
import LearningUserMenuSlot from '../plugin-slots/LearningUserMenuSlot';
import messages from './messages';
const AuthenticatedUserDropdown = ({ intl, username }) => {
const dashboardMenuItem = (
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/dashboard`}>
{intl.formatMessage(messages.dashboard)}
</Dropdown.Item>
);
const dropdownItems = [
{
message: intl.formatMessage(messages.dashboard),
href: `${getConfig().LMS_BASE_URL}/dashboard`,
},
{
message: intl.formatMessage(messages.profile),
href: `${getConfig().ACCOUNT_PROFILE_URL}/u/${username}`,
},
{
message: intl.formatMessage(messages.account),
href: getConfig().ACCOUNT_SETTINGS_URL,
},
...(getConfig().ORDER_HISTORY_URL ? [{
message: intl.formatMessage(messages.orderHistory),
href: getConfig().ORDER_HISTORY_URL,
}] : []),
{
message: intl.formatMessage(messages.signOut),
href: getConfig().LOGOUT_URL,
},
];
return (
<>
<a className="text-gray-700" href={`${getConfig().SUPPORT_URL}`}>{intl.formatMessage(messages.help)}</a>
<Dropdown className="user-dropdown ml-3">
<Dropdown.Toggle variant="outline-primary">
<FontAwesomeIcon icon={faUserCircle} className="d-md-none" size="lg" />
<span data-hj-suppress className="d-none d-md-inline">
{username}
</span>
</Dropdown.Toggle>
<Dropdown.Menu className="dropdown-menu-right">
{dashboardMenuItem}
<Dropdown.Item href={`${getConfig().ACCOUNT_PROFILE_URL}/u/${username}`}>
{intl.formatMessage(messages.profile)}
</Dropdown.Item>
<Dropdown.Item href={getConfig().ACCOUNT_SETTINGS_URL}>
{intl.formatMessage(messages.account)}
</Dropdown.Item>
{ getConfig().ORDER_HISTORY_URL && (
<Dropdown.Item href={getConfig().ORDER_HISTORY_URL}>
{intl.formatMessage(messages.orderHistory)}
</Dropdown.Item>
)}
<Dropdown.Item href={getConfig().LOGOUT_URL}>
{intl.formatMessage(messages.signOut)}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</>
<Dropdown className="user-dropdown ml-3">
<Dropdown.Toggle variant="outline-primary">
<FontAwesomeIcon icon={faUserCircle} className="d-md-none" size="lg" />
<span data-hj-suppress className="d-none d-md-inline">
{username}
</span>
</Dropdown.Toggle>
<Dropdown.Menu className="dropdown-menu-right">
<LearningUserMenuSlot items={dropdownItems} />
</Dropdown.Menu>
</Dropdown>
);
};

View File

@@ -7,7 +7,10 @@ import { AppContext } from '@edx/frontend-platform/react';
import AnonymousUserMenu from './AnonymousUserMenu';
import AuthenticatedUserDropdown from './AuthenticatedUserDropdown';
import LogoSlot from '../plugin-slots/LogoSlot';
import CourseInfoSlot from '../plugin-slots/CourseInfoSlot';
import { courseInfoDataShape } from './LearningHeaderCourseInfo';
import messages from './messages';
import LearningHelpSlot from '../plugin-slots/LearningHelpSlot';
const LearningHeader = ({
courseOrg, courseNumber, courseTitle, intl, showUserDropdown,
@@ -27,14 +30,16 @@ const LearningHeader = ({
<a className="sr-only sr-only-focusable" href="#main-content">{intl.formatMessage(messages.skipNavLink)}</a>
<div className="container-xl py-2 d-flex align-items-center">
{headerLogo}
<div className="flex-grow-1 course-title-lockup" style={{ lineHeight: 1 }}>
<span className="d-block small m-0">{courseOrg} {courseNumber}</span>
<span className="d-block m-0 font-weight-bold course-title">{courseTitle}</span>
<div className="flex-grow-1 course-title-lockup d-flex" style={{ lineHeight: 1 }}>
<CourseInfoSlot courseOrg={courseOrg} courseNumber={courseNumber} courseTitle={courseTitle} />
</div>
{showUserDropdown && authenticatedUser && (
<AuthenticatedUserDropdown
username={authenticatedUser.username}
/>
<>
<LearningHelpSlot />
<AuthenticatedUserDropdown
username={authenticatedUser.username}
/>
</>
)}
{showUserDropdown && !authenticatedUser && (
<AnonymousUserMenu />
@@ -45,9 +50,9 @@ const LearningHeader = ({
};
LearningHeader.propTypes = {
courseOrg: PropTypes.string,
courseNumber: PropTypes.string,
courseTitle: PropTypes.string,
courseOrg: courseInfoDataShape.courseOrg,
courseNumber: courseInfoDataShape.courseNumber,
courseTitle: courseInfoDataShape.courseTitle,
intl: intlShape.isRequired,
showUserDropdown: PropTypes.bool,
};

View File

@@ -0,0 +1,23 @@
import React from 'react';
import PropTypes from 'prop-types';
const LearningHeaderCourseInfo = ({
courseOrg,
courseNumber,
courseTitle,
}) => (
<div style={{ minWidth: 0 }}>
<span className="d-block small m-0">{courseOrg} {courseNumber}</span>
<span className="d-block m-0 font-weight-bold course-title">{courseTitle}</span>
</div>
);
export const courseInfoDataShape = {
courseOrg: PropTypes.string,
courseNumber: PropTypes.string,
courseTitle: PropTypes.string,
};
LearningHeaderCourseInfo.propTypes = courseInfoDataShape;
export default LearningHeaderCourseInfo;

View File

@@ -0,0 +1,14 @@
import React from 'react';
import { getConfig } from '@edx/frontend-platform';
import { useIntl } from '@edx/frontend-platform/i18n';
import messages from './messages';
const LearningHeaderHelpLink = () => {
const intl = useIntl();
return (
<a className="text-gray-700" href={`${getConfig().SUPPORT_URL}`}>{intl.formatMessage(messages.help)}</a>
);
};
export default LearningHeaderHelpLink;

View File

@@ -0,0 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Dropdown } from '@openedx/paragon';
const LearningHeaderUserMenuItems = ({ items }) => items.map((item) => (
<Dropdown.Item href={item.href}>
{item.message}
</Dropdown.Item>
));
export const learningHeaderUserMenuDataShape = {
items: PropTypes.arrayOf(PropTypes.shape({
message: PropTypes.string,
href: PropTypes.string,
})),
};
LearningHeaderUserMenuItems.propTypes = learningHeaderUserMenuDataShape;
export default LearningHeaderUserMenuItems;

View File

@@ -0,0 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button } from '@openedx/paragon';
const LearningLoggedOutButtons = ({ buttonsInfo }) => buttonsInfo.map(buttonInfo => (
<Button
className="ml-3"
variant={buttonInfo.variant ?? 'outline-primary'}
href={buttonInfo.href}
>
{buttonInfo.message}
</Button>
));
export const learningHeaderLoggedOutItemsDataShape = {
buttonsInfo: PropTypes.arrayOf(PropTypes.shape({
message: PropTypes.string,
href: PropTypes.string,
variant: PropTypes.string,
})),
};
LearningLoggedOutButtons.propTypes = learningHeaderLoggedOutItemsDataShape;
export default LearningLoggedOutButtons;