@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.
- 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.validity.js +77 -1
- package/dist/dropdown.d.ts +3 -0
- package/dist/dropdown.js +195 -1
- package/dist/dropdown.styles.js +18 -6
- package/dist/dropdown.test.events.single.js +19 -0
- package/dist/dropdown.test.interactions.js +0 -18
- package/dist/dropdown.test.interactions.multiple.js +8 -10
- package/dist/dropdown.test.interactions.single.js +18 -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/tag.styles.js +1 -1
- 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/package.json +1 -1
@@ -1,6 +1,6 @@
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
2
2
|
import './dropdown.option.js';
|
3
|
-
import { expect, fixture, html } from '@open-wc/testing';
|
3
|
+
import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
|
4
4
|
import GlideCoreDropdown from './dropdown.js';
|
5
5
|
GlideCoreDropdown.shadowRootOptions.mode = 'open';
|
6
6
|
it('is valid if not required and no option is selected', async () => {
|
@@ -28,6 +28,23 @@ it('is invalid if required and no option is selected', async () => {
|
|
28
28
|
expect(component.checkValidity()).to.be.false;
|
29
29
|
expect(component.reportValidity()).to.be.false;
|
30
30
|
});
|
31
|
+
it('is invalid if required and no option is selected when `filterable`', async () => {
|
32
|
+
const component = await fixture(html `<glide-core-dropdown
|
33
|
+
label="Label"
|
34
|
+
placeholder="Placeholder"
|
35
|
+
filterable
|
36
|
+
required
|
37
|
+
>
|
38
|
+
<glide-core-dropdown-option
|
39
|
+
label="Label"
|
40
|
+
value="value"
|
41
|
+
></glide-core-dropdown-option>
|
42
|
+
</glide-core-dropdown>`);
|
43
|
+
expect(component.validity.valid).to.be.false;
|
44
|
+
expect(component.validity?.valueMissing).to.be.true;
|
45
|
+
expect(component.checkValidity()).to.be.false;
|
46
|
+
expect(component.reportValidity()).to.be.false;
|
47
|
+
});
|
31
48
|
it('is both invalid and valid if required but disabled and no option is selected', async () => {
|
32
49
|
const component = await fixture(html `<glide-core-dropdown
|
33
50
|
label="Label"
|
@@ -45,3 +62,157 @@ it('is both invalid and valid if required but disabled and no option is selected
|
|
45
62
|
expect(component.checkValidity()).to.be.true;
|
46
63
|
expect(component.reportValidity()).to.be.true;
|
47
64
|
});
|
65
|
+
it('sets the validity message with `setCustomValidity()`', async () => {
|
66
|
+
const component = await fixture(html `<glide-core-dropdown label="Label">
|
67
|
+
<glide-core-dropdown-option
|
68
|
+
label="Label"
|
69
|
+
value="value"
|
70
|
+
></glide-core-dropdown-option>
|
71
|
+
</glide-core-dropdown>`);
|
72
|
+
component.setCustomValidity('validity message');
|
73
|
+
expect(component.validity?.valid).to.be.false;
|
74
|
+
expect(component.validity?.customError).to.be.true;
|
75
|
+
expect(component.checkValidity()).to.be.false;
|
76
|
+
await elementUpdated(component);
|
77
|
+
// Like native, the validity message shouldn't display until `reportValidity()` is called.
|
78
|
+
expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
|
79
|
+
?.textContent).to.be.undefined;
|
80
|
+
expect(component.reportValidity()).to.be.false;
|
81
|
+
await elementUpdated(component);
|
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-dropdown label="Label">
|
87
|
+
<glide-core-dropdown-option
|
88
|
+
label="Label"
|
89
|
+
value="value"
|
90
|
+
></glide-core-dropdown-option>
|
91
|
+
</glide-core-dropdown>`);
|
92
|
+
component.setCustomValidity('validity message');
|
93
|
+
component.reportValidity();
|
94
|
+
await elementUpdated(component);
|
95
|
+
component.setCustomValidity('');
|
96
|
+
await elementUpdated(component);
|
97
|
+
expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
|
98
|
+
?.textContent).to.be.undefined;
|
99
|
+
});
|
100
|
+
it('is invalid when `setValidity()` is called', async () => {
|
101
|
+
const component = await fixture(html `<glide-core-dropdown label="Label">
|
102
|
+
<glide-core-dropdown-option
|
103
|
+
label="Label"
|
104
|
+
value="value"
|
105
|
+
></glide-core-dropdown-option>
|
106
|
+
</glide-core-dropdown>`);
|
107
|
+
component.setValidity({ customError: true }, 'validity message');
|
108
|
+
expect(component.validity.valid).to.be.false;
|
109
|
+
await elementUpdated(component);
|
110
|
+
// Like native, the validity message shouldn't display until `reportValidity()` is called.
|
111
|
+
expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
|
112
|
+
?.textContent).to.be.undefined;
|
113
|
+
expect(component.validity?.customError).to.be.true;
|
114
|
+
component.reportValidity();
|
115
|
+
await elementUpdated(component);
|
116
|
+
expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
|
117
|
+
?.textContent).to.equal('validity message');
|
118
|
+
});
|
119
|
+
it('is valid when `setValidity()` is called', async () => {
|
120
|
+
const component = await fixture(html `<glide-core-dropdown label="Label">
|
121
|
+
<glide-core-dropdown-option
|
122
|
+
label="Label"
|
123
|
+
value="value"
|
124
|
+
></glide-core-dropdown-option>
|
125
|
+
</glide-core-dropdown>`);
|
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('sets the validity message with `setCustomValidity()` when `filterable`', async () => {
|
137
|
+
const component = await fixture(html `<glide-core-dropdown label="Label" filterable>
|
138
|
+
<glide-core-dropdown-option
|
139
|
+
label="Label"
|
140
|
+
value="value"
|
141
|
+
></glide-core-dropdown-option>
|
142
|
+
</glide-core-dropdown>`);
|
143
|
+
component.setCustomValidity('validity message');
|
144
|
+
expect(component.validity?.valid).to.be.false;
|
145
|
+
expect(component.validity?.customError).to.be.true;
|
146
|
+
expect(component.checkValidity()).to.be.false;
|
147
|
+
await elementUpdated(component);
|
148
|
+
// Like native, the validity message shouldn't display until `reportValidity()` is called.
|
149
|
+
expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
|
150
|
+
?.textContent).to.be.undefined;
|
151
|
+
expect(component.reportValidity()).to.be.false;
|
152
|
+
await elementUpdated(component);
|
153
|
+
expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
|
154
|
+
?.textContent).to.equal('validity message');
|
155
|
+
});
|
156
|
+
it('removes a validity message with an empty argument to `setCustomValidity()` when `filterable`', async () => {
|
157
|
+
const component = await fixture(html `<glide-core-dropdown label="Label" filterable>
|
158
|
+
<glide-core-dropdown-option
|
159
|
+
label="Label"
|
160
|
+
value="value"
|
161
|
+
></glide-core-dropdown-option>
|
162
|
+
</glide-core-dropdown>`);
|
163
|
+
component.setCustomValidity('validity message');
|
164
|
+
component.reportValidity();
|
165
|
+
await elementUpdated(component);
|
166
|
+
component.setCustomValidity('');
|
167
|
+
await elementUpdated(component);
|
168
|
+
expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
|
169
|
+
?.textContent).to.be.undefined;
|
170
|
+
});
|
171
|
+
it('is invalid when `setValidity()` is called when `filterable`', async () => {
|
172
|
+
const component = await fixture(html `<glide-core-dropdown label="Label" filterable>
|
173
|
+
<glide-core-dropdown-option
|
174
|
+
label="Label"
|
175
|
+
value="value"
|
176
|
+
></glide-core-dropdown-option>
|
177
|
+
</glide-core-dropdown>`);
|
178
|
+
component.setValidity({ customError: true }, 'validity message');
|
179
|
+
expect(component.validity.valid).to.be.false;
|
180
|
+
await elementUpdated(component);
|
181
|
+
// Like native, the validity message shouldn't display until `reportValidity()` is called.
|
182
|
+
expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
|
183
|
+
?.textContent).to.be.undefined;
|
184
|
+
expect(component.validity?.customError).to.be.true;
|
185
|
+
component.reportValidity();
|
186
|
+
await elementUpdated(component);
|
187
|
+
expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
|
188
|
+
?.textContent).to.equal('validity message');
|
189
|
+
});
|
190
|
+
it('is valid when `setValidity()` is called when `filterable`', async () => {
|
191
|
+
const component = await fixture(html `<glide-core-dropdown label="Label" filterable>
|
192
|
+
<glide-core-dropdown-option
|
193
|
+
label="Label"
|
194
|
+
value="value"
|
195
|
+
></glide-core-dropdown-option>
|
196
|
+
</glide-core-dropdown>`);
|
197
|
+
component.setValidity({ customError: true }, 'validity message');
|
198
|
+
component.setValidity({});
|
199
|
+
await elementUpdated(component);
|
200
|
+
expect(component.validity.valid).to.be.true;
|
201
|
+
expect(component.validity.customError).to.be.false;
|
202
|
+
expect(component.reportValidity()).to.be.true;
|
203
|
+
await elementUpdated(component);
|
204
|
+
expect(component.shadowRoot?.querySelector('[data-test="validity-message"]')
|
205
|
+
?.textContent).to.be.undefined;
|
206
|
+
});
|
207
|
+
it('retains existing validity state when `setCustomValidity()` is called', async () => {
|
208
|
+
const component = await fixture(html `<glide-core-dropdown label="Label" required>
|
209
|
+
<glide-core-dropdown-option
|
210
|
+
label="Label"
|
211
|
+
value="value"
|
212
|
+
></glide-core-dropdown-option>
|
213
|
+
</glide-core-dropdown>`);
|
214
|
+
component.setCustomValidity('validity message');
|
215
|
+
expect(component.validity?.valid).to.be.false;
|
216
|
+
expect(component.validity?.customError).to.be.true;
|
217
|
+
expect(component.validity?.valueMissing).to.be.true;
|
218
|
+
});
|
package/dist/input.d.ts
CHANGED
@@ -29,6 +29,7 @@ export default class GlideCoreInput extends LitElement {
|
|
29
29
|
label?: string;
|
30
30
|
hideLabel: boolean;
|
31
31
|
orientation: 'horizontal' | 'vertical';
|
32
|
+
pattern?: string;
|
32
33
|
placeholder?: string;
|
33
34
|
clearable: boolean;
|
34
35
|
spellcheck: boolean;
|
@@ -53,11 +54,14 @@ export default class GlideCoreInput extends LitElement {
|
|
53
54
|
get isClearIconVisible(): boolean;
|
54
55
|
render(): import("lit").TemplateResult<1>;
|
55
56
|
reportValidity(): boolean;
|
57
|
+
setCustomValidity(message: string): void;
|
58
|
+
setValidity(flags?: ValidityStateFlags, message?: string): void;
|
56
59
|
constructor();
|
57
60
|
private hasFocus;
|
58
61
|
private isBlurring;
|
59
62
|
private isCheckingValidity;
|
60
63
|
private isReportValidityOrSubmit;
|
61
64
|
private passwordVisible;
|
65
|
+
private validityMessage?;
|
62
66
|
}
|
63
67
|
export {};
|
package/dist/input.js
CHANGED
@@ -1 +1,155 @@
|
|
1
|
-
var __decorate=this&&this.__decorate||function(e,
|
1
|
+
var __decorate=this&&this.__decorate||function(t,e,i,s){var a,r=arguments.length,o=r<3?e:null===s?s=Object.getOwnPropertyDescriptor(e,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(t,e,i,s);else for(var l=t.length-1;l>=0;l--)(a=t[l])&&(o=(r<3?a(o):r>3?a(e,i,o):a(e,i))||o);return r>3&&o&&Object.defineProperty(e,i,o),o};import"./icon-button.js";import"./label.js";import{LitElement,html,nothing}from"lit";import{LocalizeController}from"./library/localize.js";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property,state}from"lit/decorators.js";import{ifDefined}from"lit/directives/if-defined.js";import{unsafeHTML}from"lit/directives/unsafe-html.js";import{when}from"lit/directives/when.js";import magnifyingGlassIcon from"./icons/magnifying-glass.js";import ow from"./library/ow.js";import styles from"./input.styles.js";export const SUPPORTED_TYPES=["email","number","password","search","tel","text","url"];let GlideCoreInput=class GlideCoreInput extends LitElement{static{this.formAssociated=!0}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed",delegatesFocus:!0}}static{this.styles=styles}get form(){return this.#t.form}get validity(){return this.pattern?(this.#t.setValidity({customError:Boolean(this.validityMessage),patternMismatch:!new RegExp(this.pattern).test(this.value),valueMissing:Boolean(this.required&&!this.value)}," ",this.#e.value),this.#t.validity):!this.pattern&&this.#t.validity.patternMismatch?(this.#t.setValidity({}),this.#t.validity):!this.required||this.value||this.disabled?this.required&&this.#t.validity.valueMissing&&this.value?(this.#t.setValidity({}),this.#t.validity):(this.required||!this.#t.validity.valueMissing||this.value||this.#t.setValidity({}),this.#t.validity):(this.#t.setValidity({customError:Boolean(this.validityMessage),valueMissing:!0}," ",this.#e.value),this.#t.validity)}get willValidate(){return this.#t.willValidate}blur(){this.#e.value?.blur()}checkValidity(){this.isCheckingValidity=!0;const t=this.#t.checkValidity();return this.isCheckingValidity=!1,t}disconnectedCallback(){super.disconnectedCallback(),this.form?.removeEventListener("formdata",this.#i)}formAssociatedCallback(){this.form?.addEventListener("formdata",this.#i)}formResetCallback(){this.value=this.getAttribute("value")??""}get hasClearIcon(){return this.clearable&&!this.disabled&&!this.readonly}get isClearIconVisible(){return this.hasClearIcon&&this.value.length>0}render(){return html`
|
2
|
+
<glide-core-private-label
|
3
|
+
class=${classMap({left:"left"===this.privateSplit,middle:"middle"===this.privateSplit})}
|
4
|
+
orientation=${this.orientation}
|
5
|
+
split=${ifDefined(this.privateSplit??void 0)}
|
6
|
+
?disabled=${this.disabled}
|
7
|
+
?error=${this.#s||this.#a}
|
8
|
+
?hide=${this.hideLabel}
|
9
|
+
?required=${this.required}
|
10
|
+
>
|
11
|
+
<slot name="tooltip" slot="tooltip"></slot>
|
12
|
+
<label for="input"> ${this.label} </label>
|
13
|
+
|
14
|
+
<div
|
15
|
+
class=${classMap({"input-container":!0,focused:this.hasFocus,empty:""===this.value,disabled:this.disabled,readonly:this.readonly&&!this.disabled,error:this.#s||this.#a})}
|
16
|
+
slot="control"
|
17
|
+
>
|
18
|
+
<slot name="prefix-icon"></slot>
|
19
|
+
|
20
|
+
<input
|
21
|
+
aria-describedby="meta"
|
22
|
+
aria-invalid=${this.#s||this.#a}
|
23
|
+
id="input"
|
24
|
+
type=${"password"===this.type&&this.passwordVisible?"text":this.type}
|
25
|
+
.value=${this.value}
|
26
|
+
placeholder=${ifDefined(this.placeholder)}
|
27
|
+
autocapitalize=${this.autocapitalize}
|
28
|
+
autocomplete=${this.autocomplete}
|
29
|
+
spellcheck=${this.spellcheck}
|
30
|
+
?required=${this.required}
|
31
|
+
?readonly=${this.readonly}
|
32
|
+
?disabled=${this.disabled}
|
33
|
+
@focus=${this.#r}
|
34
|
+
@blur=${this.#o}
|
35
|
+
@change=${this.#l}
|
36
|
+
@input=${this.#n}
|
37
|
+
@keydown=${this.#d}
|
38
|
+
${ref(this.#e)}
|
39
|
+
/>
|
40
|
+
|
41
|
+
${this.hasClearIcon?html`
|
42
|
+
<glide-core-icon-button
|
43
|
+
variant="tertiary"
|
44
|
+
class=${classMap({"clear-icon-button":!0,"clear-icon-button--visible":this.isClearIconVisible})}
|
45
|
+
data-test="clear-button"
|
46
|
+
label=${this.#h.term("clearEntry",this.label)}
|
47
|
+
@click=${this.#p}
|
48
|
+
>
|
49
|
+
<!-- X icon -->
|
50
|
+
<svg
|
51
|
+
aria-hidden="true"
|
52
|
+
width="16"
|
53
|
+
height="16"
|
54
|
+
viewBox="0 0 24 24"
|
55
|
+
fill="none"
|
56
|
+
stroke="currentColor"
|
57
|
+
>
|
58
|
+
<path
|
59
|
+
d="M6 6L18 18"
|
60
|
+
stroke-width="2"
|
61
|
+
stroke-linecap="round"
|
62
|
+
stroke-linejoin="round"
|
63
|
+
/>
|
64
|
+
<path
|
65
|
+
d="M18 6L6 18"
|
66
|
+
stroke-width="2"
|
67
|
+
stroke-linecap="round"
|
68
|
+
stroke-linejoin="round"
|
69
|
+
/>
|
70
|
+
</svg>
|
71
|
+
</glide-core-icon-button>
|
72
|
+
`:nothing}
|
73
|
+
${"password"===this.type&&this.passwordToggle&&!this.disabled?html`
|
74
|
+
<glide-core-icon-button
|
75
|
+
variant="tertiary"
|
76
|
+
class="password-toggle"
|
77
|
+
data-test="password-toggle"
|
78
|
+
label=${this.passwordVisible?"Hide password":"Show password"}
|
79
|
+
aria-controls="input"
|
80
|
+
aria-expanded=${this.passwordVisible?"true":"false"}
|
81
|
+
@click=${this.#c}
|
82
|
+
>
|
83
|
+
${this.passwordVisible?html`<svg
|
84
|
+
aria-hidden="true"
|
85
|
+
width="16"
|
86
|
+
height="16"
|
87
|
+
fill="none"
|
88
|
+
viewBox="0 0 24 24"
|
89
|
+
stroke-width="1.5"
|
90
|
+
stroke="currentColor"
|
91
|
+
>
|
92
|
+
<path
|
93
|
+
stroke-linecap="round"
|
94
|
+
stroke-linejoin="round"
|
95
|
+
d="M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88"
|
96
|
+
/>
|
97
|
+
</svg> `:html`<svg
|
98
|
+
aria-hidden="true"
|
99
|
+
width="16"
|
100
|
+
height="16"
|
101
|
+
fill="none"
|
102
|
+
viewBox="0 0 24 24"
|
103
|
+
stroke-width="1.5"
|
104
|
+
stroke="currentColor"
|
105
|
+
>
|
106
|
+
<path
|
107
|
+
stroke-linecap="round"
|
108
|
+
stroke-linejoin="round"
|
109
|
+
d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z"
|
110
|
+
/>
|
111
|
+
<path
|
112
|
+
stroke-linecap="round"
|
113
|
+
stroke-linejoin="round"
|
114
|
+
d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
115
|
+
/>
|
116
|
+
</svg>`}
|
117
|
+
</glide-core-icon-button>
|
118
|
+
`:nothing}
|
119
|
+
|
120
|
+
<div class="suffix-icon">
|
121
|
+
${"search"===this.type?magnifyingGlassIcon:html`<slot name="suffix-icon"></slot>`}
|
122
|
+
</div>
|
123
|
+
</div>
|
124
|
+
|
125
|
+
<div class="meta" id="meta" slot="description">
|
126
|
+
<slot
|
127
|
+
class=${classMap({description:!0,hidden:Boolean(this.#s&&this.validityMessage)})}
|
128
|
+
name="description"
|
129
|
+
></slot>
|
130
|
+
|
131
|
+
${when(this.#s&&this.validityMessage,(()=>html`<span class="validity-message" data-test="validity-message"
|
132
|
+
>${unsafeHTML(this.validityMessage)}</span
|
133
|
+
>`))}
|
134
|
+
${this.maxlength?html`
|
135
|
+
<div
|
136
|
+
class=${classMap({"character-count":!0,error:this.#a})}
|
137
|
+
data-test="character-count-container"
|
138
|
+
>
|
139
|
+
<!--
|
140
|
+
"aria-hidden" is used here so that the character counter
|
141
|
+
is not read aloud to screenreaders twice as we have a
|
142
|
+
more accesible, verbose description below.
|
143
|
+
-->
|
144
|
+
<span aria-hidden="true" data-test="character-count-text">
|
145
|
+
${this.#h.term("displayedCharacterCount",this.#u,this.maxlength)}
|
146
|
+
</span>
|
147
|
+
|
148
|
+
<span class="hidden" data-test="character-count-announcement"
|
149
|
+
>${this.#h.term("announcedCharacterCount",this.#u,this.maxlength)}</span
|
150
|
+
>
|
151
|
+
</div>
|
152
|
+
`:nothing}
|
153
|
+
</div>
|
154
|
+
</glide-core-private-label>
|
155
|
+
`}reportValidity(){this.isReportValidityOrSubmit=!0;const t=this.#t.reportValidity();return this.requestUpdate(),t}setCustomValidity(t){this.validityMessage=t,""===t?this.#t.setValidity({customError:!1},"",this.#e.value):this.#t.setValidity({customError:!0,patternMismatch:this.#t.validity.patternMismatch,valueMissing:this.#t.validity.valueMissing}," ",this.#e.value)}setValidity(t,e){this.validityMessage=e,this.#t.setValidity(t," ",this.#e.value)}constructor(){super(),this.type="text",this.name="",this.value="",this.hideLabel=!1,this.orientation="horizontal",this.clearable=!1,this.spellcheck=!1,this.autocapitalize="on",this.autocomplete="on",this.passwordToggle=!1,this.required=!1,this.readonly=!1,this.disabled=!1,this.hasFocus=!1,this.isBlurring=!1,this.isCheckingValidity=!1,this.isReportValidityOrSubmit=!1,this.passwordVisible=!1,this.#e=createRef(),this.#h=new LocalizeController(this),this.#i=({formData:t})=>{this.name&&this.value&&!this.disabled&&t.append(this.name,this.value)},this.#t=this.attachInternals(),this.addEventListener("invalid",(t=>{if(t?.preventDefault(),this.isCheckingValidity||this.isBlurring)return;this.isReportValidityOrSubmit=!0;this.form?.querySelector(":invalid")===this&&this.focus()}))}#e;#t;#h;get#u(){return this.value.length}#i;get#a(){return Boolean(!this.disabled&&!this.readonly&&this.maxlength&&this.#u>this.maxlength)}get#s(){return!this.disabled&&!this.validity?.valid&&this.isReportValidityOrSubmit}#o(){this.isBlurring=!0,this.reportValidity(),this.isBlurring=!1,this.hasFocus=!1}#l(t){ow(this.#e.value,ow.object.instanceOf(HTMLInputElement)),this.value=this.#e.value?.value,this.dispatchEvent(new Event(t.type,t))}#p(t){this.value="",this.dispatchEvent(new Event("clear",{bubbles:!0})),this.#e.value?.focus(),t.stopPropagation()}#r(){this.hasFocus=!0}#n(){ow(this.#e.value,ow.object.instanceOf(HTMLInputElement)),this.value=this.#e.value.value}#d(t){"Enter"===t.key&&this.form?.requestSubmit()}#c(){this.passwordVisible=!this.passwordVisible}};__decorate([property()],GlideCoreInput.prototype,"type",void 0),__decorate([property({reflect:!0})],GlideCoreInput.prototype,"name",void 0),__decorate([property()],GlideCoreInput.prototype,"value",void 0),__decorate([property({reflect:!0})],GlideCoreInput.prototype,"label",void 0),__decorate([property({attribute:"hide-label",type:Boolean})],GlideCoreInput.prototype,"hideLabel",void 0),__decorate([property({reflect:!0})],GlideCoreInput.prototype,"orientation",void 0),__decorate([property({reflect:!0})],GlideCoreInput.prototype,"pattern",void 0),__decorate([property({reflect:!0})],GlideCoreInput.prototype,"placeholder",void 0),__decorate([property({type:Boolean})],GlideCoreInput.prototype,"clearable",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreInput.prototype,"spellcheck",void 0),__decorate([property({reflect:!0})],GlideCoreInput.prototype,"autocapitalize",void 0),__decorate([property({reflect:!0})],GlideCoreInput.prototype,"autocomplete",void 0),__decorate([property({attribute:"password-toggle",type:Boolean})],GlideCoreInput.prototype,"passwordToggle",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreInput.prototype,"required",void 0),__decorate([property({type:Boolean})],GlideCoreInput.prototype,"readonly",void 0),__decorate([property({reflect:!0,type:Boolean})],GlideCoreInput.prototype,"disabled",void 0),__decorate([property()],GlideCoreInput.prototype,"privateSplit",void 0),__decorate([property({type:Number,converter:t=>t&&Number.parseInt(t,10),reflect:!0})],GlideCoreInput.prototype,"maxlength",void 0),__decorate([state()],GlideCoreInput.prototype,"hasFocus",void 0),__decorate([state()],GlideCoreInput.prototype,"isBlurring",void 0),__decorate([state()],GlideCoreInput.prototype,"isCheckingValidity",void 0),__decorate([state()],GlideCoreInput.prototype,"isReportValidityOrSubmit",void 0),__decorate([state()],GlideCoreInput.prototype,"passwordVisible",void 0),__decorate([state()],GlideCoreInput.prototype,"validityMessage",void 0),GlideCoreInput=__decorate([customElement("glide-core-input")],GlideCoreInput);export default GlideCoreInput;
|
package/dist/input.styles.js
CHANGED
@@ -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
|
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;
|