From d113c035105b940b5b9d5551b8e8c12461819b8a Mon Sep 17 00:00:00 2001 From: Arunmozhi Date: Fri, 24 Jul 2026 15:40:41 +1000 Subject: [PATCH 1/7] 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 | 12 +- src/learning-header/LearningHeader.test.jsx | 7 ++ .../HeaderNotificationsSlot/README.md | 9 +- .../LearningHeaderActionsSlot/README.md | 117 +---------------- .../LearningHeaderActionsSlot/index.jsx | 15 +-- .../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 +- 10 files changed, 299 insertions(+), 136 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/learning-header/LearningHeader.jsx b/src/learning-header/LearningHeader.jsx index 9d70ee28f6..1c3715d044 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 && ( diff --git a/src/learning-header/LearningHeader.test.jsx b/src/learning-header/LearningHeader.test.jsx index 3d80888efe..3b81610f25 100644 --- a/src/learning-header/LearningHeader.test.jsx +++ b/src/learning-header/LearningHeader.test.jsx @@ -26,4 +26,11 @@ describe('Header', () => { 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/HeaderNotificationsSlot/README.md b/src/plugin-slots/HeaderNotificationsSlot/README.md index de32da015c..24e28388a7 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 449ea9440c..531c781da5 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 6055190a52..1692047199 100644 --- a/src/plugin-slots/LearningHeaderActionsSlot/index.jsx +++ b/src/plugin-slots/LearningHeaderActionsSlot/index.jsx @@ -1,15 +1,4 @@ -import React from 'react'; -import { PluginSlot } from '@openedx/frontend-plugin-framework'; -import HeaderNotificationsSlot from '../HeaderNotificationsSlot'; -import LearningHelpSlot from '../LearningHelpSlot'; - -const LearningHeaderActionsSlot = () => ( - - - - -); +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 0000000000..2e4d0d789e --- /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 0000000000..31a40f8941 --- /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 0000000000..9d923e4a06 --- /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 0000000000..e39765c279 --- /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 3a5d4aa7a4..79810b25fe 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/) From 26b1919c528d828dd58fee375b59b4cab2179da8 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Thu, 3 Sep 2026 20:26:25 -0500 Subject: [PATCH 2/7] fix: mount LearningHeaderActionsSlot v2 for anonymous users --- src/learning-header/LearningHeader.jsx | 14 +++++--------- .../LearningHeaderActionsSlot/v2/index.jsx | 8 ++++---- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/learning-header/LearningHeader.jsx b/src/learning-header/LearningHeader.jsx index 1c3715d044..a1801bb280 100644 --- a/src/learning-header/LearningHeader.jsx +++ b/src/learning-header/LearningHeader.jsx @@ -37,15 +37,11 @@ const LearningHeader = ({
- {authenticatedUser && ( - <> - - {showUserDropdown && ( - - )} - + + {authenticatedUser && showUserDropdown && ( + )} {showUserDropdown && !authenticatedUser && ( diff --git a/src/plugin-slots/LearningHeaderActionsSlot/v2/index.jsx b/src/plugin-slots/LearningHeaderActionsSlot/v2/index.jsx index e39765c279..a276b09aac 100644 --- a/src/plugin-slots/LearningHeaderActionsSlot/v2/index.jsx +++ b/src/plugin-slots/LearningHeaderActionsSlot/v2/index.jsx @@ -3,20 +3,20 @@ import PropTypes from 'prop-types'; import { PluginSlot } from '@openedx/frontend-plugin-framework'; import LearningHeaderActionsSlotV1 from '../v1'; -const LearningHeaderActionsSlot = ({ showUserDropdown }) => ( +const LearningHeaderActionsSlot = ({ showDefaultActions }) => ( - {showUserDropdown && } + {showDefaultActions && } ); LearningHeaderActionsSlot.propTypes = { - showUserDropdown: PropTypes.bool, + showDefaultActions: PropTypes.bool, }; LearningHeaderActionsSlot.defaultProps = { - showUserDropdown: true, + showDefaultActions: true, }; export default LearningHeaderActionsSlot; From 5650a8e01113771ec6e8f0f8eb3a9256f3aeca8f Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Thu, 3 Sep 2026 20:30:25 -0500 Subject: [PATCH 3/7] docs: clarify authenticatedUser gate and slot hierarchy for LearningHeaderActionsSlot --- src/plugin-slots/HeaderNotificationsSlot/README.md | 4 ++-- src/plugin-slots/LearningHeaderActionsSlot/README.md | 2 +- src/plugin-slots/LearningHeaderActionsSlot/v1/README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugin-slots/HeaderNotificationsSlot/README.md b/src/plugin-slots/HeaderNotificationsSlot/README.md index 24e28388a7..ed95d9f72b 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.v2` (nests `.v1` — see [LearningHeaderActionsSlot](../LearningHeaderActionsSlot/v2/)) +2. **Learning Header** — via `org.openedx.frontend.layout.learning_header_actions.v1` (nested within [`.v2`](../LearningHeaderActionsSlot/v2/)) Notifications appear before the help link 3. **Studio Header** — via `org.openedx.frontend.layout.studio_header_actions.v1` @@ -28,7 +28,7 @@ Desktop Header Learning Header └── 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.learning_header_actions.v1 (only when showUserDropdown is true and a user is authenticated) ├── org.openedx.frontend.layout.header_notifications_tray.v1 ← This slot └── org.openedx.frontend.layout.header_learning_help.v1 diff --git a/src/plugin-slots/LearningHeaderActionsSlot/README.md b/src/plugin-slots/LearningHeaderActionsSlot/README.md index 531c781da5..0bd6292c7f 100644 --- a/src/plugin-slots/LearningHeaderActionsSlot/README.md +++ b/src/plugin-slots/LearningHeaderActionsSlot/README.md @@ -3,4 +3,4 @@ | 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/) | +| `org.openedx.frontend.layout.learning_header_actions.v1` | Notification tray + help link, only rendered when `showUserDropdown` is `true` and a user is authenticated | [v1 docs](./v1/) | diff --git a/src/plugin-slots/LearningHeaderActionsSlot/v1/README.md b/src/plugin-slots/LearningHeaderActionsSlot/v1/README.md index 2e4d0d789e..855891e2c8 100644 --- a/src/plugin-slots/LearningHeaderActionsSlot/v1/README.md +++ b/src/plugin-slots/LearningHeaderActionsSlot/v1/README.md @@ -6,7 +6,7 @@ - **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/). +> **Note:** This slot is only rendered when `showUserDropdown` is `true` and a user is authenticated. To render content regardless of `showUserDropdown` or authentication, use the parent [`v2` slot](../v2/). --- From 3ae94415553c95ec2e4ec869014ca7242d8c634d Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Thu, 3 Sep 2026 20:40:21 -0500 Subject: [PATCH 4/7] test: New tests to cover v2 paths --- src/learning-header/LearningHeader.test.jsx | 48 +++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/learning-header/LearningHeader.test.jsx b/src/learning-header/LearningHeader.test.jsx index 3b81610f25..cffdcd9e7e 100644 --- a/src/learning-header/LearningHeader.test.jsx +++ b/src/learning-header/LearningHeader.test.jsx @@ -1,9 +1,36 @@ import React from 'react'; +import { mergeConfig } from '@edx/frontend-platform'; +import { AppContext } from '@edx/frontend-platform/react'; +import { PLUGIN_OPERATIONS, DIRECT_PLUGIN } from '@openedx/frontend-plugin-framework'; import { authenticatedUser, initializeMockApp, render, screen, } from '../setupTest'; import { LearningHeader as Header } from '../index'; +const V2_SLOT_ID = 'org.openedx.frontend.layout.learning_header_actions.v2'; +const V2_WIDGET_TEXT = 'Test V2 Widget'; + +const configureV2Widget = () => { + mergeConfig({ + pluginSlots: { + [V2_SLOT_ID]: { + keepDefault: true, + plugins: [ + { + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'test_v2_widget', + type: DIRECT_PLUGIN, + priority: 10, + RenderWidget: () => {V2_WIDGET_TEXT}, + }, + }, + ], + }, + }, + }); +}; + describe('Header', () => { beforeAll(async () => { // We need to mock AuthService to implicitly use `getAuthenticatedUser` within `AppContext.Provider`. @@ -33,4 +60,25 @@ describe('Header', () => { expect(screen.queryByText(authenticatedUser.username)).not.toBeInTheDocument(); expect(screen.queryByText('Help')).not.toBeInTheDocument(); }); + + it('renders v2 slot content for an authenticated user even when showUserDropdown is false', () => { + configureV2Widget(); + render(
); + + expect(screen.getByText(V2_WIDGET_TEXT)).toBeInTheDocument(); + expect(screen.queryByText(authenticatedUser.username)).not.toBeInTheDocument(); + expect(screen.queryByText('Help')).not.toBeInTheDocument(); + }); + + it('renders v2 slot content for an anonymous user', () => { + configureV2Widget(); + render( + +
+ , + ); + + expect(screen.getByText(V2_WIDGET_TEXT)).toBeInTheDocument(); + expect(screen.queryByText('Help')).not.toBeInTheDocument(); + }); }); From daa487ec18ec7237c419db7e751ce6ba310277b3 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Thu, 3 Sep 2026 20:43:51 -0500 Subject: [PATCH 5/7] fix: Nits on the code --- src/plugin-slots/LearningHeaderActionsSlot/v2/index.jsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/plugin-slots/LearningHeaderActionsSlot/v2/index.jsx b/src/plugin-slots/LearningHeaderActionsSlot/v2/index.jsx index a276b09aac..2e8483737c 100644 --- a/src/plugin-slots/LearningHeaderActionsSlot/v2/index.jsx +++ b/src/plugin-slots/LearningHeaderActionsSlot/v2/index.jsx @@ -3,9 +3,10 @@ import PropTypes from 'prop-types'; import { PluginSlot } from '@openedx/frontend-plugin-framework'; import LearningHeaderActionsSlotV1 from '../v1'; -const LearningHeaderActionsSlot = ({ showDefaultActions }) => ( +const LearningHeaderActionsSlot = ({ showDefaultActions = true }) => ( {showDefaultActions && } @@ -15,8 +16,4 @@ LearningHeaderActionsSlot.propTypes = { showDefaultActions: PropTypes.bool, }; -LearningHeaderActionsSlot.defaultProps = { - showDefaultActions: true, -}; - export default LearningHeaderActionsSlot; From 8b470a5bf4724a8f5a473018719c13c43cfd7601 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Wed, 16 Sep 2026 14:29:30 -0500 Subject: [PATCH 6/7] docs: mark learning_header_actions.v1 slot as deprecated --- .../LearningHeaderActionsSlot/README.md | 2 +- .../LearningHeaderActionsSlot/v1/README.md | 5 ++++ .../LearningHeaderActionsSlot/v1/index.jsx | 27 +++++++++++++------ src/plugin-slots/README.md | 2 +- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/plugin-slots/LearningHeaderActionsSlot/README.md b/src/plugin-slots/LearningHeaderActionsSlot/README.md index 0bd6292c7f..c49a6d40e9 100644 --- a/src/plugin-slots/LearningHeaderActionsSlot/README.md +++ b/src/plugin-slots/LearningHeaderActionsSlot/README.md @@ -3,4 +3,4 @@ | 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` and a user is authenticated | [v1 docs](./v1/) | +| `org.openedx.frontend.layout.learning_header_actions.v1` **(Deprecated)** | Notification tray + help link, only rendered when `showUserDropdown` is `true` and a user is authenticated | [v1 docs](./v1/) | diff --git a/src/plugin-slots/LearningHeaderActionsSlot/v1/README.md b/src/plugin-slots/LearningHeaderActionsSlot/v1/README.md index 855891e2c8..5967a00442 100644 --- a/src/plugin-slots/LearningHeaderActionsSlot/v1/README.md +++ b/src/plugin-slots/LearningHeaderActionsSlot/v1/README.md @@ -1,5 +1,10 @@ # Learning Header Actions Slot — v1 (Notification Tray + Help Link) +> **⚠️ Deprecated:** This slot is deprecated and will be removed. Use the +> [`v2` slot](../v2/) instead, which renders unconditionally regardless of +> `showUserDropdown`. See [DEPR ticket #681](https://github.com/openedx/frontend-component-header/issues/681) +> for the removal plan and timeline. + ### Slot ID: `org.openedx.frontend.layout.learning_header_actions.v1` **Default Content:** diff --git a/src/plugin-slots/LearningHeaderActionsSlot/v1/index.jsx b/src/plugin-slots/LearningHeaderActionsSlot/v1/index.jsx index 31a40f8941..1c62f3c9cd 100644 --- a/src/plugin-slots/LearningHeaderActionsSlot/v1/index.jsx +++ b/src/plugin-slots/LearningHeaderActionsSlot/v1/index.jsx @@ -3,13 +3,24 @@ import { PluginSlot } from '@openedx/frontend-plugin-framework'; import HeaderNotificationsSlot from '../../HeaderNotificationsSlot'; import LearningHelpSlot from '../../LearningHelpSlot'; -const LearningHeaderActionsSlotV1 = () => ( - - - - -); +/** @deprecated Use `LearningHeaderActionsSlotV2` instead. See DEPR ticket openedx/frontend-component-header#681. */ +const LearningHeaderActionsSlotV1 = () => { + // eslint-disable-next-line no-console + console.warn( + '[DEPRECATED] The "org.openedx.frontend.layout.learning_header_actions.v1" plugin slot ' + + 'is deprecated and will be removed. Migrate your pluginSlots config to ' + + '"org.openedx.frontend.layout.learning_header_actions.v2". ' + + 'See https://github.com/openedx/frontend-component-header/issues/681 for details.', + ); + + return ( + + + + + ); +}; export default LearningHeaderActionsSlotV1; diff --git a/src/plugin-slots/README.md b/src/plugin-slots/README.md index 79810b25fe..fadd225e59 100644 --- a/src/plugin-slots/README.md +++ b/src/plugin-slots/README.md @@ -15,7 +15,7 @@ ### Learning Header * [`org.openedx.frontend.layout.header_learning_course_info.v1`](./CourseInfoSlot/) -* [`org.openedx.frontend.layout.learning_header_actions.v1`](./LearningHeaderActionsSlot/v1/) +* [`org.openedx.frontend.layout.learning_header_actions.v1`](./LearningHeaderActionsSlot/v1/) **(Deprecated, use `.v2`)** * [`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/) From 5fcfa39de92382506c61ac67a907272066ef3c0b Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Fri, 18 Sep 2026 16:43:30 -0500 Subject: [PATCH 7/7] fix: only warn once when v1 slot has custom pluginSlots config --- .../LearningHeaderActionsSlot/v1/index.jsx | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/plugin-slots/LearningHeaderActionsSlot/v1/index.jsx b/src/plugin-slots/LearningHeaderActionsSlot/v1/index.jsx index 1c62f3c9cd..b01e6a0744 100644 --- a/src/plugin-slots/LearningHeaderActionsSlot/v1/index.jsx +++ b/src/plugin-slots/LearningHeaderActionsSlot/v1/index.jsx @@ -1,21 +1,29 @@ import React from 'react'; +import { getConfig } from '@edx/frontend-platform'; import { PluginSlot } from '@openedx/frontend-plugin-framework'; import HeaderNotificationsSlot from '../../HeaderNotificationsSlot'; import LearningHelpSlot from '../../LearningHelpSlot'; +const SLOT_ID = 'org.openedx.frontend.layout.learning_header_actions.v1'; + +let hasWarned = false; + /** @deprecated Use `LearningHeaderActionsSlotV2` instead. See DEPR ticket openedx/frontend-component-header#681. */ const LearningHeaderActionsSlotV1 = () => { - // eslint-disable-next-line no-console - console.warn( - '[DEPRECATED] The "org.openedx.frontend.layout.learning_header_actions.v1" plugin slot ' - + 'is deprecated and will be removed. Migrate your pluginSlots config to ' - + '"org.openedx.frontend.layout.learning_header_actions.v2". ' - + 'See https://github.com/openedx/frontend-component-header/issues/681 for details.', - ); + if (!hasWarned && getConfig()?.pluginSlots?.[SLOT_ID]) { + hasWarned = true; + // eslint-disable-next-line no-console + console.warn( + `[Deprecated] The "${SLOT_ID}" plugin slot ` + + 'is deprecated and will be removed. Migrate your pluginSlots config to ' + + '"org.openedx.frontend.layout.learning_header_actions.v2". ' + + 'See https://github.com/openedx/frontend-component-header/issues/681 for details.', + ); + } return (