Compare commits
11 Commits
test_hyper
...
open-relea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
292931f2cb | ||
|
|
a580b8980a | ||
|
|
4592c0d06a | ||
|
|
91922874fc | ||
|
|
0020597d97 | ||
|
|
74ce163bec | ||
|
|
2b8edfd761 | ||
|
|
d0c4765698 | ||
|
|
d48d6ffa5d | ||
|
|
14f035435c | ||
|
|
fa25150e3d |
@@ -4,7 +4,7 @@
|
||||
<title>Course Authoring | <%= process.env.SITE_NAME %></title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="<%= process.env.FAVICON_URL %>" type="image/x-icon" />
|
||||
<link rel="shortcut icon" href="<%=htmlWebpackPlugin.options.FAVICON_URL%>" type="image/x-icon" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -71,7 +71,7 @@ const SettingCard = ({
|
||||
iconAs={Icon}
|
||||
alt={intl.formatMessage(messages.helpButtonText)}
|
||||
variant="primary"
|
||||
className=" ml-1 mr-2"
|
||||
className="flex-shrink-0 ml-1 mr-2"
|
||||
/>
|
||||
<ModalPopup
|
||||
hasArrow
|
||||
|
||||
@@ -41,6 +41,7 @@ import messages from './messages';
|
||||
import CustomPagesProvider from './CustomPagesProvider';
|
||||
import EditModal from './EditModal';
|
||||
import getPageHeadTitle from '../generic/utils';
|
||||
import { getPagePath } from '../utils';
|
||||
|
||||
const CustomPages = ({
|
||||
courseId,
|
||||
@@ -121,7 +122,7 @@ const CustomPages = ({
|
||||
ariaLabel="Custom Page breadcrumbs"
|
||||
links={[
|
||||
{ label: 'Content', href: `${config.STUDIO_BASE_URL}/course/${courseId}` },
|
||||
{ label: 'Pages and Resources', href: `/course/${courseId}/pages-and-resources` },
|
||||
{ label: 'Pages and Resources', href: getPagePath(courseId, 'true', 'tabs') },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
@import "./export-stepper/ExportStepper";
|
||||
@import "./export-footer/ExportFooter";
|
||||
|
||||
.export {
|
||||
|
||||
@@ -22,6 +22,21 @@ import {
|
||||
updateSavingStatus,
|
||||
} from './slice';
|
||||
|
||||
function setExportDate({
|
||||
date, exportStatus, exportOutput, dispatch,
|
||||
}) {
|
||||
// If there is no cookie for the last export date, set it now.
|
||||
const cookies = new Cookies();
|
||||
const cookieData = cookies.get(LAST_EXPORT_COOKIE_NAME);
|
||||
if (!cookieData?.completed) {
|
||||
setExportCookie(date, exportStatus === EXPORT_STAGES.SUCCESS);
|
||||
}
|
||||
// If we don't have export date set yet via cookie, set success date to current date.
|
||||
if (exportOutput && !cookieData?.completed) {
|
||||
dispatch(updateSuccessDate(date));
|
||||
}
|
||||
}
|
||||
|
||||
export function startExportingCourse(courseId) {
|
||||
return async (dispatch) => {
|
||||
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
|
||||
@@ -45,29 +60,30 @@ export function fetchExportStatus(courseId) {
|
||||
return async (dispatch) => {
|
||||
dispatch(updateLoadingStatus({ status: RequestStatus.IN_PROGRESS }));
|
||||
try {
|
||||
const { exportStatus, exportOutput, exportError } = await getExportStatus(courseId);
|
||||
const {
|
||||
exportStatus, exportOutput, exportError,
|
||||
} = await getExportStatus(courseId);
|
||||
dispatch(updateCurrentStage(Math.abs(exportStatus)));
|
||||
|
||||
const date = moment().valueOf();
|
||||
|
||||
setExportDate({
|
||||
date, exportStatus, exportOutput, dispatch,
|
||||
});
|
||||
|
||||
if (exportError) {
|
||||
const errorMessage = exportError.rawErrorMsg || exportError;
|
||||
const errorUnitUrl = exportError.editUnitUrl || null;
|
||||
dispatch(updateError({ msg: errorMessage, unitUrl: errorUnitUrl }));
|
||||
dispatch(updateIsErrorModalOpen(true));
|
||||
}
|
||||
|
||||
if (exportOutput) {
|
||||
if (exportOutput.startsWith('/')) {
|
||||
dispatch(updateDownloadPath(`${getConfig().STUDIO_BASE_URL}${exportOutput}`));
|
||||
} else {
|
||||
dispatch(updateDownloadPath(exportOutput));
|
||||
}
|
||||
dispatch(updateSuccessDate(moment().valueOf()));
|
||||
}
|
||||
|
||||
const cookies = new Cookies();
|
||||
const cookieData = cookies.get(LAST_EXPORT_COOKIE_NAME);
|
||||
if (!cookieData?.completed) {
|
||||
setExportCookie(moment().valueOf(), exportStatus === EXPORT_STAGES.SUCCESS);
|
||||
}
|
||||
|
||||
if (exportError) {
|
||||
const errorMessage = exportError.rawErrorMsg || exportError;
|
||||
const errorUnitUrl = exportError.editUnitUrl || null;
|
||||
dispatch(updateError({ msg: errorMessage, unitUrl: errorUnitUrl }));
|
||||
dispatch(updateIsErrorModalOpen(true));
|
||||
}
|
||||
|
||||
dispatch(updateLoadingStatus({ status: RequestStatus.SUCCESSFUL }));
|
||||
|
||||
146
src/export-page/data/thunks.test.js
Normal file
146
src/export-page/data/thunks.test.js
Normal file
@@ -0,0 +1,146 @@
|
||||
import Cookies from 'universal-cookie';
|
||||
import { fetchExportStatus } from './thunks';
|
||||
import * as api from './api';
|
||||
import { EXPORT_STAGES } from './constants';
|
||||
|
||||
jest.mock('universal-cookie', () => jest.fn().mockImplementation(() => ({
|
||||
get: jest.fn().mockImplementation(() => ({ completed: false })),
|
||||
})));
|
||||
|
||||
jest.mock('../utils', () => ({
|
||||
setExportCookie: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('fetchExportStatus thunk', () => {
|
||||
const dispatch = jest.fn();
|
||||
const getState = jest.fn();
|
||||
const courseId = 'course-123';
|
||||
const exportStatus = EXPORT_STAGES.COMPRESSING;
|
||||
const exportOutput = 'export output';
|
||||
const exportError = 'export error';
|
||||
let mockGetExportStatus;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
mockGetExportStatus = jest.spyOn(api, 'getExportStatus').mockResolvedValue({
|
||||
exportStatus,
|
||||
exportOutput,
|
||||
exportError,
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch updateCurrentStage with export status', async () => {
|
||||
mockGetExportStatus.mockResolvedValue({
|
||||
exportStatus,
|
||||
exportOutput,
|
||||
exportError,
|
||||
});
|
||||
|
||||
await fetchExportStatus(courseId)(dispatch, getState);
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
payload: exportStatus,
|
||||
type: 'exportPage/updateCurrentStage',
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch updateError on export error', async () => {
|
||||
mockGetExportStatus.mockResolvedValue({
|
||||
exportStatus,
|
||||
exportOutput,
|
||||
exportError,
|
||||
});
|
||||
|
||||
await fetchExportStatus(courseId)(dispatch, getState);
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
payload: {
|
||||
msg: exportError,
|
||||
unitUrl: null,
|
||||
},
|
||||
type: 'exportPage/updateError',
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch updateIsErrorModalOpen with true if export error', async () => {
|
||||
mockGetExportStatus.mockResolvedValue({
|
||||
exportStatus,
|
||||
exportOutput,
|
||||
exportError,
|
||||
});
|
||||
|
||||
await fetchExportStatus(courseId)(dispatch, getState);
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
payload: true,
|
||||
type: 'exportPage/updateIsErrorModalOpen',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not dispatch updateIsErrorModalOpen if no export error', async () => {
|
||||
mockGetExportStatus.mockResolvedValue({
|
||||
exportStatus,
|
||||
exportOutput,
|
||||
exportError: null,
|
||||
});
|
||||
|
||||
await fetchExportStatus(courseId)(dispatch, getState);
|
||||
|
||||
expect(dispatch).not.toHaveBeenCalledWith({
|
||||
payload: false,
|
||||
type: 'exportPage/updateIsErrorModalOpen',
|
||||
});
|
||||
});
|
||||
|
||||
it("should dispatch updateDownloadPath if there's export output", async () => {
|
||||
mockGetExportStatus.mockResolvedValue({
|
||||
exportStatus,
|
||||
exportOutput,
|
||||
exportError,
|
||||
});
|
||||
|
||||
await fetchExportStatus(courseId)(dispatch, getState);
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
payload: exportOutput,
|
||||
type: 'exportPage/updateDownloadPath',
|
||||
});
|
||||
});
|
||||
|
||||
it('should dispatch updateSuccessDate with current date if export status is success', async () => {
|
||||
mockGetExportStatus.mockResolvedValue({
|
||||
exportStatus:
|
||||
EXPORT_STAGES.SUCCESS,
|
||||
exportOutput,
|
||||
exportError,
|
||||
});
|
||||
|
||||
await fetchExportStatus(courseId)(dispatch, getState);
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith({
|
||||
payload: expect.any(Number),
|
||||
type: 'exportPage/updateSuccessDate',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not dispatch updateSuccessDate with current date if last-export cookie is already set', async () => {
|
||||
mockGetExportStatus.mockResolvedValue({
|
||||
exportStatus:
|
||||
EXPORT_STAGES.SUCCESS,
|
||||
exportOutput,
|
||||
exportError,
|
||||
});
|
||||
|
||||
Cookies.mockImplementation(() => ({
|
||||
get: jest.fn().mockReturnValueOnce({ completed: true }),
|
||||
}));
|
||||
|
||||
await fetchExportStatus(courseId)(dispatch, getState);
|
||||
|
||||
expect(dispatch).not.toHaveBeenCalledWith({
|
||||
payload: expect.any,
|
||||
type: 'exportPage/updateSuccessDate',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
.pgn__stepper-header-step-list {
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -153,7 +153,7 @@ describe('<CreateOrRerunCourseForm />', () => {
|
||||
userEvent.type(runInput, '1');
|
||||
userEvent.click(createBtn);
|
||||
});
|
||||
await axiosMock.onPost(getCreateOrRerunCourseUrl).reply(200, { url });
|
||||
await axiosMock.onPost(getCreateOrRerunCourseUrl()).reply(200, { url });
|
||||
await executeThunk(updateCreateOrRerunCourseQuery({ org: 'testX', run: 'some' }), store.dispatch);
|
||||
|
||||
expect(window.location.assign).toHaveBeenCalledWith(`${process.env.STUDIO_BASE_URL}${url}`);
|
||||
@@ -168,7 +168,7 @@ describe('<CreateOrRerunCourseForm />', () => {
|
||||
const numberInput = screen.getByPlaceholderText(messages.courseNumberPlaceholder.defaultMessage);
|
||||
const runInput = screen.getByPlaceholderText(messages.courseRunPlaceholder.defaultMessage);
|
||||
const createBtn = screen.getByRole('button', { name: messages.createButton.defaultMessage });
|
||||
await axiosMock.onPost(getCreateOrRerunCourseUrl).reply(200, { url, destinationCourseKey });
|
||||
await axiosMock.onPost(getCreateOrRerunCourseUrl()).reply(200, { url, destinationCourseKey });
|
||||
|
||||
await act(async () => {
|
||||
userEvent.type(displayNameInput, 'foo course name');
|
||||
@@ -250,7 +250,7 @@ describe('<CreateOrRerunCourseForm />', () => {
|
||||
it('shows alert error if postErrors presents', async () => {
|
||||
render(<RootWrapper {...props} />);
|
||||
await mockStore();
|
||||
await axiosMock.onPost(getCreateOrRerunCourseUrl).reply(200, { errMsg: 'aaa' });
|
||||
await axiosMock.onPost(getCreateOrRerunCourseUrl()).reply(200, { errMsg: 'aaa' });
|
||||
await executeThunk(updateCreateOrRerunCourseQuery({ org: 'testX', run: 'some' }), store.dispatch);
|
||||
|
||||
expect(screen.getByText('aaa')).toBeInTheDocument();
|
||||
|
||||
@@ -4,9 +4,9 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { convertObjectToSnakeCase } from '../../utils';
|
||||
|
||||
export const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL;
|
||||
export const getCreateOrRerunCourseUrl = new URL('course/', getApiBaseUrl()).href;
|
||||
export const getCreateOrRerunCourseUrl = () => new URL('course/', getApiBaseUrl()).href;
|
||||
export const getCourseRerunUrl = (courseId) => new URL(`/api/contentstore/v1/course_rerun/${courseId}`, getApiBaseUrl()).href;
|
||||
export const getOrganizationsUrl = new URL('organizations', getApiBaseUrl()).href;
|
||||
export const getOrganizationsUrl = () => new URL('organizations', getApiBaseUrl()).href;
|
||||
|
||||
/**
|
||||
* Get's organizations data.
|
||||
@@ -14,7 +14,7 @@ export const getOrganizationsUrl = new URL('organizations', getApiBaseUrl()).hre
|
||||
*/
|
||||
export async function getOrganizations() {
|
||||
const { data } = await getAuthenticatedHttpClient().get(
|
||||
getOrganizationsUrl,
|
||||
getOrganizationsUrl(),
|
||||
);
|
||||
return camelCaseObject(data);
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export async function getCourseRerun(courseId) {
|
||||
*/
|
||||
export async function createOrRerunCourse(courseData) {
|
||||
const { data } = await getAuthenticatedHttpClient().post(
|
||||
getCreateOrRerunCourseUrl,
|
||||
getCreateOrRerunCourseUrl(),
|
||||
convertObjectToSnakeCase(courseData, true),
|
||||
);
|
||||
return camelCaseObject(data);
|
||||
|
||||
@@ -66,10 +66,10 @@ describe('generic api calls', () => {
|
||||
org: 'edX',
|
||||
run: 'Demo_Course',
|
||||
};
|
||||
axiosMock.onPost(getCreateOrRerunCourseUrl).reply(200, courseRerunData);
|
||||
axiosMock.onPost(getCreateOrRerunCourseUrl()).reply(200, courseRerunData);
|
||||
const result = await createOrRerunCourse(courseRerunData);
|
||||
|
||||
expect(axiosMock.history.post[0].url).toEqual(getCreateOrRerunCourseUrl);
|
||||
expect(axiosMock.history.post[0].url).toEqual(getCreateOrRerunCourseUrl());
|
||||
expect(result).toEqual(courseRerunData);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,10 +62,10 @@ describe('<AssignmentSection />', () => {
|
||||
it('checking correct assignment weight of total grade value', async () => {
|
||||
const { getByTestId } = render(<RootWrapper setGradingData={setGradingData} />);
|
||||
await waitFor(() => {
|
||||
const assignmentShortLabelInput = getByTestId('assignment-weight-input');
|
||||
expect(assignmentShortLabelInput.value).toBe('100');
|
||||
fireEvent.change(assignmentShortLabelInput, { target: { value: '123' } });
|
||||
expect(testObj.graders[0].weight).toBe('123');
|
||||
const assignmentWeightInput = getByTestId('assignment-weight-input');
|
||||
expect(assignmentWeightInput.value).toBe('100');
|
||||
fireEvent.change(assignmentWeightInput, { target: { value: '123' } });
|
||||
expect(testObj.graders[0].weight).toBe(123);
|
||||
});
|
||||
});
|
||||
it('checking correct assignment total number value', async () => {
|
||||
@@ -74,7 +74,7 @@ describe('<AssignmentSection />', () => {
|
||||
const assignmentTotalNumberInput = getByTestId('assignment-minCount-input');
|
||||
expect(assignmentTotalNumberInput.value).toBe('1');
|
||||
fireEvent.change(assignmentTotalNumberInput, { target: { value: '123' } });
|
||||
expect(testObj.graders[0].minCount).toBe('123');
|
||||
expect(testObj.graders[0].minCount).toBe(123);
|
||||
});
|
||||
});
|
||||
it('checking correct assignment number of droppable value', async () => {
|
||||
@@ -83,7 +83,7 @@ describe('<AssignmentSection />', () => {
|
||||
const assignmentNumberOfDroppableInput = getByTestId('assignment-dropCount-input');
|
||||
expect(assignmentNumberOfDroppableInput.value).toBe('1');
|
||||
fireEvent.change(assignmentNumberOfDroppableInput, { target: { value: '2' } });
|
||||
expect(testObj.graders[0].dropCount).toBe('2');
|
||||
expect(testObj.graders[0].dropCount).toBe(2);
|
||||
});
|
||||
});
|
||||
it('checking correct error msg if dropCount have negative number or empty string', async () => {
|
||||
@@ -100,20 +100,20 @@ describe('<AssignmentSection />', () => {
|
||||
it('checking correct error msg if minCount have negative number or empty string', async () => {
|
||||
const { getByText, getByTestId } = render(<RootWrapper />);
|
||||
await waitFor(() => {
|
||||
const assignmentNumberOfDroppableInput = getByTestId('assignment-minCount-input');
|
||||
expect(assignmentNumberOfDroppableInput.value).toBe('1');
|
||||
fireEvent.change(assignmentNumberOfDroppableInput, { target: { value: '-2' } });
|
||||
const assignmentMinCountInput = getByTestId('assignment-minCount-input');
|
||||
expect(assignmentMinCountInput.value).toBe('1');
|
||||
fireEvent.change(assignmentMinCountInput, { target: { value: '-2' } });
|
||||
expect(getByText(messages.totalNumberErrorMessage.defaultMessage)).toBeInTheDocument();
|
||||
fireEvent.change(assignmentNumberOfDroppableInput, { target: { value: '' } });
|
||||
fireEvent.change(assignmentMinCountInput, { target: { value: '' } });
|
||||
expect(getByText(messages.totalNumberErrorMessage.defaultMessage)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
it('checking correct error msg if total weight have negative number', async () => {
|
||||
const { getByText, getByTestId } = render(<RootWrapper />);
|
||||
await waitFor(() => {
|
||||
const assignmentNumberOfDroppableInput = getByTestId('assignment-weight-input');
|
||||
expect(assignmentNumberOfDroppableInput.value).toBe('100');
|
||||
fireEvent.change(assignmentNumberOfDroppableInput, { target: { value: '-100' } });
|
||||
const assignmentWeightInput = getByTestId('assignment-weight-input');
|
||||
expect(assignmentWeightInput.value).toBe('100');
|
||||
fireEvent.change(assignmentWeightInput, { target: { value: '-100' } });
|
||||
expect(getByText(messages.weightOfTotalGradeErrorMessage.defaultMessage)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,7 +34,12 @@ const AssignmentSection = ({
|
||||
}
|
||||
|
||||
const handleAssignmentChange = (e, assignmentId) => {
|
||||
const { name, value } = e.target;
|
||||
const { name, value, type: inputType } = e.target;
|
||||
|
||||
let inputValue = value;
|
||||
if (inputType === 'number') {
|
||||
inputValue = parseInt(value, 10);
|
||||
}
|
||||
|
||||
setShowSavePrompt(true);
|
||||
|
||||
@@ -42,7 +47,7 @@ const AssignmentSection = ({
|
||||
...prevState,
|
||||
graders: graders.map(grader => {
|
||||
if (grader.id === assignmentId) {
|
||||
return { ...grader, [name]: value };
|
||||
return { ...grader, [name]: inputValue };
|
||||
}
|
||||
return grader;
|
||||
}),
|
||||
@@ -66,7 +71,7 @@ const AssignmentSection = ({
|
||||
return (
|
||||
<div className="assignment-items">
|
||||
{graders?.map((gradeField) => {
|
||||
const courseAssignmentUsage = courseAssignmentLists[gradeField.type.toLowerCase()];
|
||||
const courseAssignmentUsage = courseAssignmentLists[gradeField.type];
|
||||
const showDefinedCaseAlert = gradeField.minCount !== courseAssignmentUsage?.length
|
||||
&& Boolean(courseAssignmentUsage?.length);
|
||||
const showNotDefinedCaseAlert = !courseAssignmentUsage?.length && Boolean(gradeField.type);
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
// This SCSS was partly copied from edx/frontend-app-support-tools/src/support-header/index.scss.
|
||||
$spacer: 1rem;
|
||||
$white: #FFFFFF;
|
||||
|
||||
.btn-tertiary:hover {
|
||||
color: white;
|
||||
background-color: #00262B;
|
||||
}
|
||||
|
||||
.course-title-lockup {
|
||||
@media only screen and (max-width: 768px) {
|
||||
padding-left: .5rem;
|
||||
max-width: 70%;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 769px) {
|
||||
padding: .5rem;
|
||||
padding-right: $spacer;
|
||||
border-right: 1px solid #E5E5E5;
|
||||
min-width: 70%;
|
||||
}
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
span {
|
||||
color: #333333;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.375rem;
|
||||
}
|
||||
}
|
||||
|
||||
.site-header-mobile,
|
||||
.site-header-desktop {
|
||||
position: relative;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.site-header-mobile {
|
||||
img {
|
||||
height: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.site-header-desktop {
|
||||
height: 3.75rem;
|
||||
box-shadow: 0 1px 0 0 rgb(0 0 0 / .1);
|
||||
background: $white;
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
box-sizing: content-box;
|
||||
position: relative;
|
||||
top: -.05em;
|
||||
height: 1.75rem;
|
||||
padding: $spacer 0;
|
||||
margin-right: $spacer;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ export const getSettingMenuItems = ({ studioBaseUrl, courseId, intl }) => ([
|
||||
title: intl.formatMessage(messages['header.links.courseTeam']),
|
||||
},
|
||||
{
|
||||
href: `${studioBaseUrl}/group_configurations/course-v1:${courseId}`,
|
||||
href: `${studioBaseUrl}/group_configurations/${courseId}`,
|
||||
title: intl.formatMessage(messages['header.links.groupConfigurations']),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@import "~@edx/brand/paragon/variables";
|
||||
@import "~@edx/paragon/scss/core/core";
|
||||
@import "~@edx/brand/paragon/overrides";
|
||||
@import "header/header";
|
||||
@import "~@edx/frontend-component-header/dist/index";
|
||||
@import "assets/scss/variables";
|
||||
@import "assets/scss/form";
|
||||
@import "assets/scss/utilities";
|
||||
|
||||
@@ -42,7 +42,6 @@ const StudioHome = ({ intl }) => {
|
||||
studioRequestEmail,
|
||||
libraryAuthoringMfeUrl,
|
||||
redirectToLibraryAuthoringMfe,
|
||||
splitStudioHome,
|
||||
} = studioHomeData;
|
||||
|
||||
function getHeaderButtons() {
|
||||
@@ -68,26 +67,24 @@ const StudioHome = ({ intl }) => {
|
||||
);
|
||||
}
|
||||
|
||||
let libraryHref = `${getConfig().STUDIO_BASE_URL}/home#libraries-tab`;
|
||||
if (splitStudioHome) {
|
||||
libraryHref = `${getConfig().STUDIO_BASE_URL}/home_library`;
|
||||
}
|
||||
let libraryHref = `${getConfig().STUDIO_BASE_URL}/home_library`;
|
||||
if (redirectToLibraryAuthoringMfe) {
|
||||
libraryHref = `${libraryAuthoringMfeUrl}/create`;
|
||||
headerButtons.push(
|
||||
<Button
|
||||
variant="outline-primary"
|
||||
iconBefore={AddIcon}
|
||||
size="sm"
|
||||
disabled={showNewCourseContainer}
|
||||
href={libraryHref}
|
||||
data-testid="new-library-button"
|
||||
>
|
||||
{intl.formatMessage(messages.addNewLibraryBtnText)}
|
||||
</Button>,
|
||||
);
|
||||
}
|
||||
|
||||
headerButtons.push(
|
||||
<Button
|
||||
variant="outline-primary"
|
||||
iconBefore={AddIcon}
|
||||
size="sm"
|
||||
disabled={showNewCourseContainer}
|
||||
href={libraryHref}
|
||||
data-testid="new-library-button"
|
||||
>
|
||||
{intl.formatMessage(messages.addNewLibraryBtnText)}
|
||||
</Button>,
|
||||
);
|
||||
|
||||
return headerButtons;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,15 +116,16 @@ describe('<StudioHome />', async () => {
|
||||
});
|
||||
|
||||
describe('render new library button', () => {
|
||||
it('should not show button', async () => {
|
||||
it('href should include home_library', async () => {
|
||||
useSelector.mockReturnValue({
|
||||
...studioHomeMock,
|
||||
courseCreatorStatus: COURSE_CREATOR_STATES.granted,
|
||||
});
|
||||
const studioBaseUrl = 'http://localhost:18010';
|
||||
|
||||
const { queryByTestId } = render(<RootWrapper />);
|
||||
const createNewLibraryButton = queryByTestId('new-library-button');
|
||||
expect(createNewLibraryButton).toBeNull();
|
||||
const { getByTestId } = render(<RootWrapper />);
|
||||
const createNewLibraryButton = getByTestId('new-library-button');
|
||||
expect(createNewLibraryButton.getAttribute('href')).toBe(`${studioBaseUrl}/home_library`);
|
||||
});
|
||||
it('href should include create', async () => {
|
||||
useSelector.mockReturnValue({
|
||||
|
||||
@@ -42,7 +42,7 @@ describe('<OrganizationSection />', async () => {
|
||||
});
|
||||
store = initializeStore();
|
||||
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
|
||||
axiosMock.onDelete(getOrganizationsUrl).reply(200);
|
||||
axiosMock.onDelete(getOrganizationsUrl()).reply(200);
|
||||
await executeThunk(fetchOrganizationsQuery(), store.dispatch);
|
||||
useSelector.mockReturnValue(['edX', 'org']);
|
||||
});
|
||||
|
||||
23
src/utils.js
23
src/utils.js
@@ -95,12 +95,31 @@ export function parseArrayOrObjectValues(obj) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a correct inner path depend on config PUBLIC_PATH.
|
||||
* @param {string} checkPath - the internal route path that is validated
|
||||
* @returns {string} - the correct internal route path
|
||||
*/
|
||||
export const createCorrectInternalRoute = (checkPath) => {
|
||||
let basePath = getConfig().PUBLIC_PATH;
|
||||
|
||||
if (basePath.endsWith('/')) {
|
||||
basePath = basePath.slice(0, -1);
|
||||
}
|
||||
|
||||
if (!checkPath.startsWith(basePath)) {
|
||||
return `${basePath}${checkPath}`;
|
||||
}
|
||||
|
||||
return checkPath;
|
||||
};
|
||||
|
||||
export function getPagePath(courseId, isMfePageEnabled, urlParameter) {
|
||||
if (isMfePageEnabled === 'true') {
|
||||
if (urlParameter === 'tabs') {
|
||||
return `${getConfig().BASE_URL}/course/${courseId}/pages-and-resources`;
|
||||
return createCorrectInternalRoute(`/course/${courseId}/pages-and-resources`);
|
||||
}
|
||||
return `${getConfig().BASE_URL}/course/${courseId}/${urlParameter}`;
|
||||
return createCorrectInternalRoute(`/course/${courseId}/${urlParameter}`);
|
||||
}
|
||||
return `${getConfig().STUDIO_BASE_URL}/${urlParameter}/${courseId}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user