@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,303 @@
1
+ import { LitElement } from 'lit';
2
+ import { expect, html, fixture as _fixture, unsafeStatic, defineCE } from '@open-wc/testing';
3
+ import sinon from 'sinon';
4
+ import { MultiLevelListMixin } from '../src/MultiLevelListMixin.js';
5
+ import { InteractiveListMixin } from '../src/InteractiveListMixin.js';
6
+ import { mimicKeyPress } from '../test-helpers/mimicKeyPress.js';
7
+
8
+ class MultiLevelListClass extends MultiLevelListMixin(LitElement) {}
9
+ class InteractiveListClass extends InteractiveListMixin(LitElement) {}
10
+
11
+ /**
12
+ * @typedef {import('../src/LionMenu.js').LionMenu} LionMenu
13
+ * @typedef {import('lit').TemplateResult} TemplateResult
14
+ *
15
+ * @typedef {Object} TestConfig
16
+ * @property {string} tagString
17
+ * @property {string} tagChildString
18
+ */
19
+
20
+ const fixture = /** @type {(arg: TemplateResult) => Promise<LionMenu>} */ (_fixture);
21
+
22
+ /**
23
+ * @param {TestConfig} [customConfig]
24
+ */
25
+ export function runMultiLevelListMixinSuite(customConfig) {
26
+ const cfg = {
27
+ tagString: customConfig?.tagString || defineCE(MultiLevelListClass),
28
+ tagChildString: customConfig?.tagChildString || defineCE(InteractiveListClass),
29
+ };
30
+
31
+ const { tagString, tagChildString } = cfg;
32
+ const tag = unsafeStatic(tagString);
33
+ const tagChild = unsafeStatic(tagChildString);
34
+
35
+ describe('MultiLevelListMixin', () => {
36
+ it('has sublists', async () => {
37
+ const el = await fixture(html`
38
+ <${tag}>
39
+ <div role="listitem" id="item-0">
40
+ <button data-invoker>Item 0</button>
41
+ <${tagChild}>
42
+ <div role="listitem" id="item-0-0">
43
+ <a href="#foo">Sub item 0</a>
44
+ </div>
45
+ <div role="listitem" id="item-0-1">
46
+ <a href="#bar">Sub item 1</a>
47
+ </div>
48
+ </${tagChild}>
49
+ </div>
50
+ <div role="listitem" id="item-1">
51
+ <a href="#baz">Item 1</a>
52
+ </div>
53
+ </${tag}>
54
+ `);
55
+ const subList = /** @type {MultiLevelListClass} */ (el.querySelector(tagChildString));
56
+ expect(subList).to.exist;
57
+ expect(subList.level).to.equal(2);
58
+ });
59
+
60
+ it('inherits checked state from children when checked via href match', async () => {
61
+ const el = await fixture(html`
62
+ <${tag}>
63
+ <div role="listitem" id="item-0">
64
+ <button data-invoker>Item 0</button>
65
+ <${tagChild}>
66
+ <div role="listitem" id="item-0-0">
67
+ <a href="${window.location.href}">Sub item 0</a>
68
+ </div>
69
+ <div role="listitem" id="item-0-1">
70
+ <a href="#bar">Sub item 1</a>
71
+ </div>
72
+ </${tagChild}>
73
+ </div>
74
+ <div role="listitem" id="item-1">
75
+ <a href="#baz">Item 1</a>
76
+ </div>
77
+ </${tag}>
78
+ `);
79
+ if (el.role === 'list') {
80
+ const item0 = el.listItems[0];
81
+ expect(item0.getAttribute('aria-current')).to.equal('true');
82
+ }
83
+ });
84
+
85
+ describe('Interactions', () => {
86
+ describe('Keyboard navigation', () => {
87
+ it('navigates between L1 items on [ArrowDown] [ArrowUp] keys', async () => {
88
+ const el = await fixture(html`
89
+ <${tag}>
90
+ <div role="listitem" id="item-0">
91
+ <button data-invoker>Item 0</button>
92
+ <${tagChild}>
93
+ <div role="listitem" id="item-0-0">
94
+ <a href="${window.location.href}">Sub item 0</a>
95
+ </div>
96
+ <div role="listitem" id="item-0-1">
97
+ <a href="#bar">Sub item 1</a>
98
+ </div>
99
+ </${tagChild}>
100
+ </div>
101
+ <div role="listitem" id="item-1">
102
+ <a href="#baz">Item 1</a>
103
+ </div>
104
+ <div role="listitem" id="item-2">
105
+ <a href="#foobar">Item 2</a>
106
+ </div>
107
+ </${tag}>
108
+ `);
109
+
110
+ const { _listNode } = el;
111
+
112
+ // Normalize
113
+ el.activeIndex = 0;
114
+
115
+ mimicKeyPress(_listNode, 'ArrowDown');
116
+ expect(el.activeIndex).to.be.equal(1);
117
+ mimicKeyPress(_listNode, 'ArrowDown');
118
+ expect(el.activeIndex).to.be.equal(2);
119
+ mimicKeyPress(_listNode, 'ArrowUp');
120
+ expect(el.activeIndex).to.be.equal(1);
121
+ });
122
+
123
+ it('stops navigation by default at end of option list', async () => {
124
+ const el = await fixture(html`
125
+ <${tag}>
126
+ <div role="listitem" id="item-0">
127
+ <button data-invoker>Item 0</button>
128
+ <${tagChild}>
129
+ <div role="listitem" id="item-0-0">
130
+ <a href="${window.location.href}">Sub item 0</a>
131
+ </div>
132
+ <div role="listitem" id="item-0-1">
133
+ <a href="#bar">Sub item 1</a>
134
+ </div>
135
+ </${tagChild}>
136
+ </div>
137
+ <div role="listitem" id="item-1">
138
+ <a href="#baz">Item 1</a>
139
+ </div>
140
+ <div role="listitem" id="item-2">
141
+ <a href="#foobar">Item 2</a>
142
+ </div>
143
+ </${tag}>
144
+ `);
145
+
146
+ const { _listNode } = el;
147
+
148
+ // Normalize
149
+ el.activeIndex = 0;
150
+
151
+ mimicKeyPress(_listNode, 'ArrowUp');
152
+ expect(el.activeIndex).to.be.equal(0);
153
+
154
+ el.activeIndex = 2;
155
+ mimicKeyPress(_listNode, 'ArrowDown');
156
+ expect(el.activeIndex).to.be.equal(2);
157
+ });
158
+
159
+ it('opens subList on [Enter] key press', async () => {
160
+ const el = await fixture(html`
161
+ <${tag}>
162
+ <div role="listitem" id="item-0">
163
+ <button data-invoker>Item 0</button>
164
+ <${tagChild}>
165
+ <div role="listitem" id="item-0-0">
166
+ <div>Sub item 0</div>
167
+ </div>
168
+ <div role="listitem" id="item-0-1">
169
+ <div>Sub item 1</div>
170
+ </div>
171
+ </${tagChild}>
172
+ </div>
173
+ <div role="listitem" id="item-1">
174
+ <div>Item 1</div>
175
+ </div>
176
+ </${tag}>
177
+ `);
178
+
179
+ const { _listNode } = el;
180
+
181
+ // Normalize
182
+ el.activeIndex = 0;
183
+ const clickSpy = sinon.spy(el.activeItem, 'click');
184
+
185
+ mimicKeyPress(_listNode, 'Enter');
186
+ expect(clickSpy).to.have.been.calledOnce;
187
+ });
188
+
189
+ it('navigates between L2 items on [ArrowDown] [ArrowUp] keys', async () => {
190
+ const el = await fixture(html`
191
+ <${tag} orientation="horizontal">
192
+ <div role="listitem" id="item-0">
193
+ <button data-invoker>Item 0</button>
194
+ <${tagChild}>
195
+ <div role="listitem" id="item-0-0">
196
+ <a href="#foo">Sub item 0</a>
197
+ </div>
198
+ <div role="listitem" id="item-0-1">
199
+ <a href="#bar">Sub item 1</a>
200
+ </div>
201
+ <div role="listitem" id="item-0-2">
202
+ <a href="#baz">Sub item 2</a>
203
+ </div>
204
+ </${tagChild}>
205
+ </div>
206
+ <div role="listitem" id="item-1">
207
+ <a href="#foobar">Item 1</a>
208
+ </div>
209
+ </${tag}>
210
+ `);
211
+
212
+ const subList = /** @type {MultiLevelListClass} */ (el.querySelector(tagChildString));
213
+ const { _listNode } = subList;
214
+
215
+ // Normalize
216
+ subList.activeIndex = 0;
217
+
218
+ mimicKeyPress(_listNode, 'ArrowDown');
219
+ expect(subList.activeIndex).to.be.equal(1);
220
+ mimicKeyPress(_listNode, 'ArrowDown');
221
+ expect(subList.activeIndex).to.be.equal(2);
222
+ mimicKeyPress(_listNode, 'ArrowUp');
223
+ expect(subList.activeIndex).to.be.equal(1);
224
+ });
225
+
226
+ it('close method gets called on [ArrowUp] key when in L2 and horizontal', async () => {
227
+ const el = await fixture(html`
228
+ <${tag}>
229
+ <div role="listitem" id="item-0">
230
+ <button data-invoker>Item 0</button>
231
+ <${tagChild}>
232
+ <div role="listitem" id="item-0-0">
233
+ <div>Sub item 0</div>
234
+ </div>
235
+ <div role="listitem" id="item-0-1">
236
+ <div>Sub item 1</div>
237
+ </div>
238
+ <div role="listitem" id="item-0-2">
239
+ <div>Sub item 2</div>
240
+ </div>
241
+ </${tagChild}>
242
+ </div>
243
+ <div role="listitem" id="item-1">
244
+ <div>Item 1</div>
245
+ </div>
246
+ </${tag}>
247
+ `);
248
+ const subList = /** @type {MultiLevelListClass} */ (el.querySelector(tagChildString));
249
+ // @ts-ignore
250
+ if (subList?.close) {
251
+ // @ts-ignore
252
+ const closeSpy = sinon.spy(subList, 'close');
253
+ const { _listNode } = subList;
254
+
255
+ // Normalize
256
+ subList.activeIndex = 0;
257
+ subList.orientation = 'horizontal';
258
+
259
+ mimicKeyPress(_listNode, 'ArrowUp');
260
+ expect(closeSpy).to.have.been.calledOnce;
261
+ }
262
+ });
263
+
264
+ it('close method gets called on [ArrowLeft] key when in L2 and vertical', async () => {
265
+ const el = await fixture(html`
266
+ <${tag}>
267
+ <div role="listitem" id="item-0">
268
+ <button data-invoker>Item 0</button>
269
+ <${tagChild} orientation="vertical">
270
+ <div role="listitem" id="item-0-0">
271
+ <a href="#foo">Sub item 0</a>
272
+ </div>
273
+ <div role="listitem" id="item-0-1">
274
+ <a href="#bar">Sub item 1</a>
275
+ </div>
276
+ <div role="listitem" id="item-0-2">
277
+ <a href="#baz">Sub item 2</a>
278
+ </div>
279
+ </${tagChild}>
280
+ </div>
281
+ <div role="listitem" id="item-1">
282
+ <a href="#foobar">Item 1</a>
283
+ </div>
284
+ </${tag}>
285
+ `);
286
+ const subList = /** @type {MultiLevelListClass} */ (el.querySelector(tagChildString));
287
+ // @ts-ignore
288
+ if (subList?.close) {
289
+ // @ts-ignore
290
+ const closeSpy = sinon.spy(subList, 'close');
291
+ const { _listNode } = subList;
292
+
293
+ // Normalize
294
+ subList.activeIndex = 0;
295
+
296
+ mimicKeyPress(_listNode, 'ArrowLeft');
297
+ expect(closeSpy).to.have.been.calledOnce;
298
+ }
299
+ });
300
+ });
301
+ });
302
+ });
303
+ }
@@ -46,6 +46,8 @@ export declare class InteractiveListHost {
46
46
 
47
47
  protected _scrollTargetNode: HTMLElement;
48
48
 
49
+ protected _invokerNode: HTMLElement;
50
+
49
51
  protected _listNode: HTMLElement;
50
52
 
51
53
  // private __setupListboxNode(): void;
@@ -17,6 +17,8 @@ export interface DefineOverlayConfig {
17
17
  }
18
18
 
19
19
  export declare class OverlayHost {
20
+ static _getFocusableInvokerEl(focusableElOrWrapper: Element): Element | null;
21
+
20
22
  opened: Boolean;
21
23
  get config(): Partial<OverlayConfig>;
22
24
  set config(value: Partial<OverlayConfig>);