snapshot of https://github.com/ueberdosis/tiptap.git (main, 2026-09-15; blobs > 131072B omitted)
Some checks failed
Verify / Install node dependencies (push) Failing after 2s
Verify / Build packages (push) Has been skipped
Verify / Check linting & formatting (push) Has been skipped
Verify / Run unit tests (push) Has been skipped
Verify / Run e2e tests (shard 1/4) (push) Has been skipped
Verify / Run e2e tests (shard 2/4) (push) Has been skipped
Verify / Run e2e tests (shard 3/4) (push) Has been skipped
Verify / Run e2e tests (shard 4/4) (push) Has been skipped
Publish / Resolve publish config (push) Failing after 2s
Publish / Send notification to Slack (push) Has been skipped
Publish / Send failure notification to Slack (push) Failing after 8s
Verify / Merge e2e reports (push) Has been skipped
Publish / Build (push) Has been skipped
Publish / Bump Versions OR Release to Registry (push) Has been skipped
Some checks failed
Verify / Install node dependencies (push) Failing after 2s
Verify / Build packages (push) Has been skipped
Verify / Check linting & formatting (push) Has been skipped
Verify / Run unit tests (push) Has been skipped
Verify / Run e2e tests (shard 1/4) (push) Has been skipped
Verify / Run e2e tests (shard 2/4) (push) Has been skipped
Verify / Run e2e tests (shard 3/4) (push) Has been skipped
Verify / Run e2e tests (shard 4/4) (push) Has been skipped
Publish / Resolve publish config (push) Failing after 2s
Publish / Send notification to Slack (push) Has been skipped
Publish / Send failure notification to Slack (push) Failing after 8s
Verify / Merge e2e reports (push) Has been skipped
Publish / Build (push) Has been skipped
Publish / Bump Versions OR Release to Registry (push) Has been skipped
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { Editor } from '@tiptap/core'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import { BackgroundColor, TextStyle } from '@tiptap/extension-text-style'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vite-plus/test'
|
||||
|
||||
describe('BackgroundColor commands', () => {
|
||||
let editor: Editor
|
||||
|
||||
beforeEach(() => {
|
||||
const element = document.createElement('div')
|
||||
document.body.appendChild(element)
|
||||
editor = new Editor({
|
||||
element,
|
||||
extensions: [Document, Paragraph, Text, TextStyle, BackgroundColor],
|
||||
content: '<p>Example Text</p>',
|
||||
})
|
||||
editor.commands.selectAll()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
editor.destroy()
|
||||
})
|
||||
|
||||
it('sets the background color of the selected text', () => {
|
||||
expect(editor.isActive('textStyle', { backgroundColor: '#958DF1' })).toBe(false)
|
||||
editor.commands.setBackgroundColor('#958DF1')
|
||||
expect(editor.isActive('textStyle', { backgroundColor: '#958DF1' })).toBe(true)
|
||||
const span = editor.view.dom.querySelector('span')
|
||||
expect(span?.textContent).toBe('Example Text')
|
||||
expect(span?.style.backgroundColor).toBe('#958DF1')
|
||||
})
|
||||
|
||||
it('removes the background color of the selected text', () => {
|
||||
editor.commands.setBackgroundColor('#958DF1')
|
||||
expect(editor.getHTML()).toContain('<span')
|
||||
|
||||
editor.commands.unsetBackgroundColor()
|
||||
expect(editor.getHTML()).not.toContain('<span')
|
||||
expect(editor.isActive('textStyle', { backgroundColor: '#958DF1' })).toBe(false)
|
||||
})
|
||||
|
||||
it('exposes the active backgroundColor via getAttributes', () => {
|
||||
editor.commands.setBackgroundColor('#958DF1')
|
||||
expect(editor.getAttributes('textStyle').backgroundColor).toBe('#958DF1')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Editor } from '@tiptap/core'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import { Color, TextStyle } from '@tiptap/extension-text-style'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vite-plus/test'
|
||||
|
||||
describe('Color commands', () => {
|
||||
let editor: Editor
|
||||
|
||||
beforeEach(() => {
|
||||
const element = document.createElement('div')
|
||||
document.body.appendChild(element)
|
||||
editor = new Editor({
|
||||
element,
|
||||
extensions: [Document, Paragraph, Text, TextStyle, Color],
|
||||
content: '<p>Example Text</p>',
|
||||
})
|
||||
editor.commands.selectAll()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
editor.destroy()
|
||||
})
|
||||
|
||||
it('sets the color of the selected text', () => {
|
||||
expect(editor.isActive('textStyle', { color: '#958DF1' })).toBe(false)
|
||||
editor.commands.setColor('#958DF1')
|
||||
expect(editor.isActive('textStyle', { color: '#958DF1' })).toBe(true)
|
||||
const span = editor.view.dom.querySelector('span')
|
||||
expect(span?.textContent).toBe('Example Text')
|
||||
expect(span?.style.color).toBe('#958DF1')
|
||||
})
|
||||
|
||||
it('removes the color of the selected text', () => {
|
||||
editor.commands.setColor('#958DF1')
|
||||
expect(editor.getHTML()).toContain('<span')
|
||||
|
||||
editor.commands.unsetColor()
|
||||
expect(editor.getHTML()).not.toContain('<span')
|
||||
})
|
||||
|
||||
it('exposes the active color via getAttributes', () => {
|
||||
editor.commands.setColor('#958DF1')
|
||||
expect(editor.getAttributes('textStyle').color).toBe('#958DF1')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Editor } from '@tiptap/core'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import { FontFamily, TextStyle } from '@tiptap/extension-text-style'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vite-plus/test'
|
||||
|
||||
describe('FontFamily commands', () => {
|
||||
let editor: Editor
|
||||
|
||||
beforeEach(() => {
|
||||
const element = document.createElement('div')
|
||||
document.body.appendChild(element)
|
||||
editor = new Editor({
|
||||
element,
|
||||
extensions: [Document, Paragraph, Text, TextStyle, FontFamily],
|
||||
content: '<p>Example Text</p>',
|
||||
})
|
||||
editor.commands.selectAll()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
editor.destroy()
|
||||
})
|
||||
|
||||
it('sets the font-family of the selected text', () => {
|
||||
expect(editor.isActive('textStyle', { fontFamily: 'monospace' })).toBe(false)
|
||||
editor.commands.setFontFamily('monospace')
|
||||
expect(editor.isActive('textStyle', { fontFamily: 'monospace' })).toBe(true)
|
||||
const span = editor.view.dom.querySelector('span')
|
||||
expect(span?.textContent).toBe('Example Text')
|
||||
expect((span as HTMLSpanElement | null)?.style.fontFamily).toBe('monospace')
|
||||
})
|
||||
|
||||
it('removes the font-family of the selected text', () => {
|
||||
editor.commands.setFontFamily('monospace')
|
||||
expect(editor.getHTML()).toContain('<span')
|
||||
|
||||
editor.commands.unsetFontFamily()
|
||||
expect(editor.getHTML()).not.toContain('<span')
|
||||
})
|
||||
|
||||
it('allows CSS variables as a font-family', () => {
|
||||
editor.commands.setFontFamily('var(--title-font-family)')
|
||||
const span = editor.view.dom.querySelector('span')
|
||||
expect(span?.style.fontFamily).toBe('var(--title-font-family)')
|
||||
})
|
||||
|
||||
it('allows fonts containing multiple font families', () => {
|
||||
editor.commands.setFontFamily('"Comic Sans MS", "Comic Sans"')
|
||||
const span = editor.view.dom.querySelector('span')
|
||||
expect(span?.style.fontFamily).toBe('"Comic Sans MS", "Comic Sans"')
|
||||
})
|
||||
|
||||
it('allows fonts containing a space and number as a font-family', () => {
|
||||
editor.commands.setFontFamily('"Exo 2"')
|
||||
const span = editor.view.dom.querySelector('span')
|
||||
expect(span?.style.fontFamily).toBe('"Exo 2"')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Editor } from '@tiptap/core'
|
||||
import Blockquote from '@tiptap/extension-blockquote'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import { Color, FontSize, TextStyle } from '@tiptap/extension-text-style'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vite-plus/test'
|
||||
|
||||
describe('FontSize commands', () => {
|
||||
let editor: Editor
|
||||
|
||||
beforeEach(() => {
|
||||
const element = document.createElement('div')
|
||||
document.body.appendChild(element)
|
||||
editor = new Editor({
|
||||
element,
|
||||
extensions: [Document, Paragraph, Text, TextStyle, FontSize],
|
||||
content: '<p>Example Text</p>',
|
||||
})
|
||||
editor.commands.selectAll()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
editor.destroy()
|
||||
})
|
||||
|
||||
it('sets the font-size of the selected text', () => {
|
||||
expect(editor.isActive('textStyle', { fontSize: '28px' })).toBe(false)
|
||||
editor.commands.setFontSize('28px')
|
||||
expect(editor.isActive('textStyle', { fontSize: '28px' })).toBe(true)
|
||||
const span = editor.view.dom.querySelector('span')
|
||||
expect(span?.textContent).toBe('Example Text')
|
||||
expect(span?.style.fontSize).toBe('28px')
|
||||
})
|
||||
|
||||
it('removes the font-size of the selected text', () => {
|
||||
editor.commands.setFontSize('28px')
|
||||
expect(editor.view.dom.querySelector('span')?.style.fontSize).toBe('28px')
|
||||
|
||||
editor.commands.unsetFontSize()
|
||||
expect(editor.getHTML()).not.toContain('<span')
|
||||
})
|
||||
})
|
||||
|
||||
describe('FontSize commands inside a wrapper node', () => {
|
||||
let editor: Editor
|
||||
|
||||
const editorWith = (content: string) => {
|
||||
const element = document.createElement('div')
|
||||
document.body.appendChild(element)
|
||||
editor = new Editor({
|
||||
element,
|
||||
extensions: [Document, Paragraph, Text, Blockquote, TextStyle, Color, FontSize],
|
||||
content,
|
||||
})
|
||||
editor.commands.selectAll()
|
||||
return editor
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
editor.destroy()
|
||||
})
|
||||
|
||||
it('keeps the other text styles when unsetting inside a blockquote', () => {
|
||||
editorWith(
|
||||
'<blockquote><p><span style="color: #ff0000; font-size: 28px">Example Text</span></p></blockquote>',
|
||||
)
|
||||
|
||||
editor.commands.unsetFontSize()
|
||||
|
||||
expect(editor.getHTML()).toContain('color: #ff0000')
|
||||
expect(editor.getHTML()).not.toContain('font-size')
|
||||
})
|
||||
|
||||
it('keeps the other text styles when unsetting inside a paragraph', () => {
|
||||
editorWith('<p><span style="color: #ff0000; font-size: 28px">Example Text</span></p>')
|
||||
|
||||
editor.commands.unsetFontSize()
|
||||
|
||||
expect(editor.getHTML()).toContain('color: #ff0000')
|
||||
expect(editor.getHTML()).not.toContain('font-size')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Editor } from '@tiptap/core'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import { LineHeight, TextStyle } from '@tiptap/extension-text-style'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vite-plus/test'
|
||||
|
||||
describe('LineHeight commands', () => {
|
||||
let editor: Editor
|
||||
|
||||
beforeEach(() => {
|
||||
const element = document.createElement('div')
|
||||
document.body.appendChild(element)
|
||||
editor = new Editor({
|
||||
element,
|
||||
extensions: [Document, Paragraph, Text, TextStyle, LineHeight],
|
||||
content: '<p>Example Text</p>',
|
||||
})
|
||||
editor.commands.selectAll()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
editor.destroy()
|
||||
})
|
||||
|
||||
const cases = [
|
||||
{ value: '1.5', style: '1.5' },
|
||||
{ value: '2.0', style: '2' },
|
||||
{ value: '4.0', style: '4' },
|
||||
]
|
||||
|
||||
cases.forEach(({ value, style }) => {
|
||||
it(`sets line-height ${value} for the selected text`, () => {
|
||||
expect(editor.isActive('textStyle', { lineHeight: value })).toBe(false)
|
||||
editor.commands.toggleTextStyle({ lineHeight: value })
|
||||
expect(editor.isActive('textStyle', { lineHeight: value })).toBe(true)
|
||||
const span = editor.view.dom.querySelector('span')
|
||||
expect(span?.textContent).toBe('Example Text')
|
||||
expect(span?.style.lineHeight).toBe(style)
|
||||
})
|
||||
})
|
||||
|
||||
it('removes the line-height of the selected text', () => {
|
||||
editor.commands.toggleTextStyle({ lineHeight: '1.5' })
|
||||
expect(editor.view.dom.querySelector('span')?.style.lineHeight).toBe('1.5')
|
||||
|
||||
editor.commands.unsetLineHeight()
|
||||
expect(editor.getHTML()).not.toContain('<span')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,124 @@
|
||||
import { Editor } from '@tiptap/core'
|
||||
import Bold from '@tiptap/extension-bold'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import { TextStyleKit } from '@tiptap/extension-text-style'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vite-plus/test'
|
||||
|
||||
const demoContent = `
|
||||
<p><span>This has a <span> tag without a style attribute, so it's thrown away.</span></p>
|
||||
<p><span style="">But this one is wrapped in a <span> tag with an inline style attribute, so it's kept - even if it's empty for now.</span></p>
|
||||
<p>--- merge nested span styles option enabled ---</p>
|
||||
<p>
|
||||
<span style="color: #FF0000;">
|
||||
<span style="font-family: serif;">
|
||||
red serif
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
<span style="color: #FF0000;">
|
||||
<span style="font-family: serif;">
|
||||
<span style="color: #0000FF;">
|
||||
blue serif
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
<span style="color: #00FF00;">
|
||||
<span style="font-family: serif;">green serif </span>
|
||||
<span style="font-family: serif;color: #FF0000;">red serif</span>
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
<span>
|
||||
plain
|
||||
<span style="color: #0000FF;">blue</span>
|
||||
plain
|
||||
<span style="color: #00FF00;">
|
||||
green
|
||||
<span style="font-family: serif;">green serif</span>
|
||||
</span>
|
||||
plain
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
<span style="color: #0000FF;">
|
||||
blue
|
||||
<span style="color: #00FF00;">
|
||||
green
|
||||
<span style="font-family: serif;">
|
||||
green serif
|
||||
<span style="color: #0000FF;">blue serif</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
`
|
||||
|
||||
describe('TextStyleKit nested span merge', () => {
|
||||
let editor: Editor
|
||||
|
||||
beforeEach(() => {
|
||||
const element = document.createElement('div')
|
||||
document.body.appendChild(element)
|
||||
editor = new Editor({
|
||||
element,
|
||||
extensions: [Document, Paragraph, Text, TextStyleKit, Bold],
|
||||
content: demoContent,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
editor.destroy()
|
||||
})
|
||||
|
||||
it('merges styles of a span with one nested child span into a single span', () => {
|
||||
const spans = editor.view.dom.querySelectorAll('p:nth-child(4) > span')
|
||||
expect(spans.length).toBe(1)
|
||||
expect(spans[0].textContent?.trim()).toBe('red serif')
|
||||
expect(spans[0].getAttribute('style')).toBe('color: #FF0000; font-family: serif;')
|
||||
})
|
||||
|
||||
it('merges styles of multiple nested spans into the innermost descendant', () => {
|
||||
const spans = editor.view.dom.querySelectorAll('p:nth-child(5) > span')
|
||||
expect(spans.length).toBe(1)
|
||||
expect(spans[0].textContent?.trim()).toBe('blue serif')
|
||||
expect(spans[0].getAttribute('style')).toBe('color: #0000FF; font-family: serif;')
|
||||
})
|
||||
|
||||
it('merges parent styles into each sibling descendant span', () => {
|
||||
const spans = editor.view.dom.querySelectorAll('p:nth-child(6) > span')
|
||||
expect(spans.length).toBe(2)
|
||||
expect(spans[0].textContent?.trim()).toBe('green serif')
|
||||
expect(spans[0].getAttribute('style')).toBe('color: #00FF00; font-family: serif;')
|
||||
expect(spans[1].textContent?.trim()).toBe('red serif')
|
||||
expect(spans[1].getAttribute('style')).toBe('color: #FF0000; font-family: serif;')
|
||||
})
|
||||
|
||||
it('keeps descendant spans intact when the parent span has no style', () => {
|
||||
const spans = editor.view.dom.querySelectorAll('p:nth-child(7) > span')
|
||||
expect(spans.length).toBe(4)
|
||||
expect(spans[0].textContent?.trim()).toBe('blue')
|
||||
expect(spans[0].getAttribute('style')).toBe('color: #0000FF;')
|
||||
expect(spans[1].textContent?.trim()).toBe('green')
|
||||
expect(spans[1].getAttribute('style')).toBe('color: #00FF00;')
|
||||
expect(spans[2].textContent?.trim()).toBe('green serif')
|
||||
expect(spans[2].getAttribute('style')).toBe('color: #00FF00; font-family: serif;')
|
||||
})
|
||||
|
||||
it('merges parent styles into descendants while preserving root text spans', () => {
|
||||
const spans = editor.view.dom.querySelectorAll('p:nth-child(8) > span')
|
||||
expect(spans.length).toBe(4)
|
||||
expect(spans[0].textContent?.trim()).toBe('blue')
|
||||
expect(spans[0].getAttribute('style')).toBe('color: #0000FF;')
|
||||
expect(spans[1].textContent?.trim()).toBe('green')
|
||||
expect(spans[1].getAttribute('style')).toBe('color: #00FF00;')
|
||||
expect(spans[2].textContent?.trim()).toBe('green serif')
|
||||
expect(spans[2].getAttribute('style')).toBe('color: #00FF00; font-family: serif;')
|
||||
expect(spans[3].textContent?.trim()).toBe('blue serif')
|
||||
expect(spans[3].getAttribute('style')).toBe('color: #0000FF; font-family: serif;')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user