feat: Add slots to add tab links for courses

Adds new slot that allow adding new links to course tabs.
This commit is contained in:
kshitij.sobti
2025-03-21 17:31:29 +05:30
committed by Farhaan Bukhsh
parent f8381e7900
commit d0a8778015
6 changed files with 137 additions and 31 deletions

View File

@@ -0,0 +1,20 @@
import classNames from 'classnames';
import React from 'react';
interface CourseTabLinkProps {
slug: string;
activeTabSlug?: string;
url: string;
title: string;
}
export const CourseTabLink = ({
slug, activeTabSlug, url, title,
}: CourseTabLinkProps) => (
<a
href={url}
className={classNames('nav-item flex-shrink-0 nav-link', { active: slug === activeTabSlug })}
>
{title}
</a>
);

View File

@@ -0,0 +1,25 @@
import { CourseTabLink } from '@src/course-tabs/CourseTabLink';
import React from 'react';
interface CourseTabLinkListProps {
tabs: Array<{
title: string;
slug: string;
url: string;
}>,
activeTabSlug?: string;
}
export const CourseTabLinksList = ({ tabs, activeTabSlug }: CourseTabLinkListProps) => (
<>
{tabs.map(({ url, title, slug }) => (
<CourseTabLink
key={slug}
url={url}
slug={slug}
title={title}
activeTabSlug={activeTabSlug}
/>
))}
</>
);

View File

@@ -1,16 +1,28 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import classNames from 'classnames'; import classNames from 'classnames';
import { useIntl } from '@edx/frontend-platform/i18n';
import messages from './messages'; import { CourseTabLinksSlot } from '../plugin-slots/CourseTabLinksSlot';
import Tabs from '../generic/tabs/Tabs';
import { CoursewareSearch, CoursewareSearchToggle } from '../course-home/courseware-search'; import { CoursewareSearch, CoursewareSearchToggle } from '../course-home/courseware-search';
import { useCoursewareSearchState } from '../course-home/courseware-search/hooks'; import { useCoursewareSearchState } from '../course-home/courseware-search/hooks';
import Tabs from '../generic/tabs/Tabs';
import messages from './messages';
interface CourseTabsNavigationProps {
activeTabSlug?: string;
className?: string | null;
tabs: Array<{
title: string;
slug: string;
url: string;
}>;
}
const CourseTabsNavigation = ({ const CourseTabsNavigation = ({
activeTabSlug, className, tabs, activeTabSlug = undefined,
}) => { className = null,
tabs,
}:CourseTabsNavigationProps) => {
const intl = useIntl(); const intl = useIntl();
const { show } = useCoursewareSearchState(); const { show } = useCoursewareSearchState();
@@ -23,15 +35,7 @@ const CourseTabsNavigation = ({
className="nav-underline-tabs" className="nav-underline-tabs"
aria-label={intl.formatMessage(messages.courseMaterial)} aria-label={intl.formatMessage(messages.courseMaterial)}
> >
{tabs.map(({ url, title, slug }) => ( <CourseTabLinksSlot tabs={tabs} activeTabSlug={activeTabSlug} />
<a
key={slug}
className={classNames('nav-item flex-shrink-0 nav-link', { active: slug === activeTabSlug })}
href={url}
>
{title}
</a>
))}
</Tabs> </Tabs>
</div> </div>
<div className="search-toggle"> <div className="search-toggle">
@@ -44,19 +48,4 @@ const CourseTabsNavigation = ({
); );
}; };
CourseTabsNavigation.propTypes = {
activeTabSlug: PropTypes.string,
className: PropTypes.string,
tabs: PropTypes.arrayOf(PropTypes.shape({
title: PropTypes.string.isRequired,
slug: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
})).isRequired,
};
CourseTabsNavigation.defaultProps = {
activeTabSlug: undefined,
className: null,
};
export default CourseTabsNavigation; export default CourseTabsNavigation;

View File

@@ -0,0 +1,51 @@
# Course Tab Links Slot
### Slot ID: `org.openedx.frontend.learning.course_tab_links.v1`
### Props:
* `activeTabSlug`: The slug of the currently active tab.
## Description
This slot is used to replace/modify/hide the course tabs.
## Example
### Added link to Course Tabs
![Added "Custom Tab" to course tabs](./course-tabs-custom.png)
The following `env.config.jsx` will add a new course tab call "Custom Tab".
```js
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
import { CourseTabLink } from '@src/course-tabs/CourseTabLink';
const config = {
pluginSlots: {
"org.openedx.frontend.learning.course_tab_links.v1": {
keepDefault: true,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'custom_tab',
type: DIRECT_PLUGIN,
RenderWidget: ({ activeTabSlug })=> (
<CourseTabLink
url="/some/path"
slug="custom-link"
title="Custom Tab"
activeTabSlug={activeTabSlug}
/>
),
},
},
],
},
},
}
export default config;
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,21 @@
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import { CourseTabLinksList } from '@src/course-tabs/CourseTabLinksList';
import React from 'react';
type CourseTabList = Array<{
title: string;
slug: string;
url: string;
}>;
export const CourseTabLinksSlot = ({ tabs, activeTabSlug }: {
tabs: CourseTabList,
activeTabSlug?: string
}) => (
<PluginSlot
id="org.openedx.frontend.learning.course_tab_links.v1"
pluginProps={{ activeTabSlug }}
>
<CourseTabLinksList tabs={tabs} activeTabSlug={activeTabSlug} />
</PluginSlot>
);