feat: add returnUrl prop to editors (#341)
This commit is contained in:
@@ -15,6 +15,7 @@ export const Editor = ({
|
||||
lmsEndpointUrl,
|
||||
studioEndpointUrl,
|
||||
onClose,
|
||||
returnFunction,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
hooks.initializeApp({
|
||||
@@ -37,7 +38,7 @@ export const Editor = ({
|
||||
aria-label={blockType}
|
||||
>
|
||||
{(EditorComponent !== undefined)
|
||||
? <EditorComponent onClose={onClose} />
|
||||
? <EditorComponent {...{ onClose, returnFunction }} />
|
||||
: <FormattedMessage {...messages.couldNotFindEditor} />}
|
||||
</div>
|
||||
</div>
|
||||
@@ -48,6 +49,7 @@ Editor.defaultProps = {
|
||||
learningContextId: null,
|
||||
lmsEndpointUrl: null,
|
||||
onClose: null,
|
||||
returnFunction: null,
|
||||
studioEndpointUrl: null,
|
||||
};
|
||||
|
||||
@@ -57,6 +59,7 @@ Editor.propTypes = {
|
||||
learningContextId: PropTypes.string,
|
||||
lmsEndpointUrl: PropTypes.string,
|
||||
onClose: PropTypes.func,
|
||||
returnFunction: PropTypes.func,
|
||||
studioEndpointUrl: PropTypes.string,
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ export const EditorPage = ({
|
||||
lmsEndpointUrl,
|
||||
studioEndpointUrl,
|
||||
onClose,
|
||||
returnFunction,
|
||||
}) => (
|
||||
<Provider store={store}>
|
||||
<ErrorBoundary
|
||||
@@ -29,6 +30,7 @@ export const EditorPage = ({
|
||||
blockId,
|
||||
lmsEndpointUrl,
|
||||
studioEndpointUrl,
|
||||
returnFunction,
|
||||
}}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
@@ -39,6 +41,7 @@ EditorPage.defaultProps = {
|
||||
courseId: null,
|
||||
lmsEndpointUrl: null,
|
||||
onClose: null,
|
||||
returnFunction: null,
|
||||
studioEndpointUrl: null,
|
||||
};
|
||||
|
||||
@@ -48,6 +51,7 @@ EditorPage.propTypes = {
|
||||
courseId: PropTypes.string,
|
||||
lmsEndpointUrl: PropTypes.string,
|
||||
onClose: PropTypes.func,
|
||||
returnFunction: PropTypes.func,
|
||||
studioEndpointUrl: PropTypes.string,
|
||||
};
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ exports[`Editor render snapshot: renders correct editor given blockType (html ->
|
||||
>
|
||||
<TextEditor
|
||||
onClose={[MockFunction props.onClose]}
|
||||
returnFunction={null}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,6 +22,7 @@ exports[`Editor Page snapshots props besides blockType default to null 1`] = `
|
||||
learningContextId={null}
|
||||
lmsEndpointUrl={null}
|
||||
onClose={null}
|
||||
returnFunction={null}
|
||||
studioEndpointUrl={null}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
@@ -50,6 +51,7 @@ exports[`Editor Page snapshots rendering correctly with expected Input 1`] = `
|
||||
learningContextId="course-v1:edX+DemoX+Demo_Course"
|
||||
lmsEndpointUrl="evenfakerurl.com"
|
||||
onClose={[MockFunction props.onClose]}
|
||||
returnFunction={null}
|
||||
studioEndpointUrl="fakeurl.com"
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
|
||||
@@ -13,6 +13,7 @@ exports[`EditorContainer component render snapshot: initialized. enable save and
|
||||
Object {
|
||||
"handleCancel": Object {
|
||||
"onClose": [MockFunction props.onClose],
|
||||
"returnFunction": [MockFunction props.returnFunction],
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -73,6 +74,7 @@ exports[`EditorContainer component render snapshot: initialized. enable save and
|
||||
"handleSaveClicked": Object {
|
||||
"dispatch": [MockFunction react-redux.dispatch],
|
||||
"getContent": [MockFunction props.getContent],
|
||||
"returnFunction": [MockFunction props.returnFunction],
|
||||
"validateEntry": [MockFunction props.validateEntry],
|
||||
},
|
||||
}
|
||||
@@ -94,6 +96,7 @@ exports[`EditorContainer component render snapshot: not initialized. disable sav
|
||||
Object {
|
||||
"handleCancel": Object {
|
||||
"onClose": [MockFunction props.onClose],
|
||||
"returnFunction": [MockFunction props.returnFunction],
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -150,6 +153,7 @@ exports[`EditorContainer component render snapshot: not initialized. disable sav
|
||||
"handleSaveClicked": Object {
|
||||
"dispatch": [MockFunction react-redux.dispatch],
|
||||
"getContent": [MockFunction props.getContent],
|
||||
"returnFunction": [MockFunction props.returnFunction],
|
||||
"validateEntry": [MockFunction props.validateEntry],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -19,7 +19,12 @@ export const state = StrictDict({
|
||||
isCancelConfirmModalOpen: (val) => useState(val),
|
||||
});
|
||||
|
||||
export const handleSaveClicked = ({ dispatch, getContent, validateEntry }) => {
|
||||
export const handleSaveClicked = ({
|
||||
dispatch,
|
||||
getContent,
|
||||
validateEntry,
|
||||
returnFunction,
|
||||
}) => {
|
||||
const destination = useSelector(selectors.app.returnUrl);
|
||||
const analytics = useSelector(selectors.app.analytics);
|
||||
|
||||
@@ -28,6 +33,7 @@ export const handleSaveClicked = ({ dispatch, getContent, validateEntry }) => {
|
||||
content: getContent({ dispatch }),
|
||||
destination,
|
||||
dispatch,
|
||||
returnFunction,
|
||||
validateEntry,
|
||||
});
|
||||
};
|
||||
@@ -41,11 +47,12 @@ export const cancelConfirmModalToggle = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const handleCancel = ({ onClose }) => {
|
||||
export const handleCancel = ({ onClose, returnFunction }) => {
|
||||
if (onClose) {
|
||||
return onClose;
|
||||
}
|
||||
return navigateCallback({
|
||||
returnFunction,
|
||||
destination: useSelector(selectors.app.returnUrl),
|
||||
analyticsEvent: analyticsEvt.editorCancelClick,
|
||||
analytics: useSelector(selectors.app.analytics),
|
||||
|
||||
@@ -19,13 +19,14 @@ export const EditorContainer = ({
|
||||
getContent,
|
||||
onClose,
|
||||
validateEntry,
|
||||
returnFunction,
|
||||
// injected
|
||||
intl,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const isInitialized = hooks.isInitialized();
|
||||
const { isCancelConfirmOpen, openCancelConfirmModal, closeCancelConfirmModal } = hooks.cancelConfirmModalToggle();
|
||||
const handleCancel = hooks.handleCancel({ onClose });
|
||||
const handleCancel = hooks.handleCancel({ onClose, returnFunction });
|
||||
return (
|
||||
<div
|
||||
className="position-relative zindex-0"
|
||||
@@ -65,7 +66,12 @@ export const EditorContainer = ({
|
||||
clearSaveFailed={hooks.clearSaveError({ dispatch })}
|
||||
disableSave={!isInitialized}
|
||||
onCancel={openCancelConfirmModal}
|
||||
onSave={hooks.handleSaveClicked({ dispatch, getContent, validateEntry })}
|
||||
onSave={hooks.handleSaveClicked({
|
||||
dispatch,
|
||||
getContent,
|
||||
validateEntry,
|
||||
returnFunction,
|
||||
})}
|
||||
saveFailed={hooks.saveFailed()}
|
||||
/>
|
||||
</div>
|
||||
@@ -73,12 +79,14 @@ export const EditorContainer = ({
|
||||
};
|
||||
EditorContainer.defaultProps = {
|
||||
onClose: null,
|
||||
returnFunction: null,
|
||||
validateEntry: null,
|
||||
};
|
||||
EditorContainer.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
getContent: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func,
|
||||
returnFunction: PropTypes.func,
|
||||
validateEntry: PropTypes.func,
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
|
||||
@@ -9,6 +9,7 @@ const props = {
|
||||
getContent: jest.fn().mockName('props.getContent'),
|
||||
onClose: jest.fn().mockName('props.onClose'),
|
||||
validateEntry: jest.fn().mockName('props.validateEntry'),
|
||||
returnFunction: jest.fn().mockName('props.returnFunction'),
|
||||
// inject
|
||||
intl: { formatMessage },
|
||||
};
|
||||
@@ -51,6 +52,7 @@ describe('EditorContainer component', () => {
|
||||
dispatch: useDispatch(),
|
||||
getContent: props.getContent,
|
||||
validateEntry: props.validateEntry,
|
||||
returnFunction: props.returnFunction,
|
||||
});
|
||||
expect(el.children().at(3)
|
||||
.props().onSave).toEqual(expected);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
exports[`EditorProblemView component renders raw editor 1`] = `
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
getContent={[Function]}
|
||||
returnFunction={null}
|
||||
>
|
||||
<Component
|
||||
footerNode={
|
||||
@@ -71,6 +72,7 @@ exports[`EditorProblemView component renders raw editor 1`] = `
|
||||
exports[`EditorProblemView component renders simple view 1`] = `
|
||||
<injectIntl(ShimmedIntlComponent)
|
||||
getContent={[Function]}
|
||||
returnFunction={null}
|
||||
>
|
||||
<Component
|
||||
footerNode={
|
||||
|
||||
@@ -25,6 +25,7 @@ import ExplanationWidget from './ExplanationWidget';
|
||||
import { saveBlock } from '../../../../hooks';
|
||||
|
||||
export const EditProblemView = ({
|
||||
returnFunction,
|
||||
// redux
|
||||
problemType,
|
||||
problemState,
|
||||
@@ -50,6 +51,7 @@ export const EditProblemView = ({
|
||||
assets,
|
||||
lmsEndpointUrl,
|
||||
})}
|
||||
returnFunction={returnFunction}
|
||||
>
|
||||
<AlertModal
|
||||
title={isAdvancedProblemType ? (
|
||||
@@ -71,6 +73,7 @@ export const EditProblemView = ({
|
||||
assets,
|
||||
lmsEndpointUrl,
|
||||
})(),
|
||||
returnFunction,
|
||||
destination: returnUrl,
|
||||
dispatch,
|
||||
analytics,
|
||||
@@ -117,10 +120,12 @@ export const EditProblemView = ({
|
||||
EditProblemView.defaultProps = {
|
||||
assets: null,
|
||||
lmsEndpointUrl: null,
|
||||
returnFunction: null,
|
||||
};
|
||||
|
||||
EditProblemView.propTypes = {
|
||||
problemType: PropTypes.string.isRequired,
|
||||
returnFunction: PropTypes.func,
|
||||
// eslint-disable-next-line
|
||||
problemState: PropTypes.any.isRequired,
|
||||
assets: PropTypes.shape({}),
|
||||
|
||||
@@ -11,6 +11,7 @@ import messages from './messages';
|
||||
|
||||
export const ProblemEditor = ({
|
||||
onClose,
|
||||
returnFunction,
|
||||
// Redux
|
||||
problemType,
|
||||
blockFinished,
|
||||
@@ -48,16 +49,18 @@ export const ProblemEditor = ({
|
||||
}
|
||||
|
||||
if (problemType === null) {
|
||||
return (<SelectTypeModal onClose={onClose} />);
|
||||
return (<SelectTypeModal {...{ onClose }} />);
|
||||
}
|
||||
return (<EditProblemView onClose={onClose} />);
|
||||
return (<EditProblemView {...{ onClose, returnFunction }} />);
|
||||
};
|
||||
|
||||
ProblemEditor.defaultProps = {
|
||||
assetsFinished: null,
|
||||
returnFunction: null,
|
||||
};
|
||||
ProblemEditor.propTypes = {
|
||||
onClose: PropTypes.func.isRequired,
|
||||
returnFunction: PropTypes.func,
|
||||
// redux
|
||||
assetsFinished: PropTypes.bool,
|
||||
advancedSettingsFinished: PropTypes.bool.isRequired,
|
||||
|
||||
@@ -20,6 +20,7 @@ exports[`TextEditor snapshots block failed to load, Toast is shown 1`] = `
|
||||
}
|
||||
}
|
||||
onClose={[MockFunction props.onClose]}
|
||||
returnFunction={null}
|
||||
>
|
||||
<div
|
||||
className="editor-body h-75 overflow-auto"
|
||||
@@ -73,6 +74,7 @@ exports[`TextEditor snapshots loaded, raw editor 1`] = `
|
||||
}
|
||||
}
|
||||
onClose={[MockFunction props.onClose]}
|
||||
returnFunction={null}
|
||||
>
|
||||
<div
|
||||
className="editor-body h-75 overflow-auto"
|
||||
@@ -128,6 +130,7 @@ exports[`TextEditor snapshots not yet loaded, Spinner appears 1`] = `
|
||||
}
|
||||
}
|
||||
onClose={[MockFunction props.onClose]}
|
||||
returnFunction={null}
|
||||
>
|
||||
<div
|
||||
className="editor-body h-75 overflow-auto"
|
||||
@@ -175,6 +178,7 @@ exports[`TextEditor snapshots renders as expected with default behavior 1`] = `
|
||||
}
|
||||
}
|
||||
onClose={[MockFunction props.onClose]}
|
||||
returnFunction={null}
|
||||
>
|
||||
<div
|
||||
className="editor-body h-75 overflow-auto"
|
||||
|
||||
@@ -20,6 +20,7 @@ import { prepareEditorRef } from '../../sharedComponents/TinyMceWidget/hooks';
|
||||
|
||||
export const TextEditor = ({
|
||||
onClose,
|
||||
returnFunction,
|
||||
// redux
|
||||
isRaw,
|
||||
blockValue,
|
||||
@@ -60,6 +61,7 @@ export const TextEditor = ({
|
||||
<EditorContainer
|
||||
getContent={hooks.getContent({ editorRef, isRaw, assets })}
|
||||
onClose={onClose}
|
||||
returnFunction={returnFunction}
|
||||
>
|
||||
<div className="editor-body h-75 overflow-auto">
|
||||
<Toast show={blockFailed} onClose={hooks.nullMethod}>
|
||||
@@ -85,9 +87,11 @@ TextEditor.defaultProps = {
|
||||
isRaw: null,
|
||||
assetsFinished: null,
|
||||
assets: null,
|
||||
returnFunction: null,
|
||||
};
|
||||
TextEditor.propTypes = {
|
||||
onClose: PropTypes.func.isRequired,
|
||||
returnFunction: PropTypes.func,
|
||||
// redux
|
||||
blockValue: PropTypes.shape({
|
||||
data: PropTypes.shape({ data: PropTypes.string }),
|
||||
|
||||
@@ -6,6 +6,7 @@ exports[`VideoEditor snapshots renders as expected with default behavior 1`] = `
|
||||
>
|
||||
<EditorContainer
|
||||
onClose={[MockFunction props.onClose]}
|
||||
returnFunction={null}
|
||||
validateEntry={[MockFunction validateEntry]}
|
||||
>
|
||||
<div
|
||||
@@ -34,6 +35,7 @@ exports[`VideoEditor snapshots renders as expected with default behavior 2`] = `
|
||||
>
|
||||
<EditorContainer
|
||||
onClose={[MockFunction props.onClose]}
|
||||
returnFunction={null}
|
||||
validateEntry={[MockFunction validateEntry]}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -17,6 +17,7 @@ import messages from './messages';
|
||||
|
||||
export const VideoEditor = ({
|
||||
onClose,
|
||||
returnFunction,
|
||||
// injected
|
||||
intl,
|
||||
// redux
|
||||
@@ -31,6 +32,7 @@ export const VideoEditor = ({
|
||||
<EditorContainer
|
||||
getContent={fetchVideoContent()}
|
||||
onClose={onClose}
|
||||
returnFunction={returnFunction}
|
||||
validateEntry={validateEntry}
|
||||
>
|
||||
{studioViewFinished ? (
|
||||
@@ -59,9 +61,11 @@ export const VideoEditor = ({
|
||||
|
||||
VideoEditor.defaultProps = {
|
||||
onClose: null,
|
||||
returnFunction: null,
|
||||
};
|
||||
VideoEditor.propTypes = {
|
||||
onClose: PropTypes.func,
|
||||
returnFunction: PropTypes.func,
|
||||
// injected
|
||||
intl: intlShape.isRequired,
|
||||
// redux
|
||||
|
||||
@@ -17,6 +17,7 @@ export const navigateTo = (destination) => {
|
||||
};
|
||||
|
||||
export const navigateCallback = ({
|
||||
returnFunction,
|
||||
destination,
|
||||
analyticsEvent,
|
||||
analytics,
|
||||
@@ -24,6 +25,10 @@ export const navigateCallback = ({
|
||||
if (process.env.NODE_ENV !== 'development' && analyticsEvent && analytics) {
|
||||
sendTrackEvent(analyticsEvent, analytics);
|
||||
}
|
||||
if (returnFunction) {
|
||||
returnFunction();
|
||||
return;
|
||||
}
|
||||
module.navigateTo(destination);
|
||||
};
|
||||
|
||||
@@ -34,6 +39,7 @@ export const saveBlock = ({
|
||||
content,
|
||||
destination,
|
||||
dispatch,
|
||||
returnFunction,
|
||||
validateEntry,
|
||||
}) => {
|
||||
if (!content) {
|
||||
@@ -53,6 +59,7 @@ export const saveBlock = ({
|
||||
destination,
|
||||
analyticsEvent: analyticsEvt.editorSaveClick,
|
||||
analytics,
|
||||
returnFunction,
|
||||
}),
|
||||
content,
|
||||
}));
|
||||
|
||||
@@ -74,6 +74,7 @@ describe('hooks', () => {
|
||||
let output;
|
||||
const SAVED_ENV = process.env;
|
||||
const destination = 'hOmE';
|
||||
const returnFunction = jest.fn();
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
process.env = { ...SAVED_ENV };
|
||||
@@ -100,6 +101,15 @@ describe('hooks', () => {
|
||||
output();
|
||||
expect(spy).toHaveBeenCalledWith(destination);
|
||||
});
|
||||
it('should call returnFunction and return null', () => {
|
||||
output = hooks.navigateCallback({
|
||||
destination,
|
||||
returnFunction,
|
||||
});
|
||||
const returnedOutput = output();
|
||||
expect(returnFunction).toHaveBeenCalled();
|
||||
expect(returnedOutput).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('nullMethod', () => {
|
||||
|
||||
Reference in New Issue
Block a user