feat: article editor core
This commit is contained in:
1 parent
d493a181e1
commit
a4734b2c91
55 files changed
+4186
-496
No files matched your search
@@ -0,0 +1,85 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Editor } from '@tiptap/core'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import { ArticleIndent } from '../articleIndent'
|
||||
|
||||
function createEditor(content: string) {
|
||||
return new Editor({
|
||||
extensions: [StarterKit, ArticleIndent],
|
||||
content,
|
||||
})
|
||||
}
|
||||
|
||||
describe('Indenting a paragraph writes data-indent on the paragraph', () => {
|
||||
it('increases the indent level up to the maximum', () => {
|
||||
const editor = createEditor('<p>Hello</p>')
|
||||
editor.commands.setTextSelection(2)
|
||||
|
||||
editor.commands.indent()
|
||||
expect(editor.getHTML()).toContain('data-indent="1"')
|
||||
|
||||
editor.commands.indent()
|
||||
expect(editor.getHTML()).toContain('data-indent="2"')
|
||||
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
editor.commands.indent()
|
||||
}
|
||||
expect(editor.getHTML()).toContain('data-indent="4"')
|
||||
expect(editor.getHTML()).not.toContain('data-indent="5"')
|
||||
|
||||
editor.destroy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Outdenting a paragraph decreases the indent level', () => {
|
||||
it('stops at zero and drops the attribute', () => {
|
||||
const editor = createEditor('<p>Hello</p>')
|
||||
editor.commands.setTextSelection(2)
|
||||
editor.commands.indent()
|
||||
editor.commands.indent()
|
||||
expect(editor.getHTML()).toContain('data-indent="2"')
|
||||
|
||||
editor.commands.outdent()
|
||||
expect(editor.getHTML()).toContain('data-indent="1"')
|
||||
|
||||
editor.commands.outdent()
|
||||
expect(editor.getHTML()).not.toContain('data-indent')
|
||||
|
||||
editor.commands.outdent()
|
||||
expect(editor.getHTML()).not.toContain('data-indent')
|
||||
|
||||
editor.destroy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Indenting inside a list item sinks it instead of touching the paragraph', () => {
|
||||
it('nests the list item under the previous one', () => {
|
||||
const editor = createEditor(
|
||||
'<ul><li><p>First</p></li><li><p>Second</p></li></ul>',
|
||||
)
|
||||
editor.commands.setTextSelection(editor.state.doc.content.size - 2)
|
||||
|
||||
const indented = editor.commands.indent()
|
||||
|
||||
expect(indented).toBe(true)
|
||||
expect(editor.getHTML()).toContain('<p>First</p><ul>')
|
||||
expect(editor.getHTML()).not.toContain('data-indent')
|
||||
|
||||
editor.destroy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Outdenting a heading decreases its indent level too', () => {
|
||||
it('applies data-indent to headings', () => {
|
||||
const editor = createEditor('<h2>Title</h2>')
|
||||
editor.commands.setTextSelection(2)
|
||||
|
||||
editor.commands.indent()
|
||||
expect(editor.getHTML()).toContain('data-indent="1"')
|
||||
|
||||
editor.commands.outdent()
|
||||
expect(editor.getHTML()).not.toContain('data-indent')
|
||||
|
||||
editor.destroy()
|
||||
})
|
||||
})
|
||||
@@ -5,38 +5,56 @@ const now = new Date('2026-08-25T12:00:00Z')
|
||||
|
||||
describe('formatRelativeDate', () => {
|
||||
it('formats under an hour in minutes', () => {
|
||||
expect(formatRelativeDate('2026-08-25T11:45:00Z', now)).toBe('15 minutes ago')
|
||||
expect(formatRelativeDate('2026-08-25T11:45:00Z', now)).toBe(
|
||||
'15 minutes ago',
|
||||
)
|
||||
})
|
||||
|
||||
it('clamps a sub-minute difference to "1 minute ago"', () => {
|
||||
expect(formatRelativeDate('2026-08-25T11:59:45Z', now)).toBe('1 minute ago')
|
||||
expect(formatRelativeDate('2026-08-25T11:59:45Z', now)).toBe(
|
||||
'1 minute ago',
|
||||
)
|
||||
})
|
||||
|
||||
it('uses the singular for exactly one minute ago', () => {
|
||||
expect(formatRelativeDate('2026-08-25T11:59:00Z', now)).toBe('1 minute ago')
|
||||
expect(formatRelativeDate('2026-08-25T11:59:00Z', now)).toBe(
|
||||
'1 minute ago',
|
||||
)
|
||||
})
|
||||
|
||||
it('formats under a day in hours', () => {
|
||||
expect(formatRelativeDate('2026-08-25T10:00:00Z', now)).toBe('2 hours ago')
|
||||
expect(formatRelativeDate('2026-08-25T10:00:00Z', now)).toBe(
|
||||
'2 hours ago',
|
||||
)
|
||||
})
|
||||
|
||||
it('uses the singular for exactly one hour ago', () => {
|
||||
expect(formatRelativeDate('2026-08-25T11:00:00Z', now)).toBe('1 hour ago')
|
||||
expect(formatRelativeDate('2026-08-25T11:00:00Z', now)).toBe(
|
||||
'1 hour ago',
|
||||
)
|
||||
})
|
||||
|
||||
it('formats exactly one day back as Yesterday', () => {
|
||||
expect(formatRelativeDate('2026-08-24T11:00:00Z', now)).toBe('Yesterday')
|
||||
expect(formatRelativeDate('2026-08-24T11:00:00Z', now)).toBe(
|
||||
'Yesterday',
|
||||
)
|
||||
})
|
||||
|
||||
it('formats within the week in days', () => {
|
||||
expect(formatRelativeDate('2026-08-22T12:00:00Z', now)).toBe('3 days ago')
|
||||
expect(formatRelativeDate('2026-08-22T12:00:00Z', now)).toBe(
|
||||
'3 days ago',
|
||||
)
|
||||
})
|
||||
|
||||
it('formats exactly 7 days ago as an absolute date, not "7 days ago"', () => {
|
||||
expect(formatRelativeDate('2026-08-18T12:00:00Z', now)).toBe('18 Aug 2026')
|
||||
expect(formatRelativeDate('2026-08-18T12:00:00Z', now)).toBe(
|
||||
'18 Aug 2026',
|
||||
)
|
||||
})
|
||||
|
||||
it('formats a week or more ago as an absolute date', () => {
|
||||
expect(formatRelativeDate('2026-08-10T12:00:00Z', now)).toBe('10 Aug 2026')
|
||||
expect(formatRelativeDate('2026-08-10T12:00:00Z', now)).toBe(
|
||||
'10 Aug 2026',
|
||||
)
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user