From 0e1a7e26031afc3b2735fe342de4048a5f72639a Mon Sep 17 00:00:00 2001 From: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> Date: Tue, 25 Jul 2023 10:32:31 -0400 Subject: [PATCH] feat: make placeholder depend on api response (#537) --- .env | 2 -- .env.development | 2 -- .env.test | 2 -- src/CourseAuthoringRoutes.jsx | 10 ++-------- src/advanced-settings/AdvancedSettings.jsx | 8 ++++++++ src/advanced-settings/data/thunks.js | 6 +++++- src/custom-pages/CustomPages.jsx | 11 +++++++--- src/custom-pages/CustomPages.test.jsx | 20 ++++++++++++------- src/custom-pages/data/slice.js | 1 - src/custom-pages/data/thunks.js | 5 +++-- .../factories/mockApiResponses.jsx | 11 ++++++++++ src/pages-and-resources/pages/PageCard.jsx | 12 ----------- .../pages/PageCard.test.jsx | 5 ----- src/studio-header/Header.jsx | 4 ++-- 14 files changed, 52 insertions(+), 47 deletions(-) diff --git a/.env b/.env index 8954bf935d0..b3c18d99309 100644 --- a/.env +++ b/.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='' diff --git a/.env.development b/.env.development index 9b907ba82b0..7b74ec350cd 100644 --- a/.env.development +++ b/.env.development @@ -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='' diff --git a/.env.test b/.env.test index bebc3369d8f..3145de28832 100644 --- a/.env.test +++ b/.env.test @@ -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='' diff --git a/src/CourseAuthoringRoutes.jsx b/src/CourseAuthoringRoutes.jsx index 5bd24937a89..d016c53251e 100644 --- a/src/CourseAuthoringRoutes.jsx +++ b/src/CourseAuthoringRoutes.jsx @@ -63,10 +63,7 @@ const CourseAuthoringRoutes = ({ courseId }) => { - {process.env.ENABLE_NEW_CUSTOM_PAGES === 'true' - && ( - - )} + {process.env.ENABLE_UNIT_PAGE === 'true' @@ -109,10 +106,7 @@ const CourseAuthoringRoutes = ({ courseId }) => { )} - {process.env.ENABLE_NEW_ADVANCED_SETTINGS_PAGE === 'true' - && ( - - )} + {process.env.ENABLE_NEW_IMPORT_PAGE === 'true' diff --git a/src/advanced-settings/AdvancedSettings.jsx b/src/advanced-settings/AdvancedSettings.jsx index 3521f1b77ba..c019fbfdb84 100644 --- a/src/advanced-settings/AdvancedSettings.jsx +++ b/src/advanced-settings/AdvancedSettings.jsx @@ -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 ( + + + + ); + } const handleSettingChange = (e, settingName) => { const { value } = e.target; diff --git a/src/advanced-settings/data/thunks.js b/src/advanced-settings/data/thunks.js index 86865820938..99a421ffd84 100644 --- a/src/advanced-settings/data/thunks.js +++ b/src/advanced-settings/data/thunks.js @@ -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 })); + } } }; } diff --git a/src/custom-pages/CustomPages.jsx b/src/custom-pages/CustomPages.jsx index 87e3ead4c11..8b82cf0d0e6 100644 --- a/src/custom-pages/CustomPages.jsx +++ b/src/custom-pages/CustomPages.jsx @@ -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 ( + + + + ); + } return ( diff --git a/src/custom-pages/CustomPages.test.jsx b/src/custom-pages/CustomPages.test.jsx index 2ef77a0ab57..979145082a0 100644 --- a/src/custom-pages/CustomPages.test.jsx +++ b/src/custom-pages/CustomPages.test.jsx @@ -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(); diff --git a/src/custom-pages/data/slice.js b/src/custom-pages/data/slice.js index cc1f247285d..1e30f93187f 100644 --- a/src/custom-pages/data/slice.js +++ b/src/custom-pages/data/slice.js @@ -11,7 +11,6 @@ const slice = createSlice({ savingStatus: '', addingStatus: 'default', deletingStatus: '', - customPagesApiStatus: {}, }, reducers: { setPageIds: (state, { payload }) => { diff --git a/src/custom-pages/data/thunks.js b/src/custom-pages/data/thunks.js index d4de3dcc54d..d485c7f9c33 100644 --- a/src/custom-pages/data/thunks.js +++ b/src/custom-pages/data/thunks.js @@ -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 })); } }; } diff --git a/src/custom-pages/factories/mockApiResponses.jsx b/src/custom-pages/factories/mockApiResponses.jsx index 7acdf68b452..15f3882f6a2 100644 --- a/src/custom-pages/factories/mockApiResponses.jsx +++ b/src/custom-pages/factories/mockApiResponses.jsx @@ -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; + } +}; diff --git a/src/pages-and-resources/pages/PageCard.jsx b/src/pages-and-resources/pages/PageCard.jsx index 5891b01ce68..4d7458bbdba 100644 --- a/src/pages-and-resources/pages/PageCard.jsx +++ b/src/pages-and-resources/pages/PageCard.jsx @@ -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 ( - - - - ); - } return ( { 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]; diff --git a/src/studio-header/Header.jsx b/src/studio-header/Header.jsx index 500937d65e5..650bb4e5b01 100644 --- a/src/studio-header/Header.jsx +++ b/src/studio-header/Header.jsx @@ -41,7 +41,7 @@ const Header = ({ {intl.formatMessage(messages['header.links.updates'])} - {intl.formatMessage(messages['header.links.pages'])} + {intl.formatMessage(messages['header.links.pages'])} {intl.formatMessage(messages['header.links.filesAndUploads'])} @@ -75,7 +75,7 @@ const Header = ({ {intl.formatMessage(messages['header.links.groupConfigurations'])} - {intl.formatMessage(messages['header.links.advancedSettings'])} + {intl.formatMessage(messages['header.links.advancedSettings'])} {intl.formatMessage(messages['header.links.certificates'])}