Skip to content

Instantly share code, notes, and snippets.

@xitij2000
Created March 20, 2025 14:53
Show Gist options
  • Save xitij2000/012417d3e84a1354e9733456c866fd53 to your computer and use it in GitHub Desktop.
Save xitij2000/012417d3e84a1354e9733456c866fd53 to your computer and use it in GitHub Desktop.
Authoring MFE plugin for aspects
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
import {
Button,
Card,
Dropdown,
DropdownButton,
Icon,
IconButton,
IconButtonWithTooltip
} from '@openedx/paragon';
import { Analytics, ArrowBack, AvTimer, Close } from '@openedx/paragon/icons';
import { getCourseUnit } from "CourseAuthoring/course-unit/data/selectors";
import { useModel } from "CourseAuthoring/generic/model-store";
import React, { useEffect } from 'react';
import { useSelector } from "react-redux";
import { useParams } from "react-router-dom";
const AspectsSidebarContext = React.createContext({
showSidebar: false,
location: null,
});
const AspectsSidebarProvider = ({component}) => {
const [showSidebar, setShowSidebar] = React.useState(true);
const [location, setLocation] = React.useState(null);
return (
<AspectsSidebarContext.Provider
value={{
showSidebar,
location,
setShowSidebar,
setLocation
}}
>
{component}
</AspectsSidebarContext.Provider>
);
}
const CourseUnitButton = () => {
const {blockId} = useParams();
const {
showSidebar,
setShowSidebar,
setLocation,
} = React.useContext(AspectsSidebarContext);
return (
<Button
variant={
showSidebar ? "primary":"outline-primary"
}
iconBefore={Analytics}
onClick={() => {
setShowSidebar(!showSidebar);
setLocation(blockId);
}}
>
Analytics
</Button>
);
};
const CourseOutlineButton = () => {
const {
showSidebar,
setShowSidebar,
setLocation
} = React.useContext(AspectsSidebarContext);
return (
<Button
variant={
showSidebar ? "primary":"outline-primary"
}
iconBefore={Analytics}
onClick={() => {
setShowSidebar(!showSidebar);
setLocation(null)
}}
>
AnalyticsO
</Button>
);
};
const UnitActionsButton = ({cardId, isVertical}) => {
const {
showSidebar,
location,
setShowSidebar,
setLocation
} = React.useContext(AspectsSidebarContext);
return isVertical && (
<IconButton
isActive={showSidebar && location===cardId}
alt="Analytics"
src={Analytics}
iconAs={Icon}
variant="black"
onClick={() => {
if (location===cardId) {
setShowSidebar(false);
} else {
setLocation(cardId);
setShowSidebar(true);
}
}}
/>
);
}
const SidebarToggleWrapper = ({component}) => {
const {
showSidebar,
} = React.useContext(AspectsSidebarContext);
return !showSidebar && component;
}
const Sidebar = () => {
const {
showSidebar,
location,
setShowSidebar,
setLocation
} = React.useContext(AspectsSidebarContext);
const {courseId} = useParams();
const courseDetails = useModel('courseDetails', courseId);
const unit = useSelector(getCourseUnit);
return showSidebar && (
<Card className="bg-white rounded">
<div className="sidebar-header d-flex flex-column shadow p-2 bg-light-200 ">
<div className="d-flex flex-row align-items-center justify-content-between">
<span className="small sidebar-title d-flex">
Analytics <Icon src={Analytics}/>
</span>
<IconButtonWithTooltip tooltipContent={"Close"} tooltipPlacement="top"
alt="Close" src={Close} iconAs={Icon} variant="black"
onClick={() => {
setShowSidebar(false);
}}/>
</div>
<div className="d-flex flex-row align-items-center justify-content-between">
{location &&
<IconButton alt="Back" src={ArrowBack} iconAs={Icon} variant="black"
onClick={() => setLocation(null)} size="inline"/>}
{location ?
unit?.unit?.displayName
:courseDetails?.name}
</div>
<DropdownButton variant="outline-primary" title={
<span className="d-flex align-items-center mr-1 p-0"><Icon src={AvTimer}/>Dropdown button</span>
}>
<Dropdown.Item href="#/action-1">Action</Dropdown.Item>
<Dropdown.Item href="#/action-2">Another action</Dropdown.Item>
<Dropdown.Item href="#/action-3">Something else</Dropdown.Item>
</DropdownButton>
</div>
<iframe className="w-100" srcDoc="TEST"/>
</Card>
)
};
const config = {
pluginSlots: {
course_outline_sidebar: {
keepDefault: true,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'outline-sidebar',
priority: 1,
type: DIRECT_PLUGIN,
RenderWidget: Sidebar,
},
},
{
op: PLUGIN_OPERATIONS.Wrap,
widgetId: 'default_contents',
wrapper: SidebarToggleWrapper,
}
]
},
course_unit_sidebar: {
keepDefault: true,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'course-unit-sidebar',
priority: 1,
type: DIRECT_PLUGIN,
RenderWidget: Sidebar,
},
},
{
op: PLUGIN_OPERATIONS.Wrap,
widgetId: 'default_contents',
wrapper: SidebarToggleWrapper,
}
]
},
course_unit_header_actions_slot: {
keepDefault: true,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'unit-header-aspects-button',
priority: 60,
type: DIRECT_PLUGIN,
RenderWidget: CourseUnitButton,
},
},
]
},
course_outline_header_actions_slot: {
keepDefault: true,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'outline-header-aspects-button',
priority: 60,
type: DIRECT_PLUGIN,
RenderWidget: CourseOutlineButton,
},
},
]
},
course_outline_header_slot: {
keepDefault: true,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'units-action-aspects-button',
priority: 60,
type: DIRECT_PLUGIN,
RenderWidget: UnitActionsButton,
},
},
]
},
authoring_app_wrapper: {
keepDefault: true,
plugins: [
{
op: PLUGIN_OPERATIONS.Wrap,
widgetId: 'default_contents',
wrapper: AspectsSidebarProvider,
}
]
}
},
}
export default config;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment