From 69750674c3710806fb41b31be9924e7872101e8e Mon Sep 17 00:00:00 2001 From: Fox Piacenti Date: Fri, 1 Aug 2025 08:58:35 -0500 Subject: [PATCH] feat: User menu trigger plugin slots. (#618) --- package.json | 2 + src/desktop-header/DesktopHeader.jsx | 25 +++--- src/desktop-header/DesktopUserMenuToggle.jsx | 20 +++++ .../AuthenticatedUserDropdown.jsx | 7 +- .../LearningUserMenuToggle.jsx | 28 +++++++ src/mobile-header/MobileHeader.jsx | 11 ++- src/mobile-header/MobileUserMenuToggle.jsx | 14 ++++ src/plugin-slots/DesktopHeaderSlot/README.md | 2 +- .../DesktopUserMenuToggleSlot/README.md | 74 ++++++++++++++++++ .../desktop_user_menu_custom_component.png | Bin 0 -> 3311 bytes .../desktop_user_menu_modified_toggle.png | Bin 0 -> 4708 bytes .../DesktopUserMenuToggleSlot/index.jsx | 21 +++++ .../LearningUserMenuToggleSlot/README.md | 74 ++++++++++++++++++ ...ning_user_menu_toggle_custom_component.png | Bin 0 -> 1873 bytes ...arning_user_menu_toggle_modified_items.png | Bin 0 -> 1248 bytes .../LearningUserMenuToggleSlot/index.jsx | 22 ++++++ .../MobileUserMenuToggleSlot/README.md | 74 ++++++++++++++++++ ...bile_user_menu_toggle_custom_component.png | Bin 0 -> 1703 bytes ...mobile_user_menu_toggle_modified_items.png | Bin 0 -> 1243 bytes .../MobileUserMenuToggleSlot/index.jsx | 23 ++++++ src/plugin-slots/README.md | 3 + 21 files changed, 377 insertions(+), 23 deletions(-) create mode 100644 src/desktop-header/DesktopUserMenuToggle.jsx create mode 100644 src/learning-header/LearningUserMenuToggle.jsx create mode 100644 src/mobile-header/MobileUserMenuToggle.jsx create mode 100644 src/plugin-slots/DesktopUserMenuToggleSlot/README.md create mode 100644 src/plugin-slots/DesktopUserMenuToggleSlot/images/desktop_user_menu_custom_component.png create mode 100644 src/plugin-slots/DesktopUserMenuToggleSlot/images/desktop_user_menu_modified_toggle.png create mode 100644 src/plugin-slots/DesktopUserMenuToggleSlot/index.jsx create mode 100644 src/plugin-slots/LearningUserMenuToggleSlot/README.md create mode 100644 src/plugin-slots/LearningUserMenuToggleSlot/images/learning_user_menu_toggle_custom_component.png create mode 100644 src/plugin-slots/LearningUserMenuToggleSlot/images/learning_user_menu_toggle_modified_items.png create mode 100644 src/plugin-slots/LearningUserMenuToggleSlot/index.jsx create mode 100644 src/plugin-slots/MobileUserMenuToggleSlot/README.md create mode 100644 src/plugin-slots/MobileUserMenuToggleSlot/images/mobile_user_menu_toggle_custom_component.png create mode 100644 src/plugin-slots/MobileUserMenuToggleSlot/images/mobile_user_menu_toggle_modified_items.png create mode 100644 src/plugin-slots/MobileUserMenuToggleSlot/index.jsx 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 0000000000000000000000000000000000000000..c16bf4521f1eb77e72f12a16d164c29465d812b7 GIT binary patch literal 3311 zcmbW3S5#Bm7KS(FsF!LlKoA5KP^y#=kSGC>UX`l!5?UyMARt9Ngd$BkNDDR6i$S`G zh*AO)nqWXWF+e~%q1^3xxDV&??uWhCnDbwA{%h>D#`+@lbkx{cxL5!HU{`;rVgLY4 zmf%?H6eIYC#Ac*`pDSop6SN`90gbWtvIiX9QEv94-nL%$_U_(JC^U_!O$ii<{t+R( z?5)vAlslgh(#;+)a`NGml;G3wapjYekdon(kc3G?VUlcg z-^g!#d7R(5!|HO^GzHIVUnH`a=46CA^Q*{KBT(f#d$(2~IhP=@oA#wQg0oI$Dpz9i zT$wO=V~zJ`GJHMw3jnN9`I&$a7&CAW!U*JYFaUa304PBKzz{zG{5lN)KBxYdju-zE zyblUb|6hY2aa#`NNyDzKeu4h2<9!R!xU!MVael!ZK+0Nv8u8$&zW=8061Wm8jX2%@Xa4KGQzOC^q|Y(o;-Je4_W)-KD$Rp9mK;r*)uOad4kyks`>oR-48s; z%gUuH6TFx{fEjav1b+iGOs_Z2nRUwur27SZxFCtF3kXas$3YFpwjTfy{ zQv`=jXV?iU>kWiA)ZwYL4vX+-OqPCWT~cg_l^zMUo*Kn|ftS$dXOtEDZNc3Ew9X#G z3X(F4@Lms#Nm00^#-}35JRB8NSuXYyVEU(kQYB)7Afs(4FPNx~S4){!&ilEBA9iK~ zlOMznEU*N((z`#9yR3|7z{nBez~`Y-qSw53DcQy)w)i01) zYFu6Oe3HuWXBr(!T_bnx*iI*0I;U_puLa|7Vdhp|Gj+T94eM35){*+0WbER~Vpt`j zr^ZNW-Ou9Jt(<;^z_sW&qfJPSPS<$zSbwlwO`Dgl1Uw?1(v%67(eaq5>G(5#LCcP~rnOz@q|~ z9K+tU1i#C^Y$Kl+a8`L^No*%f`=aG@Gk@w2#f}Gm`L@zGpWF0wq*EA1tqTqG_X-Sz zBN5dm=v~(_+%ig6{MAVneKwwm0$m;Z+G3kodK1ReayjFPBUl@tT41B=i%gasxpY|I zWA$O9b$QT`*${2P+$P7M41dcRAFi+9}}*2Nvo#iMiXrQWd3vCC%m@!S$xo+gd(G0I8`TCrUwBFuv$t`o>A;Ev1 zSJgl7qn!|>uuQly=ynj&o=`f9-X|Rq=3u>x8^ThND%TzW{B{YH9Xix0pU`-ss)(TG zC)qEOe*w^Wyp7I(I!v(RxHl&CX8o8taF{k5dO=X1n6laUU4E;1R%9kbQ>YLV{(YdQ zGEw=|t{Vx?ZrhYS%U~j$|A;LgXOFuL`h{yE4fo|189dP-DeLy=)TM{>I1#+a%^K03-*OFJVo1^9)y}t- zDD~Cru@iF|`g73!lRIIntFujs{9-r+6iCHZFpgOuT0#BC3oZ(~LU%Nr$8r3(7e zqMe^4Q*r+E8*JVcFwL5GwnEzPP6HtUAzAkx3-zY;ALU*=k|NS}{4qB%a0`VedmBtN zo;{aEL--NS=Po2AFs?Gi_=;&oW@m}zwaJZyW>2g`Xo^Pd5AD9Fb+J_3`+(0Yg@|Ez znw;o^r_^ov%=6PuTe}JY*qPT-BZQq!OM$Tx<^Μbpa;N3%1_Tfrgcez5D>^ZZvN5VeYn5E@`i*AJ- z2McJw8S0?*GjAAr7rU__nz9kuKKYY1+gZz~<)&e0B`?)q7#AaWc0Dte6(6|%dBa;E zVL(#}6Mc7`EUwTQLBQJ7aev}(h zdo9XYzC!jqZ0Jg1(*0V!Nh|7qtg-B5I4aX~mMm+`8S}#mTi+r_uqwlTX>%PTKM1ae z)hNc_d7Jz(h9UtUTGh*-C2@#$u6ty@tz*WqQU!X5M3ZXQ(&I>8~_`#mRQjayKN`jS{2q((xOO(So z5JB!gAz3@f-jR_>F3-ry8uoea>FGIbF&E7Quf|Pxe}fZ?XN)VKws_4)4f~-3lf)^* zd*;G@!>B^Vsc*v&76|8G6XdrR9{V4^&U}WrHMbw}y9r)g;e4NQel*y2f8VY>zDeEZ zD(QEp&1#^fRU0UtEb=YMVQtJX(shLTE2Ttq)rH1as_Gv<#%r4?j0*)EcU9u57TK#)!0?$>e<+_Px%bweW6 z2d%BenqXtq+UCz8CdxOn|)(M@sg(U=EC9SI2umsXH2E(#vz z#X4CU69UJ48)8yLwh5~tm>k1L&OF)qBQIt$eLE&;dd?Z-(4&{`WN~WbD6BcaNL50f3;V9fx9Zd4`Y$(g(_16F<^{~GGJgK_Eal4Ek~X6Z^ZEdee!0|yN!8)e(mcg zYQKNw?0Vff{~C$(gC+SF^~G==1|7?}d6yu~#h4 z;pbzWwvj|0&D+ljc4l1J9HL+Ceyav>KwxOA#5<0}f|Z0b7;v7Ic@?86L|PhR#-J|} zvCV{gBqPYh2!zl{vbxxJ{0`}1PTmow3w+%c2yK!pQO52b$Q`bCCg2dW(lpxpz8L9v zhnh`jW?vDil<;zb8C;9+YAV{vE$ObJ;_X6}y^9;uB76MBUh65kf<3Jxe=FaA8|Fpc z53hOCt=eD@(!ZF73q2oi4x`CL@-;rM?5tV7?A!q~)@u!HB1b(dL_pw}G_-l?K7VMvDAty55n8w@P zC{z$t@=sml>!!{X{&uE>pu^Co0ARpxZ&>_70OmG{tJ~9IRyX! literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d5eefff8d060d4999c647c11a8e135ad4a7b2de6 GIT binary patch literal 4708 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 ( + + + +); + +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 0000000000000000000000000000000000000000..950f9bdb837362aaac41f66102604005910a84ba GIT binary patch literal 1873 zcmb7_X*3&%9>tBZ4_Zs7mb61_X+KGB#5>-uFRr`d88YMK(e0U$`oOjMU@0|br-*fJ{pMN*s*V|o9MNdUW zMn(-I_W~COKo^z@>4pD;_5-oIkKV+oBIU50 zOwq1`OlYZO;gV>3&1qei`eJZL)k%qeRG+I$1rK@>bKk4BXfh39F3obd!x;F#;>1=n zb5O*iYZ9_obZt05i$urU3e)UlHHfc-mE+eKYYns$ikObV1Chi?_8tQbt-0x_ST|Qh zQp9O}Cxj~AWE!Jf{}bf&G~|8t#FqkytZ}^G0p1&^tsyD7jFV{T9t;70$#hBS0A=e> z0oZBlMi6be<8;WAuqOZ&eu!6v3s*NmnS2Ho4T@Bk$2A-C>fRT0(BRt4mNKh6YMb;< zqj2Ib;nQwb3@uTeF&t~cJ%v33(-K%Li&uq@65Oe%piN1!qk-ee)%v|!5JOtxzLXc^q}d zxGAAr1?pCMy?(6^&e^uC)Uzn9+s~F(i@$?eM<@Zb!*~BEZ2QgA*~3-!SS9~lS*sf0 z_{F^zgLBFXxBxM4ae~Q1ho7E}i$cB? zP|f;RNw`Y^>3g2uS3`up;mrQ;y;1M<8KJ(7Wc2VxGg2G@Ezd+a6n1?MCd`Z)=`hhO z@C{U!hue?Ou-^jtXGgrq0gcf7u)3Y|E6r@?7ap>qwONxTSZj~hzTm&Nu5yK0I^gvw z<@i2w>&!jB;9gdKfq{Jfv53bz^=RZyFY(qwo_EJ+bXHflVXVC2d0cet z*1#X^>J1!!Z-4%q{f#5c?s>1zy$L=`A-&*ku=qCDQnBjDAFEcxUA9)Fu?<^LAut0m$$;xiZ#U*0O27TJ=TT9-p>|$w0 z^fu4uhfK@mgWbn>!P(1!D$e@)9qr5yN3Ao@XLv56SAN$?F_}&eV=WxZEfaj${E7T1 zVxH-b3-a0;fCJCLfqPfoB`@ojNUH^DHIS<&J~uY)KsEQnA;2Poo&ju&X$CypA>;hA zn0%{R@0RM+S3YC9Iid!Blh_Cq$;=1e-|UM|gjTI^Dz@!Pod~u|AkcQWdz);2TUtyV z z`*=uBG*yHL)$c%>ZXO?=d-e(XWs*3>Xq{kM&j%JRG_{(OTh|lhWPc2z^Do=@+U=|f zSC^OT_yF5ij@UKFz6ZEb)!O}}hpeI4HaI}Nb4EBm?!@ZQVf63xcjcJSB<<7ISN`r( zbqSNSag!)20AiYe^wfWNoOSayA7NKeCGEmBJli{?t&ubOCbn~Cy&YoD;1BekSz$vX z=(jG66WA@G1rxR0mDq|e0000PbVXQnQ*UN; zcVTj60B3G*ZDlQUV{&C>ZgXgFbngSdJ^%m!Ep$a#bVG7wVRUJ4ZXi@?ZDjydXmubl zFd#~GY#=f)GBqGDGdeRcIy5&RP)#61L{vXXHqih80qjsrR7L;)|NsB_`Tzg(|NsB| z?fCin|M}cJ`Tzg$+yC&@|Leom@c-5A|NrH; z`Q^9a=IY?(>E7e$+Ti8S;_dUrmmps{bK+pCv>nU$q`owsS7)v=6kgqNzZjA_G$SAv$QrH^lbl&5u-w|52JD(P%l+-~3@QG{!R-Gj6vXkx0;Y2$o-*%>LoPti5xL z$o2}Lwln545&27!q!&dak+5qE>N?r5=jH1KM&zS?E7poF$V4Or*@$#zGar92qpG)- zL61!D*m+-lQVkQ4G6cy)q(vfB;7E?_EAWbhV-PAyQ}4Pj^CePF-B@Vq#YLq%&p4hr zcXK31a-^3dJ@I4T6C)B&44XVLw)4gS)Dxp*#u#IaF~%5UrThbTrQ-)-z$rig0000< KMNUMnLSTaG*n6%3 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..86a26fb2556054bb7bcb05b256d9008e4d54193c GIT binary patch literal 1703 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 FV1g3UgZBUc literal 0 HcmV?d00001 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/)