@crowdstrike/glide-core 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/README.md +11 -1
  2. package/dist/accordion.js +1 -1
  3. package/dist/accordion.styles.js +5 -4
  4. package/dist/accordion.test.basics.js +109 -0
  5. package/dist/accordion.test.events.js +39 -0
  6. package/dist/button-group.button.js +1 -1
  7. package/dist/button-group.button.styles.js +6 -5
  8. package/dist/button-group.button.test.basics.js +169 -0
  9. package/dist/button-group.button.test.events.js +73 -0
  10. package/dist/button-group.js +1 -1
  11. package/dist/button-group.styles.js +3 -3
  12. package/dist/button-group.test.basics.js +268 -0
  13. package/dist/button-group.test.events.js +291 -0
  14. package/dist/button.d.ts +3 -2
  15. package/dist/button.js +1 -1
  16. package/dist/button.styles.js +5 -5
  17. package/dist/button.test.basics.js +202 -0
  18. package/dist/button.test.events.js +25 -0
  19. package/dist/button.test.form.js +49 -0
  20. package/dist/checkbox-group.js +1 -1
  21. package/dist/checkbox-group.styles.js +2 -2
  22. package/dist/checkbox-group.test.basics.js +119 -0
  23. package/dist/checkbox-group.test.events.js +110 -0
  24. package/dist/checkbox-group.test.focus.js +45 -0
  25. package/dist/checkbox-group.test.form.js +130 -0
  26. package/dist/checkbox-group.test.validity.js +75 -0
  27. package/dist/checkbox.js +1 -1
  28. package/dist/checkbox.styles.js +7 -4
  29. package/dist/checkbox.test.basics.js +89 -0
  30. package/dist/checkbox.test.events.js +87 -0
  31. package/dist/checkbox.test.focus.js +38 -0
  32. package/dist/checkbox.test.form.js +115 -0
  33. package/dist/checkbox.test.states.js +62 -0
  34. package/dist/checkbox.test.validity.js +51 -0
  35. package/dist/drawer.d.ts +2 -2
  36. package/dist/drawer.js +1 -15
  37. package/dist/drawer.styles.js +14 -4
  38. package/dist/drawer.test.accessibility.js +22 -0
  39. package/dist/drawer.test.basics.js +43 -0
  40. package/dist/drawer.test.closing.js +37 -0
  41. package/dist/drawer.test.events.js +52 -0
  42. package/dist/drawer.test.floating-components.d.ts +1 -0
  43. package/dist/drawer.test.floating-components.js +51 -0
  44. package/dist/drawer.test.methods.js +34 -0
  45. package/dist/dropdown.d.ts +4 -2
  46. package/dist/dropdown.js +1 -1
  47. package/dist/dropdown.option.d.ts +0 -2
  48. package/dist/dropdown.option.js +1 -1
  49. package/dist/dropdown.option.styles.js +2 -2
  50. package/dist/dropdown.option.test.basics.js +59 -0
  51. package/dist/dropdown.option.test.basics.multiple.js +26 -0
  52. package/dist/dropdown.option.test.basics.single.js +20 -0
  53. package/dist/dropdown.option.test.events.js +27 -0
  54. package/dist/dropdown.option.test.focus.js +11 -0
  55. package/dist/dropdown.option.test.interactions.multiple.js +82 -0
  56. package/dist/dropdown.option.test.interactions.single.js +22 -0
  57. package/dist/dropdown.styles.js +26 -6
  58. package/dist/dropdown.test.basics.filterable.js +84 -0
  59. package/dist/dropdown.test.basics.js +233 -0
  60. package/dist/dropdown.test.basics.multiple.js +270 -0
  61. package/dist/dropdown.test.basics.single.js +79 -0
  62. package/dist/dropdown.test.events.js +268 -0
  63. package/dist/dropdown.test.events.multiple.js +130 -0
  64. package/dist/dropdown.test.focus.d.ts +1 -0
  65. package/dist/dropdown.test.focus.filterable.js +154 -0
  66. package/dist/dropdown.test.focus.js +28 -0
  67. package/dist/dropdown.test.focus.multiple.js +181 -0
  68. package/dist/dropdown.test.focus.single.js +53 -0
  69. package/dist/dropdown.test.form.js +140 -0
  70. package/dist/dropdown.test.form.multiple.js +149 -0
  71. package/dist/dropdown.test.form.single.js +128 -0
  72. package/dist/dropdown.test.interactions.filterable.js +449 -0
  73. package/dist/dropdown.test.interactions.js +446 -0
  74. package/dist/dropdown.test.interactions.multiple.js +908 -0
  75. package/dist/dropdown.test.interactions.single.js +466 -0
  76. package/dist/dropdown.test.validity.js +46 -0
  77. package/dist/icon-button.d.ts +3 -2
  78. package/dist/icon-button.js +1 -1
  79. package/dist/icon-button.styles.js +12 -12
  80. package/dist/icon-button.test.basics.js +110 -0
  81. package/dist/icons/checked.js +1 -1
  82. package/dist/icons/magnifying-glass.js +1 -1
  83. package/dist/input.js +1 -1
  84. package/dist/input.styles.js +4 -3
  85. package/dist/input.test.basics.js +169 -0
  86. package/dist/input.test.events.js +97 -0
  87. package/dist/input.test.focus.js +54 -0
  88. package/dist/input.test.form.js +56 -0
  89. package/dist/input.test.validity.js +50 -0
  90. package/dist/label.js +1 -1
  91. package/dist/label.styles.js +13 -15
  92. package/dist/label.test.basics.js +129 -0
  93. package/dist/library/expect-argument-error.js +1 -1
  94. package/dist/library/localize.d.ts +17 -0
  95. package/dist/library/localize.js +1 -0
  96. package/dist/library/ow.js +1 -1
  97. package/dist/library/ow.test.js +55 -0
  98. package/dist/library/set-containing-block.d.ts +15 -0
  99. package/dist/library/set-containing-block.js +1 -0
  100. package/dist/menu.button.d.ts +1 -2
  101. package/dist/menu.button.js +1 -1
  102. package/dist/menu.button.styles.js +3 -3
  103. package/dist/menu.button.test.basics.js +42 -0
  104. package/dist/menu.d.ts +7 -2
  105. package/dist/menu.js +1 -1
  106. package/dist/menu.link.d.ts +1 -2
  107. package/dist/menu.link.js +1 -1
  108. package/dist/menu.link.styles.js +3 -3
  109. package/dist/menu.link.test.basics.js +46 -0
  110. package/dist/menu.options.d.ts +22 -0
  111. package/dist/menu.options.js +1 -0
  112. package/dist/menu.options.styles.d.ts +2 -0
  113. package/dist/menu.options.styles.js +33 -0
  114. package/dist/menu.options.test.basics.d.ts +2 -0
  115. package/dist/menu.options.test.basics.js +43 -0
  116. package/dist/menu.stories.d.ts +1 -0
  117. package/dist/menu.styles.js +7 -31
  118. package/dist/menu.test.basics.d.ts +1 -0
  119. package/dist/menu.test.basics.js +183 -0
  120. package/dist/menu.test.focus.d.ts +0 -1
  121. package/dist/menu.test.focus.js +84 -0
  122. package/dist/menu.test.interactions.d.ts +1 -1
  123. package/dist/menu.test.interactions.js +664 -0
  124. package/dist/modal.icon-button.js +1 -1
  125. package/dist/modal.icon-button.styles.js +2 -2
  126. package/dist/modal.icon-button.test.basics.js +45 -0
  127. package/dist/modal.js +1 -15
  128. package/dist/modal.styles.js +4 -7
  129. package/dist/modal.tertiary-icon.d.ts +1 -0
  130. package/dist/modal.tertiary-icon.js +1 -1
  131. package/dist/modal.tertiary-icon.test.basics.js +59 -0
  132. package/dist/modal.test.accessibility.js +48 -0
  133. package/dist/modal.test.basics.js +203 -0
  134. package/dist/modal.test.close.js +38 -0
  135. package/dist/modal.test.events.js +110 -0
  136. package/dist/modal.test.floating-components.d.ts +1 -0
  137. package/dist/modal.test.floating-components.js +62 -0
  138. package/dist/modal.test.lock-scroll.js +76 -0
  139. package/dist/modal.test.methods.js +23 -0
  140. package/dist/modal.test.scrollbars.js +19 -0
  141. package/dist/radio-group.js +1 -1
  142. package/dist/radio-group.styles.js +20 -24
  143. package/dist/radio-group.test.basics.js +323 -0
  144. package/dist/radio-group.test.events.js +277 -0
  145. package/dist/radio-group.test.focus.js +75 -0
  146. package/dist/radio-group.test.form.js +104 -0
  147. package/dist/radio-group.test.validity.js +228 -0
  148. package/dist/radio.js +1 -1
  149. package/dist/radio.styles.js +14 -31
  150. package/dist/split-button.d.ts +27 -0
  151. package/dist/split-button.js +1 -0
  152. package/dist/split-button.stories.d.ts +17 -0
  153. package/dist/split-button.styles.d.ts +2 -0
  154. package/dist/split-button.styles.js +102 -0
  155. package/dist/split-button.test.basics.d.ts +1 -0
  156. package/dist/split-button.test.basics.js +99 -0
  157. package/dist/split-container.d.ts +31 -0
  158. package/dist/split-container.js +1 -0
  159. package/dist/split-container.styles.d.ts +2 -0
  160. package/dist/split-container.styles.js +134 -0
  161. package/dist/split-container.test.basics.d.ts +3 -0
  162. package/dist/split-container.test.basics.js +440 -0
  163. package/dist/split-container.test.interactions.d.ts +1 -0
  164. package/dist/split-container.test.interactions.js +20 -0
  165. package/dist/split-link.d.ts +25 -0
  166. package/dist/split-link.js +1 -0
  167. package/dist/split-link.test.basics.d.ts +1 -0
  168. package/dist/split-link.test.basics.js +92 -0
  169. package/dist/split-link.test.interactions.d.ts +1 -0
  170. package/dist/split-link.test.interactions.js +19 -0
  171. package/dist/status-indicator.js +1 -1
  172. package/dist/status-indicator.styles.js +2 -2
  173. package/dist/status-indicator.test.basics.js +102 -0
  174. package/dist/styles/focus-outline.js +1 -4
  175. package/dist/styles/variables.css +1 -1
  176. package/dist/styles/visually-hidden.js +1 -11
  177. package/dist/tab.group.js +1 -1
  178. package/dist/tab.group.styles.js +2 -2
  179. package/dist/tab.group.test.basics.js +185 -0
  180. package/dist/tab.js +1 -1
  181. package/dist/tab.panel.js +1 -1
  182. package/dist/tab.panel.styles.js +3 -3
  183. package/dist/tab.styles.js +80 -55
  184. package/dist/tab.test.basics.js +71 -0
  185. package/dist/tag.js +1 -1
  186. package/dist/tag.styles.js +4 -3
  187. package/dist/tag.test.basics.js +118 -0
  188. package/dist/tag.test.events.js +16 -0
  189. package/dist/tag.test.focus.js +11 -0
  190. package/dist/tag.test.translations.d.ts +1 -0
  191. package/dist/tag.test.translations.js +25 -0
  192. package/dist/textarea.js +2 -2
  193. package/dist/textarea.styles.js +5 -4
  194. package/dist/textarea.test.basics.js +140 -0
  195. package/dist/textarea.test.events.js +204 -0
  196. package/dist/textarea.test.form.js +70 -0
  197. package/dist/textarea.test.validity.js +83 -0
  198. package/dist/toasts.js +1 -1
  199. package/dist/toasts.styles.js +2 -2
  200. package/dist/toasts.test.basics.js +94 -0
  201. package/dist/toasts.toast.js +1 -1
  202. package/dist/toasts.toast.styles.js +5 -2
  203. package/dist/toasts.toast.test.basics.js +139 -0
  204. package/dist/toggle.js +1 -1
  205. package/dist/toggle.styles.js +3 -3
  206. package/dist/toggle.test.basics.js +68 -0
  207. package/dist/toggle.test.events.js +29 -0
  208. package/dist/toggle.test.focus.js +9 -0
  209. package/dist/toggle.test.states.js +43 -0
  210. package/dist/tooltip.d.ts +2 -0
  211. package/dist/tooltip.js +1 -1
  212. package/dist/tooltip.styles.js +5 -3
  213. package/dist/tooltip.test.basics.js +64 -0
  214. package/dist/tooltip.test.interactions.js +78 -0
  215. package/dist/translations/en.d.ts +3 -0
  216. package/dist/translations/en.js +1 -0
  217. package/dist/translations/fr.d.ts +3 -0
  218. package/dist/translations/fr.js +1 -0
  219. package/dist/translations/ja.d.ts +3 -0
  220. package/dist/translations/ja.js +1 -0
  221. package/dist/tree.d.ts +1 -0
  222. package/dist/tree.item.d.ts +3 -1
  223. package/dist/tree.item.icon-button.js +1 -1
  224. package/dist/tree.item.icon-button.styles.js +2 -2
  225. package/dist/tree.item.icon-button.test.basics.js +13 -0
  226. package/dist/tree.item.js +1 -1
  227. package/dist/tree.item.menu.d.ts +2 -0
  228. package/dist/tree.item.menu.js +1 -1
  229. package/dist/tree.item.menu.styles.js +2 -2
  230. package/dist/tree.item.menu.test.basics.js +33 -0
  231. package/dist/tree.item.styles.js +23 -8
  232. package/dist/tree.item.test.basics.js +102 -0
  233. package/dist/tree.js +1 -1
  234. package/dist/tree.stories.d.ts +1 -0
  235. package/dist/tree.styles.js +2 -2
  236. package/dist/tree.test.aria.js +86 -0
  237. package/dist/tree.test.basics.js +123 -0
  238. package/dist/tree.test.events.js +19 -0
  239. package/dist/tree.test.focus.js +261 -0
  240. package/package.json +25 -18
  241. /package/dist/{dropdown.option.test.focus.multiple.d.ts → dropdown.option.test.focus.d.ts} +0 -0
  242. /package/dist/{dropdown.option.test.focus.single.d.ts → dropdown.test.events.multiple.d.ts} +0 -0
@@ -0,0 +1,33 @@
1
+ import{css}from"lit";export default[css`
2
+ :host {
3
+ display: inline-block;
4
+ inline-size: 100%;
5
+ }
6
+
7
+ .component {
8
+ &.large {
9
+ --gap: var(--glide-core-spacing-sm);
10
+ --padding-inline: var(--glide-core-spacing-sm);
11
+ --padding-block: var(--glide-core-spacing-xxs);
12
+
13
+ font-family: var(--glide-core-body-sm-font-family);
14
+ font-size: var(--glide-core-body-sm-font-size);
15
+ font-style: var(--glide-core-body-sm-font-style);
16
+ font-weight: var(--glide-core-body-sm-font-weight);
17
+ line-height: var(--glide-core-body-sm-line-height);
18
+ }
19
+
20
+ &.small {
21
+ --gap: var(--glide-core-spacing-xs);
22
+ --padding-inline: var(--glide-core-spacing-xs);
23
+ --padding-block: var(--glide-core-spacing-xxxs);
24
+ --size: 0.75rem;
25
+
26
+ font-family: var(--glide-core-body-xs-font-family);
27
+ font-size: var(--glide-core-body-xs-font-size);
28
+ font-style: var(--glide-core-body-xs-font-style);
29
+ font-weight: var(--glide-core-body-xs-font-weight);
30
+ line-height: var(--glide-core-body-xs-line-height);
31
+ }
32
+ }
33
+ `];
@@ -0,0 +1,2 @@
1
+ import './menu.button.js';
2
+ import './menu.link.js';
@@ -0,0 +1,43 @@
1
+ import './menu.button.js';
2
+ import './menu.link.js';
3
+ import { ArgumentError } from 'ow';
4
+ import { expect, fixture, html } from '@open-wc/testing';
5
+ import { repeat } from 'lit/directives/repeat.js';
6
+ import GlideCoreMenu from './menu.js';
7
+ import GlideCoreMenuOptions from './menu.options.js';
8
+ import expectArgumentError from './library/expect-argument-error.js';
9
+ import sinon from 'sinon';
10
+ GlideCoreMenu.shadowRootOptions.mode = 'open';
11
+ it('throws if it does not have a default slot', async () => {
12
+ const spy = sinon.spy();
13
+ try {
14
+ await fixture(html `<glide-core-menu-options></glide-core-menu-options>`);
15
+ }
16
+ catch (error) {
17
+ if (error instanceof ArgumentError) {
18
+ spy();
19
+ }
20
+ }
21
+ expect(spy.called).to.be.true;
22
+ });
23
+ it('throws if the default slot is the incorrect type', async () => {
24
+ await expectArgumentError(() => {
25
+ return fixture(html `<glide-core-menu-options>
26
+ <option>Option</option>
27
+ </glide-core-menu-options>`);
28
+ });
29
+ });
30
+ it('does not throw if the default slot only contains whitespace', async () => {
31
+ const spy = sinon.spy();
32
+ try {
33
+ await fixture(html `<glide-core-menu-options>
34
+ ${repeat([], () => html `<glide-core-menu-link label="Link"></glide-core-menu-link>`)}
35
+ </glide-core-menu-options>`);
36
+ }
37
+ catch (error) {
38
+ if (error instanceof ArgumentError) {
39
+ spy();
40
+ }
41
+ }
42
+ expect(spy.notCalled).to.be.true;
43
+ });
@@ -2,6 +2,7 @@ import './icons/storybook.js';
2
2
  import './menu.button.js';
3
3
  import './menu.js';
4
4
  import './menu.link.js';
5
+ import './menu.options.js';
5
6
  import type { Meta, StoryObj } from '@storybook/web-components';
6
7
  declare const meta: Meta;
7
8
  export default meta;
@@ -1,4 +1,4 @@
1
- "use strict";import{css as e}from"lit";export default[e`
1
+ import{css}from"lit";export default[css`
2
2
  :host {
3
3
  /* Contains elements with "padding", "margin", and "width". Inline by default. */
4
4
  display: inline-block;
@@ -9,17 +9,18 @@
9
9
  display: flex;
10
10
  }
11
11
 
12
- .target-container {
12
+ .target-slot {
13
13
  display: flex;
14
14
  position: relative;
15
15
  }
16
16
 
17
- .options {
17
+ .default-slot {
18
18
  background-color: var(--glide-core-surface-modal);
19
19
  border: 1px solid var(--glide-core-surface-modal);
20
20
  border-radius: var(--glide-core-spacing-xs);
21
21
  box-shadow: var(--glide-core-shadow-lg);
22
22
  box-sizing: border-box;
23
+ display: flex;
23
24
  inline-size: max-content;
24
25
  inset-block-start: 0;
25
26
  inset-inline-start: 0;
@@ -30,8 +31,8 @@
30
31
  visibility: hidden;
31
32
 
32
33
  /*
33
- ".target-container" is relative and many Menus may be stacked in a column.
34
- This ensures the ".options" of Menus earlier in the column aren't obscured
34
+ ".container" is relative and many Menus may be stacked in a column.
35
+ This ensures the ".menu" of Menus earlier in the column aren't obscured
35
36
  by the ".target-container" that come later.
36
37
  */
37
38
  z-index: 1;
@@ -39,30 +40,5 @@
39
40
  &.visible {
40
41
  visibility: visible;
41
42
  }
42
-
43
- &.large {
44
- --gap: var(--glide-core-spacing-sm);
45
- --padding-inline: var(--glide-core-spacing-sm);
46
- --padding-block: var(--glide-core-spacing-xxs);
47
-
48
- font-family: var(--glide-core-body-sm-font-family);
49
- font-size: var(--glide-core-body-sm-font-size);
50
- font-style: var(--glide-core-body-sm-font-style);
51
- font-weight: var(--glide-core-body-sm-font-weight);
52
- line-height: var(--glide-core-body-sm-line-height);
53
- }
54
-
55
- &.small {
56
- --gap: var(--glide-core-spacing-xs);
57
- --padding-inline: var(--glide-core-spacing-xs);
58
- --padding-block: var(--glide-core-spacing-xxxs);
59
- --size: 0.75rem;
60
-
61
- font-family: var(--glide-core-body-xs-font-family);
62
- font-size: var(--glide-core-body-xs-font-size);
63
- font-style: var(--glide-core-body-xs-font-style);
64
- font-weight: var(--glide-core-body-xs-font-weight);
65
- line-height: var(--glide-core-body-xs-line-height);
66
- }
67
43
  }
68
- `];
44
+ `];
@@ -1 +1,2 @@
1
1
  import './menu.button.js';
2
+ import './menu.link.js';
@@ -0,0 +1,183 @@
1
+ import './menu.button.js';
2
+ import './menu.link.js';
3
+ import { ArgumentError } from 'ow';
4
+ import { expect, fixture, html } from '@open-wc/testing';
5
+ import GlideCoreMenu from './menu.js';
6
+ import GlideCoreMenuOptions from './menu.options.js';
7
+ import expectArgumentError from './library/expect-argument-error.js';
8
+ import sinon from 'sinon';
9
+ GlideCoreMenu.shadowRootOptions.mode = 'open';
10
+ it('registers', async () => {
11
+ expect(window.customElements.get('glide-core-menu')).to.equal(GlideCoreMenu);
12
+ });
13
+ it('has defaults', async () => {
14
+ // Required attributes are supplied here and thus left unasserted below. The
15
+ // idea is that this test shouldn't fail to typecheck if these templates are
16
+ // eventually typechecked, which means supplying all required attributes and slots.
17
+ const component = await fixture(html `<glide-core-menu>
18
+ <button slot="target">Target</button>
19
+
20
+ <glide-core-menu-options>
21
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
22
+ </glide-core-menu-options>
23
+ </glide-core-menu>`);
24
+ const options = component.querySelector('glide-core-menu-options');
25
+ expect(component.getAttribute('size')).to.equal('large');
26
+ expect(component.size).to.equal('large');
27
+ expect(options?.privateSize).to.equal('large');
28
+ });
29
+ it('is accessible', async () => {
30
+ const component = await fixture(html `<glide-core-menu>
31
+ <button slot="target">Target</button>
32
+
33
+ <glide-core-menu-options>
34
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
35
+ </glide-core-menu-options>
36
+ </glide-core-menu>`);
37
+ const target = component.querySelector('button');
38
+ const options = component.querySelector('glide-core-menu-options');
39
+ expect(target?.getAttribute('aria-controls')).to.equal(options?.id);
40
+ expect(options?.ariaLabelledby).to.equal(target?.id);
41
+ await expect(component).to.be.accessible();
42
+ });
43
+ it('can be opened', async () => {
44
+ const component = await fixture(html `<glide-core-menu open>
45
+ <button slot="target">Target</button>
46
+
47
+ <glide-core-menu-options>
48
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
49
+ </glide-core-menu-options>
50
+ </glide-core-menu>`);
51
+ const defaultSlot = component?.shadowRoot?.querySelector('slot:not([name])');
52
+ const target = component.querySelector('button');
53
+ expect(component.open).to.be.true;
54
+ expect(defaultSlot?.checkVisibility({ checkVisibilityCSS: true })).to.be.true;
55
+ expect(target?.ariaExpanded).to.equal('true');
56
+ });
57
+ it('can have a default slot', async () => {
58
+ const component = await fixture(html `<glide-core-menu>
59
+ <button slot="target">Target</button>
60
+
61
+ <glide-core-menu-options>
62
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
63
+ </glide-core-menu-options>
64
+ </glide-core-menu>`);
65
+ const assignedElements = component.shadowRoot
66
+ ?.querySelectorAll('slot')[1]
67
+ .assignedElements();
68
+ expect(assignedElements?.at(0) instanceof GlideCoreMenuOptions).to.be.true;
69
+ });
70
+ it('can have a target slot', async () => {
71
+ const component = await fixture(html `<glide-core-menu>
72
+ <button slot="target">Target</button>
73
+
74
+ <glide-core-menu-options>
75
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
76
+ </glide-core-menu-options>
77
+ </glide-core-menu>`);
78
+ const assignedElements = component.shadowRoot
79
+ ?.querySelector('slot[name="target"]')
80
+ ?.assignedElements();
81
+ expect(assignedElements?.at(0)?.textContent).to.equal('Target');
82
+ });
83
+ it('activates the first menu link by default', async () => {
84
+ const component = await fixture(html `
85
+ <glide-core-menu open>
86
+ <button slot="target">Target</button>
87
+
88
+ <glide-core-menu-options>
89
+ <glide-core-menu-link label="One"></glide-core-menu-link>
90
+ <glide-core-menu-link label="Two"></glide-core-menu-link>
91
+ </glide-core-menu-options>
92
+ </glide-core-menu>
93
+ `);
94
+ const links = component.querySelectorAll('glide-core-menu-link');
95
+ const options = component.querySelector('glide-core-menu-options');
96
+ expect(links[0].privateActive).to.be.true;
97
+ expect(links[1].privateActive).to.be.false;
98
+ expect(options?.getAttribute('aria-activedescendant')).to.equal(links[0].id);
99
+ });
100
+ it('activates the first menu button by default', async () => {
101
+ const component = await fixture(html `
102
+ <glide-core-menu open>
103
+ <button slot="target">Target</button>
104
+
105
+ <glide-core-menu-options>
106
+ <glide-core-menu-button label="One"></glide-core-menu-button>
107
+ <glide-core-menu-button label="Two"></glide-core-menu-button>
108
+ </glide-core-menu-options>
109
+ </glide-core-menu>
110
+ `);
111
+ const buttons = component.querySelectorAll('glide-core-menu-button');
112
+ const options = component.querySelector('glide-core-menu-options');
113
+ expect(buttons[0].privateActive).to.be.true;
114
+ expect(buttons[1].privateActive).to.be.false;
115
+ expect(options?.getAttribute('aria-activedescendant')).to.equal(buttons[0].id);
116
+ });
117
+ it('is not opened when initially `open` and its target is `disabled`', async () => {
118
+ const component = await fixture(html `<glide-core-menu open>
119
+ <button slot="target" disabled>Target</button>
120
+
121
+ <glide-core-menu-options>
122
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
123
+ </glide-core-menu-options>
124
+ </glide-core-menu>`);
125
+ const defaultSlot = component?.shadowRoot?.querySelector('slot:not([name])');
126
+ const target = component.querySelector('button');
127
+ expect(defaultSlot?.checkVisibility({ checkVisibilityCSS: true })).not.be.ok;
128
+ expect(target?.ariaExpanded).to.equal('false');
129
+ });
130
+ it('throws if it does not have a default slot', async () => {
131
+ const spy = sinon.spy();
132
+ try {
133
+ await fixture(html `<glide-core-menu
134
+ ><button slot="target">Target</button></glide-core-menu
135
+ >`);
136
+ }
137
+ catch (error) {
138
+ if (error instanceof ArgumentError) {
139
+ spy();
140
+ }
141
+ }
142
+ expect(spy.called).to.be.true;
143
+ });
144
+ it('throws if the default slot is the incorrect type', async () => {
145
+ await expectArgumentError(() => {
146
+ return fixture(html `<glide-core-menu>
147
+ <option>Option</option>
148
+ <button slot="target">Target</button>
149
+ </glide-core-menu>`);
150
+ });
151
+ });
152
+ it('throws if it does not have a "target" slot', async () => {
153
+ const spy = sinon.spy();
154
+ try {
155
+ await fixture(html `<glide-core-menu>
156
+ <glide-core-menu-options>
157
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
158
+ </glide-core-menu-options>
159
+ </glide-core-menu>`);
160
+ }
161
+ catch (error) {
162
+ if (error instanceof ArgumentError) {
163
+ spy();
164
+ }
165
+ }
166
+ expect(spy.called).to.be.true;
167
+ });
168
+ it('sets accessibility attributes', async () => {
169
+ const component = await fixture(html `<glide-core-menu>
170
+ <button slot="target">Target</button>
171
+
172
+ <glide-core-menu-options>
173
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
174
+ </glide-core-menu-options>
175
+ </glide-core-menu>`);
176
+ const target = component.querySelector('button');
177
+ const options = component.querySelector('glide-core-menu-options');
178
+ expect(target?.getAttribute('aria-expanded')).to.equal('false');
179
+ expect(target?.getAttribute('aria-haspopup')).to.equal('true');
180
+ expect(target?.ariaExpanded).to.equal('false');
181
+ expect(target?.ariaHasPopup).to.equal('true');
182
+ expect(options?.ariaLabelledby).to.equal(target?.id);
183
+ });
@@ -1,2 +1 @@
1
- import './menu.js';
2
1
  import './menu.link.js';
@@ -0,0 +1,84 @@
1
+ import './menu.link.js';
2
+ import { assert, elementUpdated, expect, fixture, html, } from '@open-wc/testing';
3
+ import { sendKeys } from '@web/test-runner-commands';
4
+ import GlideCoreMenu from './menu.js';
5
+ import GlideCoreMenuOptions from './menu.options.js';
6
+ GlideCoreMenu.shadowRootOptions.mode = 'open';
7
+ it('focuses the target on `focus()`', async () => {
8
+ const component = await fixture(html `<glide-core-menu>
9
+ <button slot="target">Target</button>
10
+
11
+ <glide-core-menu-options>
12
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
13
+ </glide-core-menu-options>
14
+ </glide-core-menu>`);
15
+ component.focus();
16
+ const target = component.querySelector('button');
17
+ assert(target);
18
+ expect(document.activeElement).to.equal(target);
19
+ });
20
+ it('closes when it loses focus', async () => {
21
+ const component = await fixture(html `<glide-core-menu open>
22
+ <button slot="target">Target</button>
23
+
24
+ <glide-core-menu-options>
25
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
26
+ </glide-core-menu-options>
27
+ </glide-core-menu>`);
28
+ component.focus();
29
+ await sendKeys({ press: 'Tab' });
30
+ const defaultSlot = component?.shadowRoot?.querySelector('slot:not([name])');
31
+ const options = component.querySelector('glide-core-menu-options');
32
+ expect(component.open).to.be.false;
33
+ expect(defaultSlot?.checkVisibility({ checkVisibilityCSS: true })).be.false;
34
+ expect(options?.getAttribute('aria-activedescendant')).to.equal('');
35
+ });
36
+ it('remains open when the options component is focused', async () => {
37
+ const component = await fixture(html `<glide-core-menu open>
38
+ <button slot="target">Target</button>
39
+
40
+ <glide-core-menu-options>
41
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
42
+ </glide-core-menu-options>
43
+ </glide-core-menu>`);
44
+ component.focus();
45
+ const options = component.querySelector('glide-core-menu-options');
46
+ assert(options instanceof GlideCoreMenuOptions);
47
+ options.focus();
48
+ const defaultSlot = component?.shadowRoot?.querySelector('slot:not([name])');
49
+ expect(component.open).to.be.true;
50
+ expect(defaultSlot?.checkVisibility({ checkVisibilityCSS: true })).to.be.true;
51
+ });
52
+ it('remains open when an option is focused', async () => {
53
+ const component = await fixture(html `<glide-core-menu open>
54
+ <button slot="target">Target</button>
55
+
56
+ <glide-core-menu-options>
57
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
58
+ </glide-core-menu-options>
59
+ </glide-core-menu>`);
60
+ component.focus();
61
+ const option = component.querySelector('glide-core-menu-link');
62
+ assert(option);
63
+ option?.focus();
64
+ expect(component.open).to.be.true;
65
+ });
66
+ it('sets the focused option as active', async () => {
67
+ const component = await fixture(html `<glide-core-menu open>
68
+ <button slot="target">Target</button>
69
+
70
+ <glide-core-menu-options>
71
+ <glide-core-menu-button label="Button"></glide-core-menu-button>
72
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
73
+ </glide-core-menu-options>
74
+ </glide-core-menu>`);
75
+ component.focus();
76
+ const target = component.querySelector('glide-core-menu-button');
77
+ const link = component.querySelector('glide-core-menu-link');
78
+ const options = component.querySelector('glide-core-menu-options');
79
+ link?.focus();
80
+ await elementUpdated(component);
81
+ expect(target?.privateActive).to.be.false;
82
+ expect(link?.privateActive).to.be.true;
83
+ expect(options?.getAttribute('aria-activedescendant')).to.equal(link?.id);
84
+ });
@@ -1,2 +1,2 @@
1
- import './menu.js';
2
1
  import './menu.link.js';
2
+ import './menu.options.js';