Skip to content

Commit

Permalink
feat: raw html editor (#86)
Browse files Browse the repository at this point in the history
raw html editor
  • Loading branch information
rayzhou-bit committed Jun 28, 2022
1 parent b2fec70 commit b047f7a
Show file tree
Hide file tree
Showing 23 changed files with 352 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ exports[`TextEditor snapshots block failed to load, Toast is shown 1`] = `
"value": "something",
},
},
"isRaw": false,
},
}
}
Expand Down Expand Up @@ -64,6 +65,62 @@ exports[`TextEditor snapshots block failed to load, Toast is shown 1`] = `
</EditorContainer>
`;

exports[`TextEditor snapshots loaded, raw editor 1`] = `
<EditorContainer
getContent={
Object {
"getContent": Object {
"editorRef": Object {
"current": Object {
"value": "something",
},
},
"isRaw": true,
},
}
}
onClose={[MockFunction props.onClose]}
>
<div
className="editor-body h-75 overflow-auto"
>
<ImageUploadModal
clearSelection={[MockFunction hooks.selectedImage.clearSelection]}
close={[MockFunction modal.closeModal]}
editorRef={
Object {
"current": Object {
"value": "something",
},
}
}
isOpen={false}
selection="hooks.selectedImage.selection"
setSelection={[MockFunction hooks.selectedImage.setSelection]}
/>
<Toast
onClose={[MockFunction hooks.nullMethod]}
show={false}
>
<FormattedMessage
defaultMessage="Error: Could Not Load Text Content"
description="Error Message Dispayed When HTML content fails to Load"
id="authoring.texteditor.load.error"
/>
</Toast>
<RawEditor
editorRef={
Object {
"current": Object {
"value": "something",
},
}
}
/>
</div>
</EditorContainer>
`;

exports[`TextEditor snapshots not yet loaded, Spinner appears 1`] = `
<EditorContainer
getContent={
Expand All @@ -74,6 +131,7 @@ exports[`TextEditor snapshots not yet loaded, Spinner appears 1`] = `
"value": "something",
},
},
"isRaw": false,
},
}
}
Expand Down Expand Up @@ -129,6 +187,7 @@ exports[`TextEditor snapshots renders as expected with default behavior 1`] = `
"value": "something",
},
},
"isRaw": false,
},
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ImageSettingsModal renders as expected with default behavior 1`] = `
<div
className="form-group"
style={
Object {
"padding": "10px 30px",
}
}
>
<Alert
variant="danger"
>
You are using the raw HTML editor.
</Alert>
<textarea
className="form-control"
rows="12"
>
sOmErAwHtml
</textarea>
</div>
`;
33 changes: 33 additions & 0 deletions src/editors/containers/TextEditor/components/RawEditor/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Alert } from '@edx/paragon';

export const RawEditor = ({
editorRef,
text,
}) => (
<div className="form-group" style={{ padding: '10px 30px' }}>
<Alert variant="danger">
You are using the raw HTML editor.
</Alert>
<textarea
className="form-control"
ref={editorRef}
rows="12"
>
{ text }
</textarea>
</div>
);
RawEditor.defaultProps = {
editorRef: null,
};
RawEditor.propTypes = {
editorRef: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({ current: PropTypes.any }),
]),
text: PropTypes.string.isRequired,
};

export default RawEditor;
Original file line number Diff line number Diff line change
@@ -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(<RawEditor {...props} />)).toMatchSnapshot();
});
});
7 changes: 6 additions & 1 deletion src/editors/containers/TextEditor/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
15 changes: 15 additions & 0 deletions src/editors/containers/TextEditor/hooks.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down
48 changes: 32 additions & 16 deletions src/editors/containers/TextEditor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -52,9 +54,34 @@ export const TextEditor = ({

if (!refReady) { return null; }

const selectEditor = () => {
if (isRaw) {
return (
<RawEditor
editorRef={editorRef}
text={blockValue.data.data}
/>
);
}
return (
<Editor
{...hooks.editorConfig({
setEditorRef,
blockValue,
openModal,
initializeEditor,
lmsEndpointUrl,
studioEndpointUrl,
setSelection: imageSelection.setSelection,
clearSelection: imageSelection.clearSelection,
})}
/>
);
};

return (
<EditorContainer
getContent={hooks.getContent({ editorRef })}
getContent={hooks.getContent({ editorRef, isRaw })}
onClose={onClose}
>
<div className="editor-body h-75 overflow-auto">
Expand All @@ -78,21 +105,7 @@ export const TextEditor = ({
screenreadertext={intl.formatMessage(messages.spinnerScreenReaderText)}
/>
</div>
)
: (
<Editor
{...hooks.editorConfig({
setEditorRef,
blockValue,
openModal,
initializeEditor,
lmsEndpointUrl,
studioEndpointUrl,
setSelection: imageSelection.setSelection,
clearSelection: imageSelection.clearSelection,
})}
/>
)}
) : (selectEditor())}
</div>

</EditorContainer>
Expand All @@ -102,6 +115,7 @@ TextEditor.defaultProps = {
blockValue: null,
lmsEndpointUrl: null,
studioEndpointUrl: null,
isRaw: null,
};
TextEditor.propTypes = {
onClose: PropTypes.func.isRequired,
Expand All @@ -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,
};
Expand All @@ -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 = {
Expand Down
7 changes: 6 additions & 1 deletion src/editors/containers/TextEditor/index.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 } })),
Expand All @@ -80,6 +81,7 @@ describe('TextEditor', () => {
blockFailed: false,
blockFinished: true,
initializeEditor: jest.fn().mockName('args.intializeEditor'),
isRaw: false,
// inject
intl: { formatMessage },
};
Expand All @@ -95,6 +97,9 @@ describe('TextEditor', () => {
test('not yet loaded, Spinner appears', () => {
expect(shallow(<TextEditor {...props} blockFinished={false} />)).toMatchSnapshot();
});
test('loaded, raw editor', () => {
expect(shallow(<TextEditor {...props} isRaw />)).toMatchSnapshot();
});
test('block failed to load, Toast is shown', () => {
expect(shallow(<TextEditor {...props} blockFailed />)).toMatchSnapshot();
});
Expand Down
3 changes: 2 additions & 1 deletion src/editors/data/constants/requests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
});
5 changes: 5 additions & 0 deletions src/editors/data/redux/app/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const initialState = {
blockValue: null,
unitUrl: null,
blockContent: null,
studioView: null,
saveResponse: null,

blockId: null,
Expand Down Expand Up @@ -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 }),
Expand Down
1 change: 1 addition & 0 deletions src/editors/data/redux/app/reducer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ describe('app reducer', () => {
};
[
['setUnitUrl', 'unitUrl'],
['setStudioView', 'studioView'],
['setBlockContent', 'blockContent'],
['setBlockTitle', 'blockTitle'],
['setSaveResponse', 'saveResponse'],
Expand Down
Loading

0 comments on commit b047f7a

Please sign in to comment.