Skip to content

Commit

Permalink
upgrade: tiptap v2
Browse files Browse the repository at this point in the history
Migrate the entire editor to tiptap v2.

Some changes were introduces that go beyond just using the new tiptap API:

*Collaboration*

Port tiptap1 collaboration.
We still want to use our session and sync mechanism.

*Serialization*

Add Markdown extension to handle serialization.
Tiptap config extensions are not automatically added
to the prosemirror schema anymore.
The extension adds the `toMarkdown` config value to the prosemirror schema.

With the new naming scheme tiptap nodes for a number of elements
do not match the prosemirror names.
Camelcase the marks and nodes from `defaultMarkdownSerializer`
so they match the tiptap names.

*Menubar*

* Specify args for isActive function directly rather than setting a function.
* Make use of the editor instance inside the MenuBar component.
* Use the editor rather than slots for command, focused etc.
* disable icons based on editor.can
* Show menubar as long as submenus are open.
  When opening a submenu of the menubar
  keep track of the open menu even for the image and the remaining action menu.
  Also refocus the editor whenever a submenu is closed.

*MenuBubble*

Let tippy handle the positioning

Tippy is really good at positioning the menu bubble.
Remove all our workarounds and let it do its thing.

In order for this to work the content of the MenuBubble
actually needs to live inside the tippy-content.

Tippy bases its calculations on the width of tippy-content.
So if we have the content hanging in a separate div
with absolute positioning
tippy-content will be 0x0 px and not represent the actual width
of the menu bubble.

*Upgrade image node and ImageView.*

Quite a bit of the syntax changed.
We now need a wrapping `<node-view-wrapper>` element.
Pretty good docs at https://tiptap.dev/guide/node-views/vue#render-a-vue-component

We also need to handle the async action.
It will run the action on it's own.
So in `clickIcon()` we need to test if the action returned anything.

Tiptap v1 had inline images.
v2 keeps them outside of paragraphs by default.
Configure Image node to use inline images as markdownit creates inline images right now.

*Trailing Node*

Tiptap v2 does not ship the trailing node extension anymore.
Included the one from the demos and turned it from typescript into javascript.

*Tests*

In order to isolate some problems tests were added.
The tests in Undeline.spec.js were green right from the beginning.
They are not related to the fix and only helped isolate the problem.

Also introduced a cypress test for Lists
that tests the editor
without rendering the page and logging in.
It is very fast and fairly easy to write.

*Refactorings*

* Split marks into separate files.

Signed-off-by: Max <max@nextcloud.com>
  • Loading branch information
azul authored and max-nextcloud committed Feb 9, 2022
1 parent 3c9f8e7 commit edfbb70
Show file tree
Hide file tree
Showing 61 changed files with 2,432 additions and 1,297 deletions.
6 changes: 3 additions & 3 deletions css/prosemirror.scss
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,13 @@ div.ProseMirror {
}

.editor__content p.is-empty:first-child::before {
content: attr(data-empty-text);
content: attr(data-placeholder);
float: left;
color: var(--color-text-maxcontrast);
pointer-events: none;
height: 0;
}

.editor__content {
tab-size: 4;
.editor__content {
tab-size: 4;
}
67 changes: 67 additions & 0 deletions cypress/integration/ListItem.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import BulletList from './../../src/nodes/BulletList'
import ListItem from './../../src/nodes/ListItem'
import Markdown from './../../src/extensions/Markdown';
import { findChildrenByType } from 'prosemirror-utils'
import createEditor from './../../src/tests/createEditor'

describe('ListItem extension integrated in the editor', () => {

it('has attrs', () => {
const editor = createEditor({
content: '<p><ul><li>Test</li></ul></p>',
extensions: [Markdown, BulletList, ListItem]
})
const li = findListItem(editor)
expect(li.attrs).to.deep.eq({done: null, type: 0})
})

it('creates todo lists', () => {
const editor = createEditor({
content: '<p>Test</p>',
extensions: [Markdown, BulletList, ListItem]
})
editor.chain().todo_item().run()
const li = findListItem(editor)
expect(li.attrs).to.deep.eq({done: false, type: 1})
})

it('removes the list when toggling todo off', () => {
const editor = createEditor({
content: '<p>Test</p>',
extensions: [Markdown, BulletList, ListItem]
})
editor.chain().todo_item().run()
editor.chain().todo_item().run()
expect(findListItem(editor)).to.eq(undefined)
})

it('turns a bullet list into a todo list', () => {
const editor = createEditor({
content: '<p>Test</p>',
extensions: [Markdown, BulletList, ListItem]
})
editor.chain().bulletListItem().run()
editor.chain().todo_item().run()
const li = findListItem(editor)
expect(li.attrs).to.deep.eq({done: false, type: 1})
})

it('only toggles one list item', () => {
const editor = createEditor({
content: '<p><ul><li>Todo</li><li>Not to do</li></ul></p>',
extensions: [Markdown, BulletList, ListItem]
})
editor.chain().todo_item().run()
const todo = findListItem(editor, 0)
const li = findListItem(editor, 1)
expect(todo.attrs).to.deep.eq({done: false, type: 1})
expect(li.attrs).to.deep.eq({done: null, type: 0})
})

})

function findListItem(editor, index = 0) {
const doc = editor.state.doc
const type = editor.schema.nodes.listItem
return findChildrenByType(doc, type)[index]?.node;
}
3 changes: 0 additions & 3 deletions cypress/integration/share.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ describe('Open test.md in viewer', function() {
return false
})

cy.get('#fileList tr[data-file="welcome.txt"]', {timeout: 10000})
.should('contain', 'welcome.txt')

// Upload test files
cy.createFolder('folder')
cy.uploadFile('test.md', 'text/markdown', 'folder/test.md')
Expand Down
6 changes: 3 additions & 3 deletions cypress/integration/workspace.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ describe('Workspace', function() {
.type('Format me')
.type('{selectall}')
;[
['bold', 'strong'],
['italic', 'em'],
['underline', 'u'],
['bold', 'strong'],
['italic', 'em'],
['underline', 'u'],
['strike', 's']
].forEach(([button, tag]) => {
menuButton(button)
Expand Down
4 changes: 2 additions & 2 deletions js/editor-collab.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/editor-collab.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/editor-rich.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/editor-rich.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/editor.js

Large diffs are not rendered by default.

73 changes: 73 additions & 0 deletions js/editor.js.LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
/*
* @copyright Copyright (c) 2019 Azul <azul@riseup.net>
*
* @author Azul <azul@riseup.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

/*
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
*
Expand Down Expand Up @@ -42,6 +64,28 @@
*
*/

/*
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

/*
* @copyright Copyright (c) 2020 Azul <azul@riseup.net>
*
Expand Down Expand Up @@ -129,3 +173,32 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

/*
* @copyright Copyright (c) 2021, überdosis GbR
*
* @license MIT
*
*/

/*
* @copyright Copyright (c) 2022 Max <max@nextcloud.com>
*
* @author Max <max@nextcloud.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
2 changes: 1 addition & 1 deletion js/editor.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/files-modal.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion js/files-modal.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions js/highlight/json.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit edfbb70

Please sign in to comment.