diff --git a/src/course-tabs/CourseTabLink.tsx b/src/course-tabs/CourseTabLink.tsx new file mode 100644 index 0000000000..16ccdce4e8 --- /dev/null +++ b/src/course-tabs/CourseTabLink.tsx @@ -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) => ( + + {title} + +); diff --git a/src/course-tabs/CourseTabLinksList.tsx b/src/course-tabs/CourseTabLinksList.tsx new file mode 100644 index 0000000000..894debcd77 --- /dev/null +++ b/src/course-tabs/CourseTabLinksList.tsx @@ -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 }) => ( + + ))} + +); diff --git a/src/course-tabs/CourseTabsNavigation.jsx b/src/course-tabs/CourseTabsNavigation.tsx similarity index 58% rename from src/course-tabs/CourseTabsNavigation.jsx rename to src/course-tabs/CourseTabsNavigation.tsx index 9c2a12ef8c..87a1b92c4a 100644 --- a/src/course-tabs/CourseTabsNavigation.jsx +++ b/src/course-tabs/CourseTabsNavigation.tsx @@ -1,16 +1,28 @@ import React from 'react'; -import PropTypes from 'prop-types'; -import { useIntl } from '@edx/frontend-platform/i18n'; import classNames from 'classnames'; - -import messages from './messages'; -import Tabs from '../generic/tabs/Tabs'; +import { useIntl } from '@edx/frontend-platform/i18n'; +import { CourseTabLinksSlot } from '../plugin-slots/CourseTabLinksSlot'; import { CoursewareSearch, CoursewareSearchToggle } from '../course-home/courseware-search'; 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 = ({ - activeTabSlug, className, tabs, -}) => { + activeTabSlug = undefined, + className = null, + tabs, +}:CourseTabsNavigationProps) => { const intl = useIntl(); const { show } = useCoursewareSearchState(); @@ -23,15 +35,7 @@ const CourseTabsNavigation = ({ className="nav-underline-tabs" aria-label={intl.formatMessage(messages.courseMaterial)} > - {tabs.map(({ url, title, slug }) => ( - - {title} - - ))} +
@@ -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; diff --git a/src/plugin-slots/CourseTabLinksSlot/README.md b/src/plugin-slots/CourseTabLinksSlot/README.md new file mode 100644 index 0000000000..653086e248 --- /dev/null +++ b/src/plugin-slots/CourseTabLinksSlot/README.md @@ -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 })=> ( + + ), + }, + }, + ], + }, + }, +} + +export default config; +``` diff --git a/src/plugin-slots/CourseTabLinksSlot/course-tabs-custom.png b/src/plugin-slots/CourseTabLinksSlot/course-tabs-custom.png new file mode 100644 index 0000000000..6c9a4d68ac Binary files /dev/null and b/src/plugin-slots/CourseTabLinksSlot/course-tabs-custom.png differ diff --git a/src/plugin-slots/CourseTabLinksSlot/index.tsx b/src/plugin-slots/CourseTabLinksSlot/index.tsx new file mode 100644 index 0000000000..2515399e08 --- /dev/null +++ b/src/plugin-slots/CourseTabLinksSlot/index.tsx @@ -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 +}) => ( + + + +);