@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.
- package/dist/checkbox-group.d.ts +3 -0
- package/dist/checkbox-group.js +43 -1
- package/dist/checkbox-group.styles.js +12 -0
- package/dist/checkbox-group.test.validity.js +69 -0
- package/dist/checkbox.d.ts +3 -1
- package/dist/checkbox.js +135 -1
- package/dist/checkbox.styles.js +12 -0
- package/dist/checkbox.test.events.js +9 -0
- package/dist/checkbox.test.validity.js +77 -1
- package/dist/dropdown.d.ts +3 -0
- package/dist/dropdown.js +195 -1
- package/dist/dropdown.option.d.ts +1 -0
- package/dist/dropdown.option.js +1 -1
- package/dist/dropdown.option.test.events.js +9 -1
- package/dist/dropdown.option.test.interactions.single.js +2 -2
- package/dist/dropdown.styles.js +18 -6
- package/dist/dropdown.test.basics.d.ts +1 -1
- package/dist/dropdown.test.basics.js +19 -1
- package/dist/dropdown.test.basics.multiple.js +2 -1
- package/dist/dropdown.test.events.filterable.js +13 -2
- package/dist/dropdown.test.events.single.js +19 -0
- package/dist/dropdown.test.form.multiple.js +3 -2
- package/dist/dropdown.test.interactions.filterable.js +20 -0
- package/dist/dropdown.test.interactions.js +20 -14
- package/dist/dropdown.test.interactions.multiple.js +24 -10
- package/dist/dropdown.test.interactions.single.js +32 -0
- package/dist/dropdown.test.validity.js +172 -1
- package/dist/input.d.ts +4 -0
- package/dist/input.js +155 -1
- package/dist/input.styles.js +8 -0
- package/dist/input.test.validity.js +140 -62
- package/dist/menu.d.ts +2 -0
- package/dist/menu.js +1 -1
- package/dist/menu.test.basics.d.ts +1 -1
- package/dist/menu.test.basics.js +12 -27
- package/dist/menu.test.interactions.js +90 -0
- package/dist/radio-group.d.ts +3 -0
- package/dist/radio-group.js +45 -1
- package/dist/radio-group.styles.js +12 -0
- package/dist/radio-group.test.validity.js +82 -0
- package/dist/styles/variables.css +1 -1
- package/dist/tab.group.d.ts +1 -0
- package/dist/tab.group.js +1 -1
- package/dist/tab.group.styles.js +6 -0
- package/dist/tag.d.ts +1 -2
- package/dist/tag.js +1 -1
- package/dist/tag.styles.js +12 -3
- package/dist/tag.test.basics.js +1 -0
- package/dist/tag.test.interactions.js +8 -6
- package/dist/textarea.d.ts +3 -0
- package/dist/textarea.js +58 -2
- package/dist/textarea.styles.js +8 -0
- package/dist/textarea.test.events.js +0 -114
- package/dist/textarea.test.validity.js +64 -72
- package/dist/tooltip.d.ts +2 -1
- package/dist/tooltip.js +1 -1
- package/dist/tooltip.styles.js +2 -1
- package/dist/tree.item.menu.d.ts +1 -0
- package/dist/tree.item.menu.js +1 -1
- package/dist/tree.item.menu.test.basics.js +22 -1
- package/dist/tree.item.styles.js +3 -6
- package/package.json +1 -1
package/dist/checkbox-group.d.ts
CHANGED
@@ -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
|
}
|
package/dist/checkbox-group.js
CHANGED
@@ -1 +1,43 @@
|
|
1
|
-
var __decorate=this&&this.__decorate||function(e,t,i,o){var
|
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
|
+
});
|
package/dist/checkbox.d.ts
CHANGED
@@ -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
|
-
|
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,
|
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;
|
package/dist/checkbox.styles.js
CHANGED
@@ -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
|
`];
|
@@ -29,6 +29,15 @@ it('dispatches an "input" event when clicked', async () => {
|
|
29
29
|
expect(event instanceof Event).to.be.true;
|
30
30
|
expect(event.bubbles).to.be.true;
|
31
31
|
});
|
32
|
+
it('dispatches a "private-value-change" event when its `value` is changed programmatically', async () => {
|
33
|
+
const component = await fixture(html `<glide-core-checkbox label="One" value="one"></glide-core-checkbox>`);
|
34
|
+
setTimeout(() => (component.value = 'two'));
|
35
|
+
const event = await oneEvent(component, 'private-value-change');
|
36
|
+
expect(event instanceof CustomEvent).to.be.true;
|
37
|
+
expect(event.detail.old).to.equal('one');
|
38
|
+
expect(event.detail.new).to.equal('two');
|
39
|
+
expect(component.value).to.equal('two');
|
40
|
+
});
|
32
41
|
it('dispatches an "invalid" event on submit when required and unchecked', async () => {
|
33
42
|
const form = document.createElement('form');
|
34
43
|
const component = await fixture(html `<glide-core-checkbox label="Label" required></glide-core-checkbox>`, { parentNode: form });
|
@@ -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
|
+
});
|
package/dist/dropdown.d.ts
CHANGED
@@ -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
|
}
|