From 3662fadad44a610c95f6169760d48df8305cd6a0 Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Thu, 26 Sep 2024 08:38:16 -0700 Subject: [PATCH] feat!: Remove support for the (deprecated) library authoring MFE (#1327) --- src/editors/data/services/cms/api.test.ts | 14 +------ src/editors/data/services/cms/api.ts | 8 ---- src/search-modal/SearchResult.tsx | 10 +---- src/search-modal/SearchUI.test.tsx | 42 +------------------ src/studio-home/StudioHome.test.jsx | 23 +--------- src/studio-home/StudioHome.tsx | 11 +---- src/studio-home/__mocks__/studioHomeMock.js | 2 - src/studio-home/card-item/index.tsx | 2 +- .../factories/mockApiResponses.jsx | 2 - .../tabs-section/TabsSection.test.tsx | 16 ------- src/studio-home/tabs-section/index.tsx | 7 +--- .../tabs-section/libraries-v2-tab/index.tsx | 23 ++-------- src/utils.js | 24 ----------- src/utils.test.js | 29 +------------ 14 files changed, 12 insertions(+), 201 deletions(-) diff --git a/src/editors/data/services/cms/api.test.ts b/src/editors/data/services/cms/api.test.ts index 656fb4db49..d7b553fb96 100644 --- a/src/editors/data/services/cms/api.test.ts +++ b/src/editors/data/services/cms/api.test.ts @@ -69,7 +69,7 @@ describe('cms api', () => { it('should call get with normal accept header for prod', async () => { process.env.NODE_ENV = 'production'; - process.env.MFE_NAME = 'frontend-app-library-authoring'; + process.env.MFE_NAME = 'frontend-app-course-authoring'; // eslint-disable-next-line no-shadow, @typescript-eslint/no-shadow const { apiMethods } = await import('./api'); // eslint-disable-next-line no-shadow, @typescript-eslint/no-shadow @@ -90,18 +90,6 @@ describe('cms api', () => { apiMethods.fetchByUnitId({ blockId, studioEndpointUrl }); expect(getSpy).toHaveBeenCalledWith(urls.blockAncestor({ studioEndpointUrl, blockId }), {}); }); - - it('should call get with special accept header "*/*" for course-authoring', async () => { - process.env.NODE_ENV = 'development'; - process.env.MFE_NAME = 'frontend-app-library-authoring'; - // eslint-disable-next-line no-shadow, @typescript-eslint/no-shadow - const { apiMethods } = await import('./api'); - // eslint-disable-next-line no-shadow, @typescript-eslint/no-shadow - const utils = await import('./utils'); - const getSpy = jest.spyOn(utils, 'get'); - apiMethods.fetchByUnitId({ blockId, studioEndpointUrl }); - expect(getSpy).toHaveBeenCalledWith(urls.blockAncestor({ studioEndpointUrl, blockId }), { headers: { Accept: '*/*' } }); - }); }); }); diff --git a/src/editors/data/services/cms/api.ts b/src/editors/data/services/cms/api.ts index bce827b5ae..673309bb00 100644 --- a/src/editors/data/services/cms/api.ts +++ b/src/editors/data/services/cms/api.ts @@ -7,14 +7,6 @@ import { durationStringFromValue } from '../../../containers/VideoEditor/compone const fetchByUnitIdOptions: AxiosRequestConfig = {}; -// For some reason, the local webpack-dev-server of library-authoring does not accept the normal Accept header. -// This is a workaround only for that specific case; the idea is to only do this locally and only for library-authoring. -if (process.env.NODE_ENV === 'development' && process.env.MFE_NAME === 'frontend-app-library-authoring') { - fetchByUnitIdOptions.headers = { - Accept: '*/*', - }; -} - interface Pagination { start: number; end: number; diff --git a/src/search-modal/SearchResult.tsx b/src/search-modal/SearchResult.tsx index 9ba6ee9516..032d5cda1a 100644 --- a/src/search-modal/SearchResult.tsx +++ b/src/search-modal/SearchResult.tsx @@ -7,14 +7,11 @@ import { Stack, } from '@openedx/paragon'; import { OpenInNew } from '@openedx/paragon/icons'; -import { useSelector } from 'react-redux'; import { useNavigate } from 'react-router-dom'; import { getItemIcon } from '../generic/block-type-utils'; import { isLibraryKey } from '../generic/key-utils'; import { useSearchContext, type ContentHit, Highlight } from '../search-manager'; -import { getStudioHomeData } from '../studio-home/data/selectors'; -import { constructLibraryAuthoringURL } from '../utils'; import messages from './messages'; /** @@ -100,7 +97,6 @@ const SearchResult: React.FC<{ hit: ContentHit }> = ({ hit }) => { const intl = useIntl(); const navigate = useNavigate(); const { closeSearchModal } = useSearchContext(); - const { libraryAuthoringMfeUrl, redirectToLibraryAuthoringMfe } = useSelector(getStudioHomeData); /** * Returns the URL for the context of the hit @@ -119,10 +115,6 @@ const SearchResult: React.FC<{ hit: ContentHit }> = ({ hit }) => { if (isLibraryKey(contextKey)) { const urlSuffix = getLibraryComponentUrlSuffix(hit); - if (redirectToLibraryAuthoringMfe && libraryAuthoringMfeUrl) { - return constructLibraryAuthoringURL(libraryAuthoringMfeUrl, urlSuffix); - } - if (newWindow) { return `${getPath(getConfig().PUBLIC_PATH)}${urlSuffix}`; } @@ -131,7 +123,7 @@ const SearchResult: React.FC<{ hit: ContentHit }> = ({ hit }) => { // istanbul ignore next - This case should never be reached return undefined; - }, [libraryAuthoringMfeUrl, redirectToLibraryAuthoringMfe, hit]); + }, [hit]); /** * Opens the context of the hit in a new window diff --git a/src/search-modal/SearchUI.test.tsx b/src/search-modal/SearchUI.test.tsx index acb3b5efe3..5ed9b802d7 100644 --- a/src/search-modal/SearchUI.test.tsx +++ b/src/search-modal/SearchUI.test.tsx @@ -16,10 +16,6 @@ import fetchMock from 'fetch-mock-jest'; import type { Store } from 'redux'; import initializeStore from '../store'; -import { executeThunk } from '../utils'; -import { getStudioHomeApiUrl } from '../studio-home/data/api'; -import { fetchStudioHomeData } from '../studio-home/data/thunks'; -import { generateGetStudioHomeDataApiResponse } from '../studio-home/factories/mockApiResponses'; import mockResult from './__mocks__/search-result.json'; import mockEmptyResult from './__mocks__/empty-search-result.json'; import mockTagsFacetResult from './__mocks__/facet-search.json'; @@ -316,43 +312,7 @@ describe('', () => { ); }); - test('click lib component result navigates to the context', async () => { - const data = generateGetStudioHomeDataApiResponse(); - data.redirectToLibraryAuthoringMfe = true; - axiosMock.onGet(getStudioHomeApiUrl()).reply(200, data); - - await executeThunk(fetchStudioHomeData(), store.dispatch); - - const { findByRole } = rendered; - - const resultItem = await findByRole('button', { name: /Library Content/ }); - - // Clicking the "Open in new window" button should open the result in a new window: - const { open, location } = window; - window.open = jest.fn(); - fireEvent.click(within(resultItem).getByRole('button', { name: 'Open in new window' })); - expect(window.open).toHaveBeenCalledWith( - 'http://localhost:3001/library/lib:org1:libafter1', - '_blank', - ); - window.open = open; - - // @ts-ignore - window.location = { href: '' }; - // Clicking in the result should navigate to the result's URL: - fireEvent.click(resultItem); - expect(window.location.href = 'http://localhost:3001/library/lib:org1:libafter1'); - window.location = location; - }); - - test('click lib component result navigates to course-authoring/library without libraryAuthoringMfe', async () => { - const data = generateGetStudioHomeDataApiResponse(); - data.redirectToLibraryAuthoringMfe = false; - data.libraryAuthoringMfeUrl = ''; - axiosMock.onGet(getStudioHomeApiUrl()).reply(200, data); - - await executeThunk(fetchStudioHomeData(), store.dispatch); - + test('click lib component result navigates to course-authoring/library', async () => { const { findByRole } = rendered; const resultItem = await findByRole('button', { name: /Library Content/ }); diff --git a/src/studio-home/StudioHome.test.jsx b/src/studio-home/StudioHome.test.jsx index c130d63195..08d4789479 100644 --- a/src/studio-home/StudioHome.test.jsx +++ b/src/studio-home/StudioHome.test.jsx @@ -14,7 +14,7 @@ import MockAdapter from 'axios-mock-adapter'; import initializeStore from '../store'; import { RequestStatus } from '../data/constants'; import { COURSE_CREATOR_STATES } from '../constants'; -import { executeThunk, constructLibraryAuthoringURL } from '../utils'; +import { executeThunk } from '../utils'; import { studioHomeMock } from './__mocks__'; import { getStudioHomeApiUrl } from './data/api'; import { fetchStudioHomeData } from './data/thunks'; @@ -193,27 +193,6 @@ describe('', () => { window.open = open; }); - it('should navigate to the library authoring mfe', () => { - useSelector.mockReturnValue({ - ...studioHomeMock, - courseCreatorStatus: COURSE_CREATOR_STATES.granted, - splitStudioHome: true, - redirectToLibraryAuthoringMfe: true, - }); - const libraryAuthoringMfeUrl = 'http://localhost:3001'; - - const { getByTestId } = render(); - const createNewLibraryButton = getByTestId('new-library-button'); - - const { open } = window; - window.open = jest.fn(); - fireEvent.click(createNewLibraryButton); - expect(window.open).toHaveBeenCalledWith( - `${constructLibraryAuthoringURL(libraryAuthoringMfeUrl, 'create')}`, - ); - window.open = open; - }); - it('should navigate to the library authoring page in course authoring', () => { useSelector.mockReturnValue({ ...studioHomeMock, diff --git a/src/studio-home/StudioHome.tsx b/src/studio-home/StudioHome.tsx index 9af6ccb2b0..43b8caa703 100644 --- a/src/studio-home/StudioHome.tsx +++ b/src/studio-home/StudioHome.tsx @@ -13,7 +13,6 @@ import { StudioFooter } from '@edx/frontend-component-footer'; import { getConfig } from '@edx/frontend-platform'; import { useLocation, useNavigate } from 'react-router-dom'; -import { constructLibraryAuthoringURL } from '../utils'; import Loading from '../generic/Loading'; import InternetConnectionAlert from '../generic/internet-connection-alert'; import Header from '../header'; @@ -58,8 +57,6 @@ const StudioHome = () => { userIsActive, studioShortName, studioRequestEmail, - libraryAuthoringMfeUrl, - redirectToLibraryAuthoringMfe, showNewLibraryButton, } = studioHomeData; @@ -93,13 +90,7 @@ const StudioHome = () => { if (showNewLibraryButton || showV2LibraryURL) { const newLibraryClick = () => { if (showV2LibraryURL) { - if (libraryAuthoringMfeUrl && redirectToLibraryAuthoringMfe) { - // Library authoring MFE - window.open(constructLibraryAuthoringURL(libraryAuthoringMfeUrl, 'create')); - } else { - // Use course-authoring route - navigate('/library/create'); - } + navigate('/library/create'); } else { // Studio home library for legacy libraries window.open(`${getConfig().STUDIO_BASE_URL}/home_library`); diff --git a/src/studio-home/__mocks__/studioHomeMock.js b/src/studio-home/__mocks__/studioHomeMock.js index 5385201e52..a811c40518 100644 --- a/src/studio-home/__mocks__/studioHomeMock.js +++ b/src/studio-home/__mocks__/studioHomeMock.js @@ -62,9 +62,7 @@ module.exports = { }, ], librariesEnabled: true, - libraryAuthoringMfeUrl: 'http://localhost:3001', optimizationEnabled: false, - redirectToLibraryAuthoringMfe: false, requestCourseCreatorUrl: '/request_course_creator', rerunCreatorStatus: true, showNewLibraryButton: true, diff --git a/src/studio-home/card-item/index.tsx b/src/studio-home/card-item/index.tsx index 35883bb90a..d1bb2921a0 100644 --- a/src/studio-home/card-item/index.tsx +++ b/src/studio-home/card-item/index.tsx @@ -62,7 +62,7 @@ const CardItem: React.FC = ({ } = useSelector(getStudioHomeData); const destinationUrl: string = path ?? new URL(url, getConfig().STUDIO_BASE_URL).toString(); const subtitle = isLibraries ? `${org} / ${number}` : `${org} / ${number} / ${run}`; - const readOnlyItem = !(lmsLink || rerunLink || url); + const readOnlyItem = !(lmsLink || rerunLink || url || path); const showActions = !(readOnlyItem || isLibraries); const isShowRerunLink = allowCourseReruns && rerunCreatorStatus diff --git a/src/studio-home/factories/mockApiResponses.jsx b/src/studio-home/factories/mockApiResponses.jsx index a9993b2cac..7f1d2b4e5f 100644 --- a/src/studio-home/factories/mockApiResponses.jsx +++ b/src/studio-home/factories/mockApiResponses.jsx @@ -32,9 +32,7 @@ export const generateGetStudioHomeDataApiResponse = () => ({ inProcessCourseActions: [], libraries: [], librariesEnabled: true, - libraryAuthoringMfeUrl: 'http://localhost:3001/', optimizationEnabled: false, - redirectToLibraryAuthoringMfe: false, requestCourseCreatorUrl: '/request_course_creator', rerunCreatorStatus: true, showNewLibraryButton: true, diff --git a/src/studio-home/tabs-section/TabsSection.test.tsx b/src/studio-home/tabs-section/TabsSection.test.tsx index 23540af71e..0591f8c7cd 100644 --- a/src/studio-home/tabs-section/TabsSection.test.tsx +++ b/src/studio-home/tabs-section/TabsSection.test.tsx @@ -428,22 +428,6 @@ describe('', () => { expect(screen.queryByText(tabMessages.legacyLibrariesTabTitle.defaultMessage)).toBeNull(); }); - it('should redirect to library authoring mfe', async () => { - const data = generateGetStudioHomeDataApiResponse(); - data.redirectToLibraryAuthoringMfe = true; - - render(); - axiosMock.onGet(getStudioHomeApiUrl()).reply(200, data); - await executeThunk(fetchStudioHomeData(), store.dispatch); - - const librariesTab = screen.getByText(tabMessages.legacyLibrariesTabTitle.defaultMessage); - fireEvent.click(librariesTab); - - waitFor(() => { - expect(window.location.href).toBe(data.libraryAuthoringMfeUrl); - }); - }); - it('should render libraries fetch failure alert', async () => { render(); axiosMock.onGet(getStudioHomeApiUrl()).reply(200, generateGetStudioHomeDataApiResponse()); diff --git a/src/studio-home/tabs-section/index.tsx b/src/studio-home/tabs-section/index.tsx index 44e1e9ea29..ab83009a0e 100644 --- a/src/studio-home/tabs-section/index.tsx +++ b/src/studio-home/tabs-section/index.tsx @@ -63,8 +63,6 @@ const TabsSection = ({ }, [pathname]); const { - libraryAuthoringMfeUrl, - redirectToLibraryAuthoringMfe, courses, librariesEnabled, libraries, archivedCourses, numPages, coursesCount, } = useSelector(getStudioHomeData); @@ -125,10 +123,7 @@ const TabsSection = ({ eventKey={TABS_LIST.libraries} title={intl.formatMessage(messages.librariesTabTitle)} > - + , ); } diff --git a/src/studio-home/tabs-section/libraries-v2-tab/index.tsx b/src/studio-home/tabs-section/libraries-v2-tab/index.tsx index f29fa1d6c1..0ef4e6d772 100644 --- a/src/studio-home/tabs-section/libraries-v2-tab/index.tsx +++ b/src/studio-home/tabs-section/libraries-v2-tab/index.tsx @@ -7,24 +7,18 @@ import { Button, } from '@openedx/paragon'; import { useIntl } from '@edx/frontend-platform/i18n'; -import { getConfig, getPath } from '@edx/frontend-platform'; import { Error } from '@openedx/paragon/icons'; import { useContentLibraryV2List } from '../../../library-authoring'; -import { constructLibraryAuthoringURL } from '../../../utils'; import { LoadingSpinner } from '../../../generic/Loading'; import AlertMessage from '../../../generic/alert-message'; import CardItem from '../../card-item'; import messages from '../messages'; import LibrariesV2Filters from './libraries-v2-filters'; -const LibrariesV2Tab: React.FC<{ - libraryAuthoringMfeUrl: string, - redirectToLibraryAuthoringMfe: boolean -}> = ({ - libraryAuthoringMfeUrl, - redirectToLibraryAuthoringMfe, -}) => { +type Props = Record; + +const LibrariesV2Tab: React.FC = () => { const intl = useIntl(); const [currentPage, setCurrentPage] = useState(1); @@ -55,15 +49,6 @@ const LibrariesV2Tab: React.FC<{ ); } - const libURL = (id: string) => ( - libraryAuthoringMfeUrl && redirectToLibraryAuthoringMfe - ? constructLibraryAuthoringURL(libraryAuthoringMfeUrl, `library/${id}`) - // Redirection to the placeholder is done in the MFE rather than - // through the backend i.e. redirection from cms, because this this will probably change, - // hence why we use the MFE's origin - : `${window.location.origin}${getPath(getConfig().PUBLIC_PATH)}library/${id}` - ); - const hasV2Libraries = !isLoading && ((data!.results.length || 0) > 0); return ( @@ -109,7 +94,7 @@ const LibrariesV2Tab: React.FC<{ displayName={title} org={org} number={slug} - url={libURL(id)} + path={`/library/${id}`} /> )) : isFiltered && !isLoading && ( diff --git a/src/utils.js b/src/utils.js index 2abb63e5be..d4bc8f6ff3 100644 --- a/src/utils.js +++ b/src/utils.js @@ -301,27 +301,3 @@ export const getFileSizeToClosestByte = (fileSize) => { const fileSizeFixedDecimal = Number.parseFloat(size).toFixed(2); return `${fileSizeFixedDecimal} ${units[divides]}`; }; - -/** - * Constructs library authoring MFE URL with correct slashes - * @param {string} libraryAuthoringMfeUrl - the base library authoring MFE url - * @param {string} path - the library authoring MFE url path - * @returns {string} - the correct internal route path - */ -export const constructLibraryAuthoringURL = (libraryAuthoringMfeUrl, path) => { - // Remove '/' at the beginning of path if any - const trimmedPath = path.startsWith('/') - ? path.slice(1, path.length) - : path; - - let constructedUrl = libraryAuthoringMfeUrl; - // Remove trailing `/` from base if found - if (libraryAuthoringMfeUrl.endsWith('/')) { - constructedUrl = constructedUrl.slice(0, -1); - } - - // Add the `/` and path to url - constructedUrl = `${constructedUrl}/${trimmedPath}`; - - return constructedUrl; -}; diff --git a/src/utils.test.js b/src/utils.test.js index a5b12d6c37..e4aada849f 100644 --- a/src/utils.test.js +++ b/src/utils.test.js @@ -1,6 +1,6 @@ import { getConfig, getPath } from '@edx/frontend-platform'; -import { getFileSizeToClosestByte, createCorrectInternalRoute, constructLibraryAuthoringURL } from './utils'; +import { getFileSizeToClosestByte, createCorrectInternalRoute } from './utils'; jest.mock('@edx/frontend-platform', () => ({ getConfig: jest.fn(), @@ -78,30 +78,3 @@ describe('FilesAndUploads utils', () => { }); }); }); - -describe('constructLibraryAuthoringURL', () => { - it('should construct URL given no trailing `/` in base and no starting `/` in path', () => { - const libraryAuthoringMfeUrl = 'http://localhost:3001'; - const path = 'example'; - const constructedURL = constructLibraryAuthoringURL(libraryAuthoringMfeUrl, path); - expect(constructedURL).toEqual('http://localhost:3001/example'); - }); - it('should construct URL given a trailing `/` in base and no starting `/` in path', () => { - const libraryAuthoringMfeUrl = 'http://localhost:3001/'; - const path = 'example'; - const constructedURL = constructLibraryAuthoringURL(libraryAuthoringMfeUrl, path); - expect(constructedURL).toEqual('http://localhost:3001/example'); - }); - it('should construct URL with no trailing `/` in base and a starting `/` in path', () => { - const libraryAuthoringMfeUrl = 'http://localhost:3001'; - const path = '/example'; - const constructedURL = constructLibraryAuthoringURL(libraryAuthoringMfeUrl, path); - expect(constructedURL).toEqual('http://localhost:3001/example'); - }); - it('should construct URL with a trailing `/` in base and a starting `/` in path', () => { - const libraryAuthoringMfeUrl = 'http://localhost:3001/'; - const path = '/example'; - const constructedURL = constructLibraryAuthoringURL(libraryAuthoringMfeUrl, path); - expect(constructedURL).toEqual('http://localhost:3001/example'); - }); -});