feat: change image absolute urls to relative urls
This commit is contained in:
@@ -11,11 +11,45 @@ export const {
|
||||
saveBlock,
|
||||
} = appHooks;
|
||||
|
||||
export const setAssetToStaticUrl = (images, getContent) => {
|
||||
/* For assets to remain usable across course instances, we convert their url to be course-agnostic.
|
||||
* For example, /assets/course/<asset hash>/filename gets converted to /static/filename. This is
|
||||
* important for rerunning courses and importing/exporting course as the /static/ part of the url
|
||||
* allows the asset to be mapped to the new course run.
|
||||
*/
|
||||
let content = getContent();
|
||||
const imageUrls = [];
|
||||
const imgsArray = Object.values(images);
|
||||
imgsArray.forEach(image => {
|
||||
imageUrls.push({ portableUrl: image.portableUrl, displayName: image.displayName });
|
||||
});
|
||||
const imageSrcs = content.split('src="');
|
||||
imageSrcs.forEach(src => {
|
||||
if (src.startsWith('/asset') && imageUrls.length > 0) {
|
||||
const nameFromEditorSrc = src.substring(src.lastIndexOf('@') + 1, src.indexOf('"'));
|
||||
const nameFromStudioSrc = nameFromEditorSrc.substring(nameFromEditorSrc.indexOf('/') + 1);
|
||||
let portableUrl;
|
||||
imageUrls.forEach((url) => {
|
||||
if (url.displayName === nameFromEditorSrc || url.displayName === nameFromStudioSrc) {
|
||||
portableUrl = url.portableUrl;
|
||||
}
|
||||
});
|
||||
if (portableUrl) {
|
||||
const currentSrc = src.substring(0, src.indexOf('"'));
|
||||
const updatedContent = content.replace(currentSrc, portableUrl);
|
||||
content = updatedContent;
|
||||
}
|
||||
}
|
||||
});
|
||||
return content;
|
||||
};
|
||||
|
||||
export const handleSaveClicked = ({ getContent, dispatch }) => {
|
||||
const destination = useSelector(selectors.app.returnUrl);
|
||||
const analytics = useSelector(selectors.app.analytics);
|
||||
const images = useSelector(selectors.app.images);
|
||||
return () => saveBlock({
|
||||
content: getContent(),
|
||||
content: setAssetToStaticUrl(images, getContent),
|
||||
destination,
|
||||
analytics,
|
||||
dispatch,
|
||||
|
||||
@@ -11,6 +11,7 @@ jest.mock('../../data/redux', () => ({
|
||||
selectors: {
|
||||
app: {
|
||||
isInitialized: (state) => ({ isInitialized: state }),
|
||||
images: (state) => ({ images: state }),
|
||||
},
|
||||
requests: {
|
||||
isFailed: (...args) => ({ requestFailed: args }),
|
||||
@@ -25,6 +26,20 @@ jest.mock('../../hooks', () => ({
|
||||
|
||||
const dispatch = jest.fn();
|
||||
describe('EditorContainer hooks', () => {
|
||||
describe('non-state hooks', () => {
|
||||
describe('replaceStaticwithAsset', () => {
|
||||
it('returns content with updated img links', () => {
|
||||
const getContent = jest.fn(() => '<img src="/asset@asset-block/soMEImagEURl1"/> <img src="/asset@soMEImagEURl" />');
|
||||
const images = [
|
||||
{ portableUrl: '/static/soMEImagEURl', displayName: 'soMEImagEURl' },
|
||||
{ portableUrl: '/static/soMEImagEURl1', displayName: 'soMEImagEURl1' },
|
||||
];
|
||||
const content = hooks.setAssetToStaticUrl(images, getContent);
|
||||
expect(getContent).toHaveBeenCalled();
|
||||
expect(content).toEqual('<img src="/static/soMEImagEURl1"/> <img src="/static/soMEImagEURl" />');
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('forwarded hooks', () => {
|
||||
it('forwards navigateCallback from app hooks', () => {
|
||||
expect(hooks.navigateCallback).toEqual(appHooks.navigateCallback);
|
||||
@@ -41,17 +56,19 @@ describe('EditorContainer hooks', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
describe('handleSaveClicked', () => {
|
||||
it('returns callback to saveBlock with dispatch and content from getContent', () => {
|
||||
it('returns callback to saveBlock with dispatch and content from setAssetToStaticUrl', () => {
|
||||
const getContent = () => 'myTestContentValue';
|
||||
const setAssetToStaticUrl = () => 'myTestContentValue';
|
||||
const output = hooks.handleSaveClicked({
|
||||
getContent,
|
||||
images: { portableUrl: '/static/sOmEuiMAge.jpeg', displayName: 'sOmEuiMAge' },
|
||||
destination: 'testDEsTURL',
|
||||
analytics: 'soMEanALytics',
|
||||
dispatch,
|
||||
});
|
||||
output();
|
||||
expect(appHooks.saveBlock).toHaveBeenCalledWith({
|
||||
content: getContent(),
|
||||
content: setAssetToStaticUrl(reactRedux.useSelector(selectors.app.images), getContent),
|
||||
destination: reactRedux.useSelector(selectors.app.returnUrl),
|
||||
analytics: reactRedux.useSelector(selectors.app.analytics),
|
||||
dispatch,
|
||||
|
||||
@@ -11,7 +11,7 @@ export const propsString = (props) => (
|
||||
);
|
||||
|
||||
export const imgProps = ({ settings, selection }) => ({
|
||||
src: selection.externalUrl,
|
||||
src: selection.url,
|
||||
alt: settings.isDecorative ? '' : settings.altText,
|
||||
width: settings.dimensions.width,
|
||||
height: settings.dimensions.height,
|
||||
|
||||
@@ -25,9 +25,9 @@ const settings = {
|
||||
describe('ImageUploadModal', () => {
|
||||
describe('hooks', () => {
|
||||
describe('imgTag', () => {
|
||||
const selection = { externalUrl: 'sOmEuRl.cOm' };
|
||||
const selection = { url: 'sOmEuRl.cOm' };
|
||||
const expected = {
|
||||
src: selection.externalUrl,
|
||||
src: selection.url,
|
||||
alt: settings.altText,
|
||||
width: settings.dimensions.width,
|
||||
height: settings.dimensions.height,
|
||||
|
||||
@@ -62,19 +62,21 @@ export const setupCustomBehavior = ({
|
||||
|
||||
export const replaceStaticwithAsset = (editor, imageUrls) => {
|
||||
const content = editor.getContent();
|
||||
const imageSrcs = content.split('img src="');
|
||||
const imageSrcs = content.split('src="');
|
||||
imageSrcs.forEach(src => {
|
||||
if (src.startsWith('/static/') && imageUrls.length > 0) {
|
||||
const imgName = src.substring(8, src.indexOf('"'));
|
||||
let staticFullUrl;
|
||||
imageUrls.forEach((url) => {
|
||||
if (url.includes(imgName)) {
|
||||
staticFullUrl = url;
|
||||
if (imgName === url.displayName) {
|
||||
staticFullUrl = url.staticFullUrl;
|
||||
}
|
||||
});
|
||||
const currentSrc = src.substring(0, src.indexOf('"'));
|
||||
const updatedContent = content.replace(currentSrc, staticFullUrl);
|
||||
editor.setContent(updatedContent);
|
||||
if (staticFullUrl) {
|
||||
const currentSrc = src.substring(0, src.indexOf('"'));
|
||||
const updatedContent = content.replace(currentSrc, staticFullUrl);
|
||||
editor.setContent(updatedContent);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -182,7 +184,7 @@ export const fetchImageUrls = (images) => {
|
||||
const imageUrls = [];
|
||||
const imgsArray = Object.values(images);
|
||||
imgsArray.forEach(image => {
|
||||
imageUrls.push(image.staticFullUrl);
|
||||
imageUrls.push({ staticFullUrl: image.staticFullUrl, displayName: image.displayName });
|
||||
});
|
||||
return imageUrls;
|
||||
};
|
||||
|
||||
@@ -87,18 +87,22 @@ describe('TextEditor hooks', () => {
|
||||
});
|
||||
|
||||
describe('replaceStaticwithAsset', () => {
|
||||
const editor = { getContent: jest.fn(() => '<img src="/static/soMEImagEURl1.jpeg"/>'), setContent: jest.fn() };
|
||||
const imageUrls = ['soMEImagEURl1.jpeg'];
|
||||
module.replaceStaticwithAsset(editor, imageUrls);
|
||||
expect(editor.getContent).toHaveBeenCalled();
|
||||
expect(editor.setContent).toHaveBeenCalled();
|
||||
test('it calls getContent and setContent', () => {
|
||||
const editor = { getContent: jest.fn(() => '<img src="/static/soMEImagEURl1.jpeg"/>'), setContent: jest.fn() };
|
||||
const imageUrls = [{ staticFullUrl: '/assets/soMEImagEURl1.jpeg', displayName: 'soMEImagEURl1.jpeg' }];
|
||||
module.replaceStaticwithAsset(editor, imageUrls);
|
||||
expect(editor.getContent).toHaveBeenCalled();
|
||||
expect(editor.setContent).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkRelativeUrl', () => {
|
||||
const editor = { on: jest.fn() };
|
||||
const imageUrls = ['soMEImagEURl1'];
|
||||
module.checkRelativeUrl(imageUrls)(editor);
|
||||
expect(editor.on).toHaveBeenCalled();
|
||||
test('it calls editor.on', () => {
|
||||
const editor = { on: jest.fn() };
|
||||
const imageUrls = ['soMEImagEURl1'];
|
||||
module.checkRelativeUrl(imageUrls)(editor);
|
||||
expect(editor.on).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('editorConfig', () => {
|
||||
|
||||
@@ -42,6 +42,7 @@ describe('app selectors unit tests', () => {
|
||||
simpleKeys.learningContextId,
|
||||
simpleKeys.editorInitialized,
|
||||
simpleKeys.saveResponse,
|
||||
simpleKeys.lmsEndpointUrl,
|
||||
simpleKeys.studioEndpointUrl,
|
||||
simpleKeys.unitUrl,
|
||||
simpleKeys.blockTitle,
|
||||
|
||||
Reference in New Issue
Block a user