@crowdstrike/glide-core 0.11.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 (39) 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.validity.js +77 -1
  9. package/dist/dropdown.d.ts +3 -0
  10. package/dist/dropdown.js +195 -1
  11. package/dist/dropdown.styles.js +18 -6
  12. package/dist/dropdown.test.events.single.js +19 -0
  13. package/dist/dropdown.test.interactions.js +0 -18
  14. package/dist/dropdown.test.interactions.multiple.js +8 -10
  15. package/dist/dropdown.test.interactions.single.js +18 -0
  16. package/dist/dropdown.test.validity.js +172 -1
  17. package/dist/input.d.ts +4 -0
  18. package/dist/input.js +155 -1
  19. package/dist/input.styles.js +8 -0
  20. package/dist/input.test.validity.js +140 -62
  21. package/dist/menu.d.ts +2 -0
  22. package/dist/menu.js +1 -1
  23. package/dist/menu.test.basics.d.ts +1 -1
  24. package/dist/menu.test.basics.js +12 -27
  25. package/dist/menu.test.interactions.js +90 -0
  26. package/dist/radio-group.d.ts +3 -0
  27. package/dist/radio-group.js +45 -1
  28. package/dist/radio-group.styles.js +12 -0
  29. package/dist/radio-group.test.validity.js +82 -0
  30. package/dist/styles/variables.css +1 -1
  31. package/dist/tag.styles.js +1 -1
  32. package/dist/textarea.d.ts +3 -0
  33. package/dist/textarea.js +58 -2
  34. package/dist/textarea.styles.js +8 -0
  35. package/dist/textarea.test.events.js +0 -114
  36. package/dist/textarea.test.validity.js +64 -72
  37. package/dist/tooltip.d.ts +2 -1
  38. package/dist/tooltip.js +1 -1
  39. package/package.json +1 -1
@@ -41,8 +41,11 @@ export default class GlideCoreCheckboxGroup extends LitElement {
41
41
  formResetCallback(): void;
42
42
  render(): import("lit").TemplateResult<1>;
43
43
  reportValidity(): boolean;
44
+ setCustomValidity(message: string): void;
45
+ setValidity(flags?: ValidityStateFlags, message?: string): void;
44
46
  constructor();
45
47
  private isBlurring;
46
48
  private isCheckingValidity;
47
49
  private isReportValidityOrSubmit;
50
+ private validityMessage?;
48
51
  }
@@ -1 +1,43 @@
1
- var __decorate=this&&this.__decorate||function(e,t,i,o){var r,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--)(r=e[a])&&(l=(s<3?r(l):s>3?r(t,i,l):r(t,i))||l);return s>3&&l&&Object.defineProperty(t,i,l),l};import"./label.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 GlideCoreCheckbox from"./checkbox.js";import styles from"./checkbox-group.styles.js";let GlideCoreCheckboxGroup=class GlideCoreCheckboxGroup extends LitElement{static{this.formAssociated=!0}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}get disabled(){return this.#e}set disabled(e){this.#e=e;for(const t of this.#t)t.disabled=e}get required(){return this.#i}set required(e){this.#i=e;for(const t of this.#t)e?t.setValidity(this.#o.validity," "):t.setValidity({}),t.requestUpdate()}get value(){return this.#r}set value(e){this.#r=e;for(const t of this.#t){e.some((e=>e&&e===t.value))?t.checked=!0:t.value&&(t.checked=!1),t.checked&&t.disabled&&(t.disabled=!1)}}checkValidity(){this.isCheckingValidity=!0;const e=this.#o.checkValidity();return this.isCheckingValidity=!1,e}disconnectedCallback(){super.disconnectedCallback(),this.form?.removeEventListener("formdata",this.#s)}firstUpdated(){if(owSlot(this.#l.value),owSlotType(this.#l.value,[GlideCoreCheckbox]),this.disabled)for(const e of this.#t)e.disabled=!0;this.value=this.#t.filter((({checked:e,disabled:t})=>e&&!t)).map((({value:e})=>e)).filter((e=>Boolean(e)));for(const e of this.#t)e.privateVariant="minimal",e.addEventListener("blur",this.#a.bind(this))}get form(){return this.#o.form}get validity(){const e=this.#t.some((({checked:e})=>e));this.required&&!e?this.#o.setValidity({valueMissing:!0}," ",this.#d.value):this.#o.setValidity({});for(const e of this.#t)e.setValidity(this.#o.validity," "),e.requestUpdate();return this.#o.validity}get willValidate(){return this.#o.willValidate}focus(e){const t=this.#t.find((({disabled:e})=>!e));t?.focus(e)}formAssociatedCallback(){this.form?.addEventListener("formdata",this.#s)}formResetCallback(){for(const e of this.#t)e.formResetCallback()}render(){return html`<div class="component" data-test="component" ${ref(this.#d)}><glide-core-private-label split="${ifDefined(this.privateSplit??void 0)}" ?hide="${this.hideLabel}" ?disabled="${this.disabled}" ?error="${this.#c}" ?required="${this.required}"><slot name="tooltip" slot="tooltip"></slot><label id="label">${this.label}</label><div aria-labelledby="label description" role="group" slot="control" class="${classMap({"checkbox-container":!0,invalid:this.#c})}"><slot class="checkboxes" @change="${this.#n}" @private-value-change="${this.#h}" @slotchange="${this.#p}" ${ref(this.#l)}></slot></div><slot id="description" name="description" slot="description"></slot></glide-core-private-label></div>`}reportValidity(){this.isReportValidityOrSubmit=!0;const e=this.#o.reportValidity();return this.requestUpdate(),e}constructor(){super(),this.hideLabel=!1,this.name="",this.isBlurring=!1,this.isCheckingValidity=!1,this.isReportValidityOrSubmit=!1,this.#d=createRef(),this.#l=createRef(),this.#e=!1,this.#i=!1,this.#r=[],this.#s=({formData:e})=>{this.name&&this.value.length>0&&!this.disabled&&e.append(this.name,JSON.stringify(this.value))},this.#o=this.attachInternals(),this.addEventListener("invalid",(e=>{if(e?.preventDefault(),this.isCheckingValidity||this.isBlurring)return;this.isReportValidityOrSubmit=!0;this.form?.querySelector(":invalid")===this&&this.focus()}))}#d;#l;#o;#e;#i;#r;get#t(){return this.#l.value?this.#l.value.assignedElements().filter((e=>e instanceof GlideCoreCheckbox)):[]}#s;get#c(){return this.required&&!this.disabled&&!this.validity.valid&&this.isReportValidityOrSubmit}#u(){for(const e of this.#t)e.isReportValidityOrSubmit=!0}#a(e){const t=e.relatedTarget;t&&t instanceof GlideCoreCheckbox&&this.#t.includes(t)||this.#u()}#n(){this.value=this.#t.filter((({checked:e,disabled:t})=>e&&!t)).map((({value:e})=>e)).filter((e=>Boolean(e)))}#h(e){e.target instanceof GlideCoreCheckbox&&e.target.checked&&e.detail.new?this.value=this.#r.map((t=>t===e.detail.old?e.detail.new:t)):e.target instanceof GlideCoreCheckbox&&e.target.checked&&(this.value=this.#r.filter((t=>t!==e.detail.old)))}#p(){owSlot(this.#l.value),owSlotType(this.#l.value,[GlideCoreCheckbox])}};__decorate([property({reflect:!0,type:Boolean})],GlideCoreCheckboxGroup.prototype,"disabled",null),__decorate([property({attribute:"hide-label",type:Boolean})],GlideCoreCheckboxGroup.prototype,"hideLabel",void 0),__decorate([property({reflect:!0})],GlideCoreCheckboxGroup.prototype,"label",void 0),__decorate([property({reflect:!0})],GlideCoreCheckboxGroup.prototype,"name",void 0),__decorate([property()],GlideCoreCheckboxGroup.prototype,"privateSplit",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreCheckboxGroup.prototype,"required",null),__decorate([property({reflect:!0})],GlideCoreCheckboxGroup.prototype,"summary",void 0),__decorate([property({reflect:!0,type:Array})],GlideCoreCheckboxGroup.prototype,"value",null),__decorate([state()],GlideCoreCheckboxGroup.prototype,"isBlurring",void 0),__decorate([state()],GlideCoreCheckboxGroup.prototype,"isCheckingValidity",void 0),__decorate([state()],GlideCoreCheckboxGroup.prototype,"isReportValidityOrSubmit",void 0),GlideCoreCheckboxGroup=__decorate([customElement("glide-core-checkbox-group")],GlideCoreCheckboxGroup);export default GlideCoreCheckboxGroup;
1
+ var __decorate=this&&this.__decorate||function(e,t,i,o){var s,l=arguments.length,r=l<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,i):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,i,o);else for(var a=e.length-1;a>=0;a--)(s=e[a])&&(r=(l<3?s(r):l>3?s(t,i,r):s(t,i))||r);return l>3&&r&&Object.defineProperty(t,i,r),r};import"./label.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 GlideCoreCheckbox from"./checkbox.js";import styles from"./checkbox-group.styles.js";let GlideCoreCheckboxGroup=class GlideCoreCheckboxGroup extends LitElement{static{this.formAssociated=!0}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}get disabled(){return this.#e}set disabled(e){this.#e=e;for(const t of this.#t)t.disabled=e}get required(){return this.#i}set required(e){this.#i=e;for(const t of this.#t)e?t.setValidity(this.#o.validity," "):t.setValidity({}),t.requestUpdate()}get value(){return this.#s}set value(e){this.#s=e;for(const t of this.#t){e.some((e=>e&&e===t.value))?t.checked=!0:t.value&&(t.checked=!1),t.checked&&t.disabled&&(t.disabled=!1)}}checkValidity(){this.isCheckingValidity=!0;const e=this.#o.checkValidity();return this.isCheckingValidity=!1,e}disconnectedCallback(){super.disconnectedCallback(),this.form?.removeEventListener("formdata",this.#l)}firstUpdated(){if(owSlot(this.#r.value),owSlotType(this.#r.value,[GlideCoreCheckbox]),this.disabled)for(const e of this.#t)e.disabled=!0;this.value=this.#t.filter((({checked:e,disabled:t})=>e&&!t)).map((({value:e})=>e)).filter((e=>Boolean(e)));for(const e of this.#t)e.privateVariant="minimal",e.addEventListener("blur",this.#a.bind(this))}get form(){return this.#o.form}get validity(){const e=this.#t.some((({checked:e})=>e));this.required&&!e&&this.#o.setValidity({customError:Boolean(this.validityMessage),valueMissing:!0}," ",this.#d.value),this.required&&this.#o.validity.valueMissing&&e&&this.#o.setValidity({}),this.required||!this.#o.validity.valueMissing||e||this.#o.setValidity({});for(const e of this.#t)e.setValidity(this.#o.validity," "),e.requestUpdate();return this.#o.validity}get willValidate(){return this.#o.willValidate}focus(e){const t=this.#t.find((({disabled:e})=>!e));t?.focus(e)}formAssociatedCallback(){this.form?.addEventListener("formdata",this.#l)}formResetCallback(){for(const e of this.#t)e.formResetCallback()}render(){return html`<div
2
+ class="component"
3
+ data-test="component"
4
+ ${ref(this.#d)}
5
+ >
6
+ <glide-core-private-label
7
+ split=${ifDefined(this.privateSplit??void 0)}
8
+ ?hide=${this.hideLabel}
9
+ ?disabled=${this.disabled}
10
+ ?error=${this.#n}
11
+ ?required=${this.required}
12
+ >
13
+ <slot name="tooltip" slot="tooltip"></slot>
14
+ <label id="label">${this.label}</label>
15
+
16
+ <!-- "aria-describedby" is more appropriate for a description but isn't read by VoiceOver. -->
17
+ <div
18
+ aria-labelledby="label description"
19
+ role="group"
20
+ slot="control"
21
+ class=${classMap({"checkbox-container":!0,invalid:this.#n})}
22
+ >
23
+ <slot
24
+ class="checkboxes"
25
+ @change=${this.#c}
26
+ @private-value-change=${this.#h}
27
+ @slotchange=${this.#p}
28
+ ${ref(this.#r)}
29
+ ></slot>
30
+ </div>
31
+
32
+ <div id="description" slot="description">
33
+ <slot
34
+ class=${classMap({description:!0,hidden:Boolean(this.#n&&this.validityMessage)})}
35
+ name="description"
36
+ ></slot>
37
+
38
+ ${when(this.#n&&this.validityMessage,(()=>html`<span class="validity-message" data-test="validity-message"
39
+ >${unsafeHTML(this.validityMessage)}</span
40
+ >`))}
41
+ </div>
42
+ </glide-core-private-label>
43
+ </div>`}reportValidity(){this.isReportValidityOrSubmit=!0;const e=this.#o.reportValidity();return this.requestUpdate(),e}setCustomValidity(e){this.validityMessage=e,""===e?this.#o.setValidity({customError:!1},"",this.#d.value):this.#o.setValidity({customError:!0,valueMissing:this.#o.validity.valueMissing}," ",this.#d.value)}setValidity(e,t){this.validityMessage=t,this.#o.setValidity(e," ",this.#d.value)}constructor(){super(),this.hideLabel=!1,this.name="",this.isBlurring=!1,this.isCheckingValidity=!1,this.isReportValidityOrSubmit=!1,this.#d=createRef(),this.#r=createRef(),this.#e=!1,this.#i=!1,this.#s=[],this.#l=({formData:e})=>{this.name&&this.value.length>0&&!this.disabled&&e.append(this.name,JSON.stringify(this.value))},this.#o=this.attachInternals(),this.addEventListener("invalid",(e=>{if(e?.preventDefault(),this.isCheckingValidity||this.isBlurring)return;this.isReportValidityOrSubmit=!0;this.form?.querySelector(":invalid")===this&&this.focus()}))}#d;#r;#o;#e;#i;#s;get#t(){return this.#r.value?this.#r.value.assignedElements().filter((e=>e instanceof GlideCoreCheckbox)):[]}#l;get#n(){return!this.disabled&&!this.validity.valid&&this.isReportValidityOrSubmit}#u(){for(const e of this.#t)e.isReportValidityOrSubmit=!0}#a(e){const t=e.relatedTarget;t&&t instanceof GlideCoreCheckbox&&this.#t.includes(t)||this.#u()}#c(){this.value=this.#t.filter((({checked:e,disabled:t})=>e&&!t)).map((({value:e})=>e)).filter((e=>Boolean(e)))}#h(e){e.target instanceof GlideCoreCheckbox&&e.target.checked&&e.detail.new?this.value=this.#s.map((t=>t===e.detail.old?e.detail.new:t)):e.target instanceof GlideCoreCheckbox&&e.target.checked&&(this.value=this.#s.filter((t=>t!==e.detail.old)))}#p(){owSlot(this.#r.value),owSlotType(this.#r.value,[GlideCoreCheckbox])}};__decorate([property({reflect:!0,type:Boolean})],GlideCoreCheckboxGroup.prototype,"disabled",null),__decorate([property({attribute:"hide-label",type:Boolean})],GlideCoreCheckboxGroup.prototype,"hideLabel",void 0),__decorate([property({reflect:!0})],GlideCoreCheckboxGroup.prototype,"label",void 0),__decorate([property({reflect:!0})],GlideCoreCheckboxGroup.prototype,"name",void 0),__decorate([property()],GlideCoreCheckboxGroup.prototype,"privateSplit",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreCheckboxGroup.prototype,"required",null),__decorate([property({reflect:!0})],GlideCoreCheckboxGroup.prototype,"summary",void 0),__decorate([property({reflect:!0,type:Array})],GlideCoreCheckboxGroup.prototype,"value",null),__decorate([state()],GlideCoreCheckboxGroup.prototype,"isBlurring",void 0),__decorate([state()],GlideCoreCheckboxGroup.prototype,"isCheckingValidity",void 0),__decorate([state()],GlideCoreCheckboxGroup.prototype,"isReportValidityOrSubmit",void 0),__decorate([state()],GlideCoreCheckboxGroup.prototype,"validityMessage",void 0),GlideCoreCheckboxGroup=__decorate([customElement("glide-core-checkbox-group")],GlideCoreCheckboxGroup);export default GlideCoreCheckboxGroup;
@@ -29,4 +29,16 @@ import{css}from"lit";export default[css`
29
29
  grid-column: 2;
30
30
  row-gap: var(--glide-core-spacing-xs);
31
31
  }
32
+
33
+ .description {
34
+ display: block;
35
+
36
+ &.hidden {
37
+ display: none;
38
+ }
39
+ }
40
+
41
+ .validity-message {
42
+ display: block;
43
+ }
32
44
  `];
@@ -74,3 +74,72 @@ it('sets the checkbox as invalid when `required` is set to `true` dynamically',
74
74
  const checkbox = component.querySelector('glide-core-checkbox');
75
75
  expect(checkbox?.validity.valid).to.be.false;
76
76
  });
77
+ it('sets the validity message with `setCustomValidity()`', async () => {
78
+ const form = document.createElement('form');
79
+ const component = await fixture(html `<glide-core-checkbox-group label="Checkbox Group">
80
+ <glide-core-checkbox label="Checkbox" value="value"></glide-core-checkbox>
81
+ </glide-core-checkbox-group>`, { parentNode: form });
82
+ component.setCustomValidity('validity message');
83
+ expect(component.validity?.valid).to.be.false;
84
+ expect(component.validity?.customError).to.be.true;
85
+ expect(component.checkValidity()).to.be.false;
86
+ expect(component.reportValidity()).to.be.false;
87
+ await elementUpdated(component);
88
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
89
+ ?.textContent).to.equal('validity message');
90
+ });
91
+ it('removes a validity message with an empty argument to `setCustomValidity()`', async () => {
92
+ const form = document.createElement('form');
93
+ const component = await fixture(html `<glide-core-checkbox-group label="Checkbox Group">
94
+ <glide-core-checkbox label="Checkbox" value="value"></glide-core-checkbox>
95
+ </glide-core-checkbox-group>`, { parentNode: form });
96
+ component.setCustomValidity('validity message');
97
+ component.reportValidity();
98
+ await elementUpdated(component);
99
+ component.setCustomValidity('');
100
+ await elementUpdated(component);
101
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
102
+ ?.textContent).to.be.undefined;
103
+ });
104
+ it('is invalid when `setValidity()` is called', async () => {
105
+ const form = document.createElement('form');
106
+ const component = await fixture(html `<glide-core-checkbox-group label="Checkbox Group">
107
+ <glide-core-checkbox label="Checkbox" value="value"></glide-core-checkbox>
108
+ </glide-core-checkbox-group>`, { parentNode: form });
109
+ component.setValidity({ customError: true }, 'validity message');
110
+ expect(component.validity.valid).to.be.false;
111
+ await elementUpdated(component);
112
+ // Like native, the validity message shouldn't display until `reportValidity()` is called.
113
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
114
+ ?.textContent).to.be.undefined;
115
+ expect(component.validity?.customError).to.be.true;
116
+ component.reportValidity();
117
+ await elementUpdated(component);
118
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
119
+ ?.textContent).to.equal('validity message');
120
+ });
121
+ it('is valid when `setValidity()` is called', async () => {
122
+ const form = document.createElement('form');
123
+ const component = await fixture(html `<glide-core-checkbox-group label="Checkbox Group">
124
+ <glide-core-checkbox label="Checkbox" value="value"></glide-core-checkbox>
125
+ </glide-core-checkbox-group>`, { parentNode: form });
126
+ component.setValidity({ customError: true }, 'validity message');
127
+ component.setValidity({});
128
+ await elementUpdated(component);
129
+ expect(component.validity.valid).to.be.true;
130
+ expect(component.validity.customError).to.be.false;
131
+ expect(component.reportValidity()).to.be.true;
132
+ await elementUpdated(component);
133
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
134
+ ?.textContent).to.be.undefined;
135
+ });
136
+ it('retains existing validity state when `setCustomValidity()` is called', async () => {
137
+ const form = document.createElement('form');
138
+ const component = await fixture(html `<glide-core-checkbox-group label="Checkbox Group" required>
139
+ <glide-core-checkbox label="Checkbox" value="value"></glide-core-checkbox>
140
+ </glide-core-checkbox-group>`, { parentNode: form });
141
+ component.setCustomValidity('validity message');
142
+ expect(component.validity?.valid).to.be.false;
143
+ expect(component.validity?.customError).to.be.true;
144
+ expect(component.validity?.valueMissing).to.be.true;
145
+ });
@@ -51,11 +51,13 @@ export default class GlideCoreCheckbox extends LitElement {
51
51
  formResetCallback(): void;
52
52
  render(): import("lit").TemplateResult<1>;
53
53
  reportValidity(): boolean;
54
- setValidity(flags?: ValidityStateFlags, message?: string, anchor?: HTMLElement): void;
54
+ setCustomValidity(message: string): void;
55
+ setValidity(flags?: ValidityStateFlags, message?: string): void;
55
56
  get willValidate(): boolean;
56
57
  updated(): void;
57
58
  constructor();
58
59
  private isBlurring;
59
60
  private isCheckingValidity;
60
61
  private isLabelOverflow;
62
+ private validityMessage?;
61
63
  }
package/dist/checkbox.js CHANGED
@@ -1 +1,135 @@
1
- var __decorate=this&&this.__decorate||function(e,t,i,o){var r,a=arguments.length,s=a<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,i):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,i,o);else for(var l=e.length-1;l>=0;l--)(r=e[l])&&(s=(a<3?r(s):a>3?r(t,i,s):r(t,i))||s);return a>3&&s&&Object.defineProperty(t,i,s),s};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{when}from"lit/directives/when.js";import checkedIcon from"./icons/checked.js";import styles from"./checkbox.styles.js";const indeterminateIcon=html`<svg width="14" height="14" viewBox="0 0 14 14" fill="none" class="indeterminate-icon"><rect x="0.5" y="0.5" width="13" height="13" rx="3.5"/><path d="M3 5C3 3.89543 3.89543 3 5 3H9.79289C10.2383 3 10.4614 3.53857 10.1464 3.85355L3.85355 10.1464C3.53857 10.4614 3 10.2383 3 9.79289V5Z" fill="currentColor"/></svg>`;let GlideCoreCheckbox=class GlideCoreCheckbox extends LitElement{static{this.formAssociated=!0}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}get label(){return this.#e}set label(e){this.#e=e,setTimeout((()=>{this.#t()}))}get value(){return this.#i}set value(e){const t=this.#i;this.#i=e,this.dispatchEvent(new CustomEvent("private-value-change",{bubbles:!0,detail:{old:t,new:e}}))}get form(){return this.#o.form}blur(){this.#r.value?.blur()}checkValidity(){this.isCheckingValidity=!0;const e=this.#o.checkValidity();return this.isCheckingValidity=!1,e}click(){this.#r.value?.click()}connectedCallback(){super.connectedCallback(),this.#a=new IntersectionObserver((()=>{this.checkVisibility()&&this.#t()})),this.#a.observe(this)}disconnectedCallback(){super.disconnectedCallback(),this.form?.removeEventListener("formdata",this.#s),this.#a?.disconnect()}get validity(){return"minimal"===this.privateVariant||(this.required&&!this.checked?this.#o.setValidity({valueMissing:!0}," ",this.#r.value):this.#o.setValidity({})),this.#o.validity}focus(e){this.#r.value?.focus(e)}formAssociatedCallback(){this.form?.addEventListener("formdata",this.#s)}formResetCallback(){this.checked=""===this.getAttribute("checked"),this.indeterminate=""===this.getAttribute("indeterminate")}render(){return html`<div class="component" data-test="component">${when("minimal"===this.privateVariant,(()=>html`<label class="${classMap({"label-and-input-and-checkbox":!0,[this.privateSize]:!0})}" part="private-label-and-input-and-checkbox"><div class="input-and-checkbox"><input aria-invalid="${this.#l}" data-test="input" type="checkbox" .checked="${this.checked}" .inert="${this.internallyInert}" ?disabled="${this.disabled}" ?required="${this.required}" @change="${this.#n}" @input="${this.#n}" @keydown="${this.#d}" ${ref(this.#r)}><div class="${classMap({checkbox:!0,disabled:this.disabled})}"><div class="checked-icon">${checkedIcon}</div>${indeterminateIcon}</div></div><div class="icon-and-label"><slot name="private-icon"></slot><glide-core-tooltip class="label-tooltip" offset="${this.privateLabelTooltipOffset}" ?disabled="${!this.isLabelOverflow}" ?open="${this.privateShowLabelTooltip}"><div aria-hidden="true" data-test="tooltip">${this.label}</div><div class="label" slot="target" ${ref(this.#h)}>${this.label}</div></glide-core-tooltip></div></label>`),(()=>html`<glide-core-private-label orientation="${this.orientation}" split="${ifDefined(this.privateSplit??void 0)}" ?disabled="${this.disabled}" ?error="${this.#l}" ?hide="${this.hideLabel}" ?required="${this.required}"><slot name="tooltip" slot="tooltip"></slot><label for="input">${this.label}</label><div class="input-and-checkbox" slot="control"><input aria-describedby="summary description" aria-invalid="${this.#l}" data-test="input" id="input" type="checkbox" .checked="${this.checked}" ?disabled="${this.disabled}" ?required="${this.required}" @blur="${this.#c}" @change="${this.#n}" @input="${this.#n}" @keydown="${this.#d}" ${ref(this.#r)}><div class="${classMap({checkbox:!0,disabled:this.disabled,error:this.#l})}"><div class="checked-icon">${checkedIcon}</div>${indeterminateIcon}</div></div><div id="summary" slot="summary">${this.summary}</div><slot id="description" name="description" slot="description"></slot></glide-core-private-label>`))}</div>`}reportValidity(){this.isReportValidityOrSubmit=!0;const e=this.#o.reportValidity();return this.requestUpdate(),e}setValidity(e,t,i){return this.#o.setValidity(e,t,i)}get willValidate(){return this.#o.willValidate}updated(){this.#r.value&&(this.#r.value.indeterminate=this.indeterminate)}constructor(){super(),this.checked=!1,this.internallyInert=!1,this.disabled=!1,this.hideLabel=!1,this.indeterminate=!1,this.orientation="horizontal",this.name="",this.privateLabelTooltipOffset=4,this.privateShowLabelTooltip=!1,this.privateSize="large",this.required=!1,this.isReportValidityOrSubmit=!1,this.isBlurring=!1,this.isCheckingValidity=!1,this.isLabelOverflow=!1,this.#r=createRef(),this.#e="",this.#h=createRef(),this.#i="",this.#s=({formData:e})=>{this.checked&&this.name&&this.value&&!this.disabled&&e.append(this.name,this.value)},this.#o=this.attachInternals(),this.addEventListener("invalid",(e=>{if(e?.preventDefault(),this.isCheckingValidity||this.isBlurring)return;this.isReportValidityOrSubmit=!0;this.form?.querySelector(":invalid")===this&&this.focus()}))}#r;#o;#a;#e;#h;#i;#s;get#l(){return"minimal"===this.privateVariant?!this.validity.valid&&this.isReportValidityOrSubmit:this.required&&!this.disabled&&!this.validity.valid&&this.isReportValidityOrSubmit}#c(){this.isBlurring=!0,this.reportValidity(),this.isBlurring=!1}#n(e){e.target instanceof HTMLInputElement&&(this.checked=e.target.checked),this.indeterminate=!1,"change"===e.type&&this.dispatchEvent(new Event(e.type,e))}#d(e){"Enter"===e.key&&this.form?.requestSubmit()}#t(){this.#h.value&&(this.isLabelOverflow=this.#h.value.scrollWidth>this.#h.value.clientWidth)}};__decorate([property({type:Boolean})],GlideCoreCheckbox.prototype,"checked",void 0),__decorate([property({attribute:"internally-inert",type:Boolean})],GlideCoreCheckbox.prototype,"internallyInert",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreCheckbox.prototype,"disabled",void 0),__decorate([property({attribute:"hide-label",type:Boolean})],GlideCoreCheckbox.prototype,"hideLabel",void 0),__decorate([property({type:Boolean})],GlideCoreCheckbox.prototype,"indeterminate",void 0),__decorate([property({reflect:!0})],GlideCoreCheckbox.prototype,"label",null),__decorate([property({reflect:!0})],GlideCoreCheckbox.prototype,"orientation",void 0),__decorate([property({reflect:!0})],GlideCoreCheckbox.prototype,"name",void 0),__decorate([property({attribute:"private-label-tooltip-offset",reflect:!0,type:Number})],GlideCoreCheckbox.prototype,"privateLabelTooltipOffset",void 0),__decorate([property({attribute:"private-show-label-tooltip",reflect:!0,type:Boolean})],GlideCoreCheckbox.prototype,"privateShowLabelTooltip",void 0),__decorate([property({attribute:"private-size"})],GlideCoreCheckbox.prototype,"privateSize",void 0),__decorate([property()],GlideCoreCheckbox.prototype,"privateSplit",void 0),__decorate([property({attribute:"private-variant"})],GlideCoreCheckbox.prototype,"privateVariant",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreCheckbox.prototype,"required",void 0),__decorate([property({reflect:!0})],GlideCoreCheckbox.prototype,"summary",void 0),__decorate([property({reflect:!0})],GlideCoreCheckbox.prototype,"value",null),__decorate([state()],GlideCoreCheckbox.prototype,"isReportValidityOrSubmit",void 0),__decorate([state()],GlideCoreCheckbox.prototype,"isBlurring",void 0),__decorate([state()],GlideCoreCheckbox.prototype,"isCheckingValidity",void 0),__decorate([state()],GlideCoreCheckbox.prototype,"isLabelOverflow",void 0),GlideCoreCheckbox=__decorate([customElement("glide-core-checkbox")],GlideCoreCheckbox);export default GlideCoreCheckbox;
1
+ var __decorate=this&&this.__decorate||function(e,t,i,s){var a,o=arguments.length,r=o<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,i,s);else for(var l=e.length-1;l>=0;l--)(a=e[l])&&(r=(o<3?a(r):o>3?a(t,i,r):a(t,i))||r);return o>3&&r&&Object.defineProperty(t,i,r),r};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{unsafeHTML}from"lit/directives/unsafe-html.js";import{when}from"lit/directives/when.js";import checkedIcon from"./icons/checked.js";import styles from"./checkbox.styles.js";const indeterminateIcon=html`
2
+ <svg
3
+ width="14"
4
+ height="14"
5
+ viewBox="0 0 14 14"
6
+ fill="none"
7
+ class="indeterminate-icon"
8
+ >
9
+ <rect x="0.5" y="0.5" width="13" height="13" rx="3.5" />
10
+ <path
11
+ d="M3 5C3 3.89543 3.89543 3 5 3H9.79289C10.2383 3 10.4614 3.53857 10.1464 3.85355L3.85355 10.1464C3.53857 10.4614 3 10.2383 3 9.79289V5Z"
12
+ fill="currentColor"
13
+ />
14
+ </svg>
15
+ `;let GlideCoreCheckbox=class GlideCoreCheckbox extends LitElement{static{this.formAssociated=!0}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}get label(){return this.#e}set label(e){this.#e=e,setTimeout((()=>{this.#t()}))}get value(){return this.#i}set value(e){const t=this.#i;this.#i=e,this.dispatchEvent(new CustomEvent("private-value-change",{bubbles:!0,detail:{old:t,new:e}}))}get form(){return this.#s.form}blur(){this.#a.value?.blur()}checkValidity(){this.isCheckingValidity=!0;const e=this.#s.checkValidity();return this.isCheckingValidity=!1,e}click(){this.#a.value?.click()}connectedCallback(){super.connectedCallback(),this.#o=new IntersectionObserver((()=>{this.checkVisibility()&&this.#t()})),this.#o.observe(this)}disconnectedCallback(){super.disconnectedCallback(),this.form?.removeEventListener("formdata",this.#r),this.#o?.disconnect()}get validity(){return"minimal"===this.privateVariant?this.#s.validity:this.required&&!this.checked?(this.#s.setValidity({customError:Boolean(this.validityMessage),valueMissing:!0}," ",this.#a.value),this.#s.validity):this.required&&this.#s.validity.valueMissing&&this.checked?(this.#s.setValidity({}),this.#s.validity):(this.required||!this.#s.validity.valueMissing||this.checked||this.#s.setValidity({}),this.#s.validity)}focus(e){this.#a.value?.focus(e)}formAssociatedCallback(){this.form?.addEventListener("formdata",this.#r)}formResetCallback(){this.checked=""===this.getAttribute("checked"),this.indeterminate=""===this.getAttribute("indeterminate")}render(){return html`<div class="component" data-test="component">
16
+ ${when("minimal"===this.privateVariant,(()=>html`
17
+ <label
18
+ class=${classMap({"label-and-input-and-checkbox":!0,[this.privateSize]:!0})}
19
+ part="private-label-and-input-and-checkbox"
20
+ >
21
+ <div class="input-and-checkbox">
22
+ <input
23
+ aria-invalid=${this.#l}
24
+ data-test="input"
25
+ type="checkbox"
26
+ .checked=${this.checked}
27
+ .inert=${this.internallyInert}
28
+ ?disabled=${this.disabled}
29
+ ?required=${this.required}
30
+ @change=${this.#n}
31
+ @input=${this.#n}
32
+ @keydown=${this.#d}
33
+ ${ref(this.#a)}
34
+ />
35
+
36
+ <div
37
+ class=${classMap({checkbox:!0,disabled:this.disabled})}
38
+ >
39
+ <div class="checked-icon">${checkedIcon}</div>
40
+ ${indeterminateIcon}
41
+ </div>
42
+ </div>
43
+
44
+ <div class="icon-and-label">
45
+ <slot name="private-icon"></slot>
46
+
47
+ <glide-core-tooltip
48
+ class="label-tooltip"
49
+ offset=${this.privateLabelTooltipOffset}
50
+ ?disabled=${!this.isLabelOverflow}
51
+ ?open=${this.privateShowLabelTooltip}
52
+ >
53
+ <div aria-hidden="true" data-test="tooltip">${this.label}</div>
54
+
55
+ <div class="label" slot="target" ${ref(this.#h)}>
56
+ ${this.label}
57
+ </div>
58
+ </glide-core-tooltip>
59
+ </div>
60
+ </label>
61
+ `),(()=>html`<glide-core-private-label
62
+ orientation=${this.orientation}
63
+ split=${ifDefined(this.privateSplit??void 0)}
64
+ ?disabled=${this.disabled}
65
+ ?error=${this.#l}
66
+ ?hide=${this.hideLabel}
67
+ ?required=${this.required}
68
+ >
69
+ <slot name="tooltip" slot="tooltip"></slot>
70
+ <label for="input"> ${this.label} </label>
71
+
72
+ <!--
73
+ The input is described by the summary and description but not the tooltip.
74
+ Screenreaders will come across the tooltip naturally as focus moves toward
75
+ the Checkbox.
76
+
77
+
78
+
79
+ A native input isn't necessary given the component itself is form associated.
80
+ A button, for example, could also be made to work. But an input gives us a
81
+ few things that together make using one worthwhile:
82
+
83
+ - "change" and "input" events.
84
+ - Toggling checked using the spacebar.
85
+ - ":checked" and ":indeterminate" pseudo classes, which browsers don't support
86
+ on hosts even when a component is form-associated.
87
+
88
+ -
89
+
90
+ aria-invalid is set based on whether the validation feedback is displayed. This
91
+ is to handle an odd behavior with checkboxes where "Invalid Data" is announced
92
+ on required unchecked inputs. While this is technically correct, it's
93
+ inconsistent with how other form controls behave as their validity isn’t announced
94
+ on screen readers by default before validation.
95
+ -->
96
+ <div class="input-and-checkbox" slot="control">
97
+ <input
98
+ aria-describedby="summary description"
99
+ aria-invalid=${this.#l}
100
+ data-test="input"
101
+ id="input"
102
+ type="checkbox"
103
+ .checked=${this.checked}
104
+ ?disabled=${this.disabled}
105
+ ?required=${this.required}
106
+ @blur=${this.#c}
107
+ @change=${this.#n}
108
+ @input=${this.#n}
109
+ @keydown=${this.#d}
110
+ ${ref(this.#a)}
111
+ />
112
+
113
+ <div
114
+ class=${classMap({checkbox:!0,disabled:this.disabled,error:this.#l})}
115
+ >
116
+ <div class="checked-icon">${checkedIcon}</div>
117
+ ${indeterminateIcon}
118
+ </div>
119
+ </div>
120
+
121
+ <div id="summary" slot="summary">${this.summary}</div>
122
+
123
+ <div id="description" slot="description">
124
+ <slot
125
+ class=${classMap({description:!0,hidden:Boolean(this.#l&&this.validityMessage)})}
126
+ name="description"
127
+ ></slot>
128
+ ${when(this.#l&&this.validityMessage,(()=>html`<span
129
+ class="validity-message"
130
+ data-test="validity-message"
131
+ >${unsafeHTML(this.validityMessage)}</span
132
+ >`))}
133
+ </div>
134
+ </glide-core-private-label>`))}
135
+ </div>`}reportValidity(){this.isReportValidityOrSubmit=!0;const e=this.#s.reportValidity();return this.requestUpdate(),e}setCustomValidity(e){this.validityMessage=e,""===e?this.#s.setValidity({customError:!1},"",this.#a.value):this.#s.setValidity({customError:!0,valueMissing:this.#s.validity.valueMissing}," ",this.#a.value)}setValidity(e,t){this.validityMessage=t,this.#s.setValidity(e," ",this.#a.value)}get willValidate(){return this.#s.willValidate}updated(){this.#a.value&&(this.#a.value.indeterminate=this.indeterminate)}constructor(){super(),this.checked=!1,this.internallyInert=!1,this.disabled=!1,this.hideLabel=!1,this.indeterminate=!1,this.orientation="horizontal",this.name="",this.privateLabelTooltipOffset=4,this.privateShowLabelTooltip=!1,this.privateSize="large",this.required=!1,this.isReportValidityOrSubmit=!1,this.isBlurring=!1,this.isCheckingValidity=!1,this.isLabelOverflow=!1,this.#a=createRef(),this.#e="",this.#h=createRef(),this.#i="",this.#r=({formData:e})=>{this.checked&&this.name&&this.value&&!this.disabled&&e.append(this.name,this.value)},this.#s=this.attachInternals(),this.addEventListener("invalid",(e=>{if(e?.preventDefault(),this.isCheckingValidity||this.isBlurring)return;this.isReportValidityOrSubmit=!0;this.form?.querySelector(":invalid")===this&&this.focus()}))}#a;#s;#o;#e;#h;#i;#r;get#l(){return"minimal"===this.privateVariant?!this.validity.valid&&this.isReportValidityOrSubmit:!this.disabled&&!this.validity.valid&&this.isReportValidityOrSubmit}#c(){this.isBlurring=!0,this.reportValidity(),this.isBlurring=!1}#n(e){e.target instanceof HTMLInputElement&&(this.checked=e.target.checked),this.indeterminate=!1,"change"===e.type&&this.dispatchEvent(new Event(e.type,e))}#d(e){"Enter"===e.key&&this.form?.requestSubmit()}#t(){this.#h.value&&(this.isLabelOverflow=this.#h.value.scrollWidth>this.#h.value.clientWidth)}};__decorate([property({type:Boolean})],GlideCoreCheckbox.prototype,"checked",void 0),__decorate([property({attribute:"internally-inert",type:Boolean})],GlideCoreCheckbox.prototype,"internallyInert",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreCheckbox.prototype,"disabled",void 0),__decorate([property({attribute:"hide-label",type:Boolean})],GlideCoreCheckbox.prototype,"hideLabel",void 0),__decorate([property({type:Boolean})],GlideCoreCheckbox.prototype,"indeterminate",void 0),__decorate([property({reflect:!0})],GlideCoreCheckbox.prototype,"label",null),__decorate([property({reflect:!0})],GlideCoreCheckbox.prototype,"orientation",void 0),__decorate([property({reflect:!0})],GlideCoreCheckbox.prototype,"name",void 0),__decorate([property({attribute:"private-label-tooltip-offset",reflect:!0,type:Number})],GlideCoreCheckbox.prototype,"privateLabelTooltipOffset",void 0),__decorate([property({attribute:"private-show-label-tooltip",reflect:!0,type:Boolean})],GlideCoreCheckbox.prototype,"privateShowLabelTooltip",void 0),__decorate([property({attribute:"private-size"})],GlideCoreCheckbox.prototype,"privateSize",void 0),__decorate([property()],GlideCoreCheckbox.prototype,"privateSplit",void 0),__decorate([property({attribute:"private-variant"})],GlideCoreCheckbox.prototype,"privateVariant",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreCheckbox.prototype,"required",void 0),__decorate([property({reflect:!0})],GlideCoreCheckbox.prototype,"summary",void 0),__decorate([property({reflect:!0})],GlideCoreCheckbox.prototype,"value",null),__decorate([state()],GlideCoreCheckbox.prototype,"isReportValidityOrSubmit",void 0),__decorate([state()],GlideCoreCheckbox.prototype,"isBlurring",void 0),__decorate([state()],GlideCoreCheckbox.prototype,"isCheckingValidity",void 0),__decorate([state()],GlideCoreCheckbox.prototype,"isLabelOverflow",void 0),__decorate([state()],GlideCoreCheckbox.prototype,"validityMessage",void 0),GlideCoreCheckbox=__decorate([customElement("glide-core-checkbox")],GlideCoreCheckbox);export default GlideCoreCheckbox;
@@ -185,4 +185,16 @@ when browsers support them.
185
185
  text-overflow: ellipsis;
186
186
  white-space: nowrap;
187
187
  }
188
+
189
+ .description {
190
+ display: block;
191
+
192
+ &.hidden {
193
+ display: none;
194
+ }
195
+ }
196
+
197
+ .validity-message {
198
+ display: block;
199
+ }
188
200
  `];
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-expressions */
2
- import { expect, fixture, html } from '@open-wc/testing';
2
+ import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
3
3
  import GlideCoreCheckbox from './checkbox.js';
4
4
  GlideCoreCheckbox.shadowRootOptions.mode = 'open';
5
5
  it('is valid if unchecked but not required', async () => {
@@ -50,3 +50,79 @@ it('is both invalid and valid if unchecked and required but disabled', async ()
50
50
  expect(component.checkValidity()).to.be.true;
51
51
  expect(component.reportValidity()).to.be.true;
52
52
  });
53
+ it('is valid when `value` is empty and `required` is updated to `false` programmatically', async () => {
54
+ const component = await fixture(html `<glide-core-checkbox label="Label" required></glide-core-checkbox>`);
55
+ expect(component.validity?.valid).to.be.false;
56
+ expect(component.validity?.valueMissing).to.be.true;
57
+ expect(component.checkValidity()).to.be.false;
58
+ expect(component.reportValidity()).to.be.false;
59
+ await elementUpdated(component);
60
+ expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('true');
61
+ component.required = false;
62
+ await elementUpdated(component);
63
+ expect(component.validity?.valid).to.be.true;
64
+ expect(component.validity?.valueMissing).to.be.false;
65
+ expect(component.checkValidity()).to.be.true;
66
+ expect(component.reportValidity()).to.be.true;
67
+ expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('false');
68
+ });
69
+ it('sets the validity message with `setCustomValidity()`', async () => {
70
+ const component = await fixture(html `<glide-core-checkbox label="Label"></glide-core-checkbox>`);
71
+ component.setCustomValidity('validity message');
72
+ expect(component.validity?.valid).to.be.false;
73
+ expect(component.validity?.customError).to.be.true;
74
+ expect(component.checkValidity()).to.be.false;
75
+ await elementUpdated(component);
76
+ // Like native, the validity message shouldn't display until `reportValidity()` is called.
77
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
78
+ ?.textContent).to.be.undefined;
79
+ expect(component.reportValidity()).to.be.false;
80
+ await elementUpdated(component);
81
+ expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('true');
82
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
83
+ ?.textContent).to.equal('validity message');
84
+ });
85
+ it('removes a validity message with an empty argument to `setCustomValidity()`', async () => {
86
+ const component = await fixture(html `<glide-core-checkbox label="Label"></glide-core-checkbox>`);
87
+ component.setCustomValidity('validity message');
88
+ component.reportValidity();
89
+ await elementUpdated(component);
90
+ component.setCustomValidity('');
91
+ await elementUpdated(component);
92
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
93
+ ?.textContent).to.be.undefined;
94
+ });
95
+ it('is invalid when `setValidity()` is called', async () => {
96
+ const component = await fixture(html `<glide-core-checkbox label="Label"></glide-core-checkbox>`);
97
+ component.setValidity({ customError: true }, 'validity message');
98
+ expect(component.validity.valid).to.be.false;
99
+ await elementUpdated(component);
100
+ // Like native, the validity message shouldn't display until `reportValidity()` is called.
101
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
102
+ ?.textContent).to.be.undefined;
103
+ expect(component.validity?.customError).to.be.true;
104
+ component.reportValidity();
105
+ await elementUpdated(component);
106
+ expect(component.shadowRoot?.querySelector('input')?.getAttribute('aria-invalid')).to.equal('true');
107
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
108
+ ?.textContent).to.equal('validity message');
109
+ });
110
+ it('is valid when `setValidity()` is called', async () => {
111
+ const component = await fixture(html `<glide-core-checkbox label="Label"></glide-core-checkbox>`);
112
+ component.setValidity({ customError: true }, 'validity message');
113
+ component.setValidity({});
114
+ await elementUpdated(component);
115
+ expect(component.validity.valid).to.be.true;
116
+ expect(component.validity.customError).to.be.false;
117
+ expect(component.reportValidity()).to.be.true;
118
+ await elementUpdated(component);
119
+ expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
120
+ ?.textContent).to.be.undefined;
121
+ });
122
+ it('retains existing validity state when `setCustomValidity()` is called', async () => {
123
+ const component = await fixture(html `<glide-core-checkbox label="Label" required></glide-core-checkbox>`);
124
+ component.setCustomValidity('validity message');
125
+ expect(component.validity?.valid).to.be.false;
126
+ expect(component.validity?.customError).to.be.true;
127
+ expect(component.validity?.valueMissing).to.be.true;
128
+ });
@@ -65,6 +65,8 @@ export default class GlideCoreDropdown extends LitElement {
65
65
  formResetCallback(): void;
66
66
  render(): import("lit").TemplateResult<1>;
67
67
  reportValidity(): boolean;
68
+ setCustomValidity(message: string): void;
69
+ setValidity(flags?: ValidityStateFlags, message?: string): void;
68
70
  constructor();
69
71
  private ariaActivedescendant;
70
72
  private isBlurring;
@@ -75,4 +77,5 @@ export default class GlideCoreDropdown extends LitElement {
75
77
  private isReportValidityOrSubmit;
76
78
  private isShowSingleSelectIcon;
77
79
  private tagOverflowLimit;
80
+ private validityMessage?;
78
81
  }