Skip to content

Commit 7245d96

Browse files
committed
feat: Add slots to add tab links and add mechanism for plugin routes
1 parent f28528e commit 7245d96

File tree

2 files changed

+119
-109
lines changed

2 files changed

+119
-109
lines changed
Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
import React from 'react';
2-
import PropTypes from 'prop-types';
3-
import { useIntl } from '@edx/frontend-platform/i18n';
42
import classNames from 'classnames';
5-
6-
import messages from './messages';
7-
import Tabs from '../generic/tabs/Tabs';
3+
import { useIntl } from '@edx/frontend-platform/i18n';
4+
import { PluginSlot } from '@openedx/frontend-plugin-framework';
85
import { CoursewareSearch, CoursewareSearchToggle } from '../course-home/courseware-search';
96
import { useCoursewareSearchState } from '../course-home/courseware-search/hooks';
107

8+
import Tabs from '../generic/tabs/Tabs';
9+
import messages from './messages';
10+
11+
interface CourseTabsNavigationProps {
12+
activeTabSlug?: string;
13+
className?: string | null;
14+
tabs: Array<{
15+
title: string;
16+
slug: string;
17+
url: string;
18+
}>;
19+
}
20+
1121
const CourseTabsNavigation = ({
12-
activeTabSlug, className, tabs,
13-
}) => {
22+
activeTabSlug = undefined,
23+
className = null,
24+
tabs,
25+
}:CourseTabsNavigationProps) => {
1426
const intl = useIntl();
1527
const { show } = useCoursewareSearchState();
1628

@@ -23,15 +35,17 @@ const CourseTabsNavigation = ({
2335
className="nav-underline-tabs"
2436
aria-label={intl.formatMessage(messages.courseMaterial)}
2537
>
26-
{tabs.map(({ url, title, slug }) => (
27-
<a
28-
key={slug}
29-
className={classNames('nav-item flex-shrink-0 nav-link', { active: slug === activeTabSlug })}
30-
href={url}
31-
>
32-
{title}
33-
</a>
34-
))}
38+
<PluginSlot id="course_tab_links_slot">
39+
{tabs.map(({ url, title, slug }) => (
40+
<a
41+
key={slug}
42+
className={classNames('nav-item flex-shrink-0 nav-link', { active: slug === activeTabSlug })}
43+
href={url}
44+
>
45+
{title}
46+
</a>
47+
))}
48+
</PluginSlot>
3549
</Tabs>
3650
</div>
3751
<div className="search-toggle">
@@ -44,19 +58,4 @@ const CourseTabsNavigation = ({
4458
);
4559
};
4660

47-
CourseTabsNavigation.propTypes = {
48-
activeTabSlug: PropTypes.string,
49-
className: PropTypes.string,
50-
tabs: PropTypes.arrayOf(PropTypes.shape({
51-
title: PropTypes.string.isRequired,
52-
slug: PropTypes.string.isRequired,
53-
url: PropTypes.string.isRequired,
54-
})).isRequired,
55-
};
56-
57-
CourseTabsNavigation.defaultProps = {
58-
activeTabSlug: undefined,
59-
className: null,
60-
};
61-
6261
export default CourseTabsNavigation;

src/index.jsx

Lines changed: 89 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
getConfig,
55
} from '@edx/frontend-platform';
66
import { AppProvider, ErrorPage, PageWrap } from '@edx/frontend-platform/react';
7+
import { PluginSlot } from '@openedx/frontend-plugin-framework';
78
import { StrictMode } from 'react';
89
import { createRoot } from 'react-dom/client';
910
import { Routes, Route } from 'react-router-dom';
@@ -51,100 +52,110 @@ subscribe(APP_READY, () => {
5152
<UserMessagesProvider>
5253
<div className="app-container">
5354
<Routes>
54-
<Route path="*" element={<PageWrap><PageNotFound /></PageWrap>} />
55-
<Route path={ROUTES.UNSUBSCRIBE} element={<PageWrap><GoalUnsubscribe /></PageWrap>} />
56-
<Route path={ROUTES.REDIRECT} element={<PageWrap><CoursewareRedirectLandingPage /></PageWrap>} />
55+
<Route path="*" element={<PageWrap><PageNotFound /></PageWrap>} />
56+
<Route path={ROUTES.UNSUBSCRIBE} element={<PageWrap><GoalUnsubscribe /></PageWrap>} />
57+
<Route path={ROUTES.REDIRECT} element={<PageWrap><CoursewareRedirectLandingPage /></PageWrap>} />
58+
<Route
59+
path={ROUTES.PREFERENCES_UNSUBSCRIBE}
60+
element={
61+
<PageWrap><PreferencesUnsubscribe /></PageWrap>
62+
}
63+
/>
64+
<Route
65+
path={DECODE_ROUTES.ACCESS_DENIED}
66+
element={<DecodePageRoute><CourseAccessErrorPage /></DecodePageRoute>}
67+
/>
68+
<Route
69+
path={DECODE_ROUTES.HOME}
70+
element={(
71+
<DecodePageRoute>
72+
<TabContainer tab="outline" fetch={fetchOutlineTab} slice="courseHome">
73+
<OutlineTab />
74+
</TabContainer>
75+
</DecodePageRoute>
76+
)}
77+
/>
78+
<Route
79+
path={DECODE_ROUTES.LIVE}
80+
element={(
81+
<DecodePageRoute>
82+
<TabContainer tab="lti_live" fetch={fetchLiveTab} slice="courseHome">
83+
<LiveTab />
84+
</TabContainer>
85+
</DecodePageRoute>
86+
)}
87+
/>
88+
<Route
89+
path={DECODE_ROUTES.DATES}
90+
element={(
91+
<DecodePageRoute>
92+
<TabContainer tab="dates" fetch={fetchDatesTab} slice="courseHome">
93+
<DatesTab />
94+
</TabContainer>
95+
</DecodePageRoute>
96+
)}
97+
/>
98+
<Route
99+
path={DECODE_ROUTES.DISCUSSION}
100+
element={(
101+
<DecodePageRoute>
102+
<TabContainer tab="discussion" fetch={fetchDiscussionTab} slice="courseHome">
103+
<DiscussionTab />
104+
</TabContainer>
105+
</DecodePageRoute>
106+
)}
107+
/>
108+
{DECODE_ROUTES.PROGRESS.map((route) => (
57109
<Route
58-
path={ROUTES.PREFERENCES_UNSUBSCRIBE}
59-
element={
60-
<PageWrap><PreferencesUnsubscribe /></PageWrap>
61-
}
62-
/>
63-
<Route
64-
path={DECODE_ROUTES.ACCESS_DENIED}
65-
element={<DecodePageRoute><CourseAccessErrorPage /></DecodePageRoute>}
66-
/>
67-
<Route
68-
path={DECODE_ROUTES.HOME}
69-
element={(
70-
<DecodePageRoute>
71-
<TabContainer tab="outline" fetch={fetchOutlineTab} slice="courseHome">
72-
<OutlineTab />
73-
</TabContainer>
74-
</DecodePageRoute>
75-
)}
76-
/>
77-
<Route
78-
path={DECODE_ROUTES.LIVE}
110+
key={route}
111+
path={route}
79112
element={(
80113
<DecodePageRoute>
81-
<TabContainer tab="lti_live" fetch={fetchLiveTab} slice="courseHome">
82-
<LiveTab />
114+
<TabContainer
115+
tab="progress"
116+
fetch={fetchProgressTab}
117+
slice="courseHome"
118+
isProgressTab
119+
>
120+
<ProgressTab />
83121
</TabContainer>
84122
</DecodePageRoute>
85-
)}
123+
)}
86124
/>
125+
))}
126+
<Route
127+
path={DECODE_ROUTES.COURSE_END}
128+
element={(
129+
<DecodePageRoute>
130+
<TabContainer tab="courseware" fetch={fetchCourse} slice="courseware">
131+
<CourseExit />
132+
</TabContainer>
133+
</DecodePageRoute>
134+
)}
135+
/>
136+
{DECODE_ROUTES.COURSEWARE.map((route) => (
87137
<Route
88-
path={DECODE_ROUTES.DATES}
138+
key={route}
139+
path={route}
89140
element={(
90141
<DecodePageRoute>
91-
<TabContainer tab="dates" fetch={fetchDatesTab} slice="courseHome">
92-
<DatesTab />
93-
</TabContainer>
142+
<CoursewareContainer />
94143
</DecodePageRoute>
95-
)}
144+
)}
96145
/>
146+
))}
147+
{getConfig()?.PLUGIN_ROUTES?.map((route) => (
97148
<Route
98-
path={DECODE_ROUTES.DISCUSSION}
149+
key={route}
150+
path={route}
99151
element={(
100152
<DecodePageRoute>
101-
<TabContainer tab="discussion" fetch={fetchDiscussionTab} slice="courseHome">
102-
<DiscussionTab />
103-
</TabContainer>
104-
</DecodePageRoute>
105-
)}
106-
/>
107-
{DECODE_ROUTES.PROGRESS.map((route) => (
108-
<Route
109-
key={route}
110-
path={route}
111-
element={(
112-
<DecodePageRoute>
113-
<TabContainer
114-
tab="progress"
115-
fetch={fetchProgressTab}
116-
slice="courseHome"
117-
isProgressTab
118-
>
119-
<ProgressTab />
120-
</TabContainer>
121-
</DecodePageRoute>
122-
)}
123-
/>
124-
))}
125-
<Route
126-
path={DECODE_ROUTES.COURSE_END}
127-
element={(
128-
<DecodePageRoute>
129-
<TabContainer tab="courseware" fetch={fetchCourse} slice="courseware">
130-
<CourseExit />
131-
</TabContainer>
153+
<PluginSlot id="course_page_route_slot" pluginProps={{ route }} />
132154
</DecodePageRoute>
133-
)}
155+
)}
134156
/>
135-
{DECODE_ROUTES.COURSEWARE.map((route) => (
136-
<Route
137-
key={route}
138-
path={route}
139-
element={(
140-
<DecodePageRoute>
141-
<CoursewareContainer />
142-
</DecodePageRoute>
143-
)}
144-
/>
145-
))}
146-
</Routes>
147-
</div>
157+
))}
158+
</Routes></div>
148159
</UserMessagesProvider>
149160
</NoticesProvider>
150161
</PathFixesProvider>

0 commit comments

Comments
 (0)