feat: wrap existing sidebars in frontend-plugin-framework PluginSlots (#1543)

This commit is contained in:
Brian Smith
2024-12-04 10:24:03 -05:00
committed by GitHub
parent 98c670afe7
commit f5b6243c61
27 changed files with 411 additions and 17 deletions

View File

@@ -9,16 +9,15 @@ import { breakpoints, useWindowSize } from '@openedx/paragon';
import { AlertList } from '@src/generic/user-messages';
import { useModel } from '@src/generic/model-store';
import { getCoursewareOutlineSidebarSettings } from '../data/selectors';
import { Trigger as CourseOutlineTrigger } from './sidebar/sidebars/course-outline';
import Chat from './chat/Chat';
import SidebarProvider from './sidebar/SidebarContextProvider';
import SidebarTriggers from './sidebar/SidebarTriggers';
import NewSidebarProvider from './new-sidebar/SidebarContextProvider';
import NewSidebarTriggers from './new-sidebar/SidebarTriggers';
import { NotificationsDiscussionsSidebarTriggerSlot } from '../../plugin-slots/NotificationsDiscussionsSidebarTriggerSlot';
import { CelebrationModal, shouldCelebrateOnSectionLoad, WeeklyGoalCelebrationModal } from './celebration';
import CourseBreadcrumbs from './CourseBreadcrumbs';
import ContentTools from './content-tools';
import Sequence from './sequence';
import { CourseOutlineMobileSidebarTriggerSlot } from '../../plugin-slots/CourseOutlineMobileSidebarTriggerSlot';
import { CourseBreadcrumbsSlot } from '../../plugin-slots/CourseBreadcrumbsSlot';
const Course = ({
courseId,
@@ -87,7 +86,7 @@ const Course = ({
<div className="position-relative d-flex align-items-xl-center mb-4 mt-1 flex-column flex-xl-row">
{navigationDisabled || (
<>
<CourseBreadcrumbs
<CourseBreadcrumbsSlot
courseId={courseId}
sectionId={section ? section.id : null}
sequenceId={sequenceId}
@@ -109,8 +108,8 @@ const Course = ({
</>
)}
<div className="w-100 d-flex align-items-center">
<CourseOutlineTrigger isMobileView />
{isNewDiscussionSidebarViewEnabled ? <NewSidebarTriggers /> : <SidebarTriggers /> }
<CourseOutlineMobileSidebarTriggerSlot />
<NotificationsDiscussionsSidebarTriggerSlot courseId={courseId} />
</div>
</div>

View File

@@ -18,16 +18,13 @@ import SequenceContainerSlot from '../../../plugin-slots/SequenceContainerSlot';
import { getCoursewareOutlineSidebarSettings } from '../../data/selectors';
import CourseLicense from '../course-license';
import Sidebar from '../sidebar/Sidebar';
import NewSidebar from '../new-sidebar/Sidebar';
import {
Trigger as CourseOutlineTrigger,
Sidebar as CourseOutlineTray,
} from '../sidebar/sidebars/course-outline';
import { NotificationsDiscussionsSidebarSlot } from '../../../plugin-slots/NotificationsDiscussionsSidebarSlot';
import messages from './messages';
import HiddenAfterDue from './hidden-after-due';
import { SequenceNavigation, UnitNavigation } from './sequence-navigation';
import SequenceContent from './SequenceContent';
import { CourseOutlineSidebarSlot } from '../../../plugin-slots/CourseOutlineSidebarSlot';
import { CourseOutlineSidebarTriggerSlot } from '../../../plugin-slots/CourseOutlineSidebarTriggerSlot';
const Sequence = ({
unitId,
@@ -46,7 +43,6 @@ const Sequence = ({
const {
isStaff,
originalUserIsStaff,
isNewDiscussionSidebarViewEnabled,
} = useModel('courseHomeMeta', courseId);
const sequence = useModel('sequences', sequenceId);
const unit = useModel('units', unitId);
@@ -166,8 +162,8 @@ const Sequence = ({
const defaultContent = (
<>
<div className="sequence-container d-inline-flex flex-row w-100">
<CourseOutlineTrigger />
<CourseOutlineTray />
<CourseOutlineSidebarTriggerSlot />
<CourseOutlineSidebarSlot />
<div className="sequence w-100">
{!isEnabledOutlineSidebar && (
<div className="sequence-navigation-container">
@@ -209,7 +205,7 @@ const Sequence = ({
{unitHasLoaded && renderUnitNavigation(false)}
</div>
</div>
{isNewDiscussionSidebarViewEnabled ? <NewSidebar /> : <Sidebar />}
<NotificationsDiscussionsSidebarSlot courseId={courseId} />
</div>
<SequenceContainerSlot courseId={courseId} unitId={unitId} />
</>

View File

@@ -0,0 +1,43 @@
# Course Breadcrumbs Slot
### Slot ID: `course_breadcrumbs_slot`
## Description
This slot is used to replace/modify/hide the course breadcrumbs.
## Example
### Default content
![Breadcrumbs slot with default content](./screenshot_default.png)
### Replaced with custom component
![🍞 in breadcrumbs slot](./screenshot_custom.png)
The following `env.config.jsx` will replace the course breadcrumbs entirely.
```js
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
const config = {
pluginSlots: {
course_breadcrumbs_slot: {
keepDefault: false,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'custom_breadcrumbs_component',
type: DIRECT_PLUGIN,
RenderWidget: () => (
<h1>🍞</h1>
),
},
},
]
}
},
}
export default config;
```

View File

@@ -0,0 +1,32 @@
import React from 'react';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import CourseBreadcrumbs from '../../courseware/course/CourseBreadcrumbs';
interface Props {
courseId: string;
sectionId?: string;
sequenceId?: string;
unitId?: string;
isStaff?: boolean;
}
export const CourseBreadcrumbsSlot : React.FC<Props> = ({
courseId, sectionId, sequenceId, unitId, isStaff,
}) => (
<PluginSlot
id="course_breadcrumbs_slot"
slotOptions={{
mergeProps: true,
}}
>
<CourseBreadcrumbs
courseId={courseId}
sectionId={sectionId}
sequenceId={sequenceId}
isStaff={isStaff}
unitId={unitId}
/>
</PluginSlot>
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -0,0 +1,43 @@
# Course Outline Mobile Sidebar Trigger Slot
### Slot ID: `course_outline_mobile_sidebar_trigger_slot`
## Description
This slot is used to replace/modify/hide the course outline sidebar mobile trigger.
## Example
### Default content
![Trigger slot with default content](./screenshot_default.png)
### Replaced with custom component
![📌 in trigger slot](./screenshot_custom.png)
The following `env.config.jsx` will replace the course outline sidebar mobile trigger entirely.
```js
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
const config = {
pluginSlots: {
course_outline_mobile_sidebar_trigger_slot: {
keepDefault: false,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'custom_sidebar_trigger_component',
type: DIRECT_PLUGIN,
RenderWidget: () => (
<h1 className="d-xl-none">📌</h1>
),
},
},
]
}
},
}
export default config;
```

View File

@@ -0,0 +1,16 @@
import React from 'react';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import CourseOutlineTrigger from '../../courseware/course/sidebar/sidebars/course-outline/CourseOutlineTrigger';
export const CourseOutlineMobileSidebarTriggerSlot : React.FC = () => (
<PluginSlot
id="course_outline_mobile_sidebar_trigger_slot"
slotOptions={{
mergeProps: true,
}}
>
<CourseOutlineTrigger isMobileView />
</PluginSlot>
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,43 @@
# Course Outline Sidebar Slot
### Slot ID: `course_outline_sidebar_slot`
## Description
This slot is used to replace/modify/hide the course outline sidebar.
## Example
### Default content
![Sidebar slot with default content](./screenshot_default.png)
### Replaced with custom component
![📊 in sidebar slot](./screenshot_custom.png)
The following `env.config.jsx` will replace the course outline sidebar entirely.
```js
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
const config = {
pluginSlots: {
course_outline_sidebar_slot: {
keepDefault: false,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'custom_sidebar_component',
type: DIRECT_PLUGIN,
RenderWidget: () => (
<h1>📊</h1>
),
},
},
]
}
},
}
export default config;
```

View File

@@ -0,0 +1,16 @@
import React from 'react';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import CourseOutlineTray from '../../courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray';
export const CourseOutlineSidebarSlot : React.FC = () => (
<PluginSlot
id="course_outline_sidebar_slot"
slotOptions={{
mergeProps: true,
}}
>
<CourseOutlineTray />
</PluginSlot>
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -0,0 +1,43 @@
# Course Outline Sidebar Trigger Slot
### Slot ID: `course_outline_sidebar_trigger_slot`
## Description
This slot is used to replace/modify/hide the course outline sidebar trigger.
## Example
### Default content
![Trigger slot with default content](./screenshot_default.png)
### Replaced with custom component
![📌 in trigger slot](./screenshot_custom.png)
The following `env.config.jsx` will replace the course outline sidebar trigger entirely.
```js
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
const config = {
pluginSlots: {
course_outline_sidebar_trigger_slot: {
keepDefault: false,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'custom_sidebar_trigger_component',
type: DIRECT_PLUGIN,
RenderWidget: () => (
<h1 className="d-none d-xl-block">📌</h1>
),
},
},
]
}
},
}
export default config;
```

View File

@@ -0,0 +1,16 @@
import React from 'react';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import CourseOutlineTrigger from '../../courseware/course/sidebar/sidebars/course-outline/CourseOutlineTrigger';
export const CourseOutlineSidebarTriggerSlot : React.FC = () => (
<PluginSlot
id="course_outline_sidebar_trigger_slot"
slotOptions={{
mergeProps: true,
}}
>
<CourseOutlineTrigger isMobileView={false} />
</PluginSlot>
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,43 @@
# Notifications Discussions Sidebar Slot
### Slot ID: `notifications_discussions_sidebar_slot`
## Description
This slot is used to replace/modify/hide the notifications discussions sidebar.
## Example
### Default content
![Sidebar slot with default content](./screenshot_default.png)
### Replaced with custom component
![📊 in sidebar slot](./screenshot_custom.png)
The following `env.config.jsx` will replace the notifications discussions sidebar.
```js
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
const config = {
pluginSlots: {
notifications_discussions_sidebar_slot: {
keepDefault: false,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'custom_sidebar_component',
type: DIRECT_PLUGIN,
RenderWidget: () => (
<h1>📊</h1>
),
},
},
]
}
},
}
export default config;
```

View File

@@ -0,0 +1,28 @@
import React from 'react';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import { useModel } from '@src/generic/model-store';
import Sidebar from '../../courseware/course/sidebar/Sidebar';
import NewSidebar from '../../courseware/course/new-sidebar/Sidebar';
interface Props {
courseId: string;
}
export const NotificationsDiscussionsSidebarSlot : React.FC<Props> = ({ courseId }) => {
const {
isNewDiscussionSidebarViewEnabled,
} = useModel('courseHomeMeta', courseId);
return (
<PluginSlot
id="notifications_discussions_sidebar_slot"
slotOptions={{
mergeProps: true,
}}
>
{isNewDiscussionSidebarViewEnabled ? <NewSidebar /> : <Sidebar />}
</PluginSlot>
);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -0,0 +1,43 @@
# Notifications Discussions Sidebar Trigger Slot
### Slot ID: `notifications_discussions_sidebar_trigger_slot`
## Description
This slot is used to replace/modify/hide the notifications discussions sidebar trigger.
## Example
### Default content
![Trigger slot with default content](./screenshot_default.png)
### Replaced with custom component
![📬 in trigger slot](./screenshot_custom.png)
The following `env.config.jsx` will replace the notifications discussions sidebar trigger.
```js
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
const config = {
pluginSlots: {
notifications_discussions_sidebar_trigger_slot: {
keepDefault: false,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'custom_sidebar_trigger_component',
type: DIRECT_PLUGIN,
RenderWidget: () => (
<h1 className='ml-auto'>📬</h1>
),
},
},
]
}
},
}
export default config;
```

View File

@@ -0,0 +1,28 @@
import React from 'react';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import { useModel } from '@src/generic/model-store';
import SidebarTriggers from '../../courseware/course/sidebar/SidebarTriggers';
import NewSidebarTriggers from '../../courseware/course/new-sidebar/SidebarTriggers';
interface Props {
courseId: string;
}
export const NotificationsDiscussionsSidebarTriggerSlot : React.FC<Props> = ({ courseId }) => {
const {
isNewDiscussionSidebarViewEnabled,
} = useModel('courseHomeMeta', courseId);
return (
<PluginSlot
id="notifications_discussions_sidebar_trigger_slot"
slotOptions={{
mergeProps: true,
}}
>
{isNewDiscussionSidebarViewEnabled ? <NewSidebarTriggers /> : <SidebarTriggers /> }
</PluginSlot>
);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -4,3 +4,8 @@
* [`footer_slot`](./FooterSlot/)
* [`sequence_container_slot`](./SequenceContainerSlot/)
* [`unit_title_slot`](./UnitTitleSlot/)
* [`course_outline_sidebar_slot`](./CourseOutlineSidebarSlot/)
* [`course_outline_sidebar_trigger_slot`](./CourseOutlineSidebarTriggerSlot)
* [`course_outline_mobile_sidebar_trigger_slot`](./CourseOutlineMobileSidebarTriggerSlot/)
* [`notifications_discussions_sidebar_slot`](./NotificationsDiscussionsSidebarSlot/)
* [`notifications_discussions_sidebar_trigger_slot`](./NotificationsDiscussionsSidebarTriggerSlot/)