quasar-ui-danx 0.4.99 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/danx.es.js +17884 -12732
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +192 -118
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +11 -2
- package/scripts/publish.sh +76 -0
- package/src/components/Utility/Code/CodeViewer.vue +31 -14
- package/src/components/Utility/Code/CodeViewerCollapsed.vue +2 -0
- package/src/components/Utility/Code/CodeViewerFooter.vue +1 -1
- package/src/components/Utility/Code/LanguageBadge.vue +278 -5
- package/src/components/Utility/Code/MarkdownContent.vue +160 -6
- package/src/components/Utility/Code/index.ts +3 -0
- package/src/components/Utility/Markdown/ContextMenu.vue +314 -0
- package/src/components/Utility/Markdown/HotkeyHelpPopover.vue +259 -0
- package/src/components/Utility/Markdown/LineTypeMenu.vue +226 -0
- package/src/components/Utility/Markdown/LinkPopover.vue +331 -0
- package/src/components/Utility/Markdown/MarkdownEditor.vue +228 -0
- package/src/components/Utility/Markdown/MarkdownEditorContent.vue +235 -0
- package/src/components/Utility/Markdown/MarkdownEditorFooter.vue +50 -0
- package/src/components/Utility/Markdown/TablePopover.vue +420 -0
- package/src/components/Utility/Markdown/index.ts +11 -0
- package/src/components/Utility/Markdown/types.ts +27 -0
- package/src/components/Utility/index.ts +1 -0
- package/src/composables/index.ts +1 -0
- package/src/composables/markdown/features/useBlockquotes.spec.ts +428 -0
- package/src/composables/markdown/features/useBlockquotes.ts +248 -0
- package/src/composables/markdown/features/useCodeBlockManager.ts +369 -0
- package/src/composables/markdown/features/useCodeBlocks.spec.ts +779 -0
- package/src/composables/markdown/features/useCodeBlocks.ts +774 -0
- package/src/composables/markdown/features/useContextMenu.ts +444 -0
- package/src/composables/markdown/features/useFocusTracking.ts +116 -0
- package/src/composables/markdown/features/useHeadings.spec.ts +834 -0
- package/src/composables/markdown/features/useHeadings.ts +290 -0
- package/src/composables/markdown/features/useInlineFormatting.spec.ts +705 -0
- package/src/composables/markdown/features/useInlineFormatting.ts +402 -0
- package/src/composables/markdown/features/useLineTypeMenu.ts +285 -0
- package/src/composables/markdown/features/useLinks.spec.ts +369 -0
- package/src/composables/markdown/features/useLinks.ts +374 -0
- package/src/composables/markdown/features/useLists.spec.ts +834 -0
- package/src/composables/markdown/features/useLists.ts +747 -0
- package/src/composables/markdown/features/usePopoverManager.ts +181 -0
- package/src/composables/markdown/features/useTables.spec.ts +1601 -0
- package/src/composables/markdown/features/useTables.ts +1107 -0
- package/src/composables/markdown/index.ts +16 -0
- package/src/composables/markdown/useMarkdownEditor.spec.ts +332 -0
- package/src/composables/markdown/useMarkdownEditor.ts +1068 -0
- package/src/composables/markdown/useMarkdownHotkeys.spec.ts +791 -0
- package/src/composables/markdown/useMarkdownHotkeys.ts +266 -0
- package/src/composables/markdown/useMarkdownSelection.ts +219 -0
- package/src/composables/markdown/useMarkdownSync.ts +549 -0
- package/src/composables/useCodeViewerEditor.spec.ts +655 -0
- package/src/composables/useCodeViewerEditor.ts +174 -20
- package/src/helpers/formats/index.ts +1 -1
- package/src/helpers/formats/markdown/escapeHtml.ts +15 -0
- package/src/helpers/formats/markdown/escapeSequences.ts +60 -0
- package/src/helpers/formats/markdown/htmlToMarkdown/convertHeadings.ts +41 -0
- package/src/helpers/formats/markdown/htmlToMarkdown/index.spec.ts +489 -0
- package/src/helpers/formats/markdown/htmlToMarkdown/index.ts +412 -0
- package/src/helpers/formats/markdown/index.ts +92 -0
- package/src/helpers/formats/markdown/linePatterns.spec.ts +495 -0
- package/src/helpers/formats/markdown/linePatterns.ts +172 -0
- package/src/helpers/formats/markdown/parseInline.ts +124 -0
- package/src/helpers/formats/markdown/render/index.ts +92 -0
- package/src/helpers/formats/markdown/render/renderFootnotes.ts +30 -0
- package/src/helpers/formats/markdown/render/renderList.ts +69 -0
- package/src/helpers/formats/markdown/render/renderTable.ts +38 -0
- package/src/helpers/formats/markdown/state.ts +58 -0
- package/src/helpers/formats/markdown/tokenize/extractDefinitions.ts +39 -0
- package/src/helpers/formats/markdown/tokenize/index.ts +139 -0
- package/src/helpers/formats/markdown/tokenize/parseBlockquote.ts +34 -0
- package/src/helpers/formats/markdown/tokenize/parseCodeBlock.ts +85 -0
- package/src/helpers/formats/markdown/tokenize/parseDefinitionList.ts +88 -0
- package/src/helpers/formats/markdown/tokenize/parseHeading.ts +65 -0
- package/src/helpers/formats/markdown/tokenize/parseHorizontalRule.ts +22 -0
- package/src/helpers/formats/markdown/tokenize/parseList.ts +119 -0
- package/src/helpers/formats/markdown/tokenize/parseParagraph.ts +59 -0
- package/src/helpers/formats/markdown/tokenize/parseTable.ts +70 -0
- package/src/helpers/formats/markdown/tokenize/parseTaskList.ts +47 -0
- package/src/helpers/formats/markdown/tokenize/utils.ts +25 -0
- package/src/helpers/formats/markdown/types.ts +63 -0
- package/src/styles/danx.scss +1 -0
- package/src/styles/themes/danx/markdown.scss +96 -0
- package/src/test/helpers/editorTestUtils.spec.ts +296 -0
- package/src/test/helpers/editorTestUtils.ts +253 -0
- package/src/test/helpers/index.ts +1 -0
- package/src/test/setup.test.ts +12 -0
- package/src/test/setup.ts +12 -0
- package/vitest.config.ts +19 -0
- package/src/helpers/formats/renderMarkdown.ts +0 -338
|
@@ -0,0 +1,834 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { useHeadings } from './useHeadings';
|
|
3
|
+
import { useMarkdownSelection } from '../useMarkdownSelection';
|
|
4
|
+
import { createTestEditor, TestEditorResult } from '../../../test/helpers/editorTestUtils';
|
|
5
|
+
|
|
6
|
+
describe('useHeadings', () => {
|
|
7
|
+
let editor: TestEditorResult;
|
|
8
|
+
let onContentChange: ReturnType<typeof vi.fn>;
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
if (editor) {
|
|
12
|
+
editor.destroy();
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create headings composable with test editor
|
|
18
|
+
*/
|
|
19
|
+
function createHeadings() {
|
|
20
|
+
const selection = useMarkdownSelection(editor.contentRef);
|
|
21
|
+
return useHeadings({
|
|
22
|
+
contentRef: editor.contentRef,
|
|
23
|
+
selection,
|
|
24
|
+
onContentChange,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe('setHeadingLevel', () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
onContentChange = vi.fn();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('converts paragraph to H1', () => {
|
|
34
|
+
editor = createTestEditor('<p>Hello world</p>');
|
|
35
|
+
const headings = createHeadings();
|
|
36
|
+
editor.setCursorInBlock(0, 5);
|
|
37
|
+
|
|
38
|
+
headings.setHeadingLevel(1);
|
|
39
|
+
|
|
40
|
+
expect(editor.getHtml()).toBe('<h1>Hello world</h1>');
|
|
41
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('converts paragraph to H2', () => {
|
|
45
|
+
editor = createTestEditor('<p>Hello world</p>');
|
|
46
|
+
const headings = createHeadings();
|
|
47
|
+
editor.setCursorInBlock(0, 0);
|
|
48
|
+
|
|
49
|
+
headings.setHeadingLevel(2);
|
|
50
|
+
|
|
51
|
+
expect(editor.getHtml()).toBe('<h2>Hello world</h2>');
|
|
52
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('converts paragraph to H3', () => {
|
|
56
|
+
editor = createTestEditor('<p>Hello world</p>');
|
|
57
|
+
const headings = createHeadings();
|
|
58
|
+
editor.setCursorInBlock(0, 0);
|
|
59
|
+
|
|
60
|
+
headings.setHeadingLevel(3);
|
|
61
|
+
|
|
62
|
+
expect(editor.getHtml()).toBe('<h3>Hello world</h3>');
|
|
63
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('converts paragraph to H4', () => {
|
|
67
|
+
editor = createTestEditor('<p>Hello world</p>');
|
|
68
|
+
const headings = createHeadings();
|
|
69
|
+
editor.setCursorInBlock(0, 0);
|
|
70
|
+
|
|
71
|
+
headings.setHeadingLevel(4);
|
|
72
|
+
|
|
73
|
+
expect(editor.getHtml()).toBe('<h4>Hello world</h4>');
|
|
74
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('converts paragraph to H5', () => {
|
|
78
|
+
editor = createTestEditor('<p>Hello world</p>');
|
|
79
|
+
const headings = createHeadings();
|
|
80
|
+
editor.setCursorInBlock(0, 0);
|
|
81
|
+
|
|
82
|
+
headings.setHeadingLevel(5);
|
|
83
|
+
|
|
84
|
+
expect(editor.getHtml()).toBe('<h5>Hello world</h5>');
|
|
85
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('converts paragraph to H6', () => {
|
|
89
|
+
editor = createTestEditor('<p>Hello world</p>');
|
|
90
|
+
const headings = createHeadings();
|
|
91
|
+
editor.setCursorInBlock(0, 0);
|
|
92
|
+
|
|
93
|
+
headings.setHeadingLevel(6);
|
|
94
|
+
|
|
95
|
+
expect(editor.getHtml()).toBe('<h6>Hello world</h6>');
|
|
96
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('converts H1 to paragraph (level 0)', () => {
|
|
100
|
+
editor = createTestEditor('<h1>Hello world</h1>');
|
|
101
|
+
const headings = createHeadings();
|
|
102
|
+
editor.setCursorInBlock(0, 0);
|
|
103
|
+
|
|
104
|
+
headings.setHeadingLevel(0);
|
|
105
|
+
|
|
106
|
+
expect(editor.getHtml()).toBe('<p>Hello world</p>');
|
|
107
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('converts H2 to H1', () => {
|
|
111
|
+
editor = createTestEditor('<h2>Hello world</h2>');
|
|
112
|
+
const headings = createHeadings();
|
|
113
|
+
editor.setCursorInBlock(0, 0);
|
|
114
|
+
|
|
115
|
+
headings.setHeadingLevel(1);
|
|
116
|
+
|
|
117
|
+
expect(editor.getHtml()).toBe('<h1>Hello world</h1>');
|
|
118
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('converts H6 to H3', () => {
|
|
122
|
+
editor = createTestEditor('<h6>Hello world</h6>');
|
|
123
|
+
const headings = createHeadings();
|
|
124
|
+
editor.setCursorInBlock(0, 0);
|
|
125
|
+
|
|
126
|
+
headings.setHeadingLevel(3);
|
|
127
|
+
|
|
128
|
+
expect(editor.getHtml()).toBe('<h3>Hello world</h3>');
|
|
129
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('does not change when already at target level', () => {
|
|
133
|
+
editor = createTestEditor('<h2>Hello world</h2>');
|
|
134
|
+
const headings = createHeadings();
|
|
135
|
+
editor.setCursorInBlock(0, 0);
|
|
136
|
+
|
|
137
|
+
headings.setHeadingLevel(2);
|
|
138
|
+
|
|
139
|
+
expect(editor.getHtml()).toBe('<h2>Hello world</h2>');
|
|
140
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('preserves cursor position after conversion', () => {
|
|
144
|
+
editor = createTestEditor('<p>Hello world</p>');
|
|
145
|
+
const headings = createHeadings();
|
|
146
|
+
editor.setCursorInBlock(0, 5);
|
|
147
|
+
|
|
148
|
+
headings.setHeadingLevel(1);
|
|
149
|
+
|
|
150
|
+
// After conversion, cursor should still be at position 5
|
|
151
|
+
const sel = window.getSelection();
|
|
152
|
+
expect(sel).not.toBeNull();
|
|
153
|
+
expect(sel!.rangeCount).toBeGreaterThan(0);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('preserves element attributes (except id)', () => {
|
|
157
|
+
editor = createTestEditor('<p class="test-class" data-custom="value">Hello</p>');
|
|
158
|
+
const headings = createHeadings();
|
|
159
|
+
editor.setCursorInBlock(0, 0);
|
|
160
|
+
|
|
161
|
+
headings.setHeadingLevel(1);
|
|
162
|
+
|
|
163
|
+
const h1 = editor.getBlock(0);
|
|
164
|
+
expect(h1?.getAttribute('class')).toBe('test-class');
|
|
165
|
+
expect(h1?.getAttribute('data-custom')).toBe('value');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('does not copy id attribute', () => {
|
|
169
|
+
editor = createTestEditor('<p id="my-id" class="test">Hello</p>');
|
|
170
|
+
const headings = createHeadings();
|
|
171
|
+
editor.setCursorInBlock(0, 0);
|
|
172
|
+
|
|
173
|
+
headings.setHeadingLevel(1);
|
|
174
|
+
|
|
175
|
+
const h1 = editor.getBlock(0);
|
|
176
|
+
expect(h1?.getAttribute('id')).toBeNull();
|
|
177
|
+
expect(h1?.getAttribute('class')).toBe('test');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('handles DIV elements (browsers often create DIV instead of P)', () => {
|
|
181
|
+
editor = createTestEditor('<div>Hello world</div>');
|
|
182
|
+
const headings = createHeadings();
|
|
183
|
+
editor.setCursorInBlock(0, 0);
|
|
184
|
+
|
|
185
|
+
headings.setHeadingLevel(1);
|
|
186
|
+
|
|
187
|
+
expect(editor.getHtml()).toBe('<h1>Hello world</h1>');
|
|
188
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('does nothing when not in a valid block', () => {
|
|
192
|
+
editor = createTestEditor('<ul><li>List item</li></ul>');
|
|
193
|
+
const headings = createHeadings();
|
|
194
|
+
// Set cursor in list item - not a heading/paragraph block
|
|
195
|
+
const li = editor.container.querySelector('li');
|
|
196
|
+
if (li?.firstChild) {
|
|
197
|
+
editor.setCursor(li.firstChild, 0);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
headings.setHeadingLevel(1);
|
|
201
|
+
|
|
202
|
+
// Should not change the list
|
|
203
|
+
expect(editor.getHtml()).toBe('<ul><li>List item</li></ul>');
|
|
204
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe('increaseHeadingLevel', () => {
|
|
209
|
+
beforeEach(() => {
|
|
210
|
+
onContentChange = vi.fn();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('converts paragraph to H6', () => {
|
|
214
|
+
editor = createTestEditor('<p>Hello world</p>');
|
|
215
|
+
const headings = createHeadings();
|
|
216
|
+
editor.setCursorInBlock(0, 0);
|
|
217
|
+
|
|
218
|
+
headings.increaseHeadingLevel();
|
|
219
|
+
|
|
220
|
+
expect(editor.getHtml()).toBe('<h6>Hello world</h6>');
|
|
221
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('converts H6 to H5', () => {
|
|
225
|
+
editor = createTestEditor('<h6>Hello world</h6>');
|
|
226
|
+
const headings = createHeadings();
|
|
227
|
+
editor.setCursorInBlock(0, 0);
|
|
228
|
+
|
|
229
|
+
headings.increaseHeadingLevel();
|
|
230
|
+
|
|
231
|
+
expect(editor.getHtml()).toBe('<h5>Hello world</h5>');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('converts H5 to H4', () => {
|
|
235
|
+
editor = createTestEditor('<h5>Hello world</h5>');
|
|
236
|
+
const headings = createHeadings();
|
|
237
|
+
editor.setCursorInBlock(0, 0);
|
|
238
|
+
|
|
239
|
+
headings.increaseHeadingLevel();
|
|
240
|
+
|
|
241
|
+
expect(editor.getHtml()).toBe('<h4>Hello world</h4>');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('converts H4 to H3', () => {
|
|
245
|
+
editor = createTestEditor('<h4>Hello world</h4>');
|
|
246
|
+
const headings = createHeadings();
|
|
247
|
+
editor.setCursorInBlock(0, 0);
|
|
248
|
+
|
|
249
|
+
headings.increaseHeadingLevel();
|
|
250
|
+
|
|
251
|
+
expect(editor.getHtml()).toBe('<h3>Hello world</h3>');
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('converts H3 to H2', () => {
|
|
255
|
+
editor = createTestEditor('<h3>Hello world</h3>');
|
|
256
|
+
const headings = createHeadings();
|
|
257
|
+
editor.setCursorInBlock(0, 0);
|
|
258
|
+
|
|
259
|
+
headings.increaseHeadingLevel();
|
|
260
|
+
|
|
261
|
+
expect(editor.getHtml()).toBe('<h2>Hello world</h2>');
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('converts H2 to H1', () => {
|
|
265
|
+
editor = createTestEditor('<h2>Hello world</h2>');
|
|
266
|
+
const headings = createHeadings();
|
|
267
|
+
editor.setCursorInBlock(0, 0);
|
|
268
|
+
|
|
269
|
+
headings.increaseHeadingLevel();
|
|
270
|
+
|
|
271
|
+
expect(editor.getHtml()).toBe('<h1>Hello world</h1>');
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('H1 stays at H1 (cannot increase further)', () => {
|
|
275
|
+
editor = createTestEditor('<h1>Hello world</h1>');
|
|
276
|
+
const headings = createHeadings();
|
|
277
|
+
editor.setCursorInBlock(0, 0);
|
|
278
|
+
|
|
279
|
+
headings.increaseHeadingLevel();
|
|
280
|
+
|
|
281
|
+
expect(editor.getHtml()).toBe('<h1>Hello world</h1>');
|
|
282
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('full progression: P -> H6 -> H5 -> H4 -> H3 -> H2 -> H1', () => {
|
|
286
|
+
editor = createTestEditor('<p>Hello</p>');
|
|
287
|
+
const headings = createHeadings();
|
|
288
|
+
editor.setCursorInBlock(0, 0);
|
|
289
|
+
|
|
290
|
+
headings.increaseHeadingLevel();
|
|
291
|
+
expect(editor.getHtml()).toBe('<h6>Hello</h6>');
|
|
292
|
+
|
|
293
|
+
headings.increaseHeadingLevel();
|
|
294
|
+
expect(editor.getHtml()).toBe('<h5>Hello</h5>');
|
|
295
|
+
|
|
296
|
+
headings.increaseHeadingLevel();
|
|
297
|
+
expect(editor.getHtml()).toBe('<h4>Hello</h4>');
|
|
298
|
+
|
|
299
|
+
headings.increaseHeadingLevel();
|
|
300
|
+
expect(editor.getHtml()).toBe('<h3>Hello</h3>');
|
|
301
|
+
|
|
302
|
+
headings.increaseHeadingLevel();
|
|
303
|
+
expect(editor.getHtml()).toBe('<h2>Hello</h2>');
|
|
304
|
+
|
|
305
|
+
headings.increaseHeadingLevel();
|
|
306
|
+
expect(editor.getHtml()).toBe('<h1>Hello</h1>');
|
|
307
|
+
|
|
308
|
+
// Should stay at H1
|
|
309
|
+
headings.increaseHeadingLevel();
|
|
310
|
+
expect(editor.getHtml()).toBe('<h1>Hello</h1>');
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('decreaseHeadingLevel', () => {
|
|
315
|
+
beforeEach(() => {
|
|
316
|
+
onContentChange = vi.fn();
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('converts H1 to H2', () => {
|
|
320
|
+
editor = createTestEditor('<h1>Hello world</h1>');
|
|
321
|
+
const headings = createHeadings();
|
|
322
|
+
editor.setCursorInBlock(0, 0);
|
|
323
|
+
|
|
324
|
+
headings.decreaseHeadingLevel();
|
|
325
|
+
|
|
326
|
+
expect(editor.getHtml()).toBe('<h2>Hello world</h2>');
|
|
327
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('converts H2 to H3', () => {
|
|
331
|
+
editor = createTestEditor('<h2>Hello world</h2>');
|
|
332
|
+
const headings = createHeadings();
|
|
333
|
+
editor.setCursorInBlock(0, 0);
|
|
334
|
+
|
|
335
|
+
headings.decreaseHeadingLevel();
|
|
336
|
+
|
|
337
|
+
expect(editor.getHtml()).toBe('<h3>Hello world</h3>');
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('converts H3 to H4', () => {
|
|
341
|
+
editor = createTestEditor('<h3>Hello world</h3>');
|
|
342
|
+
const headings = createHeadings();
|
|
343
|
+
editor.setCursorInBlock(0, 0);
|
|
344
|
+
|
|
345
|
+
headings.decreaseHeadingLevel();
|
|
346
|
+
|
|
347
|
+
expect(editor.getHtml()).toBe('<h4>Hello world</h4>');
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('converts H4 to H5', () => {
|
|
351
|
+
editor = createTestEditor('<h4>Hello world</h4>');
|
|
352
|
+
const headings = createHeadings();
|
|
353
|
+
editor.setCursorInBlock(0, 0);
|
|
354
|
+
|
|
355
|
+
headings.decreaseHeadingLevel();
|
|
356
|
+
|
|
357
|
+
expect(editor.getHtml()).toBe('<h5>Hello world</h5>');
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('converts H5 to H6', () => {
|
|
361
|
+
editor = createTestEditor('<h5>Hello world</h5>');
|
|
362
|
+
const headings = createHeadings();
|
|
363
|
+
editor.setCursorInBlock(0, 0);
|
|
364
|
+
|
|
365
|
+
headings.decreaseHeadingLevel();
|
|
366
|
+
|
|
367
|
+
expect(editor.getHtml()).toBe('<h6>Hello world</h6>');
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('converts H6 to paragraph', () => {
|
|
371
|
+
editor = createTestEditor('<h6>Hello world</h6>');
|
|
372
|
+
const headings = createHeadings();
|
|
373
|
+
editor.setCursorInBlock(0, 0);
|
|
374
|
+
|
|
375
|
+
headings.decreaseHeadingLevel();
|
|
376
|
+
|
|
377
|
+
expect(editor.getHtml()).toBe('<p>Hello world</p>');
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('paragraph stays at paragraph (cannot decrease further)', () => {
|
|
381
|
+
editor = createTestEditor('<p>Hello world</p>');
|
|
382
|
+
const headings = createHeadings();
|
|
383
|
+
editor.setCursorInBlock(0, 0);
|
|
384
|
+
|
|
385
|
+
headings.decreaseHeadingLevel();
|
|
386
|
+
|
|
387
|
+
expect(editor.getHtml()).toBe('<p>Hello world</p>');
|
|
388
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('full progression: H1 -> H2 -> H3 -> H4 -> H5 -> H6 -> P', () => {
|
|
392
|
+
editor = createTestEditor('<h1>Hello</h1>');
|
|
393
|
+
const headings = createHeadings();
|
|
394
|
+
editor.setCursorInBlock(0, 0);
|
|
395
|
+
|
|
396
|
+
headings.decreaseHeadingLevel();
|
|
397
|
+
expect(editor.getHtml()).toBe('<h2>Hello</h2>');
|
|
398
|
+
|
|
399
|
+
headings.decreaseHeadingLevel();
|
|
400
|
+
expect(editor.getHtml()).toBe('<h3>Hello</h3>');
|
|
401
|
+
|
|
402
|
+
headings.decreaseHeadingLevel();
|
|
403
|
+
expect(editor.getHtml()).toBe('<h4>Hello</h4>');
|
|
404
|
+
|
|
405
|
+
headings.decreaseHeadingLevel();
|
|
406
|
+
expect(editor.getHtml()).toBe('<h5>Hello</h5>');
|
|
407
|
+
|
|
408
|
+
headings.decreaseHeadingLevel();
|
|
409
|
+
expect(editor.getHtml()).toBe('<h6>Hello</h6>');
|
|
410
|
+
|
|
411
|
+
headings.decreaseHeadingLevel();
|
|
412
|
+
expect(editor.getHtml()).toBe('<p>Hello</p>');
|
|
413
|
+
|
|
414
|
+
// Should stay at P
|
|
415
|
+
headings.decreaseHeadingLevel();
|
|
416
|
+
expect(editor.getHtml()).toBe('<p>Hello</p>');
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
describe('getCurrentHeadingLevel', () => {
|
|
421
|
+
beforeEach(() => {
|
|
422
|
+
onContentChange = vi.fn();
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('returns 0 for paragraph', () => {
|
|
426
|
+
editor = createTestEditor('<p>Hello world</p>');
|
|
427
|
+
const headings = createHeadings();
|
|
428
|
+
editor.setCursorInBlock(0, 0);
|
|
429
|
+
|
|
430
|
+
expect(headings.getCurrentHeadingLevel()).toBe(0);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it('returns 0 for DIV (treated as paragraph)', () => {
|
|
434
|
+
editor = createTestEditor('<div>Hello world</div>');
|
|
435
|
+
const headings = createHeadings();
|
|
436
|
+
editor.setCursorInBlock(0, 0);
|
|
437
|
+
|
|
438
|
+
expect(headings.getCurrentHeadingLevel()).toBe(0);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('returns 1 for H1', () => {
|
|
442
|
+
editor = createTestEditor('<h1>Hello world</h1>');
|
|
443
|
+
const headings = createHeadings();
|
|
444
|
+
editor.setCursorInBlock(0, 0);
|
|
445
|
+
|
|
446
|
+
expect(headings.getCurrentHeadingLevel()).toBe(1);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('returns 2 for H2', () => {
|
|
450
|
+
editor = createTestEditor('<h2>Hello world</h2>');
|
|
451
|
+
const headings = createHeadings();
|
|
452
|
+
editor.setCursorInBlock(0, 0);
|
|
453
|
+
|
|
454
|
+
expect(headings.getCurrentHeadingLevel()).toBe(2);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it('returns 3 for H3', () => {
|
|
458
|
+
editor = createTestEditor('<h3>Hello world</h3>');
|
|
459
|
+
const headings = createHeadings();
|
|
460
|
+
editor.setCursorInBlock(0, 0);
|
|
461
|
+
|
|
462
|
+
expect(headings.getCurrentHeadingLevel()).toBe(3);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('returns 4 for H4', () => {
|
|
466
|
+
editor = createTestEditor('<h4>Hello world</h4>');
|
|
467
|
+
const headings = createHeadings();
|
|
468
|
+
editor.setCursorInBlock(0, 0);
|
|
469
|
+
|
|
470
|
+
expect(headings.getCurrentHeadingLevel()).toBe(4);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('returns 5 for H5', () => {
|
|
474
|
+
editor = createTestEditor('<h5>Hello world</h5>');
|
|
475
|
+
const headings = createHeadings();
|
|
476
|
+
editor.setCursorInBlock(0, 0);
|
|
477
|
+
|
|
478
|
+
expect(headings.getCurrentHeadingLevel()).toBe(5);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it('returns 6 for H6', () => {
|
|
482
|
+
editor = createTestEditor('<h6>Hello world</h6>');
|
|
483
|
+
const headings = createHeadings();
|
|
484
|
+
editor.setCursorInBlock(0, 0);
|
|
485
|
+
|
|
486
|
+
expect(headings.getCurrentHeadingLevel()).toBe(6);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it('returns -1 when not in heading/paragraph', () => {
|
|
490
|
+
editor = createTestEditor('<ul><li>List item</li></ul>');
|
|
491
|
+
const headings = createHeadings();
|
|
492
|
+
// Set cursor in list item
|
|
493
|
+
const li = editor.container.querySelector('li');
|
|
494
|
+
if (li?.firstChild) {
|
|
495
|
+
editor.setCursor(li.firstChild, 0);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
expect(headings.getCurrentHeadingLevel()).toBe(-1);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it('returns -1 when no selection', () => {
|
|
502
|
+
editor = createTestEditor('<p>Hello world</p>');
|
|
503
|
+
const headings = createHeadings();
|
|
504
|
+
// Clear selection
|
|
505
|
+
window.getSelection()?.removeAllRanges();
|
|
506
|
+
|
|
507
|
+
expect(headings.getCurrentHeadingLevel()).toBe(-1);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it('correctly identifies level when cursor is in middle of content', () => {
|
|
511
|
+
editor = createTestEditor('<h3>Hello world</h3>');
|
|
512
|
+
const headings = createHeadings();
|
|
513
|
+
editor.setCursorInBlock(0, 6); // Middle of "Hello world"
|
|
514
|
+
|
|
515
|
+
expect(headings.getCurrentHeadingLevel()).toBe(3);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it('handles multiple blocks correctly', () => {
|
|
519
|
+
editor = createTestEditor('<h1>First</h1><p>Second</p><h3>Third</h3>');
|
|
520
|
+
const headings = createHeadings();
|
|
521
|
+
|
|
522
|
+
editor.setCursorInBlock(0, 0);
|
|
523
|
+
expect(headings.getCurrentHeadingLevel()).toBe(1);
|
|
524
|
+
|
|
525
|
+
editor.setCursorInBlock(1, 0);
|
|
526
|
+
expect(headings.getCurrentHeadingLevel()).toBe(0);
|
|
527
|
+
|
|
528
|
+
editor.setCursorInBlock(2, 0);
|
|
529
|
+
expect(headings.getCurrentHeadingLevel()).toBe(3);
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
describe('checkAndConvertHeadingPattern', () => {
|
|
534
|
+
beforeEach(() => {
|
|
535
|
+
onContentChange = vi.fn();
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it('converts "# Hello" to H1', () => {
|
|
539
|
+
editor = createTestEditor('<p># Hello</p>');
|
|
540
|
+
const headings = createHeadings();
|
|
541
|
+
editor.setCursorInBlock(0, 7); // After "# Hello"
|
|
542
|
+
|
|
543
|
+
const result = headings.checkAndConvertHeadingPattern();
|
|
544
|
+
|
|
545
|
+
expect(result).toBe(true);
|
|
546
|
+
expect(editor.getHtml()).toBe('<h1>Hello</h1>');
|
|
547
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
it('converts "## World" to H2', () => {
|
|
551
|
+
editor = createTestEditor('<p>## World</p>');
|
|
552
|
+
const headings = createHeadings();
|
|
553
|
+
editor.setCursorInBlock(0, 8);
|
|
554
|
+
|
|
555
|
+
const result = headings.checkAndConvertHeadingPattern();
|
|
556
|
+
|
|
557
|
+
expect(result).toBe(true);
|
|
558
|
+
expect(editor.getHtml()).toBe('<h2>World</h2>');
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it('converts "### Test" to H3', () => {
|
|
562
|
+
editor = createTestEditor('<p>### Test</p>');
|
|
563
|
+
const headings = createHeadings();
|
|
564
|
+
editor.setCursorInBlock(0, 8);
|
|
565
|
+
|
|
566
|
+
const result = headings.checkAndConvertHeadingPattern();
|
|
567
|
+
|
|
568
|
+
expect(result).toBe(true);
|
|
569
|
+
expect(editor.getHtml()).toBe('<h3>Test</h3>');
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
it('converts "#### Test" to H4', () => {
|
|
573
|
+
editor = createTestEditor('<p>#### Test</p>');
|
|
574
|
+
const headings = createHeadings();
|
|
575
|
+
editor.setCursorInBlock(0, 9);
|
|
576
|
+
|
|
577
|
+
const result = headings.checkAndConvertHeadingPattern();
|
|
578
|
+
|
|
579
|
+
expect(result).toBe(true);
|
|
580
|
+
expect(editor.getHtml()).toBe('<h4>Test</h4>');
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
it('converts "##### Test" to H5', () => {
|
|
584
|
+
editor = createTestEditor('<p>##### Test</p>');
|
|
585
|
+
const headings = createHeadings();
|
|
586
|
+
editor.setCursorInBlock(0, 10);
|
|
587
|
+
|
|
588
|
+
const result = headings.checkAndConvertHeadingPattern();
|
|
589
|
+
|
|
590
|
+
expect(result).toBe(true);
|
|
591
|
+
expect(editor.getHtml()).toBe('<h5>Test</h5>');
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it('converts "###### Test" to H6', () => {
|
|
595
|
+
editor = createTestEditor('<p>###### Test</p>');
|
|
596
|
+
const headings = createHeadings();
|
|
597
|
+
editor.setCursorInBlock(0, 11);
|
|
598
|
+
|
|
599
|
+
const result = headings.checkAndConvertHeadingPattern();
|
|
600
|
+
|
|
601
|
+
expect(result).toBe(true);
|
|
602
|
+
expect(editor.getHtml()).toBe('<h6>Test</h6>');
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
it('returns false when no pattern is present', () => {
|
|
606
|
+
editor = createTestEditor('<p>Hello world</p>');
|
|
607
|
+
const headings = createHeadings();
|
|
608
|
+
editor.setCursorInBlock(0, 5);
|
|
609
|
+
|
|
610
|
+
const result = headings.checkAndConvertHeadingPattern();
|
|
611
|
+
|
|
612
|
+
expect(result).toBe(false);
|
|
613
|
+
expect(editor.getHtml()).toBe('<p>Hello world</p>');
|
|
614
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
it('returns false for "# " without content', () => {
|
|
618
|
+
editor = createTestEditor('<p># </p>');
|
|
619
|
+
const headings = createHeadings();
|
|
620
|
+
editor.setCursorInBlock(0, 2);
|
|
621
|
+
|
|
622
|
+
const result = headings.checkAndConvertHeadingPattern();
|
|
623
|
+
|
|
624
|
+
expect(result).toBe(false);
|
|
625
|
+
expect(editor.getHtml()).toBe('<p># </p>');
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
it('returns false for "#" without space', () => {
|
|
629
|
+
editor = createTestEditor('<p>#Hello</p>');
|
|
630
|
+
const headings = createHeadings();
|
|
631
|
+
editor.setCursorInBlock(0, 6);
|
|
632
|
+
|
|
633
|
+
const result = headings.checkAndConvertHeadingPattern();
|
|
634
|
+
|
|
635
|
+
expect(result).toBe(false);
|
|
636
|
+
expect(editor.getHtml()).toBe('<p>#Hello</p>');
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
it('does not convert if already a heading', () => {
|
|
640
|
+
editor = createTestEditor('<h1># Hello</h1>');
|
|
641
|
+
const headings = createHeadings();
|
|
642
|
+
editor.setCursorInBlock(0, 7);
|
|
643
|
+
|
|
644
|
+
const result = headings.checkAndConvertHeadingPattern();
|
|
645
|
+
|
|
646
|
+
expect(result).toBe(false);
|
|
647
|
+
expect(editor.getHtml()).toBe('<h1># Hello</h1>');
|
|
648
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
it('converts DIV elements (browser default)', () => {
|
|
652
|
+
editor = createTestEditor('<div>## World</div>');
|
|
653
|
+
const headings = createHeadings();
|
|
654
|
+
editor.setCursorInBlock(0, 8);
|
|
655
|
+
|
|
656
|
+
const result = headings.checkAndConvertHeadingPattern();
|
|
657
|
+
|
|
658
|
+
expect(result).toBe(true);
|
|
659
|
+
expect(editor.getHtml()).toBe('<h2>World</h2>');
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
it('positions cursor at end of content after conversion', () => {
|
|
663
|
+
editor = createTestEditor('<p># Hello</p>');
|
|
664
|
+
const headings = createHeadings();
|
|
665
|
+
editor.setCursorInBlock(0, 7);
|
|
666
|
+
|
|
667
|
+
headings.checkAndConvertHeadingPattern();
|
|
668
|
+
|
|
669
|
+
// Cursor should be at end of "Hello" (offset 5)
|
|
670
|
+
const sel = window.getSelection();
|
|
671
|
+
expect(sel).not.toBeNull();
|
|
672
|
+
expect(sel!.rangeCount).toBeGreaterThan(0);
|
|
673
|
+
const range = sel!.getRangeAt(0);
|
|
674
|
+
expect(range.collapsed).toBe(true);
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
it('preserves attributes (except id) during pattern conversion', () => {
|
|
678
|
+
editor = createTestEditor('<p class="test" data-foo="bar"># Hello</p>');
|
|
679
|
+
const headings = createHeadings();
|
|
680
|
+
editor.setCursorInBlock(0, 7);
|
|
681
|
+
|
|
682
|
+
headings.checkAndConvertHeadingPattern();
|
|
683
|
+
|
|
684
|
+
const h1 = editor.getBlock(0);
|
|
685
|
+
expect(h1?.getAttribute('class')).toBe('test');
|
|
686
|
+
expect(h1?.getAttribute('data-foo')).toBe('bar');
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
it('handles content with special characters', () => {
|
|
690
|
+
editor = createTestEditor('<p>## Hello! @world #tag</p>');
|
|
691
|
+
const headings = createHeadings();
|
|
692
|
+
editor.setCursorInBlock(0, 21);
|
|
693
|
+
|
|
694
|
+
const result = headings.checkAndConvertHeadingPattern();
|
|
695
|
+
|
|
696
|
+
expect(result).toBe(true);
|
|
697
|
+
expect(editor.getHtml()).toBe('<h2>Hello! @world #tag</h2>');
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
it('handles content with multiple spaces after hash', () => {
|
|
701
|
+
editor = createTestEditor('<p># Hello</p>');
|
|
702
|
+
const headings = createHeadings();
|
|
703
|
+
editor.setCursorInBlock(0, 8);
|
|
704
|
+
|
|
705
|
+
const result = headings.checkAndConvertHeadingPattern();
|
|
706
|
+
|
|
707
|
+
expect(result).toBe(true);
|
|
708
|
+
expect(editor.getHtml()).toBe('<h1>Hello</h1>');
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
describe('edge cases', () => {
|
|
713
|
+
beforeEach(() => {
|
|
714
|
+
onContentChange = vi.fn();
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
it('handles empty paragraph', () => {
|
|
718
|
+
editor = createTestEditor('<p></p>');
|
|
719
|
+
const headings = createHeadings();
|
|
720
|
+
// Can't set cursor in empty block, use direct approach
|
|
721
|
+
const p = editor.getBlock(0);
|
|
722
|
+
if (p) {
|
|
723
|
+
const range = document.createRange();
|
|
724
|
+
range.selectNodeContents(p);
|
|
725
|
+
range.collapse(true);
|
|
726
|
+
const sel = window.getSelection();
|
|
727
|
+
sel?.removeAllRanges();
|
|
728
|
+
sel?.addRange(range);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
headings.setHeadingLevel(1);
|
|
732
|
+
|
|
733
|
+
expect(editor.getHtml()).toBe('<h1></h1>');
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
it('handles nested inline elements', () => {
|
|
737
|
+
editor = createTestEditor('<p>Hello <strong><em>bold italic</em></strong> world</p>');
|
|
738
|
+
const headings = createHeadings();
|
|
739
|
+
editor.setCursorInBlock(0, 0);
|
|
740
|
+
|
|
741
|
+
headings.setHeadingLevel(2);
|
|
742
|
+
|
|
743
|
+
// Should preserve nested formatting
|
|
744
|
+
expect(editor.getHtml()).toBe('<h2>Hello <strong><em>bold italic</em></strong> world</h2>');
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
it('handles multiple blocks - only affects current block', () => {
|
|
748
|
+
editor = createTestEditor('<p>First</p><p>Second</p><p>Third</p>');
|
|
749
|
+
const headings = createHeadings();
|
|
750
|
+
editor.setCursorInBlock(1, 0); // Cursor in second block
|
|
751
|
+
|
|
752
|
+
headings.setHeadingLevel(2);
|
|
753
|
+
|
|
754
|
+
expect(editor.getHtml()).toBe('<p>First</p><h2>Second</h2><p>Third</p>');
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
it('handles content with links', () => {
|
|
758
|
+
editor = createTestEditor('<p>Hello <a href="https://example.com">link</a> world</p>');
|
|
759
|
+
const headings = createHeadings();
|
|
760
|
+
editor.setCursorInBlock(0, 0);
|
|
761
|
+
|
|
762
|
+
headings.setHeadingLevel(1);
|
|
763
|
+
|
|
764
|
+
expect(editor.getHtml()).toBe('<h1>Hello <a href="https://example.com">link</a> world</h1>');
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
it('handles whitespace-only content', () => {
|
|
768
|
+
editor = createTestEditor('<p> </p>');
|
|
769
|
+
const headings = createHeadings();
|
|
770
|
+
editor.setCursorInBlock(0, 1);
|
|
771
|
+
|
|
772
|
+
headings.setHeadingLevel(3);
|
|
773
|
+
|
|
774
|
+
expect(editor.getHtml()).toBe('<h3> </h3>');
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
describe('list to heading workflow', () => {
|
|
779
|
+
/**
|
|
780
|
+
* These tests verify the workflow where a list item is first
|
|
781
|
+
* converted to a paragraph, then setHeadingLevel is called.
|
|
782
|
+
* This simulates the LineTypeMenu behavior in MarkdownEditor.vue.
|
|
783
|
+
*/
|
|
784
|
+
beforeEach(() => {
|
|
785
|
+
onContentChange = vi.fn();
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
it('setHeadingLevel works on paragraph created from list item', () => {
|
|
789
|
+
// Start with a list, manually convert to paragraph, then apply heading
|
|
790
|
+
editor = createTestEditor('<p>Former list item</p>');
|
|
791
|
+
const headings = createHeadings();
|
|
792
|
+
editor.setCursorInBlock(0, 0);
|
|
793
|
+
|
|
794
|
+
headings.setHeadingLevel(1);
|
|
795
|
+
|
|
796
|
+
expect(editor.getHtml()).toBe('<h1>Former list item</h1>');
|
|
797
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
it('setHeadingLevel with level 0 keeps paragraph', () => {
|
|
801
|
+
editor = createTestEditor('<p>Test paragraph</p>');
|
|
802
|
+
const headings = createHeadings();
|
|
803
|
+
editor.setCursorInBlock(0, 0);
|
|
804
|
+
|
|
805
|
+
// Setting level 0 should keep it as paragraph
|
|
806
|
+
headings.setHeadingLevel(0);
|
|
807
|
+
|
|
808
|
+
expect(editor.getHtml()).toBe('<p>Test paragraph</p>');
|
|
809
|
+
// Not called because already at level 0
|
|
810
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
it('converts to all heading levels correctly after list-to-paragraph', () => {
|
|
814
|
+
const testCases = [
|
|
815
|
+
{ level: 1, tag: 'h1' },
|
|
816
|
+
{ level: 2, tag: 'h2' },
|
|
817
|
+
{ level: 3, tag: 'h3' },
|
|
818
|
+
{ level: 4, tag: 'h4' },
|
|
819
|
+
{ level: 5, tag: 'h5' },
|
|
820
|
+
{ level: 6, tag: 'h6' },
|
|
821
|
+
];
|
|
822
|
+
|
|
823
|
+
for (const { level, tag } of testCases) {
|
|
824
|
+
editor = createTestEditor('<p>Test content</p>');
|
|
825
|
+
const headings = createHeadings();
|
|
826
|
+
editor.setCursorInBlock(0, 0);
|
|
827
|
+
|
|
828
|
+
headings.setHeadingLevel(level as 0 | 1 | 2 | 3 | 4 | 5 | 6);
|
|
829
|
+
|
|
830
|
+
expect(editor.getHtml()).toBe(`<${tag}>Test content</${tag}>`);
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
});
|
|
834
|
+
});
|