@spectrum-web-components/picker 0.11.5 → 0.12.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.
- package/custom-elements.json +62 -78
- package/package.json +39 -22
- package/sp-picker.dev.js +3 -0
- package/sp-picker.dev.js.map +7 -0
- package/sp-picker.js +3 -14
- package/sp-picker.js.map +7 -1
- package/src/Picker.dev.js +501 -0
- package/src/Picker.dev.js.map +7 -0
- package/src/Picker.js +406 -435
- package/src/Picker.js.map +7 -1
- package/src/index.dev.js +2 -0
- package/src/index.dev.js.map +7 -0
- package/src/index.js +2 -13
- package/src/index.js.map +7 -1
- package/src/picker.css.dev.js +304 -0
- package/src/picker.css.dev.js.map +7 -0
- package/src/picker.css.js +4 -15
- package/src/picker.css.js.map +7 -1
- package/src/spectrum-picker.css.dev.js +300 -0
- package/src/spectrum-picker.css.dev.js.map +7 -0
- package/src/spectrum-picker.css.js +4 -15
- package/src/spectrum-picker.css.js.map +7 -1
- package/stories/picker-sizes.stories.js +22 -30
- package/stories/picker-sizes.stories.js.map +7 -1
- package/stories/picker.stories.js +84 -95
- package/stories/picker.stories.js.map +7 -1
- package/stories/states.js +225 -236
- package/stories/states.js.map +7 -1
- package/sync/index.dev.js +8 -0
- package/sync/index.dev.js.map +7 -0
- package/sync/index.js +6 -15
- package/sync/index.js.map +7 -1
- package/sync/sp-picker.dev.js +3 -0
- package/sync/sp-picker.dev.js.map +7 -0
- package/sync/sp-picker.js +3 -14
- package/sync/sp-picker.js.map +7 -1
- package/test/benchmark/basic-test.js +256 -267
- package/test/benchmark/basic-test.js.map +7 -1
- package/test/index.js +896 -839
- package/test/index.js.map +7 -1
- package/test/picker-reparenting.test.js +71 -76
- package/test/picker-reparenting.test.js.map +7 -1
- package/test/picker-responsive.test.js +41 -53
- package/test/picker-responsive.test.js.map +7 -1
- package/test/picker-sizes.test-vrt.js +4 -15
- package/test/picker-sizes.test-vrt.js.map +7 -1
- package/test/picker-sync.test.js +5 -16
- package/test/picker-sync.test.js.map +7 -1
- package/test/picker.test-vrt.js +4 -15
- package/test/picker.test-vrt.js.map +7 -1
- package/test/picker.test.js +5 -16
- package/test/picker.test.js.map +7 -1
package/test/index.js
CHANGED
|
@@ -1,29 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
import {
|
|
2
|
+
elementUpdated,
|
|
3
|
+
expect,
|
|
4
|
+
fixture,
|
|
5
|
+
html,
|
|
6
|
+
nextFrame,
|
|
7
|
+
oneEvent,
|
|
8
|
+
waitUntil
|
|
9
|
+
} from "@open-wc/testing";
|
|
10
|
+
import "@spectrum-web-components/shared/src/focus-visible.js";
|
|
11
|
+
import { spy, stub } from "sinon";
|
|
12
|
+
import {
|
|
13
|
+
arrowDownEvent,
|
|
14
|
+
arrowLeftEvent,
|
|
15
|
+
arrowRightEvent,
|
|
16
|
+
arrowUpEvent,
|
|
17
|
+
testForLitDevWarnings,
|
|
18
|
+
tEvent
|
|
19
|
+
} from "../../../test/testing-helpers.js";
|
|
20
|
+
import {
|
|
21
|
+
a11ySnapshot,
|
|
22
|
+
findAccessibilityNode,
|
|
23
|
+
sendKeys
|
|
24
|
+
} from "@web/test-runner-commands";
|
|
25
|
+
import { iconsOnly } from "../stories/picker.stories.js";
|
|
26
|
+
import { sendMouse } from "../../../test/plugins/browser.js";
|
|
27
|
+
const isMenuActiveElement = function() {
|
|
28
|
+
var _a;
|
|
29
|
+
return ((_a = document.activeElement) == null ? void 0 : _a.localName) === "sp-menu";
|
|
22
30
|
};
|
|
23
31
|
export function runPickerTests() {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
let el;
|
|
33
|
+
const pickerFixture = async () => {
|
|
34
|
+
const test = await fixture(html`
|
|
27
35
|
<div>
|
|
28
36
|
<sp-field-label for="picker">
|
|
29
37
|
Where do you live?
|
|
@@ -44,657 +52,670 @@ export function runPickerTests() {
|
|
|
44
52
|
</sp-picker>
|
|
45
53
|
</div>
|
|
46
54
|
`);
|
|
47
|
-
|
|
55
|
+
return test.querySelector("sp-picker");
|
|
56
|
+
};
|
|
57
|
+
describe("standard", () => {
|
|
58
|
+
beforeEach(async () => {
|
|
59
|
+
el = await pickerFixture();
|
|
60
|
+
await elementUpdated(el);
|
|
61
|
+
});
|
|
62
|
+
afterEach(async () => {
|
|
63
|
+
if (el.open) {
|
|
64
|
+
const closed = oneEvent(el, "sp-closed");
|
|
65
|
+
el.open = false;
|
|
66
|
+
await closed;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
it("loads accessibly", async () => {
|
|
70
|
+
await expect(el).to.be.accessible();
|
|
71
|
+
});
|
|
72
|
+
it("closes accessibly", async () => {
|
|
73
|
+
const opened = oneEvent(el, "sp-opened");
|
|
74
|
+
el.open = true;
|
|
75
|
+
await opened;
|
|
76
|
+
expect(el.open).to.be.true;
|
|
77
|
+
const accessibleCloseButton = document.querySelector(".visually-hidden button");
|
|
78
|
+
const closed = oneEvent(el, "sp-closed");
|
|
79
|
+
accessibleCloseButton.click();
|
|
80
|
+
await closed;
|
|
81
|
+
expect(el.open).to.be.false;
|
|
82
|
+
});
|
|
83
|
+
it("accepts new selected item content", async () => {
|
|
84
|
+
const option2 = el.querySelector('[value="option-2"');
|
|
85
|
+
el.value = "option-2";
|
|
86
|
+
await elementUpdated(option2);
|
|
87
|
+
await elementUpdated(el);
|
|
88
|
+
expect(el.value).to.equal("option-2");
|
|
89
|
+
expect((el.button.textContent || "").trim()).to.equal("Select Inverse");
|
|
90
|
+
let itemUpdated = oneEvent(el, "sp-menu-item-added-or-updated");
|
|
91
|
+
const newLabel1 = "Invert Selection";
|
|
92
|
+
option2.innerHTML = newLabel1;
|
|
93
|
+
await itemUpdated;
|
|
94
|
+
await elementUpdated(el);
|
|
95
|
+
expect(el.value).to.equal("option-2");
|
|
96
|
+
expect((el.button.textContent || "").trim()).to.equal(newLabel1);
|
|
97
|
+
itemUpdated = oneEvent(el, "sp-menu-item-added-or-updated");
|
|
98
|
+
const newLabel2 = "Other option";
|
|
99
|
+
option2.childNodes[0].textContent = newLabel2;
|
|
100
|
+
await itemUpdated;
|
|
101
|
+
await elementUpdated(el);
|
|
102
|
+
expect(el.value).to.equal("option-2");
|
|
103
|
+
expect((el.button.textContent || "").trim()).to.equal(newLabel2);
|
|
104
|
+
});
|
|
105
|
+
it("accepts new selected item content when open", async () => {
|
|
106
|
+
const option2 = el.querySelector('[value="option-2"');
|
|
107
|
+
el.value = "option-2";
|
|
108
|
+
await elementUpdated(el);
|
|
109
|
+
expect(el.value).to.equal("option-2");
|
|
110
|
+
expect((el.button.textContent || "").trim()).to.equal("Select Inverse");
|
|
111
|
+
const opened = oneEvent(el, "sp-opened");
|
|
112
|
+
el.open = true;
|
|
113
|
+
await opened;
|
|
114
|
+
const itemUpdated = oneEvent(option2, "sp-menu-item-added-or-updated");
|
|
115
|
+
option2.innerHTML = "Invert Selection";
|
|
116
|
+
await itemUpdated;
|
|
117
|
+
await elementUpdated(el);
|
|
118
|
+
expect(el.value).to.equal("option-2");
|
|
119
|
+
expect((el.button.textContent || "").trim()).to.equal("Invert Selection");
|
|
120
|
+
});
|
|
121
|
+
it("unsets value when children removed", async () => {
|
|
122
|
+
el.value = "option-2";
|
|
123
|
+
await elementUpdated(el);
|
|
124
|
+
expect(el.value).to.equal("option-2");
|
|
125
|
+
expect((el.button.textContent || "").trim()).to.equal("Select Inverse");
|
|
126
|
+
const items = el.querySelectorAll("sp-menu-item");
|
|
127
|
+
const removals = [];
|
|
128
|
+
items.forEach((item) => {
|
|
129
|
+
const removal = oneEvent(el, "sp-menu-item-removed");
|
|
130
|
+
item.remove();
|
|
131
|
+
removals.push(removal);
|
|
132
|
+
});
|
|
133
|
+
await Promise.all(removals);
|
|
134
|
+
await elementUpdated(el);
|
|
135
|
+
expect(el.value).to.equal("");
|
|
136
|
+
expect((el.button.textContent || "").trim()).to.equal("");
|
|
137
|
+
});
|
|
138
|
+
it("accepts a new item and value at the same time", async () => {
|
|
139
|
+
el.value = "option-2";
|
|
140
|
+
await elementUpdated(el);
|
|
141
|
+
expect(el.value).to.equal("option-2");
|
|
142
|
+
const item = document.createElement("sp-menu-item");
|
|
143
|
+
item.value = "option-new";
|
|
144
|
+
item.textContent = "New Option";
|
|
145
|
+
el.append(item);
|
|
146
|
+
await elementUpdated(el);
|
|
147
|
+
el.value = "option-new";
|
|
148
|
+
await elementUpdated(el);
|
|
149
|
+
expect(el.value).to.equal("option-new");
|
|
150
|
+
});
|
|
151
|
+
it("accepts a new item that can be selected", async () => {
|
|
152
|
+
el.value = "option-2";
|
|
153
|
+
await elementUpdated(el);
|
|
154
|
+
expect(el.value).to.equal("option-2");
|
|
155
|
+
const item = document.createElement("sp-menu-item");
|
|
156
|
+
item.value = "option-new";
|
|
157
|
+
item.textContent = "New Option";
|
|
158
|
+
el.append(item);
|
|
159
|
+
await elementUpdated(item);
|
|
160
|
+
await elementUpdated(el);
|
|
161
|
+
let opened = oneEvent(el, "sp-opened");
|
|
162
|
+
el.open = true;
|
|
163
|
+
await opened;
|
|
164
|
+
await nextFrame();
|
|
165
|
+
const close = oneEvent(el, "sp-closed");
|
|
166
|
+
item.click();
|
|
167
|
+
await close;
|
|
168
|
+
await nextFrame();
|
|
169
|
+
expect(el.value, "first time").to.equal("option-new");
|
|
170
|
+
opened = oneEvent(el, "sp-opened");
|
|
171
|
+
el.open = true;
|
|
172
|
+
await opened;
|
|
173
|
+
await nextFrame();
|
|
174
|
+
expect(el.value, "second time").to.equal("option-new");
|
|
175
|
+
});
|
|
176
|
+
it('manages its "name" value in the accessibility tree', async () => {
|
|
177
|
+
let snapshot = await a11ySnapshot({});
|
|
178
|
+
expect(findAccessibilityNode(snapshot, (node) => node.name === "Where do you live?"), "`name` is the label text").to.not.be.null;
|
|
179
|
+
el.value = "option-2";
|
|
180
|
+
await elementUpdated(el);
|
|
181
|
+
snapshot = await a11ySnapshot({});
|
|
182
|
+
expect(findAccessibilityNode(snapshot, (node) => node.name === "Where do you live? Select Inverse"), "`name` is the label text plus the selected item text").to.not.be.null;
|
|
183
|
+
});
|
|
184
|
+
it("manages `aria-activedescendant`", async () => {
|
|
185
|
+
const firstItem = el.querySelector("sp-menu-item:nth-child(1)");
|
|
186
|
+
const secondItem = el.querySelector("sp-menu-item:nth-child(2)");
|
|
187
|
+
const opened = oneEvent(el, "sp-opened");
|
|
188
|
+
el.open = true;
|
|
189
|
+
await opened;
|
|
190
|
+
expect(el.optionsMenu.getAttribute("aria-activedescendant")).to.equal(firstItem == null ? void 0 : firstItem.id);
|
|
191
|
+
await sendKeys({ press: "ArrowDown" });
|
|
192
|
+
await elementUpdated(el);
|
|
193
|
+
expect(el.optionsMenu.getAttribute("aria-activedescendant")).to.equal(secondItem == null ? void 0 : secondItem.id);
|
|
194
|
+
});
|
|
195
|
+
it("renders invalid accessibly", async () => {
|
|
196
|
+
el.invalid = true;
|
|
197
|
+
await elementUpdated(el);
|
|
198
|
+
expect(el.invalid).to.be.true;
|
|
199
|
+
await expect(el).to.be.accessible();
|
|
200
|
+
});
|
|
201
|
+
it("renders selection accessibly", async () => {
|
|
202
|
+
el.value = "option-2";
|
|
203
|
+
await elementUpdated(el);
|
|
204
|
+
await expect(el).to.be.accessible();
|
|
205
|
+
});
|
|
206
|
+
it("opens with visible focus on a menu item on `DownArrow`", async () => {
|
|
207
|
+
const firstItem = el.querySelector("sp-menu-item");
|
|
208
|
+
await elementUpdated(el);
|
|
209
|
+
expect(firstItem.focused, "should not visually focused").to.be.false;
|
|
210
|
+
el.focus();
|
|
211
|
+
await elementUpdated(el);
|
|
212
|
+
const opened = oneEvent(el, "sp-opened");
|
|
213
|
+
await sendKeys({ press: "ArrowRight" });
|
|
214
|
+
await sendKeys({ press: "ArrowLeft" });
|
|
215
|
+
await sendKeys({ press: "ArrowDown" });
|
|
216
|
+
await opened;
|
|
217
|
+
expect(el.open).to.be.true;
|
|
218
|
+
expect(firstItem.focused, "should be visually focused").to.be.true;
|
|
219
|
+
const closed = oneEvent(el, "sp-closed");
|
|
220
|
+
await sendKeys({
|
|
221
|
+
press: "Escape"
|
|
222
|
+
});
|
|
223
|
+
await closed;
|
|
224
|
+
expect(el.open).to.be.false;
|
|
225
|
+
await waitUntil(() => !firstItem.focused, "not visually focused");
|
|
226
|
+
});
|
|
227
|
+
it("opens without visible focus on a menu item on click", async () => {
|
|
228
|
+
const firstItem = el.querySelector("sp-menu-item");
|
|
229
|
+
await elementUpdated(el);
|
|
230
|
+
const boundingRect = el.getBoundingClientRect();
|
|
231
|
+
expect(firstItem.focused, "not visually focused").to.be.false;
|
|
232
|
+
const opened = oneEvent(el, "sp-opened");
|
|
233
|
+
sendMouse({
|
|
234
|
+
steps: [
|
|
235
|
+
{
|
|
236
|
+
type: "click",
|
|
237
|
+
position: [
|
|
238
|
+
boundingRect.x + boundingRect.width / 2,
|
|
239
|
+
boundingRect.y + boundingRect.height / 2
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
});
|
|
244
|
+
await opened;
|
|
245
|
+
await elementUpdated(el);
|
|
246
|
+
expect(el.open).to.be.true;
|
|
247
|
+
expect(firstItem.focused, "still not visually focused").to.be.false;
|
|
248
|
+
});
|
|
249
|
+
it("closes when becoming disabled", async () => {
|
|
250
|
+
expect(el.open).to.be.false;
|
|
251
|
+
el.click();
|
|
252
|
+
await elementUpdated(el);
|
|
253
|
+
expect(el.open).to.be.true;
|
|
254
|
+
el.disabled = true;
|
|
255
|
+
await elementUpdated(el);
|
|
256
|
+
expect(el.open).to.be.false;
|
|
257
|
+
});
|
|
258
|
+
it("closes when clicking away", async () => {
|
|
259
|
+
el.id = "closing";
|
|
260
|
+
const other = document.createElement("div");
|
|
261
|
+
document.body.append(other);
|
|
262
|
+
await elementUpdated(el);
|
|
263
|
+
expect(el.open).to.be.false;
|
|
264
|
+
el.click();
|
|
265
|
+
await elementUpdated(el);
|
|
266
|
+
expect(el.open).to.be.true;
|
|
267
|
+
other.click();
|
|
268
|
+
await waitUntil(() => !el.open, "closed");
|
|
269
|
+
other.remove();
|
|
270
|
+
});
|
|
271
|
+
it("selects", async () => {
|
|
272
|
+
var _a, _b;
|
|
273
|
+
const secondItem = el.querySelector("sp-menu-item:nth-of-type(2)");
|
|
274
|
+
const button = el.button;
|
|
275
|
+
const opened = oneEvent(el, "sp-opened");
|
|
276
|
+
button.click();
|
|
277
|
+
await opened;
|
|
278
|
+
await elementUpdated(el);
|
|
279
|
+
expect(el.open).to.be.true;
|
|
280
|
+
expect((_a = el.selectedItem) == null ? void 0 : _a.itemText).to.be.undefined;
|
|
281
|
+
expect(el.value).to.equal("");
|
|
282
|
+
const closed = oneEvent(el, "sp-closed");
|
|
283
|
+
secondItem.click();
|
|
284
|
+
await closed;
|
|
285
|
+
expect(el.open).to.be.false;
|
|
286
|
+
expect((_b = el.selectedItem) == null ? void 0 : _b.itemText).to.equal("Select Inverse");
|
|
287
|
+
expect(el.value).to.equal("option-2");
|
|
288
|
+
});
|
|
289
|
+
it("re-selects", async () => {
|
|
290
|
+
var _a, _b, _c, _d;
|
|
291
|
+
const firstItem = el.querySelector("sp-menu-item:nth-of-type(1)");
|
|
292
|
+
const secondItem = el.querySelector("sp-menu-item:nth-of-type(2)");
|
|
293
|
+
const button = el.button;
|
|
294
|
+
const opened = oneEvent(el, "sp-opened");
|
|
295
|
+
button.click();
|
|
296
|
+
await opened;
|
|
297
|
+
await nextFrame();
|
|
298
|
+
expect(el.open).to.be.true;
|
|
299
|
+
expect((_a = el.selectedItem) == null ? void 0 : _a.itemText).to.be.undefined;
|
|
300
|
+
expect(el.value).to.equal("");
|
|
301
|
+
const closed = oneEvent(el, "sp-closed");
|
|
302
|
+
secondItem.click();
|
|
303
|
+
await closed;
|
|
304
|
+
await nextFrame();
|
|
305
|
+
expect(el.open).to.be.false;
|
|
306
|
+
expect((_b = el.selectedItem) == null ? void 0 : _b.itemText).to.equal("Select Inverse");
|
|
307
|
+
expect(el.value).to.equal("option-2");
|
|
308
|
+
const opened2 = oneEvent(el, "sp-opened");
|
|
309
|
+
button.click();
|
|
310
|
+
await opened2;
|
|
311
|
+
await nextFrame();
|
|
312
|
+
expect(el.open).to.be.true;
|
|
313
|
+
expect((_c = el.selectedItem) == null ? void 0 : _c.itemText).to.equal("Select Inverse");
|
|
314
|
+
expect(el.value).to.equal("option-2");
|
|
315
|
+
const closed2 = oneEvent(el, "sp-closed");
|
|
316
|
+
firstItem.click();
|
|
317
|
+
await closed2;
|
|
318
|
+
await nextFrame();
|
|
319
|
+
expect(el.open).to.be.false;
|
|
320
|
+
expect((_d = el.selectedItem) == null ? void 0 : _d.itemText).to.equal("Deselect");
|
|
321
|
+
expect(el.value).to.equal("Deselect");
|
|
322
|
+
});
|
|
323
|
+
it("dispatches bubbling and composed events", async () => {
|
|
324
|
+
const changeSpy = spy();
|
|
325
|
+
const parent = el.parentElement;
|
|
326
|
+
parent.attachShadow({ mode: "open" });
|
|
327
|
+
parent.shadowRoot.append(el);
|
|
328
|
+
const secondItem = el.querySelector("sp-menu-item:nth-of-type(2)");
|
|
329
|
+
parent.addEventListener("change", () => changeSpy());
|
|
330
|
+
expect(el.value).to.equal("");
|
|
331
|
+
const opened = oneEvent(el, "sp-opened");
|
|
332
|
+
el.open = true;
|
|
333
|
+
await opened;
|
|
334
|
+
await elementUpdated(el);
|
|
335
|
+
const closed = oneEvent(el, "sp-closed");
|
|
336
|
+
secondItem.click();
|
|
337
|
+
await closed;
|
|
338
|
+
await elementUpdated(el);
|
|
339
|
+
expect(el.value).to.equal(secondItem.value);
|
|
340
|
+
expect(changeSpy.calledOnce).to.be.true;
|
|
341
|
+
});
|
|
342
|
+
it("can have selection prevented", async () => {
|
|
343
|
+
var _a;
|
|
344
|
+
const preventChangeSpy = spy();
|
|
345
|
+
const secondItem = el.querySelector("sp-menu-item:nth-of-type(2)");
|
|
346
|
+
const button = el.button;
|
|
347
|
+
let opened = oneEvent(el, "sp-opened");
|
|
348
|
+
button.click();
|
|
349
|
+
await opened;
|
|
350
|
+
await elementUpdated(el);
|
|
351
|
+
expect(el.open).to.be.true;
|
|
352
|
+
expect((_a = el.selectedItem) == null ? void 0 : _a.itemText).to.be.undefined;
|
|
353
|
+
expect(el.value).to.equal("");
|
|
354
|
+
expect(secondItem.selected).to.be.false;
|
|
355
|
+
el.addEventListener("change", (event) => {
|
|
356
|
+
event.preventDefault();
|
|
357
|
+
preventChangeSpy();
|
|
358
|
+
});
|
|
359
|
+
const closed = oneEvent(el, "sp-closed");
|
|
360
|
+
opened = oneEvent(el, "sp-opened");
|
|
361
|
+
secondItem.click();
|
|
362
|
+
await closed;
|
|
363
|
+
await opened;
|
|
364
|
+
await elementUpdated(el);
|
|
365
|
+
expect(preventChangeSpy.calledOnce).to.be.true;
|
|
366
|
+
expect(secondItem.selected, "selection prevented").to.be.false;
|
|
367
|
+
});
|
|
368
|
+
it("can throw focus after `change`", async () => {
|
|
369
|
+
var _a;
|
|
370
|
+
const input = document.createElement("input");
|
|
371
|
+
document.body.append(input);
|
|
372
|
+
await elementUpdated(el);
|
|
373
|
+
const secondItem = el.querySelector("sp-menu-item:nth-of-type(2)");
|
|
374
|
+
const button = el.button;
|
|
375
|
+
const opened = oneEvent(el, "sp-opened");
|
|
376
|
+
button.click();
|
|
377
|
+
await opened;
|
|
378
|
+
await elementUpdated(el);
|
|
379
|
+
expect(el.open).to.be.true;
|
|
380
|
+
expect((_a = el.selectedItem) == null ? void 0 : _a.itemText).to.be.undefined;
|
|
381
|
+
expect(el.value).to.equal("");
|
|
382
|
+
expect(secondItem.selected).to.be.false;
|
|
383
|
+
el.addEventListener("change", () => {
|
|
384
|
+
input.focus();
|
|
385
|
+
});
|
|
386
|
+
const closed = oneEvent(el, "sp-closed");
|
|
387
|
+
secondItem.click();
|
|
388
|
+
await closed;
|
|
389
|
+
await elementUpdated(el);
|
|
390
|
+
expect(el.open).to.be.false;
|
|
391
|
+
expect(el.value, "value changed").to.equal("option-2");
|
|
392
|
+
expect(secondItem.selected, "selected changed").to.be.true;
|
|
393
|
+
await waitUntil(() => document.activeElement === input, "focus throw");
|
|
394
|
+
input.remove();
|
|
395
|
+
});
|
|
396
|
+
it("opens on ArrowUp", async () => {
|
|
397
|
+
const button = el.button;
|
|
398
|
+
el.focus();
|
|
399
|
+
await elementUpdated(el);
|
|
400
|
+
expect(el.open, "inially closed").to.be.false;
|
|
401
|
+
button.dispatchEvent(tEvent());
|
|
402
|
+
await elementUpdated(el);
|
|
403
|
+
expect(el.open, "still closed").to.be.false;
|
|
404
|
+
button.dispatchEvent(arrowUpEvent());
|
|
405
|
+
await elementUpdated(el);
|
|
406
|
+
expect(el.open, "open by ArrowUp").to.be.true;
|
|
407
|
+
await waitUntil(() => document.querySelector("active-overlay") !== null, "an active-overlay has been inserted on the page");
|
|
408
|
+
button.dispatchEvent(new KeyboardEvent("keyup", {
|
|
409
|
+
bubbles: true,
|
|
410
|
+
composed: true,
|
|
411
|
+
cancelable: true,
|
|
412
|
+
key: "Escape",
|
|
413
|
+
code: "Escape"
|
|
414
|
+
}));
|
|
415
|
+
await elementUpdated(el);
|
|
416
|
+
await waitUntil(() => el.open === false, "closed by Escape");
|
|
417
|
+
await waitUntil(() => document.querySelector("active-overlay") === null, "an active-overlay has been inserted on the page");
|
|
418
|
+
});
|
|
419
|
+
it("opens on ArrowDown", async () => {
|
|
420
|
+
var _a, _b;
|
|
421
|
+
const firstItem = el.querySelector("sp-menu-item:nth-of-type(1)");
|
|
422
|
+
const button = el.button;
|
|
423
|
+
el.focus();
|
|
424
|
+
await elementUpdated(el);
|
|
425
|
+
expect(el.open, "inially closed").to.be.false;
|
|
426
|
+
const opened = oneEvent(el, "sp-opened");
|
|
427
|
+
button.dispatchEvent(arrowDownEvent());
|
|
428
|
+
await opened;
|
|
429
|
+
await elementUpdated(el);
|
|
430
|
+
expect(el.open, "open by ArrowDown").to.be.true;
|
|
431
|
+
expect((_a = el.selectedItem) == null ? void 0 : _a.itemText).to.be.undefined;
|
|
432
|
+
expect(el.value).to.equal("");
|
|
433
|
+
const closed = oneEvent(el, "sp-closed");
|
|
434
|
+
firstItem.click();
|
|
435
|
+
await closed;
|
|
436
|
+
await elementUpdated(el);
|
|
437
|
+
expect(el.open).to.be.false;
|
|
438
|
+
expect((_b = el.selectedItem) == null ? void 0 : _b.itemText).to.equal("Deselect");
|
|
439
|
+
expect(el.value).to.equal("Deselect");
|
|
440
|
+
});
|
|
441
|
+
it("quick selects on ArrowLeft/Right", async () => {
|
|
442
|
+
const selectionSpy = spy();
|
|
443
|
+
el.addEventListener("change", (event) => {
|
|
444
|
+
const { value } = event.target;
|
|
445
|
+
selectionSpy(value);
|
|
446
|
+
});
|
|
447
|
+
const button = el.button;
|
|
448
|
+
el.focus();
|
|
449
|
+
button.dispatchEvent(arrowLeftEvent());
|
|
450
|
+
await elementUpdated(el);
|
|
451
|
+
expect(selectionSpy.callCount).to.equal(1);
|
|
452
|
+
expect(selectionSpy.calledWith("Deselected"));
|
|
453
|
+
button.dispatchEvent(arrowLeftEvent());
|
|
454
|
+
await elementUpdated(el);
|
|
455
|
+
expect(selectionSpy.callCount).to.equal(1);
|
|
456
|
+
button.dispatchEvent(arrowRightEvent());
|
|
457
|
+
await elementUpdated(el);
|
|
458
|
+
expect(selectionSpy.calledWith("option-2"));
|
|
459
|
+
button.dispatchEvent(arrowRightEvent());
|
|
460
|
+
button.dispatchEvent(arrowRightEvent());
|
|
461
|
+
button.dispatchEvent(arrowRightEvent());
|
|
462
|
+
button.dispatchEvent(arrowRightEvent());
|
|
463
|
+
await elementUpdated(el);
|
|
464
|
+
expect(selectionSpy.callCount).to.equal(5);
|
|
465
|
+
expect(selectionSpy.calledWith("Save Selection"));
|
|
466
|
+
expect(selectionSpy.calledWith("Make Work Path")).to.be.false;
|
|
467
|
+
});
|
|
468
|
+
it("quick selects first item on ArrowRight when no value", async () => {
|
|
469
|
+
const selectionSpy = spy();
|
|
470
|
+
el.addEventListener("change", (event) => {
|
|
471
|
+
const { value } = event.target;
|
|
472
|
+
selectionSpy(value);
|
|
473
|
+
});
|
|
474
|
+
const button = el.button;
|
|
475
|
+
el.focus();
|
|
476
|
+
button.dispatchEvent(arrowRightEvent());
|
|
477
|
+
await elementUpdated(el);
|
|
478
|
+
expect(selectionSpy.callCount).to.equal(1);
|
|
479
|
+
expect(selectionSpy.calledWith("Deselected"));
|
|
480
|
+
});
|
|
481
|
+
it("loads", async () => {
|
|
482
|
+
expect(el).to.not.be.undefined;
|
|
483
|
+
});
|
|
484
|
+
it("refocuses on list when open", async () => {
|
|
485
|
+
const firstItem = el.querySelector("sp-menu-item");
|
|
486
|
+
const input = document.createElement("input");
|
|
487
|
+
el.insertAdjacentElement("afterend", input);
|
|
488
|
+
el.focus();
|
|
489
|
+
await sendKeys({ press: "Tab" });
|
|
490
|
+
expect(document.activeElement === input).to.be.true;
|
|
491
|
+
await sendKeys({ press: "Shift+Tab" });
|
|
492
|
+
expect(document.activeElement === el).to.be.true;
|
|
493
|
+
await sendKeys({ press: "Enter" });
|
|
494
|
+
const opened = oneEvent(el, "sp-opened");
|
|
495
|
+
el.open = true;
|
|
496
|
+
await opened;
|
|
497
|
+
await elementUpdated(el);
|
|
498
|
+
await waitUntil(() => firstItem.focused, "The first items should have become focused visually.");
|
|
499
|
+
el.blur();
|
|
500
|
+
await elementUpdated(el);
|
|
501
|
+
expect(el.open).to.be.true;
|
|
502
|
+
el.focus();
|
|
503
|
+
await elementUpdated(el);
|
|
504
|
+
await waitUntil(() => isMenuActiveElement(), "first item refocused");
|
|
505
|
+
expect(el.open).to.be.true;
|
|
506
|
+
expect(isMenuActiveElement()).to.be.true;
|
|
507
|
+
await sendKeys({ press: "ArrowDown" });
|
|
508
|
+
await sendKeys({ press: "ArrowUp" });
|
|
509
|
+
expect(firstItem.focused).to.be.true;
|
|
510
|
+
});
|
|
511
|
+
it("does not allow tabing to close", async () => {
|
|
512
|
+
el.open = true;
|
|
513
|
+
await elementUpdated(el);
|
|
514
|
+
expect(el.open).to.be.true;
|
|
515
|
+
el.focus();
|
|
516
|
+
await elementUpdated(el);
|
|
517
|
+
await waitUntil(() => isMenuActiveElement(), "first item refocused");
|
|
518
|
+
expect(el.open).to.be.true;
|
|
519
|
+
expect(isMenuActiveElement()).to.be.true;
|
|
520
|
+
await sendKeys({ press: "Tab" });
|
|
521
|
+
expect(el.open, "stays open").to.be.true;
|
|
522
|
+
});
|
|
523
|
+
describe("tab order", () => {
|
|
524
|
+
let input1;
|
|
525
|
+
let input2;
|
|
526
|
+
beforeEach(() => {
|
|
527
|
+
const surroundingInput = () => {
|
|
528
|
+
const input = document.createElement("input");
|
|
529
|
+
input.type = "text";
|
|
530
|
+
input.tabIndex = 0;
|
|
531
|
+
return input;
|
|
532
|
+
};
|
|
533
|
+
input1 = surroundingInput();
|
|
534
|
+
input2 = surroundingInput();
|
|
535
|
+
el.insertAdjacentElement("beforebegin", input1);
|
|
536
|
+
el.insertAdjacentElement("afterend", input2);
|
|
537
|
+
});
|
|
538
|
+
afterEach(() => {
|
|
539
|
+
input1.remove();
|
|
540
|
+
input2.remove();
|
|
541
|
+
});
|
|
542
|
+
it("tabs forward through the element", async () => {
|
|
543
|
+
input1.focus();
|
|
544
|
+
await nextFrame();
|
|
545
|
+
expect(document.activeElement === input1, "focuses input 1").to.true;
|
|
546
|
+
let focused = oneEvent(el, "focus");
|
|
547
|
+
await sendKeys({ press: "Tab" });
|
|
548
|
+
await focused;
|
|
549
|
+
expect(el.focused, "focused").to.be.true;
|
|
550
|
+
expect(el.open, "closed").to.be.false;
|
|
551
|
+
expect(document.activeElement === el, "focuses el").to.be.true;
|
|
552
|
+
focused = oneEvent(input2, "focus");
|
|
553
|
+
await sendKeys({ press: "Tab" });
|
|
554
|
+
await focused;
|
|
555
|
+
expect(document.activeElement === input2, "focuses input 2").to.true;
|
|
556
|
+
});
|
|
557
|
+
it("shift+tabs backwards through the element", async () => {
|
|
558
|
+
input2.focus();
|
|
559
|
+
await nextFrame();
|
|
560
|
+
expect(document.activeElement, "focuses input 2").to.equal(input2);
|
|
561
|
+
let focused = oneEvent(el, "focus");
|
|
562
|
+
await sendKeys({ press: "Shift+Tab" });
|
|
563
|
+
await focused;
|
|
564
|
+
expect(el.focused, "focused").to.be.true;
|
|
565
|
+
expect(el.open, "closed").to.be.false;
|
|
566
|
+
expect(document.activeElement, "focuses el").to.equal(el);
|
|
567
|
+
focused = oneEvent(input1, "focus");
|
|
568
|
+
await sendKeys({ press: "Shift+Tab" });
|
|
569
|
+
await focused;
|
|
570
|
+
expect(document.activeElement, "focuses input 1").to.equal(input1);
|
|
571
|
+
});
|
|
572
|
+
it('traps tab in the menu as a `type="modal"` overlay forward', async () => {
|
|
573
|
+
el.focus();
|
|
574
|
+
await nextFrame();
|
|
575
|
+
expect(document.activeElement, "focuses el").to.equal(el);
|
|
576
|
+
const opened = oneEvent(el, "sp-opened");
|
|
577
|
+
await sendKeys({ press: "ArrowDown" });
|
|
578
|
+
await opened;
|
|
579
|
+
expect(el.open, "opened").to.be.true;
|
|
580
|
+
await waitUntil(() => isMenuActiveElement(), "first item focused");
|
|
581
|
+
const activeElement = document.activeElement;
|
|
582
|
+
const blured = oneEvent(activeElement, "blur");
|
|
583
|
+
await sendKeys({ press: "Tab" });
|
|
584
|
+
await blured;
|
|
585
|
+
expect(el.open).to.be.true;
|
|
586
|
+
expect(document.activeElement === input1).to.be.false;
|
|
587
|
+
expect(document.activeElement === input2).to.be.false;
|
|
588
|
+
});
|
|
589
|
+
it('traps tab in the menu as a `type="modal"` overlay backwards', async () => {
|
|
590
|
+
el.focus();
|
|
591
|
+
await nextFrame();
|
|
592
|
+
expect(document.activeElement, "focuses el").to.equal(el);
|
|
593
|
+
const opened = oneEvent(el, "sp-opened");
|
|
594
|
+
await sendKeys({ press: "ArrowDown" });
|
|
595
|
+
await opened;
|
|
596
|
+
expect(el.open, "opened").to.be.true;
|
|
597
|
+
await waitUntil(() => isMenuActiveElement(), "first item focused");
|
|
598
|
+
const activeElement = document.activeElement;
|
|
599
|
+
const blured = oneEvent(activeElement, "blur");
|
|
600
|
+
await sendKeys({ press: "Shift+Tab" });
|
|
601
|
+
await blured;
|
|
602
|
+
expect(el.open).to.be.true;
|
|
603
|
+
expect(document.activeElement === input1).to.be.false;
|
|
604
|
+
expect(document.activeElement === input2).to.be.false;
|
|
605
|
+
});
|
|
606
|
+
it("can close and immediate tab to the next tab stop", async () => {
|
|
607
|
+
el.focus();
|
|
608
|
+
await nextFrame();
|
|
609
|
+
expect(document.activeElement, "focuses el").to.equal(el);
|
|
610
|
+
const opened = oneEvent(el, "sp-opened");
|
|
611
|
+
await sendKeys({ press: "ArrowUp" });
|
|
612
|
+
await opened;
|
|
613
|
+
expect(el.open, "opened").to.be.true;
|
|
614
|
+
await waitUntil(() => isMenuActiveElement(), "first item focused");
|
|
615
|
+
const closed = oneEvent(el, "sp-closed");
|
|
616
|
+
el.open = false;
|
|
617
|
+
await closed;
|
|
618
|
+
expect(el.open).to.be.false;
|
|
619
|
+
expect(document.activeElement === el).to.be.true;
|
|
620
|
+
const focused = oneEvent(input2, "focus");
|
|
621
|
+
await sendKeys({ press: "Tab" });
|
|
622
|
+
await focused;
|
|
623
|
+
expect(el.open).to.be.false;
|
|
624
|
+
expect(document.activeElement === input2).to.be.true;
|
|
625
|
+
});
|
|
626
|
+
it("can close and immediate shift+tab to the previous tab stop", async () => {
|
|
627
|
+
el.focus();
|
|
628
|
+
await nextFrame();
|
|
629
|
+
expect(document.activeElement, "focuses el").to.equal(el);
|
|
630
|
+
const opened = oneEvent(el, "sp-opened");
|
|
631
|
+
await sendKeys({ press: "ArrowUp" });
|
|
632
|
+
await opened;
|
|
633
|
+
expect(el.open, "opened").to.be.true;
|
|
634
|
+
await waitUntil(() => isMenuActiveElement(), "first item focused");
|
|
635
|
+
const closed = oneEvent(el, "sp-closed");
|
|
636
|
+
el.open = false;
|
|
637
|
+
await closed;
|
|
638
|
+
expect(el.open).to.be.false;
|
|
639
|
+
expect(document.activeElement === el).to.be.true;
|
|
640
|
+
const focused = oneEvent(input1, "focus");
|
|
641
|
+
await sendKeys({ press: "Shift+Tab" });
|
|
642
|
+
await focused;
|
|
643
|
+
expect(el.open).to.be.false;
|
|
644
|
+
expect(document.activeElement === input1).to.be.true;
|
|
645
|
+
});
|
|
646
|
+
});
|
|
647
|
+
it("does not open when [readonly]", async () => {
|
|
648
|
+
el.readonly = true;
|
|
649
|
+
await elementUpdated(el);
|
|
650
|
+
const button = el.button;
|
|
651
|
+
button.click();
|
|
652
|
+
await elementUpdated(el);
|
|
653
|
+
expect(el.open).to.be.false;
|
|
654
|
+
});
|
|
655
|
+
it("scrolls selected into view on open", async () => {
|
|
656
|
+
await el.generatePopover();
|
|
657
|
+
el.popover.style.height = "40px";
|
|
658
|
+
const firstItem = el.querySelector("sp-menu-item:first-child");
|
|
659
|
+
const lastItem = el.querySelector("sp-menu-item:last-child");
|
|
660
|
+
lastItem.disabled = false;
|
|
661
|
+
el.value = lastItem.value;
|
|
662
|
+
await elementUpdated(el);
|
|
663
|
+
el.open = true;
|
|
664
|
+
await elementUpdated(el);
|
|
665
|
+
await waitUntil(() => isMenuActiveElement(), "first item focused");
|
|
666
|
+
const getParentOffset = (el2) => {
|
|
667
|
+
const parentScroll = el2.parentElement.scrollTop;
|
|
668
|
+
const parentOffset = el2.offsetTop - parentScroll;
|
|
669
|
+
return parentOffset;
|
|
670
|
+
};
|
|
671
|
+
expect(getParentOffset(lastItem)).to.be.lessThan(40);
|
|
672
|
+
expect(getParentOffset(firstItem)).to.be.lessThan(-1);
|
|
673
|
+
lastItem.dispatchEvent(new FocusEvent("focusin", { bubbles: true }));
|
|
674
|
+
lastItem.dispatchEvent(arrowDownEvent());
|
|
675
|
+
await elementUpdated(el);
|
|
676
|
+
await nextFrame();
|
|
677
|
+
expect(getParentOffset(lastItem)).to.be.greaterThan(40);
|
|
678
|
+
expect(getParentOffset(firstItem)).to.be.greaterThan(-1);
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
describe("grouped", async () => {
|
|
682
|
+
const groupedFixture = async () => {
|
|
683
|
+
return fixture(html`
|
|
684
|
+
<sp-picker
|
|
685
|
+
quiet
|
|
686
|
+
label="I would like to use Spectrum Web Components"
|
|
687
|
+
value="0"
|
|
688
|
+
>
|
|
689
|
+
<sp-menu-group>
|
|
690
|
+
<span slot="header">Timeline</span>
|
|
691
|
+
<sp-menu-item value="0" id="should-be-selected">
|
|
692
|
+
Immediately
|
|
693
|
+
</sp-menu-item>
|
|
694
|
+
<sp-menu-item value="1">
|
|
695
|
+
I'm already using them
|
|
696
|
+
</sp-menu-item>
|
|
697
|
+
<sp-menu-divider></sp-menu-divider>
|
|
698
|
+
<sp-menu-item value="2">Soon</sp-menu-item>
|
|
699
|
+
<sp-menu-item value="3">
|
|
700
|
+
As part of my next project
|
|
701
|
+
</sp-menu-item>
|
|
702
|
+
<sp-menu-item value="4">In the future</sp-menu-item>
|
|
703
|
+
</sp-menu-group>
|
|
704
|
+
</sp-picker>
|
|
705
|
+
`);
|
|
48
706
|
};
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const closed = oneEvent(el, 'sp-closed');
|
|
57
|
-
el.open = false;
|
|
58
|
-
await closed;
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
it('loads accessibly', async () => {
|
|
62
|
-
await expect(el).to.be.accessible();
|
|
63
|
-
});
|
|
64
|
-
it('closes accessibly', async () => {
|
|
65
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
66
|
-
el.open = true;
|
|
67
|
-
await opened;
|
|
68
|
-
expect(el.open).to.be.true;
|
|
69
|
-
const accessibleCloseButton = document.querySelector('.visually-hidden button');
|
|
70
|
-
const closed = oneEvent(el, 'sp-closed');
|
|
71
|
-
accessibleCloseButton.click();
|
|
72
|
-
await closed;
|
|
73
|
-
expect(el.open).to.be.false;
|
|
74
|
-
});
|
|
75
|
-
it('accepts new selected item content', async () => {
|
|
76
|
-
const option2 = el.querySelector('[value="option-2"');
|
|
77
|
-
el.value = 'option-2';
|
|
78
|
-
await elementUpdated(option2);
|
|
79
|
-
await elementUpdated(el);
|
|
80
|
-
expect(el.value).to.equal('option-2');
|
|
81
|
-
expect((el.button.textContent || '').trim()).to.equal('Select Inverse');
|
|
82
|
-
let itemUpdated = oneEvent(el, 'sp-menu-item-added-or-updated');
|
|
83
|
-
const newLabel1 = 'Invert Selection';
|
|
84
|
-
option2.innerHTML = newLabel1;
|
|
85
|
-
await itemUpdated;
|
|
86
|
-
await elementUpdated(el);
|
|
87
|
-
expect(el.value).to.equal('option-2');
|
|
88
|
-
expect((el.button.textContent || '').trim()).to.equal(newLabel1);
|
|
89
|
-
itemUpdated = oneEvent(el, 'sp-menu-item-added-or-updated');
|
|
90
|
-
const newLabel2 = 'Other option';
|
|
91
|
-
option2.childNodes[0].textContent = newLabel2;
|
|
92
|
-
await itemUpdated;
|
|
93
|
-
await elementUpdated(el);
|
|
94
|
-
expect(el.value).to.equal('option-2');
|
|
95
|
-
expect((el.button.textContent || '').trim()).to.equal(newLabel2);
|
|
96
|
-
});
|
|
97
|
-
it('accepts new selected item content when open', async () => {
|
|
98
|
-
const option2 = el.querySelector('[value="option-2"');
|
|
99
|
-
el.value = 'option-2';
|
|
100
|
-
await elementUpdated(el);
|
|
101
|
-
expect(el.value).to.equal('option-2');
|
|
102
|
-
expect((el.button.textContent || '').trim()).to.equal('Select Inverse');
|
|
103
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
104
|
-
el.open = true;
|
|
105
|
-
await opened;
|
|
106
|
-
const itemUpdated = oneEvent(option2, 'sp-menu-item-added-or-updated');
|
|
107
|
-
option2.innerHTML = 'Invert Selection';
|
|
108
|
-
await itemUpdated;
|
|
109
|
-
await elementUpdated(el);
|
|
110
|
-
expect(el.value).to.equal('option-2');
|
|
111
|
-
expect((el.button.textContent || '').trim()).to.equal('Invert Selection');
|
|
112
|
-
});
|
|
113
|
-
it('unsets value when children removed', async () => {
|
|
114
|
-
el.value = 'option-2';
|
|
115
|
-
await elementUpdated(el);
|
|
116
|
-
expect(el.value).to.equal('option-2');
|
|
117
|
-
expect((el.button.textContent || '').trim()).to.equal('Select Inverse');
|
|
118
|
-
const items = el.querySelectorAll('sp-menu-item');
|
|
119
|
-
const removals = [];
|
|
120
|
-
items.forEach((item) => {
|
|
121
|
-
const removal = oneEvent(el, 'sp-menu-item-removed');
|
|
122
|
-
item.remove();
|
|
123
|
-
removals.push(removal);
|
|
124
|
-
});
|
|
125
|
-
await Promise.all(removals);
|
|
126
|
-
await elementUpdated(el);
|
|
127
|
-
expect(el.value).to.equal('');
|
|
128
|
-
expect((el.button.textContent || '').trim()).to.equal('');
|
|
129
|
-
});
|
|
130
|
-
it('accepts a new item and value at the same time', async () => {
|
|
131
|
-
el.value = 'option-2';
|
|
132
|
-
await elementUpdated(el);
|
|
133
|
-
expect(el.value).to.equal('option-2');
|
|
134
|
-
const item = document.createElement('sp-menu-item');
|
|
135
|
-
item.value = 'option-new';
|
|
136
|
-
item.textContent = 'New Option';
|
|
137
|
-
el.append(item);
|
|
138
|
-
await elementUpdated(el);
|
|
139
|
-
el.value = 'option-new';
|
|
140
|
-
await elementUpdated(el);
|
|
141
|
-
expect(el.value).to.equal('option-new');
|
|
142
|
-
});
|
|
143
|
-
it('accepts a new item that can be selected', async () => {
|
|
144
|
-
el.value = 'option-2';
|
|
145
|
-
await elementUpdated(el);
|
|
146
|
-
expect(el.value).to.equal('option-2');
|
|
147
|
-
const item = document.createElement('sp-menu-item');
|
|
148
|
-
item.value = 'option-new';
|
|
149
|
-
item.textContent = 'New Option';
|
|
150
|
-
el.append(item);
|
|
151
|
-
await elementUpdated(item);
|
|
152
|
-
await elementUpdated(el);
|
|
153
|
-
let opened = oneEvent(el, 'sp-opened');
|
|
154
|
-
el.open = true;
|
|
155
|
-
await opened;
|
|
156
|
-
// Overlaid content is outside of the context of the Picker element
|
|
157
|
-
// and cannot be managed via its updateComplete cycle.
|
|
158
|
-
await nextFrame();
|
|
159
|
-
const close = oneEvent(el, 'sp-closed');
|
|
160
|
-
item.click();
|
|
161
|
-
await close;
|
|
162
|
-
// Overlaid content is outside of the context of the Picker element
|
|
163
|
-
// and cannot be managed via its updateComplete cycle.
|
|
164
|
-
await nextFrame();
|
|
165
|
-
expect(el.value, 'first time').to.equal('option-new');
|
|
166
|
-
opened = oneEvent(el, 'sp-opened');
|
|
167
|
-
el.open = true;
|
|
168
|
-
await opened;
|
|
169
|
-
// Overlaid content is outside of the context of the Picker element
|
|
170
|
-
// and cannot be managed via its updateComplete cycle.
|
|
171
|
-
await nextFrame();
|
|
172
|
-
expect(el.value, 'second time').to.equal('option-new');
|
|
173
|
-
});
|
|
174
|
-
it('manages its "name" value in the accessibility tree', async () => {
|
|
175
|
-
let snapshot = (await a11ySnapshot({}));
|
|
176
|
-
expect(findAccessibilityNode(snapshot, (node) => node.name === 'Where do you live?'), '`name` is the label text').to.not.be.null;
|
|
177
|
-
el.value = 'option-2';
|
|
178
|
-
await elementUpdated(el);
|
|
179
|
-
snapshot = (await a11ySnapshot({}));
|
|
180
|
-
expect(findAccessibilityNode(snapshot, (node) => node.name === 'Where do you live? Select Inverse'), '`name` is the label text plus the selected item text').to.not.be.null;
|
|
181
|
-
});
|
|
182
|
-
it('manages `aria-activedescendant`', async () => {
|
|
183
|
-
const firstItem = el.querySelector('sp-menu-item:nth-child(1)');
|
|
184
|
-
const secondItem = el.querySelector('sp-menu-item:nth-child(2)');
|
|
185
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
186
|
-
el.open = true;
|
|
187
|
-
await opened;
|
|
188
|
-
expect(el.optionsMenu.getAttribute('aria-activedescendant')).to.equal(firstItem === null || firstItem === void 0 ? void 0 : firstItem.id);
|
|
189
|
-
await sendKeys({ press: 'ArrowDown' });
|
|
190
|
-
await elementUpdated(el);
|
|
191
|
-
expect(el.optionsMenu.getAttribute('aria-activedescendant')).to.equal(secondItem === null || secondItem === void 0 ? void 0 : secondItem.id);
|
|
192
|
-
});
|
|
193
|
-
it('renders invalid accessibly', async () => {
|
|
194
|
-
el.invalid = true;
|
|
195
|
-
await elementUpdated(el);
|
|
196
|
-
expect(el.invalid).to.be.true;
|
|
197
|
-
await expect(el).to.be.accessible();
|
|
198
|
-
});
|
|
199
|
-
it('renders selection accessibly', async () => {
|
|
200
|
-
el.value = 'option-2';
|
|
201
|
-
await elementUpdated(el);
|
|
202
|
-
await expect(el).to.be.accessible();
|
|
203
|
-
});
|
|
204
|
-
it('opens with visible focus on a menu item on `DownArrow`', async () => {
|
|
205
|
-
const firstItem = el.querySelector('sp-menu-item');
|
|
206
|
-
await elementUpdated(el);
|
|
207
|
-
expect(firstItem.focused, 'should not visually focused').to.be
|
|
208
|
-
.false;
|
|
209
|
-
el.focus();
|
|
210
|
-
await elementUpdated(el);
|
|
211
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
212
|
-
await sendKeys({ press: 'ArrowRight' });
|
|
213
|
-
await sendKeys({ press: 'ArrowLeft' });
|
|
214
|
-
await sendKeys({ press: 'ArrowDown' });
|
|
215
|
-
await opened;
|
|
216
|
-
expect(el.open).to.be.true;
|
|
217
|
-
expect(firstItem.focused, 'should be visually focused').to.be.true;
|
|
218
|
-
const closed = oneEvent(el, 'sp-closed');
|
|
219
|
-
await sendKeys({
|
|
220
|
-
press: 'Escape',
|
|
221
|
-
});
|
|
222
|
-
await closed;
|
|
223
|
-
expect(el.open).to.be.false;
|
|
224
|
-
await waitUntil(() => !firstItem.focused, 'not visually focused');
|
|
225
|
-
});
|
|
226
|
-
it('opens without visible focus on a menu item on click', async () => {
|
|
227
|
-
const firstItem = el.querySelector('sp-menu-item');
|
|
228
|
-
await elementUpdated(el);
|
|
229
|
-
const boundingRect = el.getBoundingClientRect();
|
|
230
|
-
expect(firstItem.focused, 'not visually focused').to.be.false;
|
|
231
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
232
|
-
sendMouse({
|
|
233
|
-
steps: [
|
|
234
|
-
{
|
|
235
|
-
type: 'click',
|
|
236
|
-
position: [
|
|
237
|
-
boundingRect.x + boundingRect.width / 2,
|
|
238
|
-
boundingRect.y + boundingRect.height / 2,
|
|
239
|
-
],
|
|
240
|
-
},
|
|
241
|
-
],
|
|
242
|
-
});
|
|
243
|
-
await opened;
|
|
244
|
-
await elementUpdated(el);
|
|
245
|
-
expect(el.open).to.be.true;
|
|
246
|
-
expect(firstItem.focused, 'still not visually focused').to.be.false;
|
|
247
|
-
});
|
|
248
|
-
it('closes when becoming disabled', async () => {
|
|
249
|
-
expect(el.open).to.be.false;
|
|
250
|
-
el.click();
|
|
251
|
-
await elementUpdated(el);
|
|
252
|
-
expect(el.open).to.be.true;
|
|
253
|
-
el.disabled = true;
|
|
254
|
-
await elementUpdated(el);
|
|
255
|
-
expect(el.open).to.be.false;
|
|
256
|
-
});
|
|
257
|
-
it('closes when clicking away', async () => {
|
|
258
|
-
el.id = 'closing';
|
|
259
|
-
const other = document.createElement('div');
|
|
260
|
-
document.body.append(other);
|
|
261
|
-
await elementUpdated(el);
|
|
262
|
-
expect(el.open).to.be.false;
|
|
263
|
-
el.click();
|
|
264
|
-
await elementUpdated(el);
|
|
265
|
-
expect(el.open).to.be.true;
|
|
266
|
-
other.click();
|
|
267
|
-
await waitUntil(() => !el.open, 'closed');
|
|
268
|
-
other.remove();
|
|
269
|
-
});
|
|
270
|
-
it('selects', async () => {
|
|
271
|
-
var _a, _b;
|
|
272
|
-
const secondItem = el.querySelector('sp-menu-item:nth-of-type(2)');
|
|
273
|
-
const button = el.button;
|
|
274
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
275
|
-
button.click();
|
|
276
|
-
await opened;
|
|
277
|
-
await elementUpdated(el);
|
|
278
|
-
expect(el.open).to.be.true;
|
|
279
|
-
expect((_a = el.selectedItem) === null || _a === void 0 ? void 0 : _a.itemText).to.be.undefined;
|
|
280
|
-
expect(el.value).to.equal('');
|
|
281
|
-
const closed = oneEvent(el, 'sp-closed');
|
|
282
|
-
secondItem.click();
|
|
283
|
-
await closed;
|
|
284
|
-
expect(el.open).to.be.false;
|
|
285
|
-
expect((_b = el.selectedItem) === null || _b === void 0 ? void 0 : _b.itemText).to.equal('Select Inverse');
|
|
286
|
-
expect(el.value).to.equal('option-2');
|
|
287
|
-
});
|
|
288
|
-
it('re-selects', async () => {
|
|
289
|
-
var _a, _b, _c, _d;
|
|
290
|
-
const firstItem = el.querySelector('sp-menu-item:nth-of-type(1)');
|
|
291
|
-
const secondItem = el.querySelector('sp-menu-item:nth-of-type(2)');
|
|
292
|
-
const button = el.button;
|
|
293
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
294
|
-
button.click();
|
|
295
|
-
await opened;
|
|
296
|
-
await nextFrame();
|
|
297
|
-
expect(el.open).to.be.true;
|
|
298
|
-
expect((_a = el.selectedItem) === null || _a === void 0 ? void 0 : _a.itemText).to.be.undefined;
|
|
299
|
-
expect(el.value).to.equal('');
|
|
300
|
-
const closed = oneEvent(el, 'sp-closed');
|
|
301
|
-
secondItem.click();
|
|
302
|
-
await closed;
|
|
303
|
-
await nextFrame();
|
|
304
|
-
expect(el.open).to.be.false;
|
|
305
|
-
expect((_b = el.selectedItem) === null || _b === void 0 ? void 0 : _b.itemText).to.equal('Select Inverse');
|
|
306
|
-
expect(el.value).to.equal('option-2');
|
|
307
|
-
const opened2 = oneEvent(el, 'sp-opened');
|
|
308
|
-
button.click();
|
|
309
|
-
await opened2;
|
|
310
|
-
await nextFrame();
|
|
311
|
-
expect(el.open).to.be.true;
|
|
312
|
-
expect((_c = el.selectedItem) === null || _c === void 0 ? void 0 : _c.itemText).to.equal('Select Inverse');
|
|
313
|
-
expect(el.value).to.equal('option-2');
|
|
314
|
-
const closed2 = oneEvent(el, 'sp-closed');
|
|
315
|
-
firstItem.click();
|
|
316
|
-
await closed2;
|
|
317
|
-
await nextFrame();
|
|
318
|
-
expect(el.open).to.be.false;
|
|
319
|
-
expect((_d = el.selectedItem) === null || _d === void 0 ? void 0 : _d.itemText).to.equal('Deselect');
|
|
320
|
-
expect(el.value).to.equal('Deselect');
|
|
321
|
-
});
|
|
322
|
-
it('dispatches bubbling and composed events', async () => {
|
|
323
|
-
const changeSpy = spy();
|
|
324
|
-
const parent = el.parentElement;
|
|
325
|
-
parent.attachShadow({ mode: 'open' });
|
|
326
|
-
parent.shadowRoot.append(el);
|
|
327
|
-
const secondItem = el.querySelector('sp-menu-item:nth-of-type(2)');
|
|
328
|
-
parent.addEventListener('change', () => changeSpy());
|
|
329
|
-
expect(el.value).to.equal('');
|
|
330
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
331
|
-
el.open = true;
|
|
332
|
-
await opened;
|
|
333
|
-
await elementUpdated(el);
|
|
334
|
-
const closed = oneEvent(el, 'sp-closed');
|
|
335
|
-
secondItem.click();
|
|
336
|
-
await closed;
|
|
337
|
-
await elementUpdated(el);
|
|
338
|
-
expect(el.value).to.equal(secondItem.value);
|
|
339
|
-
expect(changeSpy.calledOnce).to.be.true;
|
|
340
|
-
});
|
|
341
|
-
it('can have selection prevented', async () => {
|
|
342
|
-
var _a;
|
|
343
|
-
const preventChangeSpy = spy();
|
|
344
|
-
const secondItem = el.querySelector('sp-menu-item:nth-of-type(2)');
|
|
345
|
-
const button = el.button;
|
|
346
|
-
let opened = oneEvent(el, 'sp-opened');
|
|
347
|
-
button.click();
|
|
348
|
-
await opened;
|
|
349
|
-
await elementUpdated(el);
|
|
350
|
-
expect(el.open).to.be.true;
|
|
351
|
-
expect((_a = el.selectedItem) === null || _a === void 0 ? void 0 : _a.itemText).to.be.undefined;
|
|
352
|
-
expect(el.value).to.equal('');
|
|
353
|
-
expect(secondItem.selected).to.be.false;
|
|
354
|
-
el.addEventListener('change', (event) => {
|
|
355
|
-
event.preventDefault();
|
|
356
|
-
preventChangeSpy();
|
|
357
|
-
});
|
|
358
|
-
const closed = oneEvent(el, 'sp-closed');
|
|
359
|
-
opened = oneEvent(el, 'sp-opened');
|
|
360
|
-
secondItem.click();
|
|
361
|
-
await closed;
|
|
362
|
-
await opened;
|
|
363
|
-
await elementUpdated(el);
|
|
364
|
-
expect(preventChangeSpy.calledOnce).to.be.true;
|
|
365
|
-
expect(secondItem.selected, 'selection prevented').to.be.false;
|
|
366
|
-
});
|
|
367
|
-
it('can throw focus after `change`', async () => {
|
|
368
|
-
var _a;
|
|
369
|
-
const input = document.createElement('input');
|
|
370
|
-
document.body.append(input);
|
|
371
|
-
await elementUpdated(el);
|
|
372
|
-
const secondItem = el.querySelector('sp-menu-item:nth-of-type(2)');
|
|
373
|
-
const button = el.button;
|
|
374
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
375
|
-
button.click();
|
|
376
|
-
await opened;
|
|
377
|
-
await elementUpdated(el);
|
|
378
|
-
expect(el.open).to.be.true;
|
|
379
|
-
expect((_a = el.selectedItem) === null || _a === void 0 ? void 0 : _a.itemText).to.be.undefined;
|
|
380
|
-
expect(el.value).to.equal('');
|
|
381
|
-
expect(secondItem.selected).to.be.false;
|
|
382
|
-
el.addEventListener('change', () => {
|
|
383
|
-
input.focus();
|
|
384
|
-
});
|
|
385
|
-
const closed = oneEvent(el, 'sp-closed');
|
|
386
|
-
secondItem.click();
|
|
387
|
-
await closed;
|
|
388
|
-
await elementUpdated(el);
|
|
389
|
-
expect(el.open).to.be.false;
|
|
390
|
-
expect(el.value, 'value changed').to.equal('option-2');
|
|
391
|
-
expect(secondItem.selected, 'selected changed').to.be.true;
|
|
392
|
-
await waitUntil(() => document.activeElement === input, 'focus throw');
|
|
393
|
-
input.remove();
|
|
394
|
-
});
|
|
395
|
-
it('opens on ArrowUp', async () => {
|
|
396
|
-
const button = el.button;
|
|
397
|
-
el.focus();
|
|
398
|
-
await elementUpdated(el);
|
|
399
|
-
expect(el.open, 'inially closed').to.be.false;
|
|
400
|
-
button.dispatchEvent(tEvent());
|
|
401
|
-
await elementUpdated(el);
|
|
402
|
-
expect(el.open, 'still closed').to.be.false;
|
|
403
|
-
button.dispatchEvent(arrowUpEvent());
|
|
404
|
-
await elementUpdated(el);
|
|
405
|
-
expect(el.open, 'open by ArrowUp').to.be.true;
|
|
406
|
-
await waitUntil(() => document.querySelector('active-overlay') !== null, 'an active-overlay has been inserted on the page');
|
|
407
|
-
button.dispatchEvent(new KeyboardEvent('keyup', {
|
|
408
|
-
bubbles: true,
|
|
409
|
-
composed: true,
|
|
410
|
-
cancelable: true,
|
|
411
|
-
key: 'Escape',
|
|
412
|
-
code: 'Escape',
|
|
413
|
-
}));
|
|
414
|
-
await elementUpdated(el);
|
|
415
|
-
await waitUntil(() => el.open === false, 'closed by Escape');
|
|
416
|
-
await waitUntil(() => document.querySelector('active-overlay') === null, 'an active-overlay has been inserted on the page');
|
|
417
|
-
});
|
|
418
|
-
it('opens on ArrowDown', async () => {
|
|
419
|
-
var _a, _b;
|
|
420
|
-
const firstItem = el.querySelector('sp-menu-item:nth-of-type(1)');
|
|
421
|
-
const button = el.button;
|
|
422
|
-
el.focus();
|
|
423
|
-
await elementUpdated(el);
|
|
424
|
-
expect(el.open, 'inially closed').to.be.false;
|
|
425
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
426
|
-
button.dispatchEvent(arrowDownEvent());
|
|
427
|
-
await opened;
|
|
428
|
-
await elementUpdated(el);
|
|
429
|
-
expect(el.open, 'open by ArrowDown').to.be.true;
|
|
430
|
-
expect((_a = el.selectedItem) === null || _a === void 0 ? void 0 : _a.itemText).to.be.undefined;
|
|
431
|
-
expect(el.value).to.equal('');
|
|
432
|
-
const closed = oneEvent(el, 'sp-closed');
|
|
433
|
-
firstItem.click();
|
|
434
|
-
await closed;
|
|
435
|
-
await elementUpdated(el);
|
|
436
|
-
expect(el.open).to.be.false;
|
|
437
|
-
expect((_b = el.selectedItem) === null || _b === void 0 ? void 0 : _b.itemText).to.equal('Deselect');
|
|
438
|
-
expect(el.value).to.equal('Deselect');
|
|
439
|
-
});
|
|
440
|
-
it('quick selects on ArrowLeft/Right', async () => {
|
|
441
|
-
const selectionSpy = spy();
|
|
442
|
-
el.addEventListener('change', (event) => {
|
|
443
|
-
const { value } = event.target;
|
|
444
|
-
selectionSpy(value);
|
|
445
|
-
});
|
|
446
|
-
const button = el.button;
|
|
447
|
-
el.focus();
|
|
448
|
-
button.dispatchEvent(arrowLeftEvent());
|
|
449
|
-
await elementUpdated(el);
|
|
450
|
-
expect(selectionSpy.callCount).to.equal(1);
|
|
451
|
-
expect(selectionSpy.calledWith('Deselected'));
|
|
452
|
-
button.dispatchEvent(arrowLeftEvent());
|
|
453
|
-
await elementUpdated(el);
|
|
454
|
-
expect(selectionSpy.callCount).to.equal(1);
|
|
455
|
-
button.dispatchEvent(arrowRightEvent());
|
|
456
|
-
await elementUpdated(el);
|
|
457
|
-
expect(selectionSpy.calledWith('option-2'));
|
|
458
|
-
button.dispatchEvent(arrowRightEvent());
|
|
459
|
-
button.dispatchEvent(arrowRightEvent());
|
|
460
|
-
button.dispatchEvent(arrowRightEvent());
|
|
461
|
-
button.dispatchEvent(arrowRightEvent());
|
|
462
|
-
await elementUpdated(el);
|
|
463
|
-
expect(selectionSpy.callCount).to.equal(5);
|
|
464
|
-
expect(selectionSpy.calledWith('Save Selection'));
|
|
465
|
-
expect(selectionSpy.calledWith('Make Work Path')).to.be.false;
|
|
466
|
-
});
|
|
467
|
-
it('quick selects first item on ArrowRight when no value', async () => {
|
|
468
|
-
const selectionSpy = spy();
|
|
469
|
-
el.addEventListener('change', (event) => {
|
|
470
|
-
const { value } = event.target;
|
|
471
|
-
selectionSpy(value);
|
|
472
|
-
});
|
|
473
|
-
const button = el.button;
|
|
474
|
-
el.focus();
|
|
475
|
-
button.dispatchEvent(arrowRightEvent());
|
|
476
|
-
await elementUpdated(el);
|
|
477
|
-
expect(selectionSpy.callCount).to.equal(1);
|
|
478
|
-
expect(selectionSpy.calledWith('Deselected'));
|
|
479
|
-
});
|
|
480
|
-
it('loads', async () => {
|
|
481
|
-
expect(el).to.not.be.undefined;
|
|
482
|
-
});
|
|
483
|
-
it('refocuses on list when open', async () => {
|
|
484
|
-
const firstItem = el.querySelector('sp-menu-item');
|
|
485
|
-
const input = document.createElement('input');
|
|
486
|
-
el.insertAdjacentElement('afterend', input);
|
|
487
|
-
el.focus();
|
|
488
|
-
await sendKeys({ press: 'Tab' });
|
|
489
|
-
expect(document.activeElement === input).to.be.true;
|
|
490
|
-
await sendKeys({ press: 'Shift+Tab' });
|
|
491
|
-
expect(document.activeElement === el).to.be.true;
|
|
492
|
-
await sendKeys({ press: 'Enter' });
|
|
493
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
494
|
-
el.open = true;
|
|
495
|
-
await opened;
|
|
496
|
-
await elementUpdated(el);
|
|
497
|
-
await waitUntil(() => firstItem.focused, 'The first items should have become focused visually.');
|
|
498
|
-
el.blur();
|
|
499
|
-
await elementUpdated(el);
|
|
500
|
-
expect(el.open).to.be.true;
|
|
501
|
-
el.focus();
|
|
502
|
-
await elementUpdated(el);
|
|
503
|
-
await waitUntil(() => isMenuActiveElement(), 'first item refocused');
|
|
504
|
-
expect(el.open).to.be.true;
|
|
505
|
-
expect(isMenuActiveElement()).to.be.true;
|
|
506
|
-
// Force :focus-visible heuristic
|
|
507
|
-
await sendKeys({ press: 'ArrowDown' });
|
|
508
|
-
await sendKeys({ press: 'ArrowUp' });
|
|
509
|
-
expect(firstItem.focused).to.be.true;
|
|
510
|
-
});
|
|
511
|
-
it('does not allow tabing to close', async () => {
|
|
512
|
-
el.open = true;
|
|
513
|
-
await elementUpdated(el);
|
|
514
|
-
expect(el.open).to.be.true;
|
|
515
|
-
el.focus();
|
|
516
|
-
await elementUpdated(el);
|
|
517
|
-
await waitUntil(() => isMenuActiveElement(), 'first item refocused');
|
|
518
|
-
expect(el.open).to.be.true;
|
|
519
|
-
expect(isMenuActiveElement()).to.be.true;
|
|
520
|
-
await sendKeys({ press: 'Tab' });
|
|
521
|
-
expect(el.open, 'stays open').to.be.true;
|
|
522
|
-
});
|
|
523
|
-
describe('tab order', () => {
|
|
524
|
-
let input1;
|
|
525
|
-
let input2;
|
|
526
|
-
beforeEach(() => {
|
|
527
|
-
const surroundingInput = () => {
|
|
528
|
-
const input = document.createElement('input');
|
|
529
|
-
input.type = 'text';
|
|
530
|
-
input.tabIndex = 0;
|
|
531
|
-
return input;
|
|
532
|
-
};
|
|
533
|
-
input1 = surroundingInput();
|
|
534
|
-
input2 = surroundingInput();
|
|
535
|
-
el.insertAdjacentElement('beforebegin', input1);
|
|
536
|
-
el.insertAdjacentElement('afterend', input2);
|
|
537
|
-
});
|
|
538
|
-
afterEach(() => {
|
|
539
|
-
input1.remove();
|
|
540
|
-
input2.remove();
|
|
541
|
-
});
|
|
542
|
-
it('tabs forward through the element', async () => {
|
|
543
|
-
// start at input1
|
|
544
|
-
input1.focus();
|
|
545
|
-
await nextFrame();
|
|
546
|
-
expect(document.activeElement === input1, 'focuses input 1').to
|
|
547
|
-
.true;
|
|
548
|
-
// tab to the picker
|
|
549
|
-
let focused = oneEvent(el, 'focus');
|
|
550
|
-
await sendKeys({ press: 'Tab' });
|
|
551
|
-
await focused;
|
|
552
|
-
expect(el.focused, 'focused').to.be.true;
|
|
553
|
-
expect(el.open, 'closed').to.be.false;
|
|
554
|
-
expect(document.activeElement === el, 'focuses el').to.be.true;
|
|
555
|
-
// tab through the picker to input2
|
|
556
|
-
focused = oneEvent(input2, 'focus');
|
|
557
|
-
await sendKeys({ press: 'Tab' });
|
|
558
|
-
await focused;
|
|
559
|
-
expect(document.activeElement === input2, 'focuses input 2').to
|
|
560
|
-
.true;
|
|
561
|
-
});
|
|
562
|
-
it('shift+tabs backwards through the element', async () => {
|
|
563
|
-
// start at input1
|
|
564
|
-
input2.focus();
|
|
565
|
-
await nextFrame();
|
|
566
|
-
expect(document.activeElement, 'focuses input 2').to.equal(input2);
|
|
567
|
-
// tab to the picker
|
|
568
|
-
let focused = oneEvent(el, 'focus');
|
|
569
|
-
await sendKeys({ press: 'Shift+Tab' });
|
|
570
|
-
await focused;
|
|
571
|
-
expect(el.focused, 'focused').to.be.true;
|
|
572
|
-
expect(el.open, 'closed').to.be.false;
|
|
573
|
-
expect(document.activeElement, 'focuses el').to.equal(el);
|
|
574
|
-
// tab through the picker to input2
|
|
575
|
-
focused = oneEvent(input1, 'focus');
|
|
576
|
-
await sendKeys({ press: 'Shift+Tab' });
|
|
577
|
-
await focused;
|
|
578
|
-
expect(document.activeElement, 'focuses input 1').to.equal(input1);
|
|
579
|
-
});
|
|
580
|
-
it('traps tab in the menu as a `type="modal"` overlay forward', async () => {
|
|
581
|
-
el.focus();
|
|
582
|
-
await nextFrame();
|
|
583
|
-
expect(document.activeElement, 'focuses el').to.equal(el);
|
|
584
|
-
// press down to open the picker
|
|
585
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
586
|
-
await sendKeys({ press: 'ArrowDown' });
|
|
587
|
-
await opened;
|
|
588
|
-
expect(el.open, 'opened').to.be.true;
|
|
589
|
-
await waitUntil(() => isMenuActiveElement(), 'first item focused');
|
|
590
|
-
const activeElement = document.activeElement;
|
|
591
|
-
const blured = oneEvent(activeElement, 'blur');
|
|
592
|
-
await sendKeys({ press: 'Tab' });
|
|
593
|
-
await blured;
|
|
594
|
-
expect(el.open).to.be.true;
|
|
595
|
-
expect(document.activeElement === input1).to.be.false;
|
|
596
|
-
expect(document.activeElement === input2).to.be.false;
|
|
597
|
-
});
|
|
598
|
-
it('traps tab in the menu as a `type="modal"` overlay backwards', async () => {
|
|
599
|
-
el.focus();
|
|
600
|
-
await nextFrame();
|
|
601
|
-
expect(document.activeElement, 'focuses el').to.equal(el);
|
|
602
|
-
// press down to open the picker
|
|
603
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
604
|
-
await sendKeys({ press: 'ArrowDown' });
|
|
605
|
-
await opened;
|
|
606
|
-
expect(el.open, 'opened').to.be.true;
|
|
607
|
-
await waitUntil(() => isMenuActiveElement(), 'first item focused');
|
|
608
|
-
const activeElement = document.activeElement;
|
|
609
|
-
const blured = oneEvent(activeElement, 'blur');
|
|
610
|
-
await sendKeys({ press: 'Shift+Tab' });
|
|
611
|
-
await blured;
|
|
612
|
-
expect(el.open).to.be.true;
|
|
613
|
-
expect(document.activeElement === input1).to.be.false;
|
|
614
|
-
expect(document.activeElement === input2).to.be.false;
|
|
615
|
-
});
|
|
616
|
-
it('can close and immediate tab to the next tab stop', async () => {
|
|
617
|
-
el.focus();
|
|
618
|
-
await nextFrame();
|
|
619
|
-
expect(document.activeElement, 'focuses el').to.equal(el);
|
|
620
|
-
// press down to open the picker
|
|
621
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
622
|
-
await sendKeys({ press: 'ArrowUp' });
|
|
623
|
-
await opened;
|
|
624
|
-
expect(el.open, 'opened').to.be.true;
|
|
625
|
-
await waitUntil(() => isMenuActiveElement(), 'first item focused');
|
|
626
|
-
const closed = oneEvent(el, 'sp-closed');
|
|
627
|
-
el.open = false;
|
|
628
|
-
await closed;
|
|
629
|
-
expect(el.open).to.be.false;
|
|
630
|
-
expect(document.activeElement === el).to.be.true;
|
|
631
|
-
const focused = oneEvent(input2, 'focus');
|
|
632
|
-
await sendKeys({ press: 'Tab' });
|
|
633
|
-
await focused;
|
|
634
|
-
expect(el.open).to.be.false;
|
|
635
|
-
expect(document.activeElement === input2).to.be.true;
|
|
636
|
-
});
|
|
637
|
-
it('can close and immediate shift+tab to the previous tab stop', async () => {
|
|
638
|
-
el.focus();
|
|
639
|
-
await nextFrame();
|
|
640
|
-
expect(document.activeElement, 'focuses el').to.equal(el);
|
|
641
|
-
// press down to open the picker
|
|
642
|
-
const opened = oneEvent(el, 'sp-opened');
|
|
643
|
-
await sendKeys({ press: 'ArrowUp' });
|
|
644
|
-
await opened;
|
|
645
|
-
expect(el.open, 'opened').to.be.true;
|
|
646
|
-
await waitUntil(() => isMenuActiveElement(), 'first item focused');
|
|
647
|
-
const closed = oneEvent(el, 'sp-closed');
|
|
648
|
-
el.open = false;
|
|
649
|
-
await closed;
|
|
650
|
-
expect(el.open).to.be.false;
|
|
651
|
-
expect(document.activeElement === el).to.be.true;
|
|
652
|
-
const focused = oneEvent(input1, 'focus');
|
|
653
|
-
await sendKeys({ press: 'Shift+Tab' });
|
|
654
|
-
await focused;
|
|
655
|
-
expect(el.open).to.be.false;
|
|
656
|
-
expect(document.activeElement === input1).to.be.true;
|
|
657
|
-
});
|
|
658
|
-
});
|
|
659
|
-
it('does not open when [readonly]', async () => {
|
|
660
|
-
el.readonly = true;
|
|
661
|
-
await elementUpdated(el);
|
|
662
|
-
const button = el.button;
|
|
663
|
-
button.click();
|
|
664
|
-
await elementUpdated(el);
|
|
665
|
-
expect(el.open).to.be.false;
|
|
666
|
-
});
|
|
667
|
-
it('scrolls selected into view on open', async () => {
|
|
668
|
-
await el.generatePopover();
|
|
669
|
-
el.popover.style.height =
|
|
670
|
-
'40px';
|
|
671
|
-
const firstItem = el.querySelector('sp-menu-item:first-child');
|
|
672
|
-
const lastItem = el.querySelector('sp-menu-item:last-child');
|
|
673
|
-
lastItem.disabled = false;
|
|
674
|
-
el.value = lastItem.value;
|
|
675
|
-
await elementUpdated(el);
|
|
676
|
-
el.open = true;
|
|
677
|
-
await elementUpdated(el);
|
|
678
|
-
await waitUntil(() => isMenuActiveElement(), 'first item focused');
|
|
679
|
-
const getParentOffset = (el) => {
|
|
680
|
-
const parentScroll = el.parentElement
|
|
681
|
-
.scrollTop;
|
|
682
|
-
const parentOffset = el.offsetTop - parentScroll;
|
|
683
|
-
return parentOffset;
|
|
684
|
-
};
|
|
685
|
-
expect(getParentOffset(lastItem)).to.be.lessThan(40);
|
|
686
|
-
expect(getParentOffset(firstItem)).to.be.lessThan(-1);
|
|
687
|
-
lastItem.dispatchEvent(new FocusEvent('focusin', { bubbles: true }));
|
|
688
|
-
lastItem.dispatchEvent(arrowDownEvent());
|
|
689
|
-
await elementUpdated(el);
|
|
690
|
-
await nextFrame();
|
|
691
|
-
expect(getParentOffset(lastItem)).to.be.greaterThan(40);
|
|
692
|
-
expect(getParentOffset(firstItem)).to.be.greaterThan(-1);
|
|
693
|
-
});
|
|
707
|
+
beforeEach(async () => {
|
|
708
|
+
el = await groupedFixture();
|
|
709
|
+
await elementUpdated(el);
|
|
710
|
+
});
|
|
711
|
+
it("selects the item with a matching value in a group", async () => {
|
|
712
|
+
const item = el.querySelector("#should-be-selected");
|
|
713
|
+
expect(item.selected).to.be.true;
|
|
694
714
|
});
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
715
|
+
});
|
|
716
|
+
describe("slotted label", () => {
|
|
717
|
+
const pickerFixture2 = async () => {
|
|
718
|
+
const test = await fixture(html`
|
|
698
719
|
<div>
|
|
699
720
|
<sp-field-label for="picker-slotted">
|
|
700
721
|
Where do you live?
|
|
@@ -716,26 +737,26 @@ export function runPickerTests() {
|
|
|
716
737
|
</sp-picker>
|
|
717
738
|
</div>
|
|
718
739
|
`);
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
});
|
|
740
|
+
return test.querySelector("sp-picker");
|
|
741
|
+
};
|
|
742
|
+
beforeEach(async () => {
|
|
743
|
+
el = await pickerFixture2();
|
|
744
|
+
await elementUpdated(el);
|
|
745
|
+
});
|
|
746
|
+
afterEach(async () => {
|
|
747
|
+
if (el.open) {
|
|
748
|
+
const closed = oneEvent(el, "sp-closed");
|
|
749
|
+
el.open = false;
|
|
750
|
+
await closed;
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
it("loads accessibly w/ slotted label", async () => {
|
|
754
|
+
await expect(el).to.be.accessible();
|
|
735
755
|
});
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
756
|
+
});
|
|
757
|
+
describe("deprecated", () => {
|
|
758
|
+
const pickerFixture2 = async () => {
|
|
759
|
+
const test = await fixture(html`
|
|
739
760
|
<div>
|
|
740
761
|
<sp-field-label for="picker-deprecated">
|
|
741
762
|
Where do you live?
|
|
@@ -760,85 +781,120 @@ export function runPickerTests() {
|
|
|
760
781
|
</sp-picker>
|
|
761
782
|
</div>
|
|
762
783
|
`);
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
secondItem.click();
|
|
788
|
-
await closed;
|
|
789
|
-
expect(el.open).to.be.false;
|
|
790
|
-
expect((_b = el.selectedItem) === null || _b === void 0 ? void 0 : _b.itemText).to.equal('Select Inverse');
|
|
791
|
-
expect(el.value).to.equal('option-2');
|
|
792
|
-
});
|
|
784
|
+
return test.querySelector("sp-picker");
|
|
785
|
+
};
|
|
786
|
+
describe("Dev mode", () => {
|
|
787
|
+
it("warns in Dev Mode of deprecated `<sp-menu>` usage", async () => {
|
|
788
|
+
const consoleWarnStub = stub(console, "warn");
|
|
789
|
+
el = await pickerFixture2();
|
|
790
|
+
await elementUpdated(el);
|
|
791
|
+
expect(consoleWarnStub.called).to.be.true;
|
|
792
|
+
const spyCall = consoleWarnStub.getCall(0);
|
|
793
|
+
expect(spyCall.args.at(0).includes("<sp-menu>"), "confirm <sp-menu>-centric message").to.be.true;
|
|
794
|
+
expect(spyCall.args.at(-1), "confirm `data` shape").to.deep.equal({
|
|
795
|
+
data: {
|
|
796
|
+
localName: "sp-picker",
|
|
797
|
+
type: "api",
|
|
798
|
+
level: "deprecation"
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
consoleWarnStub.restore();
|
|
802
|
+
if (el.open) {
|
|
803
|
+
const closed = oneEvent(el, "sp-closed");
|
|
804
|
+
el.open = false;
|
|
805
|
+
await closed;
|
|
806
|
+
}
|
|
807
|
+
});
|
|
793
808
|
});
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
809
|
+
describe("Dev mode ignored", () => {
|
|
810
|
+
const { ignoreWarningLocalNames } = window.__swc;
|
|
811
|
+
before(() => {
|
|
812
|
+
window.__swc.ignoreWarningLocalNames = {
|
|
813
|
+
"sp-picker": true
|
|
814
|
+
};
|
|
815
|
+
});
|
|
816
|
+
before(() => {
|
|
817
|
+
window.__swc.ignoreWarningLocalNames = ignoreWarningLocalNames;
|
|
818
|
+
});
|
|
819
|
+
beforeEach(async () => {
|
|
820
|
+
el = await pickerFixture2();
|
|
799
821
|
await elementUpdated(el);
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
el.
|
|
822
|
+
});
|
|
823
|
+
afterEach(async () => {
|
|
824
|
+
if (el.open) {
|
|
825
|
+
const closed = oneEvent(el, "sp-closed");
|
|
826
|
+
el.open = false;
|
|
827
|
+
await closed;
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
it("selects with deprecated syntax", async () => {
|
|
831
|
+
var _a, _b;
|
|
832
|
+
const secondItem = el.querySelector("sp-menu-item:nth-of-type(2)");
|
|
833
|
+
const opened = oneEvent(el, "sp-opened");
|
|
834
|
+
el.button.click();
|
|
835
|
+
await opened;
|
|
803
836
|
await elementUpdated(el);
|
|
804
|
-
|
|
805
|
-
expect(
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
const el1 = await pickerFixture();
|
|
810
|
-
el1.id = 'away';
|
|
811
|
-
el2.id = 'other';
|
|
812
|
-
await Promise.all([elementUpdated(el1), elementUpdated(el2)]);
|
|
813
|
-
expect(el1.open, 'closed 1').to.be.false;
|
|
814
|
-
expect(el2.open, 'closed 1').to.be.false;
|
|
815
|
-
let open = oneEvent(el1, 'sp-opened');
|
|
816
|
-
el1.click();
|
|
817
|
-
await open;
|
|
818
|
-
expect(el1.open).to.be.true;
|
|
819
|
-
expect(el2.open).to.be.false;
|
|
820
|
-
open = oneEvent(el2, 'sp-opened');
|
|
821
|
-
let closed = oneEvent(el1, 'sp-closed');
|
|
822
|
-
el2.click();
|
|
823
|
-
await Promise.all([open, closed]);
|
|
824
|
-
expect(el1.open).to.be.false;
|
|
825
|
-
expect(el2.open).to.be.true;
|
|
826
|
-
open = oneEvent(el1, 'sp-opened');
|
|
827
|
-
closed = oneEvent(el2, 'sp-closed');
|
|
828
|
-
el1.click();
|
|
829
|
-
await Promise.all([open, closed]);
|
|
830
|
-
expect(el1.open).to.be.true;
|
|
831
|
-
expect(el2.open).to.be.false;
|
|
832
|
-
closed = oneEvent(el1, 'sp-closed');
|
|
833
|
-
sendKeys({
|
|
834
|
-
press: 'Escape',
|
|
835
|
-
});
|
|
837
|
+
expect(el.open).to.be.true;
|
|
838
|
+
expect((_a = el.selectedItem) == null ? void 0 : _a.itemText).to.be.undefined;
|
|
839
|
+
expect(el.value).to.equal("");
|
|
840
|
+
const closed = oneEvent(el, "sp-closed");
|
|
841
|
+
secondItem.click();
|
|
836
842
|
await closed;
|
|
837
|
-
expect(
|
|
843
|
+
expect(el.open).to.be.false;
|
|
844
|
+
expect((_b = el.selectedItem) == null ? void 0 : _b.itemText).to.equal("Select Inverse");
|
|
845
|
+
expect(el.value).to.equal("option-2");
|
|
846
|
+
});
|
|
838
847
|
});
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
848
|
+
});
|
|
849
|
+
testForLitDevWarnings(async () => await pickerFixture());
|
|
850
|
+
it('manages its "name" value in the accessibility tree when [icons-only]', async () => {
|
|
851
|
+
const test = await fixture(html`
|
|
852
|
+
<div>${iconsOnly({})}</div>
|
|
853
|
+
`);
|
|
854
|
+
const el2 = test.querySelector("sp-picker");
|
|
855
|
+
await elementUpdated(el2);
|
|
856
|
+
let snapshot = await a11ySnapshot({});
|
|
857
|
+
expect(findAccessibilityNode(snapshot, (node) => node.name === "Choose an action type... Delete"), "`name` is the label text").to.not.be.null;
|
|
858
|
+
el2.value = "2";
|
|
859
|
+
await elementUpdated(el2);
|
|
860
|
+
snapshot = await a11ySnapshot({});
|
|
861
|
+
expect(findAccessibilityNode(snapshot, (node) => node.name === "Choose an action type... Copy"), "`name` is the label text plus the selected item text").to.not.be.null;
|
|
862
|
+
});
|
|
863
|
+
it("toggles between pickers", async () => {
|
|
864
|
+
const el2 = await pickerFixture();
|
|
865
|
+
const el1 = await pickerFixture();
|
|
866
|
+
el1.id = "away";
|
|
867
|
+
el2.id = "other";
|
|
868
|
+
await Promise.all([elementUpdated(el1), elementUpdated(el2)]);
|
|
869
|
+
expect(el1.open, "closed 1").to.be.false;
|
|
870
|
+
expect(el2.open, "closed 1").to.be.false;
|
|
871
|
+
let open = oneEvent(el1, "sp-opened");
|
|
872
|
+
el1.click();
|
|
873
|
+
await open;
|
|
874
|
+
expect(el1.open).to.be.true;
|
|
875
|
+
expect(el2.open).to.be.false;
|
|
876
|
+
open = oneEvent(el2, "sp-opened");
|
|
877
|
+
let closed = oneEvent(el1, "sp-closed");
|
|
878
|
+
el2.click();
|
|
879
|
+
await Promise.all([open, closed]);
|
|
880
|
+
expect(el1.open).to.be.false;
|
|
881
|
+
expect(el2.open).to.be.true;
|
|
882
|
+
open = oneEvent(el1, "sp-opened");
|
|
883
|
+
closed = oneEvent(el2, "sp-closed");
|
|
884
|
+
el1.click();
|
|
885
|
+
await Promise.all([open, closed]);
|
|
886
|
+
expect(el1.open).to.be.true;
|
|
887
|
+
expect(el2.open).to.be.false;
|
|
888
|
+
closed = oneEvent(el1, "sp-closed");
|
|
889
|
+
sendKeys({
|
|
890
|
+
press: "Escape"
|
|
891
|
+
});
|
|
892
|
+
await closed;
|
|
893
|
+
expect(el1.open).to.be.false;
|
|
894
|
+
});
|
|
895
|
+
it("displays selected item text by default", async () => {
|
|
896
|
+
var _a, _b, _c, _d;
|
|
897
|
+
const el2 = await fixture(html`
|
|
842
898
|
<sp-picker
|
|
843
899
|
value="inverse"
|
|
844
900
|
label="Select a Country with a very long label, too long in fact"
|
|
@@ -852,27 +908,30 @@ export function runPickerTests() {
|
|
|
852
908
|
<sp-menu-item disabled>Make Work Path</sp-menu-item>
|
|
853
909
|
</sp-picker>
|
|
854
910
|
`);
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
911
|
+
await elementUpdated(el2);
|
|
912
|
+
await waitUntil(() => {
|
|
913
|
+
var _a2;
|
|
914
|
+
return ((_a2 = el2.selectedItem) == null ? void 0 : _a2.itemText) === "Select Inverse";
|
|
915
|
+
}, `Selected Item Text: ${(_a = el2.selectedItem) == null ? void 0 : _a.itemText}`);
|
|
916
|
+
const firstItem = el2.querySelector("sp-menu-item:nth-of-type(1)");
|
|
917
|
+
const secondItem = el2.querySelector("sp-menu-item:nth-of-type(2)");
|
|
918
|
+
expect(el2.value).to.equal("inverse");
|
|
919
|
+
expect((_b = el2.selectedItem) == null ? void 0 : _b.itemText).to.equal("Select Inverse");
|
|
920
|
+
el2.focus();
|
|
921
|
+
await elementUpdated(el2);
|
|
922
|
+
expect(el2 === document.activeElement, `activeElement is ${(_c = document.activeElement) == null ? void 0 : _c.localName}`).to.be.true;
|
|
923
|
+
const opened = oneEvent(el2, "sp-opened");
|
|
924
|
+
sendKeys({ press: "Enter" });
|
|
925
|
+
await opened;
|
|
926
|
+
await elementUpdated(el2.optionsMenu);
|
|
927
|
+
expect(el2.optionsMenu === document.activeElement, `activeElement is ${(_d = document.activeElement) == null ? void 0 : _d.localName}`).to.be.true;
|
|
928
|
+
expect(firstItem.focused, 'firstItem NOT "focused"').to.be.false;
|
|
929
|
+
expect(secondItem.focused, 'secondItem "focused"').to.be.true;
|
|
930
|
+
expect(el2.optionsMenu.getAttribute("aria-activedescendant")).to.equal(secondItem.id);
|
|
931
|
+
});
|
|
932
|
+
it("resets value when item not available", async () => {
|
|
933
|
+
var _a;
|
|
934
|
+
const el2 = await fixture(html`
|
|
876
935
|
<sp-picker
|
|
877
936
|
value="missing"
|
|
878
937
|
label="Select a Country with a very long label, too long in fact"
|
|
@@ -886,15 +945,15 @@ export function runPickerTests() {
|
|
|
886
945
|
<sp-menu-item disabled>Make Work Path</sp-menu-item>
|
|
887
946
|
</sp-picker>
|
|
888
947
|
`);
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
948
|
+
await elementUpdated(el2);
|
|
949
|
+
await waitUntil(() => el2.value === "");
|
|
950
|
+
expect(el2.value).to.equal("");
|
|
951
|
+
expect((_a = el2.selectedItem) == null ? void 0 : _a.itemText).to.be.undefined;
|
|
952
|
+
});
|
|
953
|
+
it("allows event listeners on child items", async () => {
|
|
954
|
+
const mouseenterSpy = spy();
|
|
955
|
+
const handleMouseenter = () => mouseenterSpy();
|
|
956
|
+
const el2 = await fixture(html`
|
|
898
957
|
<sp-picker
|
|
899
958
|
label="Select a Country with a very long label, too long in fact"
|
|
900
959
|
>
|
|
@@ -906,29 +965,29 @@ export function runPickerTests() {
|
|
|
906
965
|
</sp-menu-item>
|
|
907
966
|
</sp-picker>
|
|
908
967
|
`);
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
968
|
+
await elementUpdated(el2);
|
|
969
|
+
const hoverEl = el2.querySelector("sp-menu-item");
|
|
970
|
+
const opened = oneEvent(el2, "sp-opened");
|
|
971
|
+
el2.open = true;
|
|
972
|
+
await opened;
|
|
973
|
+
await elementUpdated(el2);
|
|
974
|
+
expect(el2.open).to.be.true;
|
|
975
|
+
hoverEl.dispatchEvent(new MouseEvent("mouseenter"));
|
|
976
|
+
await elementUpdated(el2);
|
|
977
|
+
expect(el2.open).to.be.true;
|
|
978
|
+
const closed = oneEvent(el2, "sp-closed");
|
|
979
|
+
el2.open = false;
|
|
980
|
+
await closed;
|
|
981
|
+
await elementUpdated(el2);
|
|
982
|
+
expect(el2.open).to.be.false;
|
|
983
|
+
expect(mouseenterSpy.calledOnce).to.be.true;
|
|
984
|
+
});
|
|
985
|
+
it("dispatches events on open/close", async () => {
|
|
986
|
+
const openedSpy = spy();
|
|
987
|
+
const closedSpy = spy();
|
|
988
|
+
const handleOpenedSpy = (event) => openedSpy(event);
|
|
989
|
+
const handleClosedSpy = (event) => closedSpy(event);
|
|
990
|
+
const el2 = await fixture(html`
|
|
932
991
|
<sp-picker
|
|
933
992
|
label="Select a Country with a very long label, too long in fact"
|
|
934
993
|
@sp-opened=${handleOpenedSpy}
|
|
@@ -937,24 +996,22 @@ export function runPickerTests() {
|
|
|
937
996
|
<sp-menu-item value="deselect">Deselect Text</sp-menu-item>
|
|
938
997
|
</sp-picker>
|
|
939
998
|
`);
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
expect(closedEvent.detail.interaction).to.equal('modal');
|
|
958
|
-
});
|
|
999
|
+
await elementUpdated(el2);
|
|
1000
|
+
const opened = oneEvent(el2, "sp-opened");
|
|
1001
|
+
el2.open = true;
|
|
1002
|
+
await opened;
|
|
1003
|
+
await elementUpdated(el2);
|
|
1004
|
+
expect(openedSpy.calledOnce).to.be.true;
|
|
1005
|
+
expect(closedSpy.calledOnce).to.be.false;
|
|
1006
|
+
const openedEvent = openedSpy.args[0][0];
|
|
1007
|
+
expect(openedEvent.detail.interaction).to.equal("modal");
|
|
1008
|
+
const closed = oneEvent(el2, "sp-closed");
|
|
1009
|
+
el2.open = false;
|
|
1010
|
+
await closed;
|
|
1011
|
+
await elementUpdated(el2);
|
|
1012
|
+
expect(closedSpy.calledOnce).to.be.true;
|
|
1013
|
+
const closedEvent = closedSpy.args[0][0];
|
|
1014
|
+
expect(closedEvent.detail.interaction).to.equal("modal");
|
|
1015
|
+
});
|
|
959
1016
|
}
|
|
960
|
-
//# sourceMappingURL=index.js.map
|
|
1017
|
+
//# sourceMappingURL=index.js.map
|