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:
committed by
Farhaan Bukhsh
parent
f8381e7900
commit
d0a8778015
20
src/course-tabs/CourseTabLink.tsx
Normal file
20
src/course-tabs/CourseTabLink.tsx
Normal 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>
|
||||||
|
);
|
||||||
25
src/course-tabs/CourseTabLinksList.tsx
Normal file
25
src/course-tabs/CourseTabLinksList.tsx
Normal 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}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
@@ -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;
|
||||||
51
src/plugin-slots/CourseTabLinksSlot/README.md
Normal file
51
src/plugin-slots/CourseTabLinksSlot/README.md
Normal 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
|
||||||
|

|
||||||
|
|
||||||
|
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;
|
||||||
|
```
|
||||||
BIN
src/plugin-slots/CourseTabLinksSlot/course-tabs-custom.png
Normal file
BIN
src/plugin-slots/CourseTabLinksSlot/course-tabs-custom.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
21
src/plugin-slots/CourseTabLinksSlot/index.tsx
Normal file
21
src/plugin-slots/CourseTabLinksSlot/index.tsx
Normal 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>
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user