docs: ADR for text editor decisions May 2022
Mostly how to use and extended tinyMCE
This commit is contained in:
85
docs/decisions/0002-text-editor-adr.md
Normal file
85
docs/decisions/0002-text-editor-adr.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Status
|
||||
|
||||
Accepted
|
||||
|
||||
# Context
|
||||
|
||||
We seek to hoist the lecacy text (HTML) text editor out of the monolith into a decoupled React application.
|
||||
|
||||
The legacy editor delegates the bulk of its operations to the tinyMCE editor
|
||||
|
||||
|
||||
|
||||
# Decision set (each decision small)
|
||||
|
||||
1. Rely on editor-wide Blockstore networking utilities for loading from, and saving to, XBlock content
|
||||
2. Continue using the tinyMCE editor
|
||||
3. Wrap the tinyMCE editor so as to be able to handle it as a vanilla React component
|
||||
4. Use a mix of configuration and tinyMCE API calls to customize the editor as needed
|
||||
* (e.g., offer image upload and image gallery selection)
|
||||
|
||||
|
||||
|
||||
# Consequences
|
||||
|
||||
## Rely on editor-wide BlockStore networking decision
|
||||
|
||||
In the [frontend-lib-content-components repo](https://github.com/edx/frontend-lib-content-components), look at`src/editors/data/redux/thunkActions`. There, `requests.js` provides a `fetchBlock()` method that encapsulates all back-end logic for retrieving a block by id from BlockStore. A matching `saveBlock()` method is used to save changes, post-editing. The states associated with BlockStore requests are `startRequest`, `completeRequest`, and `failRequest`.
|
||||
|
||||
(drilling down, per Ben Warzeski) "Requests is a generalized interface I use to track the status of ALL network requests in that redux needs to track in the App, and is not limited to the BlockStore commms.
|
||||
The "states" associated are [inactive, pending, completed, failed], as defined in `src/editors/data/constants/requests`.
|
||||
Also in that file is a list of request categories being tracked in the requests store.
|
||||
|
||||
The methods in `thunkActions/requests.js` wrap api calls in a custom networkRequest wrapper to track their state. The individual api methods are all defined in `data/services/cms/api`.
|
||||
The goal of the `requests` thunkActions is to route the appropriate store data to the api request based on how they are being called, as well as.
|
||||
The actual chain the code goes through is
|
||||
`thunkActions/app:saveBlock` -> `thunkActions/requests:saveBlock` -> `services/cms/api:saveBlock`
|
||||
* The "app" thunk action updates the local block content, and then dispatches the request thunkAction
|
||||
* The "request" thunkAction then loads relevant redux data for the save event and calls the api.saveBlock method, wrapped such that the UI can track the request state
|
||||
* The "api" method provides the specifics for the actual network request, including prop format and url."
|
||||
|
||||
## Continue using the tinyMCE editor
|
||||
|
||||
Future customizations might not be possible if look-and-feel configurations offered by tinyMCE prove inadequate, or if the tinyMCE API falls short. For current requirements this was not a showstopper.
|
||||
|
||||
|
||||
# Wrap the tinyMCE editor for React use
|
||||
|
||||
Wrapping of the tinyMCE editor to behave like a react component is done by the simple expedient of having `TextEditor.jsx` import the [tinymce/tinymce-react](https://github.com/tinymce/tinymce-react) repo and then interacting with the Editor component defined there.
|
||||
|
||||
Interaction with the tinyMCE editor is via a reference, saved at editor initialization.
|
||||
|
||||
(per Ben Warzeski) "When wrapping the tinyMCE component we assign a `ref` to the created component so that we can access its state and apis to update the content and draw it back out.
|
||||
|
||||
* initialize with html content from xblock
|
||||
|
||||
|
||||
# Editor customization
|
||||
|
||||
(More color on this, per Ben Warzeski) "Because the tinyMCE instance is the core of the TextEditor experience (pretty much the whole thing), and it is not a custom-configuratble UI, the configuration hook provides much of our control of the UI.
|
||||
However, because TinyMCE provides extensible controls, we have a few internal-written widgets we have embedded into the workflow, specifically including the image upload/settings modal.
|
||||
|
||||
|
||||
Much of the editor configuration is actually defined in `TextEditor/pluginConfig.js`, and the editorHook mostly draws from this configuration to populate the editor."
|
||||
|
||||
## Image upload/gallery selection
|
||||
The tinyMCE editor is extended to offer an image upload/select button. When the end-user pushes this button, the editor's window is occluded by a modal dialog capable of uploading an image. The end-user can also interact with this modal window to toggle between uploading a new image, or selecting from among previously uploaded images (aka, the image gallery), to save, or to cancel out.
|
||||
|
||||
On its initialization, the tinyMCE editor is provided with an icon and a callback to associate with the image upload button.
|
||||
|
||||
(per Ben Warzeski)
|
||||
* "Tiny MCE needs a "configured" button to open the modal dialog to select/upload an image.
|
||||
* This verbiage is somewhat important distinction, because this is a configuration, not a coding feature"
|
||||
|
||||
Later, on invocation of the modal dialog, this window is initialized with a reference to the tinyMCE editor.
|
||||
If the modal window is driven to a save operation on an uploaded or a selected image, the window uses this reference to provide an image tag and image metadata to the tinyMCE editor, for inclusion at the current cursor location.
|
||||
|
||||
(per Ben Warzeski) "The wrapping modal around upload/settings has a hook that calls the editor execCommand to insert the image tag on a button click.
|
||||
The hook runs before everything renders and produces a button callback that will save a passed image to the (_sic_ tinyMCE) editor context
|
||||
|
||||
* [on image upload or gallery selection] insert image tag at cursor location with source, dimensions, and alt text
|
||||
* [on image update] Update and replace a given image tag selection with a new one, updating source, dimensions, and/or alt-text
|
||||
* Update and replace are utilizing exposed tinyMCE editor api accessed from the ref associated with the created component.
|
||||
* Modal must have a "Save" option that inserts appropriately formatted tags into the tinyMCE editor context.
|
||||
* Does not always update on relinquishing control, and communicates nothing on cancel"
|
||||
|
||||
Reference in New Issue
Block a user