diff --git a/package.json b/package.json index ee8ac82..2f0fbce 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "snapshot": "fedx-scripts jest --updateSnapshot", "start": "fedx-scripts webpack-dev-server --progress", "test": "fedx-scripts jest --coverage", + "test:dev": "fedx-scripts jest --watchAll", "types": "tsc --noEmit" }, "files": [ @@ -70,3 +71,4 @@ "react-router-dom": "^6.14.2" } } + diff --git a/src/desktop-header/DesktopHeader.jsx b/src/desktop-header/DesktopHeader.jsx index 9982950..973b86f 100644 --- a/src/desktop-header/DesktopHeader.jsx +++ b/src/desktop-header/DesktopHeader.jsx @@ -4,8 +4,9 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { getConfig } from '@edx/frontend-platform'; // Local Components +import DesktopUserMenuToggleSlot + from '../plugin-slots/DesktopUserMenuToggleSlot'; 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'; @@ -19,7 +20,6 @@ import { desktopUserMenuDataShape } from './DesktopHeaderUserMenu'; import messages from '../Header.messages'; // Assets -import { CaretIcon } from '../Icons'; class DesktopHeader extends React.Component { constructor(props) { // eslint-disable-line @typescript-eslint/no-useless-constructor @@ -51,8 +51,7 @@ class DesktopHeader extends React.Component { aria-label={intl.formatMessage(messages['header.label.account.menu.for'], { username })} className="btn btn-outline-primary d-inline-flex align-items-center pl-2 pr-3" > - - {username} + @@ -123,15 +122,15 @@ export const desktopHeaderDataShape = { 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, + secondaryMenu: desktopHeaderDataShape.secondaryMenu, + userMenu: desktopHeaderDataShape.userMenu, + loggedOutItems: desktopHeaderDataShape.loggedOutItems, + logo: desktopHeaderDataShape.logo, + logoAltText: desktopHeaderDataShape.logoAltText, + logoDestination: desktopHeaderDataShape.logoDestination, + avatar: desktopHeaderDataShape.avatar, + username: desktopHeaderDataShape.username, + loggedIn: desktopHeaderDataShape.loggedIn, // i18n intl: intlShape.isRequired, diff --git a/src/desktop-header/DesktopUserMenuToggle.jsx b/src/desktop-header/DesktopUserMenuToggle.jsx new file mode 100644 index 0000000..f905714 --- /dev/null +++ b/src/desktop-header/DesktopUserMenuToggle.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { CaretIcon } from '../Icons'; +import Avatar from '../Avatar'; + +const DesktopUserMenuToggle = ({ avatar, label }) => ( + <> + + {label} + +); + +export const DesktopUserMenuTogglePropTypes = { + avatar: PropTypes.string, + label: PropTypes.string, +}; + +DesktopUserMenuToggle.propTypes = DesktopUserMenuTogglePropTypes; + +export default DesktopUserMenuToggle; diff --git a/src/learning-header/AuthenticatedUserDropdown.jsx b/src/learning-header/AuthenticatedUserDropdown.jsx index 9336796..e270662 100644 --- a/src/learning-header/AuthenticatedUserDropdown.jsx +++ b/src/learning-header/AuthenticatedUserDropdown.jsx @@ -1,12 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faUserCircle } from '@fortawesome/free-solid-svg-icons'; import { getConfig } from '@edx/frontend-platform'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Dropdown } from '@openedx/paragon'; +import LearningUserMenuToggleSlot from '../plugin-slots/LearningUserMenuToggleSlot'; import LearningUserMenuSlot from '../plugin-slots/LearningUserMenuSlot'; import messages from './messages'; @@ -38,10 +38,7 @@ const AuthenticatedUserDropdown = ({ intl, username }) => { return ( - - - {username} - + diff --git a/src/learning-header/LearningUserMenuToggle.jsx b/src/learning-header/LearningUserMenuToggle.jsx new file mode 100644 index 0000000..9f9d623 --- /dev/null +++ b/src/learning-header/LearningUserMenuToggle.jsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import PropTypes from 'prop-types'; + +const LearningUserMenuToggle = ({ + label, + icon, +}) => ( + <> + + + {label} + + +); + +export const LearningUserMenuTogglePropTypes = { + label: PropTypes.string.isRequired, + // Full shape available by examining @fortawesome/fontawesome-common-types/index.d.ts. + icon: PropTypes.shape({ + prefix: PropTypes.string.isRequired, + iconName: PropTypes.string.isRequired, + }).isRequired, +}; + +LearningUserMenuToggle.propTypes = LearningUserMenuTogglePropTypes; + +export default LearningUserMenuToggle; diff --git a/src/mobile-header/MobileHeader.jsx b/src/mobile-header/MobileHeader.jsx index 70d808e..788d17e 100644 --- a/src/mobile-header/MobileHeader.jsx +++ b/src/mobile-header/MobileHeader.jsx @@ -4,8 +4,8 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { getConfig } from '@edx/frontend-platform'; // Local Components +import MobileUserMenuToggleSlot from '../plugin-slots/MobileUserMenuToggleSlot'; 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'; @@ -40,14 +40,17 @@ class MobileHeader extends React.Component { return ; } + renderUserMenuToggle() { + const { avatar, username } = this.props; + return ; + } + render() { const { logo, logoAltText, logoDestination, loggedIn, - avatar, - username, stickyOnMobile, intl, mainMenu, @@ -98,7 +101,7 @@ class MobileHeader extends React.Component { aria-label={intl.formatMessage(messages['header.label.account.menu'])} title={intl.formatMessage(messages['header.label.account.menu'])} > - + {this.renderUserMenuToggle()} {loggedIn ? this.renderUserMenuItems() : this.renderLoggedOutItems()} diff --git a/src/mobile-header/MobileUserMenuToggle.jsx b/src/mobile-header/MobileUserMenuToggle.jsx new file mode 100644 index 0000000..7f2eeb9 --- /dev/null +++ b/src/mobile-header/MobileUserMenuToggle.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Avatar from '../Avatar'; + +const MobileUserMenuToggle = ({ avatar, username }) => ; + +export const MobileUserMenuTogglePropTypes = { + avatar: PropTypes.string, + username: PropTypes.string, +}; + +MobileUserMenuToggle.propTypes = MobileUserMenuTogglePropTypes; + +export default MobileUserMenuToggle; diff --git a/src/plugin-slots/DesktopHeaderSlot/README.md b/src/plugin-slots/DesktopHeaderSlot/README.md index 890e51b..631d2ba 100644 --- a/src/plugin-slots/DesktopHeaderSlot/README.md +++ b/src/plugin-slots/DesktopHeaderSlot/README.md @@ -41,4 +41,4 @@ const config = { } export default config; -``` \ No newline at end of file +``` diff --git a/src/plugin-slots/DesktopUserMenuToggleSlot/README.md b/src/plugin-slots/DesktopUserMenuToggleSlot/README.md new file mode 100644 index 0000000..4e8c6a4 --- /dev/null +++ b/src/plugin-slots/DesktopUserMenuToggleSlot/README.md @@ -0,0 +1,74 @@ +# Desktop User Menu Toggle Slot + +### Slot ID: `org.openedx.frontend.layout.header_desktop_user_menu_toggle.v1` + +## Description + +This slot is used to replace/modify/hide the contents of the user menu toggle button on desktop sized screens. + +## Examples + +### Modify Label Text + +The following `env.config.jsx` will modify the label text to be something more generic: + +![Screenshot of modified label](./images/desktop_user_menu_modified_toggle.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; +import { faHouse } from '@fortawesome/free-solid-svg-icons'; + +const modifyUserMenuToggle = ( widget ) => { + widget.content.label = "My Profile"; + return widget; +}; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.header_desktop_user_menu_toggle.v1': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: modifyUserMenuToggle, + }, + ] + }, + }, +} + +export default config; +``` + +### Replace Menu toggle contents with Custom Component + +The following `env.config.jsx` will replace the contents of the learning user menu toggle button entirely (in this case with an emoji) + +![Screenshot of replaced with custom component](./images/desktop_user_menu_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.header_desktop_user_menu_toggle.v1': { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_desktop_user_menu_toggle', + type: DIRECT_PLUGIN, + RenderWidget: () => ( + 🦊 + ), + }, + }, + ] + }, + }, +} + +export default config; +``` diff --git a/src/plugin-slots/DesktopUserMenuToggleSlot/images/desktop_user_menu_custom_component.png b/src/plugin-slots/DesktopUserMenuToggleSlot/images/desktop_user_menu_custom_component.png new file mode 100644 index 0000000..c16bf45 Binary files /dev/null and b/src/plugin-slots/DesktopUserMenuToggleSlot/images/desktop_user_menu_custom_component.png differ diff --git a/src/plugin-slots/DesktopUserMenuToggleSlot/images/desktop_user_menu_modified_toggle.png b/src/plugin-slots/DesktopUserMenuToggleSlot/images/desktop_user_menu_modified_toggle.png new file mode 100644 index 0000000..d5eefff Binary files /dev/null and b/src/plugin-slots/DesktopUserMenuToggleSlot/images/desktop_user_menu_modified_toggle.png differ diff --git a/src/plugin-slots/DesktopUserMenuToggleSlot/index.jsx b/src/plugin-slots/DesktopUserMenuToggleSlot/index.jsx new file mode 100644 index 0000000..d9af0f2 --- /dev/null +++ b/src/plugin-slots/DesktopUserMenuToggleSlot/index.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import DesktopUserMenuToggle, { DesktopUserMenuTogglePropTypes } from '../../desktop-header/DesktopUserMenuToggle'; + +const DesktopUserMenuToggleSlot = ({ + avatar, + label, +}) => ( + + + +); + +DesktopUserMenuToggleSlot.propTypes = DesktopUserMenuTogglePropTypes; + +export default DesktopUserMenuToggleSlot; diff --git a/src/plugin-slots/LearningUserMenuToggleSlot/README.md b/src/plugin-slots/LearningUserMenuToggleSlot/README.md new file mode 100644 index 0000000..7e9b158 --- /dev/null +++ b/src/plugin-slots/LearningUserMenuToggleSlot/README.md @@ -0,0 +1,74 @@ +# Learning User Menu Toggle Slot + +### Slot ID: `org.openedx.frontend.layout.header_learning_user_menu_toggle.v1` + +## Description + +This slot is used to replace/modify/hide the contents of the learning user menu toggle button. + +## Examples + +### Modify Icon + +The following `env.config.jsx` will modify the icon for the learning user menu toggle button. **Note:** The icon is only shown on mobile screens. + +![Screenshot of modified items](./images/learning_user_menu_toggle_modified_items.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; +import { faHouse } from '@fortawesome/free-solid-svg-icons'; + +const modifyUserMenuToggle = ( widget ) => { + widget.content.icon = faHouse; + return widget; +}; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.header_learning_user_menu_toggle.v1': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: modifyUserMenuToggle, + }, + ] + }, + }, +} + +export default config; +``` + +### Replace Menu toggle contents with Custom Component + +The following `env.config.jsx` will replace the contents of the learning user menu toggle button's contents entirely (in this case with an emoji) + +![Screenshot of replaced with custom component](./images/learning_user_menu_toggle_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.header_learning_user_menu_toggle.v1': { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_learning_user_menu_toggle', + type: DIRECT_PLUGIN, + RenderWidget: () => ( + 🦊 + ), + }, + }, + ] + }, + }, +} + +export default config; +``` diff --git a/src/plugin-slots/LearningUserMenuToggleSlot/images/learning_user_menu_toggle_custom_component.png b/src/plugin-slots/LearningUserMenuToggleSlot/images/learning_user_menu_toggle_custom_component.png new file mode 100644 index 0000000..950f9bd Binary files /dev/null and b/src/plugin-slots/LearningUserMenuToggleSlot/images/learning_user_menu_toggle_custom_component.png differ diff --git a/src/plugin-slots/LearningUserMenuToggleSlot/images/learning_user_menu_toggle_modified_items.png b/src/plugin-slots/LearningUserMenuToggleSlot/images/learning_user_menu_toggle_modified_items.png new file mode 100644 index 0000000..40ee175 Binary files /dev/null and b/src/plugin-slots/LearningUserMenuToggleSlot/images/learning_user_menu_toggle_modified_items.png differ diff --git a/src/plugin-slots/LearningUserMenuToggleSlot/index.jsx b/src/plugin-slots/LearningUserMenuToggleSlot/index.jsx new file mode 100644 index 0000000..636989a --- /dev/null +++ b/src/plugin-slots/LearningUserMenuToggleSlot/index.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import LearningUserMenuToggle, { + LearningUserMenuTogglePropTypes, +} from '../../learning-header/LearningUserMenuToggle'; + +const LearningUserMenuToggleSlot = ({ + label, icon, +}) => ( + + + +); + +LearningUserMenuToggleSlot.propTypes = LearningUserMenuTogglePropTypes; + +export default LearningUserMenuToggleSlot; diff --git a/src/plugin-slots/MobileUserMenuToggleSlot/README.md b/src/plugin-slots/MobileUserMenuToggleSlot/README.md new file mode 100644 index 0000000..717be40 --- /dev/null +++ b/src/plugin-slots/MobileUserMenuToggleSlot/README.md @@ -0,0 +1,74 @@ +# Mobile User Menu Toggle Slot + +### Slot ID: `org.openedx.frontend.layout.header_mobile_user_menu_trigger.v1` + +## Description + +This slot is used to replace/modify/hide the contents of the user menu toggle button on mobile screens. + +## Examples + +### Modify Avatar + +The following `env.config.jsx` will modify the icon for the user menu toggle button on mobile. + +![Screenshot of modified items](./images/mobile_user_menu_toggle_modified_items.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const modifyUserMenuToggle = ( widget ) => { + // Shows a dummy image with the resolution marker '30x30'. + widget.content.avatar = "https://dummyimage.com/30x30" + return widget; +}; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.header_mobile_user_menu_trigger.v1': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Modify, + widgetId: 'default_contents', + fn: modifyUserMenuToggle, + }, + ] + }, + }, +} + +export default config; +``` + +### Replace Menu toggle contents with Custom Component + +The following `env.config.jsx` will replace the contents of the user menu toggle button's contents entirely (in this case with an emoji). + +![Screenshot of replaced with custom component](./images/mobile_user_menu_toggle_custom_component.png) + +```jsx +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.header_mobile_user_menu_trigger.v1': { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_mobile_user_menu_toggle', + type: DIRECT_PLUGIN, + RenderWidget: () => ( + 🦊 + ), + }, + }, + ] + }, + }, +} + +export default config; +``` diff --git a/src/plugin-slots/MobileUserMenuToggleSlot/images/mobile_user_menu_toggle_custom_component.png b/src/plugin-slots/MobileUserMenuToggleSlot/images/mobile_user_menu_toggle_custom_component.png new file mode 100644 index 0000000..86a26fb Binary files /dev/null and b/src/plugin-slots/MobileUserMenuToggleSlot/images/mobile_user_menu_toggle_custom_component.png differ diff --git a/src/plugin-slots/MobileUserMenuToggleSlot/images/mobile_user_menu_toggle_modified_items.png b/src/plugin-slots/MobileUserMenuToggleSlot/images/mobile_user_menu_toggle_modified_items.png new file mode 100644 index 0000000..476ddd7 Binary files /dev/null and b/src/plugin-slots/MobileUserMenuToggleSlot/images/mobile_user_menu_toggle_modified_items.png differ diff --git a/src/plugin-slots/MobileUserMenuToggleSlot/index.jsx b/src/plugin-slots/MobileUserMenuToggleSlot/index.jsx new file mode 100644 index 0000000..cb574ce --- /dev/null +++ b/src/plugin-slots/MobileUserMenuToggleSlot/index.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import MobileUserMenuToggle, { + MobileUserMenuTogglePropTypes, +} from '../../mobile-header/MobileUserMenuToggle'; + +const MobileUserMenuToggleSlot = ({ + avatar, + label, +}) => ( + + + +); + +MobileUserMenuToggleSlot.propTypes = MobileUserMenuTogglePropTypes; + +export default MobileUserMenuToggleSlot; diff --git a/src/plugin-slots/README.md b/src/plugin-slots/README.md index 2cf12ce..21e158f 100644 --- a/src/plugin-slots/README.md +++ b/src/plugin-slots/README.md @@ -9,15 +9,18 @@ * [`org.openedx.frontend.layout.header_desktop_main_menu.v1`](./DesktopMainMenuSlot/) * [`org.openedx.frontend.layout.header_desktop_secondary_menu.v1`](./DesktopSecondaryMenuSlot/) * [`org.openedx.frontend.layout.header_desktop_user_menu.v1`](./DesktopUserMenuSlot/) +* [`org.openedx.frontend.layout.header_desktop_user_menu_toggle.v1`](./DesktopUserMenuToggleSlot/) ### Learning Header * [`org.openedx.frontend.layout.header_learning_course_info.v1`](./CourseInfoSlot/) * [`org.openedx.frontend.layout.header_learning_help.v1`](./LearningHelpSlot/) * [`org.openedx.frontend.layout.header_learning_logged_out_items.v1`](./LearningLoggedOutItemsSlot/) * [`org.openedx.frontend.layout.header_learning_user_menu.v1`](./LearningUserMenuSlot/) +* [`org.openedx.frontend.layout.header_learning_user_menu.v1`](./LearningUserMenuSlot/) ### Mobile Header * [`org.openedx.frontend.layout.header_mobile.v1`](./MobileHeaderSlot/) * [`org.openedx.frontend.layout.header_mobile_logged_out_items.v1`](./MobileLoggedOutItemsSlot/) * [`org.openedx.frontend.layout.header_mobile_main_menu.v1`](./MobileMainMenuSlot/) * [`org.openedx.frontend.layout.header_mobile_user_menu.v1`](./MobileUserMenuSlot/) +* [`org.openedx.frontend.layout.header_mobile_user_menu_trigger.v1`](./MobileUserMenuToggleSlot/)