quasar-ui-danx 0.5.0 → 0.5.2
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/.claude/settings.local.json +8 -0
- package/dist/danx.es.js +16119 -10641
- package/dist/danx.es.js.map +1 -1
- package/dist/danx.umd.js +202 -123
- package/dist/danx.umd.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +8 -1
- package/src/components/Utility/Buttons/ActionButton.vue +15 -5
- package/src/components/Utility/Code/CodeViewer.vue +41 -16
- package/src/components/Utility/Code/CodeViewerCollapsed.vue +2 -0
- package/src/components/Utility/Code/CodeViewerFooter.vue +3 -1
- package/src/components/Utility/Code/LanguageBadge.vue +278 -5
- package/src/components/Utility/Code/MarkdownContent.vue +31 -163
- 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 +233 -0
- package/src/components/Utility/Markdown/MarkdownEditorContent.vue +296 -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/Widgets/LabelPillWidget.vue +20 -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 +805 -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 +388 -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 +1077 -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/useCodeFormat.ts +17 -10
- package/src/composables/useCodeViewerEditor.spec.ts +655 -0
- package/src/composables/useCodeViewerEditor.ts +174 -20
- package/src/helpers/formats/highlightCSS.ts +236 -0
- package/src/helpers/formats/highlightHTML.ts +483 -0
- package/src/helpers/formats/highlightJavaScript.ts +346 -0
- package/src/helpers/formats/highlightSyntax.ts +15 -4
- package/src/helpers/formats/index.ts +3 -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 +425 -0
- package/src/helpers/formats/markdown/index.ts +7 -0
- package/src/helpers/formats/markdown/linePatterns.spec.ts +498 -0
- package/src/helpers/formats/markdown/linePatterns.ts +172 -0
- package/src/styles/danx.scss +3 -3
- package/src/styles/index.scss +5 -5
- package/src/styles/themes/danx/code.scss +257 -1
- package/src/styles/themes/danx/index.scss +10 -10
- package/src/styles/themes/danx/markdown.scss +59 -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/highlighters.test.ts +153 -0
- package/src/test/setup.test.ts +12 -0
- package/src/test/setup.ts +12 -0
- package/src/types/widgets.d.ts +2 -2
- package/vite.config.js +5 -1
- package/vitest.config.ts +19 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { useLinks } from "./useLinks";
|
|
3
|
+
import { createTestEditor, TestEditorResult } from "../../../test/helpers/editorTestUtils";
|
|
4
|
+
|
|
5
|
+
// Mock getBoundingClientRect for Range since jsdom doesn't implement it
|
|
6
|
+
Range.prototype.getBoundingClientRect = vi.fn(() => ({
|
|
7
|
+
x: 0,
|
|
8
|
+
y: 0,
|
|
9
|
+
width: 100,
|
|
10
|
+
height: 20,
|
|
11
|
+
top: 0,
|
|
12
|
+
right: 100,
|
|
13
|
+
bottom: 20,
|
|
14
|
+
left: 0,
|
|
15
|
+
toJSON: () => ({})
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
describe("useLinks", () => {
|
|
19
|
+
let editor: TestEditorResult;
|
|
20
|
+
let onContentChange: ReturnType<typeof vi.fn>;
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
if (editor) {
|
|
24
|
+
editor.destroy();
|
|
25
|
+
}
|
|
26
|
+
vi.restoreAllMocks();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
function createLinks() {
|
|
30
|
+
return useLinks({
|
|
31
|
+
contentRef: editor.contentRef,
|
|
32
|
+
onContentChange
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe("insertLink", () => {
|
|
37
|
+
describe("with selected text", () => {
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
onContentChange = vi.fn();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("wraps selected text in anchor tag with prompted URL", () => {
|
|
43
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
44
|
+
vi.spyOn(window, "prompt").mockReturnValue("https://example.com");
|
|
45
|
+
const links = createLinks();
|
|
46
|
+
editor.selectInBlock(0, 0, 5); // Select "Hello"
|
|
47
|
+
|
|
48
|
+
links.insertLink();
|
|
49
|
+
|
|
50
|
+
expect(editor.getHtml()).toBe('<p><a href="https://example.com" target="_blank" rel="noopener noreferrer">Hello</a> world</p>');
|
|
51
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("wraps middle text in anchor tag", () => {
|
|
55
|
+
editor = createTestEditor("<p>Hello beautiful world</p>");
|
|
56
|
+
vi.spyOn(window, "prompt").mockReturnValue("https://test.com");
|
|
57
|
+
const links = createLinks();
|
|
58
|
+
editor.selectInBlock(0, 6, 15); // Select "beautiful"
|
|
59
|
+
|
|
60
|
+
links.insertLink();
|
|
61
|
+
|
|
62
|
+
expect(editor.getHtml()).toBe('<p>Hello <a href="https://test.com" target="_blank" rel="noopener noreferrer">beautiful</a> world</p>');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("trims whitespace from URL", () => {
|
|
66
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
67
|
+
vi.spyOn(window, "prompt").mockReturnValue(" https://example.com ");
|
|
68
|
+
const links = createLinks();
|
|
69
|
+
editor.selectInBlock(0, 0, 5);
|
|
70
|
+
|
|
71
|
+
links.insertLink();
|
|
72
|
+
|
|
73
|
+
expect(editor.getHtml()).toBe('<p><a href="https://example.com" target="_blank" rel="noopener noreferrer">Hello</a> world</p>');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("does nothing when user cancels prompt", () => {
|
|
77
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
78
|
+
vi.spyOn(window, "prompt").mockReturnValue(null);
|
|
79
|
+
const links = createLinks();
|
|
80
|
+
editor.selectInBlock(0, 0, 5);
|
|
81
|
+
|
|
82
|
+
links.insertLink();
|
|
83
|
+
|
|
84
|
+
expect(editor.getHtml()).toBe("<p>Hello world</p>");
|
|
85
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("does nothing when user enters empty URL", () => {
|
|
89
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
90
|
+
vi.spyOn(window, "prompt").mockReturnValue("");
|
|
91
|
+
const links = createLinks();
|
|
92
|
+
editor.selectInBlock(0, 0, 5);
|
|
93
|
+
|
|
94
|
+
links.insertLink();
|
|
95
|
+
|
|
96
|
+
expect(editor.getHtml()).toBe("<p>Hello world</p>");
|
|
97
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("does nothing when user enters whitespace-only URL", () => {
|
|
101
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
102
|
+
vi.spyOn(window, "prompt").mockReturnValue(" ");
|
|
103
|
+
const links = createLinks();
|
|
104
|
+
editor.selectInBlock(0, 0, 5);
|
|
105
|
+
|
|
106
|
+
links.insertLink();
|
|
107
|
+
|
|
108
|
+
expect(editor.getHtml()).toBe("<p>Hello world</p>");
|
|
109
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("wraps content and link is properly created", () => {
|
|
113
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
114
|
+
vi.spyOn(window, "prompt").mockReturnValue("https://example.com");
|
|
115
|
+
const links = createLinks();
|
|
116
|
+
editor.selectInBlock(0, 0, 5);
|
|
117
|
+
|
|
118
|
+
links.insertLink();
|
|
119
|
+
|
|
120
|
+
// Verify the link was created with correct text
|
|
121
|
+
const linkEl = editor.container.querySelector("a");
|
|
122
|
+
expect(linkEl).not.toBeNull();
|
|
123
|
+
expect(linkEl?.textContent).toBe("Hello");
|
|
124
|
+
expect(linkEl?.getAttribute("href")).toBe("https://example.com");
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe("without selection (cursor only)", () => {
|
|
129
|
+
beforeEach(() => {
|
|
130
|
+
onContentChange = vi.fn();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("inserts link with URL as text when no selection", () => {
|
|
134
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
135
|
+
vi.spyOn(window, "prompt").mockReturnValue("https://example.com");
|
|
136
|
+
const links = createLinks();
|
|
137
|
+
editor.setCursorInBlock(0, 6); // After "Hello "
|
|
138
|
+
|
|
139
|
+
links.insertLink();
|
|
140
|
+
|
|
141
|
+
expect(editor.getHtml()).toContain('<a href="https://example.com" target="_blank" rel="noopener noreferrer">https://example.com</a>');
|
|
142
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("does nothing when user cancels prompt", () => {
|
|
146
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
147
|
+
vi.spyOn(window, "prompt").mockReturnValue(null);
|
|
148
|
+
const links = createLinks();
|
|
149
|
+
editor.setCursorInBlock(0, 6);
|
|
150
|
+
|
|
151
|
+
links.insertLink();
|
|
152
|
+
|
|
153
|
+
expect(editor.getHtml()).toBe("<p>Hello world</p>");
|
|
154
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("does nothing when user enters empty URL", () => {
|
|
158
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
159
|
+
vi.spyOn(window, "prompt").mockReturnValue("");
|
|
160
|
+
const links = createLinks();
|
|
161
|
+
editor.setCursorInBlock(0, 6);
|
|
162
|
+
|
|
163
|
+
links.insertLink();
|
|
164
|
+
|
|
165
|
+
expect(editor.getHtml()).toBe("<p>Hello world</p>");
|
|
166
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("inserts link and link is properly created", () => {
|
|
170
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
171
|
+
vi.spyOn(window, "prompt").mockReturnValue("https://example.com");
|
|
172
|
+
const links = createLinks();
|
|
173
|
+
editor.setCursorInBlock(0, 6);
|
|
174
|
+
|
|
175
|
+
links.insertLink();
|
|
176
|
+
|
|
177
|
+
// Verify the link was created with URL as text
|
|
178
|
+
const linkEl = editor.container.querySelector("a");
|
|
179
|
+
expect(linkEl).not.toBeNull();
|
|
180
|
+
expect(linkEl?.textContent).toBe("https://example.com");
|
|
181
|
+
expect(linkEl?.getAttribute("href")).toBe("https://example.com");
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
describe("editing existing link", () => {
|
|
186
|
+
beforeEach(() => {
|
|
187
|
+
onContentChange = vi.fn();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("prompts with current URL prefilled when cursor is in link", () => {
|
|
191
|
+
editor = createTestEditor('<p><a href="https://old.com">Hello</a> world</p>');
|
|
192
|
+
const promptSpy = vi.spyOn(window, "prompt").mockReturnValue("https://new.com");
|
|
193
|
+
const links = createLinks();
|
|
194
|
+
const linkEl = editor.container.querySelector("a");
|
|
195
|
+
editor.setCursor(linkEl!.firstChild!, 3); // Inside "Hello"
|
|
196
|
+
|
|
197
|
+
links.insertLink();
|
|
198
|
+
|
|
199
|
+
expect(promptSpy).toHaveBeenCalledWith("Edit link URL:", "https://old.com");
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("updates URL when user provides new URL", () => {
|
|
203
|
+
editor = createTestEditor('<p><a href="https://old.com">Hello</a> world</p>');
|
|
204
|
+
vi.spyOn(window, "prompt").mockReturnValue("https://new.com");
|
|
205
|
+
const links = createLinks();
|
|
206
|
+
const linkEl = editor.container.querySelector("a");
|
|
207
|
+
editor.setCursor(linkEl!.firstChild!, 3);
|
|
208
|
+
|
|
209
|
+
links.insertLink();
|
|
210
|
+
|
|
211
|
+
expect(editor.getHtml()).toBe('<p><a href="https://new.com">Hello</a> world</p>');
|
|
212
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("removes link when user enters empty URL", () => {
|
|
216
|
+
editor = createTestEditor('<p><a href="https://example.com">Hello</a> world</p>');
|
|
217
|
+
vi.spyOn(window, "prompt").mockReturnValue("");
|
|
218
|
+
const links = createLinks();
|
|
219
|
+
const linkEl = editor.container.querySelector("a");
|
|
220
|
+
editor.setCursor(linkEl!.firstChild!, 3);
|
|
221
|
+
|
|
222
|
+
links.insertLink();
|
|
223
|
+
|
|
224
|
+
expect(editor.getHtml()).toBe("<p>Hello world</p>");
|
|
225
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("does nothing when user cancels prompt on existing link", () => {
|
|
229
|
+
editor = createTestEditor('<p><a href="https://example.com">Hello</a> world</p>');
|
|
230
|
+
vi.spyOn(window, "prompt").mockReturnValue(null);
|
|
231
|
+
const links = createLinks();
|
|
232
|
+
const linkEl = editor.container.querySelector("a");
|
|
233
|
+
editor.setCursor(linkEl!.firstChild!, 3);
|
|
234
|
+
|
|
235
|
+
links.insertLink();
|
|
236
|
+
|
|
237
|
+
expect(editor.getHtml()).toBe('<p><a href="https://example.com">Hello</a> world</p>');
|
|
238
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("handles link with empty href attribute", () => {
|
|
242
|
+
editor = createTestEditor('<p><a href="">Hello</a> world</p>');
|
|
243
|
+
const promptSpy = vi.spyOn(window, "prompt").mockReturnValue("https://new.com");
|
|
244
|
+
const links = createLinks();
|
|
245
|
+
const linkEl = editor.container.querySelector("a");
|
|
246
|
+
editor.setCursor(linkEl!.firstChild!, 3);
|
|
247
|
+
|
|
248
|
+
links.insertLink();
|
|
249
|
+
|
|
250
|
+
expect(promptSpy).toHaveBeenCalledWith("Edit link URL:", "");
|
|
251
|
+
expect(editor.getHtml()).toBe('<p><a href="https://new.com">Hello</a> world</p>');
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe("isInLink", () => {
|
|
257
|
+
beforeEach(() => {
|
|
258
|
+
onContentChange = vi.fn();
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("returns true when cursor is inside a link", () => {
|
|
262
|
+
editor = createTestEditor('<p><a href="https://example.com">Hello</a> world</p>');
|
|
263
|
+
const links = createLinks();
|
|
264
|
+
const linkEl = editor.container.querySelector("a");
|
|
265
|
+
editor.setCursor(linkEl!.firstChild!, 3);
|
|
266
|
+
|
|
267
|
+
expect(links.isInLink()).toBe(true);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("returns false when cursor is outside a link", () => {
|
|
271
|
+
editor = createTestEditor('<p><a href="https://example.com">Hello</a> world</p>');
|
|
272
|
+
const links = createLinks();
|
|
273
|
+
editor.setCursorInBlock(0, 8); // In " world"
|
|
274
|
+
|
|
275
|
+
expect(links.isInLink()).toBe(false);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("returns false when there are no links in content", () => {
|
|
279
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
280
|
+
const links = createLinks();
|
|
281
|
+
editor.setCursorInBlock(0, 5);
|
|
282
|
+
|
|
283
|
+
expect(links.isInLink()).toBe(false);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("returns false when contentRef is null", () => {
|
|
287
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
288
|
+
const links = useLinks({
|
|
289
|
+
contentRef: { value: null },
|
|
290
|
+
onContentChange
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
expect(links.isInLink()).toBe(false);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it("returns false when no selection exists", () => {
|
|
297
|
+
editor = createTestEditor('<p><a href="https://example.com">Hello</a> world</p>');
|
|
298
|
+
const links = createLinks();
|
|
299
|
+
|
|
300
|
+
// Clear any selection
|
|
301
|
+
window.getSelection()?.removeAllRanges();
|
|
302
|
+
|
|
303
|
+
expect(links.isInLink()).toBe(false);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe("edge cases", () => {
|
|
308
|
+
beforeEach(() => {
|
|
309
|
+
onContentChange = vi.fn();
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it("does nothing when contentRef is null", () => {
|
|
313
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
314
|
+
const links = useLinks({
|
|
315
|
+
contentRef: { value: null },
|
|
316
|
+
onContentChange
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
links.insertLink();
|
|
320
|
+
|
|
321
|
+
expect(editor.getHtml()).toBe("<p>Hello world</p>");
|
|
322
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it("does nothing when selection is outside content area", () => {
|
|
326
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
327
|
+
const links = createLinks();
|
|
328
|
+
|
|
329
|
+
// Create a selection outside the editor
|
|
330
|
+
const externalDiv = document.createElement("div");
|
|
331
|
+
externalDiv.textContent = "External text";
|
|
332
|
+
document.body.appendChild(externalDiv);
|
|
333
|
+
|
|
334
|
+
const range = document.createRange();
|
|
335
|
+
range.selectNodeContents(externalDiv);
|
|
336
|
+
const sel = window.getSelection();
|
|
337
|
+
sel?.removeAllRanges();
|
|
338
|
+
sel?.addRange(range);
|
|
339
|
+
|
|
340
|
+
links.insertLink();
|
|
341
|
+
|
|
342
|
+
expect(editor.getHtml()).toBe("<p>Hello world</p>");
|
|
343
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
344
|
+
|
|
345
|
+
externalDiv.remove();
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it("does nothing when no selection exists", () => {
|
|
349
|
+
editor = createTestEditor("<p>Hello world</p>");
|
|
350
|
+
const links = createLinks();
|
|
351
|
+
|
|
352
|
+
// Clear any selection
|
|
353
|
+
window.getSelection()?.removeAllRanges();
|
|
354
|
+
|
|
355
|
+
links.insertLink();
|
|
356
|
+
|
|
357
|
+
expect(editor.getHtml()).toBe("<p>Hello world</p>");
|
|
358
|
+
expect(onContentChange).not.toHaveBeenCalled();
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it("handles nested elements within selection", () => {
|
|
362
|
+
editor = createTestEditor("<p><strong>Hello</strong> world</p>");
|
|
363
|
+
vi.spyOn(window, "prompt").mockReturnValue("https://example.com");
|
|
364
|
+
const links = createLinks();
|
|
365
|
+
editor.selectInBlock(0, 0, 5); // Select "Hello" (inside strong)
|
|
366
|
+
|
|
367
|
+
links.insertLink();
|
|
368
|
+
|
|
369
|
+
// The link should wrap the strong element
|
|
370
|
+
expect(editor.getHtml()).toContain("https://example.com");
|
|
371
|
+
expect(onContentChange).toHaveBeenCalled();
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
describe("return type", () => {
|
|
376
|
+
beforeEach(() => {
|
|
377
|
+
onContentChange = vi.fn();
|
|
378
|
+
editor = createTestEditor("<p>test</p>");
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it("returns insertLink and isInLink functions", () => {
|
|
382
|
+
const links = createLinks();
|
|
383
|
+
|
|
384
|
+
expect(typeof links.insertLink).toBe("function");
|
|
385
|
+
expect(typeof links.isInLink).toBe("function");
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
});
|