@crowdstrike/glide-core 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/checkbox-group.d.ts +3 -0
  2. package/dist/checkbox-group.js +43 -1
  3. package/dist/checkbox-group.styles.js +12 -0
  4. package/dist/checkbox-group.test.validity.js +69 -0
  5. package/dist/checkbox.d.ts +3 -1
  6. package/dist/checkbox.js +135 -1
  7. package/dist/checkbox.styles.js +12 -0
  8. package/dist/checkbox.test.events.js +9 -0
  9. package/dist/checkbox.test.validity.js +77 -1
  10. package/dist/dropdown.d.ts +3 -0
  11. package/dist/dropdown.js +195 -1
  12. package/dist/dropdown.option.d.ts +1 -0
  13. package/dist/dropdown.option.js +1 -1
  14. package/dist/dropdown.option.test.events.js +9 -1
  15. package/dist/dropdown.option.test.interactions.single.js +2 -2
  16. package/dist/dropdown.styles.js +18 -6
  17. package/dist/dropdown.test.basics.d.ts +1 -1
  18. package/dist/dropdown.test.basics.js +19 -1
  19. package/dist/dropdown.test.basics.multiple.js +2 -1
  20. package/dist/dropdown.test.events.filterable.js +13 -2
  21. package/dist/dropdown.test.events.single.js +19 -0
  22. package/dist/dropdown.test.form.multiple.js +3 -2
  23. package/dist/dropdown.test.interactions.filterable.js +20 -0
  24. package/dist/dropdown.test.interactions.js +20 -14
  25. package/dist/dropdown.test.interactions.multiple.js +24 -10
  26. package/dist/dropdown.test.interactions.single.js +32 -0
  27. package/dist/dropdown.test.validity.js +172 -1
  28. package/dist/input.d.ts +4 -0
  29. package/dist/input.js +155 -1
  30. package/dist/input.styles.js +8 -0
  31. package/dist/input.test.validity.js +140 -62
  32. package/dist/menu.d.ts +2 -0
  33. package/dist/menu.js +1 -1
  34. package/dist/menu.test.basics.d.ts +1 -1
  35. package/dist/menu.test.basics.js +12 -27
  36. package/dist/menu.test.interactions.js +90 -0
  37. package/dist/radio-group.d.ts +3 -0
  38. package/dist/radio-group.js +45 -1
  39. package/dist/radio-group.styles.js +12 -0
  40. package/dist/radio-group.test.validity.js +82 -0
  41. package/dist/styles/variables.css +1 -1
  42. package/dist/tab.group.d.ts +1 -0
  43. package/dist/tab.group.js +1 -1
  44. package/dist/tab.group.styles.js +6 -0
  45. package/dist/tag.d.ts +1 -2
  46. package/dist/tag.js +1 -1
  47. package/dist/tag.styles.js +12 -3
  48. package/dist/tag.test.basics.js +1 -0
  49. package/dist/tag.test.interactions.js +8 -6
  50. package/dist/textarea.d.ts +3 -0
  51. package/dist/textarea.js +58 -2
  52. package/dist/textarea.styles.js +8 -0
  53. package/dist/textarea.test.events.js +0 -114
  54. package/dist/textarea.test.validity.js +64 -72
  55. package/dist/tooltip.d.ts +2 -1
  56. package/dist/tooltip.js +1 -1
  57. package/dist/tooltip.styles.js +2 -1
  58. package/dist/tree.item.menu.d.ts +1 -0
  59. package/dist/tree.item.menu.js +1 -1
  60. package/dist/tree.item.menu.test.basics.js +22 -1
  61. package/dist/tree.item.styles.js +3 -6
  62. package/package.json +1 -1
@@ -359,6 +359,22 @@ it('does not activate the next option on ArrowDown when a tag is focused', async
359
359
  await sendKeys({ press: 'ArrowDown' });
360
360
  expect(options[0]?.privateActive).to.be.true;
361
361
  });
362
+ it('updates its tag when the `label` of a selected option is changed programmatically', async () => {
363
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" multiple>
364
+ <glide-core-dropdown-option
365
+ label="One"
366
+ selected
367
+ ></glide-core-dropdown-option>
368
+
369
+ <glide-core-dropdown-option label="Two"></glide-core-dropdown-option>
370
+ </glide-core-dropdown>`);
371
+ const option = component.querySelector('glide-core-dropdown-option');
372
+ assert(option);
373
+ option.label = 'Three';
374
+ await elementUpdated(component);
375
+ const tag = component.shadowRoot?.querySelector('[data-test="tag"]');
376
+ expect(tag?.label).to.equal('Three');
377
+ });
362
378
  it('selects and deselects options when `value` is changed programmatically', async () => {
363
379
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" multiple>
364
380
  <glide-core-dropdown-option
@@ -444,14 +460,13 @@ it('updates `value` when a option is selected or deselected via Enter', async ()
444
460
  </glide-core-dropdown>`);
445
461
  // Wait for it to open.
446
462
  await aTimeout(0);
447
- const options = component.querySelectorAll('glide-core-dropdown-option');
448
- options[1].focus();
463
+ component.focus();
464
+ await sendKeys({ press: 'ArrowDown' });
449
465
  await sendKeys({ press: 'Enter' });
450
466
  expect(component.value).to.deep.equal(['one', 'two']);
451
- options[1].focus();
452
467
  await sendKeys({ press: 'Enter' });
453
468
  expect(component.value).to.deep.equal(['one']);
454
- options[2].focus();
469
+ await sendKeys({ press: 'ArrowDown' });
455
470
  await sendKeys({ press: 'Enter' });
456
471
  expect(component.value).to.deep.equal(['one']);
457
472
  });
@@ -472,14 +487,13 @@ it('updates `value` when an option is selected or deselected via Space', async (
472
487
  </glide-core-dropdown>`);
473
488
  // Wait for it to open.
474
489
  await aTimeout(0);
475
- const options = component.querySelectorAll('glide-core-dropdown-option');
476
- options[1].focus();
490
+ component.focus();
491
+ await sendKeys({ press: 'ArrowDown' });
477
492
  await sendKeys({ press: ' ' });
478
493
  expect(component.value).to.deep.equal(['one', 'two']);
479
- options[1].focus();
480
494
  await sendKeys({ press: ' ' });
481
495
  expect(component.value).to.deep.equal(['one']);
482
- options[2].focus();
496
+ await sendKeys({ press: 'ArrowDown' });
483
497
  await sendKeys({ press: ' ' });
484
498
  expect(component.value).to.deep.equal(['one']);
485
499
  });
@@ -1071,7 +1085,7 @@ it('does not set Select All as indeterminate when all options are selected', asy
1071
1085
  const selectAll = component.shadowRoot?.querySelector('[data-test="select-all"]');
1072
1086
  expect(selectAll?.privateIndeterminate).to.be.false;
1073
1087
  });
1074
- it('remains open when a tag is clicked', async () => {
1088
+ it('closes when a tag is clicked', async () => {
1075
1089
  const component = await fixture(html `<glide-core-dropdown open multiple>
1076
1090
  <glide-core-dropdown-option
1077
1091
  label="One"
@@ -1095,7 +1109,7 @@ it('remains open when a tag is clicked', async () => {
1095
1109
  position: [Math.ceil(x), Math.ceil(y)],
1096
1110
  });
1097
1111
  await elementUpdated(component);
1098
- expect(component.open).to.be.true;
1112
+ expect(component.open).to.be.false;
1099
1113
  });
1100
1114
  it('cannot be tabbed to when `disabled`', async () => {
1101
1115
  await fixture(html `<glide-core-dropdown
@@ -176,6 +176,8 @@ it('closes when an option is selected via click', async () => {
176
176
  value="value"
177
177
  ></glide-core-dropdown-option>
178
178
  </glide-core-dropdown>`);
179
+ // Wait for it to open.
180
+ await aTimeout(0);
179
181
  component.querySelector('glide-core-dropdown-option')?.click();
180
182
  expect(component.open).to.be.false;
181
183
  });
@@ -186,6 +188,8 @@ it('closes when an option is selected via Space', async () => {
186
188
  value="value"
187
189
  ></glide-core-dropdown-option>
188
190
  </glide-core-dropdown>`);
191
+ // Wait for it to open.
192
+ await aTimeout(0);
189
193
  component.focus();
190
194
  await sendKeys({ press: ' ' });
191
195
  expect(component.open).to.be.false;
@@ -197,6 +201,8 @@ it('closes when an option is selected via Enter', async () => {
197
201
  value="value"
198
202
  ></glide-core-dropdown-option>
199
203
  </glide-core-dropdown>`);
204
+ // Wait for it to open.
205
+ await aTimeout(0);
200
206
  component.focus();
201
207
  await sendKeys({ press: 'Enter' });
202
208
  expect(component.open).to.be.false;
@@ -228,6 +234,18 @@ it('closes when an option is selected via Space', async () => {
228
234
  await sendKeys({ press: ' ' });
229
235
  expect(component.open).to.be.false;
230
236
  });
237
+ it('closes when an already selected option is clicked', async () => {
238
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder" open>
239
+ <glide-core-dropdown-option
240
+ label="Label"
241
+ selected
242
+ ></glide-core-dropdown-option>
243
+ </glide-core-dropdown>`);
244
+ // Wait for it to open.
245
+ await aTimeout(0);
246
+ component.querySelector('glide-core-dropdown-option')?.click();
247
+ expect(component.open).to.be.false;
248
+ });
231
249
  it('deselects all other options when one is newly selected', async () => {
232
250
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
233
251
  <glide-core-dropdown-option
@@ -255,6 +273,20 @@ it('deselects all other options when one is newly selected', async () => {
255
273
  expect(options[1].selected).to.be.true;
256
274
  expect(options[2].selected).to.be.false;
257
275
  });
276
+ it('updates its internal label when the `label` of a selected option is changed programmatically', async () => {
277
+ const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
278
+ <glide-core-dropdown-option
279
+ label="One"
280
+ selected
281
+ ></glide-core-dropdown-option>
282
+ </glide-core-dropdown>`);
283
+ const option = component.querySelector('glide-core-dropdown-option');
284
+ assert(option);
285
+ option.label = 'Two';
286
+ await elementUpdated(component);
287
+ const label = component.shadowRoot?.querySelector('[data-test="internal-label"]');
288
+ expect(label?.textContent?.trim()).to.equal(option?.label);
289
+ });
258
290
  it('selects and deselects options when `value` is changed programmatically', async () => {
259
291
  const component = await fixture(html `<glide-core-dropdown label="Label" placeholder="Placeholder">
260
292
  <glide-core-dropdown-option
@@ -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,t,i,o){var r,s=arguments.length,a=s<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,i):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,i,o);else for(var l=e.length-1;l>=0;l--)(r=e[l])&&(a=(s<3?r(a):s>3?r(t,i,a):r(t,i))||a);return s>3&&a&&Object.defineProperty(t,i,a),a};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 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.#e.form}get validity(){return!this.required||this.value||this.disabled?this.#t?this.#e.setValidity({tooLong:!0}," ",this.#i.value):this.#e.setValidity({}):this.#e.setValidity({valueMissing:!0}," ",this.#i.value),this.#e.validity}get willValidate(){return this.#e.willValidate}blur(){this.#i.value?.blur()}checkValidity(){this.isCheckingValidity=!0;const e=this.#e.checkValidity();return this.isCheckingValidity=!1,e}disconnectedCallback(){super.disconnectedCallback(),this.form?.removeEventListener("formdata",this.#o)}formAssociatedCallback(){this.form?.addEventListener("formdata",this.#o)}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`<glide-core-private-label class="${classMap({left:"left"===this.privateSplit,middle:"middle"===this.privateSplit})}" orientation="${this.orientation}" split="${ifDefined(this.privateSplit??void 0)}" ?disabled="${this.disabled}" ?error="${this.#r||this.#t}" ?hide="${this.hideLabel}" ?required="${this.required}"><slot name="tooltip" slot="tooltip"></slot><label for="input">${this.label}</label><div class="${classMap({"input-container":!0,focused:this.hasFocus,empty:""===this.value,disabled:this.disabled,readonly:this.readonly&&!this.disabled,error:this.#r||this.#t})}" slot="control"><slot name="prefix-icon"></slot><input aria-describedby="meta" aria-invalid="${this.#r||this.#t}" id="input" type="${"password"===this.type&&this.passwordVisible?"text":this.type}" .value="${this.value}" placeholder="${ifDefined(this.placeholder)}" autocapitalize="${this.autocapitalize}" autocomplete="${this.autocomplete}" spellcheck="${this.spellcheck}" ?required="${this.required}" ?readonly="${this.readonly}" ?disabled="${this.disabled}" @focus="${this.#s}" @blur="${this.#a}" @change="${this.#l}" @input="${this.#n}" @keydown="${this.#d}" ${ref(this.#i)}> ${this.hasClearIcon?html`<glide-core-icon-button variant="tertiary" class="${classMap({"clear-icon-button":!0,"clear-icon-button--visible":this.isClearIconVisible})}" data-test="clear-button" label="${this.#h.term("clearEntry",this.label)}" @click="${this.#p}"><svg aria-hidden="true" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M6 6L18 18" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M18 6L6 18" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></glide-core-icon-button>`:nothing} ${"password"===this.type&&this.passwordToggle&&!this.disabled?html`<glide-core-icon-button variant="tertiary" class="password-toggle" data-test="password-toggle" label="${this.passwordVisible?"Hide password":"Show password"}" aria-controls="input" aria-expanded="${this.passwordVisible?"true":"false"}" @click="${this.#c}">${this.passwordVisible?html`<svg aria-hidden="true" width="16" height="16" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" 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"/></svg>`:html`<svg aria-hidden="true" width="16" height="16" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" 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"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/></svg>`}</glide-core-icon-button>`:nothing}<div class="suffix-icon">${"search"===this.type?magnifyingGlassIcon:html`<slot name="suffix-icon"></slot>`}</div></div><div class="meta" id="meta" slot="description"><slot class="description" name="description"></slot>${this.maxlength?html`<div class="${classMap({"character-count":!0,error:this.#t})}" data-test="character-count-container"><span aria-hidden="true" data-test="character-count-text">${this.#h.term("displayedCharacterCount",this.#u,this.maxlength)} </span><span class="hidden" data-test="character-count-announcement">${this.#h.term("announcedCharacterCount",this.#u,this.maxlength)}</span></div>`:nothing}</div></glide-core-private-label>`}reportValidity(){this.isReportValidityOrSubmit=!0;const e=this.#e.reportValidity();return this.requestUpdate(),e}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.#i=createRef(),this.#h=new LocalizeController(this),this.#o=({formData:e})=>{this.name&&this.value&&!this.disabled&&e.append(this.name,this.value)},this.#e=this.attachInternals(),this.addEventListener("invalid",(e=>{if(e?.preventDefault(),this.isCheckingValidity||this.isBlurring)return;this.isReportValidityOrSubmit=!0;this.form?.querySelector(":invalid")===this&&this.focus()}))}#i;#e;#h;get#u(){return this.value.length}#o;get#t(){return Boolean(!this.disabled&&!this.readonly&&this.maxlength&&this.#u>this.maxlength)}get#r(){return!this.disabled&&!this.validity?.valid&&this.isReportValidityOrSubmit}#a(){this.isBlurring=!0,this.reportValidity(),this.isBlurring=!1,this.hasFocus=!1}#l(e){ow(this.#i.value,ow.object.instanceOf(HTMLInputElement)),this.value=this.#i.value?.value,this.dispatchEvent(new Event(e.type,e))}#p(e){this.value="",this.dispatchEvent(new Event("clear",{bubbles:!0})),this.#i.value?.focus(),e.stopPropagation()}#s(){this.hasFocus=!0}#n(){ow(this.#i.value,ow.object.instanceOf(HTMLInputElement)),this.value=this.#i.value.value}#d(e){"Enter"===e.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,"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:e=>e&&Number.parseInt(e,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),GlideCoreInput=__decorate([customElement("glide-core-input")],GlideCoreInput);export default GlideCoreInput;
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;
@@ -11,6 +11,14 @@ import{css}from"lit";import visuallyHidden from"./styles/visually-hidden.js";exp
11
11
 
12
12
  .description {
13
13
  display: block;
14
+
15
+ &.hidden {
16
+ display: none;
17
+ }
18
+ }
19
+
20
+ .validity-message {
21
+ display: block;
14
22
  }
15
23
 
16
24
  .character-count {