@aquera/nile-elements 1.5.9 → 1.6.0

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.
Files changed (37) hide show
  1. package/README.md +3 -0
  2. package/dist/index.js +84 -45
  3. package/dist/nile-rich-text-editor/nile-rich-text-editor.cjs.js +1 -1
  4. package/dist/nile-rich-text-editor/nile-rich-text-editor.cjs.js.map +1 -1
  5. package/dist/nile-rich-text-editor/nile-rich-text-editor.css.cjs.js +1 -1
  6. package/dist/nile-rich-text-editor/nile-rich-text-editor.css.cjs.js.map +1 -1
  7. package/dist/nile-rich-text-editor/nile-rich-text-editor.css.esm.js +24 -0
  8. package/dist/nile-rich-text-editor/nile-rich-text-editor.esm.js +1 -1
  9. package/dist/nile-rich-text-editor/nile-rte-link.cjs.js +1 -1
  10. package/dist/nile-rich-text-editor/nile-rte-link.cjs.js.map +1 -1
  11. package/dist/nile-rich-text-editor/nile-rte-link.esm.js +59 -44
  12. package/dist/nile-rich-text-editor/utils/inline-utils.cjs.js +1 -1
  13. package/dist/nile-rich-text-editor/utils/inline-utils.cjs.js.map +1 -1
  14. package/dist/nile-rich-text-editor/utils/inline-utils.esm.js +1 -1
  15. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.css.js +24 -0
  16. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.css.js.map +1 -1
  17. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.d.ts +6 -0
  18. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js +62 -24
  19. package/dist/src/nile-rich-text-editor/nile-rich-text-editor.js.map +1 -1
  20. package/dist/src/nile-rich-text-editor/nile-rte-link.d.ts +12 -0
  21. package/dist/src/nile-rich-text-editor/nile-rte-link.js +239 -130
  22. package/dist/src/nile-rich-text-editor/nile-rte-link.js.map +1 -1
  23. package/dist/src/nile-rich-text-editor/nile-rte-link.test.d.ts +1 -0
  24. package/dist/src/nile-rich-text-editor/nile-rte-link.test.js +469 -0
  25. package/dist/src/nile-rich-text-editor/nile-rte-link.test.js.map +1 -0
  26. package/dist/src/nile-rich-text-editor/utils/inline-utils.js +13 -4
  27. package/dist/src/nile-rich-text-editor/utils/inline-utils.js.map +1 -1
  28. package/dist/src/version.js +1 -1
  29. package/dist/src/version.js.map +1 -1
  30. package/dist/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +1 -1
  32. package/src/nile-rich-text-editor/nile-rich-text-editor.css.ts +24 -0
  33. package/src/nile-rich-text-editor/nile-rich-text-editor.ts +66 -26
  34. package/src/nile-rich-text-editor/nile-rte-link.test.ts +682 -0
  35. package/src/nile-rich-text-editor/nile-rte-link.ts +195 -100
  36. package/src/nile-rich-text-editor/utils/inline-utils.ts +11 -4
  37. package/vscode-html-custom-data.json +16 -2
@@ -0,0 +1,469 @@
1
+ import { expect, fixture, html, elementUpdated } from '@open-wc/testing';
2
+ import './nile-rte-link';
3
+ function createEditor(content = '<p>hello world</p>') {
4
+ const el = document.createElement('article');
5
+ el.setAttribute('contenteditable', 'true');
6
+ el.innerHTML = content;
7
+ document.body.appendChild(el);
8
+ return el;
9
+ }
10
+ function selectText(node, start, end) {
11
+ const sel = document.getSelection();
12
+ sel.removeAllRanges();
13
+ const range = document.createRange();
14
+ const textNode = node.firstChild;
15
+ range.setStart(textNode, start);
16
+ range.setEnd(textNode, end);
17
+ sel.addRange(range);
18
+ return range;
19
+ }
20
+ function collapseAt(node, offset) {
21
+ const sel = document.getSelection();
22
+ sel.removeAllRanges();
23
+ const range = document.createRange();
24
+ const textNode = node.firstChild;
25
+ range.setStart(textNode, offset);
26
+ range.collapse(true);
27
+ sel.addRange(range);
28
+ return range;
29
+ }
30
+ function sleep(ms) {
31
+ return new Promise(r => setTimeout(r, ms));
32
+ }
33
+ describe('NileRteLink', () => {
34
+ let editor;
35
+ afterEach(() => {
36
+ editor?.remove();
37
+ });
38
+ // ── Rendering and Defaults ──
39
+ it('1. renders the component', async () => {
40
+ editor = createEditor();
41
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
42
+ expect(el).to.exist;
43
+ });
44
+ it('2. tag name is nile-rte-link', async () => {
45
+ editor = createEditor();
46
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
47
+ expect(el.tagName.toLowerCase()).to.equal('nile-rte-link');
48
+ });
49
+ it('3. default newTab is false', async () => {
50
+ editor = createEditor();
51
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
52
+ expect(el.newTab).to.be.false;
53
+ });
54
+ it('4. default placeholder is "Type or paste link here"', async () => {
55
+ editor = createEditor();
56
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
57
+ expect(el.placeholder).to.equal('Type or paste link here');
58
+ });
59
+ it('5. default label is "Link"', async () => {
60
+ editor = createEditor();
61
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
62
+ expect(el.label).to.equal('Link');
63
+ });
64
+ it('6. renders a link icon button', async () => {
65
+ editor = createEditor();
66
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
67
+ const btn = el.querySelector('nile-button');
68
+ expect(btn).to.exist;
69
+ const icon = el.querySelector('nile-icon');
70
+ expect(icon).to.exist;
71
+ expect(icon.getAttribute('name')).to.equal('link_2');
72
+ });
73
+ it('7. renders a popover element', async () => {
74
+ editor = createEditor();
75
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
76
+ const pop = el.querySelector('nile-popover');
77
+ expect(pop).to.exist;
78
+ });
79
+ it('8. custom element is defined in registry', async () => {
80
+ expect(customElements.get('nile-rte-link')).to.exist;
81
+ });
82
+ // ── Property Binding ──
83
+ it('9. set newTab to true', async () => {
84
+ editor = createEditor();
85
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor} newTab></nile-rte-link>`);
86
+ expect(el.newTab).to.be.true;
87
+ });
88
+ it('10. set custom placeholder', async () => {
89
+ editor = createEditor();
90
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor} placeholder="Enter URL"></nile-rte-link>`);
91
+ expect(el.placeholder).to.equal('Enter URL');
92
+ });
93
+ it('11. set custom label', async () => {
94
+ editor = createEditor();
95
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor} label="Add Link"></nile-rte-link>`);
96
+ expect(el.label).to.equal('Add Link');
97
+ });
98
+ it('12. set editorEl reference', async () => {
99
+ editor = createEditor();
100
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
101
+ expect(el.editorEl).to.equal(editor);
102
+ });
103
+ // ── URL Normalization ──
104
+ it('13. plain domain gets https:// prepended', async () => {
105
+ editor = createEditor();
106
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
107
+ const normalize = el.normalizeUrl.bind(el);
108
+ expect(normalize('example.com')).to.equal('https://example.com');
109
+ });
110
+ it('14. http:// URL stays unchanged', async () => {
111
+ editor = createEditor();
112
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
113
+ const normalize = el.normalizeUrl.bind(el);
114
+ expect(normalize('http://example.com')).to.equal('http://example.com');
115
+ });
116
+ it('15. https:// URL stays unchanged', async () => {
117
+ editor = createEditor();
118
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
119
+ const normalize = el.normalizeUrl.bind(el);
120
+ expect(normalize('https://example.com')).to.equal('https://example.com');
121
+ });
122
+ it('16. mailto: URL stays unchanged', async () => {
123
+ editor = createEditor();
124
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
125
+ const normalize = el.normalizeUrl.bind(el);
126
+ expect(normalize('mailto:a@b.com')).to.equal('mailto:a@b.com');
127
+ });
128
+ it('17. tel: URL stays unchanged', async () => {
129
+ editor = createEditor();
130
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
131
+ const normalize = el.normalizeUrl.bind(el);
132
+ expect(normalize('tel:+123')).to.equal('tel:+123');
133
+ });
134
+ it('18. displayUrl strips protocol for display', async () => {
135
+ editor = createEditor();
136
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
137
+ const display = el.displayUrl.bind(el);
138
+ expect(display('https://example.com')).to.equal('example.com');
139
+ expect(display('http://example.com/path')).to.equal('example.com/path');
140
+ });
141
+ // ── Link Insertion (collapsed selection) ──
142
+ it('19. inserts a new <a> tag with URL as text when selection is collapsed', async () => {
143
+ editor = createEditor('<p>text</p>');
144
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
145
+ const p = editor.querySelector('p');
146
+ collapseAt(p, 4);
147
+ el.selectionRange = document.getSelection().getRangeAt(0).cloneRange();
148
+ el.linkValue = 'https://example.com';
149
+ el.applyLink();
150
+ const a = editor.querySelector('a');
151
+ expect(a).to.exist;
152
+ expect(a.href).to.include('example.com');
153
+ expect(a.textContent).to.include('example.com');
154
+ });
155
+ it('20. inserts link with target and rel when newTab is true', async () => {
156
+ editor = createEditor('<p>text</p>');
157
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor} newTab></nile-rte-link>`);
158
+ const p = editor.querySelector('p');
159
+ collapseAt(p, 4);
160
+ el.selectionRange = document.getSelection().getRangeAt(0).cloneRange();
161
+ el.linkValue = 'https://example.com';
162
+ el.applyLink();
163
+ const a = editor.querySelector('a');
164
+ expect(a).to.exist;
165
+ expect(a.target).to.equal('_blank');
166
+ expect(a.rel).to.equal('noopener noreferrer');
167
+ });
168
+ it('21. does not set target/rel when newTab is false', async () => {
169
+ editor = createEditor('<p>text</p>');
170
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
171
+ const p = editor.querySelector('p');
172
+ collapseAt(p, 4);
173
+ el.selectionRange = document.getSelection().getRangeAt(0).cloneRange();
174
+ el.linkValue = 'https://example.com';
175
+ el.applyLink();
176
+ const a = editor.querySelector('a');
177
+ expect(a).to.exist;
178
+ expect(a.hasAttribute('target')).to.be.false;
179
+ expect(a.hasAttribute('rel')).to.be.false;
180
+ });
181
+ // ── Link Insertion (text selected) ──
182
+ it('22. wraps selected text in an <a> tag', async () => {
183
+ editor = createEditor('<p>hello world</p>');
184
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
185
+ const p = editor.querySelector('p');
186
+ selectText(p, 0, 5);
187
+ el.selectionRange = document.getSelection().getRangeAt(0).cloneRange();
188
+ el.linkValue = 'https://example.com';
189
+ el.applyLink();
190
+ const a = editor.querySelector('a');
191
+ expect(a).to.exist;
192
+ expect(a.href).to.include('example.com');
193
+ });
194
+ it('23. preserves selected text content after wrapping', async () => {
195
+ editor = createEditor('<p>hello world</p>');
196
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
197
+ const p = editor.querySelector('p');
198
+ selectText(p, 0, 5);
199
+ el.selectionRange = document.getSelection().getRangeAt(0).cloneRange();
200
+ el.linkValue = 'https://example.com';
201
+ el.applyLink();
202
+ const a = editor.querySelector('a');
203
+ expect(a.textContent).to.equal('hello');
204
+ });
205
+ // ── Link Editing ──
206
+ it('24. detects existing anchor and shows edit/unlink buttons', async () => {
207
+ editor = createEditor('<p><a href="https://example.com">link text</a></p>');
208
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
209
+ const a = editor.querySelector('a');
210
+ collapseAt(a, 2);
211
+ el.onOpen();
212
+ await elementUpdated(el);
213
+ expect(el.hasActiveLink).to.be.true;
214
+ expect(el.activeAnchor).to.equal(a);
215
+ });
216
+ it('25. pre-fills input with existing link URL (protocol stripped)', async () => {
217
+ editor = createEditor('<p><a href="https://example.com">link</a></p>');
218
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
219
+ const a = editor.querySelector('a');
220
+ collapseAt(a, 2);
221
+ el.onOpen();
222
+ await elementUpdated(el);
223
+ expect(el.linkValue).to.equal('example.com');
224
+ });
225
+ it('26. updates existing anchor href on apply', async () => {
226
+ editor = createEditor('<p><a href="https://old.com">link</a></p>');
227
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
228
+ const a = editor.querySelector('a');
229
+ collapseAt(a, 2);
230
+ el.onOpen();
231
+ await elementUpdated(el);
232
+ el.linkValue = 'https://new.com';
233
+ el.applyLink();
234
+ expect(a.href).to.include('new.com');
235
+ });
236
+ it('27. toggling newTab on edit updates target/rel attributes', async () => {
237
+ editor = createEditor('<p><a href="https://example.com">link</a></p>');
238
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor} newTab></nile-rte-link>`);
239
+ const a = editor.querySelector('a');
240
+ collapseAt(a, 2);
241
+ el.onOpen();
242
+ await elementUpdated(el);
243
+ el.linkValue = 'https://example.com';
244
+ el.applyLink();
245
+ expect(a.target).to.equal('_blank');
246
+ expect(a.rel).to.equal('noopener noreferrer');
247
+ });
248
+ // ── Link Removal ──
249
+ it('28. unlink removes the <a> tag', async () => {
250
+ editor = createEditor('<p><a href="https://example.com">link text</a></p>');
251
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
252
+ const a = editor.querySelector('a');
253
+ collapseAt(a, 2);
254
+ el.onOpen();
255
+ await elementUpdated(el);
256
+ el.onRemove();
257
+ expect(editor.querySelector('a')).to.be.null;
258
+ });
259
+ it('29. unlink preserves the text content', async () => {
260
+ editor = createEditor('<p><a href="https://example.com">link text</a></p>');
261
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
262
+ const a = editor.querySelector('a');
263
+ collapseAt(a, 2);
264
+ el.onOpen();
265
+ await elementUpdated(el);
266
+ el.onRemove();
267
+ expect(editor.querySelector('p').textContent).to.equal('link text');
268
+ });
269
+ it('30. closes popover after unlink', async () => {
270
+ editor = createEditor('<p><a href="https://example.com">link text</a></p>');
271
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
272
+ const a = editor.querySelector('a');
273
+ collapseAt(a, 2);
274
+ el.onOpen();
275
+ await elementUpdated(el);
276
+ el.onRemove();
277
+ expect(el.selectionRange).to.be.null;
278
+ expect(el.hasActiveLink).to.be.false;
279
+ });
280
+ // ── Popover Behavior ──
281
+ it('31. opens popover on button click', async () => {
282
+ editor = createEditor('<p>text</p>');
283
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
284
+ const p = editor.querySelector('p');
285
+ collapseAt(p, 2);
286
+ el.onOpen();
287
+ await sleep(50);
288
+ const pop = el.querySelector('nile-popover');
289
+ expect(pop?.isShow).to.be.true;
290
+ });
291
+ it('32. closes popover on Escape key', async () => {
292
+ editor = createEditor('<p>text</p>');
293
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
294
+ const p = editor.querySelector('p');
295
+ collapseAt(p, 2);
296
+ el.onOpen();
297
+ await sleep(50);
298
+ const event = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true });
299
+ el.onInputKeydown(event);
300
+ expect(el.selectionRange).to.be.null;
301
+ });
302
+ it('33. applies link on Enter key', async () => {
303
+ editor = createEditor('<p>text</p>');
304
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
305
+ const p = editor.querySelector('p');
306
+ collapseAt(p, 4);
307
+ el.selectionRange = document.getSelection().getRangeAt(0).cloneRange();
308
+ el.linkValue = 'https://example.com';
309
+ const event = new KeyboardEvent('keydown', { key: 'Enter', bubbles: true });
310
+ el.onInputKeydown(event);
311
+ const a = editor.querySelector('a');
312
+ expect(a).to.exist;
313
+ });
314
+ it('34. closes popover on input blur', async () => {
315
+ editor = createEditor('<p>text</p>');
316
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
317
+ const p = editor.querySelector('p');
318
+ collapseAt(p, 2);
319
+ el.onOpen();
320
+ await sleep(50);
321
+ el.ignoreBlur = false;
322
+ el.isApplying = false;
323
+ el.onInputBlur();
324
+ expect(el.selectionRange).to.be.null;
325
+ });
326
+ it('35. does not close on blur when ignoreBlur is set', async () => {
327
+ editor = createEditor('<p>text</p>');
328
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
329
+ const p = editor.querySelector('p');
330
+ collapseAt(p, 2);
331
+ el.onOpen();
332
+ await sleep(50);
333
+ el.ignoreBlur = true;
334
+ el.onInputBlur();
335
+ expect(el.selectionRange).to.not.be.null;
336
+ });
337
+ // ── Event Dispatching ──
338
+ it('36. fires nile-link-changed with href on link apply', async () => {
339
+ editor = createEditor('<p>text</p>');
340
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
341
+ const p = editor.querySelector('p');
342
+ collapseAt(p, 4);
343
+ let detail = null;
344
+ el.addEventListener('nile-link-changed', ((e) => {
345
+ detail = e.detail;
346
+ }));
347
+ el.selectionRange = document.getSelection().getRangeAt(0).cloneRange();
348
+ el.linkValue = 'https://example.com';
349
+ el.applyLink();
350
+ expect(detail).to.exist;
351
+ expect(detail.href).to.include('example.com');
352
+ });
353
+ it('37. fires nile-link-changed with empty href on unlink', async () => {
354
+ editor = createEditor('<p><a href="https://example.com">link</a></p>');
355
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
356
+ const a = editor.querySelector('a');
357
+ collapseAt(a, 2);
358
+ let detail = null;
359
+ el.addEventListener('nile-link-changed', ((e) => {
360
+ detail = e.detail;
361
+ }));
362
+ el.onOpen();
363
+ await elementUpdated(el);
364
+ el.onRemove();
365
+ expect(detail).to.exist;
366
+ expect(detail.href).to.equal('');
367
+ });
368
+ it('38. event bubbles and is composed', async () => {
369
+ editor = createEditor('<p>text</p>');
370
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
371
+ const p = editor.querySelector('p');
372
+ collapseAt(p, 4);
373
+ let bubbles = false;
374
+ let composed = false;
375
+ el.addEventListener('nile-link-changed', ((e) => {
376
+ bubbles = e.bubbles;
377
+ composed = e.composed;
378
+ }));
379
+ el.selectionRange = document.getSelection().getRangeAt(0).cloneRange();
380
+ el.linkValue = 'https://example.com';
381
+ el.applyLink();
382
+ expect(bubbles).to.be.true;
383
+ expect(composed).to.be.true;
384
+ });
385
+ // ── Validation ──
386
+ it('39. does not apply empty URL', async () => {
387
+ editor = createEditor('<p>text</p>');
388
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
389
+ const p = editor.querySelector('p');
390
+ collapseAt(p, 4);
391
+ el.selectionRange = document.getSelection().getRangeAt(0).cloneRange();
392
+ el.linkValue = '';
393
+ el.applyLink();
394
+ expect(editor.querySelector('a')).to.be.null;
395
+ });
396
+ it('40. rejects unsupported protocols', async () => {
397
+ editor = createEditor('<p>text</p>');
398
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
399
+ const p = editor.querySelector('p');
400
+ collapseAt(p, 4);
401
+ el.selectionRange = document.getSelection().getRangeAt(0).cloneRange();
402
+ el.linkValue = 'javascript:alert(1)';
403
+ el.applyLink();
404
+ expect(editor.querySelector('a')).to.be.null;
405
+ });
406
+ // ── Integration with Parent RTE ──
407
+ it('41. parent receives nile-link-changed event and updates content', async () => {
408
+ editor = createEditor('<p>text</p>');
409
+ const container = await fixture(html `
410
+ <div>
411
+ <nile-rte-link .editorEl=${editor}></nile-rte-link>
412
+ </div>
413
+ `);
414
+ const el = container.querySelector('nile-rte-link');
415
+ let received = false;
416
+ container.addEventListener('nile-link-changed', () => {
417
+ received = true;
418
+ });
419
+ const p = editor.querySelector('p');
420
+ collapseAt(p, 4);
421
+ el.selectionRange = document.getSelection().getRangeAt(0).cloneRange();
422
+ el.linkValue = 'https://example.com';
423
+ el.applyLink();
424
+ expect(received).to.be.true;
425
+ });
426
+ it('42. toolbar link button shows active state when cursor is inside a link', async () => {
427
+ editor = createEditor('<p><a href="https://example.com">link text</a></p>');
428
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
429
+ const a = editor.querySelector('a');
430
+ collapseAt(a, 2);
431
+ el.onOpen();
432
+ await elementUpdated(el);
433
+ expect(el.hasActiveLink).to.be.true;
434
+ });
435
+ it('43. toolbar link button loses active state when cursor moves outside a link', async () => {
436
+ editor = createEditor('<p>normal <a href="https://example.com">link</a> text</p>');
437
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
438
+ const p = editor.querySelector('p');
439
+ const textNode = p.firstChild;
440
+ const sel = document.getSelection();
441
+ sel.removeAllRanges();
442
+ const range = document.createRange();
443
+ range.setStart(textNode, 2);
444
+ range.collapse(true);
445
+ sel.addRange(range);
446
+ el.onOpen();
447
+ await elementUpdated(el);
448
+ expect(el.hasActiveLink).to.be.false;
449
+ expect(el.activeAnchor).to.be.null;
450
+ });
451
+ // ── CSS Injection ──
452
+ it('44. injects style tag with data-rte-link-style attribute', async () => {
453
+ editor = createEditor();
454
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
455
+ const styleTag = el.querySelector('style[data-rte-link-style]');
456
+ expect(styleTag).to.exist;
457
+ });
458
+ it('45. does not inject duplicate styles on reconnect', async () => {
459
+ editor = createEditor();
460
+ const el = await fixture(html `<nile-rte-link .editorEl=${editor}></nile-rte-link>`);
461
+ const parent = el.parentElement;
462
+ parent.removeChild(el);
463
+ parent.appendChild(el);
464
+ await elementUpdated(el);
465
+ const styleTags = el.querySelectorAll('style[data-rte-link-style]');
466
+ expect(styleTags.length).to.equal(1);
467
+ });
468
+ });
469
+ //# sourceMappingURL=nile-rte-link.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nile-rte-link.test.js","sourceRoot":"","sources":["../../../src/nile-rich-text-editor/nile-rte-link.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { expect, fixture, html, elementUpdated } from '@open-wc/testing';\nimport './nile-rte-link';\nimport type { NileRteLink } from './nile-rte-link';\n\nfunction createEditor(content = '<p>hello world</p>'): HTMLElement {\n const el = document.createElement('article');\n el.setAttribute('contenteditable', 'true');\n el.innerHTML = content;\n document.body.appendChild(el);\n return el;\n}\n\nfunction selectText(node: Node, start: number, end: number) {\n const sel = document.getSelection()!;\n sel.removeAllRanges();\n const range = document.createRange();\n const textNode = node.firstChild!;\n range.setStart(textNode, start);\n range.setEnd(textNode, end);\n sel.addRange(range);\n return range;\n}\n\nfunction collapseAt(node: Node, offset: number) {\n const sel = document.getSelection()!;\n sel.removeAllRanges();\n const range = document.createRange();\n const textNode = node.firstChild!;\n range.setStart(textNode, offset);\n range.collapse(true);\n sel.addRange(range);\n return range;\n}\n\nfunction sleep(ms: number) {\n return new Promise(r => setTimeout(r, ms));\n}\n\ndescribe('NileRteLink', () => {\n let editor: HTMLElement;\n\n afterEach(() => {\n editor?.remove();\n });\n\n // ── Rendering and Defaults ──\n\n it('1. renders the component', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n expect(el).to.exist;\n });\n\n it('2. tag name is nile-rte-link', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n expect(el.tagName.toLowerCase()).to.equal('nile-rte-link');\n });\n\n it('3. default newTab is false', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n expect(el.newTab).to.be.false;\n });\n\n it('4. default placeholder is \"Type or paste link here\"', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n expect(el.placeholder).to.equal('Type or paste link here');\n });\n\n it('5. default label is \"Link\"', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n expect(el.label).to.equal('Link');\n });\n\n it('6. renders a link icon button', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const btn = el.querySelector('nile-button');\n expect(btn).to.exist;\n const icon = el.querySelector('nile-icon');\n expect(icon).to.exist;\n expect(icon!.getAttribute('name')).to.equal('link_2');\n });\n\n it('7. renders a popover element', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const pop = el.querySelector('nile-popover');\n expect(pop).to.exist;\n });\n\n it('8. custom element is defined in registry', async () => {\n expect(customElements.get('nile-rte-link')).to.exist;\n });\n\n // ── Property Binding ──\n\n it('9. set newTab to true', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor} newTab></nile-rte-link>`\n );\n expect(el.newTab).to.be.true;\n });\n\n it('10. set custom placeholder', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor} placeholder=\"Enter URL\"></nile-rte-link>`\n );\n expect(el.placeholder).to.equal('Enter URL');\n });\n\n it('11. set custom label', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor} label=\"Add Link\"></nile-rte-link>`\n );\n expect(el.label).to.equal('Add Link');\n });\n\n it('12. set editorEl reference', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n expect(el.editorEl).to.equal(editor);\n });\n\n // ── URL Normalization ──\n\n it('13. plain domain gets https:// prepended', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const normalize = (el as any).normalizeUrl.bind(el);\n expect(normalize('example.com')).to.equal('https://example.com');\n });\n\n it('14. http:// URL stays unchanged', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const normalize = (el as any).normalizeUrl.bind(el);\n expect(normalize('http://example.com')).to.equal('http://example.com');\n });\n\n it('15. https:// URL stays unchanged', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const normalize = (el as any).normalizeUrl.bind(el);\n expect(normalize('https://example.com')).to.equal('https://example.com');\n });\n\n it('16. mailto: URL stays unchanged', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const normalize = (el as any).normalizeUrl.bind(el);\n expect(normalize('mailto:a@b.com')).to.equal('mailto:a@b.com');\n });\n\n it('17. tel: URL stays unchanged', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const normalize = (el as any).normalizeUrl.bind(el);\n expect(normalize('tel:+123')).to.equal('tel:+123');\n });\n\n it('18. displayUrl strips protocol for display', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const display = (el as any).displayUrl.bind(el);\n expect(display('https://example.com')).to.equal('example.com');\n expect(display('http://example.com/path')).to.equal('example.com/path');\n });\n\n // ── Link Insertion (collapsed selection) ──\n\n it('19. inserts a new <a> tag with URL as text when selection is collapsed', async () => {\n editor = createEditor('<p>text</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n collapseAt(p, 4);\n\n (el as any).selectionRange = document.getSelection()!.getRangeAt(0).cloneRange();\n (el as any).linkValue = 'https://example.com';\n (el as any).applyLink();\n\n const a = editor.querySelector('a');\n expect(a).to.exist;\n expect(a!.href).to.include('example.com');\n expect(a!.textContent).to.include('example.com');\n });\n\n it('20. inserts link with target and rel when newTab is true', async () => {\n editor = createEditor('<p>text</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor} newTab></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n collapseAt(p, 4);\n\n (el as any).selectionRange = document.getSelection()!.getRangeAt(0).cloneRange();\n (el as any).linkValue = 'https://example.com';\n (el as any).applyLink();\n\n const a = editor.querySelector('a');\n expect(a).to.exist;\n expect(a!.target).to.equal('_blank');\n expect(a!.rel).to.equal('noopener noreferrer');\n });\n\n it('21. does not set target/rel when newTab is false', async () => {\n editor = createEditor('<p>text</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n collapseAt(p, 4);\n\n (el as any).selectionRange = document.getSelection()!.getRangeAt(0).cloneRange();\n (el as any).linkValue = 'https://example.com';\n (el as any).applyLink();\n\n const a = editor.querySelector('a');\n expect(a).to.exist;\n expect(a!.hasAttribute('target')).to.be.false;\n expect(a!.hasAttribute('rel')).to.be.false;\n });\n\n // ── Link Insertion (text selected) ──\n\n it('22. wraps selected text in an <a> tag', async () => {\n editor = createEditor('<p>hello world</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n selectText(p, 0, 5);\n\n (el as any).selectionRange = document.getSelection()!.getRangeAt(0).cloneRange();\n (el as any).linkValue = 'https://example.com';\n (el as any).applyLink();\n\n const a = editor.querySelector('a');\n expect(a).to.exist;\n expect(a!.href).to.include('example.com');\n });\n\n it('23. preserves selected text content after wrapping', async () => {\n editor = createEditor('<p>hello world</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n selectText(p, 0, 5);\n\n (el as any).selectionRange = document.getSelection()!.getRangeAt(0).cloneRange();\n (el as any).linkValue = 'https://example.com';\n (el as any).applyLink();\n\n const a = editor.querySelector('a');\n expect(a!.textContent).to.equal('hello');\n });\n\n // ── Link Editing ──\n\n it('24. detects existing anchor and shows edit/unlink buttons', async () => {\n editor = createEditor('<p><a href=\"https://example.com\">link text</a></p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const a = editor.querySelector('a')!;\n collapseAt(a, 2);\n\n (el as any).onOpen();\n await elementUpdated(el);\n\n expect((el as any).hasActiveLink).to.be.true;\n expect((el as any).activeAnchor).to.equal(a);\n });\n\n it('25. pre-fills input with existing link URL (protocol stripped)', async () => {\n editor = createEditor('<p><a href=\"https://example.com\">link</a></p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const a = editor.querySelector('a')!;\n collapseAt(a, 2);\n\n (el as any).onOpen();\n await elementUpdated(el);\n\n expect((el as any).linkValue).to.equal('example.com');\n });\n\n it('26. updates existing anchor href on apply', async () => {\n editor = createEditor('<p><a href=\"https://old.com\">link</a></p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const a = editor.querySelector('a')!;\n collapseAt(a, 2);\n\n (el as any).onOpen();\n await elementUpdated(el);\n\n (el as any).linkValue = 'https://new.com';\n (el as any).applyLink();\n\n expect(a.href).to.include('new.com');\n });\n\n it('27. toggling newTab on edit updates target/rel attributes', async () => {\n editor = createEditor('<p><a href=\"https://example.com\">link</a></p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor} newTab></nile-rte-link>`\n );\n const a = editor.querySelector('a')!;\n collapseAt(a, 2);\n\n (el as any).onOpen();\n await elementUpdated(el);\n\n (el as any).linkValue = 'https://example.com';\n (el as any).applyLink();\n\n expect(a.target).to.equal('_blank');\n expect(a.rel).to.equal('noopener noreferrer');\n });\n\n // ── Link Removal ──\n\n it('28. unlink removes the <a> tag', async () => {\n editor = createEditor('<p><a href=\"https://example.com\">link text</a></p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const a = editor.querySelector('a')!;\n collapseAt(a, 2);\n\n (el as any).onOpen();\n await elementUpdated(el);\n (el as any).onRemove();\n\n expect(editor.querySelector('a')).to.be.null;\n });\n\n it('29. unlink preserves the text content', async () => {\n editor = createEditor('<p><a href=\"https://example.com\">link text</a></p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const a = editor.querySelector('a')!;\n collapseAt(a, 2);\n\n (el as any).onOpen();\n await elementUpdated(el);\n (el as any).onRemove();\n\n expect(editor.querySelector('p')!.textContent).to.equal('link text');\n });\n\n it('30. closes popover after unlink', async () => {\n editor = createEditor('<p><a href=\"https://example.com\">link text</a></p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const a = editor.querySelector('a')!;\n collapseAt(a, 2);\n\n (el as any).onOpen();\n await elementUpdated(el);\n (el as any).onRemove();\n\n expect((el as any).selectionRange).to.be.null;\n expect((el as any).hasActiveLink).to.be.false;\n });\n\n // ── Popover Behavior ──\n\n it('31. opens popover on button click', async () => {\n editor = createEditor('<p>text</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n collapseAt(p, 2);\n\n (el as any).onOpen();\n await sleep(50);\n\n const pop = el.querySelector('nile-popover') as any;\n expect(pop?.isShow).to.be.true;\n });\n\n it('32. closes popover on Escape key', async () => {\n editor = createEditor('<p>text</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n collapseAt(p, 2);\n\n (el as any).onOpen();\n await sleep(50);\n\n const event = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true });\n (el as any).onInputKeydown(event);\n\n expect((el as any).selectionRange).to.be.null;\n });\n\n it('33. applies link on Enter key', async () => {\n editor = createEditor('<p>text</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n collapseAt(p, 4);\n\n (el as any).selectionRange = document.getSelection()!.getRangeAt(0).cloneRange();\n (el as any).linkValue = 'https://example.com';\n\n const event = new KeyboardEvent('keydown', { key: 'Enter', bubbles: true });\n (el as any).onInputKeydown(event);\n\n const a = editor.querySelector('a');\n expect(a).to.exist;\n });\n\n it('34. closes popover on input blur', async () => {\n editor = createEditor('<p>text</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n collapseAt(p, 2);\n\n (el as any).onOpen();\n await sleep(50);\n\n (el as any).ignoreBlur = false;\n (el as any).isApplying = false;\n (el as any).onInputBlur();\n\n expect((el as any).selectionRange).to.be.null;\n });\n\n it('35. does not close on blur when ignoreBlur is set', async () => {\n editor = createEditor('<p>text</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n collapseAt(p, 2);\n\n (el as any).onOpen();\n await sleep(50);\n\n (el as any).ignoreBlur = true;\n (el as any).onInputBlur();\n\n expect((el as any).selectionRange).to.not.be.null;\n });\n\n // ── Event Dispatching ──\n\n it('36. fires nile-link-changed with href on link apply', async () => {\n editor = createEditor('<p>text</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n collapseAt(p, 4);\n\n let detail: any = null;\n el.addEventListener('nile-link-changed', ((e: CustomEvent) => {\n detail = e.detail;\n }) as EventListener);\n\n (el as any).selectionRange = document.getSelection()!.getRangeAt(0).cloneRange();\n (el as any).linkValue = 'https://example.com';\n (el as any).applyLink();\n\n expect(detail).to.exist;\n expect(detail.href).to.include('example.com');\n });\n\n it('37. fires nile-link-changed with empty href on unlink', async () => {\n editor = createEditor('<p><a href=\"https://example.com\">link</a></p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const a = editor.querySelector('a')!;\n collapseAt(a, 2);\n\n let detail: any = null;\n el.addEventListener('nile-link-changed', ((e: CustomEvent) => {\n detail = e.detail;\n }) as EventListener);\n\n (el as any).onOpen();\n await elementUpdated(el);\n (el as any).onRemove();\n\n expect(detail).to.exist;\n expect(detail.href).to.equal('');\n });\n\n it('38. event bubbles and is composed', async () => {\n editor = createEditor('<p>text</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n collapseAt(p, 4);\n\n let bubbles = false;\n let composed = false;\n el.addEventListener('nile-link-changed', ((e: CustomEvent) => {\n bubbles = e.bubbles;\n composed = e.composed;\n }) as EventListener);\n\n (el as any).selectionRange = document.getSelection()!.getRangeAt(0).cloneRange();\n (el as any).linkValue = 'https://example.com';\n (el as any).applyLink();\n\n expect(bubbles).to.be.true;\n expect(composed).to.be.true;\n });\n\n // ── Validation ──\n\n it('39. does not apply empty URL', async () => {\n editor = createEditor('<p>text</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n collapseAt(p, 4);\n\n (el as any).selectionRange = document.getSelection()!.getRangeAt(0).cloneRange();\n (el as any).linkValue = '';\n (el as any).applyLink();\n\n expect(editor.querySelector('a')).to.be.null;\n });\n\n it('40. rejects unsupported protocols', async () => {\n editor = createEditor('<p>text</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const p = editor.querySelector('p')!;\n collapseAt(p, 4);\n\n (el as any).selectionRange = document.getSelection()!.getRangeAt(0).cloneRange();\n (el as any).linkValue = 'javascript:alert(1)';\n (el as any).applyLink();\n\n expect(editor.querySelector('a')).to.be.null;\n });\n\n // ── Integration with Parent RTE ──\n\n it('41. parent receives nile-link-changed event and updates content', async () => {\n editor = createEditor('<p>text</p>');\n const container = await fixture(html`\n <div>\n <nile-rte-link .editorEl=${editor}></nile-rte-link>\n </div>\n `);\n const el = container.querySelector('nile-rte-link')! as NileRteLink;\n\n let received = false;\n container.addEventListener('nile-link-changed', () => {\n received = true;\n });\n\n const p = editor.querySelector('p')!;\n collapseAt(p, 4);\n\n (el as any).selectionRange = document.getSelection()!.getRangeAt(0).cloneRange();\n (el as any).linkValue = 'https://example.com';\n (el as any).applyLink();\n\n expect(received).to.be.true;\n });\n\n it('42. toolbar link button shows active state when cursor is inside a link', async () => {\n editor = createEditor('<p><a href=\"https://example.com\">link text</a></p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const a = editor.querySelector('a')!;\n collapseAt(a, 2);\n\n (el as any).onOpen();\n await elementUpdated(el);\n\n expect((el as any).hasActiveLink).to.be.true;\n });\n\n it('43. toolbar link button loses active state when cursor moves outside a link', async () => {\n editor = createEditor('<p>normal <a href=\"https://example.com\">link</a> text</p>');\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n\n const p = editor.querySelector('p')!;\n const textNode = p.firstChild!;\n const sel = document.getSelection()!;\n sel.removeAllRanges();\n const range = document.createRange();\n range.setStart(textNode, 2);\n range.collapse(true);\n sel.addRange(range);\n\n (el as any).onOpen();\n await elementUpdated(el);\n\n expect((el as any).hasActiveLink).to.be.false;\n expect((el as any).activeAnchor).to.be.null;\n });\n\n // ── CSS Injection ──\n\n it('44. injects style tag with data-rte-link-style attribute', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n const styleTag = el.querySelector('style[data-rte-link-style]');\n expect(styleTag).to.exist;\n });\n\n it('45. does not inject duplicate styles on reconnect', async () => {\n editor = createEditor();\n const el = await fixture<NileRteLink>(\n html`<nile-rte-link .editorEl=${editor}></nile-rte-link>`\n );\n\n const parent = el.parentElement!;\n parent.removeChild(el);\n parent.appendChild(el);\n await elementUpdated(el);\n\n const styleTags = el.querySelectorAll('style[data-rte-link-style]');\n expect(styleTags.length).to.equal(1);\n });\n});\n"]}
@@ -156,14 +156,23 @@ export function toggleInlineTag(root, tag, attrs) {
156
156
  }
157
157
  else {
158
158
  textNodes.forEach(txt => {
159
- const skip = txt.parentElement?.closest('a, .mention, nile-mention');
160
- if (skip)
159
+ const inProtected = txt.parentElement?.closest('.mention, nile-mention');
160
+ if (inProtected)
161
+ return;
162
+ const inAnchor = txt.parentElement?.closest('a');
163
+ if (inAnchor && tag === 'span')
161
164
  return;
162
165
  const wrapper = document.createElement(tag);
163
166
  if (attrs)
164
167
  Object.entries(attrs).forEach(([k, v]) => wrapper.setAttribute(k, v));
165
- txt.parentNode?.insertBefore(wrapper, txt);
166
- wrapper.appendChild(txt);
168
+ if (inAnchor) {
169
+ inAnchor.insertBefore(wrapper, txt);
170
+ wrapper.appendChild(txt);
171
+ }
172
+ else {
173
+ txt.parentNode?.insertBefore(wrapper, txt);
174
+ wrapper.appendChild(txt);
175
+ }
167
176
  });
168
177
  }
169
178
  removeEmptyAndNested(root);