@crowdstrike/glide-core 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/checkbox-group.d.ts +3 -0
  2. package/dist/checkbox-group.js +43 -1
  3. package/dist/checkbox-group.styles.js +12 -0
  4. package/dist/checkbox-group.test.validity.js +69 -0
  5. package/dist/checkbox.d.ts +3 -1
  6. package/dist/checkbox.js +135 -1
  7. package/dist/checkbox.styles.js +12 -0
  8. package/dist/checkbox.test.events.js +9 -0
  9. package/dist/checkbox.test.validity.js +77 -1
  10. package/dist/dropdown.d.ts +3 -0
  11. package/dist/dropdown.js +195 -1
  12. package/dist/dropdown.option.d.ts +1 -0
  13. package/dist/dropdown.option.js +1 -1
  14. package/dist/dropdown.option.test.events.js +9 -1
  15. package/dist/dropdown.option.test.interactions.single.js +2 -2
  16. package/dist/dropdown.styles.js +18 -6
  17. package/dist/dropdown.test.basics.d.ts +1 -1
  18. package/dist/dropdown.test.basics.js +19 -1
  19. package/dist/dropdown.test.basics.multiple.js +2 -1
  20. package/dist/dropdown.test.events.filterable.js +13 -2
  21. package/dist/dropdown.test.events.single.js +19 -0
  22. package/dist/dropdown.test.form.multiple.js +3 -2
  23. package/dist/dropdown.test.interactions.filterable.js +20 -0
  24. package/dist/dropdown.test.interactions.js +20 -14
  25. package/dist/dropdown.test.interactions.multiple.js +24 -10
  26. package/dist/dropdown.test.interactions.single.js +32 -0
  27. package/dist/dropdown.test.validity.js +172 -1
  28. package/dist/input.d.ts +4 -0
  29. package/dist/input.js +155 -1
  30. package/dist/input.styles.js +8 -0
  31. package/dist/input.test.validity.js +140 -62
  32. package/dist/menu.d.ts +2 -0
  33. package/dist/menu.js +1 -1
  34. package/dist/menu.test.basics.d.ts +1 -1
  35. package/dist/menu.test.basics.js +12 -27
  36. package/dist/menu.test.interactions.js +90 -0
  37. package/dist/radio-group.d.ts +3 -0
  38. package/dist/radio-group.js +45 -1
  39. package/dist/radio-group.styles.js +12 -0
  40. package/dist/radio-group.test.validity.js +82 -0
  41. package/dist/styles/variables.css +1 -1
  42. package/dist/tab.group.d.ts +1 -0
  43. package/dist/tab.group.js +1 -1
  44. package/dist/tab.group.styles.js +6 -0
  45. package/dist/tag.d.ts +1 -2
  46. package/dist/tag.js +1 -1
  47. package/dist/tag.styles.js +12 -3
  48. package/dist/tag.test.basics.js +1 -0
  49. package/dist/tag.test.interactions.js +8 -6
  50. package/dist/textarea.d.ts +3 -0
  51. package/dist/textarea.js +58 -2
  52. package/dist/textarea.styles.js +8 -0
  53. package/dist/textarea.test.events.js +0 -114
  54. package/dist/textarea.test.validity.js +64 -72
  55. package/dist/tooltip.d.ts +2 -1
  56. package/dist/tooltip.js +1 -1
  57. package/dist/tooltip.styles.js +2 -1
  58. package/dist/tree.item.menu.d.ts +1 -0
  59. package/dist/tree.item.menu.js +1 -1
  60. package/dist/tree.item.menu.test.basics.js +22 -1
  61. package/dist/tree.item.styles.js +3 -6
  62. package/package.json +1 -1
@@ -50,67 +50,6 @@ it('is invalid after value is cleared when required', async () => {
50
50
  await elementUpdated(component);
51
51
  expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('true');
52
52
  });
53
- it('is valid when empty and does not exceed `maxlength`', async () => {
54
- const component = await fixture(html `<glide-core-input maxlength="3"></glide-core-input>`);
55
- expect(component.validity?.valid).to.be.true;
56
- expect(component.validity?.valueMissing).to.be.false;
57
- expect(component.validity?.tooLong).to.be.false;
58
- expect(component.checkValidity()).to.be.true;
59
- expect(component.reportValidity()).to.be.true;
60
- await elementUpdated(component);
61
- expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('false');
62
- });
63
- it('is valid when filled in and does not exceed `maxlength`', async () => {
64
- const component = await fixture(html `<glide-core-input maxlength="3"></glide-core-input>`);
65
- component.focus();
66
- await sendKeys({ type: 'val' });
67
- expect(component.validity?.valid).to.be.true;
68
- expect(component.validity?.valueMissing).to.be.false;
69
- expect(component.validity?.tooLong).to.be.false;
70
- expect(component.checkValidity()).to.be.true;
71
- expect(component.reportValidity()).to.be.true;
72
- await elementUpdated(component);
73
- expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('false');
74
- });
75
- it('is valid when filled in, disabled, and value exceeds `maxlength`', async () => {
76
- const component = await fixture(html `<glide-core-input
77
- value="value"
78
- maxlength="3"
79
- disabled
80
- ></glide-core-input>`);
81
- expect(component.validity?.valid).to.be.true;
82
- expect(component.validity?.valueMissing).to.be.false;
83
- expect(component.validity?.tooLong).to.be.false;
84
- expect(component.checkValidity()).to.be.true;
85
- expect(component.reportValidity()).to.be.true;
86
- await elementUpdated(component);
87
- expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('false');
88
- });
89
- it('is valid when filled in, readonly, and value exceeds `maxlength`', async () => {
90
- const component = await fixture(html `<glide-core-input
91
- value="value"
92
- maxlength="3"
93
- readonly
94
- ></glide-core-input>`);
95
- expect(component.validity?.valid).to.be.true;
96
- expect(component.validity?.valueMissing).to.be.false;
97
- expect(component.validity?.tooLong).to.be.false;
98
- expect(component.checkValidity()).to.be.true;
99
- expect(component.reportValidity()).to.be.true;
100
- await elementUpdated(component);
101
- expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('false');
102
- });
103
- it('is invalid when filled in and exceeds `maxlength`', async () => {
104
- const component = await fixture(html `<glide-core-input maxlength="3"></glide-core-input>`);
105
- component.focus();
106
- await sendKeys({ type: 'value' });
107
- expect(component.validity?.valid).to.be.false;
108
- expect(component.validity?.tooLong).to.be.true;
109
- expect(component.checkValidity()).to.be.false;
110
- expect(component.reportValidity()).to.be.false;
111
- await elementUpdated(component);
112
- expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('true');
113
- });
114
53
  it('is valid if no value and required but disabled', async () => {
115
54
  const component = await fixture(html `<glide-core-input disabled required></glide-core-input>`);
116
55
  expect(component.validity?.valid).to.be.true;
@@ -162,7 +101,7 @@ it('is invalid when `value` is empty and `required` is set to `true` programmati
162
101
  expect(component.reportValidity()).to.be.false;
163
102
  expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('true');
164
103
  });
165
- it('is valid when `value` is empty and `required` is set to `false` programmatically', async () => {
104
+ it('is valid when `value` is empty and `required` is updated to `false` programmatically', async () => {
166
105
  const component = await fixture(html `<glide-core-input label="Label" required></glide-core-input>`);
167
106
  expect(component.validity?.valid).to.be.false;
168
107
  expect(component.validity?.valueMissing).to.be.true;
@@ -178,3 +117,142 @@ it('is valid when `value` is empty and `required` is set to `false` programmatic
178
117
  expect(component.reportValidity()).to.be.true;
179
118
  expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('false');
180
119
  });
120
+ it('is valid when the `value` attribute matches `pattern`', async () => {
121
+ const component = await fixture(html `<glide-core-input
122
+ label="Label"
123
+ pattern="[a-z]{4,8}"
124
+ value="value"
125
+ ></glide-core-input>`);
126
+ expect(component.validity?.valid).to.be.true;
127
+ expect(component.validity?.patternMismatch).to.be.false;
128
+ expect(component.checkValidity()).to.be.true;
129
+ expect(component.reportValidity()).to.be.true;
130
+ expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('false');
131
+ });
132
+ it('is valid when `value` matches `pattern` after being set programmatically', async () => {
133
+ const component = await fixture(html `<glide-core-input
134
+ label="Label"
135
+ pattern="[a-z]{4,8}"
136
+ ></glide-core-input>`);
137
+ component.value = 'value';
138
+ await elementUpdated(component);
139
+ expect(component.validity?.valid).to.be.true;
140
+ expect(component.validity?.patternMismatch).to.be.false;
141
+ expect(component.checkValidity()).to.be.true;
142
+ expect(component.reportValidity()).to.be.true;
143
+ expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('false');
144
+ });
145
+ it('is invalid when `value` does not match `pattern`', async () => {
146
+ const component = await fixture(html `<glide-core-input
147
+ label="Label"
148
+ pattern="[a-z]{4,8}"
149
+ value="1234"
150
+ ></glide-core-input>`);
151
+ expect(component.validity?.valid).to.be.false;
152
+ expect(component.validity?.patternMismatch).to.be.true;
153
+ expect(component.checkValidity()).to.be.false;
154
+ expect(component.reportValidity()).to.be.false;
155
+ await elementUpdated(component);
156
+ expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('true');
157
+ });
158
+ it('is invalid when a programmatically set `value` does not match `pattern`', async () => {
159
+ const component = await fixture(html `<glide-core-input
160
+ label="Label"
161
+ pattern="[a-z]{4,8}"
162
+ ></glide-core-input>`);
163
+ component.value = 'val';
164
+ expect(component.validity?.valid).to.be.false;
165
+ expect(component.validity?.patternMismatch).to.be.true;
166
+ expect(component.checkValidity()).to.be.false;
167
+ expect(component.reportValidity()).to.be.false;
168
+ await elementUpdated(component);
169
+ expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('true');
170
+ });
171
+ it('is invalid when `required` and `value` does not match `pattern`', async () => {
172
+ const component = await fixture(html `<glide-core-input
173
+ label="Label"
174
+ pattern="[a-z]{4,8}"
175
+ required
176
+ ></glide-core-input>`);
177
+ expect(component.validity?.valid).to.be.false;
178
+ expect(component.validity?.patternMismatch).to.be.true;
179
+ expect(component.validity?.valueMissing).to.be.true;
180
+ });
181
+ it('is valid when `pattern` is programmatically removed', async () => {
182
+ const component = await fixture(html `<glide-core-input
183
+ label="Label"
184
+ pattern="[a-z]{4,8}"
185
+ ></glide-core-input>`);
186
+ component.pattern = undefined;
187
+ await elementUpdated(component);
188
+ expect(component.validity?.valid).to.be.true;
189
+ expect(component.validity?.patternMismatch).to.be.false;
190
+ expect(component.checkValidity()).to.be.true;
191
+ expect(component.reportValidity()).to.be.true;
192
+ expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('false');
193
+ });
194
+ it('sets the validity message with `setCustomValidity()`', async () => {
195
+ const component = await fixture(html `<glide-core-input label="Label"></glide-core-input>`);
196
+ component.setCustomValidity('validity message');
197
+ expect(component.validity?.valid).to.be.false;
198
+ expect(component.validity?.customError).to.be.true;
199
+ expect(component.checkValidity()).to.be.false;
200
+ await elementUpdated(component);
201
+ // Like native, the validity message shouldn't display until `reportValidity()` is called.
202
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
203
+ ?.textContent).to.be.undefined;
204
+ expect(component.reportValidity()).to.be.false;
205
+ await elementUpdated(component);
206
+ expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('true');
207
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
208
+ ?.textContent).to.equal('validity message');
209
+ });
210
+ it('removes a validity message with an empty argument to `setCustomValidity()`', async () => {
211
+ const component = await fixture(html `<glide-core-input label="Label"></glide-core-input>`);
212
+ component.setCustomValidity('validity message');
213
+ component.reportValidity();
214
+ await elementUpdated(component);
215
+ component.setCustomValidity('');
216
+ await elementUpdated(component);
217
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
218
+ ?.textContent).to.be.undefined;
219
+ });
220
+ it('is invalid when `setValidity()` is called', async () => {
221
+ const component = await fixture(html `<glide-core-input label="Label"></glide-core-input>`);
222
+ component.setValidity({ customError: true }, 'validity message');
223
+ expect(component.validity.valid).to.be.false;
224
+ await elementUpdated(component);
225
+ // Like native, the validity message shouldn't display until `reportValidity()` is called.
226
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
227
+ ?.textContent).to.be.undefined;
228
+ expect(component.validity?.customError).to.be.true;
229
+ component.reportValidity();
230
+ await elementUpdated(component);
231
+ expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('true');
232
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
233
+ ?.textContent).to.equal('validity message');
234
+ });
235
+ it('is valid when `setValidity()` is called', async () => {
236
+ const component = await fixture(html `<glide-core-input label="Label"></glide-core-input>`);
237
+ component.setValidity({ customError: true }, 'validity message');
238
+ component.setValidity({});
239
+ await elementUpdated(component);
240
+ expect(component.validity.valid).to.be.true;
241
+ expect(component.validity.customError).to.be.false;
242
+ expect(component.reportValidity()).to.be.true;
243
+ await elementUpdated(component);
244
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
245
+ ?.textContent).to.be.undefined;
246
+ });
247
+ it('retains existing validity state when `setCustomValidity()` is called', async () => {
248
+ const component = await fixture(html `<glide-core-input
249
+ label="Label"
250
+ pattern="[a-z]{4,8}"
251
+ required
252
+ ></glide-core-input>`);
253
+ component.setCustomValidity('validity message');
254
+ expect(component.validity?.valid).to.be.false;
255
+ expect(component.validity?.customError).to.be.true;
256
+ expect(component.validity?.patternMismatch).to.be.true;
257
+ expect(component.validity?.valueMissing).to.be.true;
258
+ });
package/dist/menu.d.ts CHANGED
@@ -13,6 +13,8 @@ export default class GlideCoreMenu extends LitElement {
13
13
  #private;
14
14
  static shadowRootOptions: ShadowRootInit;
15
15
  static styles: import("lit").CSSResult[];
16
+ get offset(): number;
17
+ set offset(offset: number);
16
18
  get open(): boolean;
17
19
  set open(isOpen: boolean);
18
20
  placement: Placement;
package/dist/menu.js CHANGED
@@ -1 +1 @@
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=!1,this.#s="large",this.#l=createRef(),this.#a=()=>{this.#n?this.#n=!1:(this.open=!1,this.#r&&(this.#r.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.#c():this.#h()}get size(){return this.#s}set size(e){this.#s=e,this.#r&&(this.#r.privateSize=e)}connectedCallback(){super.connectedCallback(),document.addEventListener("click",this.#a,{capture:!0})}createRenderRoot(){return this.#d=super.createRenderRoot(),this.#d}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("click",this.#a,{capture:!0})}firstUpdated(){ow(this.#r,ow.object.instanceOf(GlideCoreMenuOptions)),owSlot(this.#t.value),owSlot(this.#l.value),owSlotType(this.#t.value,[GlideCoreMenuOptions]),this.#t.value.popover="manual";const e=this.#p?.at(0);this.open&&e&&!this.isTargetDisabled&&(e.privateActive=!0,this.#c()),this.#l.value.addEventListener("mouseup",(()=>{this.#n=!0}))}get isTargetDisabled(){const e=this.#u&&"disabled"in this.#u&&this.#u.disabled,t=this.#u&&"true"===this.#u.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.#f}" @keydown="${this.#E}" @slotchange="${this.#v}" ${ref(this.#l)}></slot><slot class="default-slot" @click="${this.#g}" @focusin="${this.#S}" @keydown="${this.#E}" @mouseover="${this.#w}" @slotchange="${this.#C}" ${ref(this.#t)}></slot></div>`}#y;#e;#t;#i;#o;#n;#d;#s;#l;get#R(){return this.#p?.find((({privateActive:e})=>e))}#a;#k(e){this.#u&&"focus"in this.#u&&this.#u?.focus(e)}#h(){this.#y?.(),this.#r&&(this.#r.ariaActivedescendant=""),this.#u&&(this.#u.ariaExpanded="false"),this.#t.value?.hidePopover()}get#r(){const e=this.#t.value?.assignedElements().at(0);return e instanceof GlideCoreMenuOptions?e:null}#C(){ow(this.#r,ow.object.instanceOf(GlideCoreMenuOptions)),owSlot(this.#t.value),owSlotType(this.#t.value,[GlideCoreMenuOptions]);const e=this.#p?.at(0);e&&(e.privateActive=!0),this.#r.privateSize=this.size}#g(){this.open=!1}#S(e){(e.target instanceof GlideCoreMenuButton||e.target instanceof GlideCoreMenuLink)&&this.#R&&this.#r&&(this.#R.privateActive=!1,e.target.privateActive=!0,this.#r.ariaActivedescendant=e.target.id)}#w(e){if(e.target instanceof GlideCoreMenuLink||e.target instanceof GlideCoreMenuButton){if(this.#p)for(const t of this.#p)t.privateActive=t===e.target;this.#r&&(this.#r.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)}#E(e){if(ow(this.#r,ow.object.instanceOf(GlideCoreMenuOptions)),[" ","Enter"].includes(e.key)&&this.open)return this.open=!1,this.#k(),this.#R?.click(),void(this.#i=!0);if(["Escape"].includes(e.key)&&this.open)return this.open=!1,void this.#k();if(["ArrowUp","ArrowDown"].includes(e.key)&&!this.open&&this.#R)return e.preventDefault(),this.open=!0,void(this.#r.ariaActivedescendant=this.#R.id);if(this.open){ow(this.#p,ow.array),ow(this.#r,ow.object.instanceOf(GlideCoreMenuOptions)),ow(this.#R,ow.object.is((e=>e instanceof GlideCoreMenuButton||e instanceof GlideCoreMenuLink)));const t=this.#p.indexOf(this.#R);if("ArrowUp"===e.key&&!e.metaKey){e.preventDefault();const i=this.#p.at(t-1);return void(i&&0!==t&&(this.#R.privateActive=!1,this.#r.ariaActivedescendant=i.id,i.privateActive=!0))}if("ArrowDown"===e.key&&!e.metaKey){e.preventDefault();const i=this.#p.at(t+1);return void(i&&(this.#R.privateActive=!1,this.#r.ariaActivedescendant=i.id,i.privateActive=!0))}if("ArrowUp"===e.key&&e.metaKey||"Home"===e.key||"PageUp"===e.key){e.preventDefault();const t=this.#p.at(0);return void(t&&(this.#R.privateActive=!1,this.#r.ariaActivedescendant=t.id,t.privateActive=!0))}if("ArrowDown"===e.key&&e.metaKey||"End"===e.key||"PageDown"===e.key){e.preventDefault();const t=this.#p.at(-1);return void(t&&(this.#R.privateActive=!1,this.#r.ariaActivedescendant=t.id,t.privateActive=!0))}}}#v(){owSlot(this.#l.value),ow(this.#u,ow.object.instanceOf(Element)),ow(this.#r,ow.object.instanceOf(GlideCoreMenuOptions));new MutationObserver((()=>{this.open&&!this.isTargetDisabled?this.#c():this.#h()})).observe(this.#u,{attributes:!0,attributeFilter:["aria-disabled","disabled"]}),this.#u.ariaHasPopup="true",this.#u.id=nanoid(),this.#u.setAttribute("aria-controls",this.#r.id),this.#r.ariaLabelledby=this.#u.id,this.open&&!this.isTargetDisabled?this.#c():this.#h()}#f(){this.isTargetDisabled?this.#h():this.#i?this.#i=!1:this.#p&&this.#p.length>0&&(this.open=!this.open)}get#p(){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))}#c(){this.#y?.(),this.#u&&this.#t.value&&(this.#y=autoUpdate(this.#u,this.#t.value,(()=>{(async()=>{if(this.#u&&this.#t.value){const{x:e,y:t,placement:i}=await computePosition(this.#u,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.#r&&this.#R?.id&&(this.#r.ariaActivedescendant=this.#R.id),this.#u&&(this.#u.ariaExpanded="true")})()})))}get#u(){return this.#l.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
+ 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=!1,this.#s="large",this.#l=createRef(),this.#a=()=>{this.#n?this.#n=!1:(this.open=!1,this.#r&&(this.#r.ariaActivedescendant=""))}}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}get offset(){return this.#c??Number.parseFloat(window.getComputedStyle(document.body).getPropertyValue("--glide-core-spacing-xxs"))*Number.parseFloat(window.getComputedStyle(document.documentElement).fontSize)}set offset(e){this.#c=e}get open(){return this.#o}set open(e){this.#o=e,e&&!this.isTargetDisabled?this.#h():this.#p()}get size(){return this.#s}set size(e){this.#s=e,this.#r&&(this.#r.privateSize=e)}connectedCallback(){super.connectedCallback(),document.addEventListener("click",this.#a,{capture:!0})}createRenderRoot(){return this.#d=super.createRenderRoot(),this.#d}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("click",this.#a,{capture:!0})}firstUpdated(){ow(this.#r,ow.object.instanceOf(GlideCoreMenuOptions)),owSlot(this.#t.value),owSlot(this.#l.value),owSlotType(this.#t.value,[GlideCoreMenuOptions]),this.#t.value.popover="manual";const e=this.#m?.at(0);this.open&&e&&!this.isTargetDisabled&&(e.privateActive=!0,this.#h()),this.#l.value.addEventListener("mouseup",(()=>{this.#n=!0}))}get isTargetDisabled(){const e=this.#u&&"disabled"in this.#u&&this.#u.disabled,t=this.#u&&"true"===this.#u.ariaDisabled;return Boolean(e)||Boolean(t)}render(){return html`<div class="component" @focusout="${this.#f}" ${ref(this.#e)}><slot class="target-slot" name="target" @click="${this.#E}" @keydown="${this.#v}" @slotchange="${this.#g}" ${ref(this.#l)}></slot><slot class="default-slot" @click="${this.#S}" @focusin="${this.#w}" @keydown="${this.#v}" @mouseover="${this.#C}" @slotchange="${this.#y}" ${ref(this.#t)}></slot></div>`}#R;#e;#t;#i;#o;#n;#c;#d;#s;#l;get#k(){return this.#m?.find((({privateActive:e})=>e))}#a;#O(e){this.#u&&"focus"in this.#u&&this.#u?.focus(e)}#p(){this.#R?.(),this.#r&&(this.#r.ariaActivedescendant=""),this.#u&&(this.#u.ariaExpanded="false"),this.#t.value?.hidePopover()}get#r(){const e=this.#t.value?.assignedElements().at(0);return e instanceof GlideCoreMenuOptions?e:null}#y(){ow(this.#r,ow.object.instanceOf(GlideCoreMenuOptions)),owSlot(this.#t.value),owSlotType(this.#t.value,[GlideCoreMenuOptions]);const e=this.#m?.at(0);e&&(e.privateActive=!0),this.#r.privateSize=this.size}#S(){this.open=!1}#w(e){(e.target instanceof GlideCoreMenuButton||e.target instanceof GlideCoreMenuLink)&&this.#k&&this.#r&&(this.#k.privateActive=!1,e.target.privateActive=!0,this.#r.ariaActivedescendant=e.target.id)}#C(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.#r&&(this.#r.ariaActivedescendant=e.target.id)}}#f(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)}#v(e){ow(this.#r,ow.object.instanceOf(GlideCoreMenuOptions));const t=this.#u instanceof HTMLSpanElement||this.#u instanceof HTMLDivElement;if([" ","Enter"].includes(e.key)&&this.open)return" "===e.key&&t&&e.preventDefault(),this.open=!1,this.#O(),this.#k?.click(),void(this.#i=!0);if([" ","Enter"].includes(e.key)&&t)return e.preventDefault(),void(this.open=!0);if(["Escape"].includes(e.key)&&this.open)return this.open=!1,void this.#O();if(["ArrowUp","ArrowDown"].includes(e.key)&&!this.open&&this.#k)return e.preventDefault(),this.open=!0,void(this.#r.ariaActivedescendant=this.#k.id);if(this.open){ow(this.#m,ow.array),ow(this.#r,ow.object.instanceOf(GlideCoreMenuOptions)),ow(this.#k,ow.object.is((e=>e instanceof GlideCoreMenuButton||e instanceof GlideCoreMenuLink)));const t=this.#m.indexOf(this.#k);if("ArrowUp"===e.key&&!e.metaKey){e.preventDefault();const i=this.#m.at(t-1);return void(i&&0!==t&&(this.#k.privateActive=!1,this.#r.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.#k.privateActive=!1,this.#r.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.#k.privateActive=!1,this.#r.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.#k.privateActive=!1,this.#r.ariaActivedescendant=t.id,t.privateActive=!0))}}}#g(){owSlot(this.#l.value),ow(this.#u,ow.object.instanceOf(Element)),ow(this.#r,ow.object.instanceOf(GlideCoreMenuOptions));new MutationObserver((()=>{this.open&&!this.isTargetDisabled?this.#h():this.#p()})).observe(this.#u,{attributes:!0,attributeFilter:["aria-disabled","disabled"]}),this.#u.ariaHasPopup="true",this.#u.id=nanoid(),this.#u.setAttribute("aria-controls",this.#r.id),this.#r.ariaLabelledby=this.#u.id;(this.#u instanceof HTMLSpanElement||this.#u instanceof HTMLDivElement)&&this.#u instanceof HTMLElement&&(this.#u.tabIndex=0),this.open&&!this.isTargetDisabled?this.#h():this.#p()}#E(){this.isTargetDisabled?this.#p():this.#i?this.#i=!1:this.#m&&this.#m.length>0&&(this.open=!this.open)}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))}#h(){this.#R?.(),this.#u&&this.#t.value&&(this.#R=autoUpdate(this.#u,this.#t.value,(()=>{(async()=>{if(this.#u&&this.#t.value){const{x:e,y:t,placement:i}=await computePosition(this.#u,this.#t.value,{placement:this.placement,middleware:[offset(this.offset),flip()]});this.#t.value.dataset.placement=i,Object.assign(this.#t.value.style,{left:`${e}px`,top:`${t}px`})}this.#t.value?.showPopover(),this.#r&&this.#k?.id&&(this.#r.ariaActivedescendant=this.#k.id),this.#u&&(this.#u.ariaExpanded="true")})()})))}get#u(){return this.#l.value?.assignedElements().at(0)}};__decorate([property({reflect:!0,type:Number})],GlideCoreMenu.prototype,"offset",null),__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 +1 @@
1
- export {};
1
+ import './menu.options.js';
@@ -1,10 +1,10 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-expressions */
2
+ import './menu.options.js';
2
3
  import { ArgumentError } from 'ow';
3
4
  import { aTimeout, expect, fixture, html } from '@open-wc/testing';
4
5
  import GlideCoreMenu from './menu.js';
5
6
  import GlideCoreMenuButton from './menu.button.js';
6
7
  import GlideCoreMenuLink from './menu.link.js';
7
- import GlideCoreMenuOptions from './menu.options.js';
8
8
  import expectArgumentError from './library/expect-argument-error.js';
9
9
  import sinon from 'sinon';
10
10
  GlideCoreMenu.shadowRootOptions.mode = 'open';
@@ -63,32 +63,6 @@ it('can be opened', async () => {
63
63
  expect(target?.ariaExpanded).to.equal('true');
64
64
  expect(options?.getAttribute('aria-activedescendant')).to.equal(link?.id);
65
65
  });
66
- it('can have a default slot', async () => {
67
- const component = await fixture(html `<glide-core-menu>
68
- <button slot="target">Target</button>
69
-
70
- <glide-core-menu-options>
71
- <glide-core-menu-link label="Link"></glide-core-menu-link>
72
- </glide-core-menu-options>
73
- </glide-core-menu>`);
74
- const assignedElements = component.shadowRoot
75
- ?.querySelectorAll('slot')[1]
76
- .assignedElements();
77
- expect(assignedElements?.at(0) instanceof GlideCoreMenuOptions).to.be.true;
78
- });
79
- it('can have a target slot', async () => {
80
- const component = await fixture(html `<glide-core-menu>
81
- <button slot="target">Target</button>
82
-
83
- <glide-core-menu-options>
84
- <glide-core-menu-link label="Link"></glide-core-menu-link>
85
- </glide-core-menu-options>
86
- </glide-core-menu>`);
87
- const assignedElements = component.shadowRoot
88
- ?.querySelector('slot[name="target"]')
89
- ?.assignedElements();
90
- expect(assignedElements?.at(0)?.textContent).to.equal('Target');
91
- });
92
66
  it('activates the first menu link by default', async () => {
93
67
  const component = await fixture(html `
94
68
  <glide-core-menu open>
@@ -144,6 +118,17 @@ it('is not opened when initially `open` and its target is `disabled`', async ()
144
118
  expect(target?.ariaExpanded).to.equal('false');
145
119
  expect(options?.getAttribute('aria-activedescendant')).to.equal('');
146
120
  });
121
+ it('adds `tabIndex` to its target when it is a `<span>`', async () => {
122
+ const component = await fixture(html `<glide-core-menu>
123
+ <span slot="target">Target</span>
124
+
125
+ <glide-core-menu-options>
126
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
127
+ </glide-core-menu-options>
128
+ </glide-core-menu>`);
129
+ const target = component.querySelector('span');
130
+ expect(target?.tabIndex).to.equal(0);
131
+ });
147
132
  it('throws if it does not have a default slot', async () => {
148
133
  await expectArgumentError(() => {
149
134
  return fixture(html `<glide-core-menu
@@ -269,6 +269,25 @@ it('opens on Enter', async () => {
269
269
  expect(options?.getAttribute('aria-activedescendant')).to.equal(link?.id);
270
270
  expect(target?.ariaExpanded).to.equal('true');
271
271
  });
272
+ it('opens on Enter when its target is a `<span>`', async () => {
273
+ const component = await fixture(html `<glide-core-menu>
274
+ <span slot="target">Target</span>
275
+
276
+ <glide-core-menu-options>
277
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
278
+ </glide-core-menu-options>
279
+ </glide-core-menu>`);
280
+ component.querySelector('span')?.focus();
281
+ await sendKeys({ press: 'Enter' });
282
+ const defaultSlot = component?.shadowRoot?.querySelector('slot:not([name])');
283
+ const options = component.querySelector('glide-core-menu-options');
284
+ const target = component.querySelector('span');
285
+ const link = component.querySelector('glide-core-menu-link');
286
+ expect(component.open).to.be.true;
287
+ expect(defaultSlot?.checkVisibility()).to.be.true;
288
+ expect(options?.getAttribute('aria-activedescendant')).to.equal(link?.id);
289
+ expect(target?.ariaExpanded).to.equal('true');
290
+ });
272
291
  it('opens on ArrowUp', async () => {
273
292
  const component = await fixture(html `<glide-core-menu>
274
293
  <button slot="target">Target</button>
@@ -326,6 +345,25 @@ it('opens on Space', async () => {
326
345
  expect(options?.getAttribute('aria-activedescendant')).to.equal(link?.id);
327
346
  expect(target?.ariaExpanded).to.equal('true');
328
347
  });
348
+ it('opens on Space when its target is a `<span>`', async () => {
349
+ const component = await fixture(html `<glide-core-menu>
350
+ <span slot="target">Target</span>
351
+
352
+ <glide-core-menu-options>
353
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
354
+ </glide-core-menu-options>
355
+ </glide-core-menu>`);
356
+ component.querySelector('span')?.focus();
357
+ await sendKeys({ press: ' ' });
358
+ const defaultSlot = component?.shadowRoot?.querySelector('slot:not([name])');
359
+ const options = component.querySelector('glide-core-menu-options');
360
+ const target = component.querySelector('span');
361
+ const link = component.querySelector('glide-core-menu-link');
362
+ expect(component.open).to.be.true;
363
+ expect(defaultSlot?.checkVisibility()).to.be.true;
364
+ expect(options?.getAttribute('aria-activedescendant')).to.equal(link?.id);
365
+ expect(target?.ariaExpanded).to.equal('true');
366
+ });
329
367
  it('does not open on Space when there are no options', async () => {
330
368
  const component = await fixture(html `<glide-core-menu>
331
369
  <button slot="target">Target</button>
@@ -494,6 +532,27 @@ it('closes when an option is selected via Enter', async () => {
494
532
  expect(options?.getAttribute('aria-activedescendant')).to.equal('');
495
533
  expect(target?.ariaExpanded).to.equal('false');
496
534
  });
535
+ it('closes when an option is selected via Enter and its target is a `<span>', async () => {
536
+ const component = await fixture(html `<glide-core-menu open>
537
+ <span slot="target">Target</span>
538
+
539
+ <glide-core-menu-options>
540
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
541
+ </glide-core-menu-options>
542
+ </glide-core-menu>`);
543
+ component.querySelector('span')?.focus();
544
+ component
545
+ .querySelector('glide-core-menu-link')
546
+ ?.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
547
+ await sendKeys({ press: 'Enter' });
548
+ const defaultSlot = component?.shadowRoot?.querySelector('slot:not([name])');
549
+ const options = component.querySelector('glide-core-menu-options');
550
+ const target = component.querySelector('span');
551
+ expect(component.open).to.be.false;
552
+ expect(defaultSlot?.checkVisibility()).to.be.false;
553
+ expect(options?.getAttribute('aria-activedescendant')).to.equal('');
554
+ expect(target?.ariaExpanded).to.equal('false');
555
+ });
497
556
  it('closes when an option is selected via Space', async () => {
498
557
  const component = await fixture(html `<glide-core-menu open>
499
558
  <button slot="target">Target</button>
@@ -512,6 +571,24 @@ it('closes when an option is selected via Space', async () => {
512
571
  expect(options?.getAttribute('aria-activedescendant')).to.equal('');
513
572
  expect(target?.ariaExpanded).to.equal('false');
514
573
  });
574
+ it('closes when an option is selected via Space and its target is a `<span>`', async () => {
575
+ const component = await fixture(html `<glide-core-menu open>
576
+ <span slot="target">Target</span>
577
+
578
+ <glide-core-menu-options>
579
+ <glide-core-menu-link label="Link"></glide-core-menu-link>
580
+ </glide-core-menu-options>
581
+ </glide-core-menu>`);
582
+ component.querySelector('span')?.focus();
583
+ await sendKeys({ press: ' ' });
584
+ const defaultSlot = component?.shadowRoot?.querySelector('slot:not([name])');
585
+ const options = component.querySelector('glide-core-menu-options');
586
+ const target = component.querySelector('span');
587
+ expect(component.open).to.be.false;
588
+ expect(defaultSlot?.checkVisibility()).to.be.false;
589
+ expect(options?.getAttribute('aria-activedescendant')).to.equal('');
590
+ expect(target?.ariaExpanded).to.equal('false');
591
+ });
515
592
  it('activates the first menu link by default', async () => {
516
593
  const component = await fixture(html `
517
594
  <glide-core-menu open>
@@ -939,3 +1016,16 @@ it('does not wrap on ArrowDown', async () => {
939
1016
  await sendKeys({ up: 'Meta' });
940
1017
  expect(options[1].privateActive).to.be.true;
941
1018
  });
1019
+ it('has `set offset()` coverage', async () => {
1020
+ const component = await fixture(html `
1021
+ <glide-core-menu>
1022
+ <button slot="target">Target</button>
1023
+
1024
+ <glide-core-menu-options>
1025
+ <glide-core-menu-link label="One"></glide-core-menu-link>
1026
+ <glide-core-menu-link label="Two"></glide-core-menu-link>
1027
+ </glide-core-menu-options>
1028
+ </glide-core-menu>
1029
+ `);
1030
+ component.offset = 10;
1031
+ });
@@ -39,10 +39,13 @@ export default class GlideCoreRadioGroup extends LitElement {
39
39
  formResetCallback(): void;
40
40
  render(): import("lit").TemplateResult<1>;
41
41
  reportValidity(): boolean;
42
+ setCustomValidity(message: string): void;
43
+ setValidity(flags?: ValidityStateFlags, message?: string): void;
42
44
  updated(changedProperties: PropertyValueMap<GlideCoreRadioGroup>): void;
43
45
  willUpdate(changedProperties: PropertyValueMap<GlideCoreRadioGroup>): void;
44
46
  constructor();
45
47
  private isBlurring;
46
48
  private isCheckingValidity;
47
49
  private isReportValidityOrSubmit;
50
+ private validityMessage?;
48
51
  }
@@ -1 +1,45 @@
1
- var __decorate=this&&this.__decorate||function(e,i,t,o){var s,a=arguments.length,d=a<3?i:null===o?o=Object.getOwnPropertyDescriptor(i,t):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)d=Reflect.decorate(e,i,t,o);else for(var r=e.length-1;r>=0;r--)(s=e[r])&&(d=(a<3?s(d):a>3?s(i,t,d):s(i,t))||d);return a>3&&d&&Object.defineProperty(i,t,d),d};import"./label.js";import"./tooltip.js";import{LitElement,html}from"lit";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{ifDefined}from"lit/directives/if-defined.js";import{owSlot,owSlotType}from"./library/ow.js";import GlideCoreRadio from"./radio.js";import styles from"./radio-group.styles.js";let GlideCoreRadioGroup=class GlideCoreRadioGroup extends LitElement{static{this.formAssociated=!0}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}checkValidity(){this.isCheckingValidity=!0;const e=this.#e.checkValidity();return this.isCheckingValidity=!1,e}disconnectedCallback(){super.disconnectedCallback(),this.form?.removeEventListener("formdata",this.#i),this.#t=void 0}firstUpdated(){owSlot(this.#o.value),owSlotType(this.#o.value,[GlideCoreRadio]),this.#t=this.#s.find((e=>e.checked)),this.#a()}focus(e){let i=this.#s.find((e=>e.checked));i||(i=this.#s.find((e=>0===e.tabIndex))),i?.focus(e)}get form(){return this.#e.form}get validity(){return this.#e.validity}get willValidate(){return this.#e.willValidate}formAssociatedCallback(){this.form?.addEventListener("formdata",this.#i)}formResetCallback(){if(this.#t&&this.contains(this.#t))for(const e of this.#s)this.#d(e===this.#t,e)}render(){return html`<div class="component" @click="${this.#r}" @keydown="${this.#l}" ${ref(this.#n)}><glide-core-private-label orientation="horizontal" split="${ifDefined(this.privateSplit??void 0)}" ?disabled="${this.disabled}" ?error="${this.#h}" ?hide="${this.hideLabel}" ?required="${this.required}"><label id="label" data-test="label">${this.label}</label><div class="${classMap({"radio-container":!0,vertical:!0,invalid:this.#h})}" role="radiogroup" slot="control" aria-labelledby="label description" @blur="${this.#c}"><slot ${ref(this.#o)} @slotchange="${this.#p}"></slot></div><slot name="tooltip" slot="tooltip"></slot><slot id="description" name="description" slot="description"></slot></glide-core-private-label></div>`}reportValidity(){this.isReportValidityOrSubmit=!0;const e=this.#e.reportValidity();return this.requestUpdate(),e}updated(e){this.hasUpdated&&(e.has("value")||e.has("required"))&&(this.#f(),this.#u(!this.#e.validity.valid),this.requestUpdate())}willUpdate(e){if(this.hasUpdated){if(e.has("required")&&this.#R(),e.has("disabled")){for(const e of this.#s)this.#m(this.disabled,e);!this.disabled&&this.#b()}if(e.has("value"))for(const e of this.#s)e.checked=e.value===this.value}}constructor(){super(),this.description="",this.disabled=!1,this.label="",this.hideLabel=!1,this.name="",this.required=!1,this.value="",this.isBlurring=!1,this.isCheckingValidity=!1,this.isReportValidityOrSubmit=!1,this.#n=createRef(),this.#o=createRef(),this.#t=void 0,this.#i=({formData:e})=>{this.name&&this.value.length>0&&!this.disabled&&e.append(this.name,this.value)},this.#e=this.attachInternals(),this.addEventListener("invalid",this.#v)}#n;#o;#t;#e;#C;#i;#a(){const e=this.#s.find((e=>e.checked));this.value=e?.value??this.#t?.value??"",this.#C=e??this.#t,this.required&&this.#R();for(const e of this.#s)this.disabled?this.#m(this.disabled,e):this.#m(e.disabled,e),e.addEventListener("blur",this.#y.bind(this));!this.disabled&&this.#b()}get#h(){const e=!this.disabled&&!this.validity.valid&&this.isReportValidityOrSubmit;return this.#u(e),e}#c(){this.isBlurring=!0,this.reportValidity(),this.isBlurring=!1}#r(e){if(this.disabled)return;if(e.target instanceof GlideCoreRadio&&e.target.disabled&&this.#C&&!this.#C.disabled)return void this.#C?.focus();const i=e.target;if(i instanceof GlideCoreRadio&&i&&!i.disabled){this.#d(!0,i);for(const e of this.#s)e!==i&&this.#d(!1,e)}}#p(){owSlot(this.#o.value),owSlotType(this.#o.value,[GlideCoreRadio]),this.#a()}#v(e){if(e.preventDefault(),!this.isCheckingValidity){if(e?.preventDefault(),this.isCheckingValidity||this.isBlurring)return;this.isReportValidityOrSubmit=!0;this.form?.querySelector(":invalid")===this&&this.focus()}}#l(e){if(!(this.disabled||e.target instanceof GlideCoreRadio&&e.target?.disabled)&&e.target instanceof GlideCoreRadio){const i=e.target;switch(e.key){case"Enter":this.form?.requestSubmit();break;case"ArrowUp":case"ArrowLeft":{e.preventDefault();let t=i.previousElementSibling;for(;(!t||t instanceof GlideCoreRadio&&t.disabled||!(t instanceof GlideCoreRadio))&&t!==i;)if(null===t){const e=this.#s.at(-1);e&&(t=e)}else t=t.previousElementSibling;t&&t instanceof GlideCoreRadio&&!t.disabled&&t!==i&&(this.#d(!1,i),this.#d(!0,t));break}case"ArrowDown":case"ArrowRight":{e.preventDefault();let t=i.nextElementSibling;for(;(!t||t instanceof GlideCoreRadio&&t.disabled||!(t instanceof GlideCoreRadio))&&t!==i;)if(null===t){const e=this.#s.at(0);e&&(t=e)}else t=t.nextElementSibling;t&&t instanceof GlideCoreRadio&&!t.disabled&&t!==i&&(this.#d(!1,i),this.#d(!0,t));break}case" ":if(e.preventDefault(),!i.disabled&&!i.checked){this.#d(!0,i);for(const e of this.#s)e!==i&&this.#d(!1,e)}}}}#y(e){const i=e.relatedTarget;i&&i instanceof GlideCoreRadio&&this.#s.includes(i)||this.#c()}#f(){const e=this.#s.find((e=>e.checked));this.required&&!e?this.#e.setValidity({valueMissing:!0}," ",this.#n.value):this.#e.setValidity({})}get#s(){return this.#o.value?.assignedElements().filter((e=>e instanceof GlideCoreRadio))??[]}#d(e,i){i.checked=e,i.tabIndex=e?0:-1,e&&(this.#C=i,this.value=i.value,i.focus(),i.dispatchEvent(new Event("change",{bubbles:!0})),i.dispatchEvent(new Event("input",{bubbles:!0})))}#m(e,i){i.disabled=e,e&&(i.tabIndex=-1)}#u(e){for(const i of this.#s)i.invalid=e}#b(){if(this.disabled||this.#s.every((e=>e.disabled)))return;let e=null;const i=this.#s.find((e=>!e.disabled&&e.checked));if(i)e=i;else{const i=this.#s.find((e=>!e.disabled));i&&(e=i)}if(e)for(const i of this.#s)i.tabIndex=i===e?0:-1}#R(){for(const e of this.#s)e.required=this.required}};__decorate([property()],GlideCoreRadioGroup.prototype,"description",void 0),__decorate([property({type:Boolean,reflect:!0})],GlideCoreRadioGroup.prototype,"disabled",void 0),__decorate([property()],GlideCoreRadioGroup.prototype,"label",void 0),__decorate([property({attribute:"hide-label",type:Boolean})],GlideCoreRadioGroup.prototype,"hideLabel",void 0),__decorate([property({reflect:!0})],GlideCoreRadioGroup.prototype,"name",void 0),__decorate([property()],GlideCoreRadioGroup.prototype,"privateSplit",void 0),__decorate([property({type:Boolean,reflect:!0})],GlideCoreRadioGroup.prototype,"required",void 0),__decorate([property({reflect:!0})],GlideCoreRadioGroup.prototype,"value",void 0),__decorate([state()],GlideCoreRadioGroup.prototype,"isBlurring",void 0),__decorate([state()],GlideCoreRadioGroup.prototype,"isCheckingValidity",void 0),__decorate([state()],GlideCoreRadioGroup.prototype,"isReportValidityOrSubmit",void 0),GlideCoreRadioGroup=__decorate([customElement("glide-core-radio-group")],GlideCoreRadioGroup);export default GlideCoreRadioGroup;
1
+ var __decorate=this&&this.__decorate||function(e,i,t,s){var o,a=arguments.length,d=a<3?i:null===s?s=Object.getOwnPropertyDescriptor(i,t):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)d=Reflect.decorate(e,i,t,s);else for(var r=e.length-1;r>=0;r--)(o=e[r])&&(d=(a<3?o(d):a>3?o(i,t,d):o(i,t))||d);return a>3&&d&&Object.defineProperty(i,t,d),d};import"./label.js";import"./tooltip.js";import{LitElement,html}from"lit";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{ifDefined}from"lit/directives/if-defined.js";import{owSlot,owSlotType}from"./library/ow.js";import{unsafeHTML}from"lit/directives/unsafe-html.js";import{when}from"lit/directives/when.js";import GlideCoreRadio from"./radio.js";import styles from"./radio-group.styles.js";let GlideCoreRadioGroup=class GlideCoreRadioGroup extends LitElement{static{this.formAssociated=!0}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}checkValidity(){this.isCheckingValidity=!0;const e=this.#e.checkValidity();return this.isCheckingValidity=!1,e}disconnectedCallback(){super.disconnectedCallback(),this.form?.removeEventListener("formdata",this.#i),this.#t=void 0}firstUpdated(){owSlot(this.#s.value),owSlotType(this.#s.value,[GlideCoreRadio]),this.#t=this.#o.find((e=>e.checked)),this.#a()}focus(e){let i=this.#o.find((e=>e.checked));i||(i=this.#o.find((e=>0===e.tabIndex))),i?.focus(e)}get form(){return this.#e.form}get validity(){const e=this.#o.some((({checked:e})=>e));return!this.required||e||this.value||this.disabled?this.required&&this.#e.validity.valueMissing&&(e||this.value)?(this.#e.setValidity({}),this.#e.validity):this.#e.validity:(this.#e.setValidity({customError:Boolean(this.validityMessage),valueMissing:!0}," ",this.#d.value),this.#e.validity)}get willValidate(){return this.#e.willValidate}formAssociatedCallback(){this.form?.addEventListener("formdata",this.#i)}formResetCallback(){if(this.#t&&this.contains(this.#t))for(const e of this.#o)this.#r(e===this.#t,e)}render(){return html`
2
+ <div
3
+ class="component"
4
+ @click=${this.#l}
5
+ @keydown=${this.#n}
6
+ ${ref(this.#d)}
7
+ >
8
+ <glide-core-private-label
9
+ orientation="horizontal"
10
+ split=${ifDefined(this.privateSplit??void 0)}
11
+ ?disabled=${this.disabled}
12
+ ?error=${this.#h}
13
+ ?hide=${this.hideLabel}
14
+ ?required=${this.required}
15
+ >
16
+ <label id="label" data-test="label"> ${this.label} </label>
17
+
18
+ <div
19
+ class=${classMap({"radio-container":!0,vertical:!0,invalid:this.#h})}
20
+ role="radiogroup"
21
+ slot="control"
22
+ aria-labelledby="label description"
23
+ @blur=${this.#c}
24
+ >
25
+ <slot
26
+ ${ref(this.#s)}
27
+ @slotchange=${this.#p}
28
+ ></slot>
29
+ </div>
30
+
31
+ <slot name="tooltip" slot="tooltip"></slot>
32
+
33
+ <div id="description" slot="description">
34
+ <slot
35
+ class=${classMap({description:!0,hidden:Boolean(this.#h&&this.validityMessage)})}
36
+ name="description"
37
+ ></slot>
38
+
39
+ ${when(this.#h&&this.validityMessage,(()=>html`<span class="validity-message" data-test="validity-message"
40
+ >${unsafeHTML(this.validityMessage)}</span
41
+ >`))}
42
+ </div>
43
+ </glide-core-private-label>
44
+ </div>
45
+ `}reportValidity(){this.isReportValidityOrSubmit=!0;const e=this.#e.reportValidity();return this.requestUpdate(),e}setCustomValidity(e){this.validityMessage=e,""===e?this.#e.setValidity({customError:!1},"",this.#d.value):this.#e.setValidity({customError:!0,valueMissing:this.#e.validity.valueMissing}," ",this.#d.value)}setValidity(e,i){this.validityMessage=i,this.#e.setValidity(e," ",this.#d.value)}updated(e){this.hasUpdated&&(e.has("value")||e.has("required"))&&(this.#u(),this.#f(!this.#e.validity.valid),this.requestUpdate())}willUpdate(e){if(this.hasUpdated){if(e.has("required")&&this.#m(),e.has("disabled")){for(const e of this.#o)this.#v(this.disabled,e);!this.disabled&&this.#R()}if(e.has("value"))for(const e of this.#o)e.checked=e.value===this.value}}constructor(){super(),this.description="",this.disabled=!1,this.label="",this.hideLabel=!1,this.name="",this.required=!1,this.value="",this.isBlurring=!1,this.isCheckingValidity=!1,this.isReportValidityOrSubmit=!1,this.#d=createRef(),this.#s=createRef(),this.#t=void 0,this.#i=({formData:e})=>{this.name&&this.value.length>0&&!this.disabled&&e.append(this.name,this.value)},this.#e=this.attachInternals(),this.addEventListener("invalid",this.#b)}#d;#s;#t;#e;#y;#i;#a(){const e=this.#o.find((e=>e.checked));this.value=e?.value??this.#t?.value??"",this.#y=e??this.#t,this.required&&this.#m();for(const e of this.#o)this.disabled?this.#v(this.disabled,e):this.#v(e.disabled,e),e.addEventListener("blur",this.#C.bind(this));!this.disabled&&this.#R()}get#h(){const e=!this.disabled&&!this.validity.valid&&this.isReportValidityOrSubmit;return this.#f(e),e}#c(){this.isBlurring=!0,this.reportValidity(),this.isBlurring=!1}#l(e){if(this.disabled)return;if(e.target instanceof GlideCoreRadio&&e.target.disabled&&this.#y&&!this.#y.disabled)return void this.#y?.focus();const i=e.target;if(i instanceof GlideCoreRadio&&i&&!i.disabled){this.#r(!0,i);for(const e of this.#o)e!==i&&this.#r(!1,e)}}#p(){owSlot(this.#s.value),owSlotType(this.#s.value,[GlideCoreRadio]),this.#a()}#b(e){if(e.preventDefault(),!this.isCheckingValidity){if(e?.preventDefault(),this.isCheckingValidity||this.isBlurring)return;this.isReportValidityOrSubmit=!0;this.form?.querySelector(":invalid")===this&&this.focus()}}#n(e){if(!(this.disabled||e.target instanceof GlideCoreRadio&&e.target?.disabled)&&e.target instanceof GlideCoreRadio){const i=e.target;switch(e.key){case"Enter":this.form?.requestSubmit();break;case"ArrowUp":case"ArrowLeft":{e.preventDefault();let t=i.previousElementSibling;for(;(!t||t instanceof GlideCoreRadio&&t.disabled||!(t instanceof GlideCoreRadio))&&t!==i;)if(null===t){const e=this.#o.at(-1);e&&(t=e)}else t=t.previousElementSibling;t&&t instanceof GlideCoreRadio&&!t.disabled&&t!==i&&(this.#r(!1,i),this.#r(!0,t));break}case"ArrowDown":case"ArrowRight":{e.preventDefault();let t=i.nextElementSibling;for(;(!t||t instanceof GlideCoreRadio&&t.disabled||!(t instanceof GlideCoreRadio))&&t!==i;)if(null===t){const e=this.#o.at(0);e&&(t=e)}else t=t.nextElementSibling;t&&t instanceof GlideCoreRadio&&!t.disabled&&t!==i&&(this.#r(!1,i),this.#r(!0,t));break}case" ":if(e.preventDefault(),!i.disabled&&!i.checked){this.#r(!0,i);for(const e of this.#o)e!==i&&this.#r(!1,e)}}}}#C(e){const i=e.relatedTarget;i&&i instanceof GlideCoreRadio&&this.#o.includes(i)||this.#c()}#u(){const e=this.#o.find((e=>e.checked));this.required&&!e?this.#e.setValidity({valueMissing:!0}," ",this.#d.value):this.#e.setValidity({})}get#o(){return this.#s.value?.assignedElements().filter((e=>e instanceof GlideCoreRadio))??[]}#r(e,i){i.checked=e,i.tabIndex=e?0:-1,e&&(this.#y=i,this.value=i.value,i.focus(),i.dispatchEvent(new Event("change",{bubbles:!0})),i.dispatchEvent(new Event("input",{bubbles:!0})))}#v(e,i){i.disabled=e,e&&(i.tabIndex=-1)}#f(e){for(const i of this.#o)i.invalid=e}#R(){if(this.disabled||this.#o.every((e=>e.disabled)))return;let e=null;const i=this.#o.find((e=>!e.disabled&&e.checked));if(i)e=i;else{const i=this.#o.find((e=>!e.disabled));i&&(e=i)}if(e)for(const i of this.#o)i.tabIndex=i===e?0:-1}#m(){for(const e of this.#o)e.required=this.required}};__decorate([property()],GlideCoreRadioGroup.prototype,"description",void 0),__decorate([property({type:Boolean,reflect:!0})],GlideCoreRadioGroup.prototype,"disabled",void 0),__decorate([property()],GlideCoreRadioGroup.prototype,"label",void 0),__decorate([property({attribute:"hide-label",type:Boolean})],GlideCoreRadioGroup.prototype,"hideLabel",void 0),__decorate([property({reflect:!0})],GlideCoreRadioGroup.prototype,"name",void 0),__decorate([property()],GlideCoreRadioGroup.prototype,"privateSplit",void 0),__decorate([property({type:Boolean,reflect:!0})],GlideCoreRadioGroup.prototype,"required",void 0),__decorate([property({reflect:!0})],GlideCoreRadioGroup.prototype,"value",void 0),__decorate([state()],GlideCoreRadioGroup.prototype,"isBlurring",void 0),__decorate([state()],GlideCoreRadioGroup.prototype,"isCheckingValidity",void 0),__decorate([state()],GlideCoreRadioGroup.prototype,"isReportValidityOrSubmit",void 0),__decorate([state()],GlideCoreRadioGroup.prototype,"validityMessage",void 0),GlideCoreRadioGroup=__decorate([customElement("glide-core-radio-group")],GlideCoreRadioGroup);export default GlideCoreRadioGroup;
@@ -33,4 +33,16 @@ import{css}from"lit";export default[css`
33
33
  glide-core-private-label::part(tooltips-and-label) {
34
34
  align-items: flex-start;
35
35
  }
36
+
37
+ .description {
38
+ display: block;
39
+
40
+ &.hidden {
41
+ display: none;
42
+ }
43
+ }
44
+
45
+ .validity-message {
46
+ display: block;
47
+ }
36
48
  `];