@lion/ui 0.18.0-menu-system.5 → 0.18.0-menu-system.6

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 (66) hide show
  1. package/components/menu/src/InteractiveListMixin.js +5 -2
  2. package/components/menu/src/MoreButtonMenuMixin.js +18 -27
  3. package/components/menu/src/utils/listItemInteractions.js +2 -1
  4. package/components/menu/test/lion-menu.test.js +2 -0
  5. package/components/menu/test-suites/InteractiveListMixin.suite.js +339 -1
  6. package/components/menu/test-suites/LionMenuHybrid.suite.js +583 -0
  7. package/components/overlays/src/OverlayController.js +1 -1
  8. package/dist-types/components/menu/src/InteractiveListMixin.d.ts +2 -2
  9. package/dist-types/components/menu/src/InteractiveListMixin.d.ts.map +1 -1
  10. package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts +0 -1
  11. package/dist-types/components/menu/src/MoreButtonMenuMixin.d.ts.map +1 -1
  12. package/dist-types/components/menu/src/utils/listItemInteractions.d.ts.map +1 -1
  13. package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts.map +1 -1
  14. package/dist-types/components/menu/test-suites/LionMenuHybrid.suite.d.ts +5 -0
  15. package/dist-types/components/menu/test-suites/LionMenuHybrid.suite.d.ts.map +1 -0
  16. package/dist-types/tsconfig.tsbuildinfo +1 -1
  17. package/docs/components/checkbox-group/overview.md +2 -2
  18. package/docs/components/collapsible/overview.md +1 -1
  19. package/docs/components/combobox/overview.md +1 -1
  20. package/docs/components/combobox/use-cases.md +1 -1
  21. package/docs/components/dialog/overview.md +1 -1
  22. package/docs/components/fieldset/overview.md +5 -5
  23. package/docs/components/form/overview.md +3 -3
  24. package/docs/components/form/use-cases.md +2 -2
  25. package/docs/components/icon/use-cases.md +1 -1
  26. package/docs/components/input/overview.md +1 -1
  27. package/docs/components/input-amount/overview.md +3 -3
  28. package/docs/components/input-date/overview.md +3 -3
  29. package/docs/components/input-datepicker/overview.md +5 -5
  30. package/docs/components/input-email/overview.md +2 -2
  31. package/docs/components/input-file/overview.md +1 -1
  32. package/docs/components/input-iban/overview.md +2 -2
  33. package/docs/components/input-range/overview.md +2 -2
  34. package/docs/components/input-stepper/overview.md +1 -1
  35. package/docs/components/input-tel/overview.md +1 -1
  36. package/docs/components/input-tel/use-cases.md +6 -6
  37. package/docs/components/input-tel-dropdown/overview.md +1 -1
  38. package/docs/components/listbox/use-cases.md +1 -1
  39. package/docs/components/radio-group/overview.md +3 -3
  40. package/docs/components/select/use-cases.md +1 -1
  41. package/docs/components/select-rich/use-cases.md +2 -2
  42. package/docs/components/tabs/examples.md +1 -1
  43. package/docs/fundamentals/index.md +4 -4
  44. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  45. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  46. package/docs/fundamentals/rationales/accessibility.md +1 -1
  47. package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
  48. package/docs/fundamentals/systems/core/overview.md +1 -1
  49. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  50. package/docs/fundamentals/systems/form/model-value.md +1 -1
  51. package/docs/fundamentals/systems/form/overview.md +28 -28
  52. package/docs/fundamentals/systems/form/validate.md +2 -2
  53. package/docs/fundamentals/systems/localize/overview.md +4 -4
  54. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  55. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  56. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  57. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  58. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  59. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  60. package/docs/guides/how-to/create-a-form.md +4 -4
  61. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  62. package/docs/guides/how-to/index.md +1 -1
  63. package/docs/guides/index.md +8 -8
  64. package/docs/guides/principles/index.md +5 -5
  65. package/docs/guides/principles/styling.md +1 -1
  66. package/package.json +1 -1
@@ -293,7 +293,7 @@ const InteractiveListMixinImplementation = superclass =>
293
293
  }
294
294
 
295
295
  // @ts-ignore - focusableEl is safely used as HTMLElement here
296
- if (!isInView(this._scrollTargetNode, focusableEl)) {
296
+ if (this.orientation === 'vertical' && !isInView(this._scrollTargetNode, focusableEl)) {
297
297
  focusableEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
298
298
  }
299
299
 
@@ -820,7 +820,10 @@ const InteractiveListMixinImplementation = superclass =>
820
820
  __syncCurrentPageWithLocationHref(location) {
821
821
  this.listItems.forEach(item => {
822
822
  if (/** @type {HTMLAnchorElement} */ (item).href) {
823
- if (location.href.includes(/** @type {HTMLAnchorElement} */ (item).href)) {
823
+ const pathname = location.pathname.endsWith('/')
824
+ ? location.pathname.substring(0, location.pathname.length - 1)
825
+ : location.pathname;
826
+ if (pathname === /** @type {HTMLAnchorElement} */ (item).href) {
824
827
  setChecked(item);
825
828
  } else {
826
829
  setChecked(item, true);
@@ -18,8 +18,8 @@ const styles = css`
18
18
  /** N.B. this is a simplified version of sr-only. when we move to overlayctrl, we should build this into overlayctrl */
19
19
  [data-more-button-menu] {
20
20
  overflow: hidden;
21
- height: 1px;
22
- width: 1px;
21
+ height: 0;
22
+ width: 0;
23
23
  }
24
24
 
25
25
  [role='list']:has(> [data-more-button-wrapper]) > [role='listitem'] > :is(button, a) {
@@ -75,11 +75,13 @@ export const MoreButtonMenuMixin = superclass =>
75
75
  moreButton?.setAttribute('aria-expanded', 'false');
76
76
  const moreButtonMenu = document.createElement('div');
77
77
  moreButtonMenu.setAttribute('data-more-button-menu', '');
78
+ moreButtonMenu.setAttribute('tabindex', '-1');
78
79
  moreButtonWrapper.appendChild(moreButtonMenu);
79
80
  this._listNode.appendChild(moreButtonWrapper);
80
81
 
81
82
  const isElementDirectFocusableItemUnderMoreButtonMenu = (/** @type {Element} */ element) =>
82
- this.listItems.includes(element) && moreButtonMenu.contains(element);
83
+ (this.listItems.includes(element) && moreButtonMenu.contains(element)) ||
84
+ element === moreButtonMenu;
83
85
 
84
86
  moreButtonMenu.addEventListener(
85
87
  'focusin',
@@ -87,6 +89,9 @@ export const MoreButtonMenuMixin = superclass =>
87
89
  const target = /** @type {HTMLElement} */ (event?.target);
88
90
  if (!isElementDirectFocusableItemUnderMoreButtonMenu(target)) return;
89
91
 
92
+ // If applicable hide any other L2 menus
93
+ this.querySelector('[level="2"]:has([data-open])')?._overlayCtrl?.hide();
94
+
90
95
  // A user did `Shift + Tab` from L2 menu to More menu
91
96
  if (target?.getAttribute('aria-expanded') === 'true') {
92
97
  // close L2 menu
@@ -110,31 +115,11 @@ export const MoreButtonMenuMixin = superclass =>
110
115
  }, 100);
111
116
  });
112
117
 
113
- this.hasMoreButtonMenuAnyFocusedFirstLevelItems = false;
114
-
115
- moreButton?.addEventListener('mousedown', () => {
116
- this.hasMoreButtonMenuAnyFocusedFirstLevelItems = false;
117
- const focusableFirstLevelItems = moreButtonMenu.querySelectorAll(
118
- ':scope > [role="listitem"] > :is(a, button)',
119
- );
120
-
121
- // @ts-ignore Type 'NodeListOf<Element>' must have a '[Symbol.iterator]()' method that returns an iterator.
122
- for (const focusableFirstLevelItem of focusableFirstLevelItems) {
123
- if (this._getDeepActiveElement() === focusableFirstLevelItem) {
124
- this.hasMoreButtonMenuAnyFocusedFirstLevelItems = true;
125
- }
126
- }
127
- });
128
-
129
118
  moreButton?.addEventListener('click', () => {
130
- if (this.hasMoreButtonMenuAnyFocusedFirstLevelItems) return;
131
- /** @type {HTMLElement | null} */
132
- const firstInteractiveItem = moreButtonMenu.querySelector(
133
- ':scope > [role="listitem"] > :is(a, button)',
134
- );
135
-
136
- firstInteractiveItem?.focus();
137
- moreButtonMenu.setAttribute('data-open', '');
119
+ if (moreButtonMenu.getAttribute('data-open') === null) {
120
+ moreButtonMenu?.focus({ preventScroll: true });
121
+ moreButtonMenu.setAttribute('data-open', '');
122
+ }
138
123
  });
139
124
  }
140
125
 
@@ -186,6 +171,9 @@ export const MoreButtonMenuMixin = superclass =>
186
171
  const previousNodes = [];
187
172
  let { previousSibling } = listItem;
188
173
  while (previousSibling) {
174
+ if (previousSibling.nodeType === Node.ELEMENT_NODE) {
175
+ break;
176
+ }
189
177
  previousNodes.unshift(previousSibling);
190
178
  if (isMarkerComment(previousSibling)) {
191
179
  break;
@@ -196,6 +184,9 @@ export const MoreButtonMenuMixin = superclass =>
196
184
  const nextNodes = [];
197
185
  let { nextSibling } = listItem;
198
186
  while (nextSibling) {
187
+ if (nextSibling.nodeType === Node.ELEMENT_NODE) {
188
+ break;
189
+ }
199
190
  nextNodes.push(nextSibling);
200
191
 
201
192
  if (isMarkerComment(nextSibling)) {
@@ -75,7 +75,8 @@ export function setChecked(item, unset = false) {
75
75
  * @param {HTMLElement} item
76
76
  */
77
77
  export function toggleChecked(item) {
78
- setChecked(item, !isChecked(item));
78
+ const shouldUnset = isChecked(item);
79
+ setChecked(item, shouldUnset);
79
80
  }
80
81
 
81
82
  /**
@@ -1,4 +1,6 @@
1
1
  import { runInteractiveListMixinSuite } from '../test-suites/InteractiveListMixin.suite.js';
2
2
  import '@lion/ui/define/lion-menu.js';
3
+ import { runLionMenuHybridSuite } from '../test-suites/LionMenuHybrid.suite.js';
3
4
 
4
5
  runInteractiveListMixinSuite({ tagString: 'lion-menu' });
6
+ runLionMenuHybridSuite();
@@ -1,5 +1,8 @@
1
1
  import { LitElement } from 'lit';
2
2
  import { expect, html, fixture as _fixture, unsafeStatic, defineCE } from '@open-wc/testing';
3
+ import { sendMouse, resetMouse } from '@web/test-runner-commands';
4
+ import { useFakeTimers } from 'sinon';
5
+ import { getDeepActiveElement } from '@lion/ui/overlays.js';
3
6
  import { InteractiveListMixin } from '../src/InteractiveListMixin.js';
4
7
 
5
8
  class InteractiveListClass extends InteractiveListMixin(LitElement) {}
@@ -29,6 +32,19 @@ const supportedRoles = [
29
32
 
30
33
  const fixture = /** @type {(arg: TemplateResult) => Promise<LionMenu>} */ (_fixture);
31
34
 
35
+ const l1Config = {
36
+ isBar: true,
37
+ hasFullWidthFlyout: true,
38
+ openableConfig: {
39
+ // We want disclosure behavior
40
+ placementMode: 'custom',
41
+ // N.B. we don't have an invoker...
42
+ isActivated: false,
43
+ inheritsReferenceWidth: 'none',
44
+ focusContentOnOpen: true,
45
+ },
46
+ };
47
+
32
48
  /**
33
49
  * @param {TestConfig} [customConfig]
34
50
  */
@@ -155,7 +171,6 @@ export function runInteractiveListMixinSuite(customConfig) {
155
171
  ${item(2, 'option')}
156
172
  </${tag}>
157
173
  `);
158
- console.debug('listItems', el.listItems);
159
174
  const item1 = el.listItems[1];
160
175
  expect(item1.getAttribute('aria-selected')).to.equal('false');
161
176
 
@@ -300,5 +315,328 @@ export function runInteractiveListMixinSuite(customConfig) {
300
315
  it('can be extended to [role=toolbar]', async () => {});
301
316
  it('can be extended to [role=tree]', async () => {});
302
317
  });
318
+
319
+ describe('Overflow handling', () => {
320
+ /** @type {import('sinon').SinonFakeTimers | null} */
321
+ let clock = null;
322
+
323
+ /** @param {HTMLElement} el */
324
+ const isMoreButtonShown = el => {
325
+ const wrapper = /** @type {HTMLElement | null} */ (
326
+ el.querySelector('[data-more-button-wrapper]')
327
+ );
328
+ return wrapper?.style.display !== 'none';
329
+ };
330
+
331
+ /** @param {HTMLElement} el */
332
+ const isMoreButtonMenuShown = el => {
333
+ const menu = el.querySelector('[data-more-button-menu]');
334
+ return menu ? getComputedStyle(menu).width !== '0px' : false;
335
+ };
336
+
337
+ /** @param {HTMLElement} el */
338
+ const getMoreButton = el =>
339
+ /** @type {HTMLElement | null} */ (el.querySelector('[data-more-button]'));
340
+ /** @param {HTMLElement} el */
341
+ const getMoreButtonMenu = el => el.querySelector('[data-more-button-menu]');
342
+
343
+ /**
344
+ * @param {HTMLElement} element
345
+ */
346
+ function getCoordinates(element) {
347
+ const { x, y, height } = element.getBoundingClientRect();
348
+
349
+ return {
350
+ x: Math.floor(x + window.pageXOffset),
351
+ y: Math.floor(y + window.pageYOffset + height / 2),
352
+ };
353
+ }
354
+ /** @param {HTMLElement} el */
355
+ const clickOnMoreButton = async el => {
356
+ const moreButton = getMoreButton(el);
357
+ if (!moreButton) {
358
+ return;
359
+ }
360
+ const { x, y } = getCoordinates(moreButton);
361
+ await sendMouse({ type: 'click', position: [x, y] });
362
+ clock?.tick(100);
363
+ };
364
+
365
+ /** @param {HTMLElement} el */
366
+ const getDirectListItemsUnderMoreButtonMenu = el => [
367
+ ...Array.from(
368
+ el.querySelectorAll('[data-more-button-menu] > [role="listitem"]'),
369
+ item => /** @type {HTMLElement} */ (item),
370
+ ),
371
+ ];
372
+
373
+ /** @param {HTMLElement} el */
374
+ const getDirectListItemsUnderMainMenu = el => [
375
+ ...Array.from(
376
+ el.querySelectorAll('[slot="list"] > [role="listitem"]'),
377
+ item => /** @type {HTMLElement} */ (item),
378
+ ),
379
+ ];
380
+
381
+ /** @param {HTMLElement & { updateComplete: Promise<unknown> }} el */
382
+ const waitResizeEventDebounce = async el => {
383
+ await el.updateComplete;
384
+ // wait for resize event debouncer
385
+ clock?.tick(55);
386
+ };
387
+
388
+ beforeEach(() => {
389
+ clock = useFakeTimers({
390
+ shouldAdvanceTime: true,
391
+ });
392
+ });
393
+ afterEach(async () => {
394
+ clock?.restore();
395
+ await resetMouse();
396
+ });
397
+
398
+ it('should show no More button when all items fit', async () => {
399
+ const el = await fixture(html`
400
+ <${tag} name="foo" ._activeMode="${'tabbable-disclosure'}" .itemWrap="${true}"
401
+ data-has-full-width-flyout orientation="horizontal" style="min-width: 170px; max-width: 170px;">
402
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
403
+ <a href="#">Item 1</a>
404
+ </div>
405
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
406
+ <a href="#">Item 2</a>
407
+ </div>
408
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
409
+ <a href="#">Item 3</a>
410
+ </div>
411
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
412
+ <button>More</button>
413
+ </div>
414
+ </${tag}>
415
+ `);
416
+ await waitResizeEventDebounce(el);
417
+ // 3 items is 150px. They fit into 170px parent
418
+ expect(isMoreButtonShown(el)).to.equal(false);
419
+ });
420
+
421
+ it('should show More button when not all items fit', async () => {
422
+ const el = await fixture(html`
423
+ <${tag}
424
+ .itemWrap="${true}"
425
+ ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
426
+ .config="${l1Config.openableConfig}"
427
+ .bar="${l1Config.isBar}"
428
+ ._activeMode="${'tabbable-disclosure'}"
429
+ style="min-width: 170px; max-width: 170px;"
430
+ >
431
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
432
+ <a href="#">Item 1</a>
433
+ </div>
434
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
435
+ <a href="#">Item 2</a>
436
+ </div>
437
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
438
+ <a href="#">Item 3</a>
439
+ </div>
440
+ <div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
441
+ <a href="#">Item 4</a>
442
+ </div>
443
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
444
+ <button>More</button>
445
+ </div>
446
+ </${tag}>
447
+ <style>
448
+ [orientation='horizontal'] ::slotted([slot='list']) {
449
+ display: flex;
450
+ }
451
+ </style>
452
+ `);
453
+
454
+ await waitResizeEventDebounce(el);
455
+ // 4 items is 200px. They don't fit into 170px parent. So More button should be shown
456
+ expect(isMoreButtonShown(el)).to.equal(true);
457
+ });
458
+
459
+ it('should show 2 items when clicking on `More` button', async () => {
460
+ const el = await fixture(html`
461
+ <${tag}
462
+ .itemWrap="${true}"
463
+ ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
464
+ .config="${l1Config.openableConfig}"
465
+ .bar="${l1Config.isBar}"
466
+ ._activeMode="${'tabbable-disclosure'}"
467
+ style="min-width: 170px; max-width: 170px;"
468
+ >
469
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
470
+ <a href="#">Item 1</a>
471
+ </div>
472
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
473
+ <a href="#">Item 2</a>
474
+ </div>
475
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
476
+ <a href="#">Item 3</a>
477
+ </div>
478
+ <div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
479
+ <a href="#">Item 4</a>
480
+ </div>
481
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
482
+ <button>More</button>
483
+ </div>
484
+ </${tag}>
485
+ `);
486
+
487
+ await waitResizeEventDebounce(el);
488
+ await clickOnMoreButton(el);
489
+
490
+ expect(isMoreButtonMenuShown(el)).to.equal(true);
491
+ const labels = getDirectListItemsUnderMoreButtonMenu(el).map(
492
+ item => item.textContent?.trim() ?? '',
493
+ );
494
+ expect(labels).to.deep.equal(['Item 3', 'Item 4']);
495
+ });
496
+
497
+ it('should focus more button menu container when clicking on `More` button', async () => {
498
+ const el = await fixture(html`
499
+ <${tag}
500
+ .itemWrap="${true}"
501
+ ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
502
+ .config="${l1Config.openableConfig}"
503
+ .bar="${l1Config.isBar}"
504
+ ._activeMode="${'tabbable-disclosure'}"
505
+ style="min-width: 170px; max-width: 170px;"
506
+ >
507
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
508
+ <a href="#">Item 1</a>
509
+ </div>
510
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
511
+ <a href="#">Item 2</a>
512
+ </div>
513
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
514
+ <a href="#">Item 3</a>
515
+ </div>
516
+ <div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
517
+ <a href="#">Item 4</a>
518
+ </div>
519
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
520
+ <button>More</button>
521
+ </div>
522
+ </${tag}>
523
+ `);
524
+
525
+ await waitResizeEventDebounce(el);
526
+ await clickOnMoreButton(el);
527
+
528
+ expect(isMoreButtonMenuShown(el)).to.equal(true);
529
+ expect(getDeepActiveElement() === getMoreButtonMenu(el)).to.equal(true);
530
+ });
531
+
532
+ it('should open and then close More button menu when clicking 2 times on the More button', async () => {
533
+ const el = await fixture(html`
534
+ <${tag}
535
+ .itemWrap="${true}"
536
+ ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
537
+ .config="${l1Config.openableConfig}"
538
+ .bar="${l1Config.isBar}"
539
+ ._activeMode="${'tabbable-disclosure'}"
540
+ style="min-width: 170px; max-width: 170px;"
541
+ >
542
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
543
+ <a href="#">Item 1</a>
544
+ </div>
545
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
546
+ <a href="#">Item 2</a>
547
+ </div>
548
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
549
+ <a href="#">Item 3</a>
550
+ </div>
551
+ <div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
552
+ <a href="#">Item 4</a>
553
+ </div>
554
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
555
+ <button>More</button>
556
+ </div>
557
+ </${tag}>
558
+ `);
559
+
560
+ await waitResizeEventDebounce(el);
561
+ expect(isMoreButtonMenuShown(el)).to.equal(false);
562
+ await clickOnMoreButton(el);
563
+ expect(isMoreButtonMenuShown(el)).to.equal(true);
564
+ await clickOnMoreButton(el);
565
+ expect(isMoreButtonMenuShown(el)).to.equal(false);
566
+ });
567
+
568
+ it('should remove `More` button when making font smaller', async () => {
569
+ const el = await fixture(html`
570
+ <${tag} name="foo" ._activeMode="${'tabbable-disclosure'}" .itemWrap="${true}"
571
+ data-has-full-width-flyout orientation="horizontal" style="min-width: 170px; max-width: 170px;">
572
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
573
+ <a href="#">Item 1</a>
574
+ </div>
575
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
576
+ <a href="#">Item 2</a>
577
+ </div>
578
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
579
+ <a href="#">Item 3</a>
580
+ </div>
581
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
582
+ <button>More</button>
583
+ </div>
584
+ </${tag}>
585
+ `);
586
+ await waitResizeEventDebounce(el);
587
+
588
+ /**
589
+ * Make font smaller for first level items to force
590
+ * the menu render all 4 items and no `More` button
591
+ */
592
+ getDirectListItemsUnderMainMenu(el).forEach(item => {
593
+ // eslint-disable-next-line no-param-reassign
594
+ item.style.fontSize = '12px';
595
+ });
596
+ el?.dispatchEvent(new CustomEvent('resize', { composed: true, bubbles: true }));
597
+ await waitResizeEventDebounce(el);
598
+ expect(isMoreButtonShown(el)).to.equal(false);
599
+ });
600
+
601
+ it('should add `More` button when making font larger', async () => {
602
+ const el = await fixture(html`
603
+ <${tag}
604
+ .itemWrap="${true}"
605
+ ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
606
+ .config="${l1Config.openableConfig}"
607
+ .bar="${l1Config.isBar}"
608
+ ._activeMode="${'tabbable-disclosure'}"
609
+ style="min-width: 170px; max-width: 170px;"
610
+ >
611
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
612
+ <a href="#">Item 1</a>
613
+ </div>
614
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
615
+ <a href="#">Item 2</a>
616
+ </div>
617
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
618
+ <a href="#">Item 3</a>
619
+ </div>
620
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
621
+ <button>More</button>
622
+ </div>
623
+ </${tag}>
624
+ `);
625
+ await waitResizeEventDebounce(el);
626
+
627
+ expect(getDirectListItemsUnderMainMenu(el).length).to.equal(3);
628
+ /**
629
+ * Make font larger for first level items to force
630
+ * the More button to appear
631
+ */
632
+ getDirectListItemsUnderMainMenu(el).forEach(item => {
633
+ // eslint-disable-next-line no-param-reassign
634
+ item.style.fontSize = '40px';
635
+ });
636
+ el?.dispatchEvent(new CustomEvent('resize', { composed: true, bubbles: true }));
637
+ await waitResizeEventDebounce(el);
638
+ expect(isMoreButtonShown(el)).to.equal(true);
639
+ });
640
+ });
303
641
  });
304
642
  }