From d7bbd40de13cd46fb5713f0ce6801f6eb250569b Mon Sep 17 00:00:00 2001 From: Jillian Date: Wed, 6 Nov 2024 06:44:09 +1030 Subject: [PATCH] fix: Hide / error on Libraries v2 pages if !librariesV2Enabled (#1449) Show an error message if the user tries to view a v2 Library while Libraries V2 are disabled in the platform. --- .../LibraryAuthoringPage.test.tsx | 17 ++++++++++++++++- src/library-authoring/LibraryAuthoringPage.tsx | 16 ++++++++++++++++ .../add-content/AddContentWorkflow.test.tsx | 10 +++++++--- .../PickLibraryContentModal.test.tsx | 4 +++- .../component-picker/ComponentPicker.test.tsx | 7 +++++++ src/library-authoring/messages.ts | 5 +++++ src/studio-home/StudioHome.tsx | 2 +- src/studio-home/hooks.jsx | 4 +++- 8 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/library-authoring/LibraryAuthoringPage.test.tsx b/src/library-authoring/LibraryAuthoringPage.test.tsx index 84e50c70fc..f6d55792ad 100644 --- a/src/library-authoring/LibraryAuthoringPage.test.tsx +++ b/src/library-authoring/LibraryAuthoringPage.test.tsx @@ -18,6 +18,8 @@ import { mockXBlockFields, } from './data/api.mocks'; import { mockContentSearchConfig } from '../search-manager/data/api.mock'; +import { studioHomeMock } from '../studio-home/__mocks__'; +import { getStudioHomeApiUrl } from '../studio-home/data/api'; import { mockBroadcastChannel } from '../generic/data/api.mock'; import { LibraryLayout } from '.'; import { getLibraryCollectionsApiUrl } from './data/api'; @@ -79,7 +81,8 @@ const libraryTitle = mockContentLibrary.libraryData.title; describe('', () => { beforeEach(() => { - initializeMocks(); + const { axiosMock } = initializeMocks(); + axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock); // The Meilisearch client-side API uses fetch, not Axios. fetchMock.mockReset(); @@ -787,4 +790,16 @@ describe('', () => { }); }); }); + + it('Shows an error if libraries V2 is disabled', async () => { + const { axiosMock } = initializeMocks(); + axiosMock.onGet(getStudioHomeApiUrl()).reply(200, { + ...studioHomeMock, + libraries_v2_enabled: false, + }); + + render(, { path, params: { libraryId: mockContentLibrary.libraryId } }); + await waitFor(() => { expect(axiosMock.history.get.length).toBe(1); }); + expect(screen.getByRole('alert')).toHaveTextContent('This page cannot be shown: Libraries v2 are disabled.'); + }); }); diff --git a/src/library-authoring/LibraryAuthoringPage.tsx b/src/library-authoring/LibraryAuthoringPage.tsx index 57ca633ab1..ebb0b03083 100644 --- a/src/library-authoring/LibraryAuthoringPage.tsx +++ b/src/library-authoring/LibraryAuthoringPage.tsx @@ -4,6 +4,7 @@ import classNames from 'classnames'; import { StudioFooter } from '@edx/frontend-component-footer'; import { useIntl } from '@edx/frontend-platform/i18n'; import { + Alert, Badge, Breadcrumb, Button, @@ -25,6 +26,7 @@ import Loading from '../generic/Loading'; import SubHeader from '../generic/sub-header/SubHeader'; import Header from '../header'; import NotFoundAlert from '../generic/NotFoundAlert'; +import { useStudioHome } from '../studio-home/hooks'; import { ClearFiltersButton, FilterByBlockType, @@ -143,6 +145,12 @@ const LibraryAuthoringPage = ({ returnToLibrarySelection }: LibraryAuthoringPage const location = useLocation(); const navigate = useNavigate(); + const { + isLoadingPage: isLoadingStudioHome, + isFailedLoadingPage: isFailedLoadingStudioHome, + librariesV2Enabled, + } = useStudioHome(); + const { libraryId, libraryData, @@ -178,6 +186,14 @@ const LibraryAuthoringPage = ({ returnToLibrarySelection }: LibraryAuthoringPage return ; } + if (!isLoadingStudioHome && (!librariesV2Enabled || isFailedLoadingStudioHome)) { + return ( + + {intl.formatMessage(messages.librariesV2DisabledError)} + + ); + } + // istanbul ignore if: this should never happen if (activeKey === undefined) { return ; diff --git a/src/library-authoring/add-content/AddContentWorkflow.test.tsx b/src/library-authoring/add-content/AddContentWorkflow.test.tsx index bd464f39b3..915cfc6fe2 100644 --- a/src/library-authoring/add-content/AddContentWorkflow.test.tsx +++ b/src/library-authoring/add-content/AddContentWorkflow.test.tsx @@ -19,6 +19,8 @@ import { } from '../data/api.mocks'; import { mockBroadcastChannel, mockClipboardEmpty } from '../../generic/data/api.mock'; import { mockContentSearchConfig, mockSearchResult } from '../../search-manager/data/api.mock'; +import { studioHomeMock } from '../../studio-home/__mocks__'; +import { getStudioHomeApiUrl } from '../../studio-home/data/api'; import LibraryLayout from '../LibraryLayout'; mockContentSearchConfig.applyMock(); @@ -46,8 +48,12 @@ const renderOpts = { }; describe('AddContentWorkflow test', () => { + beforeEach(() => { + const { axiosMock } = initializeMocks(); + axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock); + }); + it('can create an HTML component', async () => { - initializeMocks(); render(, renderOpts); // Click "New [Component]" @@ -84,7 +90,6 @@ describe('AddContentWorkflow test', () => { }); it('can create a Problem component', async () => { - initializeMocks(); render(, renderOpts); // Click "New [Component]" @@ -119,7 +124,6 @@ describe('AddContentWorkflow test', () => { }); it('can create a Video component', async () => { - initializeMocks(); render(, renderOpts); // Click "New [Component]" diff --git a/src/library-authoring/add-content/PickLibraryContentModal.test.tsx b/src/library-authoring/add-content/PickLibraryContentModal.test.tsx index d59baa5692..b7425a7eeb 100644 --- a/src/library-authoring/add-content/PickLibraryContentModal.test.tsx +++ b/src/library-authoring/add-content/PickLibraryContentModal.test.tsx @@ -6,6 +6,8 @@ import { screen, initializeMocks, } from '../../testUtils'; +import { studioHomeMock } from '../../studio-home/__mocks__'; +import { getStudioHomeApiUrl } from '../../studio-home/data/api'; import mockResult from '../__mocks__/library-search.json'; import { LibraryProvider } from '../common/context'; import { ComponentPickerModal } from '../component-picker'; @@ -16,7 +18,6 @@ import { } from '../data/api.mocks'; import { PickLibraryContentModal } from './PickLibraryContentModal'; -initializeMocks(); mockContentSearchConfig.applyMock(); mockContentLibrary.applyMock(); mockGetCollectionMetadata.applyMock(); @@ -45,6 +46,7 @@ describe('', () => { beforeEach(() => { const mocks = initializeMocks(); mockShowToast = mocks.mockShowToast; + mocks.axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock); }); it('can pick components from the modal', async () => { diff --git a/src/library-authoring/component-picker/ComponentPicker.test.tsx b/src/library-authoring/component-picker/ComponentPicker.test.tsx index 4c06a9846e..083d9a6358 100644 --- a/src/library-authoring/component-picker/ComponentPicker.test.tsx +++ b/src/library-authoring/component-picker/ComponentPicker.test.tsx @@ -27,6 +27,13 @@ jest.mock('react-router-dom', () => ({ }, }), })); +jest.mock('../../studio-home/hooks', () => ({ + useStudioHome: () => ({ + isLoadingPage: false, + isFailedLoadingPage: false, + librariesV2Enabled: true, + }), +})); mockContentLibrary.applyMock(); mockContentSearchConfig.applyMock(); mockGetCollectionMetadata.applyMock(); diff --git a/src/library-authoring/messages.ts b/src/library-authoring/messages.ts index 7ae8c2b210..f0ac2cc1d1 100644 --- a/src/library-authoring/messages.ts +++ b/src/library-authoring/messages.ts @@ -111,6 +111,11 @@ const messages = defineMessages({ defaultMessage: 'Change Library', description: 'Breadcrumbs link to return to library selection', }, + librariesV2DisabledError: { + id: 'authoring.alert.error.libraries.v2.disabled', + defaultMessage: 'This page cannot be shown: Libraries v2 are disabled.', + description: 'Error message shown to users when trying to load a libraries V2 page while libraries v2 are disabled.', + }, }); export default messages; diff --git a/src/studio-home/StudioHome.tsx b/src/studio-home/StudioHome.tsx index 56e6b00cf1..aff75fc6bb 100644 --- a/src/studio-home/StudioHome.tsx +++ b/src/studio-home/StudioHome.tsx @@ -47,7 +47,7 @@ const StudioHome = () => { setShowNewCourseContainer, librariesV1Enabled, librariesV2Enabled, - } = useStudioHome(isPaginationCoursesEnabled); + } = useStudioHome(); const v1LibraryTab = librariesV1Enabled && location?.pathname.split('/').pop() === 'libraries-v1'; const showV2LibraryURL = librariesV2Enabled && !v1LibraryTab; diff --git a/src/studio-home/hooks.jsx b/src/studio-home/hooks.jsx index 584d69d842..81762eb1ce 100644 --- a/src/studio-home/hooks.jsx +++ b/src/studio-home/hooks.jsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; +import { getConfig } from '@edx/frontend-platform'; import { RequestStatus } from '../data/constants'; import { COURSE_CREATOR_STATES } from '../constants'; @@ -14,9 +15,10 @@ import { } from './data/selectors'; import { updateSavingStatuses } from './data/slice'; -const useStudioHome = (isPaginated = false) => { +const useStudioHome = () => { const location = useLocation(); const dispatch = useDispatch(); + const isPaginated = getConfig().ENABLE_HOME_PAGE_COURSE_API_V2; const studioHomeData = useSelector(getStudioHomeData); const studioHomeCoursesParams = useSelector(getStudioHomeCoursesParams); const { isFiltered } = studioHomeCoursesParams;