From b047f7a2a838dd6bc94f039e72ed543a82a4b0f5 Mon Sep 17 00:00:00 2001 From: Raymond Zhou <56318341+rayzhou-bit@users.noreply.github.com> Date: Tue, 28 Jun 2022 11:25:09 -0700 Subject: [PATCH] feat: raw html editor (#86) raw html editor --- .../__snapshots__/index.test.jsx.snap | 59 +++++++++++++++++++ .../__snapshots__/index.test.jsx.snap | 24 ++++++++ .../TextEditor/components/RawEditor/index.jsx | 33 +++++++++++ .../components/RawEditor/index.test.jsx | 18 ++++++ src/editors/containers/TextEditor/hooks.js | 7 ++- .../containers/TextEditor/hooks.test.jsx | 15 +++++ src/editors/containers/TextEditor/index.jsx | 48 ++++++++++----- .../containers/TextEditor/index.test.jsx | 7 ++- src/editors/data/constants/requests.js | 3 +- src/editors/data/redux/app/reducer.js | 5 ++ src/editors/data/redux/app/reducer.test.js | 1 + src/editors/data/redux/app/selectors.js | 16 +++++ src/editors/data/redux/app/selectors.test.js | 28 +++++++++ src/editors/data/redux/requests/reducer.js | 2 + src/editors/data/redux/thunkActions/app.js | 9 +++ .../data/redux/thunkActions/app.test.js | 27 ++++++++- .../data/redux/thunkActions/requests.js | 24 +++++++- .../data/redux/thunkActions/requests.test.js | 16 +++++ src/editors/data/services/cms/api.js | 3 + src/editors/data/services/cms/api.test.js | 8 +++ src/editors/data/services/cms/mockApi.js | 11 ++++ src/editors/data/services/cms/urls.js | 4 ++ src/editors/data/services/cms/urls.test.js | 8 +++ 23 files changed, 353 insertions(+), 23 deletions(-) create mode 100644 src/editors/containers/TextEditor/components/RawEditor/__snapshots__/index.test.jsx.snap create mode 100644 src/editors/containers/TextEditor/components/RawEditor/index.jsx create mode 100644 src/editors/containers/TextEditor/components/RawEditor/index.test.jsx diff --git a/src/editors/containers/TextEditor/__snapshots__/index.test.jsx.snap b/src/editors/containers/TextEditor/__snapshots__/index.test.jsx.snap index 1cd6ca86b..362507ca0 100644 --- a/src/editors/containers/TextEditor/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/TextEditor/__snapshots__/index.test.jsx.snap @@ -10,6 +10,7 @@ exports[`TextEditor snapshots block failed to load, Toast is shown 1`] = ` "value": "something", }, }, + "isRaw": false, }, } } @@ -64,6 +65,62 @@ exports[`TextEditor snapshots block failed to load, Toast is shown 1`] = ` `; +exports[`TextEditor snapshots loaded, raw editor 1`] = ` + +
+ + + + + +
+
+`; + exports[`TextEditor snapshots not yet loaded, Spinner appears 1`] = ` + + You are using the raw HTML editor. + + + +`; diff --git a/src/editors/containers/TextEditor/components/RawEditor/index.jsx b/src/editors/containers/TextEditor/components/RawEditor/index.jsx new file mode 100644 index 000000000..69901ac45 --- /dev/null +++ b/src/editors/containers/TextEditor/components/RawEditor/index.jsx @@ -0,0 +1,33 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Alert } from '@edx/paragon'; + +export const RawEditor = ({ + editorRef, + text, +}) => ( +
+ + You are using the raw HTML editor. + + +
+); +RawEditor.defaultProps = { + editorRef: null, +}; +RawEditor.propTypes = { + editorRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ current: PropTypes.any }), + ]), + text: PropTypes.string.isRequired, +}; + +export default RawEditor; diff --git a/src/editors/containers/TextEditor/components/RawEditor/index.test.jsx b/src/editors/containers/TextEditor/components/RawEditor/index.test.jsx new file mode 100644 index 000000000..cf703a75a --- /dev/null +++ b/src/editors/containers/TextEditor/components/RawEditor/index.test.jsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import { RawEditor } from '.'; + +describe('ImageSettingsModal', () => { + const props = { + editorRef: { + current: { + value: 'Ref Value', + }, + }, + text: 'sOmErAwHtml', + }; + test('renders as expected with default behavior', () => { + expect(shallow()).toMatchSnapshot(); + }); +}); diff --git a/src/editors/containers/TextEditor/hooks.js b/src/editors/containers/TextEditor/hooks.js index 308ecd9ab..29dacf2df 100644 --- a/src/editors/containers/TextEditor/hooks.js +++ b/src/editors/containers/TextEditor/hooks.js @@ -112,7 +112,12 @@ export const prepareEditorRef = () => { return { editorRef, refReady, setEditorRef }; }; -export const getContent = ({ editorRef }) => () => editorRef.current?.getContent(); +export const getContent = ({ editorRef, isRaw }) => () => { + if (isRaw && editorRef && editorRef.current) { + return editorRef.current.value; + } + return editorRef.current?.getContent(); +}; export const selectedImage = (val) => { const [selection, setSelection] = module.state.imageSelection(val); diff --git a/src/editors/containers/TextEditor/hooks.test.jsx b/src/editors/containers/TextEditor/hooks.test.jsx index e1d12a4ab..2983b32a9 100644 --- a/src/editors/containers/TextEditor/hooks.test.jsx +++ b/src/editors/containers/TextEditor/hooks.test.jsx @@ -188,6 +188,21 @@ describe('TextEditor hooks', () => { }); }); + describe('getContent', () => { + const visualContent = 'sOmEViSualContent'; + const rawContent = 'soMeRawContent'; + const editorRef = { + current: { + getContent: () => visualContent, + value: rawContent, + }, + }; + test('returns correct ontent based on isRaw', () => { + expect(module.getContent({ editorRef, isRaw: false })()).toEqual(visualContent); + expect(module.getContent({ editorRef, isRaw: true })()).toEqual(rawContent); + }); + }); + describe('selectedImage hooks', () => { const val = { a: 'VaLUe' }; beforeEach(() => { diff --git a/src/editors/containers/TextEditor/index.jsx b/src/editors/containers/TextEditor/index.jsx index 928af03bd..581f3d7f7 100644 --- a/src/editors/containers/TextEditor/index.jsx +++ b/src/editors/containers/TextEditor/index.jsx @@ -31,12 +31,14 @@ import { RequestKeys } from '../../data/constants/requests'; import EditorContainer from '../EditorContainer'; import ImageUploadModal from './components/ImageUploadModal'; +import RawEditor from './components/RawEditor'; import * as hooks from './hooks'; import messages from './messages'; export const TextEditor = ({ onClose, // redux + isRaw, blockValue, lmsEndpointUrl, studioEndpointUrl, @@ -52,9 +54,34 @@ export const TextEditor = ({ if (!refReady) { return null; } + const selectEditor = () => { + if (isRaw) { + return ( + + ); + } + return ( + + ); + }; + return (
@@ -78,21 +105,7 @@ export const TextEditor = ({ screenreadertext={intl.formatMessage(messages.spinnerScreenReaderText)} />
- ) - : ( - - )} + ) : (selectEditor())}
@@ -102,6 +115,7 @@ TextEditor.defaultProps = { blockValue: null, lmsEndpointUrl: null, studioEndpointUrl: null, + isRaw: null, }; TextEditor.propTypes = { onClose: PropTypes.func.isRequired, @@ -114,6 +128,7 @@ TextEditor.propTypes = { blockFailed: PropTypes.bool.isRequired, blockFinished: PropTypes.bool.isRequired, initializeEditor: PropTypes.func.isRequired, + isRaw: PropTypes.bool, // inject intl: intlShape.isRequired, }; @@ -124,6 +139,7 @@ export const mapStateToProps = (state) => ({ studioEndpointUrl: selectors.app.studioEndpointUrl(state), blockFailed: selectors.requests.isFailed(state, { requestKey: RequestKeys.fetchBlock }), blockFinished: selectors.requests.isFinished(state, { requestKey: RequestKeys.fetchBlock }), + isRaw: selectors.app.isRaw(state), }); export const mapDispatchToProps = { diff --git a/src/editors/containers/TextEditor/index.test.jsx b/src/editors/containers/TextEditor/index.test.jsx index 34be09ecc..04db506cf 100644 --- a/src/editors/containers/TextEditor/index.test.jsx +++ b/src/editors/containers/TextEditor/index.test.jsx @@ -61,7 +61,8 @@ jest.mock('../../data/redux', () => ({ app: { blockValue: jest.fn(state => ({ blockValue: state })), lmsEndpointUrl: jest.fn(state => ({ lmsEndpointUrl: state })), - studioEndpointUrl: jest.fn(state => ({ lmsEndpointUrl: state })), + studioEndpointUrl: jest.fn(state => ({ studioEndpointUrl: state })), + isRaw: jest.fn(state => ({ isRaw: state })), }, requests: { isFailed: jest.fn((state, params) => ({ isFailed: { state, params } })), @@ -80,6 +81,7 @@ describe('TextEditor', () => { blockFailed: false, blockFinished: true, initializeEditor: jest.fn().mockName('args.intializeEditor'), + isRaw: false, // inject intl: { formatMessage }, }; @@ -95,6 +97,9 @@ describe('TextEditor', () => { test('not yet loaded, Spinner appears', () => { expect(shallow()).toMatchSnapshot(); }); + test('loaded, raw editor', () => { + expect(shallow()).toMatchSnapshot(); + }); test('block failed to load, Toast is shown', () => { expect(shallow()).toMatchSnapshot(); }); diff --git a/src/editors/data/constants/requests.js b/src/editors/data/constants/requests.js index a9a39e7e3..4a953c2dc 100644 --- a/src/editors/data/constants/requests.js +++ b/src/editors/data/constants/requests.js @@ -9,8 +9,9 @@ export const RequestStates = StrictDict({ export const RequestKeys = StrictDict({ fetchBlock: 'fetchBlock', + fetchImages: 'fetchImages', + fetchStudioView: 'fetchStudioView', fetchUnit: 'fetchUnit', saveBlock: 'saveBlock', - fetchImages: 'fetchImages', uploadImage: 'uploadImage', }); diff --git a/src/editors/data/redux/app/reducer.js b/src/editors/data/redux/app/reducer.js index 39e34557b..f963df46a 100644 --- a/src/editors/data/redux/app/reducer.js +++ b/src/editors/data/redux/app/reducer.js @@ -6,6 +6,7 @@ const initialState = { blockValue: null, unitUrl: null, blockContent: null, + studioView: null, saveResponse: null, blockId: null, @@ -36,6 +37,10 @@ const app = createSlice({ blockValue: payload, blockTitle: payload.data.display_name, }), + setStudioView: (state, { payload }) => ({ + ...state, + studioView: payload, + }), setBlockContent: (state, { payload }) => ({ ...state, blockContent: payload }), setBlockTitle: (state, { payload }) => ({ ...state, blockTitle: payload }), setSaveResponse: (state, { payload }) => ({ ...state, saveResponse: payload }), diff --git a/src/editors/data/redux/app/reducer.test.js b/src/editors/data/redux/app/reducer.test.js index fd2eb4f22..767da4be4 100644 --- a/src/editors/data/redux/app/reducer.test.js +++ b/src/editors/data/redux/app/reducer.test.js @@ -43,6 +43,7 @@ describe('app reducer', () => { }; [ ['setUnitUrl', 'unitUrl'], + ['setStudioView', 'studioView'], ['setBlockContent', 'blockContent'], ['setBlockTitle', 'blockTitle'], ['setSaveResponse', 'saveResponse'], diff --git a/src/editors/data/redux/app/selectors.js b/src/editors/data/redux/app/selectors.js index 50ec2370b..1dbf6c9db 100644 --- a/src/editors/data/redux/app/selectors.js +++ b/src/editors/data/redux/app/selectors.js @@ -14,6 +14,7 @@ export const simpleSelectors = { blockId: mkSimpleSelector(app => app.blockId), blockType: mkSimpleSelector(app => app.blockType), blockValue: mkSimpleSelector(app => app.blockValue), + studioView: mkSimpleSelector(app => app.studioView), learningContextId: mkSimpleSelector(app => app.learningContextId), editorInitialized: mkSimpleSelector(app => app.editorInitialized), saveResponse: mkSimpleSelector(app => app.saveResponse), @@ -55,6 +56,7 @@ export const displayTitle = createSelector( : blockType[0].toUpperCase() + blockType.substring(1); }, ); + export const analytics = createSelector( [ module.simpleSelectors.blockId, @@ -66,10 +68,24 @@ export const analytics = createSelector( ), ); +export const isRaw = createSelector( + [module.simpleSelectors.studioView], + (studioView) => { + if (!studioView || !studioView.data || !studioView.data.html) { + return null; + } + if (studioView.data.html.includes('data-editor="raw"')) { + return true; + } + return false; + }, +); + export default { ...simpleSelectors, isInitialized, returnUrl, displayTitle, analytics, + isRaw, }; diff --git a/src/editors/data/redux/app/selectors.test.js b/src/editors/data/redux/app/selectors.test.js index c025c1223..882e0c024 100644 --- a/src/editors/data/redux/app/selectors.test.js +++ b/src/editors/data/redux/app/selectors.test.js @@ -45,6 +45,7 @@ describe('app selectors unit tests', () => { simpleKeys.studioEndpointUrl, simpleKeys.unitUrl, simpleKeys.blockTitle, + simpleKeys.studioView, ].map(testSimpleSelector); }); }); @@ -111,4 +112,31 @@ describe('app selectors unit tests', () => { expect(selectors.displayTitle.cb('random', null)).toEqual('Random'); }); }); + + describe('isRaw', () => { + const studioViewRaw = { + data: { + html: 'data-editor="raw"', + }, + }; + const studioViewVisual = { + data: { + html: 'sOmEthIngElse', + }, + }; + it('is memoized based on studioView', () => { + expect(selectors.isRaw.preSelectors).toEqual([ + simpleSelectors.studioView, + ]); + }); + it('returns null if studioView is null', () => { + expect(selectors.isRaw.cb(null)).toEqual(null); + }); + it('returns true if studioView is raw', () => { + expect(selectors.isRaw.cb(studioViewRaw)).toEqual(true); + }); + it('returns false if the studioView is not Raw', () => { + expect(selectors.isRaw.cb(studioViewVisual)).toEqual(false); + }); + }); }); diff --git a/src/editors/data/redux/requests/reducer.js b/src/editors/data/redux/requests/reducer.js index 64d6dbf49..cec256bf7 100644 --- a/src/editors/data/redux/requests/reducer.js +++ b/src/editors/data/redux/requests/reducer.js @@ -7,9 +7,11 @@ import { RequestStates, RequestKeys } from '../../constants/requests'; const initialState = { [RequestKeys.fetchUnit]: { status: RequestStates.inactive }, [RequestKeys.fetchBlock]: { status: RequestStates.inactive }, + [RequestKeys.fetchStudioView]: { status: RequestStates.inactive }, [RequestKeys.saveBlock]: { status: RequestStates.inactive }, [RequestKeys.fetchImages]: { status: RequestStates.inactive }, [RequestKeys.uploadImage]: { status: RequestStates.inactive }, + }; // eslint-disable-next-line no-unused-vars diff --git a/src/editors/data/redux/thunkActions/app.js b/src/editors/data/redux/thunkActions/app.js index d30911cb4..7a900cd73 100644 --- a/src/editors/data/redux/thunkActions/app.js +++ b/src/editors/data/redux/thunkActions/app.js @@ -10,6 +10,13 @@ export const fetchBlock = () => (dispatch) => { })); }; +export const fetchStudioView = () => (dispatch) => { + dispatch(requests.fetchStudioView({ + onSuccess: (response) => dispatch(actions.app.setStudioView(response)), + onFailure: (e) => dispatch(actions.app.setStudioView(e)), + })); +}; + export const fetchUnit = () => (dispatch) => { dispatch(requests.fetchUnit({ onSuccess: (response) => dispatch(actions.app.setUnitUrl(response)), @@ -27,6 +34,7 @@ export const initialize = (data) => (dispatch) => { dispatch(actions.app.initialize(data)); dispatch(module.fetchBlock()); dispatch(module.fetchUnit()); + dispatch(module.fetchStudioView()); }; /** @@ -67,4 +75,5 @@ export default StrictDict({ fetchImages, uploadImage, fetchVideos, + fetchStudioView, }); diff --git a/src/editors/data/redux/thunkActions/app.test.js b/src/editors/data/redux/thunkActions/app.test.js index 8501d5f49..e11876b87 100644 --- a/src/editors/data/redux/thunkActions/app.test.js +++ b/src/editors/data/redux/thunkActions/app.test.js @@ -8,6 +8,7 @@ jest.mock('./requests', () => ({ saveBlock: (args) => ({ saveBlock: args }), fetchImages: (args) => ({ fetchImages: args }), uploadImage: (args) => ({ uploadImage: args }), + fetchStudioView: (args) => ({ fetchStudioView: args }), })); jest.mock('../../../utils', () => ({ @@ -37,6 +38,27 @@ describe('app thunkActions', () => { expect(dispatch).toHaveBeenCalledWith(actions.app.setBlockValue(testValue)); }); }); + + describe('fetchStudioView', () => { + beforeEach(() => { + thunkActions.fetchStudioView()(dispatch); + [[dispatchedAction]] = dispatch.mock.calls; + }); + it('dispatches fetchStudioView action', () => { + expect(dispatchedAction.fetchStudioView).not.toEqual(undefined); + }); + it('dispatches actions.app.setStudioViewe on success', () => { + dispatch.mockClear(); + dispatchedAction.fetchStudioView.onSuccess(testValue); + expect(dispatch).toHaveBeenCalledWith(actions.app.setStudioView(testValue)); + }); + it('dispatches setStudioView on failure', () => { + dispatch.mockClear(); + dispatchedAction.fetchStudioView.onFailure(testValue); + expect(dispatch).toHaveBeenCalledWith(actions.app.setStudioView(testValue)); + }); + }); + describe('fetchUnit', () => { beforeEach(() => { thunkActions.fetchUnit()(dispatch); @@ -58,17 +80,20 @@ describe('app thunkActions', () => { }); describe('initialize', () => { it('dispatches actions.app.initialize, and then fetches both block and unit', () => { - const { fetchBlock, fetchUnit } = thunkActions; + const { fetchBlock, fetchUnit, fetchStudioView } = thunkActions; thunkActions.fetchBlock = () => 'fetchBlock'; thunkActions.fetchUnit = () => 'fetchUnit'; + thunkActions.fetchStudioView = () => 'fetchStudioView'; thunkActions.initialize(testValue)(dispatch); expect(dispatch.mock.calls).toEqual([ [actions.app.initialize(testValue)], [thunkActions.fetchBlock()], [thunkActions.fetchUnit()], + [thunkActions.fetchStudioView()], ]); thunkActions.fetchBlock = fetchBlock; thunkActions.fetchUnit = fetchUnit; + thunkActions.fetchStudioView = fetchStudioView; }); }); describe('saveBlock', () => { diff --git a/src/editors/data/redux/thunkActions/requests.js b/src/editors/data/redux/thunkActions/requests.js index 529197161..8c402a80c 100644 --- a/src/editors/data/redux/thunkActions/requests.js +++ b/src/editors/data/redux/thunkActions/requests.js @@ -50,6 +50,23 @@ export const fetchBlock = ({ ...rest }) => (dispatch, getState) => { })); }; +/** + * Tracked fetchStudioView api method. + * Tracked to the `fetchBlock` request key. + * @param {[func]} onSuccess - onSuccess method ((response) => { ... }) + * @param {[func]} onFailure - onFailure method ((error) => { ... }) + */ +export const fetchStudioView = ({ ...rest }) => (dispatch, getState) => { + dispatch(module.networkRequest({ + requestKey: RequestKeys.fetchStudioView, + promise: api.fetchStudioView({ + studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), + blockId: selectors.app.blockId(getState()), + }), + ...rest, + })); +}; + /** * Tracked fetchByUnitId api method. * Tracked to the `fetchUnit` request key. @@ -111,9 +128,10 @@ export const fetchImages = ({ ...rest }) => (dispatch, getState) => { }; export default StrictDict({ - uploadImage, - fetchImages, - fetchUnit, fetchBlock, + fetchImages, + fetchStudioView, + fetchUnit, saveBlock, + uploadImage, }); diff --git a/src/editors/data/redux/thunkActions/requests.test.js b/src/editors/data/redux/thunkActions/requests.test.js index 2b8b7b791..326f685bb 100644 --- a/src/editors/data/redux/thunkActions/requests.test.js +++ b/src/editors/data/redux/thunkActions/requests.test.js @@ -18,6 +18,7 @@ jest.mock('../app/selectors', () => ({ jest.mock('../../services/cms/api', () => ({ fetchBlockById: ({ id, url }) => ({ id, url }), + fetchStudioView: ({ id, url }) => ({ id, url }), fetchByUnitId: ({ id, url }) => ({ id, url }), saveBlock: (args) => args, fetchImages: ({ id, url }) => ({ id, url }), @@ -191,6 +192,21 @@ describe('requests thunkActions module', () => { }, }); }); + describe('fetchStudioView', () => { + testNetworkRequestAction({ + action: requests.fetchStudioView, + args: fetchParams, + expectedString: 'with fetchStudioView promise', + expectedData: { + ...fetchParams, + requestKey: RequestKeys.fetchStudioView, + promise: api.fetchStudioView({ + studioEndpointUrl: selectors.app.studioEndpointUrl(testState), + blockId: selectors.app.blockId(testState), + }), + }, + }); + }); describe('fetchImages', () => { let fetchImages; diff --git a/src/editors/data/services/cms/api.js b/src/editors/data/services/cms/api.js index 795acece2..a7999fa34 100644 --- a/src/editors/data/services/cms/api.js +++ b/src/editors/data/services/cms/api.js @@ -11,6 +11,9 @@ export const apiMethods = { fetchByUnitId: ({ blockId, studioEndpointUrl }) => get( urls.blockAncestor({ studioEndpointUrl, blockId }), ), + fetchStudioView: ({ blockId, studioEndpointUrl }) => get( + urls.blockStudioView({ studioEndpointUrl, blockId }), + ), fetchImages: ({ learningContextId, studioEndpointUrl }) => get( urls.courseImages({ studioEndpointUrl, learningContextId }), ), diff --git a/src/editors/data/services/cms/api.test.js b/src/editors/data/services/cms/api.test.js index 2d9d6868c..cd736e64d 100644 --- a/src/editors/data/services/cms/api.test.js +++ b/src/editors/data/services/cms/api.test.js @@ -15,6 +15,7 @@ jest.mock('../../../utils', () => { jest.mock('./urls', () => ({ block: jest.fn().mockName('urls.block'), blockAncestor: jest.fn().mockName('urls.blockAncestor'), + blockStudioView: jest.fn().mockName('urls.StudioView'), courseImages: jest.fn().mockName('urls.courseImages'), courseAssets: jest.fn().mockName('urls.courseAssets'), })); @@ -50,6 +51,13 @@ describe('cms api', () => { }); }); + describe('fetchStudioView', () => { + it('should call get with url.blockStudioView', () => { + apiMethods.fetchStudioView({ blockId, studioEndpointUrl }); + expect(get).toHaveBeenCalledWith(urls.blockStudioView({ studioEndpointUrl, blockId })); + }); + }); + describe('fetchImages', () => { it('should call get with url.courseImages', () => { apiMethods.fetchImages({ learningContextId, studioEndpointUrl }); diff --git a/src/editors/data/services/cms/mockApi.js b/src/editors/data/services/cms/mockApi.js index 350984bab..1e1abad85 100644 --- a/src/editors/data/services/cms/mockApi.js +++ b/src/editors/data/services/cms/mockApi.js @@ -13,11 +13,22 @@ export const fetchBlockById = ({ blockId, studioEndpointUrl }) => mockPromise({ }, }); +// TODO: update to return block data appropriate per block ID, which will equal block type +// eslint-disable-next-line +export const fetchStudioView = ({ blockId, studioEndpointUrl }) => mockPromise({ + data: { + data_editor: 'raw', + data: '

Test prompt content

', + display_name: 'My Text Prompt', + }, +}); + // TODO: update to return block data appropriate per block ID, which will equal block type // eslint-disable-next-line export const fetchByUnitId = ({ blockId, studioEndpointUrl }) => mockPromise({ data: { ancestors: [{ id: 'unitUrl' }] }, }); + // eslint-disable-next-line export const fetchImages = ({ learningContextId, studioEndpointUrl }) => mockPromise({ data: { diff --git a/src/editors/data/services/cms/urls.js b/src/editors/data/services/cms/urls.js index 6bb655088..b81614ffd 100644 --- a/src/editors/data/services/cms/urls.js +++ b/src/editors/data/services/cms/urls.js @@ -23,6 +23,10 @@ export const blockAncestor = ({ studioEndpointUrl, blockId }) => ( `${block({ studioEndpointUrl, blockId })}?fields=ancestorInfo` ); +export const blockStudioView = ({ studioEndpointUrl, blockId }) => ( + `${block({ studioEndpointUrl, blockId })}/studio_view` +); + export const courseAssets = ({ studioEndpointUrl, learningContextId }) => ( `${studioEndpointUrl}/assets/${learningContextId}/` ); diff --git a/src/editors/data/services/cms/urls.test.js b/src/editors/data/services/cms/urls.test.js index 2e42f4a68..3e8580d00 100644 --- a/src/editors/data/services/cms/urls.test.js +++ b/src/editors/data/services/cms/urls.test.js @@ -4,6 +4,7 @@ import { libraryV1, block, blockAncestor, + blockStudioView, courseAssets, courseImages, } from './urls'; @@ -57,6 +58,13 @@ describe('cms url methods', () => { .toEqual(`${block({ studioEndpointUrl, blockId })}?fields=ancestorInfo`); }); }); + describe('blockStudioView', () => { + it('returns url with studioEndpointUrl, blockId and studio_view query', () => { + expect(blockStudioView({ studioEndpointUrl, blockId })) + .toEqual(`${block({ studioEndpointUrl, blockId })}/studio_view`); + }); + }); + describe('courseAssets', () => { it('returns url with studioEndpointUrl and learningContextId', () => { expect(courseAssets({ studioEndpointUrl, learningContextId }))