diff --git a/src/DesktopHeader.jsx b/src/DesktopHeader.jsx deleted file mode 100644 index 10c2a7161..000000000 --- a/src/DesktopHeader.jsx +++ /dev/null @@ -1,222 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import { getConfig } from '@edx/frontend-platform'; - -// Local Components -import { Menu, MenuTrigger, MenuContent } from './Menu'; -import Avatar from './Avatar'; -import LogoSlot from './plugin-slots/LogoSlot'; - -// i18n -import messages from './Header.messages'; - -// Assets -import { CaretIcon } from './Icons'; - -class DesktopHeader extends React.Component { - constructor(props) { // eslint-disable-line no-useless-constructor - super(props); - } - - renderMenu(menu) { - // Nodes are accepted as a prop - if (!Array.isArray(menu)) { - return menu; - } - - return menu.map((menuItem) => { - const { - type, - href, - content, - submenuContent, - disabled, - isActive, - onClick, - } = menuItem; - - if (type === 'item') { - return ( - - {content} - - ); - } - - return ( - - - {content} - - - {submenuContent} - - - ); - }); - } - - renderMainMenu() { - const { mainMenu } = this.props; - return this.renderMenu(mainMenu); - } - - renderSecondaryMenu() { - const { secondaryMenu } = this.props; - return this.renderMenu(secondaryMenu); - } - - renderUserMenu() { - const { - userMenu, - avatar, - username, - intl, - } = this.props; - - return ( - - - - {username} - - - {userMenu.map((group, index) => ( - // eslint-disable-next-line react/jsx-no-comment-textnodes,react/no-array-index-key - - {group.heading &&
{group.heading}
} - {group.items.map(({ - type, content, href, disabled, isActive, onClick, - }) => ( - - {content} - - ))} - {index < userMenu.length - 1 &&
} - - ))} - -
- ); - } - - renderLoggedOutItems() { - const { loggedOutItems } = this.props; - - return loggedOutItems.map((item, i, arr) => ( - - {item.content} - - )); - } - - render() { - const { - logo, - logoAltText, - logoDestination, - loggedIn, - intl, - } = this.props; - const logoProps = { src: logo, alt: logoAltText, href: logoDestination }; - const logoClasses = getConfig().AUTHN_MINIMAL_HEADER ? 'mw-100' : null; - - return ( -
- {intl.formatMessage(messages['header.label.skip.nav'])} -
-
- - - -
-
-
- ); - } -} - -DesktopHeader.propTypes = { - mainMenu: PropTypes.oneOfType([ - PropTypes.node, - PropTypes.array, - ]), - secondaryMenu: PropTypes.oneOfType([ - PropTypes.node, - PropTypes.array, - ]), - userMenu: PropTypes.arrayOf(PropTypes.shape({ - heading: PropTypes.string, - items: PropTypes.arrayOf(PropTypes.shape({ - type: PropTypes.oneOf(['item', 'menu']), - href: PropTypes.string, - content: PropTypes.string, - isActive: PropTypes.bool, - onClick: PropTypes.func, - })), - })), - 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, - - // i18n - intl: intlShape.isRequired, -}; - -DesktopHeader.defaultProps = { - mainMenu: [], - secondaryMenu: [], - userMenu: [], - loggedOutItems: [], - logo: null, - logoAltText: null, - logoDestination: null, - avatar: null, - username: null, - loggedIn: false, -}; - -export default injectIntl(DesktopHeader); diff --git a/src/Header.jsx b/src/Header.jsx index bff801fe0..2f190f205 100644 --- a/src/Header.jsx +++ b/src/Header.jsx @@ -11,8 +11,8 @@ import { } from '@edx/frontend-platform'; import PropTypes from 'prop-types'; -import DesktopHeader from './DesktopHeader'; -import MobileHeader from './MobileHeader'; +import DesktopHeaderSlot from './plugin-slots/DesktopHeaderSlot'; +import MobileHeaderSlot from './plugin-slots/MobileHeaderSlot'; import messages from './Header.messages'; @@ -123,10 +123,10 @@ const Header = ({ return ( <> - + - + ); diff --git a/src/Logo.jsx b/src/Logo.jsx index 2353eb3b8..08f832ffe 100644 --- a/src/Logo.jsx +++ b/src/Logo.jsx @@ -12,10 +12,12 @@ const Logo = ({ ); -Logo.propTypes = { +export const logoDataShape = { href: PropTypes.string.isRequired, src: PropTypes.string.isRequired, alt: PropTypes.string.isRequired, }; +Logo.propTypes = logoDataShape; + export default Logo; diff --git a/src/desktop-header/DesktopHeader.jsx b/src/desktop-header/DesktopHeader.jsx new file mode 100644 index 000000000..4b5e4939f --- /dev/null +++ b/src/desktop-header/DesktopHeader.jsx @@ -0,0 +1,153 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import { getConfig } from '@edx/frontend-platform'; + +// Local Components +import { Menu, MenuTrigger, MenuContent } from '../Menu'; +import Avatar from '../Avatar'; +import LogoSlot from '../plugin-slots/LogoSlot'; +import DesktopLoggedOutItemsSlot from '../plugin-slots/DesktopLoggedOutItemsSlot'; +import { desktopLoggedOutItemsDataShape } from './DesktopLoggedOutItems'; +import DesktopMainMenuSlot from '../plugin-slots/DesktopMainMenuSlot'; +import { desktopHeaderMainOrSecondaryMenuDataShape } from './DesktopHeaderMainOrSecondaryMenu'; +import DesktopSecondaryMenuSlot from '../plugin-slots/DesktopSecondaryMenuSlot'; +import DesktopUserMenuSlot from '../plugin-slots/DesktopUserMenuSlot'; +import { desktopUserMenuDataShape } from './DesktopHeaderUserMenu'; + +// i18n +import messages from '../Header.messages'; + +// Assets +import { CaretIcon } from '../Icons'; + +class DesktopHeader extends React.Component { + constructor(props) { // eslint-disable-line no-useless-constructor + super(props); + } + + renderMainMenu() { + const { mainMenu } = this.props; + return ; + } + + renderSecondaryMenu() { + const { secondaryMenu } = this.props; + return ; + } + + renderUserMenu() { + const { + userMenu, + avatar, + username, + intl, + } = this.props; + + return ( + + + + {username} + + + + + + ); + } + + renderLoggedOutItems() { + const { loggedOutItems } = this.props; + return ; + } + + render() { + const { + logo, + logoAltText, + logoDestination, + loggedIn, + intl, + } = this.props; + const logoProps = { src: logo, alt: logoAltText, href: logoDestination }; + const logoClasses = getConfig().AUTHN_MINIMAL_HEADER ? 'mw-100' : null; + + return ( +
+ {intl.formatMessage(messages['header.label.skip.nav'])} +
+
+ + + +
+
+
+ ); + } +} + +export const desktopHeaderDataShape = { + mainMenu: desktopHeaderMainOrSecondaryMenuDataShape, + secondaryMenu: desktopHeaderMainOrSecondaryMenuDataShape, + userMenu: desktopUserMenuDataShape, + loggedOutItems: desktopLoggedOutItemsDataShape, + logo: PropTypes.string, + logoAltText: PropTypes.string, + logoDestination: PropTypes.string, + avatar: PropTypes.string, + username: PropTypes.string, + loggedIn: PropTypes.bool, +}; + +DesktopHeader.propTypes = { + mainMenu: desktopHeaderDataShape.mainMenu, + secondaryMenu: desktopHeaderDataShape.secondaryMenumainMenu, + userMenu: desktopHeaderDataShape.userMenumainMenu, + loggedOutItems: desktopHeaderDataShape.loggedOutItemsmainMenu, + logo: desktopHeaderDataShape.logomainMenu, + logoAltText: desktopHeaderDataShape.logoAltTextmainMenu, + logoDestination: desktopHeaderDataShape.logoDestinationmainMenu, + avatar: desktopHeaderDataShape.avatarmainMenu, + username: desktopHeaderDataShape.usernamemainMenu, + loggedIn: desktopHeaderDataShape.loggedInmainMenu, + + // i18n + intl: intlShape.isRequired, +}; + +DesktopHeader.defaultProps = { + mainMenu: [], + secondaryMenu: [], + userMenu: [], + loggedOutItems: [], + logo: null, + logoAltText: null, + logoDestination: null, + avatar: null, + username: null, + loggedIn: false, +}; + +export default injectIntl(DesktopHeader); diff --git a/src/desktop-header/DesktopHeaderMainOrSecondaryMenu.jsx b/src/desktop-header/DesktopHeaderMainOrSecondaryMenu.jsx new file mode 100644 index 000000000..d3d4d51fe --- /dev/null +++ b/src/desktop-header/DesktopHeaderMainOrSecondaryMenu.jsx @@ -0,0 +1,59 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { Menu, MenuTrigger, MenuContent } from '../Menu'; +import { CaretIcon } from '../Icons'; + +const DesktopHeaderMainOrSecondaryMenu = ({ menu }) => { + // Nodes are accepted as a prop + if (!Array.isArray(menu)) { + return menu; + } + + return menu.map((menuItem) => { + const { + type, + href, + content, + submenuContent, + disabled, + isActive, + onClick, + } = menuItem; + + if (type === 'item') { + return ( + + {content} + + ); + } + + return ( + + + {content} + + + {submenuContent} + + + ); + }); +}; + +export const desktopHeaderMainOrSecondaryMenuDataShape = PropTypes.oneOfType([ + PropTypes.node, + PropTypes.array, +]); + +DesktopHeaderMainOrSecondaryMenu.propTypes = { + menu: desktopHeaderMainOrSecondaryMenuDataShape, +}; + +export default DesktopHeaderMainOrSecondaryMenu; diff --git a/src/desktop-header/DesktopHeaderUserMenu.jsx b/src/desktop-header/DesktopHeaderUserMenu.jsx new file mode 100644 index 000000000..b273696a8 --- /dev/null +++ b/src/desktop-header/DesktopHeaderUserMenu.jsx @@ -0,0 +1,39 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const DesktopHeaderUserMenu = ({ menu }) => menu.map((group, index) => ( + // eslint-disable-next-line react/jsx-no-comment-textnodes,react/no-array-index-key + + {group.heading &&
{group.heading}
} + {group.items.map(({ + type, content, href, disabled, isActive, onClick, + }) => ( + + {content} + + ))} + {index < menu.length - 1 &&
} + +)); + +export const desktopUserMenuDataShape = PropTypes.arrayOf(PropTypes.shape({ + heading: PropTypes.string, + items: PropTypes.arrayOf(PropTypes.shape({ + type: PropTypes.oneOf(['item', 'menu']), + href: PropTypes.string, + content: PropTypes.string, + isActive: PropTypes.bool, + onClick: PropTypes.func, + })), +})); + +DesktopHeaderUserMenu.propTypes = { + menu: desktopUserMenuDataShape, +}; + +export default DesktopHeaderUserMenu; diff --git a/src/desktop-header/DesktopLoggedOutItems.jsx b/src/desktop-header/DesktopLoggedOutItems.jsx new file mode 100644 index 000000000..a4cd0622d --- /dev/null +++ b/src/desktop-header/DesktopLoggedOutItems.jsx @@ -0,0 +1,24 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const DesktopLoggedOutItems = ({ items }) => items.map((item, i, arr) => ( + + {item.content} + +)); + +export const desktopLoggedOutItemsDataShape = PropTypes.arrayOf(PropTypes.shape({ + type: PropTypes.oneOf(['item', 'menu']), + href: PropTypes.string, + content: PropTypes.string, +})); + +DesktopLoggedOutItems.propTypes = { + items: desktopLoggedOutItemsDataShape, +}; + +export default DesktopLoggedOutItems; diff --git a/src/learning-header/AnonymousUserMenu.jsx b/src/learning-header/AnonymousUserMenu.jsx index 74c3f3e21..d98593c6b 100644 --- a/src/learning-header/AnonymousUserMenu.jsx +++ b/src/learning-header/AnonymousUserMenu.jsx @@ -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 }) => ( -
- - -
-); +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 ; +}; AnonymousUserMenu.propTypes = { intl: intlShape.isRequired, diff --git a/src/learning-header/AuthenticatedUserDropdown.jsx b/src/learning-header/AuthenticatedUserDropdown.jsx index 9caadb412..4a912281e 100644 --- a/src/learning-header/AuthenticatedUserDropdown.jsx +++ b/src/learning-header/AuthenticatedUserDropdown.jsx @@ -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 = ( - - {intl.formatMessage(messages.dashboard)} - - ); + 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 ( - <> - {intl.formatMessage(messages.help)} - - - - - {username} - - - - {dashboardMenuItem} - - {intl.formatMessage(messages.profile)} - - - {intl.formatMessage(messages.account)} - - { getConfig().ORDER_HISTORY_URL && ( - - {intl.formatMessage(messages.orderHistory)} - - )} - - {intl.formatMessage(messages.signOut)} - - - - + + + + + {username} + + + + + + ); }; diff --git a/src/learning-header/LearningHeader.jsx b/src/learning-header/LearningHeader.jsx index 4341d63a8..723b5927d 100644 --- a/src/learning-header/LearningHeader.jsx +++ b/src/learning-header/LearningHeader.jsx @@ -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 = ({ {intl.formatMessage(messages.skipNavLink)}
{headerLogo} -
- {courseOrg} {courseNumber} - {courseTitle} +
+
{showUserDropdown && authenticatedUser && ( - + <> + + + )} {showUserDropdown && !authenticatedUser && ( @@ -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, }; diff --git a/src/learning-header/LearningHeaderCourseInfo.jsx b/src/learning-header/LearningHeaderCourseInfo.jsx new file mode 100644 index 000000000..f42992d45 --- /dev/null +++ b/src/learning-header/LearningHeaderCourseInfo.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const LearningHeaderCourseInfo = ({ + courseOrg, + courseNumber, + courseTitle, +}) => ( +
+ {courseOrg} {courseNumber} + {courseTitle} +
+); + +export const courseInfoDataShape = { + courseOrg: PropTypes.string, + courseNumber: PropTypes.string, + courseTitle: PropTypes.string, +}; + +LearningHeaderCourseInfo.propTypes = courseInfoDataShape; + +export default LearningHeaderCourseInfo; diff --git a/src/learning-header/LearningHeaderHelpLink.jsx b/src/learning-header/LearningHeaderHelpLink.jsx new file mode 100644 index 000000000..b9b23a122 --- /dev/null +++ b/src/learning-header/LearningHeaderHelpLink.jsx @@ -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 ( + {intl.formatMessage(messages.help)} + ); +}; + +export default LearningHeaderHelpLink; diff --git a/src/learning-header/LearningHeaderUserMenuItems.jsx b/src/learning-header/LearningHeaderUserMenuItems.jsx new file mode 100644 index 000000000..40c66e510 --- /dev/null +++ b/src/learning-header/LearningHeaderUserMenuItems.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { Dropdown } from '@openedx/paragon'; + +const LearningHeaderUserMenuItems = ({ items }) => items.map((item) => ( + + {item.message} + +)); + +export const learningHeaderUserMenuDataShape = { + items: PropTypes.arrayOf(PropTypes.shape({ + message: PropTypes.string, + href: PropTypes.string, + })), +}; + +LearningHeaderUserMenuItems.propTypes = learningHeaderUserMenuDataShape; + +export default LearningHeaderUserMenuItems; diff --git a/src/learning-header/LearningLoggedOutButtons.jsx b/src/learning-header/LearningLoggedOutButtons.jsx new file mode 100644 index 000000000..7bd8e1f90 --- /dev/null +++ b/src/learning-header/LearningLoggedOutButtons.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { Button } from '@openedx/paragon'; + +const LearningLoggedOutButtons = ({ buttonsInfo }) => buttonsInfo.map(buttonInfo => ( + +)); + +export const learningHeaderLoggedOutItemsDataShape = { + buttonsInfo: PropTypes.arrayOf(PropTypes.shape({ + message: PropTypes.string, + href: PropTypes.string, + variant: PropTypes.string, + })), +}; + +LearningLoggedOutButtons.propTypes = learningHeaderLoggedOutItemsDataShape; + +export default LearningLoggedOutButtons; diff --git a/src/MobileHeader.jsx b/src/mobile-header/MobileHeader.jsx similarity index 57% rename from src/MobileHeader.jsx rename to src/mobile-header/MobileHeader.jsx index 750e50b54..7a04ec7e7 100644 --- a/src/MobileHeader.jsx +++ b/src/mobile-header/MobileHeader.jsx @@ -4,107 +4,40 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { getConfig } from '@edx/frontend-platform'; // Local Components -import { Menu, MenuTrigger, MenuContent } from './Menu'; -import Avatar from './Avatar'; -import LogoSlot from './plugin-slots/LogoSlot'; +import { Menu, MenuTrigger, MenuContent } from '../Menu'; +import Avatar from '../Avatar'; +import LogoSlot from '../plugin-slots/LogoSlot'; +import MobileLoggedOutItemsSlot from '../plugin-slots/MobileLoggedOutItemsSlot'; +import { mobileHeaderLoggedOutItemsDataShape } from './MobileLoggedOutItems'; +import MobileMainMenuSlot from '../plugin-slots/MobileMainMenuSlot'; +import { mobileHeaderMainMenuDataShape } from './MobileHeaderMainMenu'; +import MobileUserMenuSlot from '../plugin-slots/MobileUserMenuSlot'; +import { mobileHeaderUserMenuDataShape } from './MobileHeaderUserMenu'; // i18n -import messages from './Header.messages'; +import messages from '../Header.messages'; // Assets -import { MenuIcon } from './Icons'; +import { MenuIcon } from '../Icons'; class MobileHeader extends React.Component { constructor(props) { // eslint-disable-line no-useless-constructor super(props); } - renderMenu(menu) { - // Nodes are accepted as a prop - if (!Array.isArray(menu)) { - return menu; - } - - return menu.map((menuItem) => { - const { - type, - href, - content, - submenuContent, - disabled, - isActive, - onClick, - } = menuItem; - - if (type === 'item') { - return ( - - {content} - - ); - } - - return ( - - - {content} - - - {submenuContent} - - - ); - }); - } - renderMainMenu() { - const { mainMenu } = this.props; - return this.renderMenu(mainMenu); - } - - renderSecondaryMenu() { - const { secondaryMenu } = this.props; - return this.renderMenu(secondaryMenu); + const { mainMenu, secondaryMenu } = this.props; + return ; } renderUserMenuItems() { const { userMenu } = this.props; - - return userMenu.map((group) => ( - group.items.map(({ - type, content, href, disabled, isActive, onClick, - }) => ( -
  • - - {content} - -
  • - )) - )); + return ; } renderLoggedOutItems() { const { loggedOutItems } = this.props; - - return loggedOutItems.map(({ type, href, content }, i, arr) => ( -
  • - - {content} - -
  • - )); + return ; } render() { @@ -149,7 +82,6 @@ class MobileHeader extends React.Component { className="nav flex-column pin-left pin-right border-top shadow py-2" > {this.renderMainMenu()} - {this.renderSecondaryMenu()}
    @@ -179,30 +111,11 @@ class MobileHeader extends React.Component { } } -MobileHeader.propTypes = { - mainMenu: PropTypes.oneOfType([ - PropTypes.node, - PropTypes.array, - ]), - secondaryMenu: PropTypes.oneOfType([ - PropTypes.node, - PropTypes.array, - ]), - userMenu: PropTypes.arrayOf(PropTypes.shape({ - heading: PropTypes.string, - items: PropTypes.arrayOf(PropTypes.shape({ - type: PropTypes.oneOf(['item', 'menu']), - href: PropTypes.string, - content: PropTypes.string, - isActive: PropTypes.bool, - onClick: PropTypes.func, - })), - })), - loggedOutItems: PropTypes.arrayOf(PropTypes.shape({ - type: PropTypes.oneOf(['item', 'menu']), - href: PropTypes.string, - content: PropTypes.string, - })), +export const mobileHeaderDataShape = { + mainMenu: mobileHeaderMainMenuDataShape, + secondaryMenu: mobileHeaderMainMenuDataShape, + userMenu: mobileHeaderUserMenuDataShape, + loggedOutItems: mobileHeaderLoggedOutItemsDataShape, logo: PropTypes.string, logoAltText: PropTypes.string, logoDestination: PropTypes.string, @@ -210,6 +123,20 @@ MobileHeader.propTypes = { username: PropTypes.string, loggedIn: PropTypes.bool, stickyOnMobile: PropTypes.bool, +}; + +MobileHeader.propTypes = { + mainMenu: mobileHeaderDataShape.mainMenu, + secondaryMenu: mobileHeaderDataShape.secondaryMenu, + userMenu: mobileHeaderDataShape.userMenu, + loggedOutItems: mobileHeaderDataShape.loggedOutItems, + logo: mobileHeaderDataShape.logo, + logoAltText: mobileHeaderDataShape.logoAltText, + logoDestination: mobileHeaderDataShape.logoDestination, + avatar: mobileHeaderDataShape.avatar, + username: mobileHeaderDataShape.username, + loggedIn: mobileHeaderDataShape.loggedIn, + stickyOnMobile: mobileHeaderDataShape.stickyOnMobile, // i18n intl: intlShape.isRequired, diff --git a/src/mobile-header/MobileHeaderMainMenu.jsx b/src/mobile-header/MobileHeaderMainMenu.jsx new file mode 100644 index 000000000..095128ff1 --- /dev/null +++ b/src/mobile-header/MobileHeaderMainMenu.jsx @@ -0,0 +1,58 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { Menu, MenuTrigger, MenuContent } from '../Menu'; + +const MobileHeaderMainMenu = ({ menu }) => { + // Nodes are accepted as a prop + if (!Array.isArray(menu)) { + return menu; + } + + return menu.map((menuItem) => { + const { + type, + href, + content, + submenuContent, + disabled, + isActive, + onClick, + } = menuItem; + + if (type === 'item') { + return ( + + {content} + + ); + } + + return ( + + + {content} + + + {submenuContent} + + + ); + }); +}; + +export const mobileHeaderMainMenuDataShape = PropTypes.oneOfType([ + PropTypes.node, + PropTypes.array, +]); + +MobileHeaderMainMenu.propTypes = { + menu: mobileHeaderMainMenuDataShape, +}; + +export default MobileHeaderMainMenu; diff --git a/src/mobile-header/MobileHeaderUserMenu.jsx b/src/mobile-header/MobileHeaderUserMenu.jsx new file mode 100644 index 000000000..17ad2d8b5 --- /dev/null +++ b/src/mobile-header/MobileHeaderUserMenu.jsx @@ -0,0 +1,35 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const MobileHeaderUserMenu = ({ menu }) => menu.map((group) => ( + group.items.map(({ + type, content, href, disabled, isActive, onClick, + }) => ( +
  • + + {content} + +
  • + )) +)); + +export const mobileHeaderUserMenuDataShape = PropTypes.arrayOf(PropTypes.shape({ + heading: PropTypes.string, + items: PropTypes.arrayOf(PropTypes.shape({ + type: PropTypes.oneOf(['item', 'menu']), + href: PropTypes.string, + content: PropTypes.string, + isActive: PropTypes.bool, + onClick: PropTypes.func, + })), +})); + +MobileHeaderUserMenu.propTypes = { + menu: mobileHeaderUserMenuDataShape, +}; + +export default MobileHeaderUserMenu; diff --git a/src/mobile-header/MobileLoggedOutItems.jsx b/src/mobile-header/MobileLoggedOutItems.jsx new file mode 100644 index 000000000..40f2579a7 --- /dev/null +++ b/src/mobile-header/MobileLoggedOutItems.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const MobileLoggedOutItems = ({ items }) => items.map(({ type, href, content }, i, arr) => ( +
  • + + {content} + +
  • +)); + +export const mobileHeaderLoggedOutItemsDataShape = PropTypes.arrayOf(PropTypes.shape({ + type: PropTypes.oneOf(['item', 'menu']), + href: PropTypes.string, + content: PropTypes.string, +})); + +MobileLoggedOutItems.propTypes = { + menu: mobileHeaderLoggedOutItemsDataShape, +}; + +export default MobileLoggedOutItems; diff --git a/src/plugin-slots/CourseInfoSlot/README.md b/src/plugin-slots/CourseInfoSlot/README.md new file mode 100644 index 000000000..574de5500 --- /dev/null +++ b/src/plugin-slots/CourseInfoSlot/README.md @@ -0,0 +1,125 @@ +# Course Info Slot + +### Slot ID: `course_info_slot` + +## Description + +This slot is used to replace/modify/hide the course info. + +## Examples + +### Replace Course Title + +The following `env.config.jsx` will replace the course title. + +![Screenshot of replaced course title](./images/replace_course_title.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const replaceCourseTitle = ( widget ) => { + widget.content.courseTitle = "Custom Course Title"; + return widget; +}; + +const config = { + pluginSlots: { + course_info_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: replaceCourseTitle, + }, + ] + }, + }, +} + +export default config; +``` + +### Replace Course Info with Custom Component + +The following `env.config.jsx` will replace the course info entirely (in this case with a centered πŸ—ΊοΈ `h1`) + +![Screenshot of replaced course info with custom component](./images/replace_course_info_with_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + course_info_slot: { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_course_info_component', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

    πŸ—ΊοΈ

    + ), + }, + }, + ] + } + }, +} + +export default config; +``` + +### Add Custom Components before and after Course Info + +The following `env.config.jsx` will place custom components before and after the course info (in this case centered `h1`s with 🌜 and πŸŒ›). + +![Screenshot of added custom components before and after course info](./images/add_custom_components_before_and_after_course_info.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + course_info_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_before_course_info_component', + type: DIRECT_PLUGIN, + priority: 10, + RenderWidget: () => ( +

    🌜

    + ), + }, + }, + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_after_course_info_component', + type: DIRECT_PLUGIN, + priority: 90, + RenderWidget: () => ( +

    πŸŒ›

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` diff --git a/src/plugin-slots/CourseInfoSlot/images/add_custom_components_before_and_after_course_info.png b/src/plugin-slots/CourseInfoSlot/images/add_custom_components_before_and_after_course_info.png new file mode 100644 index 000000000..ed0ec695d Binary files /dev/null and b/src/plugin-slots/CourseInfoSlot/images/add_custom_components_before_and_after_course_info.png differ diff --git a/src/plugin-slots/CourseInfoSlot/images/replace_course_info_with_custom_component.png b/src/plugin-slots/CourseInfoSlot/images/replace_course_info_with_custom_component.png new file mode 100644 index 000000000..68338ba90 Binary files /dev/null and b/src/plugin-slots/CourseInfoSlot/images/replace_course_info_with_custom_component.png differ diff --git a/src/plugin-slots/CourseInfoSlot/images/replace_course_title.png b/src/plugin-slots/CourseInfoSlot/images/replace_course_title.png new file mode 100644 index 000000000..5358afde5 Binary files /dev/null and b/src/plugin-slots/CourseInfoSlot/images/replace_course_title.png differ diff --git a/src/plugin-slots/CourseInfoSlot/index.jsx b/src/plugin-slots/CourseInfoSlot/index.jsx new file mode 100644 index 000000000..db7c3f253 --- /dev/null +++ b/src/plugin-slots/CourseInfoSlot/index.jsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import LearningHeaderCourseInfo, { courseInfoDataShape } from '../../learning-header/LearningHeaderCourseInfo'; + +const CourseInfoSlot = ({ + courseOrg, + courseNumber, + courseTitle, + ...attributes +}) => ( + + + +); + +CourseInfoSlot.propTypes = courseInfoDataShape; + +export default CourseInfoSlot; diff --git a/src/plugin-slots/DesktopHeaderSlot/README.md b/src/plugin-slots/DesktopHeaderSlot/README.md new file mode 100644 index 000000000..eb40189d9 --- /dev/null +++ b/src/plugin-slots/DesktopHeaderSlot/README.md @@ -0,0 +1,41 @@ +# Desktop Header Slot + +### Slot ID: `desktop_header_slot` + +## Description + +This slot is used to replace/modify/hide the entire desktop header. + +## Examples + +### Custom Component + +The following `env.config.jsx` will replace the desktop header entirely (in this case with a centered πŸ—ΊοΈ `h1`) + +![Screenshot of custom component](./images/desktop_header_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + desktop_header_slot: { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_desktop_header_component', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

    πŸ—ΊοΈ

    + ), + }, + }, + ] + } + }, +} + +export default config; +``` \ No newline at end of file diff --git a/src/plugin-slots/DesktopHeaderSlot/images/desktop_header_custom_component.png b/src/plugin-slots/DesktopHeaderSlot/images/desktop_header_custom_component.png new file mode 100644 index 000000000..3d663f719 Binary files /dev/null and b/src/plugin-slots/DesktopHeaderSlot/images/desktop_header_custom_component.png differ diff --git a/src/plugin-slots/DesktopHeaderSlot/index.jsx b/src/plugin-slots/DesktopHeaderSlot/index.jsx new file mode 100644 index 000000000..aeaac542e --- /dev/null +++ b/src/plugin-slots/DesktopHeaderSlot/index.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import DesktopHeader, { desktopHeaderDataShape } from '../../desktop-header/DesktopHeader'; + +const DesktopHeaderSlot = ({ + props, +}) => ( + + + +); + +DesktopHeaderSlot.propTypes = desktopHeaderDataShape; + +export default DesktopHeaderSlot; diff --git a/src/plugin-slots/DesktopLoggedOutItemsSlot/README.md b/src/plugin-slots/DesktopLoggedOutItemsSlot/README.md new file mode 100644 index 000000000..8d8249e2f --- /dev/null +++ b/src/plugin-slots/DesktopLoggedOutItemsSlot/README.md @@ -0,0 +1,134 @@ +# Desktop Logged Out Items Slot + +### Slot ID: `desktop_logged_out_items_slot` + +## Description + +This slot is used to replace/modify/hide the items shown on desktop when the user is logged out. + +## Examples + +### Modify Items + +The following `env.config.jsx` will modify the items shown on desktop when the user is logged out. + +![Screenshot of modified items](./images/desktop_logged_out_items_modify_items.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const modifyLoggedOutItems = ( widget ) => { + widget.content.items = [ + { + type: 'item', + href: 'https://openedx.org/', + content: 'openedx.org', + }, + { + type: 'item', + href: 'https://docs.openedx.org/en/latest/', + content: 'Documentation', + }, + { + type: 'item', + href: 'https://discuss.openedx.org/', + content: 'Forums', + } + ]; + return widget; +}; + +const config = { + pluginSlots: { + desktop_logged_out_items_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: modifyLoggedOutItems, + }, + ] + }, + }, +} + +export default config; +``` + +### Replace with Custom Component + +The following `env.config.jsx` will replace the items shown on desktop when the user is logged out entirely (in this case with a centered πŸ—ΊοΈ `h1`) + +![Screenshot of custom component](./images/desktop_logged_out_items_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + desktop_logged_out_items_slot: { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_logged_out_items_component', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

    πŸ—ΊοΈ

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + +### Add Custom Components before and after + +The following `env.config.jsx` will place custom components before and after the items shown on desktop when the user is logged out (in this case centered `h1`s with 🌜 and πŸŒ›). + +![Screenshot of custom components before and after](./images/desktop_logged_out_items_custom_components_before_after.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + desktop_logged_out_items_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_before_logged_out_items_component', + type: DIRECT_PLUGIN, + priority: 10, + RenderWidget: () => ( +

    🌜

    + ), + }, + }, + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_after_logged_out_items_component', + type: DIRECT_PLUGIN, + priority: 90, + RenderWidget: () => ( +

    πŸŒ›

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + diff --git a/src/plugin-slots/DesktopLoggedOutItemsSlot/images/desktop_logged_out_items_custom_component.png b/src/plugin-slots/DesktopLoggedOutItemsSlot/images/desktop_logged_out_items_custom_component.png new file mode 100644 index 000000000..62282e08c Binary files /dev/null and b/src/plugin-slots/DesktopLoggedOutItemsSlot/images/desktop_logged_out_items_custom_component.png differ diff --git a/src/plugin-slots/DesktopLoggedOutItemsSlot/images/desktop_logged_out_items_custom_components_before_after.png b/src/plugin-slots/DesktopLoggedOutItemsSlot/images/desktop_logged_out_items_custom_components_before_after.png new file mode 100644 index 000000000..36b431aca Binary files /dev/null and b/src/plugin-slots/DesktopLoggedOutItemsSlot/images/desktop_logged_out_items_custom_components_before_after.png differ diff --git a/src/plugin-slots/DesktopLoggedOutItemsSlot/images/desktop_logged_out_items_modify_items.png b/src/plugin-slots/DesktopLoggedOutItemsSlot/images/desktop_logged_out_items_modify_items.png new file mode 100644 index 000000000..4f59f0993 Binary files /dev/null and b/src/plugin-slots/DesktopLoggedOutItemsSlot/images/desktop_logged_out_items_modify_items.png differ diff --git a/src/plugin-slots/DesktopLoggedOutItemsSlot/index.jsx b/src/plugin-slots/DesktopLoggedOutItemsSlot/index.jsx new file mode 100644 index 000000000..b9676b2d6 --- /dev/null +++ b/src/plugin-slots/DesktopLoggedOutItemsSlot/index.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import DesktopLoggedOutItems, { desktopLoggedOutItemsDataShape } from '../../desktop-header/DesktopLoggedOutItems'; + +const DesktopLoggedOutItemsSlot = ({ + items, +}) => ( + + + +); + +DesktopLoggedOutItemsSlot.propTypes = { + items: desktopLoggedOutItemsDataShape, +}; + +export default DesktopLoggedOutItemsSlot; diff --git a/src/plugin-slots/DesktopMainMenuSlot/README.md b/src/plugin-slots/DesktopMainMenuSlot/README.md new file mode 100644 index 000000000..969179170 --- /dev/null +++ b/src/plugin-slots/DesktopMainMenuSlot/README.md @@ -0,0 +1,134 @@ +# Desktop Main Menu Slot + +### Slot ID: `desktop_main_menu_slot` + +## Description + +This slot is used to replace/modify/hide the desktop main menu. + +## Examples + +### Modify Items + +The following `env.config.jsx` will modify the items in the desktop main menu. + +![Screenshot of modified items](./images/desktop_main_menu_modify_items.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const modifyMainMenu = ( widget ) => { + widget.content.menu = [ + { + type: 'item', + href: 'https://openedx.org/', + content: 'openedx.org', + }, + { + type: 'item', + href: 'https://docs.openedx.org/en/latest/', + content: 'Documentation', + }, + { + type: 'item', + href: 'https://discuss.openedx.org/', + content: 'Forums', + } + ]; + return widget; +}; + +const config = { + pluginSlots: { + desktop_main_menu_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: modifyMainMenu, + }, + ] + }, + }, +} + +export default config; +``` + +### Replace Menu with Custom Component + +The following `env.config.jsx` will replace the desktop main menu entirely (in this case with a centered πŸ—ΊοΈ `h1`) + +![Screenshot of custom component](./images/desktop_main_menu_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + desktop_main_menu_slot: { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_main_menu_component', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

    πŸ—ΊοΈ

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + +### Add Custom Components before and after Menu + +The following `env.config.jsx` will place custom components before and after the desktop main menu (in this case centered `h1`s with 🌜 and πŸŒ›). + +![Screenshot of custom components before and after](./images/desktop_main_menu_custom_components_before_after.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + desktop_main_menu_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_before_main_menu_component', + type: DIRECT_PLUGIN, + priority: 10, + RenderWidget: () => ( +

    🌜

    + ), + }, + }, + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_after_main_menu_component', + type: DIRECT_PLUGIN, + priority: 90, + RenderWidget: () => ( +

    πŸŒ›

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + diff --git a/src/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_custom_component.png b/src/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_custom_component.png new file mode 100644 index 000000000..ec29483f0 Binary files /dev/null and b/src/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_custom_component.png differ diff --git a/src/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_custom_components_before_after.png b/src/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_custom_components_before_after.png new file mode 100644 index 000000000..52c0cc7f5 Binary files /dev/null and b/src/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_custom_components_before_after.png differ diff --git a/src/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_modify_items.png b/src/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_modify_items.png new file mode 100644 index 000000000..3537756bd Binary files /dev/null and b/src/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_modify_items.png differ diff --git a/src/plugin-slots/DesktopMainMenuSlot/index.jsx b/src/plugin-slots/DesktopMainMenuSlot/index.jsx new file mode 100644 index 000000000..b0026b919 --- /dev/null +++ b/src/plugin-slots/DesktopMainMenuSlot/index.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import DesktopHeaderMainOrSecondaryMenu, { desktopHeaderMainOrSecondaryMenuDataShape } from '../../desktop-header/DesktopHeaderMainOrSecondaryMenu'; + +const DesktopMainMenuSlot = ({ + menu, +}) => ( + + + +); + +DesktopMainMenuSlot.propTypes = { + menu: desktopHeaderMainOrSecondaryMenuDataShape, +}; + +export default DesktopMainMenuSlot; diff --git a/src/plugin-slots/DesktopSecondaryMenuSlot/README.md b/src/plugin-slots/DesktopSecondaryMenuSlot/README.md new file mode 100644 index 000000000..3351b07e1 --- /dev/null +++ b/src/plugin-slots/DesktopSecondaryMenuSlot/README.md @@ -0,0 +1,129 @@ +# Desktop Secondary Menu Slot + +### Slot ID: `desktop_secondary_menu_slot` + +## Description + +This slot is used to replace/modify/hide the desktop secondary menu. + +## Examples + +### Modify Items + +The following `env.config.jsx` will modify the items in the desktop secondary menu. + +![Screenshot of modified items](./images/desktop_secondary_menu_modify_items.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const modifySecondaryMenu = ( widget ) => { + widget.content.menu = [ + { + type: 'item', + href: 'https://www.youtube.com/c/openedx', + content: 'Open edX on YouTube', + }, + { + type: 'item', + href: 'https://github.com/openedx/', + content: 'Open edX on GitHub', + } + ]; + return widget; +}; + +const config = { + pluginSlots: { + desktop_secondary_menu_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: modifySecondaryMenu, + }, + ] + }, + }, +} + +export default config; +``` + +### Replace Menu with Custom Component + +The following `env.config.jsx` will replace the desktop secondary menu entirely (in this case with a centered πŸ—ΊοΈ `h1`) + +![Screenshot of custom component](./images/desktop_secondary_menu_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + desktop_secondary_menu_slot: { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_secondary_menu_component', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

    πŸ—ΊοΈ

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + +### Add Custom Components before and after Menu + +The following `env.config.jsx` will place custom components before and after the desktop secondary menu (in this case centered `h1`s with 🌜 and πŸŒ›). + +![Screenshot of custom components before and after](./images/desktop_secondary_menu_custom_components_before_after.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + desktop_secondary_menu_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_before_secondary_menu_component', + type: DIRECT_PLUGIN, + priority: 10, + RenderWidget: () => ( +

    🌜

    + ), + }, + }, + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_after_secondary_menu_component', + type: DIRECT_PLUGIN, + priority: 90, + RenderWidget: () => ( +

    πŸŒ›

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + diff --git a/src/plugin-slots/DesktopSecondaryMenuSlot/images/desktop_secondary_menu_custom_component.png b/src/plugin-slots/DesktopSecondaryMenuSlot/images/desktop_secondary_menu_custom_component.png new file mode 100644 index 000000000..cbfb69eba Binary files /dev/null and b/src/plugin-slots/DesktopSecondaryMenuSlot/images/desktop_secondary_menu_custom_component.png differ diff --git a/src/plugin-slots/DesktopSecondaryMenuSlot/images/desktop_secondary_menu_custom_components_before_after.png b/src/plugin-slots/DesktopSecondaryMenuSlot/images/desktop_secondary_menu_custom_components_before_after.png new file mode 100644 index 000000000..baaa9e2c8 Binary files /dev/null and b/src/plugin-slots/DesktopSecondaryMenuSlot/images/desktop_secondary_menu_custom_components_before_after.png differ diff --git a/src/plugin-slots/DesktopSecondaryMenuSlot/images/desktop_secondary_menu_modify_items.png b/src/plugin-slots/DesktopSecondaryMenuSlot/images/desktop_secondary_menu_modify_items.png new file mode 100644 index 000000000..18d858d2c Binary files /dev/null and b/src/plugin-slots/DesktopSecondaryMenuSlot/images/desktop_secondary_menu_modify_items.png differ diff --git a/src/plugin-slots/DesktopSecondaryMenuSlot/index.jsx b/src/plugin-slots/DesktopSecondaryMenuSlot/index.jsx new file mode 100644 index 000000000..79c1f5fad --- /dev/null +++ b/src/plugin-slots/DesktopSecondaryMenuSlot/index.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import DesktopHeaderMainOrSecondaryMenu, { desktopHeaderMainOrSecondaryMenuDataShape } from '../../desktop-header/DesktopHeaderMainOrSecondaryMenu'; + +const DesktopSecondaryMenuSlot = ({ + menu, +}) => ( + + + +); + +DesktopSecondaryMenuSlot.propTypes = { + menu: desktopHeaderMainOrSecondaryMenuDataShape, +}; + +export default DesktopSecondaryMenuSlot; diff --git a/src/plugin-slots/DesktopUserMenuSlot/README.md b/src/plugin-slots/DesktopUserMenuSlot/README.md new file mode 100644 index 000000000..e98b33d63 --- /dev/null +++ b/src/plugin-slots/DesktopUserMenuSlot/README.md @@ -0,0 +1,141 @@ +# Desktop User Menu Slot + +### Slot ID: `desktop_user_menu_slot` + +## Description + +This slot is used to replace/modify/hide the desktop user menu. + +## Examples + +### Modify Items + +The following `env.config.jsx` will modify the items in the desktop user menu. + +![Screenshot of modified items](./images/desktop_user_menu_modify_items.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const modifyUserMenu = ( widget ) => { + widget.content.menu = [ + { + items: [ + { + type: 'item', + href: 'https://openedx.org/', + content: 'openedx.org', + }, + { + type: 'item', + href: 'https://docs.openedx.org/en/latest/', + content: 'Documentation', + }, + ] + }, + { + items: [ + { + type: 'item', + href: 'https://discuss.openedx.org/', + content: 'Forums', + } + ] + } + ]; + return widget; +}; + +const config = { + pluginSlots: { + desktop_user_menu_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: modifyUserMenu, + }, + ] + }, + }, +} + +export default config; +``` + +### Replace Menu with Custom Component + +The following `env.config.jsx` will replace the desktop user menu entirely (in this case with a centered πŸ—ΊοΈ `h1`) + +![Screenshot of custom component](./images/desktop_user_menu_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + desktop_user_menu_slot: { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_user_menu_component', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

    πŸ—ΊοΈ

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + +### Add Custom Components before and after Menu + +The following `env.config.jsx` will place custom components before and after the desktop user menu (in this case centered `h1`s with 🌞 and 🌚). + +![Screenshot of custom components before and after](./images/desktop_user_menu_custom_components_before_after.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + desktop_user_menu_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_before_user_menu_component', + type: DIRECT_PLUGIN, + priority: 10, + RenderWidget: () => ( +

    🌞

    + ), + }, + }, + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_after_user_menu_component', + type: DIRECT_PLUGIN, + priority: 90, + RenderWidget: () => ( +

    🌚

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` diff --git a/src/plugin-slots/DesktopUserMenuSlot/images/desktop_user_menu_custom_component.png b/src/plugin-slots/DesktopUserMenuSlot/images/desktop_user_menu_custom_component.png new file mode 100644 index 000000000..05034c484 Binary files /dev/null and b/src/plugin-slots/DesktopUserMenuSlot/images/desktop_user_menu_custom_component.png differ diff --git a/src/plugin-slots/DesktopUserMenuSlot/images/desktop_user_menu_custom_components_before_after.png b/src/plugin-slots/DesktopUserMenuSlot/images/desktop_user_menu_custom_components_before_after.png new file mode 100644 index 000000000..e8410c6e3 Binary files /dev/null and b/src/plugin-slots/DesktopUserMenuSlot/images/desktop_user_menu_custom_components_before_after.png differ diff --git a/src/plugin-slots/DesktopUserMenuSlot/images/desktop_user_menu_modify_items.png b/src/plugin-slots/DesktopUserMenuSlot/images/desktop_user_menu_modify_items.png new file mode 100644 index 000000000..3412e0bd0 Binary files /dev/null and b/src/plugin-slots/DesktopUserMenuSlot/images/desktop_user_menu_modify_items.png differ diff --git a/src/plugin-slots/DesktopUserMenuSlot/index.jsx b/src/plugin-slots/DesktopUserMenuSlot/index.jsx new file mode 100644 index 000000000..1df367c99 --- /dev/null +++ b/src/plugin-slots/DesktopUserMenuSlot/index.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import DesktopHeaderUserMenu, { desktopUserMenuDataShape } from '../../desktop-header/DesktopHeaderUserMenu'; + +const DesktopUserMenuSlot = ({ + menu, +}) => ( + + + +); + +DesktopUserMenuSlot.propTypes = { + menu: desktopUserMenuDataShape, +}; + +export default DesktopUserMenuSlot; diff --git a/src/plugin-slots/LearningHelpSlot/README.md b/src/plugin-slots/LearningHelpSlot/README.md new file mode 100644 index 000000000..0f28edf38 --- /dev/null +++ b/src/plugin-slots/LearningHelpSlot/README.md @@ -0,0 +1,41 @@ +# Learning Help Slot + +### Slot ID: `learning_help_slot` + +## Description + +This slot is used to replace/modify/hide the learning help link. + +## Examples + +### Custom Component + +The following `env.config.jsx` will replace the help link entirely (in this case with a centered πŸ—ΊοΈ `h1`) + +![Screenshot of replaced learning help with custom component](./images/learning_help_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + learning_help_slot: { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_learning_help_component', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

    πŸ—ΊοΈ

    + ), + }, + }, + ] + } + }, +} + +export default config; +``` diff --git a/src/plugin-slots/LearningHelpSlot/images/learning_help_custom_component.png b/src/plugin-slots/LearningHelpSlot/images/learning_help_custom_component.png new file mode 100644 index 000000000..52eb9b305 Binary files /dev/null and b/src/plugin-slots/LearningHelpSlot/images/learning_help_custom_component.png differ diff --git a/src/plugin-slots/LearningHelpSlot/index.jsx b/src/plugin-slots/LearningHelpSlot/index.jsx new file mode 100644 index 000000000..57643023a --- /dev/null +++ b/src/plugin-slots/LearningHelpSlot/index.jsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import LearningHeaderHelpLink from '../../learning-header/LearningHeaderHelpLink'; + +const LearningHelpSlot = () => ( + + + +); + +export default LearningHelpSlot; diff --git a/src/plugin-slots/LearningLoggedOutItemsSlot/README.md b/src/plugin-slots/LearningLoggedOutItemsSlot/README.md new file mode 100644 index 000000000..a803d0181 --- /dev/null +++ b/src/plugin-slots/LearningLoggedOutItemsSlot/README.md @@ -0,0 +1,132 @@ +# Learning Logged Out Items Slot + +### Slot ID: `learning_logged_out_items_slot` + +## Description + +This slot is used to replace/modify/hide the items shown on the learning header when the user is logged out. + +## Examples + +### Modify Items + +The following `env.config.jsx` will modify the items shown on the learning header when the user is logged out. + +![Screenshot of modified items](./images/learning_logged_out_items_modified_items.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const modifyLoggedOutItems = ( widget ) => { + widget.content.buttonsInfo = [ + { + href: 'https://docs.openedx.org/en/latest/', + message: 'Documentation', + }, + { + href: 'https://discuss.openedx.org/', + message: 'Forums', + }, + { + href: 'https://openedx.org/', + message: 'openedx.org', + variant: 'primary', + }, + ]; + return widget; +}; + +const config = { + pluginSlots: { + learning_logged_out_items_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: modifyLoggedOutItems, + }, + ] + }, + }, +} + +export default config; +``` + +### Replace with Custom Component + +The following `env.config.jsx` will replace the items shown in the learning header when the user is logged out entirely (in this case with a centered πŸ—ΊοΈ `h1`) + +![Screenshot of replaced with custom component](./images/learning_logged_out_items_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + learning_logged_out_items_slot: { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_logged_out_items_component', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

    πŸ—ΊοΈ

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + +### Add Custom Components before and after + +The following `env.config.jsx` will place custom components before and after the items shown in the learning header when the user is logged out (in this case centered `h1`s with 🌜 and πŸŒ›). + +![Screenshot of added custom components before and after](./images/learning_logged_out_items_custom_components_before_after.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + learning_logged_out_items_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_before_logged_out_items_component', + type: DIRECT_PLUGIN, + priority: 10, + RenderWidget: () => ( +

    🌜

    + ), + }, + }, + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_after_logged_out_items_component', + type: DIRECT_PLUGIN, + priority: 90, + RenderWidget: () => ( +

    πŸŒ›

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + diff --git a/src/plugin-slots/LearningLoggedOutItemsSlot/images/learning_logged_out_items_custom_component.png b/src/plugin-slots/LearningLoggedOutItemsSlot/images/learning_logged_out_items_custom_component.png new file mode 100644 index 000000000..bce54d3e3 Binary files /dev/null and b/src/plugin-slots/LearningLoggedOutItemsSlot/images/learning_logged_out_items_custom_component.png differ diff --git a/src/plugin-slots/LearningLoggedOutItemsSlot/images/learning_logged_out_items_custom_components_before_after.png b/src/plugin-slots/LearningLoggedOutItemsSlot/images/learning_logged_out_items_custom_components_before_after.png new file mode 100644 index 000000000..b4cc6110c Binary files /dev/null and b/src/plugin-slots/LearningLoggedOutItemsSlot/images/learning_logged_out_items_custom_components_before_after.png differ diff --git a/src/plugin-slots/LearningLoggedOutItemsSlot/images/learning_logged_out_items_modified_items.png b/src/plugin-slots/LearningLoggedOutItemsSlot/images/learning_logged_out_items_modified_items.png new file mode 100644 index 000000000..7eb0f59cc Binary files /dev/null and b/src/plugin-slots/LearningLoggedOutItemsSlot/images/learning_logged_out_items_modified_items.png differ diff --git a/src/plugin-slots/LearningLoggedOutItemsSlot/index.jsx b/src/plugin-slots/LearningLoggedOutItemsSlot/index.jsx new file mode 100644 index 000000000..daf64a6b8 --- /dev/null +++ b/src/plugin-slots/LearningLoggedOutItemsSlot/index.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import LearningLoggedOutButtons, { learningHeaderLoggedOutItemsDataShape } from '../../learning-header/LearningLoggedOutButtons'; + +const LearningLoggedOutItemsSlot = ({ + buttonsInfo, +}) => ( + + + +); + +LearningLoggedOutItemsSlot.propTypes = learningHeaderLoggedOutItemsDataShape; + +export default LearningLoggedOutItemsSlot; diff --git a/src/plugin-slots/LearningUserMenuSlot/README.md b/src/plugin-slots/LearningUserMenuSlot/README.md new file mode 100644 index 000000000..97fa467fe --- /dev/null +++ b/src/plugin-slots/LearningUserMenuSlot/README.md @@ -0,0 +1,130 @@ +# Learning User Menu Slot + +### Slot ID: `learning_user_menu_slot` + +## Description + +This slot is used to replace/modify/hide the learning user menu. + +## Examples + +### Modify Items + +The following `env.config.jsx` will modify the items in the learning user menu. + +![Screenshot of modified items](./images/learning_user_menu_modified_items.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const modifyUserMenu = ( widget ) => { + widget.content.items = [ + { + href: 'https://openedx.org/', + message: 'openedx.org', + }, + { + href: 'https://docs.openedx.org/en/latest/', + message: 'Documentation', + }, + { + href: 'https://discuss.openedx.org/', + message: 'Forums', + } + ]; + return widget; +}; + +const config = { + pluginSlots: { + learning_user_menu_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: modifyUserMenu, + }, + ] + }, + }, +} + +export default config; +``` + +### Replace Menu with Custom Component + +The following `env.config.jsx` will replace the items in the learning user menu entirely (in this case with a centered πŸ—ΊοΈ `h1`) + +![Screenshot of replaced with custom component](./images/learning_user_menu_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + learning_user_menu_slot: { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_user_menu_component', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

    πŸ—ΊοΈ

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + +### Add Custom Components before and after Menu + +The following `env.config.jsx` will place custom components before and after the learning user menu (in this case centered `h1`s with 🌞 and 🌚). + +![Screenshot of custom components before and after](./images/learning_user_menu_custom_components_before_after.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + learning_user_menu_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_before_user_menu_component', + type: DIRECT_PLUGIN, + priority: 10, + RenderWidget: () => ( +

    🌞

    + ), + }, + }, + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_after_user_menu_component', + type: DIRECT_PLUGIN, + priority: 90, + RenderWidget: () => ( +

    🌚

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` diff --git a/src/plugin-slots/LearningUserMenuSlot/images/learning_user_menu_custom_component.png b/src/plugin-slots/LearningUserMenuSlot/images/learning_user_menu_custom_component.png new file mode 100644 index 000000000..09bfd24e2 Binary files /dev/null and b/src/plugin-slots/LearningUserMenuSlot/images/learning_user_menu_custom_component.png differ diff --git a/src/plugin-slots/LearningUserMenuSlot/images/learning_user_menu_custom_components_before_after.png b/src/plugin-slots/LearningUserMenuSlot/images/learning_user_menu_custom_components_before_after.png new file mode 100644 index 000000000..e4e7d5635 Binary files /dev/null and b/src/plugin-slots/LearningUserMenuSlot/images/learning_user_menu_custom_components_before_after.png differ diff --git a/src/plugin-slots/LearningUserMenuSlot/images/learning_user_menu_modified_items.png b/src/plugin-slots/LearningUserMenuSlot/images/learning_user_menu_modified_items.png new file mode 100644 index 000000000..95ca5718c Binary files /dev/null and b/src/plugin-slots/LearningUserMenuSlot/images/learning_user_menu_modified_items.png differ diff --git a/src/plugin-slots/LearningUserMenuSlot/index.jsx b/src/plugin-slots/LearningUserMenuSlot/index.jsx new file mode 100644 index 000000000..b78e3ae86 --- /dev/null +++ b/src/plugin-slots/LearningUserMenuSlot/index.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import LearningHeaderUserMenuItems, { learningHeaderUserMenuDataShape } from '../../learning-header/LearningHeaderUserMenuItems'; + +const LearningUserMenuSlot = ({ + items, +}) => ( + + + +); + +LearningUserMenuSlot.propTypes = learningHeaderUserMenuDataShape; + +export default LearningUserMenuSlot; diff --git a/src/plugin-slots/LogoSlot/index.jsx b/src/plugin-slots/LogoSlot/index.jsx index 0f61e8499..42f926299 100644 --- a/src/plugin-slots/LogoSlot/index.jsx +++ b/src/plugin-slots/LogoSlot/index.jsx @@ -1,7 +1,6 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { PluginSlot } from '@openedx/frontend-plugin-framework'; -import Logo from '../../Logo'; +import Logo, { logoDataShape } from '../../Logo'; const LogoSlot = ({ href, src, alt, ...attributes @@ -16,10 +15,6 @@ const LogoSlot = ({ ); -LogoSlot.propTypes = { - href: PropTypes.string.isRequired, - src: PropTypes.string.isRequired, - alt: PropTypes.string.isRequired, -}; +LogoSlot.propTypes = logoDataShape; export default LogoSlot; diff --git a/src/plugin-slots/MobileHeaderSlot/README.md b/src/plugin-slots/MobileHeaderSlot/README.md new file mode 100644 index 000000000..566f47081 --- /dev/null +++ b/src/plugin-slots/MobileHeaderSlot/README.md @@ -0,0 +1,41 @@ +# Mobile Header Slot + +### Slot ID: `mobile_header_slot` + +## Description + +This slot is used to replace/modify/hide the entire mobile header. + +## Examples + +### Custom Component + +The following `env.config.jsx` will replace the mobile header entirely (in this case with a centered πŸ—ΊοΈ `h1`) + +![Screenshot of custom component](./images/mobile_header_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + mobile_header_slot: { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_mobile_header_component', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

    πŸ—ΊοΈ

    + ), + }, + }, + ] + } + }, +} + +export default config; +``` \ No newline at end of file diff --git a/src/plugin-slots/MobileHeaderSlot/images/mobile_header_custom_component.png b/src/plugin-slots/MobileHeaderSlot/images/mobile_header_custom_component.png new file mode 100644 index 000000000..df026339c Binary files /dev/null and b/src/plugin-slots/MobileHeaderSlot/images/mobile_header_custom_component.png differ diff --git a/src/plugin-slots/MobileHeaderSlot/index.jsx b/src/plugin-slots/MobileHeaderSlot/index.jsx new file mode 100644 index 000000000..b0b6c3830 --- /dev/null +++ b/src/plugin-slots/MobileHeaderSlot/index.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import MobileHeader, { mobileHeaderDataShape } from '../../mobile-header/MobileHeader'; + +const MobileHeaderSlot = ({ + props, +}) => ( + + + +); + +MobileHeaderSlot.propTypes = mobileHeaderDataShape; + +export default MobileHeaderSlot; diff --git a/src/plugin-slots/MobileLoggedOutItemsSlot/README.md b/src/plugin-slots/MobileLoggedOutItemsSlot/README.md new file mode 100644 index 000000000..ffa8dd619 --- /dev/null +++ b/src/plugin-slots/MobileLoggedOutItemsSlot/README.md @@ -0,0 +1,134 @@ +# Mobile Logged Out Items Slot + +### Slot ID: `mobile_logged_out_items_slot` + +## Description + +This slot is used to replace/modify/hide the mobile user menu when logged out. + +## Examples + +### Modify Items + +The following `env.config.jsx` will modify the items in mobile user menu when logged out. + +![Screenshot of modified items](./images/mobile_logged_out_items_modify_items.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const modifyLoggedOutItems = ( widget ) => { + widget.content.items = [ + { + type: 'item', + href: 'https://openedx.org/', + content: 'openedx.org', + }, + { + type: 'item', + href: 'https://docs.openedx.org/en/latest/', + content: 'Documentation', + }, + { + type: 'item', + href: 'https://discuss.openedx.org/', + content: 'Forums', + } + ]; + return widget; +}; + +const config = { + pluginSlots: { + mobile_logged_out_items_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: modifyLoggedOutItems, + }, + ] + }, + }, +} + +export default config; +``` + +### Replace Items with Custom Component + +The following `env.config.jsx` will replace the items in mobile user menu when logged out entirely (in this case with a centered πŸ—ΊοΈ `h1`) + +![Screenshot of custom component](./images/mobile_logged_out_items_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + mobile_logged_out_items_slot: { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_logged_out_items_component', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

    πŸ—ΊοΈ

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + +### Add Custom Components before and after Items + +The following `env.config.jsx` will place custom components before and after the items in mobile user menu when logged out (in this case centered `h1`s with 🌞 and 🌚). + +![Screenshot of custom components before and after](./images/mobile_logged_out_items_custom_components_before_after.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + mobile_logged_out_items_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_before_logged_out_items_component', + type: DIRECT_PLUGIN, + priority: 10, + RenderWidget: () => ( +

    🌞

    + ), + }, + }, + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_after_logged_out_items_component', + type: DIRECT_PLUGIN, + priority: 90, + RenderWidget: () => ( +

    🌚

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + diff --git a/src/plugin-slots/MobileLoggedOutItemsSlot/images/mobile_logged_out_items_custom_component.png b/src/plugin-slots/MobileLoggedOutItemsSlot/images/mobile_logged_out_items_custom_component.png new file mode 100644 index 000000000..a6f3ba1bc Binary files /dev/null and b/src/plugin-slots/MobileLoggedOutItemsSlot/images/mobile_logged_out_items_custom_component.png differ diff --git a/src/plugin-slots/MobileLoggedOutItemsSlot/images/mobile_logged_out_items_custom_components_before_after.png b/src/plugin-slots/MobileLoggedOutItemsSlot/images/mobile_logged_out_items_custom_components_before_after.png new file mode 100644 index 000000000..11d9d25b5 Binary files /dev/null and b/src/plugin-slots/MobileLoggedOutItemsSlot/images/mobile_logged_out_items_custom_components_before_after.png differ diff --git a/src/plugin-slots/MobileLoggedOutItemsSlot/images/mobile_logged_out_items_modify_items.png b/src/plugin-slots/MobileLoggedOutItemsSlot/images/mobile_logged_out_items_modify_items.png new file mode 100644 index 000000000..36b0527f8 Binary files /dev/null and b/src/plugin-slots/MobileLoggedOutItemsSlot/images/mobile_logged_out_items_modify_items.png differ diff --git a/src/plugin-slots/MobileLoggedOutItemsSlot/index.jsx b/src/plugin-slots/MobileLoggedOutItemsSlot/index.jsx new file mode 100644 index 000000000..490dda04b --- /dev/null +++ b/src/plugin-slots/MobileLoggedOutItemsSlot/index.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import MobileLoggedOutItems, { mobileHeaderLoggedOutItemsDataShape } from '../../mobile-header/MobileLoggedOutItems'; + +const MobileLoggedOutItemsSlot = ({ + items, +}) => ( + + + +); + +MobileLoggedOutItemsSlot.propTypes = { + items: mobileHeaderLoggedOutItemsDataShape, +}; + +export default MobileLoggedOutItemsSlot; diff --git a/src/plugin-slots/MobileMainMenuSlot/README.md b/src/plugin-slots/MobileMainMenuSlot/README.md new file mode 100644 index 000000000..5eebd7bb7 --- /dev/null +++ b/src/plugin-slots/MobileMainMenuSlot/README.md @@ -0,0 +1,134 @@ +# Mobile Main Menu Slot + +### Slot ID: `mobile_main_menu_slot` + +## Description + +This slot is used to replace/modify/hide the mobile main menu. + +## Examples + +### Modify Items + +The following `env.config.jsx` will modify the items in the mobile main menu. + +![Screenshot of modified items](./images/mobile_main_menu_modify_items.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const modifyMainMenu = ( widget ) => { + widget.content.menu = [ + { + type: 'item', + href: 'https://openedx.org/', + content: 'openedx.org', + }, + { + type: 'item', + href: 'https://docs.openedx.org/en/latest/', + content: 'Documentation', + }, + { + type: 'item', + href: 'https://discuss.openedx.org/', + content: 'Forums', + } + ]; + return widget; +}; + +const config = { + pluginSlots: { + mobile_main_menu_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: modifyMainMenu, + }, + ] + }, + }, +} + +export default config; +``` + +### Replace Menu with Custom Component + +The following `env.config.jsx` will replace the mobile main menu entirely (in this case with a centered πŸ—ΊοΈ `h1`) + +![Screenshot of custom component](./images/mobile_main_menu_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + mobile_main_menu_slot: { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_main_menu_component', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

    πŸ—ΊοΈ

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + +### Add Custom Components before and after Menu + +The following `env.config.jsx` will place custom components before and after the mobile main menu (in this case centered `h1`s with 🌞 and 🌚). + +![Screenshot of custom components before and after](./images/mobile_main_menu_custom_components_before_after.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + mobile_main_menu_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_before_main_menu_component', + type: DIRECT_PLUGIN, + priority: 10, + RenderWidget: () => ( +

    🌞

    + ), + }, + }, + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_after_main_menu_component', + type: DIRECT_PLUGIN, + priority: 90, + RenderWidget: () => ( +

    🌚

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + diff --git a/src/plugin-slots/MobileMainMenuSlot/images/mobile_main_menu_custom_component.png b/src/plugin-slots/MobileMainMenuSlot/images/mobile_main_menu_custom_component.png new file mode 100644 index 000000000..00c82a787 Binary files /dev/null and b/src/plugin-slots/MobileMainMenuSlot/images/mobile_main_menu_custom_component.png differ diff --git a/src/plugin-slots/MobileMainMenuSlot/images/mobile_main_menu_custom_components_before_after.png b/src/plugin-slots/MobileMainMenuSlot/images/mobile_main_menu_custom_components_before_after.png new file mode 100644 index 000000000..eda0f9a07 Binary files /dev/null and b/src/plugin-slots/MobileMainMenuSlot/images/mobile_main_menu_custom_components_before_after.png differ diff --git a/src/plugin-slots/MobileMainMenuSlot/images/mobile_main_menu_modify_items.png b/src/plugin-slots/MobileMainMenuSlot/images/mobile_main_menu_modify_items.png new file mode 100644 index 000000000..0f2e75bad Binary files /dev/null and b/src/plugin-slots/MobileMainMenuSlot/images/mobile_main_menu_modify_items.png differ diff --git a/src/plugin-slots/MobileMainMenuSlot/index.jsx b/src/plugin-slots/MobileMainMenuSlot/index.jsx new file mode 100644 index 000000000..b26cfc257 --- /dev/null +++ b/src/plugin-slots/MobileMainMenuSlot/index.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import MobileHeaderMainMenu, { mobileHeaderMainMenuDataShape } from '../../mobile-header/MobileHeaderMainMenu'; + +const MobileMainMenuSlot = ({ + menu, +}) => ( + + + +); + +MobileMainMenuSlot.propTypes = { + menu: mobileHeaderMainMenuDataShape, +}; + +export default MobileMainMenuSlot; diff --git a/src/plugin-slots/MobileUserMenuSlot/README.md b/src/plugin-slots/MobileUserMenuSlot/README.md new file mode 100644 index 000000000..d75e08e85 --- /dev/null +++ b/src/plugin-slots/MobileUserMenuSlot/README.md @@ -0,0 +1,142 @@ +# Mobile User Menu Slot + +### Slot ID: `mobile_user_menu_slot` + +## Description + +This slot is used to replace/modify/hide the mobile user menu. + +## Examples + +### Modify Items + +The following `env.config.jsx` will modify the items in the mobile user menu. + +![Screenshot of modified items](./images/mobile_user_menu_modify_items.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const modifyUserMenu = ( widget ) => { + widget.content.menu = [ + { + items: [ + { + type: 'item', + href: 'https://openedx.org/', + content: 'openedx.org', + }, + { + type: 'item', + href: 'https://docs.openedx.org/en/latest/', + content: 'Documentation', + }, + ] + }, + { + items: [ + { + type: 'item', + href: 'https://discuss.openedx.org/', + content: 'Forums', + } + ] + } + ]; + return widget; +}; + +const config = { + pluginSlots: { + mobile_user_menu_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: modifyUserMenu, + }, + ] + }, + }, +} + +export default config; +``` + +### Replace Menu with Custom Component + +The following `env.config.jsx` will replace the mobile main user entirely (in this case with a centered πŸ—ΊοΈ `h1`) + +![Screenshot of custom component](./images/mobile_user_menu_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + mobile_user_menu_slot: { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_user_menu_component', + type: DIRECT_PLUGIN, + RenderWidget: () => ( +

    πŸ—ΊοΈ

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + +### Add Custom Components before and after Menu + +The following `env.config.jsx` will place custom components before and after the mobile user menu (in this case centered `h1`s with 🌞 and 🌚). + +![Screenshot of custom components before and after](./images/mobile_user_menu_custom_components_before_after.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + mobile_user_menu_slot: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_before_user_menu_component', + type: DIRECT_PLUGIN, + priority: 10, + RenderWidget: () => ( +

    🌞

    + ), + }, + }, + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_after_user_menu_component', + type: DIRECT_PLUGIN, + priority: 90, + RenderWidget: () => ( +

    🌚

    + ), + }, + }, + ] + }, + }, +} + +export default config; +``` + diff --git a/src/plugin-slots/MobileUserMenuSlot/images/mobile_user_menu_custom_component.png b/src/plugin-slots/MobileUserMenuSlot/images/mobile_user_menu_custom_component.png new file mode 100644 index 000000000..c6c302ab1 Binary files /dev/null and b/src/plugin-slots/MobileUserMenuSlot/images/mobile_user_menu_custom_component.png differ diff --git a/src/plugin-slots/MobileUserMenuSlot/images/mobile_user_menu_custom_components_before_after.png b/src/plugin-slots/MobileUserMenuSlot/images/mobile_user_menu_custom_components_before_after.png new file mode 100644 index 000000000..d635fb75b Binary files /dev/null and b/src/plugin-slots/MobileUserMenuSlot/images/mobile_user_menu_custom_components_before_after.png differ diff --git a/src/plugin-slots/MobileUserMenuSlot/images/mobile_user_menu_modify_items.png b/src/plugin-slots/MobileUserMenuSlot/images/mobile_user_menu_modify_items.png new file mode 100644 index 000000000..33d068faf Binary files /dev/null and b/src/plugin-slots/MobileUserMenuSlot/images/mobile_user_menu_modify_items.png differ diff --git a/src/plugin-slots/MobileUserMenuSlot/index.jsx b/src/plugin-slots/MobileUserMenuSlot/index.jsx new file mode 100644 index 000000000..51f48bf49 --- /dev/null +++ b/src/plugin-slots/MobileUserMenuSlot/index.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import MobileHeaderUserMenu, { mobileHeaderUserMenuDataShape } from '../../mobile-header/MobileHeaderUserMenu'; + +const MobileUserMenuSlot = ({ + menu, +}) => ( + + + +); + +MobileUserMenuSlot.propTypes = { + menu: mobileHeaderUserMenuDataShape, +}; + +export default MobileUserMenuSlot; diff --git a/src/plugin-slots/README.md b/src/plugin-slots/README.md index a7cc56b23..12071fb4c 100644 --- a/src/plugin-slots/README.md +++ b/src/plugin-slots/README.md @@ -1,3 +1,15 @@ # `frontend-component-header` Plugin Slots * [`logo_slot`](./LogoSlot/) +* [`desktop_main_menu_slot`](./DesktopMainMenuSlot/) +* [`desktop_secondary_menu_slot`](./DesktopSecondaryMenuSlot/) +* [`mobile_main_menu_slot`](./MobileMainMenuSlot/) +* [`course_info_slot`](./CourseInfoSlot/) +* [`learning_help_slot`](./LearningHelpSlot/) +* [`desktop_logged_out_items_slot`](./DesktopLoggedOutItemsSlot/) +* [`mobile_logged_out_items_slot`](./MobileLoggedOutItemsSlot/) +* [`mobile_user_menu_slot`](./MobileUserMenuSlot/) +* [`desktop_user_menu_slot`](./DesktopUserMenuSlot/) +* [`learning_user_menu_slot`](./LearningUserMenuSlot/) +* [`learning_logged_out_items_slot`](./LearningLoggedOutItemsSlot/) +* [`desktop_header_slot`](./DesktopHeaderSlot/)