From c65f60ec105181b78f14bba896325235b7c29588 Mon Sep 17 00:00:00 2001 From: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> Date: Tue, 28 Feb 2023 16:37:52 -0500 Subject: [PATCH] feat: refactor tinymce editor to sharedComponents (#255) --- .../containers/EditorContainer/index.jsx | 2 +- .../__snapshots__/index.test.jsx.snap | 16 +- .../__snapshots__/index.test.jsx.snap | 21 +- .../EditProblemView/QuestionWidget/index.jsx | 57 ++-- .../QuestionWidget/index.test.jsx | 35 +- .../QuestionWidget/messages.js | 5 + .../SwitchToAdvancedEditorCard.jsx | 3 +- .../SwitchToAdvancedEditorCard.test.jsx.snap | 2 +- .../components/EditProblemView/index.jsx | 1 + .../components/EditProblemView/index.test.jsx | 5 +- src/editors/containers/ProblemEditor/hooks.js | 73 ----- .../containers/ProblemEditor/hooks.test.js | 96 ------ .../containers/ProblemEditor/index.jsx | 10 +- .../containers/ProblemEditor/index.test.jsx | 23 +- .../__snapshots__/index.test.jsx.snap | 282 +++------------- src/editors/containers/TextEditor/hooks.js | 172 ---------- .../containers/TextEditor/hooks.test.jsx | 218 +------------ src/editors/containers/TextEditor/index.jsx | 66 +--- .../containers/TextEditor/index.test.jsx | 38 +-- src/editors/data/constants/tinyMCE.js | 4 + .../__snapshots__/index.test.jsx.snap} | 2 +- .../BaseModal/index.jsx} | 0 .../BaseModal/index.test.jsx} | 2 +- .../BaseModal}/messages.js | 2 +- .../ImageSettingsModal/AltTextControls.jsx | 0 .../AltTextControls.test.jsx | 2 +- .../ImageSettingsModal/DimensionControls.jsx | 0 .../DimensionControls.test.jsx | 2 +- .../AltTextControls.test.jsx.snap | 0 .../DimensionControls.test.jsx.snap | 0 .../__snapshots__/index.test.jsx.snap | 0 .../ImageSettingsModal/hooks.js | 2 +- .../ImageSettingsModal/hooks.test.js | 4 +- .../ImageSettingsModal/index.jsx | 4 +- .../ImageSettingsModal/index.scss | 0 .../ImageSettingsModal/index.test.jsx | 2 +- .../ImageSettingsModal/messages.js | 0 .../SelectImageModal/Gallery.jsx | 4 +- .../SelectImageModal/Gallery.test.jsx | 8 +- .../SelectImageModal/GalleryCard.jsx | 0 .../SelectImageModal/GalleryCard.test.jsx | 0 .../SelectImageModal/SearchSort.jsx | 0 .../SelectImageModal/SearchSort.test.jsx | 2 +- .../__snapshots__/Gallery.test.jsx.snap | 0 .../__snapshots__/GalleryCard.test.jsx.snap | 0 .../__snapshots__/SearchSort.test.jsx.snap | 0 .../__snapshots__/index.test.jsx.snap | 0 .../SelectImageModal/hooks.js | 2 +- .../SelectImageModal/hooks.test.js | 8 +- .../SelectImageModal/index.jsx | 14 +- .../SelectImageModal/index.test.jsx | 22 +- .../SelectImageModal/messages.js | 0 .../SelectImageModal/utils.js | 2 +- .../__snapshots__/index.test.jsx.snap} | 0 .../ImageUploadModal/index.jsx} | 6 +- .../ImageUploadModal/index.test.jsx} | 10 +- .../__snapshots__/index.test.jsx.snap | 0 .../SourceCodeModal/hooks.js | 0 .../SourceCodeModal/hooks.test.js | 0 .../SourceCodeModal/index.jsx | 2 +- .../SourceCodeModal/index.test.jsx | 2 +- .../SourceCodeModal/messages.js | 0 .../__snapshots__/index.test.jsx.snap | 152 +++++++++ .../sharedComponents/TinyMceWidget/hooks.js | 294 +++++++++++++++++ .../TinyMceWidget/hooks.test.js | 301 ++++++++++++++++++ .../sharedComponents/TinyMceWidget/index.jsx | 86 +++++ .../TinyMceWidget/index.test.jsx | 76 +++++ .../TinyMceWidget/pluginConfig.js} | 44 +-- 68 files changed, 1172 insertions(+), 1014 deletions(-) rename src/editors/{containers/TextEditor/components/__snapshots__/BaseModal.test.jsx.snap => sharedComponents/BaseModal/__snapshots__/index.test.jsx.snap} (93%) rename src/editors/{containers/TextEditor/components/BaseModal.jsx => sharedComponents/BaseModal/index.jsx} (100%) rename src/editors/{containers/TextEditor/components/BaseModal.test.jsx => sharedComponents/BaseModal/index.test.jsx} (92%) rename src/editors/{containers/TextEditor/components => sharedComponents/BaseModal}/messages.js (72%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/ImageSettingsModal/AltTextControls.jsx (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/ImageSettingsModal/AltTextControls.test.jsx (94%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/ImageSettingsModal/DimensionControls.jsx (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/ImageSettingsModal/DimensionControls.test.jsx (95%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/ImageSettingsModal/__snapshots__/AltTextControls.test.jsx.snap (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/ImageSettingsModal/__snapshots__/DimensionControls.test.jsx.snap (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/ImageSettingsModal/__snapshots__/index.test.jsx.snap (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/ImageSettingsModal/hooks.js (99%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/ImageSettingsModal/hooks.test.js (99%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/ImageSettingsModal/index.jsx (96%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/ImageSettingsModal/index.scss (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/ImageSettingsModal/index.test.jsx (95%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/ImageSettingsModal/messages.js (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/Gallery.jsx (94%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/Gallery.test.jsx (89%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/GalleryCard.jsx (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/GalleryCard.test.jsx (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/SearchSort.jsx (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/SearchSort.test.jsx (96%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/__snapshots__/Gallery.test.jsx.snap (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/__snapshots__/GalleryCard.test.jsx.snap (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/__snapshots__/SearchSort.test.jsx.snap (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/__snapshots__/index.test.jsx.snap (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/hooks.js (98%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/hooks.test.js (98%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/index.jsx (86%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/index.test.jsx (81%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/messages.js (100%) rename src/editors/{containers/TextEditor/components => sharedComponents/ImageUploadModal}/SelectImageModal/utils.js (95%) rename src/editors/{containers/TextEditor/components/__snapshots__/ImageUploadModal.test.jsx.snap => sharedComponents/ImageUploadModal/__snapshots__/index.test.jsx.snap} (100%) rename src/editors/{containers/TextEditor/components/ImageUploadModal.jsx => sharedComponents/ImageUploadModal/index.jsx} (95%) rename src/editors/{containers/TextEditor/components/ImageUploadModal.test.jsx => sharedComponents/ImageUploadModal/index.test.jsx} (95%) rename src/editors/{containers/TextEditor/components => sharedComponents}/SourceCodeModal/__snapshots__/index.test.jsx.snap (100%) rename src/editors/{containers/TextEditor/components => sharedComponents}/SourceCodeModal/hooks.js (100%) rename src/editors/{containers/TextEditor/components => sharedComponents}/SourceCodeModal/hooks.test.js (100%) rename src/editors/{containers/TextEditor/components => sharedComponents}/SourceCodeModal/index.jsx (95%) rename src/editors/{containers/TextEditor/components => sharedComponents}/SourceCodeModal/index.test.jsx (93%) rename src/editors/{containers/TextEditor/components => sharedComponents}/SourceCodeModal/messages.js (100%) create mode 100644 src/editors/sharedComponents/TinyMceWidget/__snapshots__/index.test.jsx.snap create mode 100644 src/editors/sharedComponents/TinyMceWidget/hooks.js create mode 100644 src/editors/sharedComponents/TinyMceWidget/hooks.test.js create mode 100644 src/editors/sharedComponents/TinyMceWidget/index.jsx create mode 100644 src/editors/sharedComponents/TinyMceWidget/index.test.jsx rename src/editors/{data/constants/tinyMCEConfig.js => sharedComponents/TinyMceWidget/pluginConfig.js} (66%) diff --git a/src/editors/containers/EditorContainer/index.jsx b/src/editors/containers/EditorContainer/index.jsx index 16fc89d73..9cc96572d 100644 --- a/src/editors/containers/EditorContainer/index.jsx +++ b/src/editors/containers/EditorContainer/index.jsx @@ -11,7 +11,7 @@ import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/ import EditorFooter from './components/EditorFooter'; import TitleHeader from './components/TitleHeader'; import * as hooks from './hooks'; -import BaseModal from '../TextEditor/components/BaseModal'; +import BaseModal from '../../sharedComponents/BaseModal'; import messages from './messages'; export const EditorContainer = ({ diff --git a/src/editors/containers/ProblemEditor/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/__snapshots__/index.test.jsx.snap index 7cd7c9768..f1383f015 100644 --- a/src/editors/containers/ProblemEditor/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/__snapshots__/index.test.jsx.snap @@ -1,6 +1,18 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ProblemEditor snapshots block not yet loaded, Spinner appears 1`] = ` +exports[`ProblemEditor snapshots assets loaded, block and studio view not yet loaded, Spinner appears 1`] = ` +
+ +
+`; + +exports[`ProblemEditor snapshots block loaded, studio view and assets not yet loaded, Spinner appears 1`] = `
@@ -36,7 +48,7 @@ exports[`ProblemEditor snapshots renders as expected with default behavior 1`] =
`; -exports[`ProblemEditor snapshots studio view not yet loaded, Spinner appears 1`] = ` +exports[`ProblemEditor snapshots studio view loaded, block and assets not yet loaded, Spinner appears 1`] = `
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/__snapshots__/index.test.jsx.snap index 5ea0c51e1..206c3ca41 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/__snapshots__/index.test.jsx.snap @@ -13,15 +13,18 @@ exports[`QuestionWidget render snapshot: renders correct default 1`] = ` id="authoring.questionwidget.question.questionWidgetTitle" />
- `; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx index 8786180b5..076e0de05 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.jsx @@ -1,51 +1,44 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Editor } from '@tinymce/tinymce-react'; import { connect } from 'react-redux'; -import { injectIntl, FormattedMessage } from '@edx/frontend-platform/i18n'; +import { injectIntl, FormattedMessage, intlShape } from '@edx/frontend-platform/i18n'; import * as hooks from '../../../hooks'; import { selectors, actions } from '../../../../../data/redux'; import { messages } from './messages'; import './index.scss'; -import 'tinymce'; -import 'tinymce/themes/silver'; -import 'tinymce/skins/ui/oxide/skin.css'; -import 'tinymce/icons/default'; -import 'tinymce/plugins/link'; -import 'tinymce/plugins/lists'; -import 'tinymce/plugins/table'; -import 'tinymce/plugins/hr'; -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'; -import 'tinymce/plugins/image'; -import 'tinymce/plugins/imagetools'; +import TinyMceWidget from '../../../../../sharedComponents/TinyMceWidget'; export const QuestionWidget = ({ // redux isLibrary, question, updateQuestion, + assets, + lmsEndpointUrl, + studioEndpointUrl, + // injected + intl, }) => { - const { refReady, setEditorRef } = hooks.prepareEditorRef(); + const { editorRef, refReady, setEditorRef } = hooks.prepareEditorRef(); if (!refReady) { return null; } return (
-
); @@ -53,17 +46,25 @@ export const QuestionWidget = ({ QuestionWidget.defaultProps = { isLibrary: null, + assets: null, }; QuestionWidget.propTypes = { // redux isLibrary: PropTypes.bool, + lmsEndpointUrl: PropTypes.string.isRequired, + studioEndpointUrl: PropTypes.string.isRequired, + assets: PropTypes.shape({}), question: PropTypes.string.isRequired, updateQuestion: PropTypes.func.isRequired, + // injected + intl: intlShape.isRequired, }; - export const mapStateToProps = (state) => ({ + assets: selectors.app.assets(state), isLibrary: selectors.app.isLibrary(state), question: selectors.problem.question(state), + lmsEndpointUrl: selectors.app.lmsEndpointUrl(state), + studioEndpointUrl: selectors.app.studioEndpointUrl(state), }); export const mapDispatchToProps = { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.test.jsx index 51660e389..10e60fa67 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/index.test.jsx @@ -1,6 +1,7 @@ /* eslint-disable react/prop-types */ import React from 'react'; import { shallow } from 'enzyme'; +import { formatMessage } from '../../../../../../testUtils'; import { actions, selectors } from '../../../../../data/redux'; import { QuestionWidget, mapStateToProps, mapDispatchToProps } from '.'; @@ -13,6 +14,9 @@ jest.mock('../../../../../data/redux', () => ({ selectors: { app: { isLibrary: jest.fn(state => ({ isLibrary: state })), + lmsEndpointUrl: jest.fn(state => ({ lmsEndpointUrl: state })), + studioEndpointUrl: jest.fn(state => ({ studioEndpointUrl: state })), + assets: jest.fn(state => ({ assets: state })), }, problem: { question: jest.fn(state => ({ question: state })), @@ -20,17 +24,6 @@ jest.mock('../../../../../data/redux', () => ({ }, })); -// Per https://github.com/tinymce/tinymce-react/issues/91 React unit testing in JSDOM is not supported by tinymce. -// Consequently, mock the Editor out. -jest.mock('@tinymce/tinymce-react', () => { - const originalModule = jest.requireActual('@tinymce/tinymce-react'); - return { - __esModule: true, - ...originalModule, - Editor: () => 'TiNYmCE EDitOR', - }; -}); - jest.mock('../../../hooks', () => ({ prepareEditorRef: jest.fn(() => ({ refReady: true, @@ -44,6 +37,11 @@ describe('QuestionWidget', () => { isLibrary: false, question: 'This is my question', updateQuestion: jest.fn(), + lmsEndpointUrl: 'sOmEvaLue.cOm', + studioEndpointUrl: 'sOmEoThERvaLue.cOm', + assets: {}, + // injected + intl: { formatMessage }, }; describe('render', () => { test('snapshot: renders correct default', () => { @@ -55,9 +53,24 @@ describe('QuestionWidget', () => { test('isLibrary from app.isLibrary', () => { expect(mapStateToProps(testState).isLibrary).toEqual(selectors.app.isLibrary(testState)); }); + test('assets from app.assets', () => { + expect( + mapStateToProps(testState).assets, + ).toEqual(selectors.app.assets(testState)); + }); test('question from problem.question', () => { expect(mapStateToProps(testState).question).toEqual(selectors.problem.question(testState)); }); + test('lmsEndpointUrl from app.lmsEndpointUrl', () => { + expect( + mapStateToProps(testState).lmsEndpointUrl, + ).toEqual(selectors.app.lmsEndpointUrl(testState)); + }); + test('studioEndpointUrl from app.studioEndpointUrl', () => { + expect( + mapStateToProps(testState).studioEndpointUrl, + ).toEqual(selectors.app.studioEndpointUrl(testState)); + }); }); describe('mapDispatchToProps', () => { test('updateField from actions.problem.updateQuestion', () => { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/messages.js b/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/messages.js index 2d96f56e1..48354a96d 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/messages.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/QuestionWidget/messages.js @@ -4,6 +4,11 @@ export const messages = { defaultMessage: 'Question', description: 'Question Title', }, + placeholder: { + id: 'authoring.problemEditor.questionwidget.placeholder', + defaultMessage: 'Enter your question', + description: 'Placeholder text for tinyMCE editor', + }, }; export default messages; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx index 03882ee8d..3b00cdab7 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/SwitchToAdvancedEditorCard.jsx @@ -5,7 +5,7 @@ import { Card } from '@edx/paragon'; import PropTypes from 'prop-types'; import messages from '../messages'; import { thunkActions } from '../../../../../../data/redux'; -import BaseModal from '../../../../../TextEditor/components/BaseModal'; +import BaseModal from '../../../../../../sharedComponents/BaseModal'; import Button from '../../../../../../sharedComponents/Button'; import { confirmSwitchToAdvancedEditor } from '../hooks'; import { ProblemTypeKeys } from '../../../../../../data/constants/problem'; @@ -27,6 +27,7 @@ export const SwitchToAdvancedEditorCard = ({ confirmAction={( diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/SwitchToAdvancedEditorCard.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/SwitchToAdvancedEditorCard.test.jsx.snap index dc1e81250..2ecc2a26c 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/SwitchToAdvancedEditorCard.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/SwitchToAdvancedEditorCard.test.jsx.snap @@ -11,7 +11,7 @@ exports[`SwitchToAdvancedEditorCard snapshot snapshot: SwitchToAdvancedEditorCar className={null} onClick={[Function]} text={null} - variant="default" + variant="primary" > { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx index 05e934a07..54f1c5bfb 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/index.test.jsx @@ -6,7 +6,10 @@ import RawEditor from '../../../../sharedComponents/RawEditor'; describe('EditorProblemView component', () => { test('renders simple view', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); expect(wrapper.find(AnswerWidget).length).toBe(1); }); diff --git a/src/editors/containers/ProblemEditor/hooks.js b/src/editors/containers/ProblemEditor/hooks.js index da61935c7..fcdd28863 100644 --- a/src/editors/containers/ProblemEditor/hooks.js +++ b/src/editors/containers/ProblemEditor/hooks.js @@ -2,85 +2,12 @@ import { useRef, useCallback, useState, useEffect, } from 'react'; import { StrictDict } from '../../utils'; -import tinyMCEConfig from '../../data/constants/tinyMCEConfig'; import * as module from './hooks'; export const state = StrictDict({ refReady: (val) => useState(val), }); -export const parseContentForLabels = ({ editor, updateQuestion }) => { - let content = editor.getContent(); - if (content && content?.length > 0) { - const parsedLabels = content.split(/