diff --git a/src/Header.jsx b/src/Header.jsx index 2f190f205..0aee2c839 100644 --- a/src/Header.jsx +++ b/src/Header.jsx @@ -45,9 +45,10 @@ subscribe(APP_CONFIG_INITIALIZED, () => { * See the documentation for the structure of secondary menu item. * @param {list} userMenuItems - The list of user menu items to display. * See the documentation for the structure of user menu item. + * @param {string} logoDestination - The destination of the logo. */ const Header = ({ - intl, mainMenuItems, secondaryMenuItems, userMenuItems, + intl, mainMenuItems, secondaryMenuItems, userMenuItems, logoDestination, }) => { const { authenticatedUser, config } = useContext(AppContext); @@ -110,7 +111,7 @@ const Header = ({ const props = { logo: config.LOGO_URL, logoAltText: config.SITE_NAME, - logoDestination: `${config.LMS_BASE_URL}/dashboard`, + logoDestination: logoDestination || `${config.LMS_BASE_URL}/dashboard`, loggedIn: authenticatedUser !== null, username: authenticatedUser !== null ? authenticatedUser.username : null, avatar: authenticatedUser !== null ? authenticatedUser.avatar : null, @@ -136,6 +137,7 @@ Header.defaultProps = { mainMenuItems: null, secondaryMenuItems: null, userMenuItems: null, + logoDestination: null, }; Header.propTypes = { @@ -157,6 +159,10 @@ Header.propTypes = { isActive: PropTypes.bool, })), })), + logoDestination: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.oneOf([false, null]), + ]), }; export default injectIntl(Header); diff --git a/src/Header.test.jsx b/src/Header.test.jsx index 51fef2089..abebbf833 100644 --- a/src/Header.test.jsx +++ b/src/Header.test.jsx @@ -101,4 +101,30 @@ describe('
', () => { expect(wrapper.toJSON()).toMatchSnapshot(); }); + + it('renders correctly with custom logoDestination', () => { + const contextValue = { + authenticatedUser: null, + config: { + LMS_BASE_URL: process.env.LMS_BASE_URL, + SITE_NAME: process.env.SITE_NAME, + LOGIN_URL: process.env.LOGIN_URL, + LOGOUT_URL: process.env.LOGOUT_URL, + LOGO_URL: process.env.LOGO_URL, + }, + }; + const customLogoDestination = '/custom-destination'; + const component = ( + + + +
+ + + + ); + + const wrapper = TestRenderer.create(component); + expect(wrapper.toJSON()).toMatchSnapshot(); + }); }); diff --git a/src/__snapshots__/Header.test.jsx.snap b/src/__snapshots__/Header.test.jsx.snap index 781e7f729..9c0615bba 100644 --- a/src/__snapshots__/Header.test.jsx.snap +++ b/src/__snapshots__/Header.test.jsx.snap @@ -443,3 +443,63 @@ exports[`
renders correctly for authenticated mobile 1`] = `
`; + +exports[`
renders correctly with custom logoDestination 1`] = ` +
+ + Skip to main content + + +
+`; diff --git a/src/desktop-header/DesktopHeader.jsx b/src/desktop-header/DesktopHeader.jsx index 4b5e4939f..6cc810217 100644 --- a/src/desktop-header/DesktopHeader.jsx +++ b/src/desktop-header/DesktopHeader.jsx @@ -115,7 +115,10 @@ export const desktopHeaderDataShape = { loggedOutItems: desktopLoggedOutItemsDataShape, logo: PropTypes.string, logoAltText: PropTypes.string, - logoDestination: PropTypes.string, + logoDestination: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.oneOf([false, null]), + ]), avatar: PropTypes.string, username: PropTypes.string, loggedIn: PropTypes.bool, @@ -128,7 +131,7 @@ DesktopHeader.propTypes = { loggedOutItems: desktopHeaderDataShape.loggedOutItemsmainMenu, logo: desktopHeaderDataShape.logomainMenu, logoAltText: desktopHeaderDataShape.logoAltTextmainMenu, - logoDestination: desktopHeaderDataShape.logoDestinationmainMenu, + logoDestination: desktopHeaderDataShape.logoDestination, avatar: desktopHeaderDataShape.avatarmainMenu, username: desktopHeaderDataShape.usernamemainMenu, loggedIn: desktopHeaderDataShape.loggedInmainMenu, diff --git a/src/mobile-header/MobileHeader.jsx b/src/mobile-header/MobileHeader.jsx index 7a04ec7e7..b98ef801e 100644 --- a/src/mobile-header/MobileHeader.jsx +++ b/src/mobile-header/MobileHeader.jsx @@ -118,7 +118,10 @@ export const mobileHeaderDataShape = { loggedOutItems: mobileHeaderLoggedOutItemsDataShape, logo: PropTypes.string, logoAltText: PropTypes.string, - logoDestination: PropTypes.string, + logoDestination: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.oneOf([false, null]), + ]), avatar: PropTypes.string, username: PropTypes.string, loggedIn: PropTypes.bool, diff --git a/src/plugin-slots/DesktopMainMenuSlot/README.md b/src/plugin-slots/DesktopMainMenuSlot/README.md index b266cdc6b..4650ead9c 100644 --- a/src/plugin-slots/DesktopMainMenuSlot/README.md +++ b/src/plugin-slots/DesktopMainMenuSlot/README.md @@ -135,3 +135,54 @@ const config = { export default config; ``` +### Add Custom Marketing links + +This `env.config.jsx` will add new custom **Marketing links** to the desktop main menu. + +![Screenshot of custom marketing links](./images/desktop_main_menu_marketing_links.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const modifyMainMenu = (widget) => { + const existingMenu = widget.RenderWidget.props.menu || []; + + const newMarketingLinks = [ + { + type: 'item', + href: 'https://example.com/how-it-works', + content: 'How it works', + }, + { + type: 'item', + href: 'https://example.com/courses', + content: 'Courses', + }, + { + type: 'item', + href: 'https://example.com/schools', + content: 'Schools', + } + ]; + + widget.content.menu = [...existingMenu, ...newMarketingLinks]; + return widget; +}; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.header_desktop_main_menu.v1': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: modifyMainMenu, + }, + ] + }, + }, +} + +export default config; +``` diff --git a/src/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_marketing_links.png b/src/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_marketing_links.png new file mode 100644 index 000000000..3e3e6acb6 Binary files /dev/null and b/src/plugin-slots/DesktopMainMenuSlot/images/desktop_main_menu_marketing_links.png differ