@crowdstrike/glide-core 0.8.0 → 0.9.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 (152) hide show
  1. package/dist/button-group.button.d.ts +14 -15
  2. package/dist/button-group.button.js +1 -1
  3. package/dist/button-group.button.styles.js +75 -52
  4. package/dist/button-group.button.test.basics.d.ts +1 -1
  5. package/dist/button-group.button.test.basics.js +83 -147
  6. package/dist/button-group.button.test.events.js +8 -67
  7. package/dist/button-group.button.test.focus.js +13 -0
  8. package/dist/button-group.button.test.interactions.d.ts +1 -0
  9. package/dist/button-group.button.test.interactions.js +42 -0
  10. package/dist/button-group.d.ts +7 -10
  11. package/dist/button-group.js +1 -1
  12. package/dist/button-group.stories.d.ts +1 -5
  13. package/dist/button-group.styles.js +18 -6
  14. package/dist/button-group.test.basics.js +113 -234
  15. package/dist/button-group.test.events.js +210 -263
  16. package/dist/button-group.test.focus.d.ts +1 -0
  17. package/dist/button-group.test.focus.js +39 -0
  18. package/dist/button-group.test.interactions.d.ts +1 -0
  19. package/dist/button-group.test.interactions.js +91 -0
  20. package/dist/button.test.basics.js +1 -1
  21. package/dist/checkbox-group.js +1 -1
  22. package/dist/checkbox-group.styles.js +1 -1
  23. package/dist/checkbox-group.test.basics.js +1 -1
  24. package/dist/checkbox-group.test.events.js +4 -4
  25. package/dist/checkbox-group.test.focus.js +4 -3
  26. package/dist/checkbox.d.ts +7 -1
  27. package/dist/checkbox.js +1 -1
  28. package/dist/checkbox.styles.js +10 -0
  29. package/dist/checkbox.test.events.js +4 -4
  30. package/dist/checkbox.test.focus.js +2 -2
  31. package/dist/{checkbox.test.states.js → checkbox.test.interactions.js} +24 -1
  32. package/dist/drawer.js +1 -1
  33. package/dist/dropdown.d.ts +6 -4
  34. package/dist/dropdown.js +1 -1
  35. package/dist/dropdown.option.d.ts +6 -2
  36. package/dist/dropdown.option.js +1 -1
  37. package/dist/dropdown.option.styles.js +13 -0
  38. package/dist/dropdown.option.test.basics.js +6 -3
  39. package/dist/dropdown.option.test.events.js +1 -1
  40. package/dist/dropdown.option.test.focus.js +1 -1
  41. package/dist/dropdown.option.test.interactions.multiple.js +1 -54
  42. package/dist/dropdown.option.test.interactions.single.js +51 -9
  43. package/dist/dropdown.styles.js +20 -19
  44. package/dist/dropdown.test.basics.js +143 -2
  45. package/dist/dropdown.test.basics.multiple.js +5 -2
  46. package/dist/dropdown.test.events.filterable.js +74 -0
  47. package/dist/dropdown.test.events.js +49 -160
  48. package/dist/dropdown.test.events.multiple.js +265 -8
  49. package/dist/dropdown.test.events.single.js +199 -2
  50. package/dist/dropdown.test.focus.filterable.js +9 -5
  51. package/dist/dropdown.test.focus.js +1 -1
  52. package/dist/dropdown.test.focus.multiple.js +1 -1
  53. package/dist/dropdown.test.focus.single.js +1 -1
  54. package/dist/dropdown.test.interactions.filterable.js +68 -11
  55. package/dist/dropdown.test.interactions.js +94 -5
  56. package/dist/dropdown.test.interactions.multiple.js +202 -5
  57. package/dist/dropdown.test.interactions.single.js +68 -6
  58. package/dist/form-controls-layout.test.basics.js +1 -1
  59. package/dist/icon-button.test.basics.js +1 -1
  60. package/dist/icons/checked.d.ts +1 -1
  61. package/dist/icons/checked.js +1 -1
  62. package/dist/icons/magnifying-glass.js +1 -1
  63. package/dist/input.d.ts +0 -6
  64. package/dist/input.js +1 -1
  65. package/dist/input.styles.js +7 -2
  66. package/dist/input.test.basics.js +19 -5
  67. package/dist/input.test.events.js +4 -4
  68. package/dist/input.test.focus.js +4 -4
  69. package/dist/input.test.translations.d.ts +1 -0
  70. package/dist/input.test.translations.js +38 -0
  71. package/dist/input.test.validity.js +133 -4
  72. package/dist/label.d.ts +1 -1
  73. package/dist/label.js +1 -1
  74. package/dist/label.styles.js +22 -13
  75. package/dist/label.test.basics.js +26 -24
  76. package/dist/library/expect-argument-error.js +1 -1
  77. package/dist/library/localize.d.ts +3 -1
  78. package/dist/menu.d.ts +3 -5
  79. package/dist/menu.js +1 -1
  80. package/dist/menu.options.test.basics.js +2 -2
  81. package/dist/menu.styles.js +1 -15
  82. package/dist/menu.test.basics.d.ts +1 -2
  83. package/dist/menu.test.basics.js +22 -6
  84. package/dist/menu.test.focus.d.ts +1 -0
  85. package/dist/menu.test.focus.js +13 -6
  86. package/dist/menu.test.interactions.js +212 -56
  87. package/dist/modal.icon-button.test.basics.js +1 -1
  88. package/dist/modal.js +1 -1
  89. package/dist/modal.styles.js +18 -13
  90. package/dist/modal.tertiary-icon.d.ts +0 -1
  91. package/dist/modal.tertiary-icon.js +1 -1
  92. package/dist/modal.tertiary-icon.test.basics.js +1 -1
  93. package/dist/modal.test.basics.js +1 -1
  94. package/dist/modal.test.events.js +10 -10
  95. package/dist/radio-group.js +1 -1
  96. package/dist/radio-group.styles.js +1 -1
  97. package/dist/radio-group.test.focus.js +3 -3
  98. package/dist/radio.d.ts +1 -0
  99. package/dist/radio.js +1 -1
  100. package/dist/radio.styles.js +33 -0
  101. package/dist/split-container.test.basics.js +4 -0
  102. package/dist/split-link.test.interactions.js +1 -1
  103. package/dist/styles/variables.css +1 -1
  104. package/dist/tab.d.ts +1 -1
  105. package/dist/tab.group.js +1 -1
  106. package/dist/tab.group.test.basics.js +1 -1
  107. package/dist/tab.group.test.interactions.js +198 -2
  108. package/dist/tab.js +1 -1
  109. package/dist/tab.panel.d.ts +1 -0
  110. package/dist/tab.panel.js +1 -1
  111. package/dist/tab.panel.styles.js +11 -1
  112. package/dist/tag.test.basics.js +2 -2
  113. package/dist/textarea.d.ts +0 -1
  114. package/dist/textarea.js +2 -2
  115. package/dist/textarea.stories.d.ts +3 -4
  116. package/dist/textarea.styles.js +14 -3
  117. package/dist/textarea.test.basics.js +80 -44
  118. package/dist/textarea.test.events.js +56 -41
  119. package/dist/textarea.test.translations.d.ts +1 -0
  120. package/dist/textarea.test.translations.js +34 -0
  121. package/dist/textarea.test.validity.js +104 -20
  122. package/dist/toasts.js +1 -1
  123. package/dist/toasts.styles.js +8 -1
  124. package/dist/toasts.test.basics.js +20 -0
  125. package/dist/toggle.js +1 -1
  126. package/dist/toggle.test.focus.js +1 -1
  127. package/dist/toggle.test.interactions.d.ts +1 -0
  128. package/dist/tooltip.d.ts +7 -5
  129. package/dist/tooltip.js +1 -1
  130. package/dist/tooltip.styles.js +90 -25
  131. package/dist/tooltip.test.basics.js +38 -3
  132. package/dist/tooltip.test.interactions.js +136 -34
  133. package/dist/translations/en.js +1 -1
  134. package/dist/translations/fr.js +1 -1
  135. package/dist/translations/ja.js +1 -1
  136. package/dist/tree.d.ts +0 -1
  137. package/dist/tree.item.d.ts +1 -2
  138. package/dist/tree.item.js +1 -1
  139. package/dist/tree.item.menu.d.ts +0 -1
  140. package/dist/tree.item.menu.js +1 -1
  141. package/dist/tree.js +1 -1
  142. package/dist/tree.test.basics.js +1 -1
  143. package/package.json +2 -4
  144. package/dist/drawer.test.floating-components.d.ts +0 -1
  145. package/dist/drawer.test.floating-components.js +0 -52
  146. package/dist/library/set-containing-block.d.ts +0 -15
  147. package/dist/library/set-containing-block.js +0 -1
  148. package/dist/modal.test.floating-components.js +0 -63
  149. /package/dist/{checkbox.test.states.d.ts → button-group.button.test.focus.d.ts} +0 -0
  150. /package/dist/{modal.test.floating-components.d.ts → checkbox.test.interactions.d.ts} +0 -0
  151. /package/dist/{toggle.test.states.d.ts → dropdown.test.events.filterable.d.ts} +0 -0
  152. /package/dist/{toggle.test.states.js → toggle.test.interactions.js} +0 -0
@@ -247,12 +247,208 @@ it('scrolls using keyboard when there is overflow and only a few pixels of overf
247
247
  await sendKeys({ press: 'ArrowRight' });
248
248
  await waitUntil(() => tabGroup?.shadowRoot?.querySelector('[data-test="overflow-end-button"]') === null);
249
249
  expect(tabGroup?.tabElements[3]).to.have.focus;
250
- expect(spy.called).to.be.true;
250
+ expect(spy.callCount).to.equal(1);
251
251
  spy.resetHistory();
252
252
  await sendKeys({ press: 'ArrowLeft' });
253
253
  await sendKeys({ press: 'ArrowLeft' });
254
254
  await sendKeys({ press: 'ArrowLeft' });
255
255
  await waitUntil(() => tabGroup?.shadowRoot?.querySelector('[data-test="overflow-start-button"]') === null);
256
256
  expect(tabGroup?.tabElements[0]).to.have.focus;
257
- expect(spy.called).to.be.true;
257
+ expect(spy.callCount).to.equal(1);
258
+ });
259
+ it('has only one active tab that is tabbable after pressing the Enter key', async () => {
260
+ const tabGroup = await fixture(html `
261
+ <glide-core-tab-group>
262
+ <glide-core-tab slot="nav" panel="1">Tab 1</glide-core-tab>
263
+ <glide-core-tab slot="nav" panel="2">Tab 2</glide-core-tab>
264
+ <glide-core-tab slot="nav" panel="3">Tab 3</glide-core-tab>
265
+ <glide-core-tab slot="nav" panel="4">Tab 4</glide-core-tab>
266
+
267
+ <glide-core-tab-panel name="1">Content for Tab 1</glide-core-tab-panel>
268
+ <glide-core-tab-panel name="2">Content for Tab 2</glide-core-tab-panel>
269
+ <glide-core-tab-panel name="3">Content for Tab 3</glide-core-tab-panel>
270
+ <glide-core-tab-panel name="4">Content for Tab 4</glide-core-tab-panel>
271
+ </glide-core-tab-group>
272
+ `);
273
+ const [firstTab, secondTab, thirdTab, fourthTab] = tabGroup.tabElements;
274
+ expect(firstTab.active).to.be.true;
275
+ expect(secondTab.active).to.be.false;
276
+ expect(thirdTab.active).to.be.false;
277
+ expect(fourthTab.active).to.be.false;
278
+ expect(firstTab.tabIndex).to.equal(0);
279
+ expect(secondTab.tabIndex).to.equal(-1);
280
+ expect(thirdTab.tabIndex).to.equal(-1);
281
+ expect(fourthTab.tabIndex).to.equal(-1);
282
+ firstTab.focus();
283
+ await sendKeys({ press: 'ArrowRight' });
284
+ await sendKeys({ press: 'Enter' });
285
+ expect(firstTab.active).to.be.false;
286
+ expect(secondTab.active).to.be.true;
287
+ expect(thirdTab.active).to.be.false;
288
+ expect(fourthTab.active).to.be.false;
289
+ expect(firstTab.tabIndex).to.equal(-1);
290
+ expect(secondTab.tabIndex).to.equal(0);
291
+ expect(thirdTab.tabIndex).to.equal(-1);
292
+ expect(fourthTab.tabIndex).to.equal(-1);
293
+ await sendKeys({ press: 'End' });
294
+ await sendKeys({ press: 'Enter' });
295
+ expect(firstTab.active).to.be.false;
296
+ expect(secondTab.active).to.be.false;
297
+ expect(thirdTab.active).to.be.false;
298
+ expect(fourthTab.active).to.be.true;
299
+ expect(firstTab.tabIndex).to.equal(-1);
300
+ expect(secondTab.tabIndex).to.equal(-1);
301
+ expect(thirdTab.tabIndex).to.equal(-1);
302
+ expect(fourthTab.tabIndex).to.equal(0);
303
+ await sendKeys({ press: 'ArrowLeft' });
304
+ await sendKeys({ press: 'Enter' });
305
+ expect(firstTab.active).to.be.false;
306
+ expect(secondTab.active).to.be.false;
307
+ expect(thirdTab.active).to.be.true;
308
+ expect(fourthTab.active).to.be.false;
309
+ expect(firstTab.tabIndex).to.equal(-1);
310
+ expect(secondTab.tabIndex).to.equal(-1);
311
+ expect(thirdTab.tabIndex).to.equal(0);
312
+ expect(fourthTab.tabIndex).to.equal(-1);
313
+ await sendKeys({ press: 'Home' });
314
+ await sendKeys({ press: 'Enter' });
315
+ expect(firstTab.active).to.be.true;
316
+ expect(secondTab.active).to.be.false;
317
+ expect(thirdTab.active).to.be.false;
318
+ expect(fourthTab.active).to.be.false;
319
+ expect(firstTab.tabIndex).to.equal(0);
320
+ expect(secondTab.tabIndex).to.equal(-1);
321
+ expect(thirdTab.tabIndex).to.equal(-1);
322
+ expect(fourthTab.tabIndex).to.equal(-1);
323
+ });
324
+ it('has only one active tab that is tabbable when clicked', async () => {
325
+ const tabGroup = await fixture(html `
326
+ <glide-core-tab-group>
327
+ <glide-core-tab slot="nav" panel="1">Tab 1</glide-core-tab>
328
+ <glide-core-tab slot="nav" panel="2">Tab 2</glide-core-tab>
329
+ <glide-core-tab slot="nav" panel="3">Tab 3</glide-core-tab>
330
+
331
+ <glide-core-tab-panel name="1">Content for Tab 1</glide-core-tab-panel>
332
+ <glide-core-tab-panel name="2">Content for Tab 2</glide-core-tab-panel>
333
+ <glide-core-tab-panel name="3">Content for Tab 3</glide-core-tab-panel>
334
+ </glide-core-tab-group>
335
+ `);
336
+ const [firstTab, secondTab, thirdTab] = tabGroup.tabElements;
337
+ expect(firstTab.active).to.be.true;
338
+ expect(secondTab.active).to.be.false;
339
+ expect(thirdTab.active).to.be.false;
340
+ expect(firstTab.tabIndex).to.equal(0);
341
+ expect(secondTab.tabIndex).to.equal(-1);
342
+ expect(thirdTab.tabIndex).to.equal(-1);
343
+ secondTab.click();
344
+ expect(firstTab.active).to.be.false;
345
+ expect(secondTab.active).to.be.true;
346
+ expect(thirdTab.active).to.be.false;
347
+ expect(firstTab.tabIndex).to.equal(-1);
348
+ expect(secondTab.tabIndex).to.equal(0);
349
+ expect(thirdTab.tabIndex).to.equal(-1);
350
+ thirdTab.click();
351
+ expect(firstTab.active).to.be.false;
352
+ expect(secondTab.active).to.be.false;
353
+ expect(thirdTab.active).to.be.true;
354
+ expect(firstTab.tabIndex).to.equal(-1);
355
+ expect(secondTab.tabIndex).to.equal(-1);
356
+ expect(thirdTab.tabIndex).to.equal(0);
357
+ });
358
+ it('has only one tab panel that is active and tabbable when a tab is clicked', async () => {
359
+ const tabGroup = await fixture(html `
360
+ <glide-core-tab-group>
361
+ <glide-core-tab slot="nav" panel="1">Tab 1</glide-core-tab>
362
+ <glide-core-tab slot="nav" panel="2">Tab 2</glide-core-tab>
363
+
364
+ <glide-core-tab-panel name="1">Content for Tab 1</glide-core-tab-panel>
365
+ <glide-core-tab-panel name="2">Content for Tab 2</glide-core-tab-panel>
366
+ </glide-core-tab-group>
367
+ `);
368
+ const [, secondTab] = tabGroup.tabElements;
369
+ const [firstPanel, secondPanel] = tabGroup.panelElements;
370
+ expect(firstPanel.isActive).to.be.true;
371
+ expect(secondPanel.isActive).to.be.false;
372
+ expect(firstPanel.tabIndex).to.equal(0);
373
+ expect(secondPanel.tabIndex).to.equal(-1);
374
+ secondTab.click();
375
+ expect(firstPanel.isActive).to.be.false;
376
+ expect(secondPanel.isActive).to.be.true;
377
+ expect(firstPanel.tabIndex).to.equal(-1);
378
+ expect(secondPanel.tabIndex).to.equal(0);
379
+ });
380
+ it('has only one tab panel that is active and tabbable when using the keyboard to make selections', async () => {
381
+ const tabGroup = await fixture(html `
382
+ <glide-core-tab-group>
383
+ <glide-core-tab slot="nav" panel="1">Tab 1</glide-core-tab>
384
+ <glide-core-tab slot="nav" panel="2">Tab 2</glide-core-tab>
385
+
386
+ <glide-core-tab-panel name="1">Content for Tab 1</glide-core-tab-panel>
387
+ <glide-core-tab-panel name="2">Content for Tab 2</glide-core-tab-panel>
388
+ </glide-core-tab-group>
389
+ `);
390
+ const [firstTab] = tabGroup.tabElements;
391
+ const [firstPanel, secondPanel] = tabGroup.panelElements;
392
+ expect(firstPanel.isActive).to.be.true;
393
+ expect(secondPanel.isActive).to.be.false;
394
+ expect(firstPanel.tabIndex).to.equal(0);
395
+ expect(secondPanel.tabIndex).to.equal(-1);
396
+ firstTab.focus();
397
+ await sendKeys({ press: 'ArrowRight' });
398
+ await sendKeys({ press: 'Enter' });
399
+ expect(firstPanel.isActive).to.be.false;
400
+ expect(secondPanel.isActive).to.be.true;
401
+ expect(firstPanel.tabIndex).to.equal(-1);
402
+ expect(secondPanel.tabIndex).to.equal(0);
403
+ });
404
+ it('sets the last keyboard focused tab as tabbable ', async () => {
405
+ const tabGroup = await fixture(html `
406
+ <glide-core-tab-group>
407
+ <glide-core-tab slot="nav" panel="1">Tab 1</glide-core-tab>
408
+ <glide-core-tab slot="nav" panel="2">Tab 2</glide-core-tab>
409
+
410
+ <glide-core-tab-panel name="1">Content for Tab 1</glide-core-tab-panel>
411
+ <glide-core-tab-panel name="2">Content for Tab 2</glide-core-tab-panel>
412
+ </glide-core-tab-group>
413
+ `);
414
+ const [firstTab, secondTab] = tabGroup.tabElements;
415
+ expect(firstTab.active).to.be.true;
416
+ expect(secondTab.active).to.be.false;
417
+ expect(firstTab.tabIndex).to.equal(0);
418
+ expect(secondTab.tabIndex).to.equal(-1);
419
+ firstTab.focus();
420
+ await sendKeys({ press: 'ArrowRight' });
421
+ expect(firstTab.active).to.be.true;
422
+ expect(secondTab.active).to.be.false;
423
+ expect(firstTab.tabIndex).to.equal(-1);
424
+ expect(secondTab.tabIndex).to.equal(0);
425
+ });
426
+ it('sets the active tab as tabbable on tab blur', async () => {
427
+ // This behavior is to ensure that the last active tab is the first tabbable
428
+ // element in the component.
429
+ const tabGroup = await fixture(html `
430
+ <glide-core-tab-group>
431
+ <glide-core-tab slot="nav" panel="1">Tab 1</glide-core-tab>
432
+ <glide-core-tab slot="nav" panel="2">Tab 2</glide-core-tab>
433
+
434
+ <glide-core-tab-panel name="1">Content for Tab 1</glide-core-tab-panel>
435
+ <glide-core-tab-panel name="2">Content for Tab 2</glide-core-tab-panel>
436
+ </glide-core-tab-group>
437
+ `);
438
+ const [firstTab, secondTab] = tabGroup.tabElements;
439
+ expect(firstTab.active).to.be.true;
440
+ expect(secondTab.active).to.be.false;
441
+ expect(firstTab.tabIndex).to.equal(0);
442
+ expect(secondTab.tabIndex).to.equal(-1);
443
+ firstTab.focus();
444
+ await sendKeys({ press: 'ArrowRight' });
445
+ expect(firstTab.active).to.be.true;
446
+ expect(secondTab.active).to.be.false;
447
+ expect(firstTab.tabIndex).to.equal(-1);
448
+ expect(secondTab.tabIndex).to.equal(0);
449
+ secondTab.blur();
450
+ expect(firstTab.active).to.be.true;
451
+ expect(secondTab.active).to.be.false;
452
+ expect(firstTab.tabIndex).to.equal(0);
453
+ expect(secondTab.tabIndex).to.equal(-1);
258
454
  });
package/dist/tab.js CHANGED
@@ -1 +1 @@
1
- var GlideCoreTab_1,__decorate=this&&this.__decorate||function(t,e,i,s){var o,a=arguments.length,r=a<3?e:null===s?s=Object.getOwnPropertyDescriptor(e,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(t,e,i,s);else for(var l=t.length-1;l>=0;l--)(o=t[l])&&(r=(a<3?o(r):a>3?o(e,i,r):o(e,i))||r);return a>3&&r&&Object.defineProperty(e,i,r),r};import{LitElement,html}from"lit";import{classMap}from"lit/directives/class-map.js";import{customElement,property}from"lit/decorators.js";import styles from"./tab.styles.js";let GlideCoreTab=class GlideCoreTab extends LitElement{constructor(){super(...arguments),this.panel="",this.active=!1,this.disabled=!1}static{GlideCoreTab_1=this}static{this.instanceCount=0}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}firstUpdated(){this.setAttribute("role","tab"),this.hasAttribute("id")||(this.id="glide-core-tab-"+GlideCoreTab_1.instanceCount++)}render(){return html`<div class="${classMap({component:!0,active:this.active,disabled:this.disabled})}"><span class="container"><slot name="icon"></slot><div class="default-slot"><slot></slot></div></span></div>`}updated(t){t.has("active")&&this.setAttribute("aria-selected",this.active?"true":"false"),t.has("disabled")&&(this.disabled?(this.setAttribute("aria-disabled","true"),this.setAttribute("tabindex","-1")):(this.removeAttribute("aria-disabled"),this.setAttribute("tabindex","0")))}};__decorate([property({reflect:!0})],GlideCoreTab.prototype,"panel",void 0),__decorate([property({type:Boolean,reflect:!0})],GlideCoreTab.prototype,"active",void 0),__decorate([property({type:Boolean,reflect:!0})],GlideCoreTab.prototype,"disabled",void 0),GlideCoreTab=GlideCoreTab_1=__decorate([customElement("glide-core-tab")],GlideCoreTab);export default GlideCoreTab;
1
+ var __decorate=this&&this.__decorate||function(t,e,i,o){var s,r=arguments.length,a=r<3?e:null===o?o=Object.getOwnPropertyDescriptor(e,i):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(t,e,i,o);else for(var l=t.length-1;l>=0;l--)(s=t[l])&&(a=(r<3?s(a):r>3?s(e,i,a):s(e,i))||a);return r>3&&a&&Object.defineProperty(e,i,a),a};import{LitElement,html}from"lit";import{classMap}from"lit/directives/class-map.js";import{customElement,property}from"lit/decorators.js";import{nanoid}from"nanoid";import styles from"./tab.styles.js";let GlideCoreTab=class GlideCoreTab extends LitElement{constructor(){super(...arguments),this.panel="",this.active=!1,this.disabled=!1,this.#t=nanoid()}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}firstUpdated(){this.setAttribute("role","tab"),this.id=this.#t}render(){return html`<div class="${classMap({component:!0,active:this.active,disabled:this.disabled})}"><span class="container"><slot name="icon"></slot><div class="default-slot"><slot></slot></div></span></div>`}updated(t){t.has("active")&&this.setAttribute("aria-selected",this.active?"true":"false"),t.has("disabled")&&(this.disabled?(this.setAttribute("aria-disabled","true"),this.setAttribute("tabindex","-1")):this.removeAttribute("aria-disabled"))}#t};__decorate([property({reflect:!0})],GlideCoreTab.prototype,"panel",void 0),__decorate([property({type:Boolean,reflect:!0})],GlideCoreTab.prototype,"active",void 0),__decorate([property({type:Boolean,reflect:!0})],GlideCoreTab.prototype,"disabled",void 0),GlideCoreTab=__decorate([customElement("glide-core-tab")],GlideCoreTab);export default GlideCoreTab;
@@ -11,6 +11,7 @@ declare global {
11
11
  *
12
12
  */
13
13
  export default class GlideCoreTabPanel extends LitElement {
14
+ #private;
14
15
  static instanceCount: number;
15
16
  static shadowRootOptions: ShadowRootInit;
16
17
  static styles: import("lit").CSSResult[];
package/dist/tab.panel.js CHANGED
@@ -1 +1 @@
1
- var GlideCoreTabPanel_1,__decorate=this&&this.__decorate||function(e,t,i,o){var a,s=arguments.length,r=s<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 l=e.length-1;l>=0;l--)(a=e[l])&&(r=(s<3?a(r):s>3?a(t,i,r):a(t,i))||r);return s>3&&r&&Object.defineProperty(t,i,r),r};import{LitElement,html}from"lit";import{classMap}from"lit/directives/class-map.js";import{customElement,property}from"lit/decorators.js";import styles from"./tab.panel.styles.js";let GlideCoreTabPanel=class GlideCoreTabPanel extends LitElement{constructor(){super(...arguments),this.name="",this.isActive=!0}static{GlideCoreTabPanel_1=this}static{this.instanceCount=0}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}firstUpdated(){this.setAttribute("role","tabpanel"),this.hasAttribute("id")||(this.id="glide-core-tab-panel-"+GlideCoreTabPanel_1.instanceCount++)}render(){return html`<div class="${classMap({component:!0,hidden:!this.isActive})}"><slot></slot></div>`}updated(e){super.updated(e),e.has("isActive")&&this.setAttribute("aria-hidden",this.isActive?"false":"true")}};__decorate([property({reflect:!0})],GlideCoreTabPanel.prototype,"name",void 0),__decorate([property({type:Boolean})],GlideCoreTabPanel.prototype,"isActive",void 0),GlideCoreTabPanel=GlideCoreTabPanel_1=__decorate([customElement("glide-core-tab-panel")],GlideCoreTabPanel);export default GlideCoreTabPanel;
1
+ var __decorate=this&&this.__decorate||function(e,t,i,o){var s,r=arguments.length,a=r<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--)(s=e[l])&&(a=(r<3?s(a):r>3?s(t,i,a):s(t,i))||a);return r>3&&a&&Object.defineProperty(t,i,a),a};import{LitElement,html}from"lit";import{classMap}from"lit/directives/class-map.js";import{customElement,property}from"lit/decorators.js";import{nanoid}from"nanoid";import styles from"./tab.panel.styles.js";let GlideCoreTabPanel=class GlideCoreTabPanel extends LitElement{constructor(){super(...arguments),this.name="",this.isActive=!0,this.#e=nanoid()}static{this.instanceCount=0}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}firstUpdated(){this.setAttribute("role","tabpanel"),this.id=this.#e}render(){return html`<div class="${classMap({component:!0,hidden:!this.isActive})}"><slot></slot></div>`}updated(e){super.updated(e),e.has("isActive")&&this.setAttribute("aria-hidden",this.isActive?"false":"true")}#e};__decorate([property({reflect:!0})],GlideCoreTabPanel.prototype,"name",void 0),__decorate([property({type:Boolean})],GlideCoreTabPanel.prototype,"isActive",void 0),GlideCoreTabPanel=__decorate([customElement("glide-core-tab-panel")],GlideCoreTabPanel);export default GlideCoreTabPanel;
@@ -1,6 +1,16 @@
1
- import{css}from"lit";import visuallyHidden from"./styles/visually-hidden.js";export default[css`
1
+ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import visuallyHidden from"./styles/visually-hidden.js";export default[css`
2
+ :host(:focus-visible) {
3
+ outline: none;
4
+ }
5
+
6
+ /* stylelint-disable-next-line csstools/use-nesting */
7
+ :host(:focus-visible) .component {
8
+ ${focusOutline};
9
+ }
10
+
2
11
  .component {
3
12
  font-family: var(--glide-core-font-sans);
13
+ outline: none;
4
14
  }
5
15
 
6
16
  .hidden {
@@ -79,7 +79,7 @@ it('throws an error when the default slot is empty', async () => {
79
79
  spy();
80
80
  }
81
81
  }
82
- expect(spy.called).to.be.true;
82
+ expect(spy.callCount).to.equal(1);
83
83
  });
84
84
  it('does not throw an error when the default slot is non-empty', async () => {
85
85
  const spy = sinon.spy();
@@ -91,7 +91,7 @@ it('does not throw an error when the default slot is non-empty', async () => {
91
91
  spy();
92
92
  }
93
93
  }
94
- expect(spy.notCalled).to.be.true;
94
+ expect(spy.callCount).to.equal(0);
95
95
  });
96
96
  it('toggles the "activate" and "deactivate" clases when the button is clicked', async () => {
97
97
  const element = await fixture(html `<glide-core-tag removable-label="test-aria-label"
@@ -43,7 +43,6 @@ export default class GlideCoreTextarea extends LitElement {
43
43
  formResetCallback(): void;
44
44
  render(): import("lit").TemplateResult<1>;
45
45
  reportValidity(): boolean;
46
- updated(): void;
47
46
  constructor();
48
47
  private isBlurring;
49
48
  private isCheckingValidity;
package/dist/textarea.js CHANGED
@@ -1,2 +1,2 @@
1
- var __decorate=this&&this.__decorate||function(e,t,i,a){var r,o=arguments.length,l=o<3?t:null===a?a=Object.getOwnPropertyDescriptor(t,i):a;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)l=Reflect.decorate(e,t,i,a);else for(var s=e.length-1;s>=0;s--)(r=e[s])&&(l=(o<3?r(l):o>3?r(t,i,l):r(t,i))||l);return o>3&&l&&Object.defineProperty(t,i,l),l};import"./label.js";import{LitElement,html}from"lit";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property,state}from"lit/decorators.js";import{ifDefined}from"lit/directives/if-defined.js";import{when}from"lit/directives/when.js";import ow from"./library/ow.js";import styles from"./textarea.styles.js";let GlideCoreTextarea=class GlideCoreTextarea extends LitElement{static{this.formAssociated=!0}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed",delegatesFocus:!0}}static{this.styles=styles}blur(){this.#e.value?.blur()}checkValidity(){this.isCheckingValidity=!0;const e=this.#t.checkValidity();return this.isCheckingValidity=!1,e}disconnectedCallback(){super.disconnectedCallback(),this.form?.removeEventListener("formdata",this.#i),this.removeEventListener("invalid",this.#a)}get form(){return this.#t.form}get validity(){return this.#t.validity}get willValidate(){return this.#t.willValidate}formAssociatedCallback(){this.form?.addEventListener("formdata",this.#i)}formResetCallback(){this.value=this.getAttribute("value")??""}render(){return html`<glide-core-label split="${ifDefined(this.privateSplit??void 0)}" orientation="${this.orientation}" ?disabled="${this.disabled}" ?error="${this.#r||this.#o}" ?hide="${this.hideLabel}" ?required="${this.required}"><slot name="tooltip" slot="tooltip"></slot><label class="label" for="textarea">${this.label}</label><div class="textarea-container" slot="control"><textarea class="${classMap({error:this.#r||this.#o})}" id="textarea" name="${ifDefined(this.name)}" placeholder="${ifDefined(this.placeholder)}" rows="${this.rows}" autocapitalize="${ifDefined(this.autocapitalize)}" spellcheck="${this.spellcheck}" ?required="${this.required}" ?readonly="${this.readonly}" ?disabled="${this.disabled}" aria-describedby="meta" ${ref(this.#e)} @input="${this.#l}" @change="${this.#s}" @blur="${this.#d}">
2
- </textarea></div><div class="meta" data-test-description-container id="meta" slot="description"><slot name="description"></slot>${when(this.maxlength,(()=>html`<div class="${classMap({"character-count":!0,error:this.#o})}" data-test-maxlength>${this.value.length}/${this.maxlength}</div>`))}</div></glide-core-label>`}reportValidity(){this.isReportValidityOrSubmit=!0;const e=this.#t.reportValidity();return this.requestUpdate(),e}updated(){this.#e.value&&(this.#e.value.value=this.value),this.#n()}constructor(){super(),this.value="",this.label="",this.hideLabel=!1,this.orientation="horizontal",this.placeholder="",this.rows=2,this.required=!1,this.readonly=!1,this.disabled=!1,this.spellcheck=!1,this.autocapitalize="on",this.isBlurring=!1,this.isCheckingValidity=!1,this.isReportValidityOrSubmit=!1,this.#e=createRef(),this.#i=({formData:e})=>{this.value.length>0&&this.name&&this.value&&!this.disabled&&e.append(this.name,this.value)},this.#t=this.attachInternals(),this.addEventListener("invalid",this.#a)}#t;#e;#i;get#r(){return!this.disabled&&!this.readonly&&!this.validity.valid&&this.isReportValidityOrSubmit}get#o(){return!(!this.maxlength||this.disabled)&&this.value.length>this.maxlength}#d(){this.isBlurring=!0,this.reportValidity(),this.isBlurring=!1}#s(e){ow(this.#e.value,ow.object.instanceOf(HTMLTextAreaElement));const t=this.#e.value.value;this.value=t,this.#n(),this.dispatchEvent(new Event(e.type,e))}#l(){ow(this.#e.value,ow.object.instanceOf(HTMLTextAreaElement));const e=this.#e.value.value;this.value=e,this.#t.setFormValue(this.value),this.#n()}#a(e){if(e?.preventDefault(),this.isCheckingValidity||this.isBlurring)return;this.isReportValidityOrSubmit=!0;this.form?.querySelector(":invalid")===this&&this.focus()}async#n(){const e=this.#e.value;this.#o?this.#t.setValidity({...e?.validity,tooLong:!0}," ",e):this.#t.setValidity(e?.validity,e?.validationMessage,e),await this.updateComplete}};__decorate([property()],GlideCoreTextarea.prototype,"value",void 0),__decorate([property()],GlideCoreTextarea.prototype,"label",void 0),__decorate([property({attribute:"hide-label",type:Boolean})],GlideCoreTextarea.prototype,"hideLabel",void 0),__decorate([property({reflect:!0})],GlideCoreTextarea.prototype,"orientation",void 0),__decorate([property()],GlideCoreTextarea.prototype,"placeholder",void 0),__decorate([property({reflect:!0,type:Number})],GlideCoreTextarea.prototype,"rows",void 0),__decorate([property({type:Boolean})],GlideCoreTextarea.prototype,"required",void 0),__decorate([property({type:Boolean})],GlideCoreTextarea.prototype,"readonly",void 0),__decorate([property({type:Boolean})],GlideCoreTextarea.prototype,"disabled",void 0),__decorate([property({type:Number,converter:e=>e&&Number.parseInt(e,10)})],GlideCoreTextarea.prototype,"maxlength",void 0),__decorate([property({reflect:!0})],GlideCoreTextarea.prototype,"name",void 0),__decorate([property({type:Boolean})],GlideCoreTextarea.prototype,"spellcheck",void 0),__decorate([property()],GlideCoreTextarea.prototype,"autocapitalize",void 0),__decorate([property()],GlideCoreTextarea.prototype,"privateSplit",void 0),__decorate([state()],GlideCoreTextarea.prototype,"isBlurring",void 0),__decorate([state()],GlideCoreTextarea.prototype,"isCheckingValidity",void 0),__decorate([state()],GlideCoreTextarea.prototype,"isReportValidityOrSubmit",void 0),GlideCoreTextarea=__decorate([customElement("glide-core-textarea")],GlideCoreTextarea);export default GlideCoreTextarea;
1
+ var __decorate=this&&this.__decorate||function(e,t,i,a){var r,o=arguments.length,l=o<3?t:null===a?a=Object.getOwnPropertyDescriptor(t,i):a;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)l=Reflect.decorate(e,t,i,a);else for(var s=e.length-1;s>=0;s--)(r=e[s])&&(l=(o<3?r(l):o>3?r(t,i,l):r(t,i))||l);return o>3&&l&&Object.defineProperty(t,i,l),l};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 ow from"./library/ow.js";import styles from"./textarea.styles.js";let GlideCoreTextarea=class GlideCoreTextarea extends LitElement{static{this.formAssociated=!0}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed",delegatesFocus:!0}}static{this.styles=styles}blur(){this.#e.value?.blur()}checkValidity(){this.isCheckingValidity=!0;const e=this.#t.checkValidity();return this.isCheckingValidity=!1,e}disconnectedCallback(){super.disconnectedCallback(),this.form?.removeEventListener("formdata",this.#i)}get form(){return this.#t.form}get validity(){return!this.required||this.value||this.disabled?this.#a?this.#t.setValidity({tooLong:!0}," ",this.#e.value):this.#t.setValidity({}):this.#t.setValidity({valueMissing:!0}," ",this.#e.value),this.#t.validity}get willValidate(){return this.#t.willValidate}formAssociatedCallback(){this.form?.addEventListener("formdata",this.#i)}formResetCallback(){this.value=this.getAttribute("value")??""}render(){return html`<glide-core-private-label split="${ifDefined(this.privateSplit??void 0)}" orientation="${this.orientation}" ?disabled="${this.disabled}" ?error="${this.#r||this.#a}" ?hide="${this.hideLabel}" ?required="${this.required}"><slot name="tooltip" slot="tooltip"></slot><label class="label" for="textarea">${this.label}</label><div class="textarea-container" slot="control"><textarea aria-describedby="meta" aria-invalid="${this.#r||this.#a}" class="${classMap({error:this.#r||this.#a})}" id="textarea" name="${ifDefined(this.name)}" placeholder="${ifDefined(this.placeholder)}" rows="${this.rows}" autocapitalize="${ifDefined(this.autocapitalize)}" spellcheck="${this.spellcheck}" .value="${this.value}" ?required="${this.required}" ?readonly="${this.readonly}" ?disabled="${this.disabled}" ${ref(this.#e)} @input="${this.#o}" @change="${this.#l}" @blur="${this.#s}">
2
+ </textarea></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.#a})}" data-test="character-count-container"><span aria-hidden="true" data-test="character-count-text">${this.#d.term("displayedCharacterCount",this.#n,this.maxlength)} </span><span class="hidden" data-test="character-count-announcement">${this.#d.term("announcedCharacterCount",this.#n,this.maxlength)}</span></div>`:nothing}</div></glide-core-private-label>`}reportValidity(){this.isReportValidityOrSubmit=!0;const e=this.#t.reportValidity();return this.requestUpdate(),e}constructor(){super(),this.value="",this.label="",this.hideLabel=!1,this.orientation="horizontal",this.placeholder="",this.rows=2,this.required=!1,this.readonly=!1,this.disabled=!1,this.spellcheck=!1,this.autocapitalize="on",this.isBlurring=!1,this.isCheckingValidity=!1,this.isReportValidityOrSubmit=!1,this.#d=new LocalizeController(this),this.#e=createRef(),this.#i=({formData:e})=>{this.name&&this.value&&!this.disabled&&e.append(this.name,this.value)},this.#t=this.attachInternals(),this.addEventListener("invalid",(e=>{if(e?.preventDefault(),this.isCheckingValidity||this.isBlurring)return;this.isReportValidityOrSubmit=!0;this.form?.querySelector(":invalid")===this&&this.focus()}))}#t;#d;#e;#i;get#r(){return!this.disabled&&!this.readonly&&!this.validity.valid&&this.isReportValidityOrSubmit}get#n(){return this.value.length}get#a(){return Boolean(!this.disabled&&!this.readonly&&this.maxlength&&this.#n>this.maxlength)}#s(){this.isBlurring=!0,this.reportValidity(),this.isBlurring=!1}#l(e){ow(this.#e.value,ow.object.instanceOf(HTMLTextAreaElement)),this.value=this.#e.value.value,this.dispatchEvent(new Event(e.type,e))}#o(){ow(this.#e.value,ow.object.instanceOf(HTMLTextAreaElement)),this.value=this.#e.value.value}};__decorate([property()],GlideCoreTextarea.prototype,"value",void 0),__decorate([property()],GlideCoreTextarea.prototype,"label",void 0),__decorate([property({attribute:"hide-label",type:Boolean})],GlideCoreTextarea.prototype,"hideLabel",void 0),__decorate([property({reflect:!0})],GlideCoreTextarea.prototype,"orientation",void 0),__decorate([property()],GlideCoreTextarea.prototype,"placeholder",void 0),__decorate([property({reflect:!0,type:Number})],GlideCoreTextarea.prototype,"rows",void 0),__decorate([property({type:Boolean})],GlideCoreTextarea.prototype,"required",void 0),__decorate([property({type:Boolean})],GlideCoreTextarea.prototype,"readonly",void 0),__decorate([property({type:Boolean})],GlideCoreTextarea.prototype,"disabled",void 0),__decorate([property({type:Number,converter:e=>e&&Number.parseInt(e,10)})],GlideCoreTextarea.prototype,"maxlength",void 0),__decorate([property({reflect:!0})],GlideCoreTextarea.prototype,"name",void 0),__decorate([property({type:Boolean})],GlideCoreTextarea.prototype,"spellcheck",void 0),__decorate([property()],GlideCoreTextarea.prototype,"autocapitalize",void 0),__decorate([property()],GlideCoreTextarea.prototype,"privateSplit",void 0),__decorate([state()],GlideCoreTextarea.prototype,"isBlurring",void 0),__decorate([state()],GlideCoreTextarea.prototype,"isCheckingValidity",void 0),__decorate([state()],GlideCoreTextarea.prototype,"isReportValidityOrSubmit",void 0),GlideCoreTextarea=__decorate([customElement("glide-core-textarea")],GlideCoreTextarea);export default GlideCoreTextarea;
@@ -2,12 +2,11 @@ import type { Meta, StoryObj } from '@storybook/web-components';
2
2
  declare const meta: Meta;
3
3
  export default meta;
4
4
  export declare const Default: StoryObj;
5
- export declare const Required: StoryObj;
6
- export declare const Vertical: StoryObj;
5
+ export declare const WithError: StoryObj;
6
+ export declare const Description: StoryObj;
7
7
  export declare const Readonly: StoryObj;
8
8
  export declare const Disabled: StoryObj;
9
9
  export declare const Placeholder: StoryObj;
10
10
  export declare const MaxLength: StoryObj;
11
- export declare const Description: StoryObj;
12
- export declare const WithError: StoryObj;
11
+ export declare const MaxLengthAndDescription: StoryObj;
13
12
  export declare const Tooltip: StoryObj;
@@ -1,5 +1,5 @@
1
- import{css}from"lit";export default css`
2
- glide-core-label::part(tooltips-and-label) {
1
+ import{css,unsafeCSS}from"lit";import visuallyHidden from"./styles/visually-hidden.js";const fieldSizingContent=unsafeCSS("\n field-sizing: content;\n");export default css`
2
+ glide-core-private-label::part(tooltips-and-label) {
3
3
  align-items: flex-start;
4
4
  margin-block-start: var(--glide-core-spacing-sm);
5
5
  }
@@ -8,6 +8,10 @@ import{css}from"lit";export default css`
8
8
  display: flex;
9
9
  }
10
10
 
11
+ .description {
12
+ display: block;
13
+ }
14
+
11
15
  textarea {
12
16
  appearance: none;
13
17
  background-color: var(--glide-core-surface-base-lighter);
@@ -20,11 +24,14 @@ import{css}from"lit";export default css`
20
24
  font-family: var(--glide-core-body-xs-font-family);
21
25
  font-size: var(--glide-core-body-sm-font-size);
22
26
  font-weight: var(--glide-core-body-xs-font-weight);
23
- min-block-size: 1.1875rem;
27
+ max-block-size: 5lh;
28
+ min-block-size: 3lh;
24
29
  padding: var(--glide-core-spacing-xs) var(--glide-core-spacing-sm);
25
30
  resize: vertical;
26
31
  transition: border-color 200ms ease-in-out;
27
32
 
33
+ ${fieldSizingContent};
34
+
28
35
  &:hover {
29
36
  border-color: var(--glide-core-border-base);
30
37
  }
@@ -66,5 +73,9 @@ import{css}from"lit";export default css`
66
73
  &.error {
67
74
  font-weight: var(--glide-core-font-weight-bold);
68
75
  }
76
+
77
+ .hidden {
78
+ ${visuallyHidden};
79
+ }
69
80
  }
70
81
  `;
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-expressions */
2
- import { expect, fixture } from '@open-wc/testing';
2
+ import { expect, fixture, html } from '@open-wc/testing';
3
3
  import { sendKeys } from '@web/test-runner-commands';
4
4
  import GlideCoreTextarea from './textarea.js';
5
5
  GlideCoreTextarea.shadowRootOptions.mode = 'open';
@@ -7,13 +7,17 @@ it('registers', async () => {
7
7
  expect(window.customElements.get('glide-core-textarea')).to.equal(GlideCoreTextarea);
8
8
  });
9
9
  it('is accessible', async () => {
10
- const template = `<glide-core-textarea value="value" label="label"></glide-core-textarea>`;
11
- const element = await fixture(template);
10
+ const element = await fixture(html `<glide-core-textarea
11
+ value="value"
12
+ label="label"
13
+ ></glide-core-textarea>`);
12
14
  await expect(element).to.be.accessible();
13
15
  });
14
16
  it('renders a textarea with two rows and value when attribute `value` is set ', async () => {
15
- const template = `<glide-core-textarea value="value" label="label"></glide-core-textarea>`;
16
- const element = await fixture(template);
17
+ const element = await fixture(html `<glide-core-textarea
18
+ value="value"
19
+ label="label"
20
+ ></glide-core-textarea>`);
17
21
  const textarea = element.shadowRoot.querySelector('textarea');
18
22
  expect(element);
19
23
  expect(element).to.have.attribute('rows', '2');
@@ -21,119 +25,151 @@ it('renders a textarea with two rows and value when attribute `value` is set ',
21
25
  expect(textarea).to.have.attribute('rows', '2');
22
26
  });
23
27
  it('renders the `rows` attribute on the textarea when set', async () => {
24
- const template = `<glide-core-textarea value="value" label="label" rows="5"></glide-core-textarea>`;
25
- const element = await fixture(template);
28
+ const element = await fixture(html `<glide-core-textarea
29
+ value="value"
30
+ label="label"
31
+ rows="5"
32
+ ></glide-core-textarea>`);
26
33
  const textarea = element.shadowRoot.querySelector('textarea');
27
34
  expect(textarea).to.have.attribute('rows', '5');
28
35
  });
29
36
  it('renders a label when attribute `label` is set', async () => {
30
- const template = `<glide-core-textarea value="value" label="label"></glide-core-textarea>`;
31
- const element = await fixture(template);
37
+ const element = await fixture(html `<glide-core-textarea
38
+ value="value"
39
+ label="label"
40
+ ></glide-core-textarea>`);
32
41
  const label = element.shadowRoot.querySelector('label');
33
42
  expect(label).to.exist;
34
43
  expect(label?.textContent?.trim()).to.be.equal('label');
35
44
  });
36
45
  it('renders the textarea as readonly when attribute `readonly` is set', async () => {
37
- const template = `<glide-core-textarea value="value" label="label" readonly></glide-core-textarea>`;
38
- const element = await fixture(template);
46
+ const element = await fixture(html `<glide-core-textarea
47
+ value="value"
48
+ label="label"
49
+ readonly
50
+ ></glide-core-textarea>`);
39
51
  const textarea = element.shadowRoot.querySelector('textarea');
40
52
  expect(textarea).to.have.attribute('readonly');
41
53
  });
42
54
  it('renders the textarea as disabled when attribute `disabled` is set', async () => {
43
- const template = `<glide-core-textarea value="value" label="label" disabled></glide-core-textarea>`;
44
- const element = await fixture(template);
55
+ const element = await fixture(html `<glide-core-textarea
56
+ value="value"
57
+ label="label"
58
+ disabled
59
+ ></glide-core-textarea>`);
45
60
  const textarea = element.shadowRoot.querySelector('textarea');
46
61
  expect(textarea).to.have.attribute('disabled');
47
62
  });
48
63
  it('renders the textarea with a placeholder when attribute `placeholder` is set', async () => {
49
- const template = `<glide-core-textarea value="" label="label" placeholder="placeholder"></glide-core-textarea>`;
50
- const element = await fixture(template);
64
+ const element = await fixture(html `<glide-core-textarea
65
+ value=""
66
+ label="label"
67
+ placeholder="placeholder"
68
+ ></glide-core-textarea>`);
51
69
  const textarea = element.shadowRoot.querySelector('textarea');
52
70
  expect(textarea).to.have.attribute('placeholder', 'placeholder');
53
71
  });
54
72
  it('renders `required` on textarea when set', async () => {
55
- const template = `<glide-core-textarea value="value" label="label" required></glide-core-textarea>`;
56
- const element = await fixture(template);
73
+ const element = await fixture(html `<glide-core-textarea
74
+ value="value"
75
+ label="label"
76
+ required
77
+ ></glide-core-textarea>`);
57
78
  const textarea = element.shadowRoot.querySelector('textarea');
58
79
  expect(textarea).to.have.attribute('required');
59
80
  });
60
81
  it('renders a `name` attribute on the textarea when set', async () => {
61
- const template = `<glide-core-textarea value="value" label="label" name="test-name"></glide-core-textarea>`;
62
- const element = await fixture(template);
82
+ const element = await fixture(html `<glide-core-textarea
83
+ value="value"
84
+ label="label"
85
+ name="test-name"
86
+ ></glide-core-textarea>`);
63
87
  const textarea = element.shadowRoot.querySelector('textarea');
64
88
  expect(textarea).to.have.attribute('name', 'test-name');
65
89
  });
66
90
  it('supports a "tooltip" slot', async () => {
67
- const template = `
91
+ const element = await fixture(html `
68
92
  <glide-core-textarea value="value" label="label" required>
69
93
  <div slot="tooltip">Tooltip</div>
70
94
  </glide-core-textarea>
71
- `;
72
- const element = await fixture(template);
95
+ `);
73
96
  const assignedElements = element.shadowRoot
74
97
  ?.querySelector('slot[name="tooltip"]')
75
98
  ?.assignedElements();
76
99
  expect(assignedElements?.at(0)?.textContent).to.equal('Tooltip');
77
100
  });
78
101
  it('renders a slot with description', async () => {
79
- const template = `<glide-core-textarea value="value" label="label"><span slot="description" data-test-content>Description</slot></glide-core-textarea>`;
80
- const element = await fixture(template);
102
+ const element = await fixture(html `<glide-core-textarea value="value" label="label"
103
+ ><span slot="description" data-test-content
104
+ >Description</span
105
+ ></glide-core-textarea
106
+ >`);
81
107
  expect(element).to.exist;
82
108
  const contentRendered = element.querySelector('[data-test-content]');
83
109
  expect(contentRendered).to.exist;
84
110
  expect(contentRendered?.textContent).to.be.equal('Description');
85
111
  });
112
+ it('displays visually hidden character count text for screenreaders', async () => {
113
+ const element = await fixture(html `<glide-core-textarea
114
+ label="label"
115
+ maxlength="10"
116
+ ></glide-core-textarea>`);
117
+ const maxCharacterCountAnnouncement = element.shadowRoot?.querySelector('[data-test="character-count-announcement"]');
118
+ expect(maxCharacterCountAnnouncement?.textContent?.trim()).to.equal('Character count 0 of 10');
119
+ });
86
120
  it('renders a character count when attribute `maxlength` is set greater than zero', async () => {
87
- const template = `<glide-core-textarea value="value" label="label" maxlength="10"><span slot="description">Description</span></glide-core-textarea>`;
88
- const element = await fixture(template);
89
- const container = element.shadowRoot.querySelector('[data-test-maxlength]');
121
+ const element = await fixture(html `<glide-core-textarea value="value" label="label" maxlength="10"
122
+ ><span slot="description">Description</span></glide-core-textarea
123
+ >`);
124
+ const container = element.shadowRoot.querySelector('[data-test="character-count-text"]');
90
125
  expect(container?.textContent?.trim()).to.be.equal('5/10');
91
126
  });
92
127
  it('does not render a character count when attribute `maxlength` is set less than than zero', async () => {
93
- const template = `<glide-core-textarea value="value" label="label" maxlength="0"><span slot="description" data-test-content>Description</span></glide-core-textarea>`;
94
- const element = await fixture(template);
95
- const container = element.shadowRoot.querySelector('[data-test-description-container]');
96
- expect(container?.textContent?.trim()).to.be.equal('');
128
+ const element = await fixture(html `<glide-core-textarea value="value" label="label" maxlength="0"
129
+ ><span slot="description">Description</span></glide-core-textarea
130
+ >`);
131
+ const container = element.shadowRoot?.querySelector('[data-test="character-count-container"]');
132
+ expect(container).to.be.null;
97
133
  });
98
134
  it('focuses the textarea when the label is clicked', async () => {
99
- const template = `<glide-core-textarea value="value" label="label"></glide-core-textarea>`;
100
- const element = await fixture(template);
135
+ const element = await fixture(html `<glide-core-textarea
136
+ value="value"
137
+ label="label"
138
+ ></glide-core-textarea>`);
101
139
  const label = element.shadowRoot.querySelector('label');
102
140
  label?.click();
103
141
  expect(element).to.have.focus;
104
142
  await expect(element.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.be.equal('textarea');
105
143
  });
106
144
  it('has tabbable textarea', async () => {
107
- const template = `<glide-core-textarea value="value" label="label"></glide-core-textarea>`;
108
- const element = await fixture(template);
145
+ const element = await fixture(html `<glide-core-textarea
146
+ value="value"
147
+ label="label"
148
+ ></glide-core-textarea>`);
109
149
  await sendKeys({ press: 'Tab' });
110
150
  expect(element).to.have.focus;
111
151
  await expect(element.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.be.equal('textarea');
112
152
  });
113
153
  it('renders text when typed into text area', async () => {
114
- const template = `<glide-core-textarea value="" label="label"></glide-core-textarea>`;
115
- const element = await fixture(template);
154
+ const element = await fixture(html `<glide-core-textarea value="" label="label"></glide-core-textarea>`);
116
155
  const textarea = element.shadowRoot.querySelector('textarea');
117
156
  element.focus();
118
157
  await sendKeys({ type: 'test text' });
119
158
  expect(textarea?.value).to.equal('test text');
120
159
  });
121
160
  it('returns the content of the textarea when getting the `value` property', async () => {
122
- const template = `<glide-core-textarea value="" label="label"></glide-core-textarea>`;
123
- const element = await fixture(template);
161
+ const element = await fixture(html `<glide-core-textarea value="" label="label"></glide-core-textarea>`);
124
162
  element.focus();
125
163
  await sendKeys({ type: 'test text' });
126
164
  expect(element.value).to.equal('test text');
127
165
  });
128
- it('focuses the textarea when `focus` is called', async () => {
129
- const template = `<glide-core-textarea value="" label="label"></glide-core-textarea>`;
130
- const element = await fixture(template);
166
+ it('focuses the textarea when `focus()` is called', async () => {
167
+ const element = await fixture(html `<glide-core-textarea value="" label="label"></glide-core-textarea>`);
131
168
  element.focus();
132
169
  await expect(element.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.be.equal('textarea');
133
170
  });
134
171
  it('blurs the textarea when `blur` is called', async () => {
135
- const template = `<glide-core-textarea value="" label="label"></glide-core-textarea>`;
136
- const element = await fixture(template);
172
+ const element = await fixture(html `<glide-core-textarea value="" label="label"></glide-core-textarea>`);
137
173
  element.focus();
138
174
  await expect(element.shadowRoot?.activeElement?.tagName.toLocaleLowerCase()).to.be.equal('textarea');
139
175
  element.blur();