@crowdstrike/glide-core 0.7.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 (203) hide show
  1. package/README.md +44 -5
  2. package/dist/accordion.test.basics.js +1 -0
  3. package/dist/accordion.test.events.js +1 -0
  4. package/dist/button-group.button.d.ts +14 -15
  5. package/dist/button-group.button.js +1 -1
  6. package/dist/button-group.button.styles.js +75 -52
  7. package/dist/button-group.button.test.basics.d.ts +1 -1
  8. package/dist/button-group.button.test.basics.js +84 -147
  9. package/dist/button-group.button.test.events.js +9 -67
  10. package/dist/button-group.button.test.focus.js +13 -0
  11. package/dist/button-group.button.test.interactions.d.ts +1 -0
  12. package/dist/button-group.button.test.interactions.js +42 -0
  13. package/dist/button-group.d.ts +7 -10
  14. package/dist/button-group.js +1 -1
  15. package/dist/button-group.stories.d.ts +1 -5
  16. package/dist/button-group.styles.js +18 -6
  17. package/dist/button-group.test.basics.js +114 -234
  18. package/dist/button-group.test.events.js +211 -263
  19. package/dist/button-group.test.focus.d.ts +1 -0
  20. package/dist/button-group.test.focus.js +39 -0
  21. package/dist/button-group.test.interactions.d.ts +1 -0
  22. package/dist/button-group.test.interactions.js +91 -0
  23. package/dist/button.test.basics.js +2 -1
  24. package/dist/button.test.events.js +1 -0
  25. package/dist/button.test.form.js +1 -0
  26. package/dist/checkbox-group.js +1 -1
  27. package/dist/checkbox-group.styles.js +1 -1
  28. package/dist/checkbox-group.test.basics.js +2 -1
  29. package/dist/checkbox-group.test.events.js +5 -4
  30. package/dist/checkbox-group.test.focus.js +5 -3
  31. package/dist/checkbox-group.test.form.js +1 -0
  32. package/dist/checkbox-group.test.validity.js +1 -0
  33. package/dist/checkbox.d.ts +7 -1
  34. package/dist/checkbox.js +1 -1
  35. package/dist/checkbox.styles.js +11 -3
  36. package/dist/checkbox.test.basics.js +1 -0
  37. package/dist/checkbox.test.events.js +5 -4
  38. package/dist/checkbox.test.focus.js +2 -2
  39. package/dist/checkbox.test.form.js +1 -0
  40. package/dist/{checkbox.test.states.js → checkbox.test.interactions.js} +25 -1
  41. package/dist/checkbox.test.validity.js +1 -0
  42. package/dist/drawer.js +1 -1
  43. package/dist/drawer.test.basics.js +1 -0
  44. package/dist/drawer.test.closing.js +1 -0
  45. package/dist/drawer.test.events.js +1 -0
  46. package/dist/drawer.test.methods.js +1 -0
  47. package/dist/dropdown.d.ts +6 -4
  48. package/dist/dropdown.js +1 -1
  49. package/dist/dropdown.option.d.ts +7 -2
  50. package/dist/dropdown.option.js +1 -1
  51. package/dist/dropdown.option.styles.js +13 -0
  52. package/dist/dropdown.option.test.basics.js +7 -3
  53. package/dist/dropdown.option.test.basics.multiple.js +1 -0
  54. package/dist/dropdown.option.test.basics.single.js +1 -0
  55. package/dist/dropdown.option.test.events.js +2 -1
  56. package/dist/dropdown.option.test.focus.js +1 -1
  57. package/dist/dropdown.option.test.interactions.multiple.js +2 -54
  58. package/dist/dropdown.option.test.interactions.single.js +52 -9
  59. package/dist/dropdown.styles.js +20 -19
  60. package/dist/dropdown.test.basics.filterable.js +1 -0
  61. package/dist/dropdown.test.basics.js +144 -2
  62. package/dist/dropdown.test.basics.multiple.js +6 -3
  63. package/dist/dropdown.test.basics.single.js +1 -1
  64. package/dist/dropdown.test.events.filterable.js +74 -0
  65. package/dist/dropdown.test.events.js +50 -160
  66. package/dist/dropdown.test.events.multiple.js +268 -10
  67. package/dist/dropdown.test.events.single.js +202 -4
  68. package/dist/dropdown.test.focus.filterable.js +9 -5
  69. package/dist/dropdown.test.focus.js +2 -1
  70. package/dist/dropdown.test.focus.multiple.js +1 -2
  71. package/dist/dropdown.test.focus.single.js +1 -1
  72. package/dist/dropdown.test.form.js +1 -0
  73. package/dist/dropdown.test.form.multiple.js +1 -0
  74. package/dist/dropdown.test.form.single.js +1 -0
  75. package/dist/dropdown.test.interactions.filterable.js +69 -11
  76. package/dist/dropdown.test.interactions.js +95 -5
  77. package/dist/dropdown.test.interactions.multiple.js +203 -6
  78. package/dist/dropdown.test.interactions.single.js +69 -6
  79. package/dist/dropdown.test.validity.js +1 -0
  80. package/dist/form-controls-layout.test.basics.js +2 -1
  81. package/dist/icon-button.test.basics.js +2 -1
  82. package/dist/icons/checked.d.ts +1 -1
  83. package/dist/icons/checked.js +1 -1
  84. package/dist/icons/magnifying-glass.js +1 -1
  85. package/dist/input.d.ts +0 -6
  86. package/dist/input.js +1 -1
  87. package/dist/input.styles.js +7 -2
  88. package/dist/input.test.basics.js +20 -5
  89. package/dist/input.test.events.js +5 -4
  90. package/dist/input.test.focus.js +5 -4
  91. package/dist/input.test.form.js +1 -0
  92. package/dist/input.test.translations.d.ts +1 -0
  93. package/dist/input.test.translations.js +38 -0
  94. package/dist/input.test.validity.js +134 -4
  95. package/dist/label.d.ts +1 -1
  96. package/dist/label.js +1 -1
  97. package/dist/label.styles.js +29 -20
  98. package/dist/label.test.basics.js +27 -24
  99. package/dist/library/expect-argument-error.js +1 -1
  100. package/dist/library/localize.d.ts +5 -1
  101. package/dist/library/ow.test.d.ts +2 -1
  102. package/dist/library/ow.test.js +8 -3
  103. package/dist/menu.button.test.basics.js +1 -0
  104. package/dist/menu.d.ts +3 -5
  105. package/dist/menu.js +1 -1
  106. package/dist/menu.link.test.basics.js +1 -0
  107. package/dist/menu.options.test.basics.js +3 -2
  108. package/dist/menu.styles.js +1 -15
  109. package/dist/menu.test.basics.d.ts +1 -2
  110. package/dist/menu.test.basics.js +23 -6
  111. package/dist/menu.test.events.d.ts +1 -0
  112. package/dist/menu.test.events.js +2 -1
  113. package/dist/menu.test.focus.d.ts +1 -0
  114. package/dist/menu.test.focus.js +14 -6
  115. package/dist/menu.test.interactions.js +213 -56
  116. package/dist/modal.icon-button.test.basics.js +2 -1
  117. package/dist/modal.js +1 -1
  118. package/dist/modal.styles.js +18 -13
  119. package/dist/modal.tertiary-icon.d.ts +0 -1
  120. package/dist/modal.tertiary-icon.js +1 -1
  121. package/dist/modal.tertiary-icon.test.basics.js +2 -1
  122. package/dist/modal.test.accessibility.js +1 -0
  123. package/dist/modal.test.basics.js +2 -1
  124. package/dist/modal.test.close.js +1 -0
  125. package/dist/modal.test.events.js +11 -10
  126. package/dist/modal.test.lock-scroll.js +1 -0
  127. package/dist/modal.test.methods.js +1 -0
  128. package/dist/modal.test.scrollbars.js +1 -0
  129. package/dist/radio-group.js +1 -1
  130. package/dist/radio-group.styles.js +1 -1
  131. package/dist/radio-group.test.basics.js +1 -0
  132. package/dist/radio-group.test.events.js +1 -0
  133. package/dist/radio-group.test.focus.js +4 -3
  134. package/dist/radio-group.test.form.js +1 -0
  135. package/dist/radio-group.test.validity.js +1 -0
  136. package/dist/radio.d.ts +1 -0
  137. package/dist/radio.js +1 -1
  138. package/dist/radio.styles.js +33 -0
  139. package/dist/split-button.test.basics.js +1 -0
  140. package/dist/split-container.test.basics.js +5 -0
  141. package/dist/split-link.test.basics.js +1 -0
  142. package/dist/split-link.test.interactions.js +2 -1
  143. package/dist/styles/variables.css +1 -1
  144. package/dist/tab.d.ts +1 -3
  145. package/dist/tab.group.d.ts +3 -5
  146. package/dist/tab.group.js +1 -1
  147. package/dist/tab.group.styles.js +27 -13
  148. package/dist/tab.group.test.basics.js +8 -57
  149. package/dist/tab.group.test.interactions.d.ts +3 -0
  150. package/dist/tab.group.test.interactions.js +454 -0
  151. package/dist/tab.js +1 -1
  152. package/dist/tab.panel.d.ts +1 -0
  153. package/dist/tab.panel.js +1 -1
  154. package/dist/tab.panel.styles.js +11 -1
  155. package/dist/tab.styles.js +7 -68
  156. package/dist/tab.test.basics.js +0 -20
  157. package/dist/tabs.stories.d.ts +1 -2
  158. package/dist/tag.test.basics.js +3 -2
  159. package/dist/textarea.d.ts +0 -1
  160. package/dist/textarea.js +2 -2
  161. package/dist/textarea.stories.d.ts +3 -4
  162. package/dist/textarea.styles.js +14 -3
  163. package/dist/textarea.test.basics.js +81 -44
  164. package/dist/textarea.test.events.js +57 -41
  165. package/dist/textarea.test.form.js +1 -0
  166. package/dist/textarea.test.translations.d.ts +1 -0
  167. package/dist/textarea.test.translations.js +34 -0
  168. package/dist/textarea.test.validity.js +105 -20
  169. package/dist/toasts.js +1 -1
  170. package/dist/toasts.styles.js +8 -1
  171. package/dist/toasts.test.basics.js +20 -0
  172. package/dist/toggle.js +1 -1
  173. package/dist/toggle.test.basics.js +1 -0
  174. package/dist/toggle.test.events.js +1 -0
  175. package/dist/toggle.test.focus.js +1 -1
  176. package/dist/toggle.test.interactions.d.ts +1 -0
  177. package/dist/{toggle.test.states.js → toggle.test.interactions.js} +1 -0
  178. package/dist/tooltip.d.ts +7 -5
  179. package/dist/tooltip.js +1 -1
  180. package/dist/tooltip.styles.js +90 -25
  181. package/dist/tooltip.test.basics.js +39 -3
  182. package/dist/tooltip.test.interactions.js +137 -34
  183. package/dist/translations/en.js +1 -1
  184. package/dist/translations/fr.js +1 -1
  185. package/dist/translations/ja.js +1 -1
  186. package/dist/tree.d.ts +0 -1
  187. package/dist/tree.item.d.ts +2 -3
  188. package/dist/tree.item.js +1 -1
  189. package/dist/tree.item.menu.d.ts +0 -1
  190. package/dist/tree.item.menu.js +1 -1
  191. package/dist/tree.item.test.basics.js +1 -0
  192. package/dist/tree.js +1 -1
  193. package/dist/tree.test.basics.js +2 -1
  194. package/dist/tree.test.events.js +1 -1
  195. package/package.json +40 -29
  196. package/dist/drawer.test.floating-components.d.ts +0 -1
  197. package/dist/drawer.test.floating-components.js +0 -51
  198. package/dist/library/set-containing-block.d.ts +0 -15
  199. package/dist/library/set-containing-block.js +0 -1
  200. package/dist/modal.test.floating-components.js +0 -62
  201. /package/dist/{checkbox.test.states.d.ts → button-group.button.test.focus.d.ts} +0 -0
  202. /package/dist/{modal.test.floating-components.d.ts → checkbox.test.interactions.d.ts} +0 -0
  203. /package/dist/{toggle.test.states.d.ts → dropdown.test.events.filterable.d.ts} +0 -0
@@ -1,73 +1,15 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
1
2
  import './button-group.button.js';
2
3
  import { expect, fixture, html, oneEvent } from '@open-wc/testing';
3
- import { sendKeys } from '@web/test-runner-commands';
4
4
  import GlideCoreButtonGroupButton from './button-group.button.js';
5
- import sinon from 'sinon';
6
- GlideCoreButtonGroupButton.shadowRootOptions.mode = 'open';
7
- it('emits a change event when clicked', async () => {
8
- const element = await fixture(html `<glide-core-button-group-button value="value"
9
- >Button</glide-core-button-group-button
10
- >`);
11
- const liElement = element.shadowRoot?.querySelector('li');
12
- // This pattern is adopted from https://open-wc.org/docs/testing/helpers/#testing-events
13
- // Without setTimeout the test fails. The suspicion is this is related to task scheduling,
14
- // however this can be investigated later.
5
+ it('emits a "private-selected" event when selected', async () => {
6
+ const component = await fixture(html `<glide-core-button-group-button
7
+ label="Button"
8
+ ></glide-core-button-group-button>`);
15
9
  setTimeout(() => {
16
- liElement?.click();
10
+ component.selected = true;
17
11
  });
18
- const changeEvent = await oneEvent(element, 'change');
19
- expect(changeEvent instanceof Event).to.be.true;
20
- });
21
- it('emits an input event when clicked', async () => {
22
- const element = await fixture(html `<glide-core-button-group-button value="value"
23
- >Button</glide-core-button-group-button
24
- >`);
25
- const liElement = element.shadowRoot.querySelector('li');
26
- setTimeout(() => {
27
- liElement?.click();
28
- });
29
- const inputEvent = await oneEvent(element, 'input');
30
- expect(inputEvent instanceof Event).to.be.true;
31
- });
32
- it('emits a change event when a space key is pressed and is not already selected', async () => {
33
- const buttonElement = await fixture(html `<glide-core-button-group-button value="value-1"
34
- >Button 1</glide-core-button-group-button
35
- >`);
36
- buttonElement.focus();
37
- setTimeout(async () => {
38
- await sendKeys({ press: ' ' });
39
- });
40
- const changeEvent = await oneEvent(buttonElement, 'change');
41
- expect(changeEvent instanceof Event).to.be.true;
42
- });
43
- it('does not emit change event when a space key is pressed and is selected', async () => {
44
- const buttonElement = await fixture(html `<glide-core-button-group-button value="value-1" selected
45
- >Button 1</glide-core-button-group-button
46
- > `);
47
- const spy = sinon.spy();
48
- buttonElement.addEventListener('change', spy);
49
- buttonElement.focus();
50
- await sendKeys({ press: ' ' });
51
- expect(spy.notCalled).to.be.true;
52
- });
53
- it('emits an input event when a space key is pressed and is not already selected', async () => {
54
- const buttonElement = await fixture(html ` <glide-core-button-group-button value="value-1"
55
- >Button 1</glide-core-button-group-button
56
- >`);
57
- buttonElement.focus();
58
- setTimeout(async () => {
59
- await sendKeys({ press: ' ' });
60
- });
61
- const inputEvent = await oneEvent(buttonElement, 'input');
62
- expect(inputEvent instanceof Event).to.be.true;
63
- });
64
- it('does not emit an input event when a space key is pressed and is selected', async () => {
65
- const buttonElement = await fixture(html `<glide-core-button-group-button value="value-1" selected
66
- >Button 1</glide-core-button-group-button
67
- >`);
68
- buttonElement.focus();
69
- const spy = sinon.spy();
70
- buttonElement.addEventListener('input', spy);
71
- await sendKeys({ press: ' ' });
72
- expect(spy.notCalled).to.be.true;
12
+ const event = await oneEvent(component, 'private-selected');
13
+ expect(event instanceof Event).to.be.true;
14
+ expect(event.bubbles).to.be.true;
73
15
  });
@@ -0,0 +1,13 @@
1
+ import { expect, fixture, html } from '@open-wc/testing';
2
+ import GlideCoreButtonGroupButton from './button-group.button.js';
3
+ GlideCoreButtonGroupButton.shadowRootOptions.mode = 'open';
4
+ it('focuses itself when `focus()` is called ', async () => {
5
+ const component = await fixture(html `
6
+ <glide-core-button-group-button
7
+ label="Button"
8
+ ></glide-core-button-group-button>
9
+ `);
10
+ component.focus();
11
+ const radio = component.shadowRoot?.querySelector('[role="radio"]');
12
+ expect(component.shadowRoot?.activeElement).to.equal(radio);
13
+ });
@@ -0,0 +1 @@
1
+ import './button-group.button.js';
@@ -0,0 +1,42 @@
1
+ import './button-group.button.js';
2
+ import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
3
+ import GlideCoreButtonGroupButton from './button-group.button.js';
4
+ GlideCoreButtonGroupButton.shadowRootOptions.mode = 'open';
5
+ it('sets `aria-checked` when selected programmatically', async () => {
6
+ const component = await fixture(html `<glide-core-button-group-button
7
+ label="Button"
8
+ ></glide-core-button-group-button>`);
9
+ component.selected = true;
10
+ await elementUpdated(component);
11
+ const radio = component.shadowRoot?.querySelector('[role="radio"]');
12
+ expect(radio).to.have.attribute('aria-checked', 'true');
13
+ });
14
+ it('sets `aria-checked` when deselected programmatically', async () => {
15
+ const component = await fixture(html `<glide-core-button-group-button
16
+ label="Button"
17
+ selected
18
+ ></glide-core-button-group-button>`);
19
+ component.selected = false;
20
+ await elementUpdated(component);
21
+ const radio = component.shadowRoot?.querySelector('[role="radio"]');
22
+ expect(radio).to.have.attribute('aria-checked', 'false');
23
+ });
24
+ it('sets `aria-disabled` when disabled programmatically', async () => {
25
+ const component = await fixture(html `<glide-core-button-group-button
26
+ label="Button"
27
+ ></glide-core-button-group-button>`);
28
+ component.disabled = true;
29
+ await elementUpdated(component);
30
+ const radio = component.shadowRoot?.querySelector('[role="radio"]');
31
+ expect(radio).to.have.attribute('aria-disabled', 'true');
32
+ });
33
+ it('sets `aria-disabled` when enabled programmatically', async () => {
34
+ const component = await fixture(html `<glide-core-button-group-button
35
+ label="Button"
36
+ disabled
37
+ ></glide-core-button-group-button>`);
38
+ component.disabled = false;
39
+ await elementUpdated(component);
40
+ const radio = component.shadowRoot?.querySelector('[role="radio"]');
41
+ expect(radio).to.have.attribute('aria-disabled', 'false');
42
+ });
@@ -1,26 +1,23 @@
1
- import { LitElement, type PropertyValueMap } from 'lit';
2
- import GlideCoreButtonGroupButton from './button-group.button.js';
1
+ import { LitElement } from 'lit';
3
2
  declare global {
4
3
  interface HTMLElementTagNameMap {
5
4
  'glide-core-button-group': GlideCoreButtonGroup;
6
5
  }
7
6
  }
8
- export type ButtonGroupVariant = 'icon-only';
9
- export type ButtonGroupOrientation = 'vertical' | 'horizontal';
10
7
  /**
11
- * @description A button group for use with `<glide-core-button-group-button>`.
8
+ * @description A button group.
12
9
  *
13
- * @slot - One or more `<glide-core-button-group-button>` components.
10
+ * @slot - One or more of `<glide-core-button-group-button>`.
14
11
  */
15
12
  export default class GlideCoreButtonGroup extends LitElement {
16
13
  #private;
17
14
  static shadowRootOptions: ShadowRootInit;
18
15
  static styles: import("lit").CSSResult[];
19
16
  label?: string | undefined;
20
- listItems: GlideCoreButtonGroupButton[];
21
- variant?: ButtonGroupVariant;
22
- orientation: ButtonGroupOrientation;
17
+ get variant(): 'icon-only' | undefined;
18
+ set variant(variant: 'icon-only' | undefined);
19
+ get orientation(): 'horizontal' | 'vertical';
20
+ set orientation(orientation: 'horizontal' | 'vertical');
23
21
  firstUpdated(): void;
24
22
  render(): import("lit").TemplateResult<1>;
25
- willUpdate(changedProperties: PropertyValueMap<GlideCoreButtonGroup>): void;
26
23
  }
@@ -1 +1 @@
1
- var __decorate=this&&this.__decorate||function(t,e,o,r){var i,l=arguments.length,s=l<3?e:null===r?r=Object.getOwnPropertyDescriptor(e,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(t,e,o,r);else for(var n=t.length-1;n>=0;n--)(i=t[n])&&(s=(l<3?i(s):l>3?i(e,o,s):i(e,o))||s);return l>3&&s&&Object.defineProperty(e,o,s),s};import{LitElement,html}from"lit";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property,queryAssignedElements}from"lit/decorators.js";import{owSlotType}from"./library/ow.js";import{when}from"lit/directives/when.js";import GlideCoreButtonGroupButton from"./button-group.button.js";import styles from"./button-group.styles.js";let GlideCoreButtonGroup=class GlideCoreButtonGroup extends LitElement{constructor(){super(...arguments),this.label="",this.orientation="horizontal",this.#t=createRef()}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}firstUpdated(){if(owSlotType(this.#t.value,[GlideCoreButtonGroupButton]),"vertical"===this.orientation)for(const t of this.listItems)t.toggleAttribute("vertical");if("icon-only"===this.variant)for(const t of this.listItems)t.setAttribute("variant","icon-only")}render(){return html`${when(Boolean(this.label),(()=>html`<div class="label" id="glide-core-button-group">${this.label}</div>`))}<ul aria-labelledby="glide-core-button-group" role="radiogroup" class="${classMap({"radio-group":!0,vertical:"vertical"===this.orientation})}"><slot @slotchange="${this.#e}" ${ref(this.#t)}></slot></ul>`}willUpdate(t){if(this.hasUpdated&&t.has("variant")){if("icon-only"===t.get("variant"))for(const t of this.listItems)t.removeAttribute("variant");else for(const t of this.listItems)t.setAttribute("variant","icon-only")}if(this.hasUpdated&&t.has("orientation")){const e=t.get("orientation");if("vertical"===e)for(const t of this.listItems)t.removeAttribute("vertical");else if("horizontal"===e)for(const t of this.listItems)t.toggleAttribute("vertical")}}#t;#e(){owSlotType(this.#t.value,[GlideCoreButtonGroupButton])}};__decorate([property()],GlideCoreButtonGroup.prototype,"label",void 0),__decorate([queryAssignedElements({selector:"glide-core-button-group-button"})],GlideCoreButtonGroup.prototype,"listItems",void 0),__decorate([property()],GlideCoreButtonGroup.prototype,"variant",void 0),__decorate([property()],GlideCoreButtonGroup.prototype,"orientation",void 0),GlideCoreButtonGroup=__decorate([customElement("glide-core-button-group")],GlideCoreButtonGroup);export default GlideCoreButtonGroup;
1
+ var __decorate=this&&this.__decorate||function(t,e,o,n){var r,i=arguments.length,l=i<3?e:null===n?n=Object.getOwnPropertyDescriptor(e,o):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)l=Reflect.decorate(t,e,o,n);else for(var s=t.length-1;s>=0;s--)(r=t[s])&&(l=(i<3?r(l):i>3?r(e,o,l):r(e,o))||l);return i>3&&l&&Object.defineProperty(e,o,l),l};import{LitElement,html}from"lit";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property}from"lit/decorators.js";import GlideCoreButtonGroupButton from"./button-group.button.js";import ow,{owSlot,owSlotType}from"./library/ow.js";import styles from"./button-group.styles.js";let GlideCoreButtonGroup=class GlideCoreButtonGroup extends LitElement{constructor(){super(...arguments),this.label="",this.#t="horizontal",this.#e=createRef()}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}get variant(){return this.#o}set variant(t){for(const e of this.#n)e.privateVariant=t;this.#o=t}get orientation(){return this.#t}set orientation(t){for(const e of this.#n)e.privateOrientation=t;this.#t=t}firstUpdated(){owSlot(this.#e.value),owSlotType(this.#e.value,[GlideCoreButtonGroupButton])}render(){return html`<div class="${classMap({component:!0,horizontal:"horizontal"===this.orientation,vertical:"vertical"===this.orientation})}"><div class="label" id="label" data-test="label">${this.label}</div><div aria-labelledby="label" role="radiogroup" class="${classMap({container:!0,vertical:"vertical"===this.orientation})}"><slot @click="${this.#r}" @keydown="${this.#i}" @private-selected="${this.#l}" @slotchange="${this.#s}" ${ref(this.#e)}></slot></div></div>`}#t;#e;#o;get#n(){return[...this.querySelectorAll("glide-core-button-group-button")]}#s(){ow(this.#n.length,ow.number.greaterThan(1).message("A Button Group must contain two or more Button Group Buttons.")),owSlot(this.#e.value),owSlotType(this.#e.value,[GlideCoreButtonGroupButton]);if(!this.#n.find((({disabled:t,selected:e})=>!t&&e))){const t=this.#n.find((({disabled:t})=>!t));t&&(t.selected=!0)}for(const t of this.#n)t.privateVariant=this.variant,this.orientation&&(t.privateOrientation=this.orientation)}#r(t){if(t.target instanceof HTMLElement){const e=t.target.closest("glide-core-button-group-button");!e||e.disabled||e.selected||(e.selected=!0)}}#i(t){const e=this.querySelector("glide-core-button-group-button[selected]");switch(ow(e,ow.object.instanceOf(GlideCoreButtonGroupButton)),t.key){case"ArrowUp":case"ArrowLeft":{t.preventDefault();let o=e?.previousElementSibling??this.#n.at(-1);for(;o instanceof GlideCoreButtonGroupButton&&o.disabled;)o=o.previousElementSibling??this.#n.at(-1);o instanceof GlideCoreButtonGroupButton&&(o.selected=!0);break}case"ArrowDown":case"ArrowRight":{t.preventDefault();let o=e?.nextElementSibling??this.#n.at(0);for(;o instanceof GlideCoreButtonGroupButton&&o.disabled;)o=o.nextElementSibling??this.#n.at(0);o instanceof GlideCoreButtonGroupButton&&(o.selected=!0);break}case" ":if(t.preventDefault(),t.target instanceof HTMLElement){const e=t.target.closest("glide-core-button-group-button");e&&!e.disabled&&(e.selected=!0)}}}#l(t){if(t.target instanceof GlideCoreButtonGroupButton&&t.target.selected){for(const e of this.#n)e!==t.target&&(e.selected=!1);t.target.focus(),this.dispatchEvent(new Event("change",{bubbles:!0})),this.dispatchEvent(new Event("input",{bubbles:!0}))}}};__decorate([property({reflect:!0})],GlideCoreButtonGroup.prototype,"label",void 0),__decorate([property({reflect:!0})],GlideCoreButtonGroup.prototype,"variant",null),__decorate([property({reflect:!0})],GlideCoreButtonGroup.prototype,"orientation",null),GlideCoreButtonGroup=__decorate([customElement("glide-core-button-group")],GlideCoreButtonGroup);export default GlideCoreButtonGroup;
@@ -8,9 +8,5 @@ export declare const Default: StoryObj;
8
8
  export declare const DefaultWithOrientationVertical: StoryObj;
9
9
  export declare const DefaultWithPrefixIcon: StoryObj;
10
10
  export declare const DefaultWithOrientationVerticalPrefixIcon: StoryObj;
11
- export declare const DefaultWithVariantIconOnly: StoryObj;
11
+ export declare const DefaultWithIconOnly: StoryObj;
12
12
  export declare const DefaultWithOrientationVerticalOnlyIcon: StoryObj;
13
- export declare const DefaultWithNoneSelected: StoryObj;
14
- export declare const DefaultWithSelected: StoryObj;
15
- export declare const DefaultWithDisabled: StoryObj;
16
- export declare const DefaultWithOneButton: StoryObj;
@@ -1,8 +1,24 @@
1
1
  import{css}from"lit";import visuallyHidden from"./styles/visually-hidden.js";export default[css`
2
- .radio-group {
3
- appearance: none;
2
+ .component {
4
3
  border: 1px solid var(--glide-core-border-base);
5
4
  border-radius: 0.75rem;
5
+ display: inline-block;
6
+ }
7
+
8
+ .label {
9
+ /*
10
+ Colored to pass the contrast check in the "is accessible" test. It's visually
11
+ hidden and doesn't need to meet contrast requirements. But the alternative
12
+ is to add "ignoredRules: ['color-contrast']" to that test, disabling contrast
13
+ checking for the entire component.
14
+ */
15
+ color: var(--glide-core-color-white);
16
+
17
+ ${visuallyHidden};
18
+ }
19
+
20
+ .container {
21
+ appearance: none;
6
22
  display: inline-flex;
7
23
  margin: 0;
8
24
  padding: 0;
@@ -11,8 +27,4 @@ import{css}from"lit";import visuallyHidden from"./styles/visually-hidden.js";exp
11
27
  flex-direction: column;
12
28
  }
13
29
  }
14
-
15
- .label {
16
- ${visuallyHidden};
17
- }
18
30
  `];
@@ -1,5 +1,6 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
1
2
  import './button-group.js';
2
- import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
3
+ import { expect, fixture, html } from '@open-wc/testing';
3
4
  import GlideCoreButtonGroup from './button-group.js';
4
5
  import GlideCoreButtonGroupButton from './button-group.button.js';
5
6
  import expectArgumentError from './library/expect-argument-error.js';
@@ -8,261 +9,140 @@ GlideCoreButtonGroupButton.shadowRootOptions.mode = 'open';
8
9
  it('registers', async () => {
9
10
  expect(window.customElements.get('glide-core-button-group')).to.equal(GlideCoreButtonGroup);
10
11
  });
11
- it('is accessible', async () => {
12
- const element = await fixture(html `<glide-core-button-group label="label"
13
- ><glide-core-button-group-button value="value"
14
- >Button</glide-core-button-group-button
15
- ></glide-core-button-group
16
- >`);
17
- await expect(element).to.be.accessible();
18
- });
19
- it('renders a label and unordered list', async () => {
20
- const element = await fixture(html `<glide-core-button-group label="label"
21
- ><glide-core-button-group-button value="value"
22
- >Button</glide-core-button-group-button
23
- ></glide-core-button-group
24
- >`);
25
- const ulElement = element.shadowRoot?.querySelector('ul');
26
- const labelElement = element.shadowRoot?.querySelector('div.label');
27
- expect(ulElement).to.not.be.null;
28
- expect(labelElement).to.not.be.null;
29
- expect(ulElement).to.have.attribute('aria-labelledby', labelElement?.id);
30
- });
31
- it('does not render a label when not given', async () => {
32
- const element = await fixture(html `<glide-core-button-group
33
- ><glide-core-button-group-button value="value"
34
- >Button</glide-core-button-group-button
35
- ></glide-core-button-group
36
- >`);
37
- const ulElement = element.shadowRoot?.querySelector('ul');
38
- const labelElement = element.shadowRoot?.querySelector('label');
39
- expect(ulElement).to.not.be.null;
40
- expect(labelElement).to.be.null;
41
- });
42
- it('assigns buttons the correct positional presentation when in a group', async () => {
43
- await fixture(html `<glide-core-button-group>
44
- <glide-core-button-group-button value="value-1"
45
- >Button 1</glide-core-button-group-button
46
- >
47
- <glide-core-button-group-button value="value-2"
48
- >Button 2</glide-core-button-group-button
49
- >
50
- <glide-core-button-group-button value="value-3"
51
- >Button 3</glide-core-button-group-button
52
- >
53
- <glide-core-button-group-button value="value-4"
54
- >Button 4</glide-core-button-group-button
55
- >
12
+ it('has defaults', async () => {
13
+ const component = await fixture(html `<glide-core-button-group label="Label">
14
+ <glide-core-button-group-button
15
+ label="One"
16
+ ></glide-core-button-group-button>
17
+
18
+ <glide-core-button-group-button
19
+ label="Two"
20
+ ></glide-core-button-group-button>
56
21
  </glide-core-button-group>`);
57
- const buttonElements = document.querySelectorAll('glide-core-button-group-button');
58
- expect(buttonElements.length).to.equal(4);
59
- const liElement1 = buttonElements[0].shadowRoot?.querySelector('li');
60
- expect(liElement1).to.have.class('first');
61
- const liElement2 = buttonElements[1].shadowRoot?.querySelector('li');
62
- expect(liElement2).to.have.class('inner');
63
- const liElement3 = buttonElements[2].shadowRoot?.querySelector('li');
64
- expect(liElement3).to.have.class('inner');
65
- const liElement4 = buttonElements[3].shadowRoot?.querySelector('li');
66
- expect(liElement4).to.have.class('last');
22
+ expect(component.orientation).to.equal('horizontal');
23
+ expect(component.variant).to.equal(undefined);
24
+ expect(component).to.have.attribute('orientation', 'horizontal');
25
+ expect(component).to.not.have.attribute('variant');
67
26
  });
68
- it('buttons have a vertical presention when attribute "orientation" is set to "vertical"', async () => {
69
- await fixture(html `<glide-core-button-group orientation="vertical"
70
- ><glide-core-button-group-button value="value"
71
- >Button</glide-core-button-group-button
72
- >
73
- <glide-core-button-group-button value="value-2"
74
- >Button 2</glide-core-button-group-button
75
- >
27
+ it('is accessible', async () => {
28
+ const component = await fixture(html `<glide-core-button-group label="label">
29
+ <glide-core-button-group-button
30
+ label="One"
31
+ ></glide-core-button-group-button>
32
+
33
+ <glide-core-button-group-button
34
+ label="Two"
35
+ ></glide-core-button-group-button>
76
36
  </glide-core-button-group>`);
77
- const buttonElements = document.querySelectorAll('glide-core-button-group-button');
78
- const liElement1 = buttonElements[0]?.shadowRoot?.querySelector('li');
79
- const liElement2 = buttonElements[1]?.shadowRoot?.querySelector('li');
80
- expect(buttonElements[0]).to.have.attribute('vertical');
81
- expect(liElement1).to.have.class('vertical');
82
- expect(buttonElements[1]).to.have.attribute('vertical');
83
- expect(liElement2).to.have.class('vertical');
37
+ // It's unfortunate to ignore this rule. But the label doesn't meet color
38
+ // contrast requirements.
39
+ // Axe has an `ignoreTags` but no `ignoreSelectors`.
40
+ await expect(component).to.be.accessible({
41
+ ignoredRules: ['color-contrast'],
42
+ });
84
43
  });
85
- it('does not have a vertical presention when the "orientation" is not set to "vertical"', async () => {
86
- await fixture(html `<glide-core-button-group label="label">
87
- <glide-core-button-group-button value="value"
88
- >Button</glide-core-button-group-button
89
- >
90
- <glide-core-button-group-button value="value-2"
91
- >Button 2</glide-core-button-group-button
92
- >
44
+ it('can have a label', async () => {
45
+ const component = await fixture(html `<glide-core-button-group label="label">
46
+ <glide-core-button-group-button
47
+ label="One"
48
+ ></glide-core-button-group-button>
49
+
50
+ <glide-core-button-group-button
51
+ label="Two"
52
+ ></glide-core-button-group-button>
93
53
  </glide-core-button-group>`);
94
- const buttonElements = document.querySelectorAll('glide-core-button-group-button');
95
- const liElement1 = buttonElements[0]?.shadowRoot?.querySelector('li');
96
- const liElement2 = buttonElements[1]?.shadowRoot?.querySelector('li');
97
- expect(buttonElements[0]).to.not.have.attribute('vertical');
98
- expect(liElement1).to.not.have.class('vertical');
99
- expect(buttonElements[1]).to.not.have.attribute('vertical');
100
- expect(liElement2).to.not.have.class('vertical');
101
- });
102
- it('reacts to "orientation" attribute when changed from "horizontal" to "vertical"', async () => {
103
- const element = await fixture(html `<glide-core-button-group label="label" orientation="horizontal"
104
- ><glide-core-button-group-button value="value"
105
- >Button</glide-core-button-group-button
106
- ></glide-core-button-group
107
- >`);
108
- const buttonElement = document.querySelector('glide-core-button-group-button');
109
- const liElement = buttonElement?.shadowRoot?.querySelector('li');
110
- expect(liElement).to.not.have.class('vertical');
111
- element.setAttribute('orientation', 'vertical');
112
- // wait for attributes to be set on li
113
- await elementUpdated(element);
114
- expect(liElement).to.have.class('vertical');
115
- element.setAttribute('orientation', 'horizontal');
116
- // wait for attributes to be set on li
117
- await elementUpdated(element);
118
- await expect(liElement).to.not.have.class('vertical');
54
+ const label = component.shadowRoot?.querySelector('[data-test="label"]');
55
+ const radioGroup = component.shadowRoot?.querySelector('[role="radiogroup"]');
56
+ expect(label?.textContent).to.equal('label');
57
+ expect(radioGroup).to.have.attribute('aria-labelledby', label?.id);
119
58
  });
120
- it('applies an "icon-only" variant to buttons when set on the group', async () => {
121
- await fixture(html `<glide-core-button-group label="label" variant="icon-only"
122
- ><glide-core-button-group-button value="value"
123
- ><span slot="prefix">Prefix 1</span>Button
124
- 1</glide-core-button-group-button
125
- >
126
- <glide-core-button-group-button value="value-2"
127
- ><span slot="prefix">Prefix 2</span>Button
128
- 2</glide-core-button-group-button
129
- >
59
+ it('sets the orientation of each button when horizontal', async () => {
60
+ await fixture(html `<glide-core-button-group>
61
+ <glide-core-button-group-button
62
+ label="One"
63
+ ></glide-core-button-group-button>
64
+
65
+ <glide-core-button-group-button
66
+ label="Two"
67
+ ></glide-core-button-group-button>
130
68
  </glide-core-button-group>`);
131
- const buttonElements = document.querySelectorAll('glide-core-button-group-button');
132
- const liElement1 = buttonElements[0]?.shadowRoot?.querySelector('li');
133
- const liElement2 = buttonElements[1]?.shadowRoot?.querySelector('li');
134
- expect(buttonElements[0]).to.have.attribute('variant', 'icon-only');
135
- expect(liElement1).to.have.class('icon-only');
136
- expect(buttonElements[1]).to.have.attribute('variant', 'icon-only');
137
- expect(liElement2).to.have.class('icon-only');
138
- });
139
- it('does not apply an "icon-only" variant to buttons when not set on the group', async () => {
140
- await fixture(html `<glide-core-button-group label="label"
141
- ><glide-core-button-group-button value="value"
142
- >Button</glide-core-button-group-button
143
- >
144
- <glide-core-button-group-button value="value-2"
145
- >Button 2</glide-core-button-group-button
146
- >
69
+ const buttons = document.querySelectorAll('glide-core-button-group-button');
70
+ expect(buttons[0]?.privateOrientation).to.equal('horizontal');
71
+ expect(buttons[1]?.privateOrientation).to.equal('horizontal');
72
+ });
73
+ it('sets the orientation of each button when vertical', async () => {
74
+ await fixture(html `<glide-core-button-group orientation="vertical">
75
+ <glide-core-button-group-button
76
+ label="One"
77
+ ></glide-core-button-group-button>
78
+
79
+ <glide-core-button-group-button
80
+ label="Two"
81
+ ></glide-core-button-group-button>
147
82
  </glide-core-button-group>`);
148
- const buttonElements = document.querySelectorAll('glide-core-button-group-button');
149
- const liElement1 = buttonElements[0]?.shadowRoot?.querySelector('li');
150
- const liElement2 = buttonElements[1]?.shadowRoot?.querySelector('li');
151
- expect(buttonElements[0]).to.not.have.attribute('variant');
152
- expect(liElement1).to.not.have.class('icon-only');
153
- expect(buttonElements[1]).to.not.have.attribute('variant');
154
- expect(liElement2).to.not.have.class('icon-only');
155
- });
156
- it('reacts to variant "icon-only" attribute when added and removed', async () => {
157
- await fixture(html `<glide-core-button-group label="label"
158
- ><glide-core-button-group-button value="value"
159
- ><span slot="prefix">Prefix</span>Button</glide-core-button-group-button
160
- ></glide-core-button-group
161
- >`);
162
- const element = document.querySelector('glide-core-button-group');
163
- const buttonElement = document.querySelector('glide-core-button-group-button');
164
- expect(element).to.not.be.null;
165
- expect(buttonElement).to.not.be.null;
166
- expect(buttonElement).to.not.have.attribute('variant');
167
- element?.setAttribute('variant', 'icon-only');
168
- // wait for attributes to be set
169
- await elementUpdated(element);
170
- expect(buttonElement).to.have.attribute('variant', 'icon-only');
171
- element?.removeAttribute('variant');
172
- // wait for attributes to be set
173
- await elementUpdated(element);
174
- expect(buttonElement).to.not.have.attribute('variant');
83
+ const buttons = document.querySelectorAll('glide-core-button-group-button');
84
+ expect(buttons[0]?.privateOrientation).to.equal('vertical');
85
+ expect(buttons[1]?.privateOrientation).to.equal('vertical');
86
+ });
87
+ it('sets `privateVariant` on each button', async () => {
88
+ await fixture(html `<glide-core-button-group label="label" variant="icon-only">
89
+ <glide-core-button-group-button label="One">
90
+ <div slot="prefix">Prefix</div>
91
+ </glide-core-button-group-button>
92
+
93
+ <glide-core-button-group-button label="Two">
94
+ <div slot="prefix">Prefix</div>
95
+ </glide-core-button-group-button>
96
+ </glide-core-button-group>`);
97
+ const buttons = document.querySelectorAll('glide-core-button-group-button');
98
+ expect(buttons[0].privateVariant).to.equal('icon-only');
99
+ expect(buttons[1].privateVariant).to.equal('icon-only');
175
100
  });
176
- it('throws an error when an element other than `glide-core-button-group-button` is a child of the default slot', async () => {
101
+ it('throws when its default slot is the wrong type', async () => {
177
102
  await expectArgumentError(() => {
178
103
  return fixture(html `
179
104
  <glide-core-button-group label="label">
180
- <div>Content</div>
105
+ <div></div>
181
106
  </glide-core-button-group>
182
107
  `);
183
108
  });
184
- });
185
- it('throws an error when the group has no children', async () => {
186
109
  await expectArgumentError(() => {
187
110
  return fixture(html `<glide-core-button-group label="label"> </glide-core-button-group>`);
188
111
  });
189
112
  });
190
- it("has a tabble button if it's the first element in a button group", async () => {
191
- await fixture(html `<glide-core-button-group>
192
- <glide-core-button-group-button value="value-1"
193
- >Button 1</glide-core-button-group-button
194
- >
195
- <glide-core-button-group-button value="value-2"
196
- >Button 2</glide-core-button-group-button
197
- >
198
- <glide-core-button-group-button value="value-3"
199
- >Button 3</glide-core-button-group-button
200
- >
201
- </glide-core-button-group>`);
202
- const buttonElements = document.querySelectorAll('glide-core-button-group-button');
203
- expect(buttonElements.length).to.equal(3);
204
- const liElement1 = buttonElements[0].shadowRoot?.querySelector('li');
205
- expect(liElement1).to.have.attribute('tabindex', '0');
206
- const liElement2 = buttonElements[1].shadowRoot?.querySelector('li');
207
- expect(liElement2).to.have.attribute('tabindex', '-1');
208
- const liElement3 = buttonElements[2].shadowRoot?.querySelector('li');
209
- expect(liElement3).to.have.attribute('tabindex', '-1');
210
- });
211
- it('has the first non-disabled button set as tabbable when in a group', async () => {
212
- await fixture(html `<glide-core-button-group>
213
- <glide-core-button-group-button value="value-1" disabled
214
- >Button 1</glide-core-button-group-button
215
- >
216
- <glide-core-button-group-button value="value-2"
217
- >Button 2</glide-core-button-group-button
218
- >
219
- <glide-core-button-group-button value="value-3"
220
- >Button 3</glide-core-button-group-button
221
- >
222
- </glide-core-button-group>`);
223
- const buttonElements = document.querySelectorAll('glide-core-button-group-button');
224
- expect(buttonElements.length).to.equal(3);
225
- const liElement1 = buttonElements[0].shadowRoot?.querySelector('li');
226
- expect(liElement1).to.have.attribute('tabindex', '-1');
227
- const liElement2 = buttonElements[1].shadowRoot?.querySelector('li');
228
- expect(liElement2).to.have.attribute('tabindex', '0');
229
- const liElement3 = buttonElements[2].shadowRoot?.querySelector('li');
230
- expect(liElement3).to.have.attribute('tabindex', '-1');
231
- });
232
- it('has the "selected" button as tabbable and others are not when in a group', async () => {
113
+ it('selects the first button not disabled', async () => {
233
114
  await fixture(html `<glide-core-button-group>
234
- <glide-core-button-group-button value="value-1"
235
- >Button 1</glide-core-button-group-button
236
- >
237
- <glide-core-button-group-button value="value-2" selected
238
- >Button 2</glide-core-button-group-button
239
- >
240
- <glide-core-button-group-button value="value-3"
241
- >Button 3</glide-core-button-group-button
242
- >
115
+ <glide-core-button-group-button
116
+ label="One"
117
+ disabled
118
+ ></glide-core-button-group-button>
119
+
120
+ <glide-core-button-group-button
121
+ label="Two"
122
+ ></glide-core-button-group-button>
123
+
124
+ <glide-core-button-group-button
125
+ label="Three"
126
+ ></glide-core-button-group-button>
243
127
  </glide-core-button-group>`);
244
- const buttonElements = document.querySelectorAll('glide-core-button-group-button');
245
- expect(buttonElements.length).to.equal(3);
246
- const liElement1 = buttonElements[0].shadowRoot?.querySelector('li');
247
- expect(liElement1).to.have.attribute('tabindex', '-1');
248
- const liElement2 = buttonElements[1].shadowRoot?.querySelector('li');
249
- expect(liElement2).to.have.attribute('tabindex', '0');
250
- const liElement3 = buttonElements[2].shadowRoot?.querySelector('li');
251
- expect(liElement3).to.have.attribute('tabindex', '-1');
128
+ const buttons = document.querySelectorAll('glide-core-button-group-button');
129
+ expect(buttons[0].selected).to.be.false;
130
+ expect(buttons[1].selected).to.be.true;
131
+ expect(buttons[2].selected).to.be.false;
252
132
  });
253
- it('initially no button sets itself as tabbable if all are disabled in a group', async () => {
133
+ it('selects no buttons when all are disabled', async () => {
254
134
  await fixture(html `<glide-core-button-group>
255
- <glide-core-button-group-button value="value-1" disabled
256
- >Button 1</glide-core-button-group-button
257
- >
258
- <glide-core-button-group-button value="value-2" disabled
259
- >Button 2</glide-core-button-group-button
260
- >
135
+ <glide-core-button-group-button
136
+ label="One"
137
+ disabled
138
+ ></glide-core-button-group-button>
139
+
140
+ <glide-core-button-group-button
141
+ label="Two"
142
+ disabled
143
+ ></glide-core-button-group-button>
261
144
  </glide-core-button-group>`);
262
- const buttonElements = document.querySelectorAll('glide-core-button-group-button');
263
- expect(buttonElements.length).to.equal(2);
264
- const liElement1 = buttonElements[0].shadowRoot?.querySelector('li');
265
- expect(liElement1).to.have.attribute('tabindex', '-1');
266
- const liElement2 = buttonElements[1].shadowRoot?.querySelector('li');
267
- expect(liElement2).to.have.attribute('tabindex', '-1');
145
+ const buttons = document.querySelectorAll('glide-core-button-group-button');
146
+ expect(buttons[0].selected).to.be.false;
147
+ expect(buttons[1].selected).to.be.false;
268
148
  });