feat: make placeholder depend on api response (#537)
This commit is contained in:
2
.env
2
.env
@@ -38,11 +38,9 @@ ENABLE_NEW_VIDEO_UPLOAD_PAGE = false
|
||||
ENABLE_NEW_SCHEDULE_DETAILS_PAGE = false
|
||||
ENABLE_NEW_GRADING_PAGE = false
|
||||
ENABLE_NEW_COURSE_TEAM_PAGE = false
|
||||
ENABLE_NEW_ADVANCED_SETTINGS_PAGE = false
|
||||
ENABLE_NEW_IMPORT_PAGE = false
|
||||
ENABLE_NEW_EXPORT_PAGE = false
|
||||
ENABLE_UNIT_PAGE = false
|
||||
ENABLE_NEW_CUSTOM_PAGES = false
|
||||
ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN = false
|
||||
BBB_LEARN_MORE_URL=''
|
||||
HOTJAR_APP_ID=''
|
||||
|
||||
@@ -40,11 +40,9 @@ ENABLE_NEW_VIDEO_UPLOAD_PAGE = false
|
||||
ENABLE_NEW_SCHEDULE_DETAILS_PAGE = false
|
||||
ENABLE_NEW_GRADING_PAGE = false
|
||||
ENABLE_NEW_COURSE_TEAM_PAGE = false
|
||||
ENABLE_NEW_ADVANCED_SETTINGS_PAGE = true
|
||||
ENABLE_NEW_IMPORT_PAGE = false
|
||||
ENABLE_NEW_EXPORT_PAGE = false
|
||||
ENABLE_UNIT_PAGE = false
|
||||
ENABLE_NEW_CUSTOM_PAGES = true
|
||||
ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN = false
|
||||
BBB_LEARN_MORE_URL=''
|
||||
HOTJAR_APP_ID=''
|
||||
|
||||
@@ -36,10 +36,8 @@ ENABLE_NEW_VIDEO_UPLOAD_PAGE = true
|
||||
ENABLE_NEW_SCHEDULE_DETAILS_PAGE = true
|
||||
ENABLE_NEW_GRADING_PAGE = true
|
||||
ENABLE_NEW_COURSE_TEAM_PAGE = true
|
||||
ENABLE_NEW_ADVANCED_SETTINGS_PAGE = true
|
||||
ENABLE_NEW_IMPORT_PAGE = true
|
||||
ENABLE_NEW_EXPORT_PAGE = true
|
||||
ENABLE_UNIT_PAGE = true
|
||||
ENABLE_NEW_CUSTOM_PAGES = true
|
||||
ENABLE_VIDEO_UPLOAD_PAGE_LINK_IN_CONTENT_DROPDOWN = true
|
||||
BBB_LEARN_MORE_URL=''
|
||||
|
||||
@@ -63,10 +63,7 @@ const CourseAuthoringRoutes = ({ courseId }) => {
|
||||
<ProctoredExamSettings courseId={courseId} />
|
||||
</PageRoute>
|
||||
<PageRoute path={`${path}/custom-pages`}>
|
||||
{process.env.ENABLE_NEW_CUSTOM_PAGES === 'true'
|
||||
&& (
|
||||
<CustomPages courseId={courseId} />
|
||||
)}
|
||||
<CustomPages courseId={courseId} />
|
||||
</PageRoute>
|
||||
<PageRoute path={`${path}/container/:blockId`}>
|
||||
{process.env.ENABLE_UNIT_PAGE === 'true'
|
||||
@@ -109,10 +106,7 @@ const CourseAuthoringRoutes = ({ courseId }) => {
|
||||
)}
|
||||
</PageRoute>
|
||||
<PageRoute path={`${path}/settings/advanced`}>
|
||||
{process.env.ENABLE_NEW_ADVANCED_SETTINGS_PAGE === 'true'
|
||||
&& (
|
||||
<AdvancedSettings courseId={courseId} />
|
||||
)}
|
||||
<AdvancedSettings courseId={courseId} />
|
||||
</PageRoute>
|
||||
<PageRoute path={`${path}/import`}>
|
||||
{process.env.ENABLE_NEW_IMPORT_PAGE === 'true'
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from '@edx/paragon';
|
||||
import { CheckCircle, Info, Warning } from '@edx/paragon/icons';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import Placeholder from '@edx/frontend-lib-content-components';
|
||||
|
||||
import AlertProctoringError from '../generic/AlertProctoringError';
|
||||
import InternetConnectionAlert from '../generic/internet-connection-alert';
|
||||
@@ -72,6 +73,13 @@ const AdvancedSettings = ({ intl, courseId }) => {
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
return <></>;
|
||||
}
|
||||
if (loadingSettingsStatus === RequestStatus.DENIED) {
|
||||
return (
|
||||
<div className="row justify-contnt-center m-6">
|
||||
<Placeholder />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const handleSettingChange = (e, settingName) => {
|
||||
const { value } = e.target;
|
||||
|
||||
@@ -22,7 +22,11 @@ export function fetchCourseAppSettings(courseId) {
|
||||
dispatch(fetchCourseAppsSettingsSuccess(settingValues));
|
||||
dispatch(updateLoadingStatus({ status: RequestStatus.SUCCESSFUL }));
|
||||
} catch (error) {
|
||||
dispatch(updateLoadingStatus({ status: RequestStatus.FAILED }));
|
||||
if (error.response && error.response.status === 403) {
|
||||
dispatch(updateLoadingStatus({ courseId, status: RequestStatus.DENIED }));
|
||||
} else {
|
||||
dispatch(updateLoadingStatus({ courseId, status: RequestStatus.FAILED }));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
ModalDialog,
|
||||
} from '@edx/paragon';
|
||||
import { Add, SpinnerSimple } from '@edx/paragon/icons';
|
||||
import {
|
||||
import Placeholder, {
|
||||
DraggableList,
|
||||
SortableItem,
|
||||
ErrorAlert,
|
||||
@@ -96,13 +96,18 @@ const CustomPages = ({
|
||||
},
|
||||
disabledStates: ['pending'],
|
||||
};
|
||||
|
||||
useEffect(() => { setOrderedPages(pages); }, [customPagesIds, savingStatus]);
|
||||
if (loadingStatus === RequestStatus.IN_PROGRESS) {
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
return (<></>);
|
||||
}
|
||||
|
||||
if (loadingStatus === RequestStatus.DENIED) {
|
||||
return (
|
||||
<div data-testid="under-construction-placeholder" className="row justify-contnt-center m-6">
|
||||
<Placeholder />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<CustomPagesProvider courseId={courseId}>
|
||||
<main className="container container-mw-xl p-4 pt-5">
|
||||
|
||||
@@ -19,6 +19,7 @@ import CustomPages from './CustomPages';
|
||||
import {
|
||||
generateFetchPageApiResponse,
|
||||
generateNewPageApiResponse,
|
||||
getStatusValue,
|
||||
courseId,
|
||||
initialState,
|
||||
} from './factories/mockApiResponses';
|
||||
@@ -45,12 +46,12 @@ const renderComponent = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const mockStore = async () => {
|
||||
const mockStore = async (status) => {
|
||||
const xblockAddUrl = `${getApiBaseUrl()}/xblock/`;
|
||||
const reorderUrl = `${getTabHandlerUrl(courseId)}/reorder`;
|
||||
const fetchPagesUrl = `${getTabHandlerUrl(courseId)}`;
|
||||
|
||||
axiosMock.onGet(fetchPagesUrl).reply(200, generateFetchPageApiResponse());
|
||||
axiosMock.onGet(fetchPagesUrl).reply(getStatusValue(status), generateFetchPageApiResponse());
|
||||
axiosMock.onPost(reorderUrl).reply(204);
|
||||
axiosMock.onPut(xblockAddUrl).reply(200, generateNewPageApiResponse());
|
||||
|
||||
@@ -72,21 +73,26 @@ describe('CustomPages', () => {
|
||||
store = initializeStore(initialState);
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
});
|
||||
it('should ', async () => {
|
||||
renderComponent();
|
||||
await mockStore(RequestStatus.DENIED);
|
||||
expect(screen.getByTestId('under-construction-placeholder')).toBeVisible();
|
||||
});
|
||||
it('should have breadecrumbs', async () => {
|
||||
renderComponent();
|
||||
await mockStore();
|
||||
await mockStore(RequestStatus.SUCCESSFUL);
|
||||
expect(screen.getByLabelText('Custom Page breadcrumbs')).toBeVisible();
|
||||
});
|
||||
it('should contain header row with title, add button and view live button', async () => {
|
||||
renderComponent();
|
||||
await mockStore();
|
||||
await mockStore(RequestStatus.SUCCESSFUL);
|
||||
expect(screen.getByText(messages.heading.defaultMessage)).toBeVisible();
|
||||
expect(screen.getByTestId('header-add-button')).toBeVisible();
|
||||
expect(screen.getByTestId('header-view-live-button')).toBeVisible();
|
||||
});
|
||||
it('should add new page when "add a new page button" is clicked', async () => {
|
||||
renderComponent();
|
||||
await mockStore();
|
||||
await mockStore(RequestStatus.SUCCESSFUL);
|
||||
const addButton = screen.getByTestId('body-add-button');
|
||||
expect(addButton).toBeVisible();
|
||||
await act(async () => { fireEvent.click(addButton); });
|
||||
@@ -95,7 +101,7 @@ describe('CustomPages', () => {
|
||||
});
|
||||
it('should open student view modal when "add a new page button" is clicked', async () => {
|
||||
renderComponent();
|
||||
await mockStore();
|
||||
await mockStore(RequestStatus.SUCCESSFUL);
|
||||
const viewButton = screen.getByTestId('student-view-example-button');
|
||||
expect(viewButton).toBeVisible();
|
||||
expect(screen.queryByLabelText(messages.studentViewModalTitle.defaultMessage)).toBeNull();
|
||||
@@ -104,7 +110,7 @@ describe('CustomPages', () => {
|
||||
});
|
||||
it('should update page order on drag', async () => {
|
||||
renderComponent();
|
||||
await mockStore();
|
||||
await mockStore(RequestStatus.SUCCESSFUL);
|
||||
const buttons = await screen.queryAllByRole('button');
|
||||
const draggableButton = buttons[9];
|
||||
expect(draggableButton).toBeVisible();
|
||||
|
||||
@@ -11,7 +11,6 @@ const slice = createSlice({
|
||||
savingStatus: '',
|
||||
addingStatus: 'default',
|
||||
deletingStatus: '',
|
||||
customPagesApiStatus: {},
|
||||
},
|
||||
reducers: {
|
||||
setPageIds: (state, { payload }) => {
|
||||
|
||||
@@ -38,9 +38,10 @@ export function fetchCustomPages(courseId) {
|
||||
dispatch(updateLoadingStatus({ courseId, status: RequestStatus.SUCCESSFUL }));
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status === 403) {
|
||||
dispatch(updateCustomPagesApiStatus({ status: RequestStatus.DENIED }));
|
||||
dispatch(updateLoadingStatus({ courseId, status: RequestStatus.DENIED }));
|
||||
} else {
|
||||
dispatch(updateLoadingStatus({ courseId, status: RequestStatus.FAILED }));
|
||||
}
|
||||
dispatch(updateLoadingStatus({ courseId, status: RequestStatus.FAILED }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { RequestStatus } from '../../data/constants';
|
||||
|
||||
export const courseId = 'course-v1:edX+DemoX+Demo_Course';
|
||||
|
||||
export const initialState = {
|
||||
@@ -62,3 +64,12 @@ export const generateNewPageApiResponse = () => ({
|
||||
locator: 'mOckID2',
|
||||
courseKey: courseId,
|
||||
});
|
||||
|
||||
export const getStatusValue = (status) => {
|
||||
switch (status) {
|
||||
case RequestStatus.DENIED:
|
||||
return 403;
|
||||
default:
|
||||
return 200;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -35,18 +35,6 @@ const PageCard = ({
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
const SettingsButton = () => {
|
||||
if (page.legacyLink) {
|
||||
if (process.env.ENABLE_NEW_CUSTOM_PAGES === 'true' && page.name === 'Custom pages') {
|
||||
return (
|
||||
<Hyperlink destination="custom-pages">
|
||||
<IconButton
|
||||
src={ArrowForward}
|
||||
iconAs={Icon}
|
||||
size="inline"
|
||||
alt={intl.formatMessage(messages.settings)}
|
||||
/>
|
||||
</Hyperlink>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Hyperlink destination={page.legacyLink}>
|
||||
<IconButton
|
||||
|
||||
@@ -61,11 +61,6 @@ describe('LiveSettings', () => {
|
||||
renderComponent();
|
||||
expect(queryAllByRole(container, 'button')).toHaveLength(3);
|
||||
});
|
||||
it('should navigate to custom-pages', async () => {
|
||||
renderComponent();
|
||||
const [customPagesSettingsButton] = queryAllByRole(container, 'link');
|
||||
expect(customPagesSettingsButton).toHaveAttribute('href', 'custom-pages');
|
||||
});
|
||||
it('should navigate to legacyLink', async () => {
|
||||
renderComponent();
|
||||
const textbookSettingsButton = queryAllByRole(container, 'link')[1];
|
||||
|
||||
@@ -41,7 +41,7 @@ const Header = ({
|
||||
<a rel="noopener" href={getPagePath(courseId, process.env.ENABLE_NEW_UPDATES_PAGE, 'course_info')}>{intl.formatMessage(messages['header.links.updates'])}</a>
|
||||
</div>
|
||||
<div className="mb-1 small">
|
||||
<a rel="noopener" href={getPagePath(courseId, process.env.ENABLE_NEW_CUSTOM_PAGES, 'tabs')}>{intl.formatMessage(messages['header.links.pages'])}</a>
|
||||
<a rel="noopener" href={getPagePath(courseId, 'true', 'tabs')}>{intl.formatMessage(messages['header.links.pages'])}</a>
|
||||
</div>
|
||||
<div className="mb-1 small">
|
||||
<a rel="noopener" href={getPagePath(courseId, process.env.ENABLE_NEW_FILES_UPLOADS_PAGE, 'assets')}>{intl.formatMessage(messages['header.links.filesAndUploads'])}</a>
|
||||
@@ -75,7 +75,7 @@ const Header = ({
|
||||
<a rel="noopener" href={`${config.STUDIO_BASE_URL}/group_configurations/course-v1:${courseId}`}>{intl.formatMessage(messages['header.links.groupConfigurations'])}</a>
|
||||
</div>
|
||||
<div className="mb-1 small">
|
||||
<a rel="noopener" href={getPagePath(courseId, process.env.ENABLE_NEW_ADVANCED_SETTINGS_PAGE, 'settings/advanced')}>{intl.formatMessage(messages['header.links.advancedSettings'])}</a>
|
||||
<a rel="noopener" href={`${config.STUDIO_BASE_URL}/settings/advanced/${courseId}`}>{intl.formatMessage(messages['header.links.advancedSettings'])}</a>
|
||||
</div>
|
||||
<div className="mb-1 small">
|
||||
<a rel="noopener" href={`${config.STUDIO_BASE_URL}/certificates/${courseId}`}>{intl.formatMessage(messages['header.links.certificates'])}</a>
|
||||
|
||||
Reference in New Issue
Block a user