feat: add plugins including image-upload (#12)
This adds the image upload "plugin" and all the desired built-in plugins. Per the following two tickets https://openedx.atlassian.net/browse/TNL-9367 https://openedx.atlassian.net/browse/TNL-9475 I need to add the requisite plugins, as well as a framework for the image upload modal.
This commit is contained in:
@@ -56,6 +56,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@tinymce/tinymce-react": "^3.13.0",
|
||||
"tinymce": "^5.10.2",
|
||||
"babel-polyfill": "6.26.0",
|
||||
"react-responsive": "8.2.0",
|
||||
"react-transition-group": "4.4.2"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ModalDialog } from '@edx/paragon';
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import TextEditor from './TextEditor/TextEditor';
|
||||
import VideoEditor from './VideoEditor/VideoEditor';
|
||||
@@ -41,20 +40,17 @@ export default function EditorPage({
|
||||
blockId={blockId}
|
||||
studioEndpointUrl={studioEndpointUrl}
|
||||
>
|
||||
<ModalDialog
|
||||
title={blockType}
|
||||
isOpen
|
||||
size="fullscreen"
|
||||
onClose={() => {}}
|
||||
hasCloseButton={false}
|
||||
variant="dark"
|
||||
>
|
||||
<div className="d-flex flex-column vh-100">
|
||||
<div className="d-flex flex-column vh-100">
|
||||
<div
|
||||
className="pgn__modal-fullscreen"
|
||||
role="dialog"
|
||||
aria-label={blockType}
|
||||
>
|
||||
<EditorHeader title={blockType} />
|
||||
{selectEditor(blockType)}
|
||||
<EditorFooter />
|
||||
</div>
|
||||
</ModalDialog>
|
||||
</div>
|
||||
</EditorPageProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ModalDialog, ActionRow, Button } from '@edx/paragon';
|
||||
|
||||
const ImageUploadModal = ({ isOpen, close }) => (
|
||||
<ModalDialog
|
||||
title="My dialog"
|
||||
isOpen={isOpen}
|
||||
onClose={close}
|
||||
size="lg"
|
||||
variant="default"
|
||||
hasCloseButton
|
||||
isFullscreenOnMobile
|
||||
>
|
||||
<ModalDialog.Header>
|
||||
<ModalDialog.Title>
|
||||
Im a dialog box
|
||||
</ModalDialog.Title>
|
||||
</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<p>
|
||||
Im baby palo santo ugh celiac fashion axe.
|
||||
La croix lo-fi venmo whatever.
|
||||
Beard man braid migas single-origin coffee forage ramps.
|
||||
Tumeric messenger bag bicycle rights wayfarers, try-hard cronut blue bottle health goth.
|
||||
Sriracha tumblr cardigan, cloud bread succulents tumeric copper mug marfa semiotics woke next
|
||||
level organic roof party +1 try-hard.
|
||||
</p>
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<ActionRow>
|
||||
<ModalDialog.CloseButton variant="tertiary">
|
||||
Cancel
|
||||
</ModalDialog.CloseButton>
|
||||
<Button variant="primary">
|
||||
A button
|
||||
</Button>
|
||||
</ActionRow>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
);
|
||||
|
||||
ImageUploadModal.propTypes = {
|
||||
isOpen: PropTypes.bool,
|
||||
close: PropTypes.func,
|
||||
};
|
||||
ImageUploadModal.defaultProps = {
|
||||
isOpen: false,
|
||||
close: () => {},
|
||||
};
|
||||
export default ImageUploadModal;
|
||||
@@ -1,17 +1,36 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { Spinner, Toast } from '@edx/paragon';
|
||||
import { Editor } from '@tinymce/tinymce-react';
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import {
|
||||
useToggle, Spinner, Toast,
|
||||
} from '@edx/paragon';
|
||||
import EditorPageContext from '../EditorPageContext';
|
||||
import { ActionStates } from '../data/constants';
|
||||
import ImageUploadModal from './ImageUpload/Wizard/ImageUploadModal';
|
||||
|
||||
import 'tinymce';
|
||||
import 'tinymce/themes/silver';
|
||||
import 'tinymce/skins/ui/oxide/skin.css';
|
||||
import 'tinymce/icons/default';
|
||||
import 'tinymce/plugins/link';
|
||||
import 'tinymce/plugins/table';
|
||||
import 'tinymce/plugins/codesample';
|
||||
import 'tinymce/plugins/emoticons';
|
||||
import 'tinymce/plugins/emoticons/js/emojis';
|
||||
import 'tinymce/plugins/charmap';
|
||||
import 'tinymce/plugins/code';
|
||||
import 'tinymce/plugins/autoresize';
|
||||
|
||||
const TextEditor = () => {
|
||||
const {
|
||||
blockValue, blockError, blockLoading, editorRef,
|
||||
} = useContext(EditorPageContext);
|
||||
|
||||
const [isImageUploadModalOpen, openUploadModal, closeUploadModal] = useToggle(false);
|
||||
|
||||
return (
|
||||
<div className="editor-body h-75">
|
||||
<ImageUploadModal isOpen={isImageUploadModalOpen} close={closeUploadModal} />
|
||||
<Toast show={blockError != null} onClose={() => {}}>
|
||||
<FormattedMessage
|
||||
id="authoring.texteditor.load.error"
|
||||
@@ -27,24 +46,27 @@ const TextEditor = () => {
|
||||
)
|
||||
: (
|
||||
<Editor
|
||||
onInit={(evt, editor) => { editorRef.current = editor; }}
|
||||
onInit={(evt, editor) => {
|
||||
editorRef.current = editor;
|
||||
}}
|
||||
initialValue={blockValue ? blockValue.data.data : ''}
|
||||
init={{
|
||||
height: '100%',
|
||||
setup: (editor) => {
|
||||
editor.ui.registry.addButton('imageuploadbutton', {
|
||||
icon: 'image',
|
||||
onAction: () => openUploadModal(),
|
||||
});
|
||||
},
|
||||
plugins: 'link codesample emoticons table charmap code autoresize',
|
||||
menubar: false,
|
||||
plugins: [
|
||||
'advlist autolink lists link image charmap print preview anchor',
|
||||
'searchreplace visual blocks code fullscreen',
|
||||
'insertdatetime media table paste code help wordcount',
|
||||
'autoresize',
|
||||
],
|
||||
toolbar: 'undo redo | formatselect | '
|
||||
+ 'bold italic backcolor | alignleft aligncenter '
|
||||
+ 'alignright alignjustify | bullist numlist outdent indent | '
|
||||
+ 'removeformat | help',
|
||||
+ 'alignright alignjustify | bullist numlist outdent indent |'
|
||||
+ 'imageuploadbutton | link | emoticons | table | codesample | charmap |'
|
||||
+ 'removeformat | hr |code',
|
||||
height: '100%',
|
||||
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
|
||||
max_height: 900,
|
||||
min_height: 700,
|
||||
min_height: 1000,
|
||||
branding: false,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -6,6 +6,22 @@ import 'babel-polyfill';
|
||||
|
||||
Enzyme.configure({ adapter: new Adapter() });
|
||||
|
||||
/* need to mock window for tinymce on import, as it is JSDOM incompatible */
|
||||
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation(query => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // Deprecated
|
||||
removeListener: jest.fn(), // Deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
});
|
||||
|
||||
// These configuration values are usually set in webpack's EnvironmentPlugin however
|
||||
// Jest does not use webpack so we need to set these so for testing
|
||||
process.env.ACCESS_TOKEN_COOKIE_NAME = 'edx-jwt-cookie-header-payload';
|
||||
|
||||
Reference in New Issue
Block a user