feat: Add cancel confirmation modal to Advanced editors in libraries [FC-0076] (#1672)
* Extracts the cancel confirmation modal as a new component. * Adds the cancel confirmation modal to the Advanced editors in libraries.
This commit is contained in:
@@ -4,6 +4,9 @@ import {
|
||||
render,
|
||||
initializeMocks,
|
||||
waitFor,
|
||||
screen,
|
||||
act,
|
||||
fireEvent,
|
||||
} from '../testUtils';
|
||||
import AdvancedEditor from './AdvancedEditor';
|
||||
|
||||
@@ -17,7 +20,7 @@ describe('AdvancedEditor', () => {
|
||||
initializeMocks();
|
||||
});
|
||||
|
||||
it('should call onClose when receiving "cancel-clicked" message', () => {
|
||||
it('should call onClose when receiving "cancel-clicked" message', async () => {
|
||||
render(<AdvancedEditor usageKey="test" onClose={onCloseMock} />);
|
||||
|
||||
const messageEvent = new MessageEvent('message', {
|
||||
@@ -28,8 +31,17 @@ describe('AdvancedEditor', () => {
|
||||
origin: getConfig().STUDIO_BASE_URL,
|
||||
});
|
||||
|
||||
window.dispatchEvent(messageEvent);
|
||||
act(() => {
|
||||
// Send cancel event
|
||||
window.dispatchEvent(messageEvent);
|
||||
});
|
||||
|
||||
// Expect open cancel confimation modal
|
||||
expect(await screen.findByText(/Are you sure you want to exit the editor/)).toBeInTheDocument();
|
||||
// Click on "OK"
|
||||
const confirmButton = await screen.findByRole('button', { name: 'OK' });
|
||||
fireEvent.click(confirmButton);
|
||||
// Should call `onClose`
|
||||
expect(onCloseMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { useToggle } from '@openedx/paragon';
|
||||
|
||||
import { LibraryBlock } from '../library-authoring/LibraryBlock';
|
||||
import { EditorModalWrapper } from './containers/EditorContainer';
|
||||
import { ToastContext } from '../generic/toast-context';
|
||||
|
||||
import messages from './messages';
|
||||
import CancelConfirmModal from './containers/EditorContainer/components/CancelConfirmModal';
|
||||
|
||||
interface AdvancedEditorProps {
|
||||
usageKey: string,
|
||||
onClose: Function | null,
|
||||
onClose: (() => void) | null,
|
||||
}
|
||||
|
||||
const AdvancedEditor = ({ usageKey, onClose }: AdvancedEditorProps) => {
|
||||
const intl = useIntl();
|
||||
const { showToast } = React.useContext(ToastContext);
|
||||
const [isCancelConfirmOpen, openCancelConfirmModal, closeCancelConfirmModal] = useToggle(false);
|
||||
|
||||
useEffect(() => {
|
||||
const handleIframeMessage = (event) => {
|
||||
@@ -25,9 +29,9 @@ const AdvancedEditor = ({ usageKey, onClose }: AdvancedEditorProps) => {
|
||||
if (event.data.type === 'xblock-event') {
|
||||
const { eventName, data } = event.data;
|
||||
|
||||
if (onClose && (eventName === 'cancel'
|
||||
|| (eventName === 'save' && data.state === 'end'))
|
||||
) {
|
||||
if (eventName === 'cancel') {
|
||||
openCancelConfirmModal();
|
||||
} else if (onClose && eventName === 'save' && data.state === 'end') {
|
||||
onClose();
|
||||
} else if (eventName === 'error') {
|
||||
showToast(intl.formatMessage(messages.advancedEditorGenericError));
|
||||
@@ -43,12 +47,19 @@ const AdvancedEditor = ({ usageKey, onClose }: AdvancedEditorProps) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<EditorModalWrapper onClose={onClose as () => void}>
|
||||
<LibraryBlock
|
||||
usageKey={usageKey}
|
||||
view="studio_view"
|
||||
<>
|
||||
<EditorModalWrapper onClose={openCancelConfirmModal}>
|
||||
<LibraryBlock
|
||||
usageKey={usageKey}
|
||||
view="studio_view"
|
||||
/>
|
||||
</EditorModalWrapper>
|
||||
<CancelConfirmModal
|
||||
isOpen={isCancelConfirmOpen}
|
||||
closeCancelConfirmModal={closeCancelConfirmModal}
|
||||
onCloseEditor={onClose}
|
||||
/>
|
||||
</EditorModalWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Button } from '@openedx/paragon';
|
||||
import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import BaseModal from '../../../sharedComponents/BaseModal';
|
||||
import messages from '../messages';
|
||||
|
||||
interface CancelConfirmModalProps {
|
||||
isOpen: boolean,
|
||||
closeCancelConfirmModal: () => void,
|
||||
onCloseEditor: (() => void) | null,
|
||||
}
|
||||
|
||||
const CancelConfirmModal = ({
|
||||
isOpen,
|
||||
closeCancelConfirmModal,
|
||||
onCloseEditor,
|
||||
}: CancelConfirmModalProps) => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<BaseModal
|
||||
size="md"
|
||||
confirmAction={(
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => onCloseEditor?.()}
|
||||
>
|
||||
<FormattedMessage {...messages.okButtonLabel} />
|
||||
</Button>
|
||||
)}
|
||||
isOpen={isOpen}
|
||||
close={closeCancelConfirmModal}
|
||||
title={intl.formatMessage(messages.cancelConfirmTitle)}
|
||||
>
|
||||
<FormattedMessage {...messages.cancelConfirmDescription} />
|
||||
</BaseModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CancelConfirmModal;
|
||||
@@ -15,12 +15,12 @@ import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { EditorComponent } from '../../EditorComponent';
|
||||
import { useEditorContext } from '../../EditorContext';
|
||||
import BaseModal from '../../sharedComponents/BaseModal';
|
||||
import TitleHeader from './components/TitleHeader';
|
||||
import * as hooks from './hooks';
|
||||
import messages from './messages';
|
||||
import './index.scss';
|
||||
import usePromptIfDirty from '../../../generic/promptIfDirty/usePromptIfDirty';
|
||||
import CancelConfirmModal from './components/CancelConfirmModal';
|
||||
|
||||
interface WrapperProps {
|
||||
children: React.ReactNode;
|
||||
@@ -118,29 +118,16 @@ const EditorContainer: React.FC<Props> = ({
|
||||
<FormattedMessage {...messages.contentSaveFailed} />
|
||||
</Toast>
|
||||
)}
|
||||
<BaseModal
|
||||
size="md"
|
||||
confirmAction={(
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
handleCancel();
|
||||
if (returnFunction) {
|
||||
closeCancelConfirmModal();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FormattedMessage {...messages.okButtonLabel} />
|
||||
</Button>
|
||||
)}
|
||||
<CancelConfirmModal
|
||||
isOpen={isCancelConfirmOpen}
|
||||
close={() => {
|
||||
closeCancelConfirmModal();
|
||||
closeCancelConfirmModal={closeCancelConfirmModal}
|
||||
onCloseEditor={() => {
|
||||
handleCancel();
|
||||
if (returnFunction) {
|
||||
closeCancelConfirmModal();
|
||||
}
|
||||
}}
|
||||
title={intl.formatMessage(messages.cancelConfirmTitle)}
|
||||
>
|
||||
<FormattedMessage {...messages.cancelConfirmDescription} />
|
||||
</BaseModal>
|
||||
/>
|
||||
<ModalDialog.Header className="shadow-sm zindex-10">
|
||||
<div className="d-flex flex-row justify-content-between">
|
||||
<h2 className="h3 col pl-0">
|
||||
|
||||
Reference in New Issue
Block a user