Skip to content

Commit 236860a

Browse files
authored
Merge pull request #5893 from nextcloud/fix/toc-without-transaction
2 parents 2a06114 + e0d10b2 commit 236860a

File tree

17 files changed

+442
-385
lines changed

17 files changed

+442
-385
lines changed

cypress/e2e/SmartPicker.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ describe('Smart picker', () => {
4242
.type('Heading{enter}Hello World{enter}')
4343

4444
cy.getContent()
45-
.find('h1 [data-node-view-content]')
46-
.should('have.text', 'Hello World')
45+
.find('h1')
46+
.should('contain.text', 'Hello World')
4747
})
4848

4949
it('Insert a link with the smart picker', () => {

cypress/e2e/conflict.spec.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ variants.forEach(function({ fixture, mime }) {
108108
})
109109
})
110110

111+
/**
112+
* @param {string} fileName - filename
113+
* @param {string} mime - mimetype
114+
*/
111115
function createConflict(fileName, mime) {
112116
cy.visit('/apps/files')
113117
cy.openFile(fileName)

cypress/e2e/initial.spec.js

Lines changed: 94 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -22,123 +22,110 @@ describe('Test state loading of documents', function() {
2222

2323
it('Initial content can not be undone', function() {
2424
cy.shareFile('/test.md', { edit: true })
25-
.then((token) => {
26-
cy.visit(`/s/${token}`)
27-
})
28-
.then(() => {
29-
cy.getEditor().should('be.visible')
30-
cy.getContent()
31-
.should('contain', 'Hello world')
32-
.find('h2').should('contain', 'Hello world')
33-
34-
cy.getMenu().should('be.visible')
35-
cy.getActionEntry('undo').should('be.disabled')
36-
37-
cy.getContent()
38-
.type('New content')
39-
cy.getActionEntry('undo').should('not.be.disabled')
40-
})
25+
.then(token => cy.visit(`/s/${token}`))
26+
cy.getEditor().should('be.visible')
27+
cy.getContent()
28+
.should('contain', 'Hello world')
29+
.find('h2').should('contain', 'Hello world')
30+
31+
cy.getMenu().should('be.visible')
32+
cy.getActionEntry('undo').should('be.disabled')
33+
34+
cy.getContent()
35+
.type('New content')
36+
cy.getActionEntry('undo').should('not.be.disabled')
4137
})
4238

4339
it('Consecutive sessions work properly', function() {
44-
let readToken = null
45-
let writeToken = null
4640
cy.interceptCreate()
4741
cy.shareFile('/test2.md')
48-
.then((token) => {
49-
readToken = token
50-
cy.logout()
51-
cy.visit(`/s/${readToken}`)
52-
cy.wait('@create')
53-
})
54-
.then(() => {
55-
// Open read only for the first time
56-
cy.getEditor().should('be.visible')
57-
cy.getContent()
58-
.should('contain', 'Hello world')
59-
.find('h2').should('contain', 'Hello world')
60-
cy.closeInterceptedSession(readToken)
61-
62-
// Open read only for the second time
63-
cy.reload()
64-
cy.getEditor().should('be.visible')
65-
cy.getContent()
66-
.should('contain', 'Hello world')
67-
.find('h2').should('contain', 'Hello world')
68-
cy.closeInterceptedSession(readToken)
69-
70-
cy.login(user)
71-
cy.shareFile('/test2.md', { edit: true })
72-
.then((token) => {
73-
writeToken = token
74-
// Open write link and edit something
75-
cy.visit(`/s/${writeToken}`)
76-
cy.getEditor().should('be.visible')
77-
cy.getContent()
78-
.should('contain', 'Hello world')
79-
.find('h2').should('contain', 'Hello world')
80-
cy.getContent()
81-
.type('Something new {end}')
82-
cy.intercept({ method: 'POST', url: '**/session/*/push' }).as('push')
83-
cy.intercept({ method: 'POST', url: '**/session/*/sync' }).as('sync')
84-
cy.wait('@push')
85-
cy.wait('@sync')
86-
cy.closeInterceptedSession(writeToken)
87-
88-
// Reopen read only link and check if changes are there
89-
cy.visit(`/s/${readToken}`)
90-
cy.getEditor().should('be.visible')
91-
cy.getContent()
92-
.find('h2').should('contain', 'Something new Hello world')
93-
})
94-
})
42+
.as('readToken')
43+
cy.logout()
44+
cy.get('@readToken')
45+
.then(token => cy.visit(`/s/${token}`))
46+
cy.wait('@create')
47+
// Open read only for the first time
48+
cy.getEditor().should('be.visible')
49+
cy.getContent()
50+
.should('contain', 'Hello world')
51+
.find('h2').should('contain', 'Hello world')
52+
cy.get('@readToken')
53+
.then(cy.closeInterceptedSession)
54+
55+
// Open read only for the second time
56+
cy.reload()
57+
cy.getEditor().should('be.visible')
58+
cy.getContent()
59+
.should('contain', 'Hello world')
60+
.find('h2').should('contain', 'Hello world')
61+
cy.get('@readToken')
62+
.then(cy.closeInterceptedSession)
63+
64+
cy.login(user)
65+
cy.shareFile('/test2.md', { edit: true })
66+
.as('writeToken')
67+
// Open write link and edit something
68+
cy.get('@writeToken')
69+
.then(token => cy.visit(`/s/${token}`))
70+
cy.getEditor().should('be.visible')
71+
cy.getContent()
72+
.should('contain', 'Hello world')
73+
.find('h2').should('contain', 'Hello world')
74+
cy.getContent()
75+
.type('Something new {end}')
76+
cy.intercept({ method: 'POST', url: '**/session/*/sync' }).as('sync')
77+
cy.wait('@sync')
78+
cy.get('@writeToken')
79+
.then(cy.closeInterceptedSession)
80+
81+
// Reopen read only link and check if changes are there
82+
cy.get('@readToken')
83+
.then(token => cy.visit(`/s/${token}`))
84+
cy.getEditor().should('be.visible')
85+
cy.getContent()
86+
.find('h2').should('contain', 'Something new Hello world')
9587
})
9688

9789
it('Load after state has been saved', function() {
98-
let readToken = null
99-
let writeToken = null
10090
cy.interceptCreate()
10191
cy.shareFile('/test3.md', { edit: true })
102-
.then((token) => {
103-
writeToken = token
104-
cy.logout()
105-
cy.visit(`/s/${writeToken}`)
106-
})
107-
.then(() => {
108-
// Open a file, write and save
109-
cy.getEditor().should('be.visible')
110-
cy.getContent()
111-
.should('contain', 'Hello world')
112-
.find('h2').should('contain', 'Hello world')
113-
cy.getContent()
114-
.type('Something new {end}')
115-
cy.intercept({ method: 'POST', url: '**/session/*/save' }).as('save')
116-
cy.get('.save-status button').click()
117-
cy.wait('@save', { timeout: 10000 })
118-
cy.closeInterceptedSession(writeToken)
119-
120-
// Open writable file again and assert the content
121-
cy.reload()
122-
cy.getEditor().should('be.visible')
123-
cy.getContent()
124-
.should('contain', 'Hello world')
125-
.find('h2').should('contain', 'Something new Hello world')
126-
127-
cy.login(user)
128-
cy.shareFile('/test3.md')
129-
.then((token) => {
130-
readToken = token
131-
cy.logout()
132-
cy.visit(`/s/${readToken}`)
133-
})
134-
.then(() => {
135-
// Open read only file again and assert the content
136-
cy.getEditor().should('be.visible')
137-
cy.getContent()
138-
.should('contain', 'Hello world')
139-
.find('h2').should('contain', 'Something new Hello world')
140-
})
141-
})
92+
.as('writeToken')
93+
cy.logout()
94+
cy.get('@writeToken')
95+
.then(token => cy.visit(`/s/${token}`))
96+
97+
// Open a file, write and save
98+
cy.getEditor().should('be.visible')
99+
cy.getContent()
100+
.should('contain', 'Hello world')
101+
.find('h2').should('contain', 'Hello world')
102+
cy.getContent()
103+
.type('Something new {end}')
104+
cy.intercept({ method: 'POST', url: '**/session/*/save' }).as('save')
105+
cy.get('.save-status button').click()
106+
cy.wait('@save', { timeout: 10000 })
107+
cy.get('@writeToken')
108+
.then(cy.closeInterceptedSession)
109+
110+
// Open writable file again and assert the content
111+
cy.reload()
112+
cy.getEditor().should('be.visible')
113+
cy.getContent()
114+
.should('contain', 'Hello world')
115+
.find('h2').should('contain', 'Something new Hello world')
116+
117+
cy.login(user)
118+
cy.shareFile('/test3.md')
119+
.as('readToken')
120+
cy.logout()
121+
cy.get('@readToken')
122+
.then(token => cy.visit(`/s/${token}`))
123+
124+
// Open read only file again and assert the content
125+
cy.getEditor().should('be.visible')
126+
cy.getContent()
127+
.should('contain', 'Hello world')
128+
.find('h2').should('contain', 'Something new Hello world')
142129
})
143130

144131
})

cypress/e2e/sections.spec.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ describe('Content Sections', () => {
5252
cy.openFile(fileName, { force: true })
5353
cy.getContent().type('# Heading 1{enter}')
5454
cy.getContent()
55-
.find('h1')
55+
.find('h1 > a')
5656
.should('have.attr', 'id')
5757
.and('equal', 'h-heading-1')
5858
cy.getContent()
@@ -61,7 +61,7 @@ describe('Content Sections', () => {
6161
.and('equal', '#h-heading-1')
6262
cy.getContent().type('{backspace}{backspace}2{enter}')
6363
cy.getContent()
64-
.find('h1')
64+
.find('h1 > a')
6565
.should('have.attr', 'id')
6666
.and('equal', 'h-heading-2')
6767
cy.getContent()
@@ -75,13 +75,13 @@ describe('Content Sections', () => {
7575
cy.visitTestFolder()
7676
cy.openFile('anchors.md')
7777
cy.getContent()
78-
.get('h2[id="h-bottom"]')
78+
.get('h2 > a[id="h-bottom"]')
7979
.should('not.be.inViewport')
8080
cy.getContent()
8181
.find('a[href="#h-bottom"]:not(.heading-anchor)')
8282
.click()
8383
cy.getContent()
84-
.get('h2[id="h-bottom"]')
84+
.get('h2 > a[id="h-bottom"]')
8585
.should('be.inViewport')
8686
})
8787

@@ -92,15 +92,15 @@ describe('Content Sections', () => {
9292
cy.getContent()
9393
.type('# Heading 1{enter}')
9494
cy.getContent()
95-
.find('h1')
95+
.find('h1 > a')
9696
.should('have.attr', 'id')
9797
.and('equal', 'h-heading-1')
9898
cy.getContent()
99-
.find('h1 [data-node-view-content]')
99+
.find('h1')
100100
.click({ force: true, position: 'center' })
101101
cy.getActionEntry('headings').click()
102102
cy.get('.v-popper__wrapper .open').getActionEntry('headings-h3').click()
103-
cy.getContent().find('h3')
103+
cy.getContent().find('h3 > a')
104104
.should('have.attr', 'id')
105105
.and('equal', 'h-heading-1')
106106
})

cypress/e2e/versions.spec.js

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,18 @@ describe('Versions', () => {
3535

3636
cy.get('[data-files-versions-versions-list] li a').eq(1).click()
3737
cy.get('.viewer__content #read-only-editor')
38-
.find('h1 [data-node-view-content]')
39-
.should('have.text', 'V2')
38+
.find('h1')
39+
.should('contain.text', 'V2')
4040

4141
cy.get('[data-files-versions-versions-list] li a').eq(2).click()
4242
cy.get('.viewer__content #read-only-editor')
43-
.find('h1 [data-node-view-content]')
44-
.should('have.text', 'V1')
43+
.find('h1')
44+
.should('contain.text', 'V1')
4545

4646
cy.get('[data-files-versions-versions-list] li a').eq(0).click()
4747
cy.getContent()
48-
.find('h1 [data-node-view-content]')
49-
.should('have.text', 'V3')
48+
.find('h1')
49+
.should('contain.text', 'V3')
5050
})
5151
})
5252

@@ -69,18 +69,18 @@ describe('Versions', () => {
6969

7070
cy.get('[data-files-versions-versions-list] li a').eq(1).click()
7171
cy.get('.viewer__content #read-only-editor')
72-
.find('h1 [data-node-view-content]')
73-
.should('have.text', 'V2')
72+
.find('h1')
73+
.should('contain.text', 'V2')
7474

7575
cy.get('[data-files-versions-versions-list] li a').eq(2).click()
7676
cy.get('.viewer__content #read-only-editor')
77-
.find('h1 [data-node-view-content]')
78-
.should('have.text', 'V1')
77+
.find('h1')
78+
.should('contain.text', 'V1')
7979

8080
cy.get('[data-files-versions-versions-list] li a').eq(0).click()
8181
cy.getContent()
82-
.find('h1 [data-node-view-content]')
83-
.should('have.text', 'V3')
82+
.find('h1')
83+
.should('contain.text', 'V3')
8484

8585
cy.getContent()
8686
.type('Hello')
@@ -114,11 +114,11 @@ describe('Versions', () => {
114114
.click()
115115

116116
cy.get('.viewer__content #read-only-editor')
117-
.find('h1 [data-node-view-content]')
118-
.should('have.text', 'V1')
117+
.find('h1')
118+
.should('contain.text', '#V1')
119119

120120
cy.get('.viewer__content .viewer__file--active .ProseMirror')
121-
.find('h1 [data-node-view-content]')
121+
.find('h1')
122122
.should('contain.text', 'V3')
123123
})
124124
})

src/components/Editor.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,10 @@ export default {
559559
this.document = document
560560
561561
this.syncError = null
562-
this.$editor.setEditable(!this.readOnly)
562+
const editable = !this.readOnly
563+
if (this.$editor.isEditable !== editable) {
564+
this.$editor.setEditable(editable)
565+
}
563566
},
564567
565568
onSync({ steps, document }) {

src/components/Editor/TableOfContents.vue

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<div data-text-el="editor-table-of-contents" :class="{ '--initial-render': initialRender }" class="editor--toc">
88
<ul class="editor--toc__list">
99
<li v-for="(heading) in headings"
10-
:key="heading.uuid"
10+
:key="heading.id"
1111
:data-toc-level="heading.level"
1212
class="editor--toc__item"
1313
:class="{
@@ -45,12 +45,7 @@ export default {
4545
},
4646
methods: {
4747
goto(heading) {
48-
this.$editor
49-
.chain()
50-
.focus()
51-
.setTextSelection(heading.position)
52-
.scrollIntoView()
53-
.run()
48+
document.getElementById(heading.id).scrollIntoView()
5449
5550
this.$nextTick(() => {
5651
window.location.hash = heading.id

0 commit comments

Comments
 (0)