Skip to content

Commit 0f2c48e

Browse files
author
Vinicius Reis
authored
Merge pull request #2841 from nextcloud/feature/heading-anchors
Feature: Add page anchors (inter page links)
2 parents 9973d1f + 4c87a0f commit 0f2c48e

20 files changed

Lines changed: 307 additions & 99 deletions

β€Žcss/prosemirror.scssβ€Ž

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,18 @@ div.ProseMirror {
102102
h5,
103103
h6 {
104104
font-weight: 600;
105-
line-height: 120%;
105+
line-height: 1.1em;
106106
margin-top: 24px;
107107
margin-bottom: 12px;
108108
color: var(--color-main-text);
109109
}
110110

111111
h1 {
112112
font-size: 36px;
113-
margin-top: 48px;
114113
}
115114

116115
h2 {
117116
font-size: 30px;
118-
margin-top: 48px;
119117
}
120118

121119
h3 {

β€Žcypress/e2e/outline.spec.jsβ€Ž

Lines changed: 0 additions & 74 deletions
This file was deleted.

β€Žcypress/e2e/sections.spec.jsβ€Ž

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { initUserAndFiles, randHash } from '../utils/index.js'
2+
3+
const currentUser = randHash()
4+
const fileName = 'test.md'
5+
6+
const refresh = () => cy.get('.files-controls .crumb:not(.hidden) a')
7+
.last()
8+
.click({ force: true })
9+
10+
const clickOutline = () => {
11+
cy.getActionEntry('headings')
12+
.click()
13+
14+
cy.get('.v-popper__wrapper .open').getActionEntry('outline')
15+
.click()
16+
}
17+
18+
describe('Content Sections', () => {
19+
before(function() {
20+
initUserAndFiles(currentUser, fileName)
21+
})
22+
23+
beforeEach(function() {
24+
cy.login(currentUser, 'password', {
25+
onBeforeLoad(win) {
26+
cy.stub(win, 'open')
27+
.as('winOpen')
28+
},
29+
})
30+
31+
cy.openFile(fileName)
32+
.then(() => cy.clearContent())
33+
})
34+
35+
describe('Heading anchors', () => {
36+
beforeEach(() => cy.clearContent())
37+
38+
it('Anchor exists', () => {
39+
cy.getContent()
40+
.type('# Heading\nText\n## Heading 2\nText\n## Heading 2')
41+
.then(() => {
42+
cy.getContent()
43+
.find('a.anchor-link')
44+
.should(($anchor) => {
45+
expect($anchor).to.have.length(3)
46+
expect($anchor.eq(0)).to.have.attr('href').and.equal('#heading')
47+
expect($anchor.eq(1)).to.have.attr('href').and.equal('#heading-2')
48+
expect($anchor.eq(2)).to.have.attr('href').and.equal('#heading-2--1')
49+
})
50+
})
51+
})
52+
53+
it('Anchor scrolls into view', () => {
54+
// Create link to top heading
55+
cy.getContent()
56+
.type('{selectAll}{backspace}move top\n{selectAll}')
57+
.get('.menububble button[data-text-bubble-action="add-link"]')
58+
.click({ force: true })
59+
.then(() => {
60+
cy.get('.menububble .menububble__input')
61+
.type('{shift}')
62+
.type('#top{enter}', { force: true })
63+
})
64+
// Insert content above link
65+
cy.getContent()
66+
.type('{moveToStart}\n{moveToStart}# top \n')
67+
.type('lorem ipsum \n'.repeat(25))
68+
.type('{moveToEnd}\n')
69+
.find('h1#top')
70+
.should('not.be.inViewport')
71+
// Click link and test view moved to anchor
72+
cy.getContent()
73+
.find('a:not(.anchor-link)')
74+
.click()
75+
.then(() => {
76+
cy.getContent()
77+
.get('h1[id="top"]')
78+
.should('be.inViewport')
79+
})
80+
})
81+
})
82+
83+
describe('Table of Contents', () => {
84+
beforeEach(() => cy.clearContent())
85+
86+
it('sidebar toc', () => {
87+
cy.getContent()
88+
.type('# T1 \n## T2 \n### T3 \n#### T4 \n##### T5 \n###### T6\n')
89+
.then(refresh)
90+
.then(() => cy.openFile(fileName, { force: true }))
91+
.then(clickOutline)
92+
93+
cy.getOutline()
94+
.find('header')
95+
.should('exist')
96+
97+
cy.getTOC()
98+
.find('ul li')
99+
.should('have.length', 6)
100+
cy.getTOC()
101+
.find('ul li')
102+
.each((el, index) => {
103+
cy.wrap(el)
104+
.should('have.attr', 'data-toc-level')
105+
.and('equal', String(index + 1))
106+
107+
cy.wrap(el)
108+
.find('a')
109+
.should('have.attr', 'href')
110+
.and('equal', `#t${index + 1}`)
111+
})
112+
})
113+
114+
it('empty toc', () => {
115+
refresh()
116+
.then(() => cy.openFile(fileName, { force: true }))
117+
.then(clickOutline)
118+
119+
cy.getOutline()
120+
.find('ul')
121+
.should('be.empty')
122+
})
123+
})
124+
})

β€Žcypress/support/chai.jsβ€Ž

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export default _chai => {
2+
_chai.Assertion.addMethod('inViewport', function() {
3+
const subject = this._obj
4+
5+
const height = Cypress.$(cy.state('window')).height()
6+
const width = Cypress.$(cy.state('window')).width()
7+
const rect = subject[0].getBoundingClientRect()
8+
9+
this.assert(
10+
rect.top < height && rect.bottom > 0 && rect.right <= width && rect.left >= 0,
11+
'expected #{this} to be in the viewport',
12+
'expected #{this} to not be in the viewport',
13+
this._obj
14+
)
15+
})
16+
}

β€Žcypress/support/commands.jsβ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,8 @@ Cypress.Commands.add('getTOC', () => {
292292

293293
Cypress.Commands.add('clearContent', () => {
294294
return cy.getContent()
295-
.type('{selectall}')
296-
.type('{del}')
295+
.scrollIntoView()
296+
.type('{selectAll}{backspace}', { force: true })
297297
})
298298

299299
Cypress.Commands.add('openWorkspace', (subject, name) => {

β€Žcypress/support/e2e.jsβ€Ž

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
// This file is loaded before all e2e tests
22

33
import './commands.js'
4+
import chaiExtension from './chai.js'
5+
6+
before(() => {
7+
chai.use(chaiExtension)
8+
})

β€Žjs/editor.jsβ€Ž

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žjs/editor.js.mapβ€Ž

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žjs/text-files.jsβ€Ž

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žjs/text-files.js.mapβ€Ž

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
Β (0)