feat: handle unsaved changes in text & problem editors (#1444)
The text & problem xblock editors will display a confirmation box before cancelling only if user has changed something else it will directly go back.
This commit is contained in:
@@ -7,7 +7,7 @@ import {
|
||||
} from '@openedx/paragon';
|
||||
import { EditOutline as EditIcon, MoreVert as MoveVertIcon } from '@openedx/paragon/icons';
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { getCanEdit, getCourseId } from 'CourseAuthoring/course-unit/data/selectors';
|
||||
import DeleteModal from '../../generic/delete-modal/DeleteModal';
|
||||
@@ -19,6 +19,7 @@ import { copyToClipboard } from '../../generic/data/thunks';
|
||||
import { COMPONENT_TYPES } from '../../generic/block-type-utils/constants';
|
||||
import XBlockMessages from './xblock-messages/XBlockMessages';
|
||||
import messages from './messages';
|
||||
import { createCorrectInternalRoute } from '../../utils';
|
||||
|
||||
const CourseXBlock = ({
|
||||
id, title, type, unitXBlockActions, shouldScroll, userPartitionInfo,
|
||||
@@ -28,7 +29,6 @@ const CourseXBlock = ({
|
||||
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useToggle(false);
|
||||
const [isConfigureModalOpen, openConfigureModal, closeConfigureModal] = useToggle(false);
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const canEdit = useSelector(getCanEdit);
|
||||
const courseId = useSelector(getCourseId);
|
||||
const intl = useIntl();
|
||||
@@ -58,7 +58,11 @@ const CourseXBlock = ({
|
||||
case COMPONENT_TYPES.html:
|
||||
case COMPONENT_TYPES.problem:
|
||||
case COMPONENT_TYPES.video:
|
||||
navigate(`/course/${courseId}/editor/${type}/${id}`);
|
||||
// Not using useNavigate from react router to use browser navigation
|
||||
// which allows us to block back button if unsaved changes in editor are present.
|
||||
window.location.assign(
|
||||
createCorrectInternalRoute(`/course/${courseId}/editor/${type}/${id}`),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ const blockId = '567890';
|
||||
const handleDeleteMock = jest.fn();
|
||||
const handleDuplicateMock = jest.fn();
|
||||
const handleConfigureSubmitMock = jest.fn();
|
||||
const mockedUsedNavigate = jest.fn();
|
||||
const {
|
||||
name,
|
||||
block_id: id,
|
||||
@@ -42,11 +41,6 @@ const unitXBlockActionsMock = {
|
||||
handleDuplicate: handleDuplicateMock,
|
||||
};
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useNavigate: () => mockedUsedNavigate,
|
||||
}));
|
||||
|
||||
jest.mock('react-redux', () => ({
|
||||
...jest.requireActual('react-redux'),
|
||||
useSelector: jest.fn(),
|
||||
@@ -78,6 +72,16 @@ useSelector.mockImplementation((selector) => {
|
||||
});
|
||||
|
||||
describe('<CourseXBlock />', () => {
|
||||
const locationTemp = window.location;
|
||||
beforeAll(() => {
|
||||
delete window.location;
|
||||
window.location = {
|
||||
assign: jest.fn(),
|
||||
};
|
||||
});
|
||||
afterAll(() => {
|
||||
window.location = locationTemp;
|
||||
});
|
||||
beforeEach(async () => {
|
||||
initializeMockApp({
|
||||
authenticatedUser: {
|
||||
@@ -168,8 +172,8 @@ describe('<CourseXBlock />', () => {
|
||||
expect(editButton).toBeInTheDocument();
|
||||
|
||||
userEvent.click(editButton);
|
||||
expect(mockedUsedNavigate).toHaveBeenCalled();
|
||||
expect(mockedUsedNavigate).toHaveBeenCalledWith(`/course/${courseId}/editor/html/${id}`);
|
||||
expect(window.location.assign).toHaveBeenCalled();
|
||||
expect(window.location.assign).toHaveBeenCalledWith(`/course/${courseId}/editor/html/${id}`);
|
||||
});
|
||||
|
||||
it('navigates to editor page on edit Video xblock', () => {
|
||||
@@ -182,8 +186,8 @@ describe('<CourseXBlock />', () => {
|
||||
expect(editButton).toBeInTheDocument();
|
||||
|
||||
userEvent.click(editButton);
|
||||
expect(mockedUsedNavigate).toHaveBeenCalled();
|
||||
expect(mockedUsedNavigate).toHaveBeenCalledWith(`/course/${courseId}/editor/video/${id}`);
|
||||
expect(window.location.assign).toHaveBeenCalled();
|
||||
expect(window.location.assign).toHaveBeenCalledWith(`/course/${courseId}/editor/video/${id}`);
|
||||
});
|
||||
|
||||
it('navigates to editor page on edit Problem xblock', () => {
|
||||
@@ -196,8 +200,8 @@ describe('<CourseXBlock />', () => {
|
||||
expect(editButton).toBeInTheDocument();
|
||||
|
||||
userEvent.click(editButton);
|
||||
expect(mockedUsedNavigate).toHaveBeenCalled();
|
||||
expect(mockedUsedNavigate).toHaveBeenCalledWith(`/course/${courseId}/editor/problem/${id}`);
|
||||
expect(window.location.assign).toHaveBeenCalled();
|
||||
expect(window.location.assign).toHaveBeenCalledWith(`/course/${courseId}/editor/problem/${id}`);
|
||||
expect(handleDeleteMock).toHaveBeenCalledWith(id);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user