From b0c3d740a0ac308b284590328d53c16dbaffeb13 Mon Sep 17 00:00:00 2001 From: Arunmozhi Date: Fri, 24 Jul 2026 15:40:41 +1000 Subject: [PATCH 1/3] fix: showUserDropdown hiding LearningHeaderActionsSlot The showUserDropdown prop gates both the LearningHeaderActionsSlot and the AuthenticatedUserDropdown. This makes it impossible to just hide the userDropdown without also hiding all the action items like the help menu and the notifications. This commit applies the `showUserDropdown` gate to just the user dropdown menu. Ref: https://github.com/openedx/frontend-component-header/issues/675 --- src/learning-header/LearningHeader.jsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/learning-header/LearningHeader.jsx b/src/learning-header/LearningHeader.jsx index 9d70ee28f..a5fa0de1e 100644 --- a/src/learning-header/LearningHeader.jsx +++ b/src/learning-header/LearningHeader.jsx @@ -37,12 +37,14 @@ const LearningHeader = ({
- {showUserDropdown && authenticatedUser && ( + {authenticatedUser && ( <> - + {showUserDropdown && ( + + )} )} {showUserDropdown && !authenticatedUser && ( From 56e8dd23ef8af93e026ce9f8136733b8c8461a81 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Sat, 22 Aug 2026 20:47:51 -0500 Subject: [PATCH 2/3] feat: Add V2 of header actions slot Introduce a v2 version of LearningHeaderActionsSlot that always mounts, wrapping the existing v1 slot (HeaderNotificationsSlot and LearningHelpSlot) which stays gated behind showUserDropdown. This preserves the v1 slot API contract for existing plugin consumers while allowing new plugins to attach to v2 unconditionally. Note: HeaderNotificationsSlot/LearningHelpSlot remain hidden when showUserDropdown is false. Fully decoupling them from showUserDropdown requires deprecating v1 first, and is left for a follow-up once the DEPR lands. --- src/learning-header/LearningHeader.jsx | 2 +- src/learning-header/LearningHeader.test.jsx | 7 ++++++ .../LearningHeaderActionsSlot/index.jsx | 23 +++++++++++++++---- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/learning-header/LearningHeader.jsx b/src/learning-header/LearningHeader.jsx index a5fa0de1e..1c3715d04 100644 --- a/src/learning-header/LearningHeader.jsx +++ b/src/learning-header/LearningHeader.jsx @@ -39,7 +39,7 @@ const LearningHeader = ({ {authenticatedUser && ( <> - + {showUserDropdown && ( { expect(screen.getByText(`${courseData.courseOrg} ${courseData.courseNumber}`)).toBeInTheDocument(); expect(screen.getByText(courseData.courseTitle)).toBeInTheDocument(); }); + + it('hides the user dropdown and the header actions when showUserDropdown is false', () => { + render(
); + + expect(screen.queryByText(authenticatedUser.username)).not.toBeInTheDocument(); + expect(screen.queryByText('Help')).not.toBeInTheDocument(); + }); }); diff --git a/src/plugin-slots/LearningHeaderActionsSlot/index.jsx b/src/plugin-slots/LearningHeaderActionsSlot/index.jsx index 6055190a5..c81ee7cbb 100644 --- a/src/plugin-slots/LearningHeaderActionsSlot/index.jsx +++ b/src/plugin-slots/LearningHeaderActionsSlot/index.jsx @@ -1,15 +1,30 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { PluginSlot } from '@openedx/frontend-plugin-framework'; import HeaderNotificationsSlot from '../HeaderNotificationsSlot'; import LearningHelpSlot from '../LearningHelpSlot'; -const LearningHeaderActionsSlot = () => ( +const LearningHeaderActionsSlot = ({ showUserDropdown }) => ( - - + {showUserDropdown && ( + + + + + )} ); +LearningHeaderActionsSlot.propTypes = { + showUserDropdown: PropTypes.bool, +}; + +LearningHeaderActionsSlot.defaultProps = { + showUserDropdown: true, +}; + export default LearningHeaderActionsSlot; From 3db046b64d7c09953bd050c3044e85c48dbabc02 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Sun, 23 Aug 2026 15:16:00 -0500 Subject: [PATCH 3/3] refactor: split LearningHeaderActionsSlot into v1/v2 subfolders --- .../HeaderNotificationsSlot/README.md | 9 +- .../LearningHeaderActionsSlot/README.md | 117 +---------------- .../LearningHeaderActionsSlot/index.jsx | 30 +---- .../LearningHeaderActionsSlot/v1/README.md | 118 ++++++++++++++++++ .../LearningHeaderActionsSlot/v1/index.jsx | 15 +++ .../LearningHeaderActionsSlot/v2/README.md | 117 +++++++++++++++++ .../LearningHeaderActionsSlot/v2/index.jsx | 22 ++++ src/plugin-slots/README.md | 3 +- 8 files changed, 285 insertions(+), 146 deletions(-) create mode 100644 src/plugin-slots/LearningHeaderActionsSlot/v1/README.md create mode 100644 src/plugin-slots/LearningHeaderActionsSlot/v1/index.jsx create mode 100644 src/plugin-slots/LearningHeaderActionsSlot/v2/README.md create mode 100644 src/plugin-slots/LearningHeaderActionsSlot/v2/index.jsx diff --git a/src/plugin-slots/HeaderNotificationsSlot/README.md b/src/plugin-slots/HeaderNotificationsSlot/README.md index de32da015..24e28388a 100644 --- a/src/plugin-slots/HeaderNotificationsSlot/README.md +++ b/src/plugin-slots/HeaderNotificationsSlot/README.md @@ -12,7 +12,7 @@ This slot renders the notifications tray (bell icon + notification popover) from 1. **Desktop Header** — via `org.openedx.frontend.layout.header_desktop_secondary_menu.v2` Notifications appear before secondary menu items (e.g., "New", "Help") -2. **Learning Header** — via `org.openedx.frontend.layout.learning_header_actions.v1` +2. **Learning Header** — via `org.openedx.frontend.layout.learning_header_actions.v2` (nests `.v1` — see [LearningHeaderActionsSlot](../LearningHeaderActionsSlot/v2/)) Notifications appear before the help link 3. **Studio Header** — via `org.openedx.frontend.layout.studio_header_actions.v1` @@ -27,9 +27,10 @@ Desktop Header └── org.openedx.frontend.layout.header_desktop_secondary_menu.v1 (menu items only) Learning Header -└── org.openedx.frontend.layout.learning_header_actions.v1 - ├── org.openedx.frontend.layout.header_notifications_tray.v1 ← This slot - └── org.openedx.frontend.layout.header_learning_help.v1 +└── org.openedx.frontend.layout.learning_header_actions.v2 + └── org.openedx.frontend.layout.learning_header_actions.v1 (only when showUserDropdown is true) + ├── org.openedx.frontend.layout.header_notifications_tray.v1 ← This slot + └── org.openedx.frontend.layout.header_learning_help.v1 Studio Header └── org.openedx.frontend.layout.studio_header_actions.v1 diff --git a/src/plugin-slots/LearningHeaderActionsSlot/README.md b/src/plugin-slots/LearningHeaderActionsSlot/README.md index 449ea9440..531c781da 100644 --- a/src/plugin-slots/LearningHeaderActionsSlot/README.md +++ b/src/plugin-slots/LearningHeaderActionsSlot/README.md @@ -1,115 +1,6 @@ # Learning Header Actions Slot -### Slot ID: `org.openedx.frontend.layout.learning_header_actions.v1` - -**Default Content:** -- **Notification Tray** (via `HeaderNotificationsSlot`) — Rendered before the help link -- **Help Link** (via `LearningHelpSlot`) - ---- - -### Add Custom Components before and after Learning Header Actions - -The following `env.config.jsx` inserts a custom component before the notification tray (`priority: 10`) and another after the help link (`priority: 90`). - -![Screenshot of custom components before and after learning header actions](./images/custom_components_before_and_after_learning_actions.png) - -```jsx -import React from 'react'; -import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; - -const config = { - pluginSlots: { - 'org.openedx.frontend.layout.learning_header_actions.v1': { - keepDefault: true, - plugins: [ - { - op: PLUGIN_OPERATIONS.Insert, - widget: { - id: 'custom_before_learning_actions', - type: DIRECT_PLUGIN, - priority: 10, - RenderWidget: () => ( -

🌜

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

🌛

- ), - }, - }, - ], - }, - }, -}; - -export default config; -``` - -### Hide the Entire Learning Header Actions Area - -The following `env.config.jsx` removes both the notification tray and the help link from the learning header. - -![Screenshot of hiding learning header actions area](./images/hide_learning_actions.png) - -```jsx -import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; - -const config = { - pluginSlots: { - 'org.openedx.frontend.layout.learning_header_actions.v1': { - keepDefault: true, - plugins: [ - { - op: PLUGIN_OPERATIONS.Hide, - widgetId: 'default_contents', - }, - ], - }, - }, -}; - -export default config; -``` - -### Replace the Entire Learning Header Actions Area with a Custom Component - -The following `env.config.jsx` replaces the notification tray and help link with a single custom component. - -![Screenshot of replacing learning header actions area with custom component](./images/replace_learning_actions_with_custom_component.png) - -```jsx -import React from 'react'; -import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; - -const config = { - pluginSlots: { - 'org.openedx.frontend.layout.learning_header_actions.v1': { - keepDefault: false, - plugins: [ - { - op: PLUGIN_OPERATIONS.Insert, - widget: { - id: 'custom_learning_actions', - type: DIRECT_PLUGIN, - priority: 50, - RenderWidget: () => ( - My Custom Learning Actions - ), - }, - }, - ], - }, - }, -}; - -export default config; -``` - +| Slot ID | Description | Docs | +|---------|-------------|------| +| `org.openedx.frontend.layout.learning_header_actions.v2` | Always rendered, regardless of `showUserDropdown` (Default slot) | [v2 docs](./v2/) | +| `org.openedx.frontend.layout.learning_header_actions.v1` | Notification tray + help link, only rendered when `showUserDropdown` is `true` | [v1 docs](./v1/) | diff --git a/src/plugin-slots/LearningHeaderActionsSlot/index.jsx b/src/plugin-slots/LearningHeaderActionsSlot/index.jsx index c81ee7cbb..169204719 100644 --- a/src/plugin-slots/LearningHeaderActionsSlot/index.jsx +++ b/src/plugin-slots/LearningHeaderActionsSlot/index.jsx @@ -1,30 +1,4 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { PluginSlot } from '@openedx/frontend-plugin-framework'; -import HeaderNotificationsSlot from '../HeaderNotificationsSlot'; -import LearningHelpSlot from '../LearningHelpSlot'; - -const LearningHeaderActionsSlot = ({ showUserDropdown }) => ( - - {showUserDropdown && ( - - - - - )} - -); - -LearningHeaderActionsSlot.propTypes = { - showUserDropdown: PropTypes.bool, -}; - -LearningHeaderActionsSlot.defaultProps = { - showUserDropdown: true, -}; +import LearningHeaderActionsSlot from './v2'; +export { default as LearningHeaderActionsSlotV1 } from './v1'; export default LearningHeaderActionsSlot; diff --git a/src/plugin-slots/LearningHeaderActionsSlot/v1/README.md b/src/plugin-slots/LearningHeaderActionsSlot/v1/README.md new file mode 100644 index 000000000..2e4d0d789 --- /dev/null +++ b/src/plugin-slots/LearningHeaderActionsSlot/v1/README.md @@ -0,0 +1,118 @@ +# Learning Header Actions Slot — v1 (Notification Tray + Help Link) + +### Slot ID: `org.openedx.frontend.layout.learning_header_actions.v1` + +**Default Content:** +- **Notification Tray** (via `HeaderNotificationsSlot`) — Rendered before the help link +- **Help Link** (via `LearningHelpSlot`) + +> **Note:** This slot is only rendered when `showUserDropdown` is `true`. To render content regardless of `showUserDropdown`, use the parent [`v2` slot](../v2/). + +--- + +## Examples + +### Add Custom Components before and after Learning Header Actions + +The following `env.config.jsx` inserts a custom component before the notification tray (`priority: 10`) and another after the help link (`priority: 90`). + +![Screenshot of custom components before and after learning header actions](../images/custom_components_before_and_after_learning_actions.png) + +```jsx +import React from 'react'; +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.learning_header_actions.v1': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_before_learning_actions', + type: DIRECT_PLUGIN, + priority: 10, + RenderWidget: () => ( +

🌜

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

🌛

+ ), + }, + }, + ], + }, + }, +}; + +export default config; +``` + +### Hide the Entire Learning Header Actions Area + +The following `env.config.jsx` removes both the notification tray and the help link from the learning header. + +![Screenshot of hiding learning header actions area](../images/hide_learning_actions.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.learning_header_actions.v1': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Hide, + widgetId: 'default_contents', + }, + ], + }, + }, +}; + +export default config; +``` + +### Replace the Entire Learning Header Actions Area with a Custom Component + +The following `env.config.jsx` replaces the notification tray and help link with a single custom component. + +![Screenshot of replacing learning header actions area with custom component](../images/replace_learning_actions_with_custom_component.png) + +```jsx +import React from 'react'; +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.learning_header_actions.v1': { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_learning_actions', + type: DIRECT_PLUGIN, + priority: 50, + RenderWidget: () => ( + My Custom Learning Actions + ), + }, + }, + ], + }, + }, +}; + +export default config; +``` diff --git a/src/plugin-slots/LearningHeaderActionsSlot/v1/index.jsx b/src/plugin-slots/LearningHeaderActionsSlot/v1/index.jsx new file mode 100644 index 000000000..31a40f894 --- /dev/null +++ b/src/plugin-slots/LearningHeaderActionsSlot/v1/index.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import HeaderNotificationsSlot from '../../HeaderNotificationsSlot'; +import LearningHelpSlot from '../../LearningHelpSlot'; + +const LearningHeaderActionsSlotV1 = () => ( + + + + +); + +export default LearningHeaderActionsSlotV1; diff --git a/src/plugin-slots/LearningHeaderActionsSlot/v2/README.md b/src/plugin-slots/LearningHeaderActionsSlot/v2/README.md new file mode 100644 index 000000000..9d923e4a0 --- /dev/null +++ b/src/plugin-slots/LearningHeaderActionsSlot/v2/README.md @@ -0,0 +1,117 @@ +# Learning Header Actions Slot — v2 (Full Learning Header Actions Area) + +### Slot ID: `org.openedx.frontend.layout.learning_header_actions.v2` + +**Default Content:** +- **Learning Header Actions v1** (via [`LearningHeaderActionsSlotV1`](../v1/)) — Notification tray + help link, only rendered when `showUserDropdown` is `true` + +This slot always renders, regardless of the `showUserDropdown` prop passed to `LearningHeader`. Use it to add, hide, or replace the whole actions area independently of `showUserDropdown`. + +--- + +## Examples + +### Add Custom Components before and after the Learning Header Actions Area + +The following `env.config.jsx` inserts a custom component before the notification tray/help link (`priority: 10`) and another after (`priority: 90`). These render even when `showUserDropdown` is `false`. + +![Screenshot of custom components before and after learning header actions](../images/custom_components_before_and_after_learning_actions.png) + +```jsx +import React from 'react'; +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.learning_header_actions.v2': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_before_learning_actions', + type: DIRECT_PLUGIN, + priority: 10, + RenderWidget: () => ( +

🌜

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

🌛

+ ), + }, + }, + ], + }, + }, +}; + +export default config; +``` + +### Hide the Entire Learning Header Actions Area + +The following `env.config.jsx` removes the actions area (notification tray + help link) from the learning header, regardless of `showUserDropdown`. + +![Screenshot of hiding learning header actions area](../images/hide_learning_actions.png) + +```jsx +import { PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.learning_header_actions.v2': { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Hide, + widgetId: 'default_contents', + }, + ], + }, + }, +}; + +export default config; +``` + +### Replace the Entire Learning Header Actions Area with a Custom Component + +The following `env.config.jsx` replaces the actions area with a single custom component that renders regardless of `showUserDropdown`. + +![Screenshot of replacing learning header actions area with custom component](../images/replace_learning_actions_with_custom_component.png) + +```jsx +import React from 'react'; +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; + +const config = { + pluginSlots: { + 'org.openedx.frontend.layout.learning_header_actions.v2': { + keepDefault: false, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_learning_actions', + type: DIRECT_PLUGIN, + priority: 50, + RenderWidget: () => ( + My Custom Learning Actions + ), + }, + }, + ], + }, + }, +}; + +export default config; +``` diff --git a/src/plugin-slots/LearningHeaderActionsSlot/v2/index.jsx b/src/plugin-slots/LearningHeaderActionsSlot/v2/index.jsx new file mode 100644 index 000000000..e39765c27 --- /dev/null +++ b/src/plugin-slots/LearningHeaderActionsSlot/v2/index.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; +import LearningHeaderActionsSlotV1 from '../v1'; + +const LearningHeaderActionsSlot = ({ showUserDropdown }) => ( + + {showUserDropdown && } + +); + +LearningHeaderActionsSlot.propTypes = { + showUserDropdown: PropTypes.bool, +}; + +LearningHeaderActionsSlot.defaultProps = { + showUserDropdown: true, +}; + +export default LearningHeaderActionsSlot; diff --git a/src/plugin-slots/README.md b/src/plugin-slots/README.md index 3a5d4aa7a..79810b25f 100644 --- a/src/plugin-slots/README.md +++ b/src/plugin-slots/README.md @@ -15,7 +15,8 @@ ### Learning Header * [`org.openedx.frontend.layout.header_learning_course_info.v1`](./CourseInfoSlot/) -* [`org.openedx.frontend.layout.learning_header_actions.v1`](./LearningHeaderActionsSlot/) +* [`org.openedx.frontend.layout.learning_header_actions.v1`](./LearningHeaderActionsSlot/v1/) +* [`org.openedx.frontend.layout.learning_header_actions.v2`](./LearningHeaderActionsSlot/v2/) * [`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/)