@crowdstrike/glide-core 0.5.1 → 0.5.2

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 (208) hide show
  1. package/dist/accordion.js +1 -1
  2. package/dist/accordion.styles.js +4 -4
  3. package/dist/accordion.test.basics.js +109 -0
  4. package/dist/accordion.test.events.js +39 -0
  5. package/dist/button-group.button.js +1 -1
  6. package/dist/button-group.button.styles.js +4 -4
  7. package/dist/button-group.button.test.basics.js +169 -0
  8. package/dist/button-group.button.test.events.js +73 -0
  9. package/dist/button-group.js +1 -1
  10. package/dist/button-group.styles.js +3 -3
  11. package/dist/button-group.test.basics.js +268 -0
  12. package/dist/button-group.test.events.js +291 -0
  13. package/dist/button.js +1 -1
  14. package/dist/button.styles.js +4 -4
  15. package/dist/button.test.basics.js +196 -0
  16. package/dist/button.test.events.js +25 -0
  17. package/dist/button.test.form.js +49 -0
  18. package/dist/checkbox-group.js +1 -1
  19. package/dist/checkbox-group.styles.js +2 -2
  20. package/dist/checkbox-group.test.basics.js +119 -0
  21. package/dist/checkbox-group.test.events.js +110 -0
  22. package/dist/checkbox-group.test.focus.js +45 -0
  23. package/dist/checkbox-group.test.form.js +130 -0
  24. package/dist/checkbox-group.test.validity.js +75 -0
  25. package/dist/checkbox.js +1 -1
  26. package/dist/checkbox.styles.js +3 -3
  27. package/dist/checkbox.test.basics.js +89 -0
  28. package/dist/checkbox.test.events.js +87 -0
  29. package/dist/checkbox.test.focus.js +38 -0
  30. package/dist/checkbox.test.form.js +115 -0
  31. package/dist/checkbox.test.states.js +62 -0
  32. package/dist/checkbox.test.validity.js +51 -0
  33. package/dist/drawer.d.ts +2 -2
  34. package/dist/drawer.js +1 -15
  35. package/dist/drawer.styles.js +18 -4
  36. package/dist/drawer.test.accessibility.js +22 -0
  37. package/dist/drawer.test.basics.js +43 -0
  38. package/dist/drawer.test.closing.js +37 -0
  39. package/dist/drawer.test.events.js +52 -0
  40. package/dist/drawer.test.methods.js +34 -0
  41. package/dist/dropdown.d.ts +4 -2
  42. package/dist/dropdown.js +1 -1
  43. package/dist/dropdown.option.d.ts +0 -2
  44. package/dist/dropdown.option.js +1 -1
  45. package/dist/dropdown.option.styles.js +2 -2
  46. package/dist/dropdown.option.test.basics.js +59 -0
  47. package/dist/dropdown.option.test.basics.multiple.js +26 -0
  48. package/dist/dropdown.option.test.basics.single.js +20 -0
  49. package/dist/dropdown.option.test.events.js +27 -0
  50. package/dist/dropdown.option.test.focus.js +11 -0
  51. package/dist/dropdown.option.test.interactions.multiple.js +87 -0
  52. package/dist/dropdown.option.test.interactions.single.js +22 -0
  53. package/dist/dropdown.styles.js +25 -6
  54. package/dist/dropdown.test.basics.filterable.js +84 -0
  55. package/dist/dropdown.test.basics.js +233 -0
  56. package/dist/dropdown.test.basics.multiple.js +270 -0
  57. package/dist/dropdown.test.basics.single.js +79 -0
  58. package/dist/dropdown.test.events.js +268 -0
  59. package/dist/dropdown.test.events.multiple.js +130 -0
  60. package/dist/dropdown.test.focus.d.ts +1 -0
  61. package/dist/dropdown.test.focus.filterable.js +154 -0
  62. package/dist/dropdown.test.focus.js +18 -0
  63. package/dist/dropdown.test.focus.multiple.js +181 -0
  64. package/dist/dropdown.test.focus.single.js +53 -0
  65. package/dist/dropdown.test.form.js +140 -0
  66. package/dist/dropdown.test.form.multiple.js +149 -0
  67. package/dist/dropdown.test.form.single.js +128 -0
  68. package/dist/dropdown.test.interactions.filterable.js +385 -0
  69. package/dist/dropdown.test.interactions.js +446 -0
  70. package/dist/dropdown.test.interactions.multiple.js +908 -0
  71. package/dist/dropdown.test.interactions.single.js +466 -0
  72. package/dist/dropdown.test.validity.js +46 -0
  73. package/dist/icon-button.js +1 -1
  74. package/dist/icon-button.styles.js +3 -3
  75. package/dist/icon-button.test.basics.js +103 -0
  76. package/dist/icons/checked.js +1 -1
  77. package/dist/icons/magnifying-glass.js +1 -1
  78. package/dist/input.js +1 -1
  79. package/dist/input.styles.js +3 -3
  80. package/dist/input.test.basics.js +169 -0
  81. package/dist/input.test.events.js +97 -0
  82. package/dist/input.test.focus.js +54 -0
  83. package/dist/input.test.form.js +56 -0
  84. package/dist/input.test.validity.js +50 -0
  85. package/dist/label.js +1 -1
  86. package/dist/label.styles.js +3 -3
  87. package/dist/label.test.basics.js +129 -0
  88. package/dist/library/expect-argument-error.js +1 -1
  89. package/dist/library/ow.js +1 -1
  90. package/dist/library/ow.test.js +55 -0
  91. package/dist/menu.button.d.ts +1 -2
  92. package/dist/menu.button.js +1 -1
  93. package/dist/menu.button.styles.js +3 -3
  94. package/dist/menu.button.test.basics.js +42 -0
  95. package/dist/menu.d.ts +4 -0
  96. package/dist/menu.js +1 -1
  97. package/dist/menu.link.d.ts +1 -2
  98. package/dist/menu.link.js +1 -1
  99. package/dist/menu.link.styles.js +3 -3
  100. package/dist/menu.link.test.basics.js +46 -0
  101. package/dist/menu.styles.js +6 -6
  102. package/dist/menu.test.basics.js +161 -0
  103. package/dist/menu.test.focus.d.ts +0 -1
  104. package/dist/menu.test.focus.js +66 -0
  105. package/dist/menu.test.interactions.d.ts +0 -1
  106. package/dist/menu.test.interactions.js +522 -0
  107. package/dist/modal.icon-button.js +1 -1
  108. package/dist/modal.icon-button.styles.js +2 -2
  109. package/dist/modal.icon-button.test.basics.js +45 -0
  110. package/dist/modal.js +1 -15
  111. package/dist/modal.styles.js +4 -4
  112. package/dist/modal.tertiary-icon.js +1 -1
  113. package/dist/modal.tertiary-icon.test.basics.js +59 -0
  114. package/dist/modal.test.accessibility.js +48 -0
  115. package/dist/modal.test.basics.js +203 -0
  116. package/dist/modal.test.close.js +38 -0
  117. package/dist/modal.test.events.js +110 -0
  118. package/dist/modal.test.lock-scroll.js +76 -0
  119. package/dist/modal.test.methods.js +23 -0
  120. package/dist/modal.test.scrollbars.js +19 -0
  121. package/dist/radio-group.js +1 -1
  122. package/dist/radio-group.styles.js +2 -2
  123. package/dist/radio-group.test.basics.js +323 -0
  124. package/dist/radio-group.test.events.js +277 -0
  125. package/dist/radio-group.test.focus.js +75 -0
  126. package/dist/radio-group.test.form.js +104 -0
  127. package/dist/radio-group.test.validity.js +228 -0
  128. package/dist/radio.js +1 -1
  129. package/dist/radio.styles.js +4 -4
  130. package/dist/split-button.d.ts +24 -0
  131. package/dist/split-button.js +1 -0
  132. package/dist/split-button.stories.d.ts +17 -0
  133. package/dist/split-button.styles.d.ts +2 -0
  134. package/dist/split-button.styles.js +103 -0
  135. package/dist/split-button.test.basics.d.ts +1 -0
  136. package/dist/split-button.test.basics.js +84 -0
  137. package/dist/split-container.d.ts +30 -0
  138. package/dist/split-container.js +1 -0
  139. package/dist/split-container.styles.d.ts +2 -0
  140. package/dist/split-container.styles.js +132 -0
  141. package/dist/split-container.test.basics.d.ts +3 -0
  142. package/dist/split-container.test.basics.js +445 -0
  143. package/dist/split-container.test.interactions.d.ts +1 -0
  144. package/dist/split-container.test.interactions.js +20 -0
  145. package/dist/split-link.d.ts +25 -0
  146. package/dist/split-link.js +1 -0
  147. package/dist/split-link.test.basics.d.ts +1 -0
  148. package/dist/split-link.test.basics.js +92 -0
  149. package/dist/split-link.test.interactions.d.ts +1 -0
  150. package/dist/split-link.test.interactions.js +19 -0
  151. package/dist/status-indicator.js +1 -1
  152. package/dist/status-indicator.styles.js +2 -2
  153. package/dist/status-indicator.test.basics.js +102 -0
  154. package/dist/styles/focus-outline.js +1 -4
  155. package/dist/styles/visually-hidden.js +1 -11
  156. package/dist/tab.group.js +1 -1
  157. package/dist/tab.group.styles.js +2 -2
  158. package/dist/tab.group.test.basics.js +185 -0
  159. package/dist/tab.js +1 -1
  160. package/dist/tab.panel.js +1 -1
  161. package/dist/tab.panel.styles.js +3 -3
  162. package/dist/tab.styles.js +2 -2
  163. package/dist/tab.test.basics.js +71 -0
  164. package/dist/tag.js +1 -1
  165. package/dist/tag.styles.js +3 -3
  166. package/dist/tag.test.basics.js +118 -0
  167. package/dist/tag.test.events.js +16 -0
  168. package/dist/tag.test.focus.js +11 -0
  169. package/dist/textarea.js +2 -2
  170. package/dist/textarea.styles.js +3 -3
  171. package/dist/textarea.test.basics.js +140 -0
  172. package/dist/textarea.test.events.js +204 -0
  173. package/dist/textarea.test.form.js +70 -0
  174. package/dist/textarea.test.validity.js +83 -0
  175. package/dist/toasts.js +1 -1
  176. package/dist/toasts.styles.js +2 -2
  177. package/dist/toasts.test.basics.js +94 -0
  178. package/dist/toasts.toast.js +1 -1
  179. package/dist/toasts.toast.styles.js +5 -2
  180. package/dist/toasts.toast.test.basics.js +139 -0
  181. package/dist/toggle.js +1 -1
  182. package/dist/toggle.styles.js +3 -3
  183. package/dist/toggle.test.basics.js +64 -0
  184. package/dist/toggle.test.events.js +29 -0
  185. package/dist/toggle.test.focus.js +9 -0
  186. package/dist/toggle.test.states.js +35 -0
  187. package/dist/tooltip.js +1 -1
  188. package/dist/tooltip.styles.js +3 -3
  189. package/dist/tooltip.test.basics.js +64 -0
  190. package/dist/tooltip.test.interactions.js +78 -0
  191. package/dist/tree.item.icon-button.js +1 -1
  192. package/dist/tree.item.icon-button.styles.js +2 -2
  193. package/dist/tree.item.icon-button.test.basics.js +13 -0
  194. package/dist/tree.item.js +1 -1
  195. package/dist/tree.item.menu.js +1 -1
  196. package/dist/tree.item.menu.styles.js +2 -2
  197. package/dist/tree.item.menu.test.basics.js +34 -0
  198. package/dist/tree.item.styles.js +2 -2
  199. package/dist/tree.item.test.basics.js +102 -0
  200. package/dist/tree.js +1 -1
  201. package/dist/tree.styles.js +2 -2
  202. package/dist/tree.test.aria.js +86 -0
  203. package/dist/tree.test.basics.js +123 -0
  204. package/dist/tree.test.events.js +19 -0
  205. package/dist/tree.test.focus.js +261 -0
  206. package/package.json +18 -16
  207. /package/dist/{dropdown.option.test.focus.multiple.d.ts → dropdown.option.test.focus.d.ts} +0 -0
  208. /package/dist/{dropdown.option.test.focus.single.d.ts → dropdown.test.events.multiple.d.ts} +0 -0
@@ -0,0 +1,466 @@
1
+ import { ArgumentError } from 'ow';
2
+ import { assert, elementUpdated, expect, fixture, html, } from '@open-wc/testing';
3
+ import { sendKeys } from '@web/test-runner-commands';
4
+ import GlideCoreDropdown from './dropdown.js';
5
+ import GlideCoreDropdownOption from './dropdown.option.js';
6
+ import sinon from 'sinon';
7
+ GlideCoreDropdown.shadowRootOptions.mode = 'open';
8
+ GlideCoreDropdownOption.shadowRootOptions.mode = 'open';
9
+ it('opens on click', async () => {
10
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
11
+ <glide-core-dropdown-option
12
+ label="Label"
13
+ value="value"
14
+ ></glide-core-dropdown-option>
15
+ </glide-core-dropdown>`);
16
+ // Calling `click()` would be sweet. The problem is it sets `event.detail` to `0`,
17
+ // which puts us in a guard in the event handler. `Event` has no `detail` property
18
+ // and would work. `CustomEvent` is used for completeness and to get us as close as
19
+ // possible to a real click. See the comment in the handler for more information.
20
+ component.shadowRoot
21
+ ?.querySelector('[data-test="button"]')
22
+ ?.dispatchEvent(new CustomEvent('click', { bubbles: true, detail: 1 }));
23
+ expect(component.open).to.be.true;
24
+ });
25
+ it('toggles open and closed when the button is clicked', async () => {
26
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
27
+ <glide-core-dropdown-option
28
+ label="Label"
29
+ value="value"
30
+ ></glide-core-dropdown-option>
31
+ </glide-core-dropdown>`);
32
+ // Calling `click()` would be sweet. The problem is it sets `event.detail` to `0`,
33
+ // which puts us in a guard in the event handler. `Event` has no `detail` property
34
+ // and would work. `CustomEvent` is used for completeness and to get us as close as
35
+ // possible to a real click. See the comment in the handler for more information.
36
+ component.shadowRoot
37
+ ?.querySelector('[data-test="button"]')
38
+ ?.dispatchEvent(new CustomEvent('click', { bubbles: true, detail: 1 }));
39
+ expect(component.open).to.be.false;
40
+ });
41
+ it('does not toggle open and closed when the button overflow text is clicked', async () => {
42
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
43
+ <glide-core-dropdown-option
44
+ label="Label"
45
+ value="value"
46
+ ></glide-core-dropdown-option>
47
+ </glide-core-dropdown>`);
48
+ // Calling `click()` would be sweet. The problem is it sets `event.detail` to `0`,
49
+ // which puts us in a guard in the event handler. `Event` has no `detail` property
50
+ // and would work. `CustomEvent` is used for completeness and to get us as close as
51
+ // possible to a real click. See the comment in the handler for more information.
52
+ component.shadowRoot
53
+ ?.querySelector('[data-test="tag-overflow-text"]')
54
+ ?.dispatchEvent(new CustomEvent('click', { bubbles: true, detail: 1 }));
55
+ expect(component.open).to.be.true;
56
+ });
57
+ it('selects an option on click', async () => {
58
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
59
+ <glide-core-dropdown-option
60
+ label="One"
61
+ value="one"
62
+ ></glide-core-dropdown-option>
63
+ </glide-core-dropdown>`);
64
+ const option = component.querySelector('glide-core-dropdown-option');
65
+ option?.click();
66
+ await elementUpdated(component);
67
+ const labels = component.shadowRoot?.querySelectorAll('[data-test="selected-option-label"]');
68
+ expect(option?.selected).to.be.true;
69
+ expect(labels?.length).to.equal(1);
70
+ expect(labels?.[0]?.textContent?.trim()).to.equal('One,');
71
+ });
72
+ it('selects an option on Space', async () => {
73
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
74
+ <glide-core-dropdown-option
75
+ label="One"
76
+ value="one"
77
+ ></glide-core-dropdown-option>
78
+ </glide-core-dropdown>`);
79
+ const option = component.querySelector('glide-core-dropdown-option');
80
+ option?.focus();
81
+ await sendKeys({ press: ' ' });
82
+ expect(option?.selected).to.be.true;
83
+ });
84
+ it('selects an option on Enter', async () => {
85
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
86
+ <glide-core-dropdown-option
87
+ label="One"
88
+ value="one"
89
+ ></glide-core-dropdown-option>
90
+ </glide-core-dropdown>`);
91
+ const option = component.querySelector('glide-core-dropdown-option');
92
+ option?.focus();
93
+ await sendKeys({ press: 'Enter' });
94
+ expect(option?.selected).to.be.true;
95
+ });
96
+ it('deactivates all other options on "mouseover"', async () => {
97
+ const component = await fixture(html `<glide-core-dropdown open>
98
+ <glide-core-dropdown-option
99
+ label="One"
100
+ value="one"
101
+ ></glide-core-dropdown-option>
102
+
103
+ <glide-core-dropdown-option
104
+ label="Two"
105
+ value="two"
106
+ ></glide-core-dropdown-option>
107
+ </glide-core-dropdown>`);
108
+ const options = component.querySelectorAll('glide-core-dropdown-option');
109
+ options[0]?.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
110
+ options[1]?.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
111
+ expect(options[0]?.privateActive).to.be.false;
112
+ expect(options[1]?.privateActive).to.be.true;
113
+ });
114
+ it('closes when an option is selected via click', async () => {
115
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
116
+ <glide-core-dropdown-option
117
+ label="Label"
118
+ value="value"
119
+ ></glide-core-dropdown-option>
120
+ </glide-core-dropdown>`);
121
+ component.querySelector('glide-core-dropdown-option')?.click();
122
+ expect(component.open).to.be.false;
123
+ });
124
+ it('closes when an option is selected via Enter', async () => {
125
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
126
+ <glide-core-dropdown-option
127
+ label="Label"
128
+ value="value"
129
+ ></glide-core-dropdown-option>
130
+ </glide-core-dropdown>`);
131
+ component.querySelector('glide-core-dropdown-option')?.focus();
132
+ await sendKeys({ press: 'Enter' });
133
+ expect(component.open).to.be.false;
134
+ });
135
+ it('closes when an option is selected via Space', async () => {
136
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
137
+ <glide-core-dropdown-option
138
+ label="Label"
139
+ value="value"
140
+ ></glide-core-dropdown-option>
141
+ </glide-core-dropdown>`);
142
+ const option = component.querySelector('glide-core-dropdown-option');
143
+ option?.focus();
144
+ await sendKeys({ press: ' ' });
145
+ expect(component.open).to.be.false;
146
+ });
147
+ it('deselects all other options when one is newly selected', async () => {
148
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
149
+ <glide-core-dropdown-option
150
+ label="One"
151
+ value="one"
152
+ selected
153
+ ></glide-core-dropdown-option>
154
+
155
+ <glide-core-dropdown-option
156
+ label="Two"
157
+ value="two"
158
+ ></glide-core-dropdown-option>
159
+
160
+ <glide-core-dropdown-option
161
+ label="Three"
162
+ value="three"
163
+ ></glide-core-dropdown-option>
164
+ </glide-core-dropdown>`);
165
+ component.shadowRoot
166
+ ?.querySelector('[data-test="button"]')
167
+ ?.dispatchEvent(new Event('click'));
168
+ const options = component.querySelectorAll('glide-core-dropdown-option');
169
+ options[1].click();
170
+ expect(options[0].selected).to.be.false;
171
+ expect(options[1].selected).to.be.true;
172
+ expect(options[2].selected).to.be.false;
173
+ });
174
+ it('selects and deselects options when `value` is changed programmatically', async () => {
175
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
176
+ <glide-core-dropdown-option
177
+ label="One"
178
+ value="one"
179
+ selected
180
+ ></glide-core-dropdown-option>
181
+
182
+ <glide-core-dropdown-option
183
+ label="Two"
184
+ value="two"
185
+ ></glide-core-dropdown-option>
186
+ </glide-core-dropdown>`);
187
+ component.value = ['two'];
188
+ const options = component.querySelectorAll('glide-core-dropdown-option');
189
+ expect(options[0].selected).to.be.false;
190
+ expect(options[1].selected).to.be.true;
191
+ });
192
+ it('throws when `value` is changed programmatically to include more than one value', async () => {
193
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
194
+ <glide-core-dropdown-option
195
+ label="One"
196
+ value="one"
197
+ ></glide-core-dropdown-option>
198
+
199
+ <glide-core-dropdown-option
200
+ label="Two"
201
+ value="two"
202
+ ></glide-core-dropdown-option>
203
+ </glide-core-dropdown>`);
204
+ const spy = sinon.spy();
205
+ try {
206
+ component.value = ['one', 'two'];
207
+ }
208
+ catch (error) {
209
+ if (error instanceof ArgumentError) {
210
+ spy();
211
+ }
212
+ }
213
+ expect(spy.called).to.be.true;
214
+ });
215
+ it('updates `value` when an option `value` is changed programmatically', async () => {
216
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
217
+ <glide-core-dropdown-option
218
+ label="One"
219
+ value="one"
220
+ selected
221
+ ></glide-core-dropdown-option>
222
+ </glide-core-dropdown>`);
223
+ const option = component.querySelector('glide-core-dropdown-option');
224
+ assert(option);
225
+ option.value = 'two';
226
+ expect(component.value).to.deep.equal(['two']);
227
+ });
228
+ it('updates `value` when an option is selected via click', async () => {
229
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
230
+ <glide-core-dropdown-option
231
+ label="One"
232
+ value="one"
233
+ ></glide-core-dropdown-option>
234
+
235
+ <glide-core-dropdown-option
236
+ label="Two"
237
+ value="two"
238
+ ></glide-core-dropdown-option>
239
+
240
+ <glide-core-dropdown-option label="Three"></glide-core-dropdown-option>
241
+ </glide-core-dropdown>`);
242
+ const options = component.querySelectorAll('glide-core-dropdown-option');
243
+ options[1].click();
244
+ expect(component.value).to.deep.equal(['two']);
245
+ // Reopen it.
246
+ component.shadowRoot
247
+ ?.querySelector('[data-test="button"]')
248
+ ?.dispatchEvent(new CustomEvent('click', { detail: 1 }));
249
+ // Wait for it to open.
250
+ await elementUpdated(component);
251
+ options[1].click();
252
+ expect(component.value).to.deep.equal(['two']);
253
+ // Reopen it.
254
+ component.shadowRoot
255
+ ?.querySelector('[data-test="button"]')
256
+ ?.dispatchEvent(new CustomEvent('click', { detail: 1 }));
257
+ // Wait for it to open.
258
+ await elementUpdated(component);
259
+ options[2].click();
260
+ expect(component.value).to.deep.equal([]);
261
+ });
262
+ it('updates `value` when an option is selected via Enter', async () => {
263
+ const component = await fixture(html `<glide-core-dropdown open>
264
+ <glide-core-dropdown-option
265
+ label="One"
266
+ value="one"
267
+ ></glide-core-dropdown-option>
268
+
269
+ <glide-core-dropdown-option
270
+ label="Two"
271
+ value="two"
272
+ ></glide-core-dropdown-option>
273
+
274
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
275
+ </glide-core-dropdown>`);
276
+ const options = component.querySelectorAll('glide-core-dropdown-option');
277
+ options[0].focus();
278
+ await sendKeys({ press: 'Enter' });
279
+ expect(component.value).to.deep.equal(['one']);
280
+ // Reopen it.
281
+ component.shadowRoot
282
+ ?.querySelector('[data-test="button"]')
283
+ ?.dispatchEvent(new CustomEvent('click', { bubbles: true, detail: 1 }));
284
+ // Wait for it to open.
285
+ await elementUpdated(component);
286
+ options[1].focus();
287
+ await sendKeys({ press: 'Enter' });
288
+ expect(component.value).to.deep.equal(['two']);
289
+ // Reopen it.
290
+ component.shadowRoot
291
+ ?.querySelector('[data-test="button"]')
292
+ ?.dispatchEvent(new CustomEvent('click', { bubbles: true, detail: 1 }));
293
+ // Wait for it to open.
294
+ await elementUpdated(component);
295
+ options[2].focus();
296
+ await sendKeys({ press: 'Enter' });
297
+ expect(component.value).to.deep.equal([]);
298
+ });
299
+ it('updates `value` when an option is selected via Space', async () => {
300
+ const component = await fixture(html `<glide-core-dropdown open>
301
+ <glide-core-dropdown-option
302
+ label="One"
303
+ value="one"
304
+ ></glide-core-dropdown-option>
305
+
306
+ <glide-core-dropdown-option
307
+ label="Two"
308
+ value="two"
309
+ ></glide-core-dropdown-option>
310
+
311
+ <glide-core-dropdown-option label="Three"></glide-core-dropdown-option>
312
+ </glide-core-dropdown>`);
313
+ const options = component.querySelectorAll('glide-core-dropdown-option');
314
+ options[0].focus();
315
+ await sendKeys({ press: ' ' });
316
+ expect(component.value).to.deep.equal(['one']);
317
+ // Reopen it.
318
+ component.shadowRoot
319
+ ?.querySelector('[data-test="button"]')
320
+ ?.dispatchEvent(new CustomEvent('click', { bubbles: true, detail: 1 }));
321
+ // Wait for it to open.
322
+ await elementUpdated(component);
323
+ options[1].focus();
324
+ await sendKeys({ press: ' ' });
325
+ expect(component.value).to.deep.equal(['two']);
326
+ // Reopen it.
327
+ component.shadowRoot
328
+ ?.querySelector('[data-test="button"]')
329
+ ?.dispatchEvent(new CustomEvent('click', { bubbles: true, detail: 1 }));
330
+ // Wait for it to open.
331
+ await elementUpdated(component);
332
+ options[2].focus();
333
+ await sendKeys({ press: ' ' });
334
+ expect(component.value).to.deep.equal([]);
335
+ });
336
+ it('updates `value` when `multiple` is changed to `false` programmatically', async () => {
337
+ const component = await fixture(html `<glide-core-dropdown open multiple>
338
+ <glide-core-dropdown-option
339
+ label="One"
340
+ value="one"
341
+ selected
342
+ ></glide-core-dropdown-option>
343
+
344
+ <glide-core-dropdown-option
345
+ label="Two"
346
+ value="two"
347
+ selected
348
+ ></glide-core-dropdown-option>
349
+ </glide-core-dropdown>`);
350
+ component.multiple = false;
351
+ const options = component.querySelectorAll('glide-core-dropdown-option');
352
+ const checkbox = options[1].shadowRoot?.querySelector('[data-test="checkbox"]');
353
+ expect(options[0].selected).to.be.false;
354
+ expect(options[1].selected).to.be.true;
355
+ expect(checkbox?.checked).to.be.true;
356
+ expect(component.value).to.deep.equal(['two']);
357
+ });
358
+ it('updates `value` when the `value` of the selected option is changed programmatically', async () => {
359
+ const component = await fixture(html `<glide-core-dropdown open>
360
+ <glide-core-dropdown-option
361
+ label="One"
362
+ value="one"
363
+ selected
364
+ ></glide-core-dropdown-option>
365
+
366
+ <glide-core-dropdown-option
367
+ label="Two"
368
+ value="two"
369
+ ></glide-core-dropdown-option>
370
+ </glide-core-dropdown>`);
371
+ const option = component.querySelector('glide-core-dropdown-option');
372
+ assert(option);
373
+ option.value = 'three';
374
+ expect(component.value).to.deep.equal(['three']);
375
+ });
376
+ it('updates `value` when the `value` of the selected option is removed programmatically', async () => {
377
+ const component = await fixture(html `<glide-core-dropdown open>
378
+ <glide-core-dropdown-option
379
+ label="One"
380
+ value="one"
381
+ selected
382
+ ></glide-core-dropdown-option>
383
+
384
+ <glide-core-dropdown-option
385
+ label="Two"
386
+ value="two"
387
+ ></glide-core-dropdown-option>
388
+ </glide-core-dropdown>`);
389
+ const option = component.querySelector('glide-core-dropdown-option');
390
+ assert(option);
391
+ option.value = '';
392
+ expect(component.value).to.deep.equal([]);
393
+ });
394
+ it('updates the its internal label when an option is newly selected', async () => {
395
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
396
+ <glide-core-dropdown-option
397
+ label="One"
398
+ value="one"
399
+ ></glide-core-dropdown-option>
400
+
401
+ <glide-core-dropdown-option
402
+ label="Two"
403
+ value="two"
404
+ ></glide-core-dropdown-option>
405
+ </glide-core-dropdown>`);
406
+ const option = component.querySelector('glide-core-dropdown-option:last-of-type');
407
+ option?.click();
408
+ await elementUpdated(component);
409
+ const label = component.shadowRoot?.querySelector('[data-test="internal-label"]');
410
+ expect(label?.textContent?.trim()).to.equal(option?.label);
411
+ });
412
+ it('hides Select All', async () => {
413
+ const component = await fixture(html `<glide-core-dropdown
414
+ label="Label"
415
+ placeholder="Placeholder"
416
+ select-all
417
+ >
418
+ <glide-core-dropdown-option
419
+ label="One"
420
+ value="one"
421
+ selected
422
+ ></glide-core-dropdown-option>
423
+
424
+ <glide-core-dropdown-option
425
+ label="Two"
426
+ value="two"
427
+ ></glide-core-dropdown-option>
428
+ </glide-core-dropdown>`);
429
+ const selectAll = component.shadowRoot?.querySelector('[data-test="select-all"]');
430
+ expect(selectAll?.checkVisibility()).to.not.be.ok;
431
+ });
432
+ it('does not select an option on Enter when the option is not focused', async () => {
433
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
434
+ <glide-core-dropdown-option
435
+ label="One"
436
+ value="one"
437
+ ></glide-core-dropdown-option>
438
+
439
+ <glide-core-dropdown-option
440
+ label="Two"
441
+ value="two"
442
+ ></glide-core-dropdown-option>
443
+ </glide-core-dropdown>`);
444
+ const option = component.querySelector('glide-core-dropdown-option');
445
+ option?.focus();
446
+ await sendKeys({ down: 'Tab' });
447
+ await sendKeys({ down: 'Shift' });
448
+ await sendKeys({ up: 'Tab' });
449
+ await sendKeys({ press: 'Enter' });
450
+ expect(option?.selected).to.be.false;
451
+ });
452
+ it('cannot be tabbed to when `disabled`', async () => {
453
+ await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" disabled>
454
+ <glide-core-dropdown-option
455
+ label="One"
456
+ value="one"
457
+ ></glide-core-dropdown-option>
458
+
459
+ <glide-core-dropdown-option
460
+ label="Two"
461
+ value="two"
462
+ ></glide-core-dropdown-option>
463
+ </glide-core-dropdown>`);
464
+ await sendKeys({ down: 'Tab' });
465
+ expect(document.activeElement).to.equal(document.body);
466
+ });
@@ -0,0 +1,46 @@
1
+ import './dropdown.option.js';
2
+ import { expect, fixture, html } from '@open-wc/testing';
3
+ import GlideCoreDropdown from './dropdown.js';
4
+ GlideCoreDropdown.shadowRootOptions.mode = 'open';
5
+ it('is valid if not required and no option is selected', async () => {
6
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
7
+ <glide-core-dropdown-option
8
+ label="Label"
9
+ value="value"
10
+ selected
11
+ ></glide-core-dropdown-option>
12
+ </glide-core-dropdown>`);
13
+ expect(component.validity.valid).to.be.true;
14
+ expect(component.validity?.valueMissing).to.be.false;
15
+ expect(component.checkValidity()).to.be.true;
16
+ expect(component.reportValidity()).to.be.true;
17
+ });
18
+ it('is invalid if required and no option is selected', async () => {
19
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" required>
20
+ <glide-core-dropdown-option
21
+ label="Label"
22
+ value="value"
23
+ ></glide-core-dropdown-option>
24
+ </glide-core-dropdown>`);
25
+ expect(component.validity.valid).to.be.false;
26
+ expect(component.validity?.valueMissing).to.be.true;
27
+ expect(component.checkValidity()).to.be.false;
28
+ expect(component.reportValidity()).to.be.false;
29
+ });
30
+ it('is both invalid and valid if required but disabled and no option is selected', async () => {
31
+ const component = await fixture(html `<glide-core-dropdown
32
+ label="Label"
33
+ placeholder="Placeholder"
34
+ disabled
35
+ required
36
+ >
37
+ <glide-core-dropdown-option
38
+ label="Label"
39
+ value="value"
40
+ ></glide-core-dropdown-option>
41
+ </glide-core-dropdown>`);
42
+ expect(component.validity.valid).to.be.false;
43
+ expect(component.validity?.valueMissing).to.be.true;
44
+ expect(component.checkValidity()).to.be.true;
45
+ expect(component.reportValidity()).to.be.true;
46
+ });
@@ -1 +1 @@
1
- "use strict";var g=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var s=(t,e,r,m)=>{for(var l=m>1?void 0:m?E(e,r):e,n=t.length-1,f;n>=0;n--)(f=t[n])&&(l=(m?f(e,r,l):f(l))||l);return m&&l&&g(e,r,l),l};var h=(t,e,r)=>{if(!e.has(t))throw TypeError("Cannot "+r)};var p=(t,e,r)=>(h(t,e,"read from private field"),r?r.call(t):e.get(t)),c=(t,e,r)=>{if(e.has(t))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(t):e.set(t,r)};var u=(t,e,r)=>(h(t,e,"access private method"),r);var i,d,v;import{LitElement as y,html as R}from"lit";import{classMap as S}from"lit/directives/class-map.js";import{createRef as $,ref as w}from"lit/directives/ref.js";import{customElement as M,property as o}from"lit/decorators.js";import{owSlot as b}from"./library/ow.js";import x from"./icon-button.styles.js";let a=class extends y{constructor(){super(...arguments);c(this,d);this.ariaExpanded=null;this.ariaHasPopup=null;this.disabled=!1;this.label="";this.variant="primary";c(this,i,$())}firstUpdated(){b(p(this,i).value)}render(){return R`<button aria-label="${this.label}" class="${S({component:!0,primary:this.variant==="primary",secondary:this.variant==="secondary",tertiary:this.variant==="tertiary"})}" type="button" ?disabled="${this.disabled}"><slot @slotchange="${u(this,d,v)}" ${w(p(this,i))}></slot></button>`}};i=new WeakMap,d=new WeakSet,v=function(){b(p(this,i).value)},a.shadowRootOptions={...y.shadowRootOptions,delegatesFocus:!0,mode:"closed"},a.styles=x,s([o({reflect:!0})],a.prototype,"ariaExpanded",2),s([o({reflect:!0})],a.prototype,"ariaHasPopup",2),s([o({type:Boolean,reflect:!0})],a.prototype,"disabled",2),s([o()],a.prototype,"label",2),s([o({reflect:!0})],a.prototype,"variant",2),a=s([M("glide-core-icon-button")],a);export{a as default};
1
+ var __decorate=this&&this.__decorate||function(t,e,o,r){var l,a=arguments.length,i=a<3?e:null===r?r=Object.getOwnPropertyDescriptor(e,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)i=Reflect.decorate(t,e,o,r);else for(var s=t.length-1;s>=0;s--)(l=t[s])&&(i=(a<3?l(i):a>3?l(e,o,i):l(e,o))||i);return a>3&&i&&Object.defineProperty(e,o,i),i};import{LitElement,html}from"lit";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property}from"lit/decorators.js";import{owSlot}from"./library/ow.js";import styles from"./icon-button.styles.js";let GlideCoreIconButton=class GlideCoreIconButton extends LitElement{constructor(){super(...arguments),this.ariaExpanded=null,this.ariaHasPopup=null,this.disabled=!1,this.label="",this.variant="primary",this.#t=createRef()}static{this.shadowRootOptions={...LitElement.shadowRootOptions,delegatesFocus:!0,mode:"closed"}}static{this.styles=styles}firstUpdated(){owSlot(this.#t.value)}render(){return html`<button aria-label="${this.label}" class="${classMap({component:!0,primary:"primary"===this.variant,secondary:"secondary"===this.variant,tertiary:"tertiary"===this.variant})}" type="button" ?disabled="${this.disabled}"><slot @slotchange="${this.#e}" ${ref(this.#t)}></slot></button>`}#t;#e(){owSlot(this.#t.value)}};__decorate([property({reflect:!0})],GlideCoreIconButton.prototype,"ariaExpanded",void 0),__decorate([property({reflect:!0})],GlideCoreIconButton.prototype,"ariaHasPopup",void 0),__decorate([property({type:Boolean,reflect:!0})],GlideCoreIconButton.prototype,"disabled",void 0),__decorate([property()],GlideCoreIconButton.prototype,"label",void 0),__decorate([property({reflect:!0})],GlideCoreIconButton.prototype,"variant",void 0),GlideCoreIconButton=__decorate([customElement("glide-core-icon-button")],GlideCoreIconButton);export default GlideCoreIconButton;
@@ -1,4 +1,4 @@
1
- "use strict";import{css as r}from"lit";import o from"./styles/focus-outline.js";export default[r`
1
+ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";export default[css`
2
2
  :host {
3
3
  /* Contains elements with "padding" and "width". Inline by default. */
4
4
  display: inline-block;
@@ -25,7 +25,7 @@
25
25
  }
26
26
 
27
27
  &:focus-visible {
28
- ${o};
28
+ ${focusOutline};
29
29
  outline-offset: 3px;
30
30
  }
31
31
 
@@ -109,4 +109,4 @@
109
109
  }
110
110
  }
111
111
  }
112
- `];
112
+ `];
@@ -0,0 +1,103 @@
1
+ import './icon-button.js';
2
+ import { ArgumentError } from 'ow';
3
+ import { expect, fixture, html } from '@open-wc/testing';
4
+ import GlideCoreIconButton from './icon-button.js';
5
+ import sinon from 'sinon';
6
+ GlideCoreIconButton.shadowRootOptions.mode = 'open';
7
+ const icon = html `<svg
8
+ width="16"
9
+ height="16"
10
+ stroke="currentColor"
11
+ fill="none"
12
+ stroke-linecap="round"
13
+ stroke-linejoin="round"
14
+ stroke-width="2"
15
+ viewBox="0 0 24 24"
16
+ aria-hidden="true"
17
+ >
18
+ <path d="M16.51 9.873l-4.459 4.31-4.458-4.31"></path>
19
+ </svg>`;
20
+ it('registers', async () => {
21
+ expect(window.customElements.get('glide-core-icon-button')).to.equal(GlideCoreIconButton);
22
+ });
23
+ it('is accessible', async () => {
24
+ const component = await fixture(html `<glide-core-icon-button label="test-icon-button"
25
+ >${icon}</glide-core-icon-button
26
+ >`);
27
+ await expect(component).to.be.accessible();
28
+ });
29
+ it('has defaults', async () => {
30
+ const component = await fixture(html `<glide-core-icon-button label="test-icon-button"
31
+ >${icon}</glide-core-icon-button
32
+ >`);
33
+ expect(component.shadowRoot?.querySelector('button')?.type).to.equal('button');
34
+ expect(component.shadowRoot?.querySelector('button')?.disabled).to.equal(false);
35
+ expect([
36
+ ...component.shadowRoot.querySelector('button').classList,
37
+ ]).to.deep.equal(['component', 'primary']);
38
+ });
39
+ it('delegates focus', async () => {
40
+ const component = await fixture(html `<glide-core-icon-button label="test-icon-button"
41
+ >${icon}</glide-core-icon-button
42
+ >`);
43
+ component.focus();
44
+ expect(component.shadowRoot?.activeElement).to.equal(component.shadowRoot?.querySelector('button'));
45
+ });
46
+ it('uses the provided "label" for the aria-label', async () => {
47
+ const component = await fixture(html `<glide-core-icon-button label="test-icon-button"
48
+ >${icon}</glide-core-icon-button
49
+ >`);
50
+ expect(component.shadowRoot?.querySelector('button')?.getAttribute('aria-label')).to.equal('test-icon-button');
51
+ });
52
+ it('renders a default slot', async () => {
53
+ const component = await fixture(html `<glide-core-icon-button label="test-icon-button"
54
+ ><span data-content>Inner content</span></glide-core-icon-button
55
+ >`);
56
+ expect(component.querySelector('[data-content]')).to.be.ok;
57
+ expect(component.querySelector('[data-content]')).to.be.visible;
58
+ });
59
+ it('renders a primary variant', async () => {
60
+ const component = await fixture(html `<glide-core-icon-button label="test-icon-button" variant="primary"
61
+ >${icon}</glide-core-icon-button
62
+ >`);
63
+ expect([
64
+ ...component.shadowRoot.querySelector('button').classList,
65
+ ]).to.deep.equal(['component', 'primary']);
66
+ });
67
+ it('renders a secondary variant', async () => {
68
+ const component = await fixture(html `<glide-core-icon-button label="test-icon-button" variant="secondary"
69
+ >${icon}</glide-core-icon-button
70
+ >`);
71
+ expect([
72
+ ...component.shadowRoot.querySelector('button').classList,
73
+ ]).to.deep.equal(['component', 'secondary']);
74
+ });
75
+ it('renders a tertiary variant', async () => {
76
+ const component = await fixture(html `<glide-core-icon-button label="test-icon-button" variant="tertiary"
77
+ >${icon}</glide-core-icon-button
78
+ >`);
79
+ expect([
80
+ ...component.shadowRoot.querySelector('button').classList,
81
+ ]).to.deep.equal(['component', 'tertiary']);
82
+ });
83
+ it('sets the disabled attribute', async () => {
84
+ const component = await fixture(html `<glide-core-icon-button label="test-icon-button" disabled
85
+ >${icon}</glide-core-icon-button
86
+ >`);
87
+ expect(component.disabled).to.equal(true);
88
+ expect(component.shadowRoot?.querySelector('button')?.disabled).to.equal(true);
89
+ });
90
+ it('throws if it does not have a default slot', async () => {
91
+ const spy = sinon.spy();
92
+ try {
93
+ await fixture(html `<glide-core-icon-button
94
+ label="test-icon-button"
95
+ ></glide-core-icon-button>`);
96
+ }
97
+ catch (error) {
98
+ if (error instanceof ArgumentError) {
99
+ spy();
100
+ }
101
+ }
102
+ expect(spy.called).to.be.true;
103
+ });
@@ -1 +1 @@
1
- "use strict";import{svg as e}from"lit/static-html.js";export default e`<svg fill="none" viewBox="0 0 24 24" style="height: var(--size, 0.875rem); width: var(--size, 0.875rem);"><path d="M20 6L9 17L4 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
1
+ import{svg}from"lit/static-html.js";export default svg`<svg fill="none" viewBox="0 0 24 24" style="height: var(--size, 0.875rem); width: var(--size, 0.875rem);"><path d="M20 6L9 17L4 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
@@ -1 +1 @@
1
- "use strict";import{svg as o}from"lit/static-html.js";export default o`<svg class="search-icon" fill="none" width="16" height="16" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/></svg>`;
1
+ import{svg}from"lit/static-html.js";export default svg`<svg class="search-icon" fill="none" width="16" height="16" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/></svg>`;