feat: adding feature for pagination
This commit is contained in:
1
.env
1
.env
@@ -40,3 +40,4 @@ HOTJAR_VERSION=6
|
||||
HOTJAR_DEBUG=false
|
||||
INVITE_STUDENTS_EMAIL_TO=''
|
||||
AI_TRANSLATIONS_BASE_URL=''
|
||||
ENABLE_PAGINATION_COURSES_STUDIO_HOME='true'
|
||||
|
||||
@@ -42,3 +42,4 @@ HOTJAR_VERSION=6
|
||||
HOTJAR_DEBUG=true
|
||||
INVITE_STUDENTS_EMAIL_TO="someone@domain.com"
|
||||
AI_TRANSLATIONS_BASE_URL='http://localhost:18760'
|
||||
ENABLE_PAGINATION_COURSES_STUDIO_HOME='true'
|
||||
|
||||
@@ -34,3 +34,4 @@ ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN=true
|
||||
ENABLE_TAGGING_TAXONOMY_PAGES=true
|
||||
BBB_LEARN_MORE_URL=''
|
||||
INVITE_STUDENTS_EMAIL_TO="someone@domain.com"
|
||||
ENABLE_PAGINATION_COURSES_STUDIO_HOME='true'
|
||||
|
||||
@@ -119,6 +119,7 @@ initialize({
|
||||
ENABLE_UNIT_PAGE: process.env.ENABLE_UNIT_PAGE || 'false',
|
||||
ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN: process.env.ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN || 'false',
|
||||
ENABLE_TAGGING_TAXONOMY_PAGES: process.env.ENABLE_TAGGING_TAXONOMY_PAGES || 'false',
|
||||
ENABLE_PAGINATION_COURSES_STUDIO_HOME: process.env.ENABLE_PAGINATION_COURSES_STUDIO_HOME || 'false',
|
||||
}, 'CourseAuthoringConfig');
|
||||
},
|
||||
},
|
||||
|
||||
@@ -49,6 +49,8 @@ const StudioHome = ({ intl }) => {
|
||||
redirectToLibraryAuthoringMfe,
|
||||
} = studioHomeData;
|
||||
|
||||
const isPaginationCoursesEnabled = getConfig().ENABLE_PAGINATION_COURSES_STUDIO_HOME === 'true';
|
||||
|
||||
function getHeaderButtons() {
|
||||
const headerButtons = [];
|
||||
|
||||
@@ -139,6 +141,7 @@ const StudioHome = ({ intl }) => {
|
||||
onClickNewCourse={() => setShowNewCourseContainer(true)}
|
||||
isShowProcessing={isShowProcessing}
|
||||
dispatch={dispatch}
|
||||
isPaginationCoursesEnabled={isPaginationCoursesEnabled}
|
||||
/>
|
||||
</section>
|
||||
</Layout.Element>
|
||||
|
||||
@@ -43,9 +43,21 @@ describe('<CardItem />', () => {
|
||||
const { getByText } = render(<RootWrapper {...props} />);
|
||||
expect(getByText(`${props.org} / ${props.number} / ${props.run}`)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render correct links for non-library course', () => {
|
||||
const props = studioHomeMock.archivedCourses[0];
|
||||
const { getByText, getByTestId } = render(<RootWrapper {...props} />);
|
||||
const { getByText } = render(<RootWrapper {...props} />);
|
||||
const courseTitleLink = getByText(props.displayName);
|
||||
expect(courseTitleLink).toHaveAttribute('href', `${getConfig().STUDIO_BASE_URL}${props.url}`);
|
||||
const btnReRunCourse = getByText(messages.btnReRunText.defaultMessage);
|
||||
expect(btnReRunCourse).toHaveAttribute('href', props.rerunLink);
|
||||
const viewLiveLink = getByText(messages.viewLiveBtnText.defaultMessage);
|
||||
expect(viewLiveLink).toHaveAttribute('href', props.lmsLink);
|
||||
});
|
||||
|
||||
it('should render correct links for non-library course pagination', () => {
|
||||
const props = studioHomeMock.archivedCourses[0];
|
||||
const { getByText, getByTestId } = render(<RootWrapper {...props} isPaginationEnabled />);
|
||||
const courseTitleLink = getByText(props.displayName);
|
||||
expect(courseTitleLink).toHaveAttribute('href', `${getConfig().STUDIO_BASE_URL}${props.url}`);
|
||||
const dropDownMenu = getByTestId('toggle-dropdown');
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Hyperlink,
|
||||
Dropdown,
|
||||
IconButton,
|
||||
ActionRow,
|
||||
} from '@openedx/paragon';
|
||||
import { MoreHoriz } from '@openedx/paragon/icons';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
@@ -24,6 +25,8 @@ const CardItem = ({
|
||||
number,
|
||||
run,
|
||||
isLibraries,
|
||||
courseKey,
|
||||
isPaginationEnabled,
|
||||
url,
|
||||
cmsLink,
|
||||
}) => {
|
||||
@@ -56,27 +59,48 @@ const CardItem = ({
|
||||
)}
|
||||
subtitle={subtitle}
|
||||
actions={showActions && (
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle
|
||||
as={IconButton}
|
||||
iconAs={MoreHoriz}
|
||||
variant="primary"
|
||||
data-testid="toggle-dropdown"
|
||||
/>
|
||||
<Dropdown.Menu>
|
||||
{isShowRerunLink && (
|
||||
<Dropdown.Item href={rerunLink}>
|
||||
{messages.btnReRunText.defaultMessage}
|
||||
isPaginationEnabled ? (
|
||||
<Dropdown>
|
||||
<Dropdown.Toggle
|
||||
as={IconButton}
|
||||
iconAs={MoreHoriz}
|
||||
variant="primary"
|
||||
data-testid="toggle-dropdown"
|
||||
/>
|
||||
<Dropdown.Menu>
|
||||
{isShowRerunLink && (
|
||||
<Dropdown.Item href={rerunLink}>
|
||||
{messages.btnReRunText.defaultMessage}
|
||||
</Dropdown.Item>
|
||||
)}
|
||||
<Dropdown.Item href={lmsLink}>
|
||||
{intl.formatMessage(messages.viewLiveBtnText)}
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item href={cmsLink}>
|
||||
{intl.formatMessage(messages.editStudioBtnText)}
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
) : (
|
||||
<ActionRow>
|
||||
{isShowRerunLink && (
|
||||
<Hyperlink
|
||||
className="small"
|
||||
destination={rerunLink}
|
||||
key={`action-row-rerunLink-${courseKey}`}
|
||||
>
|
||||
{intl.formatMessage(messages.btnReRunText)}
|
||||
</Hyperlink>
|
||||
)}
|
||||
<Dropdown.Item href={lmsLink}>
|
||||
<Hyperlink
|
||||
className="small ml-3"
|
||||
destination={lmsLink}
|
||||
key={`action-row-lmsLink-${courseKey}`}
|
||||
>
|
||||
{intl.formatMessage(messages.viewLiveBtnText)}
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item href={cmsLink}>
|
||||
{intl.formatMessage(messages.editStudioBtnText)}
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</Hyperlink>
|
||||
</ActionRow>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
@@ -85,6 +109,8 @@ const CardItem = ({
|
||||
|
||||
CardItem.defaultProps = {
|
||||
isLibraries: false,
|
||||
isPaginationEnabled: false,
|
||||
courseKey: '',
|
||||
rerunLink: '',
|
||||
lmsLink: '',
|
||||
run: '',
|
||||
@@ -102,6 +128,8 @@ CardItem.propTypes = {
|
||||
number: PropTypes.string.isRequired,
|
||||
url: PropTypes.string.isRequired,
|
||||
isLibraries: PropTypes.bool,
|
||||
courseKey: PropTypes.string,
|
||||
isPaginationEnabled: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default injectIntl(CardItem);
|
||||
|
||||
@@ -16,6 +16,10 @@ export async function getStudioHomeData() {
|
||||
return camelCaseObject(data);
|
||||
}
|
||||
|
||||
export async function getStudioHomeCourses(search) {
|
||||
const { data } = await getAuthenticatedHttpClient().get(`${getApiBaseUrl()}/api/contentstore/v1/home/courses${search}`);
|
||||
return camelCaseObject(data);
|
||||
}
|
||||
/**
|
||||
* Get's studio home courses.
|
||||
* @param {string} search - Query string parameters for filtering the courses.
|
||||
@@ -25,7 +29,7 @@ export async function getStudioHomeData() {
|
||||
* Features such as pagination, filtering, and ordering are better handled in the new version.
|
||||
* Please refer to this PR for further details: https://github.com/openedx/edx-platform/pull/34173
|
||||
*/
|
||||
export async function getStudioHomeCourses(search, customParams) {
|
||||
export async function getStudioHomeCoursesV2(search, customParams) {
|
||||
const { data } = await getAuthenticatedHttpClient().get(`${getApiBaseUrl()}/api/contentstore/v2/home/courses${search}`, { params: customParams });
|
||||
return camelCaseObject(data);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
sendRequestForCourseCreator,
|
||||
getApiBaseUrl,
|
||||
getStudioHomeCourses,
|
||||
getStudioHomeCoursesV2,
|
||||
getStudioHomeLibraries,
|
||||
} from './api';
|
||||
import { generateGetStudioCoursesApiResponse, generateGetStudioHomeDataApiResponse, generateGetStuioHomeLibrariesApiResponse } from '../factories/mockApiResponses';
|
||||
@@ -44,7 +45,7 @@ describe('studio-home api calls', () => {
|
||||
});
|
||||
|
||||
fit('should get studio courses data', async () => {
|
||||
const apiLink = `${getApiBaseUrl()}/api/contentstore/v2/home/courses`;
|
||||
const apiLink = `${getApiBaseUrl()}/api/contentstore/v1/home/courses`;
|
||||
axiosMock.onGet(apiLink).reply(200, generateGetStudioCoursesApiResponse());
|
||||
const result = await getStudioHomeCourses('');
|
||||
const expected = generateGetStudioCoursesApiResponse();
|
||||
@@ -53,6 +54,16 @@ describe('studio-home api calls', () => {
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
fit('should get studio courses data v2', async () => {
|
||||
const apiLink = `${getApiBaseUrl()}/api/contentstore/v2/home/courses`;
|
||||
axiosMock.onGet(apiLink).reply(200, generateGetStudioCoursesApiResponse());
|
||||
const result = await getStudioHomeCoursesV2('');
|
||||
const expected = generateGetStudioCoursesApiResponse();
|
||||
|
||||
expect(axiosMock.history.get[0].url).toEqual(apiLink);
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should get studio libraries data', async () => {
|
||||
const apiLink = `${getApiBaseUrl()}/api/contentstore/v1/home/libraries`;
|
||||
axiosMock.onGet(apiLink).reply(200, generateGetStuioHomeLibrariesApiResponse());
|
||||
|
||||
@@ -32,6 +32,12 @@ const slice = createSlice({
|
||||
Object.assign(state.studioHomeData, payload);
|
||||
},
|
||||
fetchCourseDataSuccess: (state, { payload }) => {
|
||||
const { courses, archivedCourses, inProcessCourseActions } = payload;
|
||||
state.studioHomeData.courses = courses;
|
||||
state.studioHomeData.archivedCourses = archivedCourses;
|
||||
state.studioHomeData.inProcessCourseActions = inProcessCourseActions;
|
||||
},
|
||||
fetchCourseDataSuccessV2: (state, { payload }) => {
|
||||
const { courses, archivedCourses = [], inProcessCourseActions } = payload.results;
|
||||
const { numPages, count } = payload;
|
||||
state.studioHomeData.courses = courses;
|
||||
@@ -56,6 +62,7 @@ export const {
|
||||
updateLoadingStatuses,
|
||||
fetchStudioHomeDataSuccess,
|
||||
fetchCourseDataSuccess,
|
||||
fetchCourseDataSuccessV2,
|
||||
fetchLibraryDataSuccess,
|
||||
updateStudioHomeCoursesCustomParams,
|
||||
} = slice.actions;
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
handleCourseNotification,
|
||||
getStudioHomeCourses,
|
||||
getStudioHomeLibraries,
|
||||
getStudioHomeCoursesV2,
|
||||
} from './api';
|
||||
import {
|
||||
fetchStudioHomeDataSuccess,
|
||||
@@ -12,9 +13,10 @@ import {
|
||||
updateLoadingStatuses,
|
||||
updateSavingStatuses,
|
||||
fetchLibraryDataSuccess,
|
||||
fetchCourseDataSuccessV2,
|
||||
} from './slice';
|
||||
|
||||
function fetchStudioHomeData(search, hasHomeData, customParams = {}) {
|
||||
function fetchStudioHomeData(search, hasHomeData, customParams = {}, isPaginationEnabled = false) {
|
||||
return async (dispatch) => {
|
||||
dispatch(updateLoadingStatuses({ studioHomeLoadingStatus: RequestStatus.IN_PROGRESS }));
|
||||
dispatch(updateLoadingStatuses({ courseLoadingStatus: RequestStatus.IN_PROGRESS }));
|
||||
@@ -30,8 +32,14 @@ function fetchStudioHomeData(search, hasHomeData, customParams = {}) {
|
||||
}
|
||||
}
|
||||
try {
|
||||
const coursesData = await getStudioHomeCourses(search || '', customParams);
|
||||
dispatch(fetchCourseDataSuccess(coursesData));
|
||||
if (isPaginationEnabled) {
|
||||
const coursesData = await getStudioHomeCoursesV2(search || '', customParams);
|
||||
dispatch(fetchCourseDataSuccessV2(coursesData));
|
||||
} else {
|
||||
const coursesData = await getStudioHomeCourses(search || '');
|
||||
dispatch(fetchCourseDataSuccess(coursesData));
|
||||
}
|
||||
|
||||
dispatch(updateLoadingStatuses({ courseLoadingStatus: RequestStatus.SUCCESSFUL }));
|
||||
} catch (error) {
|
||||
dispatch(updateLoadingStatuses({ courseLoadingStatus: RequestStatus.FAILED }));
|
||||
|
||||
@@ -49,6 +49,54 @@ export const generateGetStudioHomeDataApiResponse = () => ({
|
||||
});
|
||||
|
||||
export const generateGetStudioCoursesApiResponse = () => ({
|
||||
archivedCourses: [
|
||||
{
|
||||
courseKey: 'course-v1:MachineLearning+123+2023',
|
||||
displayName: 'Machine Learning',
|
||||
lmsLink: '//localhost:18000/courses/course-v1:MachineLearning+123+2023/jump_to/block-v1:MachineLearning+123+2023+type@course+block@course',
|
||||
number: '123',
|
||||
org: 'LSE',
|
||||
rerunLink: '/course_rerun/course-v1:MachineLearning+123+2023',
|
||||
run: '2023',
|
||||
url: '/course/course-v1:MachineLearning+123+2023',
|
||||
},
|
||||
{
|
||||
courseKey: 'course-v1:Design+123+e.g.2025',
|
||||
displayName: 'Design',
|
||||
lmsLink: '//localhost:18000/courses/course-v1:Design+123+e.g.2025/jump_to/block-v1:Design+123+e.g.2025+type@course+block@course',
|
||||
number: '123',
|
||||
org: 'University of Cape Town',
|
||||
rerunLink: '/course_rerun/course-v1:Design+123+e.g.2025',
|
||||
run: 'e.g.2025',
|
||||
url: '/course/course-v1:Design+123+e.g.2025',
|
||||
},
|
||||
],
|
||||
courses: [
|
||||
{
|
||||
courseKey: 'course-v1:HarvardX+123+2023',
|
||||
displayName: 'Managing Risk in the Information Age',
|
||||
lmsLink: '//localhost:18000/courses/course-v1:HarvardX+123+2023/jump_to/block-v1:HarvardX+123+2023+type@course+block@course',
|
||||
number: '123',
|
||||
org: 'HarvardX',
|
||||
rerunLink: '/course_rerun/course-v1:HarvardX+123+2023',
|
||||
run: '2023',
|
||||
url: '/course/course-v1:HarvardX+123+2023',
|
||||
},
|
||||
{
|
||||
courseKey: 'org.0/course_0/Run_0',
|
||||
displayName: 'Run 0',
|
||||
lmsLink: null,
|
||||
number: 'course_0',
|
||||
org: 'org.0',
|
||||
rerunLink: null,
|
||||
run: 'Run_0',
|
||||
url: null,
|
||||
},
|
||||
],
|
||||
inProcessCourseActions: [],
|
||||
});
|
||||
|
||||
export const generateGetStudioCoursesApiResponseV2 = () => ({
|
||||
count: 5,
|
||||
next: null,
|
||||
previous: null,
|
||||
|
||||
@@ -37,7 +37,7 @@ const useStudioHome = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const { currentPage } = studioHomeCoursesParams;
|
||||
dispatch(fetchStudioHomeData(location.search ?? '', false, { page: currentPage }));
|
||||
dispatch(fetchStudioHomeData(location.search ?? '', false, { page: currentPage }, true));
|
||||
}, [studioHomeCoursesParams.currentPage]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
initialState,
|
||||
generateGetStudioHomeDataApiResponse,
|
||||
generateGetStudioCoursesApiResponse,
|
||||
generateGetStudioCoursesApiResponseV2,
|
||||
generateGetStuioHomeLibrariesApiResponse,
|
||||
} from '../factories/mockApiResponses';
|
||||
import { getApiBaseUrl, getStudioHomeApiUrl } from '../data/api';
|
||||
@@ -27,15 +28,21 @@ const { studioShortName } = studioHomeMock;
|
||||
|
||||
let axiosMock;
|
||||
let store;
|
||||
const courseApiLink = `${getApiBaseUrl()}/api/contentstore/v2/home/courses`;
|
||||
const courseApiLink = `${getApiBaseUrl()}/api/contentstore/v1/home/courses`;
|
||||
const courseApiLinkV2 = `${getApiBaseUrl()}/api/contentstore/v2/home/courses`;
|
||||
const libraryApiLink = `${getApiBaseUrl()}/api/contentstore/v1/home/libraries`;
|
||||
|
||||
const mockDispatch = jest.fn();
|
||||
|
||||
const RootWrapper = () => (
|
||||
const RootWrapper = (overrideProps) => (
|
||||
<AppProvider store={store}>
|
||||
<IntlProvider locale="en" messages={{}}>
|
||||
<TabsSection intl={{ formatMessage: jest.fn() }} dispatch={mockDispatch} />
|
||||
<TabsSection
|
||||
intl={{ formatMessage: jest.fn() }}
|
||||
dispatch={mockDispatch}
|
||||
isPaginationCoursesEnabled={false}
|
||||
{...overrideProps}
|
||||
/>
|
||||
</IntlProvider>
|
||||
</AppProvider>
|
||||
);
|
||||
@@ -94,7 +101,7 @@ describe('<TabsSection />', () => {
|
||||
|
||||
it('should render default sections when courses are empty', async () => {
|
||||
const data = generateGetStudioCoursesApiResponse();
|
||||
data.results.courses = [];
|
||||
data.courses = [];
|
||||
|
||||
render(<RootWrapper />);
|
||||
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, generateGetStudioHomeDataApiResponse());
|
||||
@@ -120,11 +127,11 @@ describe('<TabsSection />', () => {
|
||||
});
|
||||
|
||||
it('should render pagination when there are courses', async () => {
|
||||
render(<RootWrapper />);
|
||||
render(<RootWrapper isPaginationCoursesEnabled />);
|
||||
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, generateGetStudioHomeDataApiResponse());
|
||||
axiosMock.onGet(courseApiLink).reply(200, generateGetStudioCoursesApiResponse());
|
||||
await executeThunk(fetchStudioHomeData(), store.dispatch);
|
||||
const data = generateGetStudioCoursesApiResponse();
|
||||
axiosMock.onGet(courseApiLinkV2).reply(200, generateGetStudioCoursesApiResponseV2());
|
||||
await executeThunk(fetchStudioHomeData('', true, {}, true), store.dispatch);
|
||||
const data = generateGetStudioCoursesApiResponseV2();
|
||||
const coursesLength = data.results.courses.length;
|
||||
const totalItems = data.count;
|
||||
const paginationInfoText = `Showing ${coursesLength} of ${totalItems}`;
|
||||
@@ -138,11 +145,11 @@ describe('<TabsSection />', () => {
|
||||
});
|
||||
|
||||
it('should not render pagination when there are not courses', async () => {
|
||||
const data = generateGetStudioCoursesApiResponse();
|
||||
const data = generateGetStudioCoursesApiResponseV2();
|
||||
data.results.courses = [];
|
||||
render(<RootWrapper />);
|
||||
axiosMock.onGet(getStudioHomeApiUrl()).reply(200, generateGetStudioHomeDataApiResponse());
|
||||
axiosMock.onGet(courseApiLink).reply(200, data);
|
||||
axiosMock.onGet(courseApiLinkV2).reply(200, data);
|
||||
await executeThunk(fetchStudioHomeData(), store.dispatch);
|
||||
|
||||
const pagination = screen.queryByRole('navigation');
|
||||
|
||||
@@ -27,6 +27,7 @@ const CoursesTab = ({
|
||||
dispatch,
|
||||
numPages,
|
||||
coursesCount,
|
||||
isEnabledPagination,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const {
|
||||
@@ -66,7 +67,7 @@ const CoursesTab = ({
|
||||
) : (
|
||||
<>
|
||||
{isShowProcessing && <ProcessingCourses />}
|
||||
{hasCourses && (
|
||||
{hasCourses && isEnabledPagination && (
|
||||
<div className="d-flex justify-content-end">
|
||||
<p data-testid="pagination-info">
|
||||
{intl.formatMessage(messages.coursesPaginationInfo, {
|
||||
@@ -100,11 +101,12 @@ const CoursesTab = ({
|
||||
run={run}
|
||||
url={url}
|
||||
cmsLink={cmsLink}
|
||||
isPaginated={isEnabledPagination}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
|
||||
{numPages > 1 && (
|
||||
{numPages > 1 && isEnabledPagination && (
|
||||
<Pagination
|
||||
className="d-flex justify-content-center"
|
||||
paginationLabel="pagination navigation"
|
||||
@@ -133,6 +135,12 @@ const CoursesTab = ({
|
||||
);
|
||||
};
|
||||
|
||||
CoursesTab.defaultProps = {
|
||||
numPages: 0,
|
||||
coursesCount: 0,
|
||||
isEnabledPagination: false,
|
||||
};
|
||||
|
||||
CoursesTab.propTypes = {
|
||||
coursesDataItems: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
@@ -152,8 +160,9 @@ CoursesTab.propTypes = {
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
isFailed: PropTypes.bool.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
numPages: PropTypes.number.isRequired,
|
||||
coursesCount: PropTypes.number.isRequired,
|
||||
numPages: PropTypes.number,
|
||||
coursesCount: PropTypes.number,
|
||||
isEnabledPagination: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default CoursesTab;
|
||||
|
||||
@@ -13,7 +13,12 @@ import { RequestStatus } from '../../data/constants';
|
||||
import { fetchLibraryData } from '../data/thunks';
|
||||
|
||||
const TabsSection = ({
|
||||
intl, showNewCourseContainer, onClickNewCourse, isShowProcessing, dispatch,
|
||||
intl,
|
||||
showNewCourseContainer,
|
||||
onClickNewCourse,
|
||||
isShowProcessing,
|
||||
dispatch,
|
||||
isPaginationCoursesEnabled,
|
||||
}) => {
|
||||
const TABS_LIST = {
|
||||
courses: 'courses',
|
||||
@@ -56,6 +61,7 @@ const TabsSection = ({
|
||||
dispatch={dispatch}
|
||||
numPages={numPages}
|
||||
coursesCount={coursesCount}
|
||||
isEnabledPagination={isPaginationCoursesEnabled}
|
||||
/>
|
||||
</Tab>,
|
||||
);
|
||||
@@ -118,12 +124,17 @@ const TabsSection = ({
|
||||
);
|
||||
};
|
||||
|
||||
TabsSection.defaultProps = {
|
||||
isPaginationCoursesEnabled: false,
|
||||
};
|
||||
|
||||
TabsSection.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
showNewCourseContainer: PropTypes.bool.isRequired,
|
||||
onClickNewCourse: PropTypes.func.isRequired,
|
||||
isShowProcessing: PropTypes.bool.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
isPaginationCoursesEnabled: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default injectIntl(TabsSection);
|
||||
|
||||
Reference in New Issue
Block a user