feat: wrap existing sidebars in frontend-plugin-framework PluginSlots (#1543)
@@ -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>
|
||||
|
||||
|
||||
@@ -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} />
|
||||
</>
|
||||
|
||||
43
src/plugin-slots/CourseBreadcrumbsSlot/README.md
Normal 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
|
||||

|
||||
|
||||
### Replaced with custom component
|
||||

|
||||
|
||||
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;
|
||||
```
|
||||
32
src/plugin-slots/CourseBreadcrumbsSlot/index.tsx
Normal 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>
|
||||
);
|
||||
BIN
src/plugin-slots/CourseBreadcrumbsSlot/screenshot_custom.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
src/plugin-slots/CourseBreadcrumbsSlot/screenshot_default.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
@@ -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
|
||||

|
||||
|
||||
### Replaced with custom component
|
||||

|
||||
|
||||
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;
|
||||
```
|
||||
@@ -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>
|
||||
);
|
||||
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 21 KiB |
43
src/plugin-slots/CourseOutlineSidebarSlot/README.md
Normal 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
|
||||

|
||||
|
||||
### Replaced with custom component
|
||||

|
||||
|
||||
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;
|
||||
```
|
||||
16
src/plugin-slots/CourseOutlineSidebarSlot/index.tsx
Normal 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>
|
||||
);
|
||||
BIN
src/plugin-slots/CourseOutlineSidebarSlot/screenshot_custom.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
src/plugin-slots/CourseOutlineSidebarSlot/screenshot_default.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
43
src/plugin-slots/CourseOutlineSidebarTriggerSlot/README.md
Normal 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
|
||||

|
||||
|
||||
### Replaced with custom component
|
||||

|
||||
|
||||
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;
|
||||
```
|
||||
16
src/plugin-slots/CourseOutlineSidebarTriggerSlot/index.tsx
Normal 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>
|
||||
);
|
||||
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 37 KiB |
@@ -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
|
||||

|
||||
|
||||
### Replaced with custom component
|
||||

|
||||
|
||||
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;
|
||||
```
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 42 KiB |
@@ -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
|
||||

|
||||
|
||||
### Replaced with custom component
|
||||

|
||||
|
||||
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;
|
||||
```
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 21 KiB |
@@ -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/)
|
||||
|
||||