@crowdstrike/glide-core 0.12.1 → 0.12.3

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 (61) hide show
  1. package/dist/checkbox-group.styles.js +1 -1
  2. package/dist/dropdown.d.ts +5 -1
  3. package/dist/dropdown.js +126 -58
  4. package/dist/dropdown.option.d.ts +3 -0
  5. package/dist/dropdown.option.js +1 -1
  6. package/dist/dropdown.option.styles.js +81 -16
  7. package/dist/dropdown.option.test.basics.js +4 -33
  8. package/dist/dropdown.option.test.basics.multiple.js +9 -0
  9. package/dist/dropdown.option.test.basics.single.js +8 -0
  10. package/dist/dropdown.option.test.interactions.multiple.js +12 -0
  11. package/dist/dropdown.option.test.interactions.single.js +11 -0
  12. package/dist/dropdown.styles.js +127 -29
  13. package/dist/dropdown.test.basics.filterable.js +11 -54
  14. package/dist/dropdown.test.basics.js +24 -244
  15. package/dist/dropdown.test.basics.multiple.js +13 -60
  16. package/dist/dropdown.test.basics.single.js +2 -8
  17. package/dist/dropdown.test.events.filterable.js +12 -54
  18. package/dist/dropdown.test.events.js +139 -46
  19. package/dist/dropdown.test.events.multiple.js +17 -87
  20. package/dist/dropdown.test.events.single.js +99 -60
  21. package/dist/dropdown.test.focus.filterable.js +13 -60
  22. package/dist/dropdown.test.focus.js +64 -11
  23. package/dist/dropdown.test.focus.multiple.js +36 -46
  24. package/dist/dropdown.test.focus.single.js +28 -23
  25. package/dist/dropdown.test.form.js +22 -0
  26. package/dist/dropdown.test.interactions.filterable.js +86 -72
  27. package/dist/dropdown.test.interactions.js +361 -207
  28. package/dist/dropdown.test.interactions.multiple.js +152 -279
  29. package/dist/dropdown.test.interactions.single.js +62 -98
  30. package/dist/dropdown.test.validity.js +12 -49
  31. package/dist/icons/pencil.d.ts +2 -0
  32. package/dist/icons/pencil.js +1 -0
  33. package/dist/label.js +1 -1
  34. package/dist/label.styles.js +7 -3
  35. package/dist/library/localize.d.ts +2 -0
  36. package/dist/menu.js +1 -1
  37. package/dist/menu.options.js +1 -1
  38. package/dist/menu.options.test.events.d.ts +1 -0
  39. package/dist/menu.options.test.events.js +19 -0
  40. package/dist/menu.test.interactions.d.ts +1 -0
  41. package/dist/menu.test.interactions.js +38 -0
  42. package/dist/radio-group.styles.js +1 -1
  43. package/dist/tab.group.d.ts +8 -1
  44. package/dist/tab.group.js +1 -1
  45. package/dist/tab.group.styles.js +4 -0
  46. package/dist/tab.group.test.basics.js +77 -1
  47. package/dist/tab.panel.js +1 -1
  48. package/dist/tab.panel.styles.js +15 -0
  49. package/dist/tag.d.ts +2 -0
  50. package/dist/tag.js +1 -1
  51. package/dist/tag.styles.js +46 -5
  52. package/dist/tag.test.basics.js +1 -1
  53. package/dist/tag.test.events.js +76 -3
  54. package/dist/tag.test.focus.js +1 -1
  55. package/dist/textarea.styles.js +1 -1
  56. package/dist/translations/en.js +1 -1
  57. package/dist/translations/fr.d.ts +1 -1
  58. package/dist/translations/fr.js +1 -1
  59. package/dist/translations/ja.d.ts +1 -1
  60. package/dist/translations/ja.js +1 -1
  61. package/package.json +1 -1
@@ -1,16 +1,14 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-expressions */
2
- import { aTimeout, assert, expect, fixture, html } from '@open-wc/testing';
2
+ import { aTimeout, assert, elementUpdated, expect, fixture, html, } from '@open-wc/testing';
3
3
  import { sendKeys, sendMouse } from '@web/test-runner-commands';
4
4
  import GlideCoreDropdown from './dropdown.js';
5
5
  import GlideCoreDropdownOption from './dropdown.option.js';
6
+ import sinon from 'sinon';
6
7
  GlideCoreDropdown.shadowRootOptions.mode = 'open';
7
8
  GlideCoreDropdownOption.shadowRootOptions.mode = 'open';
8
9
  it('opens when opened programmatically', async () => {
9
10
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
10
- <glide-core-dropdown-option
11
- label="Label"
12
- value="value"
13
- ></glide-core-dropdown-option>
11
+ <glide-core-dropdown-option label="Label"></glide-core-dropdown-option>
14
12
  </glide-core-dropdown>`);
15
13
  component.open = true;
16
14
  await aTimeout(0);
@@ -19,10 +17,7 @@ it('opens when opened programmatically', async () => {
19
17
  });
20
18
  it('opens on ArrowUp', async () => {
21
19
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
22
- <glide-core-dropdown-option
23
- label="Label"
24
- value="value"
25
- ></glide-core-dropdown-option>
20
+ <glide-core-dropdown-option label="Label"></glide-core-dropdown-option>
26
21
  </glide-core-dropdown>`);
27
22
  component.focus();
28
23
  await sendKeys({ press: 'ArrowUp' });
@@ -32,10 +27,7 @@ it('opens on ArrowUp', async () => {
32
27
  });
33
28
  it('does not open on ArrowUp when `disabled`', async () => {
34
29
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" disabled>
35
- <glide-core-dropdown-option
36
- label="Label"
37
- value="value"
38
- ></glide-core-dropdown-option>
30
+ <glide-core-dropdown-option label="Label"></glide-core-dropdown-option>
39
31
  </glide-core-dropdown>`);
40
32
  component.focus();
41
33
  await sendKeys({ press: 'ArrowUp' });
@@ -45,10 +37,7 @@ it('does not open on ArrowUp when `disabled`', async () => {
45
37
  });
46
38
  it('does not open on ArrowUp when `readonly`', async () => {
47
39
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" readonly>
48
- <glide-core-dropdown-option
49
- label="Label"
50
- value="value"
51
- ></glide-core-dropdown-option>
40
+ <glide-core-dropdown-option label="Label"></glide-core-dropdown-option>
52
41
  </glide-core-dropdown>`);
53
42
  component.focus();
54
43
  await sendKeys({ press: 'ArrowUp' });
@@ -58,15 +47,8 @@ it('does not open on ArrowUp when `readonly`', async () => {
58
47
  });
59
48
  it('opens on ArrowDown', async () => {
60
49
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
61
- <glide-core-dropdown-option
62
- label="One"
63
- value="one"
64
- ></glide-core-dropdown-option>
65
-
66
- <glide-core-dropdown-option
67
- label="Two"
68
- value="two"
69
- ></glide-core-dropdown-option>
50
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
51
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
70
52
  </glide-core-dropdown>`);
71
53
  component.focus();
72
54
  await sendKeys({ press: 'ArrowDown' });
@@ -76,15 +58,8 @@ it('opens on ArrowDown', async () => {
76
58
  });
77
59
  it('does not open on ArrowDown when `disabled`', async () => {
78
60
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" disabled>
79
- <glide-core-dropdown-option
80
- label="One"
81
- value="one"
82
- ></glide-core-dropdown-option>
83
-
84
- <glide-core-dropdown-option
85
- label="Two"
86
- value="two"
87
- ></glide-core-dropdown-option>
61
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
62
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
88
63
  </glide-core-dropdown>`);
89
64
  component.focus();
90
65
  await sendKeys({ press: 'ArrowDown' });
@@ -94,15 +69,8 @@ it('does not open on ArrowDown when `disabled`', async () => {
94
69
  });
95
70
  it('does not open on ArrowDown when `readonly`', async () => {
96
71
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" readonly>
97
- <glide-core-dropdown-option
98
- label="One"
99
- value="one"
100
- ></glide-core-dropdown-option>
101
-
102
- <glide-core-dropdown-option
103
- label="Two"
104
- value="two"
105
- ></glide-core-dropdown-option>
72
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
73
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
106
74
  </glide-core-dropdown>`);
107
75
  component.focus();
108
76
  await sendKeys({ press: 'ArrowDown' });
@@ -110,6 +78,30 @@ it('does not open on ArrowDown when `readonly`', async () => {
110
78
  expect(component.open).to.be.false;
111
79
  expect(options?.checkVisibility()).to.be.false;
112
80
  });
81
+ it('does not scroll the page on ArrowDown when the Add button has focus', async () => {
82
+ document.body.style.height = '200vh';
83
+ document.body.style.scrollBehavior = 'auto';
84
+ const component = await fixture(html `<glide-core-dropdown
85
+ add-button-label="Add"
86
+ label="Label"
87
+ placeholder="Placeholder"
88
+ open
89
+ >
90
+ <glide-core-dropdown-option label="Label"></glide-core-dropdown-option>
91
+ </glide-core-dropdown>`);
92
+ // Wait for it to open.
93
+ await aTimeout(0);
94
+ component.focus();
95
+ await sendKeys({ press: 'ArrowDown' }); // Add button
96
+ const spy = sinon.spy();
97
+ document.addEventListener('scroll', spy);
98
+ await sendKeys({ press: 'ArrowDown' }); // Still Add button
99
+ // The browser apparently inserts a slight delay after arrowing before scrolling,
100
+ // even when smooth scrolling is disabled. `100` is a round number that comfortably
101
+ // gets us past that delay.
102
+ await aTimeout(100);
103
+ expect(spy.callCount).to.equal(0);
104
+ });
113
105
  it('opens on Space', async () => {
114
106
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
115
107
  <glide-core-dropdown-option
@@ -125,10 +117,7 @@ it('opens on Space', async () => {
125
117
  });
126
118
  it('does not open on Space when `disabled`', async () => {
127
119
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" disabled>
128
- <glide-core-dropdown-option
129
- label="Label"
130
- value="value"
131
- ></glide-core-dropdown-option>
120
+ <glide-core-dropdown-option label="Label"></glide-core-dropdown-option>
132
121
  </glide-core-dropdown>`);
133
122
  component.focus();
134
123
  await sendKeys({ press: ' ' });
@@ -138,10 +127,7 @@ it('does not open on Space when `disabled`', async () => {
138
127
  });
139
128
  it('does not open on Space when `readonly`', async () => {
140
129
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" readonly>
141
- <glide-core-dropdown-option
142
- label="Label"
143
- value="value"
144
- ></glide-core-dropdown-option>
130
+ <glide-core-dropdown-option label="Label"></glide-core-dropdown-option>
145
131
  </glide-core-dropdown>`);
146
132
  component.focus();
147
133
  await sendKeys({ press: ' ' });
@@ -153,10 +139,7 @@ it('does not open on Space when `readonly`', async () => {
153
139
  it('opens when opened programmatically via the click handler of another element', async () => {
154
140
  const div = document.createElement('div');
155
141
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
156
- <glide-core-dropdown-option
157
- label="Label"
158
- value="value"
159
- ></glide-core-dropdown-option>
142
+ <glide-core-dropdown-option label="Label"></glide-core-dropdown-option>
160
143
  </glide-core-dropdown>`, { parentNode: div });
161
144
  const button = document.createElement('button');
162
145
  button.addEventListener('click', () => (component.open = true));
@@ -177,15 +160,13 @@ it('closes when something outside of it is clicked', async () => {
177
160
  >
178
161
  <glide-core-dropdown-option
179
162
  label="One"
180
- value="one"
181
163
  selected
182
164
  ></glide-core-dropdown-option>
183
165
 
184
- <glide-core-dropdown-option
185
- label="Two"
186
- value="two"
187
- ></glide-core-dropdown-option>
166
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
188
167
  </glide-core-dropdown>`);
168
+ // Wait for it to open.
169
+ await aTimeout(0);
189
170
  document.body.click();
190
171
  expect(component.open).to.be.false;
191
172
  });
@@ -198,19 +179,49 @@ it('closes on Escape', async () => {
198
179
  >
199
180
  <glide-core-dropdown-option
200
181
  label="One"
201
- value="one"
202
182
  selected
203
183
  ></glide-core-dropdown-option>
204
184
 
205
- <glide-core-dropdown-option
206
- label="Two"
207
- value="two"
208
- ></glide-core-dropdown-option>
185
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
209
186
  </glide-core-dropdown>`);
187
+ // Wait for it to open.
188
+ await aTimeout(0);
210
189
  component.focus();
211
190
  await sendKeys({ press: 'Escape' });
212
191
  expect(component.open).to.be.false;
213
192
  });
193
+ it('closes on edit via click', async () => {
194
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
195
+ <glide-core-dropdown-option
196
+ label="One"
197
+ editable
198
+ selected
199
+ ></glide-core-dropdown-option>
200
+
201
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
202
+ </glide-core-dropdown>`);
203
+ component
204
+ .querySelector('glide-core-dropdown-option')
205
+ ?.shadowRoot?.querySelector('[data-test="edit-button"]')
206
+ ?.click();
207
+ expect(component.open).to.be.false;
208
+ });
209
+ it('closes when the Add button is clicked', async () => {
210
+ const component = await fixture(html `<glide-core-dropdown
211
+ add-button-label="Add"
212
+ label="Label"
213
+ placeholder="Placeholder"
214
+ open
215
+ >
216
+ <glide-core-dropdown-option label="Label"></glide-core-dropdown-option>
217
+ </glide-core-dropdown>`);
218
+ // Wait for it to open.
219
+ await aTimeout(0);
220
+ component.shadowRoot
221
+ ?.querySelector('[data-test="add-button"]')
222
+ ?.click();
223
+ expect(component.open).to.be.false;
224
+ });
214
225
  it('opens when open and enabled programmatically', async () => {
215
226
  const component = await fixture(html `<glide-core-dropdown
216
227
  label="Label"
@@ -220,14 +231,10 @@ it('opens when open and enabled programmatically', async () => {
220
231
  >
221
232
  <glide-core-dropdown-option
222
233
  label="One"
223
- value="one"
224
234
  selected
225
235
  ></glide-core-dropdown-option>
226
236
 
227
- <glide-core-dropdown-option
228
- label="Two"
229
- value="two"
230
- ></glide-core-dropdown-option>
237
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
231
238
  </glide-core-dropdown>`);
232
239
  component.disabled = false;
233
240
  // Wait for it to open.
@@ -239,14 +246,10 @@ it('closes when open and disabled programmatically', async () => {
239
246
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
240
247
  <glide-core-dropdown-option
241
248
  label="One"
242
- value="one"
243
249
  selected
244
250
  ></glide-core-dropdown-option>
245
251
 
246
- <glide-core-dropdown-option
247
- label="Two"
248
- value="two"
249
- ></glide-core-dropdown-option>
252
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
250
253
  </glide-core-dropdown>`);
251
254
  // Wait for it to open.
252
255
  await aTimeout(0);
@@ -256,254 +259,364 @@ it('closes when open and disabled programmatically', async () => {
256
259
  });
257
260
  it('activates an option on "mouseover"', async () => {
258
261
  const component = await fixture(html `<glide-core-dropdown open>
259
- <glide-core-dropdown-option
260
- label="One"
261
- value="one"
262
- ></glide-core-dropdown-option>
263
-
264
- <glide-core-dropdown-option
265
- label="Two"
266
- value="two"
267
- ></glide-core-dropdown-option>
262
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
263
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
268
264
  </glide-core-dropdown>`);
269
265
  const options = component.querySelectorAll('glide-core-dropdown-option');
270
266
  options[1]?.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
267
+ await elementUpdated(component);
268
+ expect(options[0]?.privateActive).to.be.false;
271
269
  expect(options[1]?.privateActive).to.be.true;
272
270
  });
273
271
  it('activates the next option on ArrowDown', async () => {
274
272
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
275
- <glide-core-dropdown-option
276
- label="One"
277
- value="one"
278
- ></glide-core-dropdown-option>
279
-
280
- <glide-core-dropdown-option
281
- label="Two"
282
- value="two"
283
- ></glide-core-dropdown-option>
273
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
274
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
284
275
  </glide-core-dropdown>`);
285
276
  // Wait for it to open.
286
277
  await aTimeout(0);
278
+ component.focus();
279
+ await sendKeys({ press: 'ArrowDown' }); // Two
287
280
  const options = component.querySelectorAll('glide-core-dropdown-option');
288
- options[0]?.focus();
289
- await sendKeys({ press: 'ArrowDown' });
290
281
  expect(options[0]?.privateActive).to.be.false;
291
282
  expect(options[1]?.privateActive).to.be.true;
292
283
  });
293
- it('activates the previous option on ArrowUp', async () => {
284
+ it('activates the Edit button on ArrowDown', async () => {
294
285
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
295
286
  <glide-core-dropdown-option
296
- label="One"
297
- value="one"
287
+ label="Label"
288
+ editable
298
289
  ></glide-core-dropdown-option>
299
290
 
291
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
292
+ </glide-core-dropdown>`);
293
+ // Wait for it to open.
294
+ await aTimeout(0);
295
+ component.focus();
296
+ await sendKeys({ press: 'ArrowDown' }); // One's edit button
297
+ const options = component.querySelectorAll('glide-core-dropdown-option');
298
+ expect(options[0]?.privateActive).to.be.true;
299
+ expect(options[0]?.privateIsEditActive).true;
300
+ expect(options[0]?.privateIsOpenTooltip).false;
301
+ expect(options[1]?.privateActive).to.be.false;
302
+ expect(options[1]?.privateIsEditActive).to.be.false;
303
+ });
304
+ it('activates the next option on ArrowDown when the Edit button is active', async () => {
305
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
300
306
  <glide-core-dropdown-option
301
- label="Two"
302
- value="two"
307
+ label="Label"
308
+ editable
303
309
  ></glide-core-dropdown-option>
310
+
311
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
304
312
  </glide-core-dropdown>`);
305
313
  // Wait for it to open.
306
314
  await aTimeout(0);
315
+ component.focus();
316
+ await sendKeys({ press: 'ArrowDown' }); // One's edit button
317
+ await sendKeys({ press: 'ArrowDown' }); // Two
307
318
  const options = component.querySelectorAll('glide-core-dropdown-option');
308
- options[1]?.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
309
- options[1]?.focus();
319
+ expect(options[0]?.privateActive).to.be.false;
320
+ expect(options[0]?.privateIsEditActive).false;
310
321
  expect(options[1]?.privateActive).to.be.true;
311
- await sendKeys({ press: 'ArrowUp' });
322
+ expect(options[1]?.privateIsEditActive).to.be.false;
323
+ expect(options[1]?.privateIsOpenTooltip).true;
324
+ });
325
+ it('activates the previous option on ArrowUp', async () => {
326
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
327
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
328
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
329
+ </glide-core-dropdown>`);
330
+ // Wait for it to open.
331
+ await aTimeout(0);
332
+ component.focus();
333
+ await sendKeys({ press: 'ArrowDown' }); // Two
334
+ await sendKeys({ press: 'ArrowUp' }); // One
335
+ const options = component.querySelectorAll('glide-core-dropdown-option');
312
336
  expect(options[0]?.privateActive).to.be.true;
313
337
  expect(options[1]?.privateActive).to.be.false;
314
338
  });
315
- it('activates the first option on Home', async () => {
339
+ it('activates the Edit button on on ArrowUp', async () => {
316
340
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
317
341
  <glide-core-dropdown-option
318
- label="One"
319
- value="one"
342
+ label="Label"
343
+ editable
320
344
  ></glide-core-dropdown-option>
321
345
 
322
346
  <glide-core-dropdown-option
323
347
  label="Two"
324
- value="two"
348
+ editable
325
349
  ></glide-core-dropdown-option>
326
350
  </glide-core-dropdown>`);
327
351
  // Wait for it to open.
328
352
  await aTimeout(0);
353
+ component.focus();
354
+ await sendKeys({ press: 'ArrowDown' }); // One's Edit button
355
+ await sendKeys({ press: 'ArrowDown' }); // Two
356
+ await sendKeys({ press: 'ArrowDown' }); // Two's Edit button
357
+ await sendKeys({ press: 'ArrowUp' }); // Two
358
+ await sendKeys({ press: 'ArrowUp' }); // One's Edit button
329
359
  const options = component.querySelectorAll('glide-core-dropdown-option');
330
- options[1]?.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
331
- expect(options[1].privateActive).to.be.true;
332
- options[1].focus();
333
- await sendKeys({ press: 'Home' });
334
360
  expect(options[0]?.privateActive).to.be.true;
361
+ expect(options[0]?.privateIsEditActive).to.be.true;
362
+ expect(options[0]?.privateIsOpenTooltip).false;
335
363
  expect(options[1]?.privateActive).to.be.false;
364
+ expect(options[1]?.privateIsEditActive).to.be.false;
365
+ expect(options[1]?.privateIsOpenTooltip).to.be.false;
336
366
  });
337
- it('activates the first option on PageUp', async () => {
338
- const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
339
- <glide-core-dropdown-option
340
- label="One"
341
- value="one"
342
- ></glide-core-dropdown-option>
367
+ it('activates previously active option on ArrowUp', async () => {
368
+ const component = await fixture(html `<glide-core-dropdown
369
+ add-button-label="Add"
370
+ label="Label"
371
+ placeholder="Placeholder"
372
+ open
373
+ >
374
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
375
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
376
+ </glide-core-dropdown>`);
377
+ // Wait for it to open.
378
+ await aTimeout(0);
379
+ component.focus();
380
+ await sendKeys({ press: 'ArrowDown' }); // Two
381
+ await sendKeys({ press: 'ArrowDown' }); // Add button
382
+ await sendKeys({ press: 'ArrowUp' }); // Two
383
+ const options = component.querySelectorAll('glide-core-dropdown-option');
384
+ expect(options[0]?.privateActive).to.be.false;
385
+ expect(options[0]?.privateIsEditActive).to.be.false;
386
+ expect(options[0]?.privateIsOpenTooltip).to.be.false;
387
+ expect(options[1]?.privateActive).to.be.true;
388
+ expect(options[1]?.privateIsOpenTooltip).to.be.true;
389
+ expect(options[1]?.privateIsEditActive).to.be.false;
390
+ });
391
+ it('activates the Edit button on ArrowUp', async () => {
392
+ const component = await fixture(html `<glide-core-dropdown
393
+ add-button-label="Add"
394
+ label="Label"
395
+ placeholder="Placeholder"
396
+ open
397
+ >
398
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
343
399
 
344
400
  <glide-core-dropdown-option
345
401
  label="Two"
346
- value="two"
402
+ editable
347
403
  ></glide-core-dropdown-option>
348
404
  </glide-core-dropdown>`);
349
405
  // Wait for it to open.
350
406
  await aTimeout(0);
407
+ component.focus();
408
+ await sendKeys({ press: 'ArrowDown' }); // Two
409
+ await sendKeys({ press: 'ArrowDown' }); // Two's Edit button
410
+ await sendKeys({ press: 'ArrowDown' }); // Add button
411
+ await sendKeys({ press: 'ArrowUp' }); // Two's Edit button
351
412
  const options = component.querySelectorAll('glide-core-dropdown-option');
352
- options[1].dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
353
- options[1].focus();
413
+ expect(options[0]?.privateActive).to.be.false;
414
+ expect(options[0]?.privateIsEditActive).to.be.false;
415
+ expect(options[0]?.privateIsOpenTooltip).to.be.false;
354
416
  expect(options[1]?.privateActive).to.be.true;
417
+ expect(options[1]?.privateIsEditActive).to.be.true;
418
+ expect(options[1]?.privateIsOpenTooltip).to.be.false;
419
+ });
420
+ it('activates the first option on Home', async () => {
421
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
422
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
423
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
424
+ </glide-core-dropdown>`);
425
+ // Wait for it to open.
426
+ await aTimeout(0);
427
+ component.focus();
428
+ await sendKeys({ press: 'End' });
429
+ await sendKeys({ press: 'Home' });
430
+ const options = component.querySelectorAll('glide-core-dropdown-option');
431
+ expect(options[0]?.privateActive).to.be.true;
432
+ expect(options[0]?.privateIsEditActive).to.be.false;
433
+ expect(options[0]?.privateIsOpenTooltip).to.be.true;
434
+ expect(options[1]?.privateActive).to.be.false;
435
+ expect(options[1]?.privateIsEditActive).to.be.false;
436
+ expect(options[1]?.privateIsOpenTooltip).to.be.false;
437
+ });
438
+ it('activates the first option on PageUp', async () => {
439
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
440
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
441
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
442
+ </glide-core-dropdown>`);
443
+ // Wait for it to open.
444
+ await aTimeout(0);
445
+ component.focus();
446
+ await sendKeys({ press: 'PageDown' });
355
447
  await sendKeys({ press: 'PageUp' });
448
+ const options = component.querySelectorAll('glide-core-dropdown-option');
356
449
  expect(options[0]?.privateActive).to.be.true;
450
+ expect(options[0]?.privateIsEditActive).to.be.false;
451
+ expect(options[0]?.privateIsOpenTooltip).to.be.true;
357
452
  expect(options[1]?.privateActive).to.be.false;
453
+ expect(options[1]?.privateIsEditActive).to.be.false;
454
+ expect(options[1]?.privateIsOpenTooltip).to.be.false;
358
455
  });
359
456
  it('activates the first option on ArrowUp + Meta', async () => {
360
457
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
361
- <glide-core-dropdown-option
362
- label="One"
363
- value="one"
364
- ></glide-core-dropdown-option>
365
-
366
- <glide-core-dropdown-option
367
- label="Two"
368
- value="two"
369
- ></glide-core-dropdown-option>
458
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
459
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
370
460
  </glide-core-dropdown>`);
371
461
  // Wait for it to open.
372
462
  await aTimeout(0);
373
- const options = component.querySelectorAll('glide-core-dropdown-option');
374
- options[1]?.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
375
- expect(options[1]?.privateActive).to.be.true;
376
- options[1].focus();
463
+ component.focus();
464
+ await sendKeys({ press: 'End' });
377
465
  await sendKeys({ down: 'Meta' });
378
466
  await sendKeys({ press: 'ArrowUp' });
379
467
  await sendKeys({ up: 'Meta' });
468
+ const options = component.querySelectorAll('glide-core-dropdown-option');
380
469
  expect(options[0]?.privateActive).to.be.true;
470
+ expect(options[0]?.privateIsEditActive).to.be.false;
471
+ expect(options[0]?.privateIsOpenTooltip).to.be.true;
381
472
  expect(options[1]?.privateActive).to.be.false;
473
+ expect(options[1]?.privateIsEditActive).to.be.false;
474
+ expect(options[1]?.privateIsOpenTooltip).to.be.false;
382
475
  });
383
476
  it('activates the last option on End', async () => {
384
477
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
385
- <glide-core-dropdown-option
386
- label="One"
387
- value="one"
388
- ></glide-core-dropdown-option>
389
-
390
- <glide-core-dropdown-option
391
- label="Two"
392
- value="two"
393
- ></glide-core-dropdown-option>
478
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
479
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
394
480
  </glide-core-dropdown>`);
395
481
  // Wait for it to open.
396
482
  await aTimeout(0);
397
- const options = component.querySelectorAll('glide-core-dropdown-option');
398
- options[0]?.focus();
483
+ component.focus();
399
484
  await sendKeys({ press: 'End' });
485
+ const options = component.querySelectorAll('glide-core-dropdown-option');
400
486
  expect(options[0]?.privateActive).to.be.false;
487
+ expect(options[0]?.privateIsEditActive).to.be.false;
488
+ expect(options[0]?.privateIsOpenTooltip).to.be.false;
401
489
  expect(options[1]?.privateActive).to.be.true;
490
+ expect(options[1]?.privateIsEditActive).to.be.false;
491
+ expect(options[1]?.privateIsOpenTooltip).to.be.true;
402
492
  });
403
493
  it('activates the last option on PageDown', async () => {
404
494
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
405
- <glide-core-dropdown-option
406
- label="One"
407
- value="one"
408
- ></glide-core-dropdown-option>
409
-
410
- <glide-core-dropdown-option
411
- label="Two"
412
- value="two"
413
- ></glide-core-dropdown-option>
495
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
496
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
414
497
  </glide-core-dropdown>`);
415
498
  // Wait for it to open.
416
499
  await aTimeout(0);
417
- const options = component.querySelectorAll('glide-core-dropdown-option');
418
- options[0]?.focus();
500
+ component.focus();
419
501
  await sendKeys({ press: 'PageDown' });
502
+ const options = component.querySelectorAll('glide-core-dropdown-option');
420
503
  expect(options[0]?.privateActive).to.be.false;
504
+ expect(options[0]?.privateIsEditActive).to.be.false;
505
+ expect(options[0]?.privateIsOpenTooltip).to.be.false;
421
506
  expect(options[1]?.privateActive).to.be.true;
507
+ expect(options[1]?.privateIsEditActive).to.be.false;
508
+ expect(options[1]?.privateIsOpenTooltip).to.be.true;
422
509
  });
423
510
  it('activates the last option on Meta + ArrowDown', async () => {
424
511
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
425
- <glide-core-dropdown-option
426
- label="One"
427
- value="one"
428
- ></glide-core-dropdown-option>
429
-
430
- <glide-core-dropdown-option
431
- label="Two"
432
- value="two"
433
- ></glide-core-dropdown-option>
512
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
513
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
434
514
  </glide-core-dropdown>`);
435
515
  // Wait for it to open.
436
516
  await aTimeout(0);
437
- const options = component.querySelectorAll('glide-core-dropdown-option');
438
- options[0]?.focus();
517
+ component.focus();
439
518
  await sendKeys({ down: 'Meta' });
440
519
  await sendKeys({ press: 'ArrowDown' });
441
520
  await sendKeys({ up: 'Meta' });
521
+ const options = component.querySelectorAll('glide-core-dropdown-option');
522
+ expect(options[0]?.privateActive).to.be.false;
523
+ expect(options[0]?.privateIsEditActive).to.be.false;
524
+ expect(options[0]?.privateIsOpenTooltip).to.be.false;
525
+ expect(options[1]?.privateActive).to.be.true;
526
+ expect(options[1]?.privateIsEditActive).to.be.false;
527
+ expect(options[1]?.privateIsOpenTooltip).to.be.true;
528
+ });
529
+ it('activates the previously active option when tabbing back from the Add button', async () => {
530
+ const component = await fixture(html `<glide-core-dropdown
531
+ add-button-label="Add"
532
+ label="Label"
533
+ placeholder="Placeholder"
534
+ open
535
+ >
536
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
537
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
538
+ <glide-core-dropdown-option label="Three"></glide-core-dropdown-option>
539
+ </glide-core-dropdown>`);
540
+ // Wait for it to open.
541
+ await aTimeout(0);
542
+ component.focus();
543
+ await sendKeys({ press: 'ArrowDown' }); // Two
544
+ await sendKeys({ press: 'Tab' });
545
+ await sendKeys({ down: 'Shift' });
546
+ await sendKeys({ press: 'Tab' });
547
+ await sendKeys({ up: 'Shift' });
548
+ const options = component.querySelectorAll('glide-core-dropdown-option');
442
549
  expect(options[0]?.privateActive).to.be.false;
550
+ expect(options[0]?.privateIsEditActive).to.be.false;
551
+ expect(options[0]?.privateIsOpenTooltip).to.be.false;
443
552
  expect(options[1]?.privateActive).to.be.true;
553
+ expect(options[1]?.privateIsEditActive).to.be.false;
554
+ expect(options[1]?.privateIsOpenTooltip).to.be.true;
555
+ expect(options[2]?.privateActive).to.be.false;
556
+ expect(options[2]?.privateIsEditActive).to.be.false;
557
+ expect(options[2]?.privateIsOpenTooltip).to.be.false;
558
+ });
559
+ it('deactivates the active option when the Add button is tabbed to', async () => {
560
+ const component = await fixture(html `<glide-core-dropdown
561
+ add-button-label="Add"
562
+ label="Label"
563
+ placeholder="Placeholder"
564
+ open
565
+ >
566
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
567
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
568
+ <glide-core-dropdown-option label="Three"></glide-core-dropdown-option>
569
+ </glide-core-dropdown>`);
570
+ // Wait for it to open.
571
+ await aTimeout(0);
572
+ component.focus();
573
+ await sendKeys({ press: 'Tab' });
574
+ const options = component.querySelectorAll('glide-core-dropdown-option');
575
+ expect(options[0]?.privateActive).to.be.false;
576
+ expect(options[0]?.privateIsEditActive).to.be.false;
577
+ expect(options[0]?.privateIsOpenTooltip).to.be.false;
578
+ expect(options[1]?.privateActive).to.be.false;
444
579
  });
445
580
  it('does not wrap on ArrowUp', async () => {
446
581
  const component = await fixture(html `<glide-core-dropdown open>
447
- <glide-core-dropdown-option
448
- label="One"
449
- value="one"
450
- ></glide-core-dropdown-option>
451
-
452
- <glide-core-dropdown-option
453
- label="Two"
454
- value="two"
455
- ></glide-core-dropdown-option>
582
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
583
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
456
584
  </glide-core-dropdown>`);
457
- const options = component.querySelectorAll('glide-core-dropdown-option');
458
- options[0]?.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
459
- options[0]?.focus();
585
+ // Wait for it to open.
586
+ await aTimeout(0);
587
+ component.focus();
460
588
  await sendKeys({ press: 'ArrowUp' });
589
+ const options = component.querySelectorAll('glide-core-dropdown-option');
461
590
  expect(options[0]?.privateActive).to.be.true;
462
591
  expect(options[1]?.privateActive).to.be.false;
463
592
  });
464
593
  it('does not wrap on ArrowDown', async () => {
465
594
  const component = await fixture(html `<glide-core-dropdown open>
466
- <glide-core-dropdown-option
467
- label="One"
468
- value="one"
469
- ></glide-core-dropdown-option>
470
-
471
- <glide-core-dropdown-option
472
- label="Two"
473
- value="two"
474
- ></glide-core-dropdown-option>
595
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
596
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
475
597
  </glide-core-dropdown>`);
598
+ // Wait for it to open.
599
+ await aTimeout(0);
600
+ component.focus();
601
+ await sendKeys({ press: 'ArrowDown' }); // Two
602
+ await sendKeys({ press: 'ArrowDown' }); // Two
476
603
  const options = component.querySelectorAll('glide-core-dropdown-option');
477
- options[1]?.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
478
- expect(options[1].privateActive).to.be.true;
479
- options[1]?.focus();
480
- await sendKeys({ press: 'ArrowDown' });
481
604
  expect(options[0]?.privateActive).to.be.false;
482
605
  expect(options[1]?.privateActive).to.be.true;
483
606
  });
484
607
  it('updates `privateSize` on every option when `size` is changed programmatically', async () => {
485
608
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
486
- <glide-core-dropdown-option
487
- label="One"
488
- value="one"
489
- ></glide-core-dropdown-option>
490
-
491
- <glide-core-dropdown-option
492
- label="Two"
493
- value="two"
494
- ></glide-core-dropdown-option>
609
+ <glide-core-dropdown-option label="One"></glide-core-dropdown-option>
610
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
495
611
  </glide-core-dropdown>`);
496
612
  component.size = 'small';
497
613
  const options = component.querySelectorAll('glide-core-dropdown-option');
498
614
  expect(options[0].privateSize).to.equal('small');
499
615
  expect(options[1].privateSize).to.equal('small');
500
616
  });
501
- it('opens when something other than the button is clicked', async () => {
617
+ it('opens when something other than the primary button is clicked', async () => {
502
618
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
503
- <glide-core-dropdown-option
504
- label="Label"
505
- value="value"
506
- ></glide-core-dropdown-option>
619
+ <glide-core-dropdown-option label="Label"></glide-core-dropdown-option>
507
620
  </glide-core-dropdown>`);
508
621
  const internalLabel = component.shadowRoot?.querySelector('[data-test="internal-label"]');
509
622
  assert(internalLabel);
@@ -516,6 +629,47 @@ it('opens when something other than the button is clicked', async () => {
516
629
  });
517
630
  expect(component.open).to.be.true;
518
631
  });
632
+ it('does not open on edit via click', async () => {
633
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
634
+ <glide-core-dropdown-option
635
+ label="Label"
636
+ editable
637
+ selected
638
+ ></glide-core-dropdown-option>
639
+ </glide-core-dropdown>`);
640
+ component.shadowRoot
641
+ ?.querySelector('[data-test="edit-button"]')
642
+ ?.click();
643
+ expect(component.open).to.be.false;
644
+ });
645
+ it('does not open on edit via Enter', async () => {
646
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
647
+ <glide-core-dropdown-option
648
+ label="Label"
649
+ editable
650
+ selected
651
+ ></glide-core-dropdown-option>
652
+ </glide-core-dropdown>`);
653
+ component.shadowRoot
654
+ ?.querySelector('[data-test="edit-button"]')
655
+ ?.focus();
656
+ await sendKeys({ press: 'Enter' });
657
+ expect(component.open).to.be.false;
658
+ });
659
+ it('does not open on edit via Space', async () => {
660
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
661
+ <glide-core-dropdown-option
662
+ label="Label"
663
+ editable
664
+ selected
665
+ ></glide-core-dropdown-option>
666
+ </glide-core-dropdown>`);
667
+ component.shadowRoot
668
+ ?.querySelector('[data-test="edit-button"]')
669
+ ?.focus();
670
+ await sendKeys({ press: ' ' });
671
+ expect(component.open).to.be.false;
672
+ });
519
673
  it('hides the tooltip of the active option when opened via click', async () => {
520
674
  // The period is arbitrary. 500 of them ensures we exceed the maximum
521
675
  // width even if it's increased.
@@ -531,7 +685,7 @@ it('hides the tooltip of the active option when opened via click', async () => {
531
685
  // and would work. `CustomEvent` is used for completeness and to get us as close as
532
686
  // possible to a real click. See the comment in the handler for more information.
533
687
  component.shadowRoot
534
- ?.querySelector('[data-test="button"]')
688
+ ?.querySelector('[data-test="primary-button"]')
535
689
  ?.dispatchEvent(new CustomEvent('click', { bubbles: true, detail: 1 }));
536
690
  // Wait for it to open.
537
691
  await aTimeout(0);