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

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 (115) hide show
  1. package/components/menu/src/InteractiveListMixin.js +9 -10
  2. package/components/menu/src/LionMenu.js +1 -1
  3. package/components/menu/src/LionTree.js +1 -1
  4. package/components/menu/src/MultiLevelListMixin.js +46 -9
  5. package/components/menu/test/InteractiveListMixin.test.js +3 -0
  6. package/components/menu/test/MoreButtonMenuMixin.test.js +4 -0
  7. package/components/menu/test/MultiLevelListMixin.test.js +3 -0
  8. package/components/menu/test/lion-menu-hybrid.test.js +3 -0
  9. package/components/menu/test/lion-menu.test.js +7 -2
  10. package/components/menu/test/lion-toolbar.test.js +8 -0
  11. package/components/menu/test/lion-tree.test.js +7 -0
  12. package/components/menu/test-helpers/mimicKeyPress.js +9 -0
  13. package/components/menu/test-suites/InteractiveListMixin.suite.js +154 -342
  14. package/components/menu/test-suites/LionMenu.suite.js +113 -0
  15. package/components/menu/test-suites/LionMenuHybrid.suite.js +1 -1
  16. package/components/menu/test-suites/MoreButtonMenuMixin.suite.js +372 -0
  17. package/components/menu/test-suites/MultiLevelListMixin.suite.js +303 -0
  18. package/components/menu/types/InteractiveListMixinTypes.d.ts +2 -0
  19. package/components/overlays/types/OverlayMixinTypes.ts +2 -0
  20. package/custom-elements.json +245 -153
  21. package/dist-types/components/combobox/src/LionCombobox.d.ts +1 -1
  22. package/dist-types/components/dialog/src/LionDialog.d.ts +1 -1
  23. package/dist-types/components/input-datepicker/src/LionInputDatepicker.d.ts +1 -1
  24. package/dist-types/components/menu/src/InteractiveListMixin.d.ts +3 -3
  25. package/dist-types/components/menu/src/InteractiveListMixin.d.ts.map +1 -1
  26. package/dist-types/components/menu/src/LionMenu.d.ts +75 -5
  27. package/dist-types/components/menu/src/LionMenu.d.ts.map +1 -1
  28. package/dist-types/components/menu/src/LionMenuOverlay.d.ts +4 -2
  29. package/dist-types/components/menu/src/LionMenuOverlay.d.ts.map +1 -1
  30. package/dist-types/components/menu/src/LionTree.d.ts +2 -2
  31. package/dist-types/components/menu/src/MultiLevelListMixin.d.ts +10 -2
  32. package/dist-types/components/menu/src/MultiLevelListMixin.d.ts.map +1 -1
  33. package/dist-types/components/menu/test/InteractiveListMixin.test.d.ts +2 -0
  34. package/dist-types/components/menu/test/InteractiveListMixin.test.d.ts.map +1 -0
  35. package/dist-types/components/menu/test/MoreButtonMenuMixin.test.d.ts +2 -0
  36. package/dist-types/components/menu/test/MoreButtonMenuMixin.test.d.ts.map +1 -0
  37. package/dist-types/components/menu/test/MultiLevelListMixin.test.d.ts +2 -0
  38. package/dist-types/components/menu/test/MultiLevelListMixin.test.d.ts.map +1 -0
  39. package/dist-types/components/menu/test/lion-menu-hybrid.test.d.ts +2 -0
  40. package/dist-types/components/menu/test/lion-menu-hybrid.test.d.ts.map +1 -0
  41. package/dist-types/components/menu/test/lion-toolbar.test.d.ts +2 -0
  42. package/dist-types/components/menu/test/lion-toolbar.test.d.ts.map +1 -0
  43. package/dist-types/components/menu/test/lion-tree.test.d.ts +2 -0
  44. package/dist-types/components/menu/test/lion-tree.test.d.ts.map +1 -0
  45. package/dist-types/components/menu/test-helpers/mimicKeyPress.d.ts +7 -0
  46. package/dist-types/components/menu/test-helpers/mimicKeyPress.d.ts.map +1 -0
  47. package/dist-types/components/menu/test-suites/InteractiveListMixin.suite.d.ts.map +1 -1
  48. package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts +9 -0
  49. package/dist-types/components/menu/test-suites/LionMenu.suite.d.ts.map +1 -0
  50. package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts +14 -0
  51. package/dist-types/components/menu/test-suites/MoreButtonMenuMixin.suite.d.ts.map +1 -0
  52. package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts +11 -0
  53. package/dist-types/components/menu/test-suites/MultiLevelListMixin.suite.d.ts.map +1 -0
  54. package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts +1 -0
  55. package/dist-types/components/overlays/types/OverlayMixinTypes.d.ts.map +1 -1
  56. package/dist-types/components/select-rich/src/LionSelectRich.d.ts +1 -1
  57. package/dist-types/components/tooltip/src/LionTooltip.d.ts +1 -1
  58. package/dist-types/exports/menu-test-suites.d.ts +2 -0
  59. package/dist-types/tsconfig.tsbuildinfo +1 -1
  60. package/docs/components/checkbox-group/overview.md +2 -2
  61. package/docs/components/collapsible/overview.md +1 -1
  62. package/docs/components/combobox/overview.md +1 -1
  63. package/docs/components/combobox/use-cases.md +1 -1
  64. package/docs/components/dialog/overview.md +1 -1
  65. package/docs/components/fieldset/overview.md +5 -5
  66. package/docs/components/form/overview.md +3 -3
  67. package/docs/components/form/use-cases.md +2 -2
  68. package/docs/components/icon/use-cases.md +1 -1
  69. package/docs/components/input/overview.md +1 -1
  70. package/docs/components/input-amount/overview.md +3 -3
  71. package/docs/components/input-date/overview.md +3 -3
  72. package/docs/components/input-datepicker/overview.md +5 -5
  73. package/docs/components/input-email/overview.md +2 -2
  74. package/docs/components/input-file/overview.md +1 -1
  75. package/docs/components/input-iban/overview.md +2 -2
  76. package/docs/components/input-range/overview.md +2 -2
  77. package/docs/components/input-stepper/overview.md +1 -1
  78. package/docs/components/input-tel/overview.md +1 -1
  79. package/docs/components/input-tel/use-cases.md +6 -6
  80. package/docs/components/input-tel-dropdown/overview.md +1 -1
  81. package/docs/components/listbox/use-cases.md +1 -1
  82. package/docs/components/menu/api-table.md +44 -39
  83. package/docs/components/radio-group/overview.md +3 -3
  84. package/docs/components/select/use-cases.md +1 -1
  85. package/docs/components/select-rich/use-cases.md +2 -2
  86. package/docs/components/tabs/examples.md +1 -1
  87. package/docs/fundamentals/index.md +4 -4
  88. package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
  89. package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
  90. package/docs/fundamentals/rationales/accessibility.md +1 -1
  91. package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
  92. package/docs/fundamentals/systems/core/overview.md +1 -1
  93. package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
  94. package/docs/fundamentals/systems/form/model-value.md +1 -1
  95. package/docs/fundamentals/systems/form/overview.md +28 -28
  96. package/docs/fundamentals/systems/form/validate.md +2 -2
  97. package/docs/fundamentals/systems/localize/overview.md +4 -4
  98. package/docs/fundamentals/systems/overlays/configuration.md +2 -2
  99. package/docs/fundamentals/systems/overlays/overview.md +5 -5
  100. package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
  101. package/docs/fundamentals/tools/helpers/overview.md +1 -1
  102. package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
  103. package/docs/guides/how-to/create-a-custom-field.md +5 -5
  104. package/docs/guides/how-to/create-a-form.md +4 -4
  105. package/docs/guides/how-to/extend-a-native-input.md +2 -2
  106. package/docs/guides/how-to/index.md +1 -1
  107. package/docs/guides/index.md +8 -8
  108. package/docs/guides/principles/index.md +5 -5
  109. package/docs/guides/principles/styling.md +1 -1
  110. package/exports/menu-test-suites.js +2 -0
  111. package/menu-test-suites.js +2 -0
  112. package/package.json +1 -1
  113. package/components/menu/src/utils/uuid.js +0 -3
  114. package/dist-types/components/menu/src/utils/uuid.d.ts +0 -2
  115. package/dist-types/components/menu/src/utils/uuid.d.ts.map +0 -1
@@ -0,0 +1,113 @@
1
+ /* eslint-disable lit-a11y/click-events-have-key-events */
2
+ import { defineCE, expect, fixture as _fixture, html, unsafeStatic } from '@open-wc/testing';
3
+ import { LionMenu } from '../src/LionMenu.js';
4
+
5
+ /**
6
+ * @typedef {import('lit').TemplateResult} TemplateResult
7
+ *
8
+ * @typedef {Object} TestConfig
9
+ * @property {string} tagString
10
+ */
11
+
12
+ const fixture = /** @type {(arg: TemplateResult) => Promise<LionMenu>} */ (_fixture);
13
+
14
+ /**
15
+ * @param {TestConfig} [customConfig]
16
+ */
17
+ export function runLionMenuSuite(customConfig) {
18
+ const cfg = {
19
+ tagString: customConfig?.tagString || defineCE(LionMenu),
20
+ };
21
+
22
+ const { tagString } = cfg;
23
+ const tag = unsafeStatic(tagString);
24
+
25
+ describe('LionMenu', () => {
26
+ it('sets the correct role to the listNode', async () => {
27
+ const el = await fixture(html`
28
+ <${tag}>
29
+ <div role="listitem" id="item-0">
30
+ <a href="#foo">Item 0</a>
31
+ </div>
32
+ <div role="listitem" id="item-1">
33
+ <a href="#bar">Item 1</a>
34
+ </div>
35
+ </${tag}>
36
+ `);
37
+
38
+ const listNode = el.querySelector('[slot="list"]');
39
+ expect(listNode?.getAttribute('role')).to.equal('menu');
40
+ });
41
+
42
+ it('sets the correct role to the listNode when activeMode is tabbable-disclosure', async () => {
43
+ const el = await fixture(html`
44
+ <${tag} ._activeMode=${'tabbable-disclosure'}>
45
+ <div role="listitem" id="item-0">
46
+ <a href="#foo">Item 0</a>
47
+ </div>
48
+ <div role="listitem" id="item-1">
49
+ <a href="#bar">Item 1</a>
50
+ </div>
51
+ </${tag}>
52
+ `);
53
+ const listNode = el.querySelector('[slot="list"]');
54
+ expect(listNode?.getAttribute('role')).to.equal('list');
55
+ });
56
+
57
+ it('sets the correct roles when it is a bar', async () => {
58
+ const el = await fixture(html`
59
+ <${tag}>
60
+ <div role="listitem" id="item-0">
61
+ <a href="#foo">Item 0</a>
62
+ </div>
63
+ <div role="listitem" id="item-1">
64
+ <a href="#bar">Item 1</a>
65
+ </div>
66
+ </${tag}>
67
+ `);
68
+
69
+ el._listRole = 'menu';
70
+ expect(el._listRole).to.equal('menu');
71
+ expect(el.orientation).to.equal('vertical');
72
+ el.bar = true;
73
+ await el.updateComplete;
74
+ expect(el._listRole).to.equal('menubar');
75
+ expect(el.orientation).to.equal('horizontal');
76
+ });
77
+
78
+ describe('Programmatic interaction', () => {
79
+ it('allows groups within one menu level', async () => {
80
+ const el = await fixture(html`
81
+ <${tag}>
82
+ <div role="menuitemcheckbox" aria-checked="false">Bold</div>
83
+ <div role="menuitemcheckbox" aria-checked="false">Italic</div>
84
+ <div role="separator"></div>
85
+ <div role="group" aria-label="Text Color">
86
+ <div role="menuitemradio" aria-checked="false">Blue</div>
87
+ <div role="menuitemradio" aria-checked="false">Red</div>
88
+ <div role="menuitemradio" aria-checked="false">Green</div>
89
+ </div>
90
+ </${tag}>
91
+ `);
92
+ el.setCheckedIndex(1);
93
+ const item1 = el.listItems[1];
94
+ const item3 = el.listItems[3];
95
+ expect(item1.getAttribute('aria-checked')).to.equal('true');
96
+
97
+ // change within checkbox group doesn't trigger a change on checkbox
98
+ el.setCheckedIndex(0);
99
+ expect(item1.getAttribute('aria-checked')).to.equal('true');
100
+
101
+ // update outside checkbox group doesn't trigger a change on checkbox
102
+ el.setCheckedIndex(3);
103
+ expect(item1.getAttribute('aria-checked')).to.equal('true');
104
+ expect(item3.getAttribute('aria-checked')).to.equal('true');
105
+
106
+ // change within radio group triggers a change on radio button and not on checkbox
107
+ el.setCheckedIndex(4);
108
+ expect(item1.getAttribute('aria-checked')).to.equal('true');
109
+ expect(item3.getAttribute('aria-checked')).to.equal('false');
110
+ });
111
+ });
112
+ });
113
+ }
@@ -129,7 +129,7 @@ export function runLionMenuHybridSuite({ klass = LionMenuHybrid } = {}) {
129
129
 
130
130
  /**
131
131
  * We need actual 100 ms timeout when More button menu is getting hidden,
132
- * `useFakeTimers` is not an option. Othewise Safari fails.
132
+ * `useFakeTimers` is not an option. Otherwise Safari fails.
133
133
  * See more info in the description of `_createMoreButtonWrapper` method in `MoreButtonMenuMixin`
134
134
  */
135
135
  const waitAfterMoreButtonMenuHides = async () => {
@@ -0,0 +1,372 @@
1
+ import { LitElement } from 'lit';
2
+ import { expect, html, fixture as _fixture, unsafeStatic, defineCE } from '@open-wc/testing';
3
+ import { sendMouse, resetMouse } from '@web/test-runner-commands';
4
+ import sinon from 'sinon';
5
+ import { getDeepActiveElement } from '@lion/ui/overlays.js';
6
+ import { InteractiveListMixin } from '../src/InteractiveListMixin.js';
7
+
8
+ class InteractiveListClass extends InteractiveListMixin(LitElement) {}
9
+
10
+ /**
11
+ * @typedef {import('../types/InteractiveListMixinTypes.js').InteractiveListItemRole} InteractiveListItemRole
12
+ */
13
+
14
+ /**
15
+ * @typedef {import('../src/LionMenu.js').LionMenu} LionMenu
16
+ * @typedef {import('lit').TemplateResult} TemplateResult
17
+ *
18
+ * * @typedef {Object} TestConfig
19
+ * @property {string} tagString
20
+ */
21
+
22
+ const fixture = /** @type {(arg: TemplateResult) => Promise<LionMenu>} */ (_fixture);
23
+
24
+ const l1Config = {
25
+ isBar: true,
26
+ hasFullWidthFlyout: true,
27
+ openableConfig: {
28
+ // We want disclosure behavior
29
+ placementMode: 'custom',
30
+ // N.B. we don't have an invoker...
31
+ isActivated: false,
32
+ inheritsReferenceWidth: 'none',
33
+ focusContentOnOpen: true,
34
+ },
35
+ };
36
+
37
+ /**
38
+ * @param {TestConfig} [customConfig]
39
+ */
40
+ export function runMoreButtonMenuMixinSuite(customConfig) {
41
+ const cfg = {
42
+ tagString: customConfig?.tagString || defineCE(InteractiveListClass),
43
+ };
44
+
45
+ const { tagString } = cfg;
46
+ const tag = unsafeStatic(tagString);
47
+
48
+ describe('MoreButtonMenuMixin', () => {
49
+ describe('Overflow handling', () => {
50
+ /** @type {import('sinon').SinonFakeTimers | null} */
51
+ let clock = null;
52
+
53
+ /** @param {HTMLElement} el */
54
+ const isMoreButtonShown = el => {
55
+ const wrapper = /** @type {HTMLElement | null} */ (
56
+ el.querySelector('[data-more-button-wrapper]')
57
+ );
58
+ return wrapper?.style.display !== 'none';
59
+ };
60
+
61
+ /** @param {HTMLElement} el */
62
+ const isMoreButtonMenuShown = el => {
63
+ const menu = el.querySelector('[data-more-button-menu]');
64
+ return menu ? getComputedStyle(menu).width !== '0px' : false;
65
+ };
66
+
67
+ /** @param {HTMLElement} el */
68
+ const getMoreButton = el =>
69
+ /** @type {HTMLElement | null} */ (el.querySelector('[data-more-button]'));
70
+ /** @param {HTMLElement} el */
71
+ const getMoreButtonMenu = el => el.querySelector('[data-more-button-menu]');
72
+
73
+ /**
74
+ * @param {HTMLElement} element
75
+ */
76
+ function getCoordinates(element) {
77
+ const { x, y, height } = element.getBoundingClientRect();
78
+
79
+ return {
80
+ x: Math.floor(x + window.pageXOffset),
81
+ y: Math.floor(y + window.pageYOffset + height / 2),
82
+ };
83
+ }
84
+ /** @param {HTMLElement} el */
85
+ const clickOnMoreButton = async el => {
86
+ const moreButton = getMoreButton(el);
87
+ if (!moreButton) {
88
+ return;
89
+ }
90
+ const { x, y } = getCoordinates(moreButton);
91
+ await sendMouse({ type: 'click', position: [x, y] });
92
+ clock?.tick(100);
93
+ };
94
+
95
+ /** @param {HTMLElement} el */
96
+ const getDirectListItemsUnderMoreButtonMenu = el => [
97
+ ...Array.from(
98
+ el.querySelectorAll('[data-more-button-menu] > [role="listitem"]'),
99
+ item => /** @type {HTMLElement} */ (item),
100
+ ),
101
+ ];
102
+
103
+ /** @param {HTMLElement} el */
104
+ const getDirectListItemsUnderMainMenu = el => [
105
+ ...Array.from(
106
+ el.querySelectorAll('[slot="list"] > [role="listitem"]'),
107
+ item => /** @type {HTMLElement} */ (item),
108
+ ),
109
+ ];
110
+
111
+ /** @param {HTMLElement & { updateComplete: Promise<unknown> }} el */
112
+ const waitResizeEventDebounce = async el => {
113
+ await el.updateComplete;
114
+ // wait for resize event debouncer
115
+ clock?.tick(55);
116
+ };
117
+
118
+ beforeEach(() => {
119
+ clock = sinon.useFakeTimers({
120
+ shouldAdvanceTime: true,
121
+ });
122
+ });
123
+ afterEach(async () => {
124
+ clock?.restore();
125
+ await resetMouse();
126
+ });
127
+
128
+ it('should show no More button when all items fit', async () => {
129
+ const el = await fixture(html`
130
+ <${tag} name="foo" ._activeMode="${'tabbable-disclosure'}" .itemWrap="${true}"
131
+ data-has-full-width-flyout orientation="horizontal" style="min-width: 170px; max-width: 170px;">
132
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
133
+ <a href="#">Item 1</a>
134
+ </div>
135
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
136
+ <a href="#">Item 2</a>
137
+ </div>
138
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
139
+ <a href="#">Item 3</a>
140
+ </div>
141
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
142
+ <button>More</button>
143
+ </div>
144
+ </${tag}>
145
+ `);
146
+ await waitResizeEventDebounce(el);
147
+ // 3 items is 150px. They fit into 170px parent
148
+ expect(isMoreButtonShown(el)).to.equal(false);
149
+ });
150
+
151
+ it('should show More button when not all items fit', async () => {
152
+ const el = await fixture(html`
153
+ <${tag}
154
+ .itemWrap="${true}"
155
+ ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
156
+ .config="${l1Config.openableConfig}"
157
+ .bar="${l1Config.isBar}"
158
+ ._activeMode="${'tabbable-disclosure'}"
159
+ style="min-width: 170px; max-width: 170px;"
160
+ >
161
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
162
+ <a href="#">Item 1</a>
163
+ </div>
164
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
165
+ <a href="#">Item 2</a>
166
+ </div>
167
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
168
+ <a href="#">Item 3</a>
169
+ </div>
170
+ <div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
171
+ <a href="#">Item 4</a>
172
+ </div>
173
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
174
+ <button>More</button>
175
+ </div>
176
+ </${tag}>
177
+ <style>
178
+ [orientation='horizontal'] ::slotted([slot='list']) {
179
+ display: flex;
180
+ }
181
+ </style>
182
+ `);
183
+
184
+ await waitResizeEventDebounce(el);
185
+ // 4 items is 200px. They don't fit into 170px parent. So More button should be shown
186
+ expect(isMoreButtonShown(el)).to.equal(true);
187
+ });
188
+
189
+ it('should show 2 items when clicking on `More` button', async () => {
190
+ const el = await fixture(html`
191
+ <${tag}
192
+ .itemWrap="${true}"
193
+ ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
194
+ .config="${l1Config.openableConfig}"
195
+ .bar="${l1Config.isBar}"
196
+ ._activeMode="${'tabbable-disclosure'}"
197
+ style="min-width: 170px; max-width: 170px;"
198
+ >
199
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
200
+ <a href="#">Item 1</a>
201
+ </div>
202
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
203
+ <a href="#">Item 2</a>
204
+ </div>
205
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
206
+ <a href="#">Item 3</a>
207
+ </div>
208
+ <div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
209
+ <a href="#">Item 4</a>
210
+ </div>
211
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
212
+ <button>More</button>
213
+ </div>
214
+ </${tag}>
215
+ `);
216
+
217
+ await waitResizeEventDebounce(el);
218
+ await clickOnMoreButton(el);
219
+
220
+ expect(isMoreButtonMenuShown(el)).to.equal(true);
221
+ const labels = getDirectListItemsUnderMoreButtonMenu(el).map(
222
+ item => item.textContent?.trim() ?? '',
223
+ );
224
+ expect(labels).to.deep.equal(['Item 3', 'Item 4']);
225
+ });
226
+
227
+ it('should focus more button menu container when clicking on `More` button', async () => {
228
+ const el = await fixture(html`
229
+ <${tag}
230
+ .itemWrap="${true}"
231
+ ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
232
+ .config="${l1Config.openableConfig}"
233
+ .bar="${l1Config.isBar}"
234
+ ._activeMode="${'tabbable-disclosure'}"
235
+ style="min-width: 170px; max-width: 170px;"
236
+ >
237
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
238
+ <a href="#">Item 1</a>
239
+ </div>
240
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
241
+ <a href="#">Item 2</a>
242
+ </div>
243
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
244
+ <a href="#">Item 3</a>
245
+ </div>
246
+ <div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
247
+ <a href="#">Item 4</a>
248
+ </div>
249
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
250
+ <button>More</button>
251
+ </div>
252
+ </${tag}>
253
+ `);
254
+
255
+ await waitResizeEventDebounce(el);
256
+ await clickOnMoreButton(el);
257
+
258
+ expect(isMoreButtonMenuShown(el)).to.equal(true);
259
+ expect(getDeepActiveElement() === getMoreButtonMenu(el)).to.equal(true);
260
+ });
261
+
262
+ it('should open and then close More button menu when clicking 2 times on the More button', async () => {
263
+ const el = await fixture(html`
264
+ <${tag}
265
+ .itemWrap="${true}"
266
+ ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
267
+ .config="${l1Config.openableConfig}"
268
+ .bar="${l1Config.isBar}"
269
+ ._activeMode="${'tabbable-disclosure'}"
270
+ style="min-width: 170px; max-width: 170px;"
271
+ >
272
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
273
+ <a href="#">Item 1</a>
274
+ </div>
275
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
276
+ <a href="#">Item 2</a>
277
+ </div>
278
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
279
+ <a href="#">Item 3</a>
280
+ </div>
281
+ <div role="listitem" id="item4" style="min-width: 50px; max-width: 50px;">
282
+ <a href="#">Item 4</a>
283
+ </div>
284
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
285
+ <button>More</button>
286
+ </div>
287
+ </${tag}>
288
+ `);
289
+
290
+ await waitResizeEventDebounce(el);
291
+ expect(isMoreButtonMenuShown(el)).to.equal(false);
292
+ await clickOnMoreButton(el);
293
+ expect(isMoreButtonMenuShown(el)).to.equal(true);
294
+ await clickOnMoreButton(el);
295
+ expect(isMoreButtonMenuShown(el)).to.equal(false);
296
+ });
297
+
298
+ it('should remove `More` button when making font smaller', async () => {
299
+ const el = await fixture(html`
300
+ <${tag} name="foo" ._activeMode="${'tabbable-disclosure'}" .itemWrap="${true}"
301
+ data-has-full-width-flyout orientation="horizontal" style="min-width: 170px; max-width: 170px;">
302
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
303
+ <a href="#">Item 1</a>
304
+ </div>
305
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
306
+ <a href="#">Item 2</a>
307
+ </div>
308
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
309
+ <a href="#">Item 3</a>
310
+ </div>
311
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
312
+ <button>More</button>
313
+ </div>
314
+ </${tag}>
315
+ `);
316
+ await waitResizeEventDebounce(el);
317
+
318
+ /**
319
+ * Make font smaller for first level items to force
320
+ * the menu render all 4 items and no `More` button
321
+ */
322
+ getDirectListItemsUnderMainMenu(el).forEach(item => {
323
+ // eslint-disable-next-line no-param-reassign
324
+ item.style.fontSize = '12px';
325
+ });
326
+ el?.dispatchEvent(new CustomEvent('resize', { composed: true, bubbles: true }));
327
+ await waitResizeEventDebounce(el);
328
+ expect(isMoreButtonShown(el)).to.equal(false);
329
+ });
330
+
331
+ it('should add `More` button when making font larger', async () => {
332
+ const el = await fixture(html`
333
+ <${tag}
334
+ .itemWrap="${true}"
335
+ ?data-has-full-width-flyout="${l1Config.hasFullWidthFlyout}"
336
+ .config="${l1Config.openableConfig}"
337
+ .bar="${l1Config.isBar}"
338
+ ._activeMode="${'tabbable-disclosure'}"
339
+ style="min-width: 170px; max-width: 170px;"
340
+ >
341
+ <div role="listitem" id="item1" style="min-width: 50px; max-width: 50px;">
342
+ <a href="#">Item 1</a>
343
+ </div>
344
+ <div role="listitem" id="item2" style="min-width: 50px; max-width: 50px;">
345
+ <a href="#">Item 2</a>
346
+ </div>
347
+ <div role="listitem" id="item3" style="min-width: 50px; max-width: 50px;">
348
+ <a href="#">Item 3</a>
349
+ </div>
350
+ <div slot="more-button" style="min-width: 50px; max-width: 50px;">
351
+ <button>More</button>
352
+ </div>
353
+ </${tag}>
354
+ `);
355
+ await waitResizeEventDebounce(el);
356
+
357
+ expect(getDirectListItemsUnderMainMenu(el).length).to.equal(3);
358
+ /**
359
+ * Make font larger for first level items to force
360
+ * the More button to appear
361
+ */
362
+ getDirectListItemsUnderMainMenu(el).forEach(item => {
363
+ // eslint-disable-next-line no-param-reassign
364
+ item.style.fontSize = '40px';
365
+ });
366
+ el?.dispatchEvent(new CustomEvent('resize', { composed: true, bubbles: true }));
367
+ await waitResizeEventDebounce(el);
368
+ expect(isMoreButtonShown(el)).to.equal(true);
369
+ });
370
+ });
371
+ });
372
+ }