@crowdstrike/glide-core 0.7.0 → 0.9.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 (203) hide show
  1. package/README.md +44 -5
  2. package/dist/accordion.test.basics.js +1 -0
  3. package/dist/accordion.test.events.js +1 -0
  4. package/dist/button-group.button.d.ts +14 -15
  5. package/dist/button-group.button.js +1 -1
  6. package/dist/button-group.button.styles.js +75 -52
  7. package/dist/button-group.button.test.basics.d.ts +1 -1
  8. package/dist/button-group.button.test.basics.js +84 -147
  9. package/dist/button-group.button.test.events.js +9 -67
  10. package/dist/button-group.button.test.focus.js +13 -0
  11. package/dist/button-group.button.test.interactions.d.ts +1 -0
  12. package/dist/button-group.button.test.interactions.js +42 -0
  13. package/dist/button-group.d.ts +7 -10
  14. package/dist/button-group.js +1 -1
  15. package/dist/button-group.stories.d.ts +1 -5
  16. package/dist/button-group.styles.js +18 -6
  17. package/dist/button-group.test.basics.js +114 -234
  18. package/dist/button-group.test.events.js +211 -263
  19. package/dist/button-group.test.focus.d.ts +1 -0
  20. package/dist/button-group.test.focus.js +39 -0
  21. package/dist/button-group.test.interactions.d.ts +1 -0
  22. package/dist/button-group.test.interactions.js +91 -0
  23. package/dist/button.test.basics.js +2 -1
  24. package/dist/button.test.events.js +1 -0
  25. package/dist/button.test.form.js +1 -0
  26. package/dist/checkbox-group.js +1 -1
  27. package/dist/checkbox-group.styles.js +1 -1
  28. package/dist/checkbox-group.test.basics.js +2 -1
  29. package/dist/checkbox-group.test.events.js +5 -4
  30. package/dist/checkbox-group.test.focus.js +5 -3
  31. package/dist/checkbox-group.test.form.js +1 -0
  32. package/dist/checkbox-group.test.validity.js +1 -0
  33. package/dist/checkbox.d.ts +7 -1
  34. package/dist/checkbox.js +1 -1
  35. package/dist/checkbox.styles.js +11 -3
  36. package/dist/checkbox.test.basics.js +1 -0
  37. package/dist/checkbox.test.events.js +5 -4
  38. package/dist/checkbox.test.focus.js +2 -2
  39. package/dist/checkbox.test.form.js +1 -0
  40. package/dist/{checkbox.test.states.js → checkbox.test.interactions.js} +25 -1
  41. package/dist/checkbox.test.validity.js +1 -0
  42. package/dist/drawer.js +1 -1
  43. package/dist/drawer.test.basics.js +1 -0
  44. package/dist/drawer.test.closing.js +1 -0
  45. package/dist/drawer.test.events.js +1 -0
  46. package/dist/drawer.test.methods.js +1 -0
  47. package/dist/dropdown.d.ts +6 -4
  48. package/dist/dropdown.js +1 -1
  49. package/dist/dropdown.option.d.ts +7 -2
  50. package/dist/dropdown.option.js +1 -1
  51. package/dist/dropdown.option.styles.js +13 -0
  52. package/dist/dropdown.option.test.basics.js +7 -3
  53. package/dist/dropdown.option.test.basics.multiple.js +1 -0
  54. package/dist/dropdown.option.test.basics.single.js +1 -0
  55. package/dist/dropdown.option.test.events.js +2 -1
  56. package/dist/dropdown.option.test.focus.js +1 -1
  57. package/dist/dropdown.option.test.interactions.multiple.js +2 -54
  58. package/dist/dropdown.option.test.interactions.single.js +52 -9
  59. package/dist/dropdown.styles.js +20 -19
  60. package/dist/dropdown.test.basics.filterable.js +1 -0
  61. package/dist/dropdown.test.basics.js +144 -2
  62. package/dist/dropdown.test.basics.multiple.js +6 -3
  63. package/dist/dropdown.test.basics.single.js +1 -1
  64. package/dist/dropdown.test.events.filterable.js +74 -0
  65. package/dist/dropdown.test.events.js +50 -160
  66. package/dist/dropdown.test.events.multiple.js +268 -10
  67. package/dist/dropdown.test.events.single.js +202 -4
  68. package/dist/dropdown.test.focus.filterable.js +9 -5
  69. package/dist/dropdown.test.focus.js +2 -1
  70. package/dist/dropdown.test.focus.multiple.js +1 -2
  71. package/dist/dropdown.test.focus.single.js +1 -1
  72. package/dist/dropdown.test.form.js +1 -0
  73. package/dist/dropdown.test.form.multiple.js +1 -0
  74. package/dist/dropdown.test.form.single.js +1 -0
  75. package/dist/dropdown.test.interactions.filterable.js +69 -11
  76. package/dist/dropdown.test.interactions.js +95 -5
  77. package/dist/dropdown.test.interactions.multiple.js +203 -6
  78. package/dist/dropdown.test.interactions.single.js +69 -6
  79. package/dist/dropdown.test.validity.js +1 -0
  80. package/dist/form-controls-layout.test.basics.js +2 -1
  81. package/dist/icon-button.test.basics.js +2 -1
  82. package/dist/icons/checked.d.ts +1 -1
  83. package/dist/icons/checked.js +1 -1
  84. package/dist/icons/magnifying-glass.js +1 -1
  85. package/dist/input.d.ts +0 -6
  86. package/dist/input.js +1 -1
  87. package/dist/input.styles.js +7 -2
  88. package/dist/input.test.basics.js +20 -5
  89. package/dist/input.test.events.js +5 -4
  90. package/dist/input.test.focus.js +5 -4
  91. package/dist/input.test.form.js +1 -0
  92. package/dist/input.test.translations.d.ts +1 -0
  93. package/dist/input.test.translations.js +38 -0
  94. package/dist/input.test.validity.js +134 -4
  95. package/dist/label.d.ts +1 -1
  96. package/dist/label.js +1 -1
  97. package/dist/label.styles.js +29 -20
  98. package/dist/label.test.basics.js +27 -24
  99. package/dist/library/expect-argument-error.js +1 -1
  100. package/dist/library/localize.d.ts +5 -1
  101. package/dist/library/ow.test.d.ts +2 -1
  102. package/dist/library/ow.test.js +8 -3
  103. package/dist/menu.button.test.basics.js +1 -0
  104. package/dist/menu.d.ts +3 -5
  105. package/dist/menu.js +1 -1
  106. package/dist/menu.link.test.basics.js +1 -0
  107. package/dist/menu.options.test.basics.js +3 -2
  108. package/dist/menu.styles.js +1 -15
  109. package/dist/menu.test.basics.d.ts +1 -2
  110. package/dist/menu.test.basics.js +23 -6
  111. package/dist/menu.test.events.d.ts +1 -0
  112. package/dist/menu.test.events.js +2 -1
  113. package/dist/menu.test.focus.d.ts +1 -0
  114. package/dist/menu.test.focus.js +14 -6
  115. package/dist/menu.test.interactions.js +213 -56
  116. package/dist/modal.icon-button.test.basics.js +2 -1
  117. package/dist/modal.js +1 -1
  118. package/dist/modal.styles.js +18 -13
  119. package/dist/modal.tertiary-icon.d.ts +0 -1
  120. package/dist/modal.tertiary-icon.js +1 -1
  121. package/dist/modal.tertiary-icon.test.basics.js +2 -1
  122. package/dist/modal.test.accessibility.js +1 -0
  123. package/dist/modal.test.basics.js +2 -1
  124. package/dist/modal.test.close.js +1 -0
  125. package/dist/modal.test.events.js +11 -10
  126. package/dist/modal.test.lock-scroll.js +1 -0
  127. package/dist/modal.test.methods.js +1 -0
  128. package/dist/modal.test.scrollbars.js +1 -0
  129. package/dist/radio-group.js +1 -1
  130. package/dist/radio-group.styles.js +1 -1
  131. package/dist/radio-group.test.basics.js +1 -0
  132. package/dist/radio-group.test.events.js +1 -0
  133. package/dist/radio-group.test.focus.js +4 -3
  134. package/dist/radio-group.test.form.js +1 -0
  135. package/dist/radio-group.test.validity.js +1 -0
  136. package/dist/radio.d.ts +1 -0
  137. package/dist/radio.js +1 -1
  138. package/dist/radio.styles.js +33 -0
  139. package/dist/split-button.test.basics.js +1 -0
  140. package/dist/split-container.test.basics.js +5 -0
  141. package/dist/split-link.test.basics.js +1 -0
  142. package/dist/split-link.test.interactions.js +2 -1
  143. package/dist/styles/variables.css +1 -1
  144. package/dist/tab.d.ts +1 -3
  145. package/dist/tab.group.d.ts +3 -5
  146. package/dist/tab.group.js +1 -1
  147. package/dist/tab.group.styles.js +27 -13
  148. package/dist/tab.group.test.basics.js +8 -57
  149. package/dist/tab.group.test.interactions.d.ts +3 -0
  150. package/dist/tab.group.test.interactions.js +454 -0
  151. package/dist/tab.js +1 -1
  152. package/dist/tab.panel.d.ts +1 -0
  153. package/dist/tab.panel.js +1 -1
  154. package/dist/tab.panel.styles.js +11 -1
  155. package/dist/tab.styles.js +7 -68
  156. package/dist/tab.test.basics.js +0 -20
  157. package/dist/tabs.stories.d.ts +1 -2
  158. package/dist/tag.test.basics.js +3 -2
  159. package/dist/textarea.d.ts +0 -1
  160. package/dist/textarea.js +2 -2
  161. package/dist/textarea.stories.d.ts +3 -4
  162. package/dist/textarea.styles.js +14 -3
  163. package/dist/textarea.test.basics.js +81 -44
  164. package/dist/textarea.test.events.js +57 -41
  165. package/dist/textarea.test.form.js +1 -0
  166. package/dist/textarea.test.translations.d.ts +1 -0
  167. package/dist/textarea.test.translations.js +34 -0
  168. package/dist/textarea.test.validity.js +105 -20
  169. package/dist/toasts.js +1 -1
  170. package/dist/toasts.styles.js +8 -1
  171. package/dist/toasts.test.basics.js +20 -0
  172. package/dist/toggle.js +1 -1
  173. package/dist/toggle.test.basics.js +1 -0
  174. package/dist/toggle.test.events.js +1 -0
  175. package/dist/toggle.test.focus.js +1 -1
  176. package/dist/toggle.test.interactions.d.ts +1 -0
  177. package/dist/{toggle.test.states.js → toggle.test.interactions.js} +1 -0
  178. package/dist/tooltip.d.ts +7 -5
  179. package/dist/tooltip.js +1 -1
  180. package/dist/tooltip.styles.js +90 -25
  181. package/dist/tooltip.test.basics.js +39 -3
  182. package/dist/tooltip.test.interactions.js +137 -34
  183. package/dist/translations/en.js +1 -1
  184. package/dist/translations/fr.js +1 -1
  185. package/dist/translations/ja.js +1 -1
  186. package/dist/tree.d.ts +0 -1
  187. package/dist/tree.item.d.ts +2 -3
  188. package/dist/tree.item.js +1 -1
  189. package/dist/tree.item.menu.d.ts +0 -1
  190. package/dist/tree.item.menu.js +1 -1
  191. package/dist/tree.item.test.basics.js +1 -0
  192. package/dist/tree.js +1 -1
  193. package/dist/tree.test.basics.js +2 -1
  194. package/dist/tree.test.events.js +1 -1
  195. package/package.json +40 -29
  196. package/dist/drawer.test.floating-components.d.ts +0 -1
  197. package/dist/drawer.test.floating-components.js +0 -51
  198. package/dist/library/set-containing-block.d.ts +0 -15
  199. package/dist/library/set-containing-block.js +0 -1
  200. package/dist/modal.test.floating-components.js +0 -62
  201. /package/dist/{checkbox.test.states.d.ts → button-group.button.test.focus.d.ts} +0 -0
  202. /package/dist/{modal.test.floating-components.d.ts → checkbox.test.interactions.d.ts} +0 -0
  203. /package/dist/{toggle.test.states.d.ts → dropdown.test.events.filterable.d.ts} +0 -0
@@ -1,14 +1,17 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
1
2
  import './input.js';
2
- import { expect, fixture, html } from '@open-wc/testing';
3
+ import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
3
4
  import { sendKeys } from '@web/test-runner-commands';
4
- import Input from './input.js';
5
- Input.shadowRootOptions.mode = 'open';
5
+ import GlideCoreInput from './input.js';
6
+ GlideCoreInput.shadowRootOptions.mode = 'open';
6
7
  it('is valid if empty but not required', async () => {
7
8
  const input = await fixture(html `<glide-core-input></glide-core-input>`);
8
9
  expect(input.validity?.valid).to.be.true;
9
10
  expect(input.validity?.valueMissing).to.be.false;
10
11
  expect(input.checkValidity()).to.be.true;
11
12
  expect(input.reportValidity()).to.be.true;
13
+ await elementUpdated(input);
14
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'false');
12
15
  });
13
16
  it('is valid after being filled in when empty and required', async () => {
14
17
  const input = await fixture(html `<glide-core-input required></glide-core-input>`);
@@ -18,6 +21,8 @@ it('is valid after being filled in when empty and required', async () => {
18
21
  expect(input.validity?.valueMissing).to.be.false;
19
22
  expect(input.checkValidity()).to.be.true;
20
23
  expect(input.reportValidity()).to.be.true;
24
+ await elementUpdated(input);
25
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'false');
21
26
  });
22
27
  it('is invalid if no value and required', async () => {
23
28
  const input = await fixture(html `<glide-core-input required></glide-core-input>`);
@@ -26,6 +31,8 @@ it('is invalid if no value and required', async () => {
26
31
  expect(input.willValidate).to.be.true;
27
32
  expect(input.checkValidity()).to.be.false;
28
33
  expect(input.reportValidity()).to.be.false;
34
+ await elementUpdated(input);
35
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'true');
29
36
  });
30
37
  it('is invalid after value is cleared when required', async () => {
31
38
  const input = await fixture(html `<glide-core-input
@@ -33,13 +40,76 @@ it('is invalid after value is cleared when required', async () => {
33
40
  value="value"
34
41
  required
35
42
  ></glide-core-input>`);
36
- const clearButton = input.shadowRoot?.querySelector('.clear-icon-button');
43
+ const clearButton = input.shadowRoot?.querySelector('[data-test="clear-button"]');
37
44
  clearButton?.click();
38
45
  await input.updateComplete;
39
46
  expect(input.validity?.valid).to.be.false;
40
47
  expect(input.validity?.valueMissing).to.be.true;
41
48
  expect(input.checkValidity()).to.be.false;
42
49
  expect(input.reportValidity()).to.be.false;
50
+ await elementUpdated(input);
51
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'true');
52
+ });
53
+ it('is valid when empty and does not exceed `maxlength`', async () => {
54
+ const input = await fixture(html `<glide-core-input maxlength="3"></glide-core-input>`);
55
+ expect(input.validity?.valid).to.be.true;
56
+ expect(input.validity?.valueMissing).to.be.false;
57
+ expect(input.validity?.tooLong).to.be.false;
58
+ expect(input.checkValidity()).to.be.true;
59
+ expect(input.reportValidity()).to.be.true;
60
+ await elementUpdated(input);
61
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'false');
62
+ });
63
+ it('is valid when filled in and does not exceed `maxlength`', async () => {
64
+ const input = await fixture(html `<glide-core-input maxlength="3"></glide-core-input>`);
65
+ input.focus();
66
+ await sendKeys({ type: 'val' });
67
+ expect(input.validity?.valid).to.be.true;
68
+ expect(input.validity?.valueMissing).to.be.false;
69
+ expect(input.validity?.tooLong).to.be.false;
70
+ expect(input.checkValidity()).to.be.true;
71
+ expect(input.reportValidity()).to.be.true;
72
+ await elementUpdated(input);
73
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'false');
74
+ });
75
+ it('is valid when filled in, disabled, and value exceeds `maxlength`', async () => {
76
+ const input = await fixture(html `<glide-core-input
77
+ value="value"
78
+ maxlength="3"
79
+ disabled
80
+ ></glide-core-input>`);
81
+ expect(input.validity?.valid).to.be.true;
82
+ expect(input.validity?.valueMissing).to.be.false;
83
+ expect(input.validity?.tooLong).to.be.false;
84
+ expect(input.checkValidity()).to.be.true;
85
+ expect(input.reportValidity()).to.be.true;
86
+ await elementUpdated(input);
87
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'false');
88
+ });
89
+ it('is valid when filled in, readonly, and value exceeds `maxlength`', async () => {
90
+ const input = await fixture(html `<glide-core-input
91
+ value="value"
92
+ maxlength="3"
93
+ readonly
94
+ ></glide-core-input>`);
95
+ expect(input.validity?.valid).to.be.true;
96
+ expect(input.validity?.valueMissing).to.be.false;
97
+ expect(input.validity?.tooLong).to.be.false;
98
+ expect(input.checkValidity()).to.be.true;
99
+ expect(input.reportValidity()).to.be.true;
100
+ await elementUpdated(input);
101
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'false');
102
+ });
103
+ it('is invalid when filled in and exceeds `maxlength`', async () => {
104
+ const input = await fixture(html `<glide-core-input maxlength="3"></glide-core-input>`);
105
+ input.focus();
106
+ await sendKeys({ type: 'value' });
107
+ expect(input.validity?.valid).to.be.false;
108
+ expect(input.validity?.tooLong).to.be.true;
109
+ expect(input.checkValidity()).to.be.false;
110
+ expect(input.reportValidity()).to.be.false;
111
+ await elementUpdated(input);
112
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'true');
43
113
  });
44
114
  it('is valid if no value and required but disabled', async () => {
45
115
  const input = await fixture(html `<glide-core-input disabled required></glide-core-input>`);
@@ -47,4 +117,64 @@ it('is valid if no value and required but disabled', async () => {
47
117
  expect(input.validity?.valueMissing).to.be.false;
48
118
  expect(input.checkValidity()).to.be.true;
49
119
  expect(input.reportValidity()).to.be.true;
120
+ await elementUpdated(input);
121
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'false');
122
+ });
123
+ it('updates validity when `required` and `value` is changed programmatically', async () => {
124
+ const input = await fixture(html `<glide-core-input label="Label" required></glide-core-input>`);
125
+ expect(input.validity?.valid).to.be.false;
126
+ expect(input.validity?.valueMissing).to.be.true;
127
+ expect(input.checkValidity()).to.be.false;
128
+ expect(input.reportValidity()).to.be.false;
129
+ await elementUpdated(input);
130
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'true');
131
+ input.value = 'text';
132
+ await elementUpdated(input);
133
+ expect(input.validity?.valid).to.be.true;
134
+ expect(input.validity?.valueMissing).to.be.false;
135
+ expect(input.checkValidity()).to.be.true;
136
+ expect(input.reportValidity()).to.be.true;
137
+ await elementUpdated(input);
138
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'false');
139
+ // Resetting the value to empty to ensure it goes
140
+ // back to an invalid state
141
+ input.value = '';
142
+ await elementUpdated(input);
143
+ expect(input.validity?.valid).to.be.false;
144
+ expect(input.validity?.valueMissing).to.be.true;
145
+ expect(input.checkValidity()).to.be.false;
146
+ expect(input.reportValidity()).to.be.false;
147
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'true');
148
+ });
149
+ it('is invalid when `value` is empty and `required` is set to `true` programmatically', async () => {
150
+ const input = await fixture(html `<glide-core-input label="Label"></glide-core-input>`);
151
+ expect(input.validity?.valid).to.be.true;
152
+ expect(input.validity?.valueMissing).to.be.false;
153
+ expect(input.checkValidity()).to.be.true;
154
+ expect(input.reportValidity()).to.be.true;
155
+ await elementUpdated(input);
156
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'false');
157
+ input.required = true;
158
+ await elementUpdated(input);
159
+ expect(input.validity?.valid).to.be.false;
160
+ expect(input.validity?.valueMissing).to.be.true;
161
+ expect(input.checkValidity()).to.be.false;
162
+ expect(input.reportValidity()).to.be.false;
163
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'true');
164
+ });
165
+ it('is valid when `value` is empty and `required` is set to `false` programmatically', async () => {
166
+ const input = await fixture(html `<glide-core-input label="Label" required></glide-core-input>`);
167
+ expect(input.validity?.valid).to.be.false;
168
+ expect(input.validity?.valueMissing).to.be.true;
169
+ expect(input.checkValidity()).to.be.false;
170
+ expect(input.reportValidity()).to.be.false;
171
+ await elementUpdated(input);
172
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'true');
173
+ input.required = false;
174
+ await elementUpdated(input);
175
+ expect(input.validity?.valid).to.be.true;
176
+ expect(input.validity?.valueMissing).to.be.false;
177
+ expect(input.checkValidity()).to.be.true;
178
+ expect(input.reportValidity()).to.be.true;
179
+ expect(input.shadowRoot?.querySelector('input')).to.have.attribute('aria-invalid', 'false');
50
180
  });
package/dist/label.d.ts CHANGED
@@ -2,7 +2,7 @@ import './tooltip.js';
2
2
  import { LitElement } from 'lit';
3
3
  declare global {
4
4
  interface HTMLElementTagNameMap {
5
- 'glide-core-label': GlideCoreLabel;
5
+ 'glide-core-private-label': GlideCoreLabel;
6
6
  }
7
7
  }
8
8
  /**
package/dist/label.js CHANGED
@@ -1 +1 @@
1
- var __decorate=this&&this.__decorate||function(e,t,o,l){var i,s=arguments.length,r=s<3?t:null===l?l=Object.getOwnPropertyDescriptor(t,o):l;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,o,l);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(r=(s<3?i(r):s>3?i(t,o,r):i(t,o))||r);return s>3&&r&&Object.defineProperty(t,o,r),r};import"./tooltip.js";import{LitElement,html}from"lit";import{LocalizeController}from"./library/localize.js";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property,state}from"lit/decorators.js";import ow,{owSlot}from"./library/ow.js";import styles from"./label.styles.js";import{svg}from"lit";const infoCircleIcon=svg`<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"><path d="M12 16L12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="8" r="1" fill="currentColor">`;let GlideCoreLabel=class GlideCoreLabel extends LitElement{constructor(){super(...arguments),this.disabled=!1,this.error=!1,this.hide=!1,this.orientation="horizontal",this.required=!1,this.hasDescriptionSlot=!1,this.hasSummarySlot=!1,this.hasTooltipSlot=!1,this.isLabelTooltip=!1,this.label="",this.#e=createRef(),this.#t=createRef(),this.#o=createRef(),this.#l=createRef(),this.#i=new LocalizeController(this),this.#s=createRef(),this.#r=createRef()}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}firstUpdated(){owSlot(this.#t.value),owSlot(this.#e.value)}render(){return html`<div class="${classMap({component:!0,horizontal:"horizontal"===this.orientation,vertical:"vertical"===this.orientation,left:"left"===this.split,middle:"middle"===this.split,"hidden-label":this.hide})}"><div class="${classMap({"tooltips-and-label":!0,hidden:this.hide,left:"left"===this.split,middle:"middle"===this.split})}" part="tooltips-and-label"><glide-core-tooltip class="${classMap({"optional-tooltip":!0,vertical:"vertical"===this.orientation,visible:this.hasTooltipSlot})}" placement="${"vertical"===this.orientation?"right":"bottom"}"><span class="optional-tooltip-target" slot="target" tabindex="0"><svg aria-label="${this.#i.term("moreInformation")}" width="16" height="16" viewBox="0 0 24 24" fill="none">${infoCircleIcon}</svg></span><slot name="tooltip" @slotchange="${this.#a}" ${ref(this.#r)}></slot></glide-core-tooltip><glide-core-tooltip class="label-overflow-tooltip" placement="right" ?disabled="${!this.isLabelTooltip}"><div class="${classMap({label:!0,disabled:this.disabled})}" data-test="label" slot="target" ${ref(this.#l)}><slot @slotchange="${this.#n}" ${ref(this.#t)}></slot>${this.required?html`<span aria-hidden="true" class="required-symbol">*</span>`:""}</div><div aria-hidden="true">${this.label}</div></glide-core-tooltip></div><div class="control-and-summary"><slot class="${classMap({control:!0,error:this.error,disabled:this.disabled,vertical:"vertical"===this.orientation,summaryless:!this.hasSummarySlot,"hidden-label":this.hide})}" name="control" @slotchange="${this.#d}" ${ref(this.#e)}></slot><slot class="${classMap({summary:!0,error:this.error})}" name="summary" @slotchange="${this.#c}" ${ref(this.#s)}></slot></div><slot class="${classMap({description:!0,visible:this.hasDescriptionSlot,error:this.error,tooltip:this.hasTooltipSlot})}" id="description" name="description" @slotchange="${this.#h}" ${ref(this.#o)}></slot></div>`}#e;#t;#o;#l;#i;#s;#r;#d(){owSlot(this.#e.value)}#n(){owSlot(this.#t.value);const e=this.#t.value?.assignedElements().at(0),t=this.#l.value;ow(e,ow.object.instanceOf(Element)),ow(t,ow.object.instanceOf(HTMLElement)),e.textContent&&(this.label=e.textContent);new ResizeObserver((()=>{this.isLabelTooltip=e.getBoundingClientRect().width>t.getBoundingClientRect().width})).observe(t)}#h(){const e=this.#o.value?.assignedNodes({flatten:!0});this.hasDescriptionSlot=Boolean(e&&e.length>0)}#c(){const e=this.#s.value?.assignedNodes({flatten:!0});this.hasSummarySlot=Boolean(e&&e.length>0)}#a(){const e=this.#r.value?.assignedNodes({flatten:!0});this.hasTooltipSlot=Boolean(e&&e.length>0)}};__decorate([property({reflect:!0,type:Boolean})],GlideCoreLabel.prototype,"disabled",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreLabel.prototype,"error",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreLabel.prototype,"hide",void 0),__decorate([property({reflect:!0})],GlideCoreLabel.prototype,"orientation",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreLabel.prototype,"required",void 0),__decorate([property()],GlideCoreLabel.prototype,"split",void 0),__decorate([state()],GlideCoreLabel.prototype,"hasDescriptionSlot",void 0),__decorate([state()],GlideCoreLabel.prototype,"hasSummarySlot",void 0),__decorate([state()],GlideCoreLabel.prototype,"hasTooltipSlot",void 0),__decorate([state()],GlideCoreLabel.prototype,"isLabelTooltip",void 0),__decorate([state()],GlideCoreLabel.prototype,"label",void 0),GlideCoreLabel=__decorate([customElement("glide-core-label")],GlideCoreLabel);export default GlideCoreLabel;
1
+ var __decorate=this&&this.__decorate||function(e,t,o,l){var i,s=arguments.length,r=s<3?t:null===l?l=Object.getOwnPropertyDescriptor(t,o):l;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,o,l);else for(var a=e.length-1;a>=0;a--)(i=e[a])&&(r=(s<3?i(r):s>3?i(t,o,r):i(t,o))||r);return s>3&&r&&Object.defineProperty(t,o,r),r};import"./tooltip.js";import{LitElement,html,svg}from"lit";import{LocalizeController}from"./library/localize.js";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property,state}from"lit/decorators.js";import ow,{owSlot}from"./library/ow.js";import styles from"./label.styles.js";const infoCircleIcon=svg`<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"><path d="M12 16L12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="8" r="1" fill="currentColor">`;let GlideCoreLabel=class GlideCoreLabel extends LitElement{constructor(){super(...arguments),this.disabled=!1,this.error=!1,this.hide=!1,this.orientation="horizontal",this.required=!1,this.hasDescriptionSlot=!1,this.hasSummarySlot=!1,this.hasTooltipSlot=!1,this.isLabelTooltip=!1,this.label="",this.#e=createRef(),this.#t=createRef(),this.#o=createRef(),this.#l=createRef(),this.#i=new LocalizeController(this),this.#s=createRef(),this.#r=createRef()}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}firstUpdated(){owSlot(this.#t.value),owSlot(this.#e.value)}render(){return html`<div class="${classMap({component:!0,horizontal:"horizontal"===this.orientation,vertical:"vertical"===this.orientation,left:"left"===this.split,middle:"middle"===this.split,"hidden-label":this.hide})}"><div class="${classMap({"tooltips-and-label":!0,hidden:this.hide,left:"left"===this.split,middle:"middle"===this.split})}" part="tooltips-and-label"><glide-core-tooltip class="${classMap({"optional-tooltip":!0,vertical:"vertical"===this.orientation,visible:this.hasTooltipSlot})}" placement="${"vertical"===this.orientation?"right":"bottom"}"><span class="optional-tooltip-target" slot="target" tabindex="0"><svg aria-label="${this.#i.term("moreInformation")}" width="16" height="16" viewBox="0 0 24 24" fill="none">${infoCircleIcon}</svg></span><slot name="tooltip" @slotchange="${this.#a}" ${ref(this.#r)}></slot></glide-core-tooltip><glide-core-tooltip class="label-tooltip" placement="right" ?disabled="${!this.isLabelTooltip}"><div class="${classMap({label:!0,disabled:this.disabled})}" data-test="label" slot="target" ${ref(this.#l)}><slot @slotchange="${this.#n}" ${ref(this.#t)}></slot>${this.required?html`<span aria-hidden="true" class="required-symbol">*</span>`:""}</div><div aria-hidden="true">${this.label}</div></glide-core-tooltip></div><div class="control-and-summary"><slot class="${classMap({control:!0,error:this.error,disabled:this.disabled,vertical:"vertical"===this.orientation,summaryless:!this.hasSummarySlot,"hidden-label":this.hide})}" name="control" @slotchange="${this.#d}" ${ref(this.#e)}></slot><slot class="${classMap({summary:!0,error:this.error})}" name="summary" @slotchange="${this.#c}" ${ref(this.#s)}></slot></div><slot class="${classMap({description:!0,visible:this.hasDescriptionSlot,error:this.error,tooltip:this.hasTooltipSlot})}" id="description" name="description" @slotchange="${this.#h}" ${ref(this.#o)}></slot></div>`}#e;#t;#o;#l;#i;#s;#r;#d(){owSlot(this.#e.value)}#n(){owSlot(this.#t.value);const e=this.#t.value?.assignedElements().at(0),t=this.#l.value;ow(e,ow.object.instanceOf(Element)),ow(t,ow.object.instanceOf(HTMLElement)),e.textContent&&(this.label=e.textContent);new ResizeObserver((()=>{this.isLabelTooltip=e.getBoundingClientRect().width>t.getBoundingClientRect().width})).observe(t)}#h(){const e=this.#o.value?.assignedNodes({flatten:!0});this.hasDescriptionSlot=Boolean(e&&e.length>0)}#c(){const e=this.#s.value?.assignedNodes({flatten:!0});this.hasSummarySlot=Boolean(e&&e.length>0)}#a(){const e=this.#r.value?.assignedNodes({flatten:!0});this.hasTooltipSlot=Boolean(e&&e.length>0)}};__decorate([property({reflect:!0,type:Boolean})],GlideCoreLabel.prototype,"disabled",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreLabel.prototype,"error",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreLabel.prototype,"hide",void 0),__decorate([property({reflect:!0})],GlideCoreLabel.prototype,"orientation",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreLabel.prototype,"required",void 0),__decorate([property()],GlideCoreLabel.prototype,"split",void 0),__decorate([state()],GlideCoreLabel.prototype,"hasDescriptionSlot",void 0),__decorate([state()],GlideCoreLabel.prototype,"hasSummarySlot",void 0),__decorate([state()],GlideCoreLabel.prototype,"hasTooltipSlot",void 0),__decorate([state()],GlideCoreLabel.prototype,"isLabelTooltip",void 0),__decorate([state()],GlideCoreLabel.prototype,"label",void 0),GlideCoreLabel=__decorate([customElement("glide-core-private-label")],GlideCoreLabel);export default GlideCoreLabel;
@@ -3,13 +3,7 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import
3
3
  &.horizontal {
4
4
  column-gap: var(--glide-core-spacing-sm);
5
5
  display: grid;
6
-
7
- /*
8
- Since 1fr is actually minmax(auto, 1fr), this explicit minmax will make it so
9
- the first column is sized to its content, and the second column fills the rest
10
- of the space.
11
- */
12
- grid-template-columns: auto minmax(0, 1fr);
6
+ grid-template-columns: auto minmax(min-content, 1fr);
13
7
  }
14
8
 
15
9
  &.vertical {
@@ -22,7 +16,7 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import
22
16
  }
23
17
 
24
18
  &.middle {
25
- grid-template-columns: 1fr 1fr;
19
+ grid-template-columns: 50% 50%;
26
20
  }
27
21
 
28
22
  &.hidden-label {
@@ -35,10 +29,21 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import
35
29
  align-items: center;
36
30
  column-gap: var(--glide-core-spacing-xs);
37
31
  display: flex;
38
- justify-content: flex-end;
39
32
 
40
- /* Prevent it from growing larger than its column percentage when a child of Form Controls Layout. */
41
- min-inline-size: 0;
33
+ /*
34
+ Prevents it from growing larger than its column percentage when a child of Form Controls
35
+ Layout. Also allows for an ellipsis on the label. See the linked comment for why it's "3ch"
36
+ instead of "0".
37
+
38
+ - https://css-tricks.com/flexbox-truncated-text/#aa-the-solution-is-min-width-0-on-the-flex-child
39
+ - https://github.com/CrowdStrike/glide-core/pull/317#issuecomment-2297025365
40
+ */
41
+ min-inline-size: 3ch;
42
+
43
+ &.middle,
44
+ &.left {
45
+ justify-content: flex-end;
46
+ }
42
47
 
43
48
  &.hidden {
44
49
  ${visuallyHidden};
@@ -54,9 +59,9 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import
54
59
 
55
60
  &.visible {
56
61
  /*
57
- The default is "display: content". But "order" does not work with
58
- "display: content" and "order" is needed above.
59
- */
62
+ The default is "display: content". But "order" does not work with
63
+ "display: content" and "order" is needed above.
64
+ */
60
65
  display: block;
61
66
  }
62
67
  }
@@ -68,10 +73,10 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import
68
73
  color: var(--glide-core-text-body-1);
69
74
 
70
75
  /*
71
- Any "display" that's not inline-level will do. We don't want the button to
72
- acquire a line box, which will make it taller than its content and thus
73
- make it difficult to center vertically with the label.
74
- */
76
+ Any "display" that's not inline-level will do. We don't want the button to
77
+ acquire a line box, which will make it taller than its content and thus
78
+ make it difficult to center vertically with the label.
79
+ */
75
80
 
76
81
  display: flex;
77
82
  padding: 0;
@@ -110,8 +115,12 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import
110
115
  }
111
116
  }
112
117
 
113
- .label-overflow-tooltip {
114
- inline-size: 100%;
118
+ .label-tooltip {
119
+ display: flex;
120
+ max-inline-size: 100%;
121
+
122
+ /* https://css-tricks.com/flexbox-truncated-text/#aa-the-solution-is-min-width-0-on-the-flex-child */
123
+ min-inline-size: 0;
115
124
  }
116
125
 
117
126
  .required-symbol {
@@ -1,16 +1,17 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
1
2
  import { ArgumentError } from 'ow';
2
3
  import { expect, fixture, html, waitUntil } from '@open-wc/testing';
3
4
  import GlideCoreLabel from './label.js';
4
5
  import sinon from 'sinon';
5
6
  GlideCoreLabel.shadowRootOptions.mode = 'open';
6
7
  it('registers', async () => {
7
- expect(window.customElements.get('glide-core-label')).to.equal(GlideCoreLabel);
8
+ expect(window.customElements.get('glide-core-private-label')).to.equal(GlideCoreLabel);
8
9
  });
9
10
  it('has defaults', async () => {
10
- const component = await fixture(html `<glide-core-label>
11
+ const component = await fixture(html `<glide-core-private-label>
11
12
  <label for="input">Label</label>
12
13
  <input id="input" slot="control" />
13
- </glide-core-label>`);
14
+ </glide-core-private-label>`);
14
15
  expect(component.getAttribute('error')).to.equal(null);
15
16
  expect(component.error).to.equal(false);
16
17
  expect(component.getAttribute('hide')).to.equal(null);
@@ -21,80 +22,80 @@ it('has defaults', async () => {
21
22
  expect(component.required).to.be.false;
22
23
  });
23
24
  it('is accessible', async () => {
24
- const component = await fixture(html `<glide-core-label>
25
+ const component = await fixture(html `<glide-core-private-label>
25
26
  <label for="input">Label</label>
26
27
  <input id="input" slot="control" />
27
28
  <div slot="tooltip">Tooltip</div>
28
29
  <div slot="description">Description</div>
29
- </glide-core-label>`);
30
+ </glide-core-private-label>`);
30
31
  await expect(component).to.be.accessible();
31
32
  });
32
33
  it('can have a label', async () => {
33
- const component = await fixture(html `<glide-core-label>
34
+ const component = await fixture(html `<glide-core-private-label>
34
35
  <label for="input">Label</label>
35
36
  <input id="input" slot="control" />
36
- </glide-core-label>`);
37
+ </glide-core-private-label>`);
37
38
  const assignedElements = component.shadowRoot
38
39
  ?.querySelector('slot:not([name])')
39
40
  ?.assignedElements();
40
41
  expect(assignedElements?.at(0)?.textContent).to.equal('Label');
41
42
  });
42
43
  it('can have a description', async () => {
43
- const component = await fixture(html `<glide-core-label>
44
+ const component = await fixture(html `<glide-core-private-label>
44
45
  <label for="input">Label</label>
45
46
  <input id="input" slot="control" />
46
47
  <div slot="description">Description</div>
47
- </glide-core-label>`);
48
+ </glide-core-private-label>`);
48
49
  const assignedElements = component.shadowRoot
49
50
  ?.querySelector('slot[name="description"]')
50
51
  ?.assignedElements();
51
52
  expect(assignedElements?.at(0)?.textContent).to.equal('Description');
52
53
  });
53
54
  it('can have a tooltip', async () => {
54
- const component = await fixture(html `<glide-core-label>
55
+ const component = await fixture(html `<glide-core-private-label>
55
56
  <label for="input">Label</label>
56
57
  <input id="input" slot="control" />
57
58
  <div slot="tooltip">Tooltip</div>
58
- </glide-core-label>`);
59
+ </glide-core-private-label>`);
59
60
  const assignedElements = component.shadowRoot
60
61
  ?.querySelector('slot[name="tooltip"]')
61
62
  ?.assignedElements();
62
63
  expect(assignedElements?.at(0)?.textContent).to.equal('Tooltip');
63
64
  });
64
65
  it('can be required', async () => {
65
- const component = await fixture(html `<glide-core-label required>
66
+ const component = await fixture(html `<glide-core-private-label required>
66
67
  <label for="input">Label</label>
67
68
  <input id="input" slot="control" />
68
- </glide-core-label>`);
69
+ </glide-core-private-label>`);
69
70
  expect(component.hasAttribute('required')).to.be.true;
70
71
  expect(component.required).to.equal(true);
71
72
  const label = component.shadowRoot?.querySelector('[data-test="label"]');
72
73
  expect(label?.textContent?.includes('*')).to.be.true;
73
74
  });
74
75
  it('can have an `error`', async () => {
75
- const component = await fixture(html `<glide-core-label ?error=${true}>
76
+ const component = await fixture(html `<glide-core-private-label ?error=${true}>
76
77
  <label for="input">Label</label>
77
78
  <input id="input" slot="control" />
78
- </glide-core-label>`);
79
+ </glide-core-private-label>`);
79
80
  expect(component.hasAttribute('error')).to.be.true;
80
81
  expect(component.error).to.equal(true);
81
82
  });
82
83
  it('places the tooltip on the bottom when horizontal', async () => {
83
- const component = await fixture(html `<glide-core-label>
84
+ const component = await fixture(html `<glide-core-private-label>
84
85
  <label for="input">Label</label>
85
86
  <input id="input" slot="control" />
86
87
  <div slot="tooltip">Tooltip</div>
87
- </glide-core-label>`);
88
+ </glide-core-private-label>`);
88
89
  expect(component.shadowRoot
89
90
  ?.querySelector('glide-core-tooltip')
90
91
  ?.getAttribute('placement')).to.equal('bottom');
91
92
  });
92
93
  it('places the tooltip on the right when vertical', async () => {
93
- const component = await fixture(html `<glide-core-label orientation="vertical">
94
+ const component = await fixture(html `<glide-core-private-label orientation="vertical">
94
95
  <label for="input">Label</label>
95
96
  <input id="input" slot="control" />
96
97
  <div slot="tooltip">Tooltip</div>
97
- </glide-core-label>`);
98
+ </glide-core-private-label>`);
98
99
  expect(component.shadowRoot
99
100
  ?.querySelector('glide-core-tooltip')
100
101
  ?.getAttribute('placement')).to.equal('right');
@@ -102,29 +103,31 @@ it('places the tooltip on the right when vertical', async () => {
102
103
  it('throws if it does not have a default slot', async () => {
103
104
  const spy = sinon.spy();
104
105
  try {
105
- await fixture(html `<glide-core-label><input slot="control" /></glide-core-label>`);
106
+ await fixture(html `<glide-core-private-label
107
+ ><input slot="control"
108
+ /></glide-core-private-label>`);
106
109
  }
107
110
  catch (error) {
108
111
  if (error instanceof ArgumentError) {
109
112
  spy();
110
113
  }
111
114
  }
112
- expect(spy.called).to.be.true;
115
+ expect(spy.callCount).to.equal(1);
113
116
  });
114
117
  it('throws if it does not have a "control" slot', async () => {
115
118
  const spy = sinon.spy();
116
119
  const stub = sinon.stub(console, 'error');
117
120
  try {
118
- await fixture(html `<glide-core-label>
121
+ await fixture(html `<glide-core-private-label>
119
122
  <label>Label</label>
120
- </glide-core-label>`);
123
+ </glide-core-private-label>`);
121
124
  }
122
125
  catch (error) {
123
126
  if (error instanceof ArgumentError) {
124
127
  spy();
125
128
  }
126
129
  }
127
- expect(spy.called).to.be.true;
130
+ expect(spy.callCount).to.equal(1);
128
131
  // It's not clear to me why the error logged by Ow shows up in the console
129
132
  // here and not in the above test or elsewhere. A bug in Web Test Runner?
130
133
  // Something I don't understand about Lit's lifecycle?
@@ -1 +1 @@
1
- import{ArgumentError}from"ow";import{expect}from"@open-wc/testing";import sinon from"sinon";export default async function(o){const r=window.onerror;window.onerror=null;const n=sinon.spy();try{await o.call(context)}catch(o){o instanceof ArgumentError&&n()}expect(n.called).to.be.true,window.onerror=r}
1
+ import{ArgumentError}from"ow";import{expect}from"@open-wc/testing";import sinon from"sinon";export default async function(o){const n=window.onerror;window.onerror=null;const r=sinon.spy();try{await o.call(context)}catch(o){o instanceof ArgumentError&&r()}expect(r.callCount).to.equal(1),window.onerror=n}
@@ -10,8 +10,12 @@ export interface Translation extends DefaultTranslation {
10
10
  dismiss: string;
11
11
  open: string;
12
12
  selectAll: string;
13
- clearEntry: string;
14
13
  moreInformation: string;
15
14
  notifications: string;
15
+ nextTab: string;
16
+ previousTab: string;
17
+ announcedCharacterCount: (current: number, maximum: number) => string;
18
+ displayedCharacterCount: (current: number, maximum: number) => string;
19
+ clearEntry: (label: string) => string;
16
20
  removeTag: (name: string) => string;
17
21
  }
@@ -1,4 +1,5 @@
1
1
  import { LitElement } from 'lit';
2
2
  export default class GlideCoreSlot extends LitElement {
3
- render(): any;
3
+ static shadowRootOptions: ShadowRootInit;
4
+ render(): import("lit").TemplateResult<1>;
4
5
  }
@@ -1,24 +1,29 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
1
2
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
3
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
4
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
5
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
6
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
7
  };
7
- /* eslint-disable @crowdstrike/glide-core-eslint-plugin/prefer-closed-shadow-root */
8
- /* This is a test file that doesn't contain one of our components, so we don't need to enforce this rule. */
9
8
  import { LitElement } from 'lit';
10
9
  import { assert, expect, fixture, html } from '@open-wc/testing';
11
10
  import { customElement } from 'lit/decorators.js';
11
+ import { html as litHtml } from 'lit';
12
12
  import { owSlot, owSlotType } from './ow.js';
13
13
  let GlideCoreSlot = class GlideCoreSlot extends LitElement {
14
+ static { this.shadowRootOptions = {
15
+ ...LitElement.shadowRootOptions,
16
+ mode: 'closed',
17
+ }; }
14
18
  render() {
15
- return html `<slot></slot>`;
19
+ return litHtml `<slot></slot>`;
16
20
  }
17
21
  };
18
22
  GlideCoreSlot = __decorate([
19
23
  customElement('glide-core-slot')
20
24
  ], GlideCoreSlot);
21
25
  export default GlideCoreSlot;
26
+ GlideCoreSlot.shadowRootOptions.mode = 'open';
22
27
  it('throws when a slot lacks a node', async () => {
23
28
  const component = await fixture(html `<glide-core-slot></glide-core-slot>`);
24
29
  const slot = component.shadowRoot?.querySelector('slot');
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
1
2
  import { expect, fixture, html } from '@open-wc/testing';
2
3
  import GlideCoreMenuButton from './menu.button.js';
3
4
  GlideCoreMenuButton.shadowRootOptions.mode = 'open';
package/dist/menu.d.ts CHANGED
@@ -8,7 +8,7 @@ declare global {
8
8
  /**
9
9
  * @description A basic menu.
10
10
  *
11
- * @slot - <glide-core-menu-options>.
11
+ * @slot - `<glide-core-menu-options>`.
12
12
  * @slot target - A focusable element against which Menu will be positioned. Opens and closes Menu when interacted with.
13
13
  */
14
14
  export default class GlideCoreMenu extends LitElement {
@@ -18,15 +18,13 @@ export default class GlideCoreMenu extends LitElement {
18
18
  get open(): boolean;
19
19
  set open(isOpen: boolean);
20
20
  placement: Placement;
21
- containingBlock?: Element;
22
21
  get size(): 'small' | 'large';
23
22
  set size(size: 'small' | 'large');
24
23
  connectedCallback(): void;
25
24
  createRenderRoot(): ShadowRoot;
26
25
  disconnectedCallback(): void;
27
26
  firstUpdated(): void;
28
- focus(): void;
27
+ focus(options?: FocusOptions): void;
28
+ private get isTargetDisabled();
29
29
  render(): import("lit").TemplateResult<1>;
30
- setContainingBlock(containingBlock: Element): void;
31
- private isTargetDisabled;
32
30
  }
package/dist/menu.js CHANGED
@@ -1 +1 @@
1
- var __decorate=this&&this.__decorate||function(e,t,i,o){var n,s=arguments.length,a=s<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,i):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,i,o);else for(var l=e.length-1;l>=0;l--)(n=e[l])&&(a=(s<3?n(a):s>3?n(t,i,a):n(t,i))||a);return s>3&&a&&Object.defineProperty(t,i,a),a};import{LitElement,html}from"lit";import{autoUpdate,computePosition,flip,offset,platform}from"@floating-ui/dom";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property,state}from"lit/decorators.js";import{nanoid}from"nanoid";import{offsetParent}from"composed-offset-position";import GlideCoreMenuButton from"./menu.button.js";import GlideCoreMenuLink from"./menu.link.js";import GlideCoreMenuOptions from"./menu.options.js";import ow,{owSlot,owSlotType}from"./library/ow.js";import styles from"./menu.styles.js";let GlideCoreMenu=class GlideCoreMenu extends LitElement{constructor(){super(...arguments),this.placement="bottom-start",this.isTargetDisabled=!1,this.#e=createRef(),this.#t=createRef(),this.#i=!1,this.#o=!1,this.#n="large",this.#s=createRef(),this.#a=e=>{e.target&&e.target instanceof Node&&this.contains(e.target)||(this.open=!1,this.#l&&(this.#l.ariaActivedescendant=""))}}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}get open(){return this.#o}set open(e){this.#o=e,e&&!this.isTargetDisabled&&this.#l?(this.#r(),this.#l.ariaActivedescendant=this.#c?.id??""):this.#l&&(this.#p?.(),this.#l.ariaActivedescendant=""),this.#d&&(this.#d.ariaExpanded=e&&!this.isTargetDisabled?"true":"false")}get size(){return this.#n}set size(e){this.#n=e,this.#l&&(this.#l.privateSize=e)}connectedCallback(){super.connectedCallback(),document.addEventListener("click",this.#a,{capture:!0})}createRenderRoot(){return this.#h=super.createRenderRoot(),this.#h}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("click",this.#a,{capture:!0})}firstUpdated(){ow(this.#l,ow.object.instanceOf(GlideCoreMenuOptions)),owSlot(this.#t.value),owSlot(this.#s.value),owSlotType(this.#t.value,[GlideCoreMenuOptions]);const e=this.#m?.at(0);this.open&&e&&(this.#r(),e.privateActive=!0,this.#l.ariaActivedescendant=e.id)}focus(){this.#d&&"focus"in this.#d&&this.#d?.focus()}render(){return html`<div class="component" @focusout="${this.#f}" ${ref(this.#e)}><slot class="target-slot" name="target" @click="${this.#u}" @keydown="${this.#E}" @slotchange="${this.#g}" ${ref(this.#s)}></slot><slot class="${classMap({"default-slot":!0,visible:this.open})}" @click="${this.#v}" @focusin="${this.#C}" @keydown="${this.#E}" @mouseover="${this.#w}" @slotchange="${this.#S}" ${ref(this.#t)}></slot></div>`}setContainingBlock(e){this.containingBlock=e}#p;#e;#t;#i;#o;#h;#n;#s;get#c(){return this.#m?.find((({privateActive:e})=>e))}get#l(){const e=this.#t.value?.assignedElements().at(0);return e||null}#a;#S(){ow(this.#l,ow.object.instanceOf(GlideCoreMenuOptions)),owSlot(this.#t.value),owSlotType(this.#t.value,[GlideCoreMenuOptions]);const e=this.#m?.at(0);e&&(e.privateActive=!0),this.#l.privateSize=this.size}#v(){this.open=!1,this.#l&&(this.#l.ariaActivedescendant="")}#C(e){(e.target instanceof GlideCoreMenuButton||e.target instanceof GlideCoreMenuLink)&&this.#c&&this.#l&&(e.target.privateActive=!0,this.#c.privateActive=!1,this.#l.ariaActivedescendant=e.target.id)}#w(e){if(e.target instanceof GlideCoreMenuLink||e.target instanceof GlideCoreMenuButton){if(this.#m)for(const t of this.#m)t.privateActive=t===e.target;this.#l&&(this.#l.ariaActivedescendant=e.target.id)}}#f(e){const t=e.relatedTarget instanceof HTMLElement&&this.#h?.contains(e.relatedTarget),i=e.relatedTarget instanceof GlideCoreMenuOptions,o=e.relatedTarget instanceof GlideCoreMenuButton||e.relatedTarget instanceof GlideCoreMenuLink;t||i||o||(this.open=!1)}#E(e){if(ow(this.#l,ow.object.instanceOf(GlideCoreMenuOptions)),[" ","Enter","Escape"].includes(e.key)&&this.open)return this.open=!1,this.#l.ariaActivedescendant="",this.focus(),void(this.#i=!0);if([" ","ArrowUp","ArrowDown"].includes(e.key)&&!this.open&&this.#c)return e.preventDefault(),this.open=!0,void(this.#l.ariaActivedescendant=this.#c.id);if(this.open){ow(this.#m,ow.array),ow(this.#l,ow.object.instanceOf(GlideCoreMenuOptions)),ow(this.#c,ow.object.is((e=>e instanceof GlideCoreMenuButton||e instanceof GlideCoreMenuLink)));const t=this.#m.indexOf(this.#c);if("ArrowUp"===e.key&&!e.metaKey){e.preventDefault();const i=this.#m.at(t-1);return void(i&&0!==t&&(this.#c.privateActive=!1,this.#l.ariaActivedescendant=i.id,i.privateActive=!0))}if("ArrowDown"===e.key&&!e.metaKey){e.preventDefault();const i=this.#m.at(t+1);return void(i&&(this.#c.privateActive=!1,this.#l.ariaActivedescendant=i.id,i.privateActive=!0))}if("ArrowUp"===e.key&&e.metaKey||"Home"===e.key||"PageUp"===e.key){e.preventDefault();const t=this.#m.at(0);return void(t&&(this.#c.privateActive=!1,this.#l.ariaActivedescendant=t.id,t.privateActive=!0))}if("ArrowDown"===e.key&&e.metaKey||"End"===e.key||"PageDown"===e.key){e.preventDefault();const t=this.#m.at(-1);return void(t&&(this.#c.privateActive=!1,this.#l.ariaActivedescendant=t.id,t.privateActive=!0))}}}#g(){owSlot(this.#s.value),ow(this.#d,ow.object.instanceOf(Element)),ow(this.#l,ow.object.instanceOf(GlideCoreMenuOptions)),this.#y(),this.isTargetDisabled&&(this.open=!1);new MutationObserver((e=>{e.some((e=>"disabled"===e.attributeName||"aria-disabled"===e.attributeName))&&(this.#y(),this.isTargetDisabled&&(this.open=!1))})).observe(this.#d,{attributes:!0}),this.#d.ariaHasPopup="true",this.#d.ariaExpanded=this.open?"true":"false",this.#d.id=nanoid(),this.#d.setAttribute("aria-controls",this.#l.id),this.#l.ariaLabelledby=this.#d.id}#u(){this.isTargetDisabled||this.#i?this.#i=!1:(this.#d instanceof HTMLElement&&(this.#d.ariaExpanded=this.open?"true":"false"),this.#m&&this.#m.length>0&&(this.open=!this.open),this.open&&this.#c&&this.#l?this.#l.ariaActivedescendant=this.#c.id:!this.open&&this.#l&&(this.#l.ariaActivedescendant="",this.focus()))}get#m(){let e=this.#t.value?.assignedElements()?.at(0)?.children;const t=e?.[0];if(t instanceof HTMLSlotElement&&(e=t.assignedElements()),e)return[...e].filter((e=>e instanceof GlideCoreMenuLink||e instanceof GlideCoreMenuButton))}#y(){const e=this.#d&&"disabled"in this.#d&&this.#d.disabled,t=this.#d&&"true"===this.#d.ariaDisabled;this.isTargetDisabled=Boolean(e)||Boolean(t)}#r(){this.#d&&this.#t.value&&(this.#p=autoUpdate(this.#d,this.#t.value,(()=>{(async()=>{if(this.#d&&this.#t.value){const{x:e,y:t,placement:i}=await computePosition(this.#d,this.#t.value,{platform:{...platform,getOffsetParent:e=>this.containingBlock??platform.getOffsetParent(e,offsetParent)},placement:this.placement,middleware:[offset({mainAxis:16*Number.parseFloat(window.getComputedStyle(document.body).getPropertyValue("--glide-core-spacing-xxs"))}),flip()]});this.#t.value.dataset.placement=i,Object.assign(this.#t.value.style,{left:`${e}px`,top:`${t}px`})}})()})))}get#d(){return this.#s.value?.assignedElements().at(0)}};__decorate([property({reflect:!0,type:Boolean})],GlideCoreMenu.prototype,"open",null),__decorate([property({reflect:!0})],GlideCoreMenu.prototype,"placement",void 0),__decorate([state()],GlideCoreMenu.prototype,"containingBlock",void 0),__decorate([property({reflect:!0})],GlideCoreMenu.prototype,"size",null),__decorate([state()],GlideCoreMenu.prototype,"isTargetDisabled",void 0),GlideCoreMenu=__decorate([customElement("glide-core-menu")],GlideCoreMenu);export default GlideCoreMenu;
1
+ var __decorate=this&&this.__decorate||function(e,t,i,o){var n,s=arguments.length,l=s<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,i):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)l=Reflect.decorate(e,t,i,o);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(l=(s<3?n(l):s>3?n(t,i,l):n(t,i))||l);return s>3&&l&&Object.defineProperty(t,i,l),l};import{LitElement,html}from"lit";import{autoUpdate,computePosition,flip,offset}from"@floating-ui/dom";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property}from"lit/decorators.js";import{nanoid}from"nanoid";import GlideCoreMenuButton from"./menu.button.js";import GlideCoreMenuLink from"./menu.link.js";import GlideCoreMenuOptions from"./menu.options.js";import ow,{owSlot,owSlotType}from"./library/ow.js";import styles from"./menu.styles.js";let GlideCoreMenu=class GlideCoreMenu extends LitElement{constructor(){super(...arguments),this.placement="bottom-start",this.#e=createRef(),this.#t=createRef(),this.#i=!1,this.#o=!1,this.#n="large",this.#s=createRef(),this.#l=e=>{e.target&&e.target instanceof Node&&this.contains(e.target)||(this.open=!1,this.#a&&(this.#a.ariaActivedescendant=""))}}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}get open(){return this.#o}set open(e){this.#o=e,e&&!this.isTargetDisabled?this.#r():this.#c()}get size(){return this.#n}set size(e){this.#n=e,this.#a&&(this.#a.privateSize=e)}connectedCallback(){super.connectedCallback(),document.addEventListener("click",this.#l,{capture:!0})}createRenderRoot(){return this.#d=super.createRenderRoot(),this.#d}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("click",this.#l,{capture:!0})}firstUpdated(){ow(this.#a,ow.object.instanceOf(GlideCoreMenuOptions)),owSlot(this.#t.value),owSlot(this.#s.value),owSlotType(this.#t.value,[GlideCoreMenuOptions]),this.#t.value.popover="manual";const e=this.#h?.at(0);this.open&&e&&!this.isTargetDisabled&&(e.privateActive=!0,this.#r())}focus(e){this.#p&&"focus"in this.#p&&this.#p?.focus(e)}get isTargetDisabled(){const e=this.#p&&"disabled"in this.#p&&this.#p.disabled,t=this.#p&&"true"===this.#p.ariaDisabled;return Boolean(e)||Boolean(t)}render(){return html`<div class="component" @focusout="${this.#m}" ${ref(this.#e)}><slot class="target-slot" name="target" @click="${this.#u}" @keydown="${this.#f}" @slotchange="${this.#E}" ${ref(this.#s)}></slot><slot class="default-slot" @click="${this.#v}" @focusin="${this.#g}" @keydown="${this.#f}" @mouseover="${this.#w}" @slotchange="${this.#S}" ${ref(this.#t)}></slot></div>`}#C;#e;#t;#i;#o;#d;#n;#s;get#y(){return this.#h?.find((({privateActive:e})=>e))}#l;#c(){this.#C?.(),this.#a&&(this.#a.ariaActivedescendant=""),this.#p&&(this.#p.ariaExpanded="false"),this.#t.value?.hidePopover()}get#a(){const e=this.#t.value?.assignedElements().at(0);return e instanceof GlideCoreMenuOptions?e:null}#S(){ow(this.#a,ow.object.instanceOf(GlideCoreMenuOptions)),owSlot(this.#t.value),owSlotType(this.#t.value,[GlideCoreMenuOptions]);const e=this.#h?.at(0);e&&(e.privateActive=!0),this.#a.privateSize=this.size}#v(){this.open=!1}#g(e){(e.target instanceof GlideCoreMenuButton||e.target instanceof GlideCoreMenuLink)&&this.#y&&this.#a&&(e.target.privateActive=!0,this.#y.privateActive=!1,this.#a.ariaActivedescendant=e.target.id)}#w(e){if(e.target instanceof GlideCoreMenuLink||e.target instanceof GlideCoreMenuButton){if(this.#h)for(const t of this.#h)t.privateActive=t===e.target;this.#a&&(this.#a.ariaActivedescendant=e.target.id)}}#m(e){const t=e.relatedTarget instanceof HTMLElement&&this.#d?.contains(e.relatedTarget),i=e.relatedTarget instanceof GlideCoreMenuOptions,o=e.relatedTarget instanceof GlideCoreMenuButton||e.relatedTarget instanceof GlideCoreMenuLink;t||i||o||(this.open=!1)}#f(e){if(ow(this.#a,ow.object.instanceOf(GlideCoreMenuOptions)),[" ","Enter","Escape"].includes(e.key)&&this.open)return this.open=!1,this.focus(),void(this.#i=!0);if([" ","ArrowUp","ArrowDown"].includes(e.key)&&!this.open&&this.#y)return e.preventDefault(),this.open=!0,void(this.#a.ariaActivedescendant=this.#y.id);if(this.open){ow(this.#h,ow.array),ow(this.#a,ow.object.instanceOf(GlideCoreMenuOptions)),ow(this.#y,ow.object.is((e=>e instanceof GlideCoreMenuButton||e instanceof GlideCoreMenuLink)));const t=this.#h.indexOf(this.#y);if("ArrowUp"===e.key&&!e.metaKey){e.preventDefault();const i=this.#h.at(t-1);return void(i&&0!==t&&(this.#y.privateActive=!1,this.#a.ariaActivedescendant=i.id,i.privateActive=!0))}if("ArrowDown"===e.key&&!e.metaKey){e.preventDefault();const i=this.#h.at(t+1);return void(i&&(this.#y.privateActive=!1,this.#a.ariaActivedescendant=i.id,i.privateActive=!0))}if("ArrowUp"===e.key&&e.metaKey||"Home"===e.key||"PageUp"===e.key){e.preventDefault();const t=this.#h.at(0);return void(t&&(this.#y.privateActive=!1,this.#a.ariaActivedescendant=t.id,t.privateActive=!0))}if("ArrowDown"===e.key&&e.metaKey||"End"===e.key||"PageDown"===e.key){e.preventDefault();const t=this.#h.at(-1);return void(t&&(this.#y.privateActive=!1,this.#a.ariaActivedescendant=t.id,t.privateActive=!0))}}}#E(){owSlot(this.#s.value),ow(this.#p,ow.object.instanceOf(Element)),ow(this.#a,ow.object.instanceOf(GlideCoreMenuOptions));new MutationObserver((e=>{e.some((e=>"disabled"===e.attributeName||"aria-disabled"===e.attributeName))&&(this.open&&!this.isTargetDisabled?this.#r():this.#c())})).observe(this.#p,{attributes:!0}),this.#p.ariaHasPopup="true",this.#p.id=nanoid(),this.#p.setAttribute("aria-controls",this.#a.id),this.#a.ariaLabelledby=this.#p.id,this.open&&!this.isTargetDisabled?this.#r():this.#c()}#u(){this.isTargetDisabled?this.#c():this.#i?this.#i=!1:(this.#h&&this.#h.length>0&&(this.open=!this.open),!this.open&&this.#a&&this.focus())}get#h(){let e=this.#t.value?.assignedElements()?.at(0)?.children;const t=e?.[0];if(t instanceof HTMLSlotElement&&(e=t.assignedElements()),e)return[...e].filter((e=>e instanceof GlideCoreMenuLink||e instanceof GlideCoreMenuButton))}#r(){this.#C?.(),this.#p&&this.#t.value&&(this.#C=autoUpdate(this.#p,this.#t.value,(()=>{(async()=>{if(this.#p&&this.#t.value){const{x:e,y:t,placement:i}=await computePosition(this.#p,this.#t.value,{placement:this.placement,middleware:[offset({mainAxis:Number.parseFloat(window.getComputedStyle(document.body).getPropertyValue("--glide-core-spacing-xxs"))*Number.parseFloat(window.getComputedStyle(document.documentElement).fontSize)}),flip()]});this.#t.value.dataset.placement=i,Object.assign(this.#t.value.style,{left:`${e}px`,top:`${t}px`})}this.#t.value?.showPopover(),this.#a&&this.#y?.id&&(this.#a.ariaActivedescendant=this.#y.id),this.#p&&(this.#p.ariaExpanded="true")})()})))}get#p(){return this.#s.value?.assignedElements().at(0)}};__decorate([property({reflect:!0,type:Boolean})],GlideCoreMenu.prototype,"open",null),__decorate([property({reflect:!0})],GlideCoreMenu.prototype,"placement",void 0),__decorate([property({reflect:!0})],GlideCoreMenu.prototype,"size",null),GlideCoreMenu=__decorate([customElement("glide-core-menu")],GlideCoreMenu);export default GlideCoreMenu;
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
1
2
  import { expect, fixture, html } from '@open-wc/testing';
2
3
  import GlideCoreMenuLink from './menu.link.js';
3
4
  GlideCoreMenuLink.shadowRootOptions.mode = 'open';
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
1
2
  import './menu.button.js';
2
3
  import './menu.link.js';
3
4
  import { ArgumentError } from 'ow';
@@ -18,7 +19,7 @@ it('throws if it does not have a default slot', async () => {
18
19
  spy();
19
20
  }
20
21
  }
21
- expect(spy.called).to.be.true;
22
+ expect(spy.callCount).to.equal(1);
22
23
  });
23
24
  it('throws if the default slot is the incorrect type', async () => {
24
25
  await expectArgumentError(() => {
@@ -39,5 +40,5 @@ it('does not throw if the default slot only contains whitespace', async () => {
39
40
  spy();
40
41
  }
41
42
  }
42
- expect(spy.notCalled).to.be.true;
43
+ expect(spy.callCount).to.equal(0);
43
44
  });