diff --git a/package-lock.json b/package-lock.json
index 6d4708ae2..605c13a07 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3714,14 +3714,15 @@
}
},
"@edx/frontend-lib-content-components": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@edx/frontend-lib-content-components/-/frontend-lib-content-components-1.1.1.tgz",
- "integrity": "sha512-mWfWCKj+eKgWhjgoOIEye/0+kZwWRVxXhC4NwCtgW+NHfFI/rHVPOraE2NOoNH+ml4XDLmVEjz7s93OIyyV8Nw==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@edx/frontend-lib-content-components/-/frontend-lib-content-components-1.2.0.tgz",
+ "integrity": "sha512-I3FY2qxzxNTvs+snltbZ/QIFl+oTNTb9pXUOAZx+ObKEoHjFvgVwpXhjbMOM5ZFGErpUfTDfTzkW0vpyB9854w==",
"requires": {
"@tinymce/tinymce-react": "^3.13.0",
"babel-polyfill": "6.26.0",
"react-responsive": "8.2.0",
- "react-transition-group": "4.4.2"
+ "react-transition-group": "4.4.2",
+ "tinymce": "^5.10.2"
},
"dependencies": {
"react-responsive": {
@@ -24068,9 +24069,9 @@
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
"tinymce": {
- "version": "5.10.2",
- "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-5.10.2.tgz",
- "integrity": "sha512-5QhnZ6c8F28fYucLLc00MM37fZoAZ4g7QCYzwIl38i5TwJR5xGqzOv6YMideyLM4tytCzLCRwJoQen2LI66p5A=="
+ "version": "5.10.3",
+ "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-5.10.3.tgz",
+ "integrity": "sha512-O59ssHNnujWvSk5Gt8hIGrdNCMKVWVQv9F8siAgLTRgTh0t3NDHrP1UlLtCxArUi9DPWZvlBeUz8D5fJTu7vnA=="
},
"tmp": {
"version": "0.0.33",
diff --git a/package.json b/package.json
index bef6051a1..09367f968 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,7 @@
"dependencies": {
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
"@edx/frontend-component-footer": "10.1.6",
- "@edx/frontend-lib-content-components": "1.1.1",
+ "@edx/frontend-lib-content-components": "1.2.0",
"@edx/frontend-platform": "1.14.0",
"@edx/paragon": "16.17.0",
"@fortawesome/fontawesome-svg-core": "1.2.28",
diff --git a/src/CourseAuthoringPage.jsx b/src/CourseAuthoringPage.jsx
index 17a076259..ad1c548df 100644
--- a/src/CourseAuthoringPage.jsx
+++ b/src/CourseAuthoringPage.jsx
@@ -3,6 +3,9 @@ import PropTypes from 'prop-types';
import Footer from '@edx/frontend-component-footer';
import { useDispatch, useSelector } from 'react-redux';
+import {
+ useLocation,
+} from 'react-router-dom';
import Header from './studio-header/Header';
import { fetchCourseDetail } from './data/thunks';
import { useModel } from './generic/model-store';
@@ -46,9 +49,14 @@ export default function CourseAuthoringPage({ courseId, children }) {
);
+ const { pathname } = useLocation();
return (
- {inProgress ?
:
}
+ {/* While V2 Editors are tempoarily served from thier own pages
+ using url pattern containing /editor/,
+ we shouldn't have the header and footer on these pages.
+ This functionality will be removed in TNL-9591 */}
+ {inProgress ? !pathname.includes('/editor/') &&
:
}
{children}
{!inProgress &&
}
diff --git a/src/CourseAuthoringPage.test.jsx b/src/CourseAuthoringPage.test.jsx
index d3953d9e1..d2f74e97f 100644
--- a/src/CourseAuthoringPage.test.jsx
+++ b/src/CourseAuthoringPage.test.jsx
@@ -14,6 +14,13 @@ import { executeThunk } from './utils';
import { fetchCourseApps } from './pages-and-resources/data/thunks';
const courseId = 'course-v1:edX+TestX+Test_Course';
+let mockPathname = '/evilguy/';
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useLocation: () => ({
+ pathname: mockPathname,
+ }),
+}));
let axiosMock;
let store;
let container;
@@ -61,3 +68,56 @@ describe('DiscussionsSettings', () => {
expect(queryByTestId(container, 'permissionDeniedAlert')).toBeInTheDocument();
});
});
+
+describe('Editor Pages Load no header', () => {
+ const mockStoreSuccess = async () => {
+ const apiBaseUrl = getConfig().STUDIO_BASE_URL;
+ const courseAppsApiUrl = `${apiBaseUrl}/api/course_apps/v1/apps`;
+ axiosMock.onGet(`${courseAppsApiUrl}/${courseId}`).reply(200, {
+ response: { status: 200 },
+ });
+ await executeThunk(fetchCourseApps(courseId), store.dispatch);
+ };
+ beforeEach(() => {
+ initializeMockApp({
+ authenticatedUser: {
+ userId: 3,
+ username: 'abc123',
+ administrator: true,
+ roles: [],
+ },
+ });
+ store = initializeStore();
+ axiosMock = new MockAdapter(getAuthenticatedHttpClient());
+ });
+ test('renders no loading wheel on editor pages', async () => {
+ mockPathname = '/editor/';
+ await mockStoreSuccess();
+ const wrapper = render(
+
+
+
+
+
+
+
+ ,
+ );
+ expect(wrapper.queryByRole('status')).not.toBeInTheDocument();
+ });
+ test('renders loading wheel on non editor pages', async () => {
+ mockPathname = '/evilguy/';
+ await mockStoreSuccess();
+ const wrapper = render(
+
+
+
+
+
+
+
+ ,
+ );
+ expect(wrapper.queryByRole('status')).toBeInTheDocument();
+ });
+});
diff --git a/src/setupTest.js b/src/setupTest.js
index 5bffda04c..061bc866c 100755
--- a/src/setupTest.js
+++ b/src/setupTest.js
@@ -4,6 +4,22 @@ import '@testing-library/jest-dom';
import '@testing-library/jest-dom/extend-expect';
import ReactDOM from 'react-dom';
+/* need to mock window for tinymce on import, as it is JSDOM incompatible */
+
+Object.defineProperty(window, 'matchMedia', {
+ writable: true,
+ value: jest.fn().mockImplementation(query => ({
+ matches: false,
+ media: query,
+ onchange: null,
+ addListener: jest.fn(), // Deprecated
+ removeListener: jest.fn(), // Deprecated
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ dispatchEvent: jest.fn(),
+ })),
+});
+
// Modal creates a portal. Overriding ReactDOM.createPortal allows portals to be tested in jest.
ReactDOM.createPortal = node => node;