@kubex/zinc 1.1.163 → 1.1.165

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.
@@ -1,6 +1,6 @@
1
1
  import '../../../dist/zn.min.js';
2
2
  import {aTimeout, expect, fixture, html, waitUntil} from '@open-wc/testing';
3
- import {EDITOR_ACTIONS} from './actions';
3
+ import {EDITOR_ACTIONS, isApple} from './actions';
4
4
  import {FEATURE_KEYS} from './feature-keys';
5
5
  import {parse as remarkdParse} from 'remarkd-js';
6
6
  import type ZnButton from '../button';
@@ -352,8 +352,10 @@ Second"></zn-remarkd-editor>`);
352
352
  .filter(group => getComputedStyle(group).display === 'none');
353
353
  expect(collapsed.length, 'nothing collapsed at 240px').to.be.greaterThan(0);
354
354
 
355
- // No action is lost: the collapsed groups' actions are all in the menu.
356
- const menuItems = el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar-more zn-menu-item');
355
+ // One top-level entry per collapsed group, each holding that group's actions.
356
+ const groupItems = el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar-more > zn-menu > zn-menu-item');
357
+ expect(groupItems.length).to.equal(collapsed.length);
358
+ const menuItems = el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar-more zn-menu[slot="submenu"] zn-menu-item');
357
359
  expect(menuItems.length).to.be.greaterThan(0);
358
360
 
359
361
  // The trigger itself must stay on-screen — a group that doesn't fit must not force its
@@ -372,7 +374,7 @@ Second"></zn-remarkd-editor>`);
372
374
  .to.equal(expectedCount);
373
375
  });
374
376
 
375
- it('should scroll the overflow menu instead of running off the viewport', async () => {
377
+ it('should cap each overflow submenu so it scrolls instead of running off the viewport', async () => {
376
378
  const el = await fixture<ZnRemarkdEditor>(html`
377
379
  <zn-remarkd-editor style="width: 240px"></zn-remarkd-editor>`);
378
380
  await waitUntil(() => el.shadowRoot!.querySelector('.remarkd-editor__toolbar-more'),
@@ -384,14 +386,14 @@ Second"></zn-remarkd-editor>`);
384
386
  // wait used for the same reason in expanding-action's drop-panel overflow tests.
385
387
  await new Promise(resolve => requestAnimationFrame(resolve));
386
388
 
387
- // At 240px, every action lands in the menu — enough zn-menu-items to overflow the
388
- // max-height. The scroll container is zn-menu's own inner `.menu` div (set via the
389
- // --zn-menu-max-height custom property), not the zn-menu host itself: a constraint on
390
- // the host leaves the host with room to spare, so a wheel over an item finds `.menu`
391
- // has nothing to scroll and never chains out to the host.
392
- const menu = dropdown.querySelector('zn-menu')!;
389
+ // Inline is the longest group, so its submenu is the one that can outgrow the viewport.
390
+ // The cap lands on zn-menu's inner `.menu` div (via --zn-menu-max-height), not the host:
391
+ // a constraint on the host leaves `.menu` with nothing to scroll.
392
+ const inlineItem = [...dropdown.querySelectorAll(':scope > zn-menu > zn-menu-item')]
393
+ .find(item => item.textContent?.includes('Strong'))!;
394
+ const menu = inlineItem.querySelector('zn-menu[slot="submenu"]')!;
393
395
  const inner = menu.shadowRoot!.querySelector<HTMLElement>('.menu')!;
394
- expect(inner.scrollHeight, 'the menu needs room to scroll').to.be.greaterThan(inner.clientHeight);
396
+ expect(getComputedStyle(inner).maxHeight).to.not.equal('none');
395
397
  expect(getComputedStyle(inner).overflow).to.equal('auto');
396
398
  });
397
399
 
@@ -416,7 +418,7 @@ Second"></zn-remarkd-editor>`);
416
418
  // trigger wouldn't actually move focus in a test DOM, so the blur is reproduced directly.
417
419
  input.dispatchEvent(new Event('blur'));
418
420
 
419
- const strong = [...dropdown.querySelectorAll('zn-menu-item')]
421
+ const strong = [...dropdown.querySelectorAll('zn-menu[slot="submenu"] zn-menu-item')]
420
422
  .find(item => item.textContent?.includes('Strong'));
421
423
  expect(strong, 'Strong should be reachable through the overflow menu').to.exist;
422
424
  strong!.dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
@@ -482,7 +484,7 @@ Second"></zn-remarkd-editor>`);
482
484
  const input = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!;
483
485
  input.setSelectionRange(0, 5);
484
486
  const strong = [...el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button')]
485
- .find(b => b.getAttribute('tooltip') === 'Strong')!;
487
+ .find(b => b.getAttribute('label') === 'Strong')!;
486
488
  strong.dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
487
489
  await el.updateComplete;
488
490
 
@@ -492,6 +494,163 @@ Second"></zn-remarkd-editor>`);
492
494
  expect([result.selectionStart, result.selectionEnd]).to.eql([2, 7]);
493
495
  });
494
496
 
497
+ describe('keyboard shortcuts', () => {
498
+ const apple = isApple();
499
+ const press = (input: HTMLTextAreaElement, key: string, init: KeyboardEventInit = {}) => {
500
+ const event = new KeyboardEvent('keydown', {key, bubbles: true, composed: true, cancelable: true, ...init});
501
+ input.dispatchEvent(event);
502
+ return event;
503
+ };
504
+ const mod = (init: KeyboardEventInit = {}): KeyboardEventInit =>
505
+ apple ? {metaKey: true, ...init} : {ctrlKey: true, ...init};
506
+
507
+ async function editing(value: string, start: number, end: number) {
508
+ const el = await fixture<ZnRemarkdEditor>(html`
509
+ <zn-remarkd-editor value=${value}></zn-remarkd-editor>`);
510
+ el.shadowRoot!.querySelector<HTMLElement>('.remarkd-editor__rendered')!.click();
511
+ await el.updateComplete;
512
+ const input = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!;
513
+ input.setSelectionRange(start, end);
514
+ return {el, input};
515
+ }
516
+
517
+ it('should bold the selection with the platform modifier + B', async () => {
518
+ const {el, input} = await editing('hello world', 0, 5);
519
+ const event = press(input, 'b', mod());
520
+ await el.updateComplete;
521
+ expect(event.defaultPrevented).to.be.true;
522
+ expect(input.value).to.equal('**hello** world');
523
+ });
524
+
525
+ it('should toggle the mark back off on a second press', async () => {
526
+ const {el, input} = await editing('hello world', 0, 5);
527
+ press(input, 'b', mod());
528
+ await el.updateComplete;
529
+ press(input, 'b', mod());
530
+ await el.updateComplete;
531
+ expect(input.value).to.equal('hello world');
532
+ });
533
+
534
+ it('should map shift shortcuts, e.g. strikethrough', async () => {
535
+ const {el, input} = await editing('hello world', 0, 5);
536
+ press(input, 'X', mod({shiftKey: true}));
537
+ await el.updateComplete;
538
+ expect(input.value).to.equal('~~hello~~ world');
539
+ });
540
+
541
+ it('should ignore the other platform\'s modifier', async () => {
542
+ const {el, input} = await editing('hello world', 0, 5);
543
+ const event = press(input, 'b', apple ? {ctrlKey: true} : {metaKey: true});
544
+ await el.updateComplete;
545
+ expect(event.defaultPrevented).to.be.false;
546
+ expect(input.value).to.equal('hello world');
547
+ });
548
+
549
+ it('should ignore AltGr (Ctrl+Alt) combinations', async () => {
550
+ const {el, input} = await editing('hello world', 0, 5);
551
+ press(input, 'b', mod({altKey: true}));
552
+ await el.updateComplete;
553
+ expect(input.value).to.equal('hello world');
554
+ });
555
+
556
+ it('should show the shortcut in the toolbar tooltip', async () => {
557
+ const el = await fixture<ZnRemarkdEditor>(html`<zn-remarkd-editor></zn-remarkd-editor>`);
558
+ const strong = [...el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button')]
559
+ .find(b => b.getAttribute('label') === 'Strong')!;
560
+ expect(strong.getAttribute('tooltip')).to.equal(apple ? 'Strong (⌘B)' : 'Strong (Ctrl+B)');
561
+ });
562
+ });
563
+
564
+ describe('list continuation', () => {
565
+ const enter = (input: HTMLTextAreaElement) => {
566
+ const event = new KeyboardEvent('keydown', {key: 'Enter', bubbles: true, composed: true, cancelable: true});
567
+ input.dispatchEvent(event);
568
+ return event;
569
+ };
570
+
571
+ it('should continue a numbered list on Enter in the open block', async () => {
572
+ const el = await fixture<ZnRemarkdEditor>(html`
573
+ <zn-remarkd-editor value="1. hello"></zn-remarkd-editor>`);
574
+ el.shadowRoot!.querySelector<HTMLElement>('.remarkd-editor__rendered')!.click();
575
+ await el.updateComplete;
576
+ const input = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!;
577
+ input.setSelectionRange(8, 8);
578
+ let inputs = 0;
579
+ el.addEventListener('zn-input', () => inputs++);
580
+
581
+ expect(enter(input).defaultPrevented).to.be.true;
582
+ await el.updateComplete;
583
+ expect(input.value).to.equal('1. hello\n2. ');
584
+ expect(input.selectionStart).to.equal(12);
585
+ expect(inputs).to.equal(1);
586
+ });
587
+
588
+ it('should leave Enter as a plain newline outside a list', async () => {
589
+ const el = await fixture<ZnRemarkdEditor>(html`
590
+ <zn-remarkd-editor value="hello"></zn-remarkd-editor>`);
591
+ el.shadowRoot!.querySelector<HTMLElement>('.remarkd-editor__rendered')!.click();
592
+ await el.updateComplete;
593
+ const input = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!;
594
+ input.setSelectionRange(5, 5);
595
+ expect(enter(input).defaultPrevented).to.be.false;
596
+ });
597
+
598
+ /** Opens the first block and replaces its draft with `draft`, caret at the end. */
599
+ async function editDraft(draft: string) {
600
+ const el = await fixture<ZnRemarkdEditor>(html`
601
+ <zn-remarkd-editor value="placeholder"></zn-remarkd-editor>`);
602
+ el.shadowRoot!.querySelector<HTMLElement>('.remarkd-editor__rendered')!.click();
603
+ await el.updateComplete;
604
+ return {el, input: typeInBlock(el, draft)};
605
+ }
606
+
607
+ it('should finish the block on Enter at an empty trailing list item', async () => {
608
+ const {el, input} = await editDraft('- a\n- ');
609
+ expect(enter(input).defaultPrevented).to.be.true;
610
+ await el.updateComplete;
611
+
612
+ expect(el.value).to.equal('- a');
613
+ const draft = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!;
614
+ expect(draft.value, 'a fresh block is opened below').to.equal('');
615
+ });
616
+
617
+ it('should finish the block on a double Enter after a paragraph', async () => {
618
+ const {el, input} = await editDraft('hello\n');
619
+ expect(enter(input).defaultPrevented).to.be.true;
620
+ await el.updateComplete;
621
+
622
+ expect(el.value).to.equal('hello');
623
+ expect(el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!.value).to.equal('');
624
+ });
625
+
626
+ it('should keep a blank line inside an open container', async () => {
627
+ const {input} = await editDraft('```\ncode\n');
628
+ expect(enter(input).defaultPrevented).to.be.false;
629
+ });
630
+
631
+ it('should clear a mid-list empty item without finishing the block', async () => {
632
+ const {el, input} = await editDraft('- a\n- \n- b');
633
+ input.setSelectionRange(6, 6);
634
+ expect(enter(input).defaultPrevented).to.be.true;
635
+ await el.updateComplete;
636
+ expect(input.value).to.equal('- a\n\n- b');
637
+ });
638
+
639
+ it('should continue a list on Enter in the raw source view', async () => {
640
+ const el = await fixture<ZnRemarkdEditor>(html`
641
+ <zn-remarkd-editor allow-raw value="- [ ] first"></zn-remarkd-editor>`);
642
+ el.shadowRoot!.querySelector('.remarkd-editor__raw-toggle')!
643
+ .dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
644
+ await el.updateComplete;
645
+ const raw = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__raw')!;
646
+ raw.setSelectionRange(11, 11);
647
+
648
+ expect(enter(raw).defaultPrevented).to.be.true;
649
+ await el.updateComplete;
650
+ expect(el.value).to.equal('- [ ] first\n- [ ] ');
651
+ });
652
+ });
653
+
495
654
  it('should insert a placeholder when nothing is selected', async () => {
496
655
  const el = await fixture<ZnRemarkdEditor>(html`
497
656
  <zn-remarkd-editor value="hello"></zn-remarkd-editor>`);
@@ -501,7 +660,7 @@ Second"></zn-remarkd-editor>`);
501
660
  input.setSelectionRange(5, 5);
502
661
 
503
662
  [...el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button')]
504
- .find(b => b.getAttribute('tooltip') === 'Strong')!
663
+ .find(b => b.getAttribute('label') === 'Strong')!
505
664
  .dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
506
665
  await el.updateComplete;
507
666
 
@@ -519,7 +678,7 @@ Second"></zn-remarkd-editor>`);
519
678
  const input = el.shadowRoot!.querySelector<HTMLTextAreaElement>('.remarkd-editor__input')!;
520
679
  input.setSelectionRange(2, 7); // hello, inside the marks
521
680
  [...el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button')]
522
- .find(b => b.getAttribute('tooltip') === 'Strong')!
681
+ .find(b => b.getAttribute('label') === 'Strong')!
523
682
  .dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
524
683
  await el.updateComplete;
525
684
 
@@ -536,7 +695,7 @@ Second"></zn-remarkd-editor>`);
536
695
  input.setSelectionRange(2, 7); // hello, inside the strike marks
537
696
 
538
697
  [...el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button')]
539
- .find(b => b.getAttribute('tooltip') === 'Subscript')!
698
+ .find(b => b.getAttribute('label') === 'Subscript')!
540
699
  .dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
541
700
  await el.updateComplete;
542
701
 
@@ -549,11 +708,11 @@ Second"></zn-remarkd-editor>`);
549
708
  const el = await fixture<ZnRemarkdEditor>(html`
550
709
  <zn-remarkd-editor value="hello" link-url="/links"></zn-remarkd-editor>`);
551
710
  const strong = [...el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button')]
552
- .find(b => b.getAttribute('tooltip') === 'Strong')!;
711
+ .find(b => b.getAttribute('label') === 'Strong')!;
553
712
  expect(strong.hasAttribute('disabled')).to.be.true;
554
713
 
555
714
  const link = [...el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button')]
556
- .find(b => b.getAttribute('tooltip') === 'Link to article')!;
715
+ .find(b => b.getAttribute('label') === 'Link to article')!;
557
716
  expect(link.hasAttribute('disabled')).to.be.true;
558
717
  });
559
718
 
@@ -566,7 +725,7 @@ Second"></zn-remarkd-editor>`);
566
725
  input.setSelectionRange(2, 7); // Label, inside the link markup
567
726
 
568
727
  [...el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button')]
569
- .find(b => b.getAttribute('tooltip') === 'Link')!
728
+ .find(b => b.getAttribute('label') === 'Link')!
570
729
  .dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
571
730
  await el.updateComplete;
572
731
 
@@ -587,7 +746,7 @@ Second"></zn-remarkd-editor>`);
587
746
  input.setSelectionRange(1, 6); // Label, inside the real link markup
588
747
 
589
748
  [...el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button')]
590
- .find(b => b.getAttribute('tooltip') === 'Link')!
749
+ .find(b => b.getAttribute('label') === 'Link')!
591
750
  .dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
592
751
  await el.updateComplete;
593
752
 
@@ -601,7 +760,7 @@ Second"></zn-remarkd-editor>`);
601
760
  // Two actions share the "Code" tooltip (inline-code in the inline group, code-fence in
602
761
  // the blocks group) — disambiguate by the group the button actually lives in.
603
762
  const codeButton = [...el.shadowRoot!.querySelectorAll<HTMLElement>('.remarkd-editor__toolbar zn-button')]
604
- .find(b => b.getAttribute('tooltip') === 'Code'
763
+ .find(b => b.getAttribute('label') === 'Code'
605
764
  && b.closest('.toolbar__group')?.getAttribute('data-group') === 'blocks')!;
606
765
  codeButton.dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
607
766
  await el.updateComplete;
@@ -619,7 +778,7 @@ Second"></zn-remarkd-editor>`);
619
778
  await el.updateComplete;
620
779
 
621
780
  const strong = [...el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button')]
622
- .find(b => b.getAttribute('tooltip') === 'Strong')!;
781
+ .find(b => b.getAttribute('label') === 'Strong')!;
623
782
  const mousedown = new MouseEvent('mousedown', {bubbles: true, composed: true, cancelable: true});
624
783
  strong.dispatchEvent(mousedown);
625
784
 
@@ -900,7 +1059,7 @@ Second"></zn-remarkd-editor>`);
900
1059
  it('should show an inline zn-file picker from the toolbar image button', async () => {
901
1060
  const el = await fixture<ZnRemarkdEditor>(html`
902
1061
  <zn-remarkd-editor attachment-url="/upload"></zn-remarkd-editor>`);
903
- const imageButton = el.shadowRoot!.querySelector('.remarkd-editor__toolbar zn-button[tooltip="Image"]')!;
1062
+ const imageButton = el.shadowRoot!.querySelector('.remarkd-editor__toolbar zn-button[label="Image"]')!;
904
1063
  imageButton.dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
905
1064
  await el.updateComplete;
906
1065
 
@@ -1139,7 +1298,7 @@ include::inc-1[Payment Terms]"></zn-remarkd-editor>`);
1139
1298
  <zn-remarkd-editor include-url="/options" value="# Title"></zn-remarkd-editor>`);
1140
1299
 
1141
1300
  const includeButton = el.shadowRoot!
1142
- .querySelector<HTMLElement>('.remarkd-editor__toolbar zn-button[tooltip="Include"]')!;
1301
+ .querySelector<HTMLElement>('.remarkd-editor__toolbar zn-button[label="Include"]')!;
1143
1302
  includeButton.dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
1144
1303
 
1145
1304
  await waitUntil(() => el.shadowRoot!.querySelector('.remarkd-editor__include-option'),
@@ -1176,7 +1335,7 @@ include::inc-1[Payment Terms]"></zn-remarkd-editor>`);
1176
1335
  const el = await fixture<ZnRemarkdEditor>(html`
1177
1336
  <zn-remarkd-editor include-url="/options" value="# Title"></zn-remarkd-editor>`);
1178
1337
  const includeButton = el.shadowRoot!
1179
- .querySelector<HTMLElement>('.remarkd-editor__toolbar zn-button[tooltip="Include"]')!;
1338
+ .querySelector<HTMLElement>('.remarkd-editor__toolbar zn-button[label="Include"]')!;
1180
1339
  includeButton.dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
1181
1340
  await waitUntil(() => el.shadowRoot!.querySelectorAll('.remarkd-editor__include-option').length === 2,
1182
1341
  'the include picker never listed both');
@@ -1195,7 +1354,7 @@ include::inc-1[Payment Terms]"></zn-remarkd-editor>`);
1195
1354
  const el = await fixture<ZnRemarkdEditor>(html`
1196
1355
  <zn-remarkd-editor value="# Title"></zn-remarkd-editor>`);
1197
1356
  const tooltips = Array.from(el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button'))
1198
- .map(button => button.getAttribute('tooltip'));
1357
+ .map(button => button.getAttribute('label'));
1199
1358
  expect(tooltips).to.not.contain('Image');
1200
1359
  });
1201
1360
 
@@ -1203,7 +1362,7 @@ include::inc-1[Payment Terms]"></zn-remarkd-editor>`);
1203
1362
  const el = await fixture<ZnRemarkdEditor>(html`
1204
1363
  <zn-remarkd-editor value="# Title" attachment-url="/attachments"></zn-remarkd-editor>`);
1205
1364
  const tooltips = Array.from(el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button'))
1206
- .map(button => button.getAttribute('tooltip'));
1365
+ .map(button => button.getAttribute('label'));
1207
1366
  expect(tooltips).to.contain('Image');
1208
1367
  });
1209
1368
 
@@ -1211,7 +1370,7 @@ include::inc-1[Payment Terms]"></zn-remarkd-editor>`);
1211
1370
  const el = await fixture<ZnRemarkdEditor>(html`
1212
1371
  <zn-remarkd-editor value="# Title"></zn-remarkd-editor>`);
1213
1372
  const tooltips = Array.from(el.shadowRoot!.querySelectorAll('.remarkd-editor__toolbar zn-button'))
1214
- .map(button => button.getAttribute('tooltip'));
1373
+ .map(button => button.getAttribute('label'));
1215
1374
  expect(tooltips).to.not.contain('Include');
1216
1375
  });
1217
1376
  // A list that never arrived is not evidence an embed is broken: flagging every
@@ -1242,7 +1401,7 @@ include::inc-1[Payment Terms]"></zn-remarkd-editor>`);
1242
1401
  const el = await fixture<ZnRemarkdEditor>(html`
1243
1402
  <zn-remarkd-editor include-url="/options" value="# Title"></zn-remarkd-editor>`);
1244
1403
  const includeButton = el.shadowRoot!
1245
- .querySelector<HTMLElement>('.remarkd-editor__toolbar zn-button[tooltip="Include"]')!;
1404
+ .querySelector<HTMLElement>('.remarkd-editor__toolbar zn-button[label="Include"]')!;
1246
1405
  includeButton.dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
1247
1406
 
1248
1407
  await waitUntil(() => el.shadowRoot!.querySelector('.remarkd-editor__include-picker-empty')?.textContent
@@ -1461,7 +1620,7 @@ const x = {product};
1461
1620
  await dropdown.show();
1462
1621
  await new Promise(resolve => requestAnimationFrame(resolve));
1463
1622
 
1464
- const linkItem = [...dropdown.querySelectorAll('zn-menu-item')]
1623
+ const linkItem = [...dropdown.querySelectorAll('zn-menu[slot="submenu"] zn-menu-item')]
1465
1624
  .find(item => item.textContent?.includes('Link to article'));
1466
1625
  expect(linkItem, 'the article-link action should be reachable through the overflow menu').to.exist;
1467
1626
  // ZnButton overrides click() without dispatching a DOM event, so the menu item's
@@ -1584,7 +1743,7 @@ const x = {product};
1584
1743
  await el.updateComplete;
1585
1744
  const input = typeInBlock(el, text);
1586
1745
  input.setSelectionRange(from, to);
1587
- el.shadowRoot!.querySelector<ZnButton>('zn-button[tooltip="Link to article"]')!
1746
+ el.shadowRoot!.querySelector<ZnButton>('zn-button[label="Link to article"]')!
1588
1747
  .dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
1589
1748
  await waitUntil(() => el.shadowRoot!.querySelector('.remarkd-editor__link-picker'),
1590
1749
  'the link picker never opened');
@@ -21,3 +21,4 @@ export type {ZnThemeChangeEvent} from './zn-theme-change';
21
21
  export type {ZnThemeSubmitEvent} from './zn-theme-submit';
22
22
  export type {ZnSlashSelectEvent} from './zn-slash-select';
23
23
  export type {ZnSlashInsertEvent} from './zn-slash-insert';
24
+ export type {ZnRepositionEvent} from './zn-reposition';
@@ -0,0 +1,7 @@
1
+ export type ZnRepositionEvent = CustomEvent<Record<PropertyKey, never>>;
2
+
3
+ declare global {
4
+ interface GlobalEventHandlersEventMap {
5
+ 'zn-reposition': ZnRepositionEvent;
6
+ }
7
+ }