Skip to content

Commit 5e1a23e

Browse files
committed
feat: replace LearnerTools with LearnerToolsSlot and update tests
1 parent 5083cd9 commit 5e1a23e

File tree

7 files changed

+43
-86
lines changed

7 files changed

+43
-86
lines changed

src/courseware/course/Course.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import { breakpoints, useWindowSize } from '@openedx/paragon';
88

99
import { AlertList } from '@src/generic/user-messages';
1010
import { useModel } from '@src/generic/model-store';
11-
import LearnerTools from './learner-tools/LearnerTools';
11+
import { getCoursewareOutlineSidebarSettings } from '../data/selectors';
12+
import { LearnerToolsSlot } from '../../plugin-slots/LearnerToolsSlot';
1213
import SidebarProvider from './sidebar/SidebarContextProvider';
1314
import NewSidebarProvider from './new-sidebar/SidebarContextProvider';
1415
import { NotificationsDiscussionsSidebarTriggerSlot } from '../../plugin-slots/NotificationsDiscussionsSidebarTriggerSlot';
@@ -89,7 +90,7 @@ const Course = ({
8990
unitId={unitId}
9091
/>
9192
{shouldDisplayLearnerTools && (
92-
<LearnerTools
93+
<LearnerToolsSlot
9394
enrollmentMode={course.enrollmentMode}
9495
isStaff={isStaff}
9596
courseId={courseId}

src/courseware/course/Course.test.jsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ jest.mock('@edx/frontend-lib-special-exams', () => {
2525
});
2626
const mockLearnerToolsTestId = 'fake-learner-tools';
2727
jest.mock(
28-
'./learner-tools/LearnerTools',
29-
// eslint-disable-next-line react/prop-types
30-
() => function ({ courseId }) {
31-
return <div className="fake-learner-tools" data-testid={mockLearnerToolsTestId}>LearnerTools contents {courseId} </div>;
32-
},
28+
'../../plugin-slots/LearnerToolsSlot',
29+
() => ({
30+
// eslint-disable-next-line react/prop-types
31+
LearnerToolsSlot({ courseId }) {
32+
return <div className="fake-learner-tools" data-testid={mockLearnerToolsTestId}>LearnerTools contents {courseId} </div>;
33+
},
34+
}),
3335
);
3436

3537
const recordFirstSectionCelebration = jest.fn();
@@ -368,7 +370,6 @@ describe('Course', () => {
368370

369371
it('displays learner tools when screen is wide enough (browser)', async () => {
370372
const courseMetadata = Factory.build('courseMetadata', {
371-
learning_assistant_enabled: true,
372373
enrollment: { mode: 'verified' },
373374
});
374375
const testStore = await initializeTestStore({ courseMetadata }, false);
@@ -388,7 +389,6 @@ describe('Course', () => {
388389
it('does not display learner tools when screen is too narrow (mobile)', async () => {
389390
global.innerWidth = breakpoints.extraSmall.minWidth;
390391
const courseMetadata = Factory.build('courseMetadata', {
391-
learning_assistant_enabled: true,
392392
enrollment: { mode: 'verified' },
393393
});
394394
const testStore = await initializeTestStore({ courseMetadata }, false);

src/courseware/course/learner-tools/LearnerTools.test.jsx

Lines changed: 0 additions & 71 deletions
This file was deleted.

src/courseware/course/learner-tools/index.js

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Learner Tools Slot
2+
3+
### Slot ID: `org.openedx.frontend.learning.learner_tools.v1`
4+
5+
### Slot ID Aliases
6+
* `learner_tools_slot`
7+
8+
### Description
9+
This plugin slot provides a location for learner-facing tools and features to be displayed during course content navigation. The slot is rendered via a React portal to `document.body` to ensure proper positioning and stacking context.
10+
11+
### Props:
12+
* `courseId` - The unique identifier for the current course
13+
* `unitId` - The unique identifier for the current unit/vertical being viewed
14+
* `userId` - The authenticated user's ID (automatically retrieved from auth context)
15+
* `isStaff` - Boolean indicating whether the user has staff/instructor privileges
16+
* `enrollmentMode` - The user's enrollment mode (e.g., 'audit', 'verified', 'honor', etc.)
17+
18+
### Usage
19+
Plugins registered to this slot can use the provided context to:
20+
- Display course-specific tools based on courseId and unitId
21+
- Show different features based on user's enrollment mode
22+
- Provide staff-only functionality when isStaff is true
23+
- Query additional data from Redux store or backend APIs as needed
24+
25+
### Notes
26+
- Returns `null` if user is not authenticated
27+
- Plugins should manage their own feature flag checks and requirements
28+
- The slot uses a portal to render to `document.body` for flexible positioning

src/courseware/course/learner-tools/LearnerTools.jsx renamed to src/plugin-slots/LearnerToolsSlot/index.jsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
33
import { PluginSlot } from '@openedx/frontend-plugin-framework';
44
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
55

6-
const LearnerTools = ({
6+
export const LearnerToolsSlot = ({
77
enrollmentMode = null,
88
isStaff,
99
courseId,
@@ -31,18 +31,17 @@ const LearnerTools = ({
3131
// Plugins will query their own requirements from Redux/config
3232
return createPortal(
3333
<PluginSlot
34-
id="learner_tools_slot"
34+
id="org.openedx.frontend.learning.learner_tools.v1"
35+
idAliases={['learner_tools_slot']}
3536
pluginProps={pluginContext}
3637
/>,
3738
document.body,
3839
);
3940
};
4041

41-
LearnerTools.propTypes = {
42+
LearnerToolsSlot.propTypes = {
4243
isStaff: PropTypes.bool.isRequired,
4344
enrollmentMode: PropTypes.string,
4445
courseId: PropTypes.string.isRequired,
4546
unitId: PropTypes.string.isRequired,
4647
};
47-
48-
export default LearnerTools;

src/plugin-slots/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* [`org.openedx.frontend.learning.course_outline_tab_notifications.v1`](./CourseOutlineTabNotificationsSlot/)
1212
* [`org.openedx.frontend.learning.course_recommendations.v1`](./CourseRecommendationsSlot/)
1313
* [`org.openedx.frontend.learning.gated_unit_content_message.v1`](./GatedUnitContentMessageSlot/)
14+
* [`org.openedx.frontend.learning.learner_tools.v1`](./LearnerToolsSlot/)
1415
* [`org.openedx.frontend.learning.next_unit_top_nav_trigger.v1`](./NextUnitTopNavTriggerSlot/)
1516
* [`org.openedx.frontend.learning.notification_tray.v1`](./NotificationTraySlot/)
1617
* [`org.openedx.frontend.learning.notification_widget.v1`](./NotificationWidgetSlot/)

0 commit comments

Comments
 (0)