Commit fe17d707 authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch '340565-aqualls-edit-editor-page' into 'master'

Editorial revisions of new content editor page

See merge request gitlab-org/gitlab!70013
parents 4b5da0c7 bacf8c83
...@@ -20,10 +20,14 @@ the native ...@@ -20,10 +20,14 @@ the native
Follow these instructions to include the Content Editor in a feature. Follow these instructions to include the Content Editor in a feature.
1. [Include the Content Editor component](#include-the-content-editor-component).
1. [Set and get Markdown](#set-and-get-markdown).
1. [Listen for changes](#listen-for-changes).
### Include the Content Editor component ### Include the Content Editor component
Import the `ContentEditor` Vue component. We recommend using asynchronous named imports to Import the `ContentEditor` Vue component. We recommend using asynchronous named imports to
take advantage of caching as the ContentEditor is a big dependency. take advantage of caching, as the ContentEditor is a big dependency.
```html ```html
<script> <script>
...@@ -43,18 +47,20 @@ The Content Editor requires two properties: ...@@ -43,18 +47,20 @@ The Content Editor requires two properties:
- `renderMarkdown` is an asynchronous function that returns the response (String) of invoking the - `renderMarkdown` is an asynchronous function that returns the response (String) of invoking the
[Markdown API](../../api/markdown.md). [Markdown API](../../api/markdown.md).
- `uploadsPath` is a URL that points to a [GitLab upload service](../uploads.md#upload-encodings) with `multipart/form-data` support. - `uploadsPath` is a URL that points to a [GitLab upload service](../uploads.md#upload-encodings)
with `multipart/form-data` support.
See the [`WikiForm.vue`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue#L207) component for a production example of these two properties. See the [`WikiForm.vue`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue#L207)
component for a production example of these two properties.
### Set and get Markdown ### Set and get Markdown
The `ContentEditor` Vue component doesn't implement Vue data binding flow (v-model) The `ContentEditor` Vue component doesn't implement Vue data binding flow (`v-model`)
because setting and getting Markdown are expensive operations. Data binding would because setting and getting Markdown are expensive operations. Data binding would
trigger these operations every time that the user interacts with the component. trigger these operations every time the user interacts with the component.
Instead, you should obtain an instance of the `ContentEditor` class by listening to the Instead, you should obtain an instance of the `ContentEditor` class by listening to the
`initialized` event. `initialized` event:
```html ```html
<script> <script>
...@@ -90,7 +96,7 @@ export default { ...@@ -90,7 +96,7 @@ export default {
### Listen for changes ### Listen for changes
You can still react to changes in the Content Editor. Reacting to changes helps You can still react to changes in the Content Editor. Reacting to changes helps
you know if the document is empty or dirty. Use `@change` event handler for you know if the document is empty or dirty. Use the `@change` event handler for
this purpose. this purpose.
```html ```html
...@@ -127,11 +133,11 @@ export default { ...@@ -127,11 +133,11 @@ export default {
The Content Editor is composed of three main layers: The Content Editor is composed of three main layers:
- The editing tools UI like the toolbar and the table structure editor. They - **The editing tools UI**, like the toolbar and the table structure editor. They
display the editor's state and mutate it by dispatching commands. display the editor's state and mutate it by dispatching commands.
- The Tiptap Editor object manages the editor's state, - **The Tiptap Editor object** manages the editor's state,
and expose business logic as commands executed by the editing tools UI. and exposes business logic as commands executed by the editing tools UI.
- The Markdown serializer transforms a Markdown source string into a ProseMirror - **The Markdown serializer** transforms a Markdown source string into a ProseMirror
document and vice versa. document and vice versa.
### Editing tools UI ### Editing tools UI
...@@ -141,7 +147,7 @@ dispatch [commands](https://www.tiptap.dev/api/commands/#commands) to mutate it. ...@@ -141,7 +147,7 @@ dispatch [commands](https://www.tiptap.dev/api/commands/#commands) to mutate it.
They are located in the `~/content_editor/components` directory. For example, They are located in the `~/content_editor/components` directory. For example,
the **Bold** toolbar button displays the editor's state by becoming active when the **Bold** toolbar button displays the editor's state by becoming active when
the user selects bold text. This button also dispatches the `toggleBold` command the user selects bold text. This button also dispatches the `toggleBold` command
to format text as bold. to format text as bold:
```mermaid ```mermaid
sequenceDiagram sequenceDiagram
...@@ -154,7 +160,7 @@ sequenceDiagram ...@@ -154,7 +160,7 @@ sequenceDiagram
#### Node views #### Node views
We implement [node views](https://www.tiptap.dev/guide/node-views/vue/#node-views-with-vue) We implement [node views](https://www.tiptap.dev/guide/node-views/vue/#node-views-with-vue)
to provide inline editing tools for some content types like tables and images. Node views to provide inline editing tools for some content types, like tables and images. Node views
allow separating the presentation of a content type from its allow separating the presentation of a content type from its
[model](https://prosemirror.net/docs/guide/#doc.data_structures). Using a Vue component in [model](https://prosemirror.net/docs/guide/#doc.data_structures). Using a Vue component in
the presentation layer enables sophisticated editing experiences in the Content Editor. the presentation layer enables sophisticated editing experiences in the Content Editor.
...@@ -163,8 +169,11 @@ Node views are located in `~/content_editor/components/wrappers`. ...@@ -163,8 +169,11 @@ Node views are located in `~/content_editor/components/wrappers`.
#### Dispatch commands #### Dispatch commands
You can inject the Tiptap Editor object to Vue components to dispatch You can inject the Tiptap Editor object to Vue components to dispatch
commands. **notice**: Do not implement logic that changes the editor's commands.
state in Vue components. Encapsulate this logic in commands and dispatch
NOTE:
Do not implement logic that changes the editor's
state in Vue components. Encapsulate this logic in commands, and dispatch
the command from the component's methods. the command from the component's methods.
```html ```html
...@@ -190,10 +199,17 @@ export default { ...@@ -190,10 +199,17 @@ export default {
#### Query editor's state #### Query editor's state
Use the `EditorStateObserver` renderless component to react to changes in the Use the `EditorStateObserver` renderless component to react to changes in the
editor's state like when the document or the selection changes. You can listen to editor's state, such as when the document or the selection changes. You can listen to
the following events: `docUpdate`, `selectionUpdate`, `transaction`, `focus`, `blur`, the following events:
and `error`. Learn more about these events in
[Tiptap's event guide](https://www.tiptap.dev/api/events/). - `docUpdate`
- `selectionUpdate`
- `transaction`
- `focus`
- `blur`
- `error`.
Learn more about these events in [Tiptap's event guide](https://www.tiptap.dev/api/events/).
```html ```html
<script> <script>
...@@ -232,8 +248,8 @@ export default { ...@@ -232,8 +248,8 @@ export default {
The Tiptap [Editor](https://www.tiptap.dev/api/editor) class manages The Tiptap [Editor](https://www.tiptap.dev/api/editor) class manages
the editor's state and encapsulates all the business logic that powers the editor's state and encapsulates all the business logic that powers
the Content Editor. The Content Editor takes care of constructing a the Content Editor. The Content Editor constructs a new instance of this class and
new instance of this class and providing all the necessary extensions to support provides all the necessary extensions to support
[GitLab Flavored Markdown](../../user/markdown.md). [GitLab Flavored Markdown](../../user/markdown.md).
#### Implement new extensions #### Implement new extensions
...@@ -244,15 +260,14 @@ We recommend checking the list of built-in [nodes](https://www.tiptap.dev/api/no ...@@ -244,15 +260,14 @@ We recommend checking the list of built-in [nodes](https://www.tiptap.dev/api/no
[marks](https://www.tiptap.dev/api/marks) before implementing a new extension [marks](https://www.tiptap.dev/api/marks) before implementing a new extension
from scratch. from scratch.
You should store the Content Editor extensions in the Store the Content Editor extensions in the `~/content_editor/extensions` directory.
`~/content_editor/extensions` directory. When using a Tiptap's built-in When using a Tiptap's built-in extension, wrap it in a ES6 module inside this directory:
extension, wrap it in a ES6 module inside this directory.
```javascript ```javascript
export { Bold as default } from '@tiptap/extension-bold'; export { Bold as default } from '@tiptap/extension-bold';
``` ```
Use the `extend` method to customize the Extension's behavior Use the `extend` method to customize the Extension's behavior:
```javascript ```javascript
import { HardBreak } from '@tiptap/extension-hard-break'; import { HardBreak } from '@tiptap/extension-hard-break';
...@@ -269,7 +284,7 @@ export default HardBreak.extend({ ...@@ -269,7 +284,7 @@ export default HardBreak.extend({
#### Register extensions #### Register extensions
Register the new extension in `~/content_editor/services/create_content_editor.js`. Import Register the new extension in `~/content_editor/services/create_content_editor.js`. Import
the extension module and add it to the `builtInContentEditorExtensions` array. the extension module and add it to the `builtInContentEditorExtensions` array:
```javascript ```javascript
import Emoji from '../extensions/emoji'; import Emoji from '../extensions/emoji';
...@@ -293,7 +308,7 @@ The Markdown Serializer transforms a Markdown String to a ...@@ -293,7 +308,7 @@ The Markdown Serializer transforms a Markdown String to a
Deserialization is the process of converting Markdown to a ProseMirror document. Deserialization is the process of converting Markdown to a ProseMirror document.
We take advantage of ProseMirror's We take advantage of ProseMirror's
[HTML parsing and serialization capabilities](https://prosemirror.net/docs/guide/#schema.serialization_and_parsing) [HTML parsing and serialization capabilities](https://prosemirror.net/docs/guide/#schema.serialization_and_parsing)
by first rendering the Markdown as HTML using the [Markdown API endpoint](../../api/markdown.md). by first rendering the Markdown as HTML using the [Markdown API endpoint](../../api/markdown.md):
```mermaid ```mermaid
sequenceDiagram sequenceDiagram
...@@ -323,7 +338,7 @@ Editor uses [`prosemirror-markdown`](https://github.com/ProseMirror/prosemirror- ...@@ -323,7 +338,7 @@ Editor uses [`prosemirror-markdown`](https://github.com/ProseMirror/prosemirror-
to serialize documents. We recommend reading the to serialize documents. We recommend reading the
[MarkdownSerializer](https://github.com/ProseMirror/prosemirror-markdown#class-markdownserializer) [MarkdownSerializer](https://github.com/ProseMirror/prosemirror-markdown#class-markdownserializer)
and [MarkdownSerializerState](https://github.com/ProseMirror/prosemirror-markdown#class-markdownserializerstate) and [MarkdownSerializerState](https://github.com/ProseMirror/prosemirror-markdown#class-markdownserializerstate)
classes documentation before implementing a serializer. classes documentation before implementing a serializer:
```mermaid ```mermaid
sequenceDiagram sequenceDiagram
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment