mirror of
https://github.com/ueberdosis/tiptap.git
synced 2025-06-14 22:32:24 +08:00
Fix getMarkRange not finding marks when at the start of a mark (#5717)
* fix getMarkRange to always find a mark, even if cursor is at start of mark * added changesets * added integration tests for getMarkRange * remove console.logs * added forward and backward boundary checks for tests
This commit is contained in:
parent
d96f679585
commit
4efd2278a1
5
.changeset/sweet-masks-smash.md
Normal file
5
.changeset/sweet-masks-smash.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@tiptap/core": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixed an issue with getMarkRange not returning the correct range when cursor is at the start of the specified mark
|
@ -29,17 +29,20 @@ export function getMarkRange(
|
|||||||
if (!$pos || !type) {
|
if (!$pos || !type) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = $pos.parent.childAfter($pos.parentOffset)
|
let start = $pos.parent.childAfter($pos.parentOffset)
|
||||||
|
|
||||||
if ($pos.parentOffset === start.offset && start.offset !== 0) {
|
// If the cursor is at the start of a text node that does not have the mark, look backward
|
||||||
|
if (!start.node || !start.node.marks.some(mark => mark.type === type)) {
|
||||||
start = $pos.parent.childBefore($pos.parentOffset)
|
start = $pos.parent.childBefore($pos.parentOffset)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!start.node) {
|
// If there is no text node with the mark even backward, return undefined
|
||||||
|
if (!start.node || !start.node.marks.some(mark => mark.type === type)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We now know that the cursor is either at the start, middle or end of a text node with the specified mark
|
||||||
|
// so we can look it up on the targeted mark
|
||||||
const mark = findMarkInSet([...start.node.marks], type, attributes)
|
const mark = findMarkInSet([...start.node.marks], type, attributes)
|
||||||
|
|
||||||
if (!mark) {
|
if (!mark) {
|
||||||
|
143
tests/cypress/integration/core/getMarkRange.spec.ts
Normal file
143
tests/cypress/integration/core/getMarkRange.spec.ts
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
import {
|
||||||
|
getMarkRange,
|
||||||
|
getSchemaByResolvedExtensions,
|
||||||
|
} from '@tiptap/core'
|
||||||
|
import Document from '@tiptap/extension-document'
|
||||||
|
import Link from '@tiptap/extension-link'
|
||||||
|
import Paragraph from '@tiptap/extension-paragraph'
|
||||||
|
import Text from '@tiptap/extension-text'
|
||||||
|
import { Node } from '@tiptap/pm/model'
|
||||||
|
|
||||||
|
describe('getMarkRange', () => {
|
||||||
|
const document = {
|
||||||
|
type: 'doc',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
content: [
|
||||||
|
{ type: 'text', text: 'This is a ' },
|
||||||
|
{ type: 'text', text: 'linked', marks: [{ type: 'link', attrs: { href: 'https://tiptap.dev' } }] },
|
||||||
|
{ type: 'text', text: ' text.' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = getSchemaByResolvedExtensions([
|
||||||
|
Document,
|
||||||
|
Paragraph,
|
||||||
|
Text,
|
||||||
|
Link.configure({ openOnClick: false }),
|
||||||
|
])
|
||||||
|
|
||||||
|
it('gets the correct range for a position inside the mark', () => {
|
||||||
|
const doc = Node.fromJSON(schema, document)
|
||||||
|
const $pos = doc.resolve(14)
|
||||||
|
const range = getMarkRange($pos, schema.marks.link)
|
||||||
|
|
||||||
|
expect(range).to.deep.eq({
|
||||||
|
from: 11,
|
||||||
|
to: 17,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('gets the correct range for a position at the start of the mark', () => {
|
||||||
|
const doc = Node.fromJSON(schema, document)
|
||||||
|
const $pos = doc.resolve(11)
|
||||||
|
const range = getMarkRange($pos, schema.marks.link)
|
||||||
|
|
||||||
|
expect(range).to.deep.eq({
|
||||||
|
from: 11,
|
||||||
|
to: 17,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('gets the correct range for a position at the end of the mark', () => {
|
||||||
|
const doc = Node.fromJSON(schema, document)
|
||||||
|
const $pos = doc.resolve(17)
|
||||||
|
const range = getMarkRange($pos, schema.marks.link)
|
||||||
|
|
||||||
|
expect(range).to.deep.eq({
|
||||||
|
from: 11,
|
||||||
|
to: 17,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('gets undefined if a mark is not found', () => {
|
||||||
|
const doc = Node.fromJSON(schema, document)
|
||||||
|
const $pos = doc.resolve(6)
|
||||||
|
const range = getMarkRange($pos, schema.marks.link)
|
||||||
|
|
||||||
|
expect(range).to.eq(undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('doesnt cross node boundaries on backward check', () => {
|
||||||
|
const testDocument = {
|
||||||
|
type: 'doc',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
content: [
|
||||||
|
{ type: 'text', text: 'This is a text with a ' },
|
||||||
|
{ type: 'text', text: 'link.', marks: [{ type: 'link', attrs: { href: 'https://tiptap.dev' } }] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
content: [
|
||||||
|
{ type: 'text', text: 'This is a text without a link.' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = Node.fromJSON(schema, testDocument)
|
||||||
|
const $pos = doc.resolve(28)
|
||||||
|
const range = getMarkRange($pos, schema.marks.link)
|
||||||
|
|
||||||
|
expect(range).to.deep.eq({
|
||||||
|
from: 23,
|
||||||
|
to: 28,
|
||||||
|
})
|
||||||
|
|
||||||
|
const nextRange = getMarkRange(doc.resolve(30), schema.marks.link)
|
||||||
|
|
||||||
|
expect(nextRange).to.eq(undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('doesnt cross node boundaries on forward check', () => {
|
||||||
|
const testDocument = {
|
||||||
|
type: 'doc',
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
content: [
|
||||||
|
{ type: 'text', text: 'This is a text without a link.' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
content: [
|
||||||
|
{ type: 'text', text: 'A link', marks: [{ type: 'link', attrs: { href: 'https://tiptap.dev' } }] },
|
||||||
|
{ type: 'text', text: ' is at the start of this paragraph.' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
const doc = Node.fromJSON(schema, testDocument)
|
||||||
|
|
||||||
|
const range = getMarkRange(doc.resolve(32), schema.marks.link)
|
||||||
|
|
||||||
|
expect(range).to.eq(undefined)
|
||||||
|
|
||||||
|
const $pos = doc.resolve(33)
|
||||||
|
const nextRange = getMarkRange($pos, schema.marks.link)
|
||||||
|
|
||||||
|
expect(nextRange).to.deep.eq({
|
||||||
|
from: 33,
|
||||||
|
to: 39,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user