Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/text.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cypress/e2e/outline.spec.js74
-rw-r--r--cypress/e2e/sections.spec.js124
-rw-r--r--src/nodes/Heading/HeadingView.vue114
-rw-r--r--src/nodes/Heading/index.js15
4 files changed, 74 insertions, 253 deletions
diff --git a/cypress/e2e/outline.spec.js b/cypress/e2e/outline.spec.js
new file mode 100644
index 000000000..b441a65d6
--- /dev/null
+++ b/cypress/e2e/outline.spec.js
@@ -0,0 +1,74 @@
+import { randHash } from '../utils/index.js'
+
+const currentUser = randHash()
+
+const refresh = () => cy.get('.files-controls .crumb:not(.hidden) a')
+ .last()
+ .click({ force: true })
+
+const clickOutline = () => {
+ cy.getActionEntry('headings')
+ .click()
+
+ cy.get('.v-popper__wrapper .open').getActionEntry('outline')
+ .click()
+}
+
+const createMarkdown = (fileName, content) => {
+ return cy.createFile(fileName, content, 'text/markdown')
+ .then(refresh)
+}
+
+describe('Table of Contents', () => {
+ before(() => {
+ // Init user
+ cy.nextcloudCreateUser(currentUser, 'password')
+ cy.login(currentUser, 'password')
+ })
+
+ beforeEach(() => {
+ cy.login(currentUser, 'password')
+ })
+
+ it('sidebar toc', () => {
+ const fileName = 'toc.md'
+
+ createMarkdown(fileName, '# T1 \n ## T2 \n ### T3 \n #### T4 \n ##### T5 \n ###### T6')
+ .then(refresh)
+ .then(() => cy.openFile(fileName, { force: true }))
+ .then(clickOutline)
+
+ cy.getOutline()
+ .find('header')
+ .should('exist')
+
+ cy.getTOC()
+ .find('ul li')
+ .should('have.length', 6)
+ cy.getTOC()
+ .find('ul li')
+ .each((el, index) => {
+ cy.wrap(el)
+ .should('have.attr', 'data-toc-level')
+ .and('equal', String(index + 1))
+
+ cy.wrap(el)
+ .find('a')
+ .should('have.attr', 'href')
+ .and('equal', `#t${index + 1}`)
+ })
+ })
+
+ it('empty toc', () => {
+ const fileName = 'empty.md'
+
+ createMarkdown(fileName, '')
+ .then(refresh)
+ .then(() => cy.openFile(fileName, { force: true }))
+ .then(clickOutline)
+
+ cy.getOutline()
+ .find('ul')
+ .should('be.empty')
+ })
+})
diff --git a/cypress/e2e/sections.spec.js b/cypress/e2e/sections.spec.js
deleted file mode 100644
index 89a3991a3..000000000
--- a/cypress/e2e/sections.spec.js
+++ /dev/null
@@ -1,124 +0,0 @@
-import { initUserAndFiles, randHash } from '../utils/index.js'
-
-const currentUser = randHash()
-const fileName = 'test.md'
-
-const refresh = () => cy.get('.files-controls .crumb:not(.hidden) a')
- .last()
- .click({ force: true })
-
-const clickOutline = () => {
- cy.getActionEntry('headings')
- .click()
-
- cy.get('.v-popper__wrapper .open').getActionEntry('outline')
- .click()
-}
-
-describe('Content Sections', () => {
- before(function() {
- initUserAndFiles(currentUser, fileName)
- })
-
- beforeEach(function() {
- cy.login(currentUser, 'password', {
- onBeforeLoad(win) {
- cy.stub(win, 'open')
- .as('winOpen')
- },
- })
-
- cy.openFile(fileName)
- .then(() => cy.clearContent())
- })
-
- describe('Heading anchors', () => {
- beforeEach(() => cy.clearContent())
-
- it('Anchor exists', () => {
- cy.getContent()
- .type('# Heading\nText\n## Heading 2\nText\n## Heading 2')
- .then(() => {
- cy.getContent()
- .find('a.anchor-link')
- .should(($anchor) => {
- expect($anchor).to.have.length(3)
- expect($anchor.eq(0)).to.have.attr('href').and.equal('#heading')
- expect($anchor.eq(1)).to.have.attr('href').and.equal('#heading-2')
- expect($anchor.eq(2)).to.have.attr('href').and.equal('#heading-2--1')
- })
- })
- })
-
- it('Anchor scrolls into view', () => {
- // Create link to top heading
- cy.getContent()
- .type('{selectAll}{backspace}move top\n{selectAll}')
- .get('.menububble button[data-text-bubble-action="add-link"]')
- .click({ force: true })
- .then(() => {
- cy.get('.menububble .menububble__input')
- .type('{shift}')
- .type('#top{enter}', { force: true })
- })
- // Insert content above link
- cy.getContent()
- .type('{moveToStart}\n{moveToStart}# top \n')
- .type('lorem ipsum \n'.repeat(25))
- .type('{moveToEnd}\n')
- .find('h1#top')
- .should('not.be.inViewport')
- // Click link and test view moved to anchor
- cy.getContent()
- .find('a:not(.anchor-link)')
- .click()
- .then(() => {
- cy.getContent()
- .get('h1[id="top"]')
- .should('be.inViewport')
- })
- })
- })
-
- describe('Table of Contents', () => {
- beforeEach(() => cy.clearContent())
-
- it('sidebar toc', () => {
- cy.getContent()
- .type('# T1 \n## T2 \n### T3 \n#### T4 \n##### T5 \n###### T6\n')
- .then(refresh)
- .then(() => cy.openFile(fileName, { force: true }))
- .then(clickOutline)
-
- cy.getOutline()
- .find('header')
- .should('exist')
-
- cy.getTOC()
- .find('ul li')
- .should('have.length', 6)
- cy.getTOC()
- .find('ul li')
- .each((el, index) => {
- cy.wrap(el)
- .should('have.attr', 'data-toc-level')
- .and('equal', String(index + 1))
-
- cy.wrap(el)
- .find('a')
- .should('have.attr', 'href')
- .and('equal', `#t${index + 1}`)
- })
- })
-
- it('empty toc', () => {
- refresh()
- .then(() => cy.openFile(fileName, { force: true }))
- .then(clickOutline)
-
- cy.getOutline()
- .find('ul')
- .should('be.empty')
- })
- })
-})
diff --git a/src/nodes/Heading/HeadingView.vue b/src/nodes/Heading/HeadingView.vue
deleted file mode 100644
index 7e8b28e83..000000000
--- a/src/nodes/Heading/HeadingView.vue
+++ /dev/null
@@ -1,114 +0,0 @@
-<!--
- - @copyright Copyright (c) 2022
- -
- - @license AGPL-3.0-or-later
- -
- - 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/>.
- -
- -->
-
-<template>
- <NodeViewWrapper ref="content"
- :as="domElement"
- v-bind="node.attrs"
- tabindex="-1">
- <a aria-hidden="true"
- class="anchor-link"
- :href="href"
- :title="t('text', 'Link to this section')"
- @click.stop="click">{{ linkSymbol }}</a>
- <NodeViewContent />
- </NodeViewWrapper>
-</template>
-
-<script>
-import Vue from 'vue'
-import { NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2'
-import { useEditorMixin } from '../../components/Editor.provider.js'
-
-export default Vue.extend({
- name: 'HeadingView',
- components: {
- NodeViewWrapper,
- NodeViewContent,
- },
- mixins: [useEditorMixin],
- props: {
- node: {
- type: Object,
- required: true,
- },
- extension: {
- type: Object,
- required: true,
- },
- },
-
- computed: {
- href() {
- return `#${this.node.attrs.id}`
- },
- domElement() {
- const hasLevel = this.extension.options.levels.includes(this.node.attrs.level)
- const level = hasLevel ? this.node.attrs.level : this.extension.options.levels[0]
- return `h${level}`
- },
- linkSymbol() {
- return this.extension.options.linkSymbol
- },
- t: () => window.t,
- },
-
- methods: {
- click() {
- this.$refs.content.$el.scrollIntoView()
- window.location.hash = this.href
- },
- },
-})
-</script>
-
-<style scoped lang="scss">
-div.ProseMirror {
- /* Anchor links */
- h1, h2, h3, h4, h5, h6 {
- position: relative;
- .anchor-link {
- opacity: 0;
- padding: 0;
- left: -1em;
- bottom: 0;
- font-size: max(1em, 16px);
- position: absolute;
- text-decoration: none;
- transition-duration: .15s;
- transition-property: opacity;
- transition-timing-function: cubic-bezier(.4,0,.2,1);
- }
-
- &:hover .anchor-link {
- opacity: 0.5;
- }
- }
-
- // Shrink clickable area of anchor permalinks while editing
- &.ProseMirror-focused[contenteditable="true"] {
- h1,h2,h3,h4,h5,h6 {
- .anchor-link {
- width: fit-content;
- }
- }
- }
-}
-</style>
diff --git a/src/nodes/Heading/index.js b/src/nodes/Heading/index.js
index 63614da7e..ce7e73e78 100644
--- a/src/nodes/Heading/index.js
+++ b/src/nodes/Heading/index.js
@@ -1,18 +1,8 @@
import TipTapHeading from '@tiptap/extension-heading'
-import { VueNodeViewRenderer } from '@tiptap/vue-2'
import debounce from 'debounce'
-
-import HeadingView from './HeadingView.vue'
import { setHeadings, extractHeadings } from './extractor.js'
const Heading = TipTapHeading.extend({
- addOptions() {
- return {
- ...this.parent?.(),
- linkSymbol: '#',
- }
- },
-
addAttributes() {
return {
...this.parent(),
@@ -26,11 +16,6 @@ const Heading = TipTapHeading.extend({
},
}
},
-
- addNodeView() {
- return VueNodeViewRenderer(HeadingView)
- },
-
addKeyboardShortcuts() {
return this.options.levels.reduce((items, level) => ({
...items,