@spectrum-web-components/menu 1.3.0-beta.2 → 1.3.0-testing.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 (46) hide show
  1. package/package.json +11 -12
  2. package/LICENSE +0 -201
  3. package/stories/index.js +0 -90
  4. package/stories/index.js.map +0 -7
  5. package/stories/menu-divider.stories.js +0 -32
  6. package/stories/menu-divider.stories.js.map +0 -7
  7. package/stories/menu-group.stories.js +0 -144
  8. package/stories/menu-group.stories.js.map +0 -7
  9. package/stories/menu-item.disconnected.stories.js +0 -178
  10. package/stories/menu-item.disconnected.stories.js.map +0 -7
  11. package/stories/menu-item.stories.js +0 -73
  12. package/stories/menu-item.stories.js.map +0 -7
  13. package/stories/menu-sizes.stories.js +0 -11
  14. package/stories/menu-sizes.stories.js.map +0 -7
  15. package/stories/menu.stories.js +0 -427
  16. package/stories/menu.stories.js.map +0 -7
  17. package/stories/submenu.stories.js +0 -415
  18. package/stories/submenu.stories.js.map +0 -7
  19. package/test/benchmark/test-basic.js +0 -24
  20. package/test/benchmark/test-basic.js.map +0 -7
  21. package/test/menu-divider.test-vrt.js +0 -5
  22. package/test/menu-divider.test-vrt.js.map +0 -7
  23. package/test/menu-group.test-vrt.js +0 -5
  24. package/test/menu-group.test-vrt.js.map +0 -7
  25. package/test/menu-group.test.js +0 -401
  26. package/test/menu-group.test.js.map +0 -7
  27. package/test/menu-item.disconnected.test-vrt.js +0 -5
  28. package/test/menu-item.disconnected.test-vrt.js.map +0 -7
  29. package/test/menu-item.test-vrt.js +0 -5
  30. package/test/menu-item.test-vrt.js.map +0 -7
  31. package/test/menu-item.test.js +0 -227
  32. package/test/menu-item.test.js.map +0 -7
  33. package/test/menu-memory.test.js +0 -5
  34. package/test/menu-memory.test.js.map +0 -7
  35. package/test/menu-selects.test.js +0 -523
  36. package/test/menu-selects.test.js.map +0 -7
  37. package/test/menu-sizes.test-vrt.js +0 -5
  38. package/test/menu-sizes.test-vrt.js.map +0 -7
  39. package/test/menu.test-vrt.js +0 -5
  40. package/test/menu.test-vrt.js.map +0 -7
  41. package/test/menu.test.js +0 -563
  42. package/test/menu.test.js.map +0 -7
  43. package/test/submenu.test-vrt.js +0 -5
  44. package/test/submenu.test-vrt.js.map +0 -7
  45. package/test/submenu.test.js +0 -1028
  46. package/test/submenu.test.js.map +0 -7
@@ -1,1028 +0,0 @@
1
- "use strict";
2
- import "@spectrum-web-components/menu/sp-menu.js";
3
- import "@spectrum-web-components/menu/sp-menu-item.js";
4
- import {
5
- aTimeout,
6
- elementUpdated,
7
- expect,
8
- html,
9
- nextFrame,
10
- oneEvent
11
- } from "@open-wc/testing";
12
- import { fixture } from "../../../test/testing-helpers.js";
13
- import { sendMouse } from "../../../test/plugins/browser.js";
14
- import { spy } from "sinon";
15
- import { sendKeys } from "@web/test-runner-commands";
16
- import "@spectrum-web-components/action-menu/sp-action-menu.js";
17
- import "@spectrum-web-components/menu/sp-menu-group.js";
18
- import "@spectrum-web-components/overlay/sp-overlay.js";
19
- import "@spectrum-web-components/icons-workflow/icons/sp-icon-show-menu.js";
20
- import { slottableRequest } from "@spectrum-web-components/overlay/src/slottable-request-directive.js";
21
- import { isWebKit } from "@spectrum-web-components/shared";
22
- const selectsWithKeyboardData = [
23
- {
24
- dir: "ltr",
25
- openKey: "ArrowRight",
26
- closeKey: "ArrowLeft"
27
- },
28
- {
29
- dir: "rtl",
30
- openKey: "ArrowLeft",
31
- closeKey: "ArrowRight"
32
- }
33
- ];
34
- describe("Submenu", () => {
35
- function selectWithPointer() {
36
- it("with pointer", async function() {
37
- const rootItemBoundingRect = this.rootItem.getBoundingClientRect();
38
- expect(this.rootItem.open).to.be.false;
39
- const opened = oneEvent(this.rootItem, "sp-opened");
40
- await sendMouse({
41
- steps: [
42
- {
43
- type: "move",
44
- position: [
45
- rootItemBoundingRect.left + rootItemBoundingRect.width / 2,
46
- rootItemBoundingRect.top + rootItemBoundingRect.height / 2
47
- ]
48
- }
49
- ]
50
- });
51
- await opened;
52
- expect(this.rootItem.open).to.be.true;
53
- const item2 = document.querySelector(".submenu-item-2");
54
- const item2BoundingRect = item2.getBoundingClientRect();
55
- const closed = oneEvent(this.rootItem, "sp-closed");
56
- await sendMouse({
57
- steps: [
58
- {
59
- type: "click",
60
- position: [
61
- item2BoundingRect.left + item2BoundingRect.width / 2,
62
- item2BoundingRect.top + item2BoundingRect.height / 2
63
- ]
64
- }
65
- ]
66
- });
67
- await closed;
68
- expect(
69
- this.submenuChanged.withArgs("Two").calledOnce,
70
- `submenu changed ${this.submenuChanged.callCount} times`
71
- ).to.be.true;
72
- expect(
73
- this.rootChanged.withArgs("Has submenu").calledOnce,
74
- "root changed"
75
- ).to.be.true;
76
- });
77
- }
78
- function selectsWithKeyboard(testData) {
79
- it(`with keyboard: ${testData.dir}`, async function() {
80
- this.el.parentElement.dir = testData.dir;
81
- await elementUpdated(this.el);
82
- expect(
83
- this.rootItem.open,
84
- `rootItem open before ${testData.openKey}`
85
- ).to.be.false;
86
- const input = document.createElement("input");
87
- this.el.insertAdjacentElement("beforebegin", input);
88
- this.el.focus();
89
- if (!isWebKit) {
90
- await sendKeys({
91
- press: "Shift+Tab"
92
- });
93
- expect(document.activeElement).to.equal(input);
94
- await sendKeys({
95
- press: "Tab"
96
- });
97
- expect(document.activeElement).to.equal(this.el);
98
- }
99
- await sendKeys({
100
- press: "ArrowDown"
101
- });
102
- await elementUpdated(this.rootItem);
103
- expect(
104
- this.rootItem.focused,
105
- `rootItem focused before ${testData.openKey}`
106
- ).to.be.true;
107
- let opened = oneEvent(this.rootItem, "sp-opened");
108
- await sendKeys({
109
- press: testData.openKey
110
- });
111
- await opened;
112
- const rootItem = this.el.querySelector(".root");
113
- let submenu = this.el.querySelector('[slot="submenu"]');
114
- let submenuItem = this.el.querySelector(
115
- ".submenu-item-1"
116
- );
117
- expect(
118
- this.rootItem.open,
119
- `rootItem open after ${testData.openKey}`
120
- ).to.be.true;
121
- expect(document.activeElement).to.equal(submenuItem);
122
- let closed = oneEvent(this.rootItem, "sp-closed");
123
- await sendKeys({
124
- press: testData.closeKey
125
- });
126
- await closed;
127
- expect(
128
- this.rootItem.open,
129
- `rootItem open after ${testData.closeKey}`
130
- ).to.be.false;
131
- expect(document.activeElement).to.equal(rootItem);
132
- opened = oneEvent(this.rootItem, "sp-opened");
133
- await sendKeys({
134
- press: testData.openKey
135
- });
136
- await opened;
137
- submenu = this.el.querySelector('[slot="submenu"]');
138
- expect(this.rootItem.open).to.be.true;
139
- expect(submenuItem.focused).to.be.true;
140
- expect(document.activeElement).to.equal(submenuItem);
141
- await sendKeys({
142
- press: "ArrowDown"
143
- });
144
- await elementUpdated(submenu);
145
- await elementUpdated(submenuItem);
146
- submenuItem = this.el.querySelector(".submenu-item-2");
147
- expect(submenuItem.focused, `submenu focused`).to.be.true;
148
- expect(document.activeElement === submenuItem, `submenu active`).to.be.true;
149
- closed = oneEvent(this.rootItem, "sp-closed");
150
- await sendKeys({
151
- press: "Enter"
152
- });
153
- await closed;
154
- expect(this.submenuChanged.calledWith("Two"), "submenu changed").to.be.true;
155
- expect(this.rootChanged.called, "root has changed").to.be.true;
156
- expect(
157
- this.rootChanged.calledWith("Has submenu"),
158
- "root specifically changed"
159
- ).to.be.true;
160
- });
161
- }
162
- function returnsFocusToRootWhenClosingSubmenu() {
163
- it("returns visible focus when submenu closed", async function() {
164
- var _a;
165
- const input = document.createElement("input");
166
- this.el.insertAdjacentElement("beforebegin", input);
167
- if (!isWebKit) {
168
- await sendKeys({
169
- press: "Shift+Tab"
170
- });
171
- expect(document.activeElement).to.equal(input);
172
- await sendKeys({
173
- press: "Tab"
174
- });
175
- expect(document.activeElement).to.equal(this.el);
176
- }
177
- this.el.focus();
178
- await sendKeys({
179
- press: "ArrowDown"
180
- });
181
- await elementUpdated(this.el);
182
- await nextFrame();
183
- await nextFrame();
184
- expect(this.rootItem.active, "not active").to.be.false;
185
- expect(
186
- this.rootItem.focused,
187
- `focused: ${(_a = document.activeElement) == null ? void 0 : _a.localName}`
188
- ).to.be.true;
189
- expect(this.rootItem.open, "not open").to.be.false;
190
- expect(document.activeElement).to.equal(this.rootItem);
191
- const opened = oneEvent(this.rootItem, "sp-opened");
192
- await sendKeys({
193
- press: "ArrowRight"
194
- });
195
- await opened;
196
- expect(this.rootItem.active).to.be.true;
197
- expect(this.rootItem.focused).to.be.false;
198
- expect(this.rootItem.open).to.be.true;
199
- await sendKeys({
200
- press: "ArrowDown"
201
- });
202
- expect(this.rootItem.active).to.be.true;
203
- expect(this.rootItem.focused).to.be.false;
204
- expect(this.rootItem.open).to.be.true;
205
- const closed = oneEvent(this.rootItem, "sp-closed");
206
- await sendKeys({
207
- press: "ArrowLeft"
208
- });
209
- await closed;
210
- expect(this.rootItem.active).to.be.false;
211
- expect(this.rootItem.focused).to.be.true;
212
- expect(this.rootItem.open).to.be.false;
213
- });
214
- }
215
- function closesOnPointerLeave() {
216
- it("closes on `pointerleave`", async function() {
217
- const rootItemBoundingRect = this.rootItem.getBoundingClientRect();
218
- expect(this.rootItem.open).to.be.false;
219
- const opened = oneEvent(this.rootItem, "sp-opened");
220
- await sendMouse({
221
- steps: [
222
- {
223
- type: "move",
224
- position: [
225
- rootItemBoundingRect.left + rootItemBoundingRect.width / 2,
226
- rootItemBoundingRect.top + rootItemBoundingRect.height / 2
227
- ]
228
- }
229
- ]
230
- });
231
- await opened;
232
- expect(this.rootItem.open).to.be.true;
233
- const closed = oneEvent(this.rootItem, "sp-closed");
234
- await sendMouse({
235
- steps: [
236
- {
237
- type: "move",
238
- position: [
239
- rootItemBoundingRect.left + rootItemBoundingRect.width / 2,
240
- rootItemBoundingRect.top + rootItemBoundingRect.height * 2
241
- ]
242
- }
243
- ]
244
- });
245
- await closed;
246
- expect(this.rootItem.open).to.be.false;
247
- });
248
- }
249
- function persistsThroughMouseLeaveAndReturn() {
250
- it("stays open when mousing off menu item and back again", async function() {
251
- const rootItemBoundingRect = this.rootItem.getBoundingClientRect();
252
- expect(this.rootItem.open).to.be.false;
253
- const opened = oneEvent(this.rootItem, "sp-opened");
254
- await sendMouse({
255
- steps: [
256
- {
257
- type: "move",
258
- position: [
259
- rootItemBoundingRect.left + rootItemBoundingRect.width / 2,
260
- rootItemBoundingRect.top + rootItemBoundingRect.height / 2
261
- ]
262
- }
263
- ]
264
- });
265
- await sendMouse({
266
- steps: [
267
- {
268
- type: "move",
269
- position: [
270
- rootItemBoundingRect.left + rootItemBoundingRect.width / 2,
271
- rootItemBoundingRect.top + rootItemBoundingRect.height * 2
272
- ]
273
- }
274
- ]
275
- });
276
- await sendMouse({
277
- steps: [
278
- {
279
- type: "move",
280
- position: [
281
- rootItemBoundingRect.left + rootItemBoundingRect.width / 2,
282
- rootItemBoundingRect.top + rootItemBoundingRect.height / 2
283
- ]
284
- }
285
- ]
286
- });
287
- await opened;
288
- expect(this.rootItem.open).to.be.true;
289
- const closed = oneEvent(this.rootItem, "sp-closed");
290
- await sendMouse({
291
- steps: [
292
- {
293
- type: "move",
294
- position: [
295
- rootItemBoundingRect.left + rootItemBoundingRect.width / 2,
296
- rootItemBoundingRect.top + rootItemBoundingRect.height * 2
297
- ]
298
- }
299
- ]
300
- });
301
- await closed;
302
- });
303
- }
304
- function doesNotOpenWhenDisabled() {
305
- it("does not open when disabled", async function() {
306
- this.rootItem.disabled = true;
307
- await elementUpdated(this.rootItem);
308
- const rootItemBoundingRect = this.rootItem.getBoundingClientRect();
309
- expect(this.rootItem.open).to.be.false;
310
- await sendMouse({
311
- steps: [
312
- {
313
- type: "move",
314
- position: [
315
- rootItemBoundingRect.left + rootItemBoundingRect.width / 2,
316
- rootItemBoundingRect.top + rootItemBoundingRect.height / 2
317
- ]
318
- }
319
- ]
320
- });
321
- await new Promise((r) => setTimeout(r, 200));
322
- expect(this.rootItem.open).to.be.false;
323
- });
324
- }
325
- function persistsWhenMovingBetweenItemAndSubmenu() {
326
- it("stays open when mousing between menu item and submenu", async function() {
327
- const rootItemBoundingRect = this.rootItem.getBoundingClientRect();
328
- expect(this.rootItem.open).to.be.false;
329
- const opened = oneEvent(this.rootItem, "sp-opened");
330
- await sendMouse({
331
- steps: [
332
- {
333
- type: "move",
334
- position: [
335
- rootItemBoundingRect.left + rootItemBoundingRect.width / 2,
336
- rootItemBoundingRect.top + rootItemBoundingRect.height / 2
337
- ]
338
- }
339
- ]
340
- });
341
- await opened;
342
- await nextFrame();
343
- await nextFrame();
344
- const subItem = this.el.querySelector(
345
- ".submenu-item-2"
346
- );
347
- const clickSpy = spy();
348
- subItem.addEventListener("click", () => clickSpy());
349
- const subItemBoundingRect = subItem.getBoundingClientRect();
350
- expect(this.rootItem.open).to.be.true;
351
- await sendMouse({
352
- steps: [
353
- {
354
- type: "move",
355
- position: [
356
- subItemBoundingRect.left + subItemBoundingRect.width / 2,
357
- subItemBoundingRect.top + subItemBoundingRect.height / 2
358
- ]
359
- }
360
- ]
361
- });
362
- expect(this.rootItem.open).to.be.true;
363
- await aTimeout(150);
364
- expect(this.rootItem.open).to.be.true;
365
- const closed = oneEvent(this.rootItem, "sp-closed");
366
- await sendMouse({
367
- steps: [
368
- {
369
- type: "click",
370
- position: [
371
- subItemBoundingRect.left + subItemBoundingRect.width / 2,
372
- subItemBoundingRect.top + subItemBoundingRect.height / 2
373
- ]
374
- }
375
- ]
376
- });
377
- await closed;
378
- expect(clickSpy.callCount).to.equal(1);
379
- });
380
- }
381
- function continuesToOpenWhenMovingBetweenItemAndSubmenu() {
382
- it("continues to open when mousing between menu item and submenu", async function() {
383
- const rootItemBoundingRect = this.rootItem.getBoundingClientRect();
384
- expect(this.rootItem.open).to.be.false;
385
- const opened = oneEvent(this.rootItem, "sp-opened");
386
- await sendMouse({
387
- steps: [
388
- {
389
- type: "move",
390
- position: [
391
- rootItemBoundingRect.left + rootItemBoundingRect.width / 2,
392
- rootItemBoundingRect.top + rootItemBoundingRect.height / 2
393
- ]
394
- }
395
- ]
396
- });
397
- await nextFrame();
398
- await nextFrame();
399
- await nextFrame();
400
- await nextFrame();
401
- await nextFrame();
402
- await nextFrame();
403
- await nextFrame();
404
- await nextFrame();
405
- const subItem = this.el.querySelector(
406
- ".submenu-item-2"
407
- );
408
- const clickSpy = spy();
409
- subItem.addEventListener("click", () => clickSpy());
410
- const subItemBoundingRect = subItem.getBoundingClientRect();
411
- await sendMouse({
412
- steps: [
413
- {
414
- type: "move",
415
- position: [
416
- subItemBoundingRect.left + subItemBoundingRect.width / 2,
417
- subItemBoundingRect.top + subItemBoundingRect.height / 2
418
- ]
419
- }
420
- ]
421
- });
422
- await opened;
423
- expect(this.rootItem.open).to.be.true;
424
- await aTimeout(150);
425
- expect(this.rootItem.open).to.be.true;
426
- const closed = oneEvent(this.rootItem, "sp-closed");
427
- await sendMouse({
428
- steps: [
429
- {
430
- type: "click",
431
- position: [
432
- subItemBoundingRect.left + subItemBoundingRect.width / 2,
433
- subItemBoundingRect.top + subItemBoundingRect.height / 2
434
- ]
435
- }
436
- ]
437
- });
438
- await closed;
439
- expect(clickSpy.callCount).to.equal(1);
440
- });
441
- }
442
- const renderSubmenu = () => html`
443
- <sp-menu-item class="submenu-item-1">One</sp-menu-item>
444
- <sp-menu-item class="submenu-item-2">Two</sp-menu-item>
445
- <sp-menu-item class="submenu-item-3">Three</sp-menu-item>
446
- `;
447
- describe("static DOM", () => {
448
- beforeEach(async function() {
449
- this.rootChanged = spy();
450
- this.submenuChanged = spy();
451
- this.el = await fixture(html`
452
- <sp-menu
453
- @change=${(event) => {
454
- this.rootChanged(event.target.value);
455
- }}
456
- >
457
- <sp-menu-item>No submenu</sp-menu-item>
458
- <sp-menu-item class="root">
459
- Has submenu
460
- <sp-menu
461
- slot="submenu"
462
- @change=${(event) => {
463
- this.submenuChanged(event.target.value);
464
- }}
465
- >
466
- ${renderSubmenu()}
467
- </sp-menu>
468
- </sp-menu-item>
469
- </sp-menu>
470
- `);
471
- await elementUpdated(this.el);
472
- this.rootItem = this.el.querySelector(".root");
473
- await elementUpdated(this.rootItem);
474
- });
475
- describe("selects", () => {
476
- selectWithPointer();
477
- selectsWithKeyboardData.map((testData) => {
478
- selectsWithKeyboard(testData);
479
- });
480
- });
481
- closesOnPointerLeave();
482
- returnsFocusToRootWhenClosingSubmenu();
483
- persistsThroughMouseLeaveAndReturn();
484
- doesNotOpenWhenDisabled();
485
- persistsWhenMovingBetweenItemAndSubmenu();
486
- continuesToOpenWhenMovingBetweenItemAndSubmenu();
487
- });
488
- describe("directive", () => {
489
- beforeEach(async function() {
490
- this.rootChanged = spy();
491
- this.submenuChanged = spy();
492
- this.el = await fixture(html`
493
- <sp-menu
494
- @change=${(event) => {
495
- this.rootChanged(event.target.value);
496
- }}
497
- >
498
- <sp-menu-item>No submenu</sp-menu-item>
499
- <sp-menu-item class="root">
500
- Has submenu
501
- <sp-menu
502
- slot="submenu"
503
- @change=${(event) => {
504
- this.submenuChanged(event.target.value);
505
- }}
506
- ${slottableRequest(renderSubmenu)}
507
- ></sp-menu>
508
- </sp-menu-item>
509
- </sp-menu>
510
- `);
511
- await elementUpdated(this.el);
512
- this.rootItem = this.el.querySelector(".root");
513
- await elementUpdated(this.rootItem);
514
- });
515
- describe("selects", () => {
516
- selectWithPointer();
517
- selectsWithKeyboardData.map((testData) => {
518
- selectsWithKeyboard(testData);
519
- });
520
- });
521
- closesOnPointerLeave();
522
- returnsFocusToRootWhenClosingSubmenu();
523
- persistsThroughMouseLeaveAndReturn();
524
- doesNotOpenWhenDisabled();
525
- persistsWhenMovingBetweenItemAndSubmenu();
526
- continuesToOpenWhenMovingBetweenItemAndSubmenu();
527
- });
528
- it("closes deep tree on selection", async function() {
529
- const rootChanged = spy();
530
- const submenuChanged = spy();
531
- const subSubmenuChanged = spy();
532
- const el = await fixture(html`
533
- <sp-menu
534
- @change=${(event) => {
535
- rootChanged(event.target.value);
536
- }}
537
- >
538
- <sp-menu-item class="root">
539
- Has submenu
540
- <sp-menu
541
- slot="submenu"
542
- @change=${(event) => {
543
- submenuChanged(event.target.value);
544
- }}
545
- >
546
- <sp-menu-item class="submenu-item-1">One</sp-menu-item>
547
- <sp-menu-item class="submenu-item-2">
548
- Two
549
- <sp-menu
550
- slot="submenu"
551
- @change=${(event) => {
552
- subSubmenuChanged(event.target.value);
553
- }}
554
- >
555
- <sp-menu-item class="sub-submenu-item-1">
556
- A
557
- </sp-menu-item>
558
- <sp-menu-item class="sub-submenu-item-2">
559
- B
560
- </sp-menu-item>
561
- <sp-menu-item class="sub-submenu-item-3">
562
- C
563
- </sp-menu-item>
564
- </sp-menu>
565
- </sp-menu-item>
566
- <sp-menu-item class="submenu-item-3">
567
- Three
568
- </sp-menu-item>
569
- </sp-menu>
570
- </sp-menu-item>
571
- </sp-menu>
572
- `);
573
- const rootItem = el.querySelector(".root");
574
- const rootItemBoundingRect = rootItem.getBoundingClientRect();
575
- const item2 = document.querySelector(".submenu-item-2");
576
- const itemC = document.querySelector(".sub-submenu-item-3");
577
- expect(rootItem.open).to.be.false;
578
- let opened = oneEvent(rootItem, "sp-opened");
579
- await sendMouse({
580
- steps: [
581
- {
582
- type: "move",
583
- position: [
584
- rootItemBoundingRect.left + rootItemBoundingRect.width / 2,
585
- rootItemBoundingRect.top + rootItemBoundingRect.height / 2
586
- ]
587
- }
588
- ]
589
- });
590
- await opened;
591
- expect(rootItem.open).to.be.true;
592
- const item2BoundingRect = item2.getBoundingClientRect();
593
- opened = oneEvent(item2, "sp-opened");
594
- await sendMouse({
595
- steps: [
596
- {
597
- type: "move",
598
- position: [
599
- item2BoundingRect.left + item2BoundingRect.width / 2,
600
- item2BoundingRect.top + item2BoundingRect.height / 2
601
- ]
602
- }
603
- ]
604
- });
605
- await opened;
606
- expect(item2.open).to.be.true;
607
- const closed = oneEvent(rootItem, "sp-closed");
608
- const itemCBoundingRect = itemC.getBoundingClientRect();
609
- await sendMouse({
610
- steps: [
611
- {
612
- type: "click",
613
- position: [
614
- itemCBoundingRect.left + itemCBoundingRect.width / 2,
615
- itemCBoundingRect.top + itemCBoundingRect.height / 2
616
- ]
617
- }
618
- ]
619
- });
620
- await closed;
621
- expect(rootChanged.calledWith("Has submenu"), "root changed").to.be.true;
622
- expect(submenuChanged.calledWith("Two"), "submenu changed").to.be.true;
623
- expect(subSubmenuChanged.calledWith("C"), "sub submenu changed").to.be.true;
624
- });
625
- it("closes all descendant submenus when closing a ancestor menu", async () => {
626
- const el = await fixture(html`
627
- <sp-action-menu label="Closing ancestors will close submenus">
628
- <sp-icon-show-menu slot="icon"></sp-icon-show-menu>
629
- <sp-menu-group role="none" id="group">
630
- <span slot="header">New York</span>
631
- <sp-menu-item>Bronx</sp-menu-item>
632
- <sp-menu-item id="submenu-item-1">
633
- Brooklyn
634
- <sp-menu slot="submenu" id="submenu-1">
635
- <sp-menu-item id="submenu-item-2">
636
- Ft. Greene
637
- <sp-menu slot="submenu" id="submenu-2">
638
- <sp-menu-item>S. Oxford St</sp-menu-item>
639
- <sp-menu-item>S. Portland Ave</sp-menu-item>
640
- <sp-menu-item>S. Elliot Pl</sp-menu-item>
641
- </sp-menu>
642
- </sp-menu-item>
643
- <sp-menu-item disabled>Park Slope</sp-menu-item>
644
- <sp-menu-item>Williamsburg</sp-menu-item>
645
- </sp-menu>
646
- </sp-menu-item>
647
- <sp-menu-item id="submenu-item-3">
648
- Manhattan
649
- <sp-menu slot="submenu" id="submenu-3">
650
- <sp-menu-item disabled>SoHo</sp-menu-item>
651
- <sp-menu-item>
652
- Union Square
653
- <sp-menu slot="submenu">
654
- <sp-menu-item>14th St</sp-menu-item>
655
- <sp-menu-item>Broadway</sp-menu-item>
656
- <sp-menu-item>Park Ave</sp-menu-item>
657
- </sp-menu>
658
- </sp-menu-item>
659
- <sp-menu-item>Upper East Side</sp-menu-item>
660
- </sp-menu>
661
- </sp-menu-item>
662
- </sp-menu-group>
663
- </sp-action-menu>
664
- `);
665
- const rootMenu1 = el.querySelector("#submenu-item-1");
666
- const rootMenu2 = el.querySelector("#submenu-item-3");
667
- const childMenu2 = el.querySelector("#submenu-item-2");
668
- expect(el.open).to.be.false;
669
- let opened = oneEvent(el, "sp-opened");
670
- el.click();
671
- await opened;
672
- expect(el.open).to.be.true;
673
- opened = oneEvent(rootMenu1, "sp-opened");
674
- rootMenu1.dispatchEvent(
675
- new PointerEvent("pointerenter", { bubbles: true })
676
- );
677
- await opened;
678
- expect(rootMenu1.open).to.be.true;
679
- opened = oneEvent(childMenu2, "sp-opened");
680
- childMenu2.dispatchEvent(
681
- new PointerEvent("pointerenter", { bubbles: true })
682
- );
683
- await opened;
684
- expect(childMenu2.open).to.be.true;
685
- const childMenu2Closed = oneEvent(childMenu2, "sp-closed");
686
- const rootMenu1Closed = oneEvent(rootMenu1, "sp-closed");
687
- const rootMenu2Opened = oneEvent(rootMenu2, "sp-opened");
688
- rootMenu2.dispatchEvent(
689
- new PointerEvent("pointerenter", { bubbles: true })
690
- );
691
- await childMenu2Closed;
692
- await rootMenu1Closed;
693
- await rootMenu2Opened;
694
- });
695
- describe("deep tree", () => {
696
- beforeEach(async function() {
697
- this.el = await fixture(html`
698
- <sp-action-menu label="Deep submenu tree">
699
- <sp-icon-show-menu slot="icon"></sp-icon-show-menu>
700
- <sp-menu-group role="none">
701
- <span slot="header">New York</span>
702
- <sp-menu-item id="no-submenu">Bronx</sp-menu-item>
703
- <sp-menu-item id="submenu-item-1">
704
- Brooklyn
705
- <sp-menu slot="submenu">
706
- <sp-menu-item id="submenu-item-2">
707
- Ft. Greene
708
- <sp-menu slot="submenu">
709
- <sp-menu-item>
710
- S. Oxford St
711
- </sp-menu-item>
712
- <sp-menu-item>
713
- S. Portland Ave
714
- </sp-menu-item>
715
- <sp-menu-item>
716
- S. Elliot Pl
717
- </sp-menu-item>
718
- </sp-menu>
719
- </sp-menu-item>
720
- <sp-menu-item disabled>Park Slope</sp-menu-item>
721
- <sp-menu-item id="ancestor-item">
722
- Williamsburg
723
- </sp-menu-item>
724
- </sp-menu>
725
- </sp-menu-item>
726
- <sp-menu-item id="submenu-item-3">
727
- Manhattan
728
- <sp-menu slot="submenu">
729
- <sp-menu-item disabled>SoHo</sp-menu-item>
730
- <sp-menu-item>
731
- Union Square
732
- <sp-menu slot="submenu">
733
- <sp-menu-item>14th St</sp-menu-item>
734
- <sp-menu-item>Broadway</sp-menu-item>
735
- <sp-menu-item>Park Ave</sp-menu-item>
736
- </sp-menu>
737
- </sp-menu-item>
738
- <sp-menu-item>Upper East Side</sp-menu-item>
739
- </sp-menu>
740
- </sp-menu-item>
741
- </sp-menu-group>
742
- </sp-action-menu>
743
- `);
744
- await nextFrame();
745
- await nextFrame();
746
- });
747
- it("closes back to the first overlay without a `root` when clicking away", async function() {
748
- const rootMenu1 = this.el.querySelector("#submenu-item-1");
749
- const childMenu2 = this.el.querySelector("#submenu-item-2");
750
- expect(this.el.open).to.be.false;
751
- let opened = oneEvent(this.el, "sp-opened");
752
- this.el.click();
753
- await opened;
754
- expect(this.el.open).to.be.true;
755
- opened = oneEvent(rootMenu1, "sp-opened");
756
- rootMenu1.dispatchEvent(
757
- new PointerEvent("pointerenter", { bubbles: true })
758
- );
759
- await opened;
760
- opened = oneEvent(childMenu2, "sp-opened");
761
- childMenu2.dispatchEvent(
762
- new PointerEvent("pointerenter", { bubbles: true })
763
- );
764
- await opened;
765
- const closed = Promise.all([
766
- oneEvent(childMenu2, "sp-closed"),
767
- oneEvent(rootMenu1, "sp-closed"),
768
- oneEvent(this.el, "sp-closed")
769
- ]);
770
- await sendMouse({
771
- steps: [
772
- {
773
- type: "click",
774
- position: [600, 5]
775
- }
776
- ]
777
- });
778
- await closed;
779
- });
780
- it("closes descendant menus when Menu Item in ancestor without a submenu is pointerentered", async function() {
781
- const rootMenu = this.el.querySelector(
782
- "#submenu-item-1"
783
- );
784
- const noSubmenu = this.el.querySelector("#no-submenu");
785
- expect(this.el.open).to.be.false;
786
- let opened = oneEvent(this.el, "sp-opened");
787
- this.el.click();
788
- await opened;
789
- expect(this.el.open).to.be.true;
790
- opened = oneEvent(rootMenu, "sp-opened");
791
- rootMenu.dispatchEvent(
792
- new PointerEvent("pointerenter", { bubbles: true })
793
- );
794
- await opened;
795
- const closed = oneEvent(rootMenu, "sp-closed");
796
- noSubmenu.dispatchEvent(
797
- new PointerEvent("pointerenter", { bubbles: true })
798
- );
799
- await closed;
800
- });
801
- it("closes descendant menus when Menu Item in ancestor is clicked", async function() {
802
- const rootMenu1 = this.el.querySelector(
803
- "#submenu-item-1"
804
- );
805
- const childMenu2 = this.el.querySelector(
806
- "#submenu-item-2"
807
- );
808
- const ancestorItem = this.el.querySelector(
809
- "#ancestor-item"
810
- );
811
- expect(this.el.open).to.be.false;
812
- let opened = oneEvent(this.el, "sp-opened");
813
- this.el.click();
814
- await opened;
815
- expect(this.el.open).to.be.true;
816
- opened = oneEvent(rootMenu1, "sp-opened");
817
- rootMenu1.dispatchEvent(
818
- new PointerEvent("pointerenter", { bubbles: true })
819
- );
820
- await opened;
821
- opened = oneEvent(childMenu2, "sp-opened");
822
- childMenu2.dispatchEvent(
823
- new PointerEvent("pointerenter", { bubbles: true })
824
- );
825
- await opened;
826
- const closed = Promise.all([
827
- oneEvent(childMenu2, "sp-closed"),
828
- oneEvent(rootMenu1, "sp-closed"),
829
- oneEvent(this.el, "sp-closed")
830
- ]);
831
- const rect = ancestorItem.getBoundingClientRect();
832
- await sendMouse({
833
- steps: [
834
- {
835
- type: "click",
836
- position: [
837
- rect.left + rect.width / 2,
838
- rect.top + rect.height / 2
839
- ]
840
- }
841
- ]
842
- });
843
- await closed;
844
- });
845
- });
846
- it('cleans up submenus that close before they are "open"', async () => {
847
- if ("showPopover" in document.createElement("div")) {
848
- return;
849
- }
850
- await sendMouse({
851
- steps: [
852
- {
853
- type: "move",
854
- position: [1, 1]
855
- }
856
- ]
857
- });
858
- const el = await fixture(html`
859
- <sp-menu>
860
- <sp-menu-item class="root-1">
861
- Has submenu
862
- <sp-menu slot="submenu">${renderSubmenu()}</sp-menu>
863
- </sp-menu-item>
864
- <sp-menu-item class="root-2">
865
- Has submenu
866
- <sp-menu slot="submenu">${renderSubmenu()}</sp-menu>
867
- </sp-menu-item>
868
- </sp-menu>
869
- `);
870
- await elementUpdated(el);
871
- const rootItem1 = el.querySelector(".root-1");
872
- const rootItem2 = el.querySelector(".root-2");
873
- expect(rootItem1.open, "initially closed 1").to.be.false;
874
- expect(rootItem2.open, "initially closed 2").to.be.false;
875
- const rootItemBoundingRect1 = rootItem1.getBoundingClientRect();
876
- const rootItemBoundingRect2 = rootItem2.getBoundingClientRect();
877
- await sendMouse({
878
- steps: [
879
- {
880
- type: "move",
881
- position: [
882
- rootItemBoundingRect1.left + rootItemBoundingRect1.width / 2,
883
- rootItemBoundingRect1.top + rootItemBoundingRect1.height / 2
884
- ]
885
- }
886
- ]
887
- });
888
- await sendMouse({
889
- steps: [
890
- {
891
- type: "move",
892
- position: [
893
- rootItemBoundingRect2.left + rootItemBoundingRect2.width / 2,
894
- rootItemBoundingRect2.top + rootItemBoundingRect2.height / 2
895
- ]
896
- }
897
- ]
898
- });
899
- await sendMouse({
900
- steps: [
901
- {
902
- type: "move",
903
- position: [
904
- rootItemBoundingRect1.left + rootItemBoundingRect1.width / 2,
905
- rootItemBoundingRect1.top + rootItemBoundingRect1.height / 2
906
- ]
907
- }
908
- ]
909
- });
910
- await sendMouse({
911
- steps: [
912
- {
913
- type: "move",
914
- position: [
915
- rootItemBoundingRect2.left + rootItemBoundingRect2.width / 2,
916
- rootItemBoundingRect2.top + rootItemBoundingRect2.height / 2
917
- ]
918
- }
919
- ]
920
- });
921
- await nextFrame();
922
- await nextFrame();
923
- await nextFrame();
924
- await nextFrame();
925
- await nextFrame();
926
- await nextFrame();
927
- const closed = oneEvent(rootItem2, "sp-closed");
928
- await sendMouse({
929
- steps: [
930
- {
931
- type: "move",
932
- position: [
933
- rootItemBoundingRect2.left + rootItemBoundingRect2.width / 2,
934
- rootItemBoundingRect2.top + rootItemBoundingRect2.height * 2
935
- ]
936
- }
937
- ]
938
- });
939
- await closed;
940
- expect(rootItem1.open, "finally closed 1").to.be.false;
941
- expect(rootItem2.open, "finally closed 2").to.be.false;
942
- });
943
- it("allows using non-menu-item elements as the root of a submenu", async () => {
944
- var _a;
945
- const el = await fixture(html`
946
- <sp-menu>
947
- <sp-menu-item class="root">
948
- Has submenu
949
- <div role="menuitem" slot="submenu">
950
- <sp-menu-item class="submenu-1">One</sp-menu-item>
951
- <sp-menu-item>Two</sp-menu-item>
952
- <sp-menu-item>Three</sp-menu-item>
953
- </div
954
- ></div>
955
- </sp-menu-item>
956
- </sp-menu>
957
- `);
958
- await elementUpdated(el);
959
- const rootItem = el.querySelector(".root");
960
- const rootItemBoundingRect = rootItem.getBoundingClientRect();
961
- await sendMouse({
962
- steps: [
963
- {
964
- type: "move",
965
- position: [
966
- rootItemBoundingRect.left + rootItemBoundingRect.width / 2,
967
- rootItemBoundingRect.top + rootItemBoundingRect.height / 2
968
- ]
969
- }
970
- ]
971
- });
972
- expect(rootItem.open).to.be.true;
973
- const firstSubMenuItemRect = (_a = el.querySelector(".submenu-1")) == null ? void 0 : _a.getBoundingClientRect();
974
- if (!firstSubMenuItemRect) {
975
- throw new Error("Submenu item not found");
976
- }
977
- await sendMouse({
978
- steps: [
979
- {
980
- type: "click",
981
- position: [
982
- firstSubMenuItemRect.left + firstSubMenuItemRect.width / 2,
983
- firstSubMenuItemRect.top + firstSubMenuItemRect.height / 2
984
- ]
985
- }
986
- ]
987
- });
988
- });
989
- it("should make submenu scrollable when content exceeds max height", async () => {
990
- const el = await fixture(html`
991
- <sp-menu>
992
- <sp-menu-item>
993
- Parent Item
994
- <div role="menu" slot="submenu">
995
- ${Array(20).fill(0).map(
996
- (_, i) => html`
997
- <sp-menu-item>
998
- Submenu Item ${i + 1}
999
- </sp-menu-item>
1000
- `
1001
- )}
1002
- </div>
1003
- </sp-menu-item>
1004
- </sp-menu>
1005
- `);
1006
- await elementUpdated(el);
1007
- const menuItem = el.querySelector("sp-menu-item");
1008
- const submenu = menuItem.querySelector(
1009
- '[slot="submenu"]'
1010
- );
1011
- const opened = oneEvent(menuItem, "sp-opened");
1012
- menuItem.dispatchEvent(
1013
- new PointerEvent("pointerenter", { bubbles: true })
1014
- );
1015
- await opened;
1016
- submenu.style.maxHeight = "200px";
1017
- await elementUpdated(submenu);
1018
- const computedStyle = window.getComputedStyle(submenu);
1019
- expect(computedStyle.overflowY).to.equal("auto");
1020
- expect(submenu.scrollHeight).to.be.greaterThan(submenu.clientHeight);
1021
- const initialScrollTop = submenu.scrollTop;
1022
- submenu.scrollTop = 50;
1023
- await elementUpdated(submenu);
1024
- expect(submenu.scrollTop).to.equal(50);
1025
- expect(submenu.scrollTop).to.not.equal(initialScrollTop);
1026
- });
1027
- });
1028
- //# sourceMappingURL=submenu.test.js.map