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:
@@ -1,21 +1,15 @@
|
||||
import React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import PromptIfDirty from './PromptIfDirty';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import usePromptIfDirty from './usePromptIfDirty';
|
||||
|
||||
describe('PromptIfDirty', () => {
|
||||
let container = null;
|
||||
describe('usePromptIfDirty', () => {
|
||||
let mockEvent = null;
|
||||
|
||||
beforeEach(() => {
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
mockEvent = new Event('beforeunload');
|
||||
jest.spyOn(window, 'addEventListener');
|
||||
jest.spyOn(window, 'removeEventListener');
|
||||
jest.spyOn(mockEvent, 'preventDefault');
|
||||
Object.defineProperty(mockEvent, 'returnValue', { writable: true });
|
||||
mockEvent.returnValue = '';
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -23,49 +17,32 @@ describe('PromptIfDirty', () => {
|
||||
window.removeEventListener.mockRestore();
|
||||
mockEvent.preventDefault.mockRestore();
|
||||
mockEvent = null;
|
||||
unmountComponentAtNode(container);
|
||||
container.remove();
|
||||
container = null;
|
||||
});
|
||||
|
||||
it('should add event listener on mount', () => {
|
||||
act(() => {
|
||||
render(<PromptIfDirty dirty />, container);
|
||||
});
|
||||
renderHook(() => usePromptIfDirty(() => true));
|
||||
|
||||
expect(window.addEventListener).toHaveBeenCalledWith('beforeunload', expect.any(Function));
|
||||
});
|
||||
|
||||
it('should remove event listener on unmount', () => {
|
||||
act(() => {
|
||||
render(<PromptIfDirty dirty />, container);
|
||||
});
|
||||
act(() => {
|
||||
unmountComponentAtNode(container);
|
||||
});
|
||||
const { unmount } = renderHook(() => usePromptIfDirty(() => true));
|
||||
unmount();
|
||||
|
||||
expect(window.removeEventListener).toHaveBeenCalledWith('beforeunload', expect.any(Function));
|
||||
});
|
||||
|
||||
it('should call preventDefault and set returnValue when dirty is true', () => {
|
||||
act(() => {
|
||||
render(<PromptIfDirty dirty />, container);
|
||||
});
|
||||
act(() => {
|
||||
window.dispatchEvent(mockEvent);
|
||||
});
|
||||
renderHook(() => usePromptIfDirty(() => true));
|
||||
window.dispatchEvent(mockEvent);
|
||||
|
||||
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
||||
expect(mockEvent.returnValue).toBe('');
|
||||
expect(mockEvent.returnValue).toBe(true);
|
||||
});
|
||||
|
||||
it('should not call preventDefault when dirty is false', () => {
|
||||
act(() => {
|
||||
render(<PromptIfDirty dirty={false} />, container);
|
||||
});
|
||||
act(() => {
|
||||
window.dispatchEvent(mockEvent);
|
||||
});
|
||||
renderHook(() => usePromptIfDirty(() => false));
|
||||
window.dispatchEvent(mockEvent);
|
||||
|
||||
expect(mockEvent.preventDefault).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const PromptIfDirty = ({ dirty }) => {
|
||||
const usePromptIfDirty = (checkIfDirty : () => boolean) => {
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line consistent-return
|
||||
const handleBeforeUnload = (event) => {
|
||||
if (dirty) {
|
||||
if (checkIfDirty()) {
|
||||
event.preventDefault();
|
||||
// Included for legacy support, e.g. Chrome/Edge < 119
|
||||
event.returnValue = true; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
};
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
@@ -14,11 +15,9 @@ const PromptIfDirty = ({ dirty }) => {
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
};
|
||||
}, [dirty]);
|
||||
}, [checkIfDirty]);
|
||||
|
||||
return null;
|
||||
};
|
||||
PromptIfDirty.propTypes = {
|
||||
dirty: PropTypes.bool.isRequired,
|
||||
};
|
||||
export default PromptIfDirty;
|
||||
|
||||
export default usePromptIfDirty;
|
||||
Reference in New Issue
Block a user