@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
package/README.md CHANGED
@@ -1,13 +1,21 @@
1
- # Glide Core Components
1
+ <p align="center">
2
+ <a href="https://glide-core.crowdstrike-ux.workers.dev">
3
+ <img src="https://github.com/CrowdStrike/glide-core/blob/main/.github/glide-core.png?raw=true" alt="Glide Core logo" width="300" />
4
+ </a>
5
+ </p>
6
+
7
+ <h1 align="center">The Glide Design System from CrowdStrike</h1>
8
+
9
+ <br>
2
10
 
3
11
  This package contains Web Components built with [Lit](https://lit.dev/).
4
12
 
5
- ## Installation
13
+ ## Usage
6
14
 
7
- ### 1. Add Lit as a dependency
15
+ ### 1. Add this package and Lit as dependencies
8
16
 
9
17
  ```bash
10
- pnpm i @crowdstrike/glide-core lit
18
+ pnpm install @crowdstrike/glide-core lit
11
19
  ```
12
20
 
13
21
  ### 2. Import the fonts and variables
@@ -29,7 +37,38 @@ import '@crowdstrike/glide-core/button.js';
29
37
  <glide-core-button size="small">Button</glide-core-button>
30
38
  ```
31
39
 
32
- ## Adding CSS Custom Properties
40
+ ## Development
41
+
42
+ ### Contributing
43
+
44
+ Follow our [Contribution Guidelines](./CONTRIBUTING.md).
45
+
46
+ ### Getting started
47
+
48
+ First you'll need to install the dependencies for the repository. We use [PNPM](https://pnpm.io). Run the following command from the root of the repository:
49
+
50
+ ```bash
51
+ pnpm install
52
+ pnpm dlx playwright install
53
+ ```
54
+
55
+ > [!NOTE]
56
+ >
57
+ > - If you have `ignore-scripts=true` in your `~/.npmrc`, you'll also need to run `pnpm prepare`, which will install some Git hooks for linting, formatting, typechecking, and testing.
58
+ > - We recommend using [Corepack](https://pnpm.io/installation#using-corepack) to help manage the version of `pnpm` installed in this repository; however, it is not a requirement.
59
+
60
+ ### Running Storybook
61
+
62
+ Run the following command from the root of the repository:
63
+
64
+ ```bash
65
+ pnpm start
66
+ ```
67
+
68
+ After Storybook's build completes, your browser should navigate to the Storybook instance.
69
+ If this doesn't happen automatically, a URL will be shown in your terminal for you to browse to.
70
+
71
+ ### Adding CSS Custom Properties
33
72
 
34
73
  Glide Core uses scripts from [@crowdstrike/design-tokens](https://www.npmjs.com/package/@crowdstrike/design-tokens) to import variables from Figma and transform them into CSS custom properties.
35
74
  This allows us to maintain a single source of truth for color, typography, spacing, etc.
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
1
2
  import './accordion.js';
2
3
  import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
3
4
  import GlideCoreAccordion from './accordion.js';
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
1
2
  import './accordion.js';
2
3
  import { elementUpdated, expect, fixture, html, oneEvent, } from '@open-wc/testing';
3
4
  import GlideCoreAccordion from './accordion.js';
@@ -1,33 +1,32 @@
1
- import { LitElement, type PropertyValueMap } from 'lit';
2
- import type { ButtonGroupVariant } from './button-group.js';
1
+ import { LitElement } from 'lit';
3
2
  declare global {
4
3
  interface HTMLElementTagNameMap {
5
4
  'glide-core-button-group-button': GlideCoreButtonGroupButton;
6
5
  }
7
6
  }
8
7
  /**
9
- * @description A button for use with `<glide-core-button-group>` with label and optional icon.
8
+ * @description A button with a label and optional icon for use in a `<glide-core-button-group>`.
10
9
  *
11
- * @event change - Dispatched when clicked or selected by key press.
12
- * @event input - Dispatched when clicked or selected by key press.
10
+ * @event change - Dispatched when the button is selected.
11
+ * @event input - Dispatched when the button is selected.
13
12
 
14
- * @slot prefix - Icon content.
15
- * @slot - Label content.
13
+ * @slot prefix - An icon.
14
+ * @slot - A label.
16
15
  */
17
16
  export default class GlideCoreButtonGroupButton extends LitElement {
18
17
  #private;
19
18
  static shadowRootOptions: ShadowRootInit;
20
19
  static styles: import("lit").CSSResult[];
21
- selected: boolean;
20
+ label?: string | undefined;
21
+ get selected(): boolean;
22
+ set selected(isSelected: boolean);
22
23
  disabled: boolean;
23
- value: string;
24
- variant?: ButtonGroupVariant;
25
- vertical: boolean;
24
+ value?: string | undefined;
25
+ privateOrientation: 'horizontal' | 'vertical';
26
+ privateVariant?: 'icon-only';
27
+ click(): void;
26
28
  firstUpdated(): void;
27
29
  focus(options?: FocusOptions): void;
28
30
  render(): import("lit").TemplateResult<1>;
29
- willUpdate(changedProperties: PropertyValueMap<GlideCoreButtonGroupButton>): void;
30
- private isSingleButton;
31
- private isTabbable;
32
- private position;
31
+ private hasPrefixSlot;
33
32
  }
@@ -1 +1 @@
1
- var _a,GlideCoreButtonGroupButton_1,__decorate=this&&this.__decorate||function(t,e,o,i){var s,l=arguments.length,n=l<3?e:null===i?i=Object.getOwnPropertyDescriptor(e,o):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(t,e,o,i);else for(var r=t.length-1;r>=0;r--)(s=t[r])&&(n=(l<3?s(n):l>3?s(e,o,n):s(e,o))||n);return l>3&&n&&Object.defineProperty(e,o,n),n};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{owSlot,owSlotType}from"./library/ow.js";import styles from"./button-group.button.styles.js";let GlideCoreButtonGroupButton=class GlideCoreButtonGroupButton extends LitElement{constructor(){super(...arguments),this.selected=!1,this.disabled=!1,this.value="",this.vertical=!1,this.isSingleButton=!1,this.isTabbable=!1,this.position="inner",this.#t=createRef(),this.#e=createRef(),this.#o=createRef()}static{GlideCoreButtonGroupButton_1=this}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}firstUpdated(){if(owSlot(this.#t.value),owSlotType(this.#t.value,[Text]),this.selected&&(this.isTabbable=!0),"icon-only"===this.variant&&owSlot(this.#o.value),this.#i.length>0){if(this.#i.length>1&&this.#i.at(0)===this?this.position="first":this.#i.length>1&&this.#i.at(-1)===this?this.position="last":1===this.#i.length&&(this.isSingleButton=!0),this.#i.every((t=>t.disabled)))return;const t=this.#i.find((t=>!t.disabled&&t.selected));if(t&&t===this)this.isTabbable=!0;else if(!t){const t=this.#i.find((t=>!t.disabled));t&&t===this&&(this.isTabbable=!0)}}}focus(t){this.#e.value?.focus(t)}render(){return html`<li role="radio" aria-checked="${this.selected}" aria-disabled="${this.disabled}" tabindex="${!this.isTabbable||this.disabled?-1:0}" @click="${this.#s}" @keydown="${this.#l}" ${ref(this.#e)} class="${classMap({component:!0,selected:this.selected,disabled:this.disabled,[this.position]:!0,vertical:this.vertical,single:this.isSingleButton,"icon-only":"icon-only"===this.variant})}"><slot name="prefix" ${ref(this.#o)}></slot><span class="${classMap({"visually-hidden":"icon-only"===this.variant})}"><slot @slotchange="${this.#n}" ${ref(this.#t)}></slot></span></li>`}willUpdate(t){if(this.hasUpdated&&t.has("selected")){const e=t.get("selected");if(!0===e)this.isTabbable=!1;else if(!1===e){this.isTabbable=!0,this.focus();for(const t of this.#i)t!==this&&t.selected&&(t.selected=!1)}}}#t;#e;#o;get#i(){return[...this.closest("glide-core-button-group")?.querySelectorAll("glide-core-button-group-button")??[]]}#r(t=this){t.dispatchEvent(new CustomEvent("change",{bubbles:!0,detail:t.value})),t.dispatchEvent(new CustomEvent("input",{bubbles:!0,detail:t.value}))}#s(){this.disabled||this.selected||(this.selected=!0,this.#r())}#n(){owSlot(this.#t.value),owSlotType(this.#t.value,[Text])}#l(t){if(!(this.disabled||this.#i.length<2&&" "!==t.key))switch(t.key){case"ArrowUp":case"ArrowLeft":{t.preventDefault(),this.selected=!1;let e=this.previousElementSibling;for(;!e||e instanceof GlideCoreButtonGroupButton_1&&e.disabled;)if(null===e){const t=this.#i.at(-1);t&&(e=t)}else e=e.previousElementSibling;e&&e instanceof GlideCoreButtonGroupButton_1&&(e.selected=!0,this.#r(e));break}case"ArrowDown":case"ArrowRight":{t.preventDefault(),this.selected=!1;let e=this.nextElementSibling;for(;!e||e instanceof GlideCoreButtonGroupButton_1&&e.disabled;)if(null===e){const t=this.#i.at(0);t&&(e=t)}else e=e.nextElementSibling;e&&e instanceof GlideCoreButtonGroupButton_1&&(e.selected=!0,this.#r(e));break}case" ":t.preventDefault(),this.disabled||this.selected||(this.selected=!0,this.#r())}}};__decorate([property({type:Boolean,reflect:!0})],GlideCoreButtonGroupButton.prototype,"selected",void 0),__decorate([property({type:Boolean,reflect:!0})],GlideCoreButtonGroupButton.prototype,"disabled",void 0),__decorate([property()],GlideCoreButtonGroupButton.prototype,"value",void 0),__decorate([property()],GlideCoreButtonGroupButton.prototype,"variant",void 0),__decorate([property({type:Boolean})],GlideCoreButtonGroupButton.prototype,"vertical",void 0),__decorate([state()],GlideCoreButtonGroupButton.prototype,"isSingleButton",void 0),__decorate([state()],GlideCoreButtonGroupButton.prototype,"isTabbable",void 0),__decorate([state()],GlideCoreButtonGroupButton.prototype,"position",void 0),GlideCoreButtonGroupButton=GlideCoreButtonGroupButton_1=__decorate([customElement("glide-core-button-group-button")],GlideCoreButtonGroupButton);export default GlideCoreButtonGroupButton;
1
+ var __decorate=this&&this.__decorate||function(e,t,o,i){var r,l=arguments.length,s=l<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,o):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,o,i);else for(var n=e.length-1;n>=0;n--)(r=e[n])&&(s=(l<3?r(s):l>3?r(t,o,s):r(t,o))||s);return l>3&&s&&Object.defineProperty(t,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,state}from"lit/decorators.js";import ow,{owSlot}from"./library/ow.js";import styles from"./button-group.button.styles.js";let GlideCoreButtonGroupButton=class GlideCoreButtonGroupButton extends LitElement{constructor(){super(...arguments),this.label="",this.disabled=!1,this.value="",this.privateOrientation="horizontal",this.hasPrefixSlot=!1,this.#e=createRef(),this.#t=!1,this.#o=createRef()}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}get selected(){return this.#t}set selected(e){this.#t=e,this.dispatchEvent(new Event("private-selected",{bubbles:!0}))}click(){this.#e.value?.click()}firstUpdated(){"icon-only"===this.privateVariant&&owSlot(this.#o.value)}focus(e){this.#e.value?.focus(e)}render(){return html`<div aria-checked="${this.selected}" aria-disabled="${this.disabled}" class="${classMap({component:!0,selected:this.selected,disabled:this.disabled,[this.privateOrientation]:!0,prefix:this.hasPrefixSlot,"icon-only":"icon-only"===this.privateVariant})}" role="radio" tabindex="${!this.selected||this.disabled?-1:0}" ${ref(this.#e)}><slot name="prefix" @slotchange="${this.#i}" ${ref(this.#o)}></slot><div class="${classMap({label:!0,"visually-hidden":"icon-only"===this.privateVariant})}">${this.label}</div></div>`}#e;#t;#o;#i(){ow(this.#o.value,ow.object.instanceOf(HTMLElement)),this.hasPrefixSlot=this.#o.value?.assignedNodes().length>0}};__decorate([property({reflect:!0})],GlideCoreButtonGroupButton.prototype,"label",void 0),__decorate([property({type:Boolean,reflect:!0})],GlideCoreButtonGroupButton.prototype,"selected",null),__decorate([property({type:Boolean,reflect:!0})],GlideCoreButtonGroupButton.prototype,"disabled",void 0),__decorate([property({reflect:!0})],GlideCoreButtonGroupButton.prototype,"value",void 0),__decorate([property()],GlideCoreButtonGroupButton.prototype,"privateOrientation",void 0),__decorate([property()],GlideCoreButtonGroupButton.prototype,"privateVariant",void 0),__decorate([state()],GlideCoreButtonGroupButton.prototype,"hasPrefixSlot",void 0),GlideCoreButtonGroupButton=__decorate([customElement("glide-core-button-group-button")],GlideCoreButtonGroupButton);export default GlideCoreButtonGroupButton;
@@ -1,9 +1,35 @@
1
1
  import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import visuallyHidden from"./styles/visually-hidden.js";export default[css`
2
+ :host(:first-of-type) {
3
+ .component {
4
+ &.horizontal {
5
+ border-end-start-radius: 0.6875rem;
6
+ border-start-start-radius: 0.6875rem;
7
+ }
8
+
9
+ &.vertical {
10
+ border-start-end-radius: 0.6875rem;
11
+ border-start-start-radius: 0.6875rem;
12
+ }
13
+ }
14
+ }
15
+
16
+ :host(:last-of-type) {
17
+ .component {
18
+ &.horizontal {
19
+ border-end-end-radius: 0.6875rem;
20
+ border-start-end-radius: 0.6875rem;
21
+ }
22
+
23
+ &.vertical {
24
+ border-end-end-radius: 0.6875rem;
25
+ border-end-start-radius: 0.6875rem;
26
+ }
27
+ }
28
+ }
29
+
2
30
  .component {
3
31
  align-items: center;
4
32
  appearance: none;
5
- border: none;
6
- border-inline-end: 1px solid var(--glide-core-border-base-lighter);
7
33
  cursor: pointer;
8
34
  display: flex;
9
35
  font-family: var(--glide-core-font-sans);
@@ -15,7 +41,6 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import
15
41
  line-height: 1;
16
42
  min-block-size: 1.125rem;
17
43
  min-inline-size: 5.1875rem;
18
- outline: none;
19
44
  padding-block: var(--glide-core-spacing-xs);
20
45
  padding-inline: var(--glide-core-spacing-md);
21
46
  transition-duration: 150ms;
@@ -24,66 +49,39 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import
24
49
  user-select: none;
25
50
  white-space: nowrap;
26
51
 
27
- &.icon-only {
28
- min-inline-size: 0;
29
- padding: var(--glide-core-spacing-xs);
52
+ &:focus {
53
+ outline: none;
30
54
  }
31
55
 
32
- &.single {
33
- border: none;
34
- border-radius: 0.6875rem;
35
- }
56
+ &:focus-visible {
57
+ background-color: var(--glide-core-surface-selected);
58
+ border-color: var(--glide-core-border-focus);
59
+ color: var(--glide-core-color-white);
60
+
61
+ ${focusOutline};
62
+ outline-offset: 2px;
36
63
 
37
- &.first {
38
- border-radius: 0.6875rem 0 0 0.6875rem;
64
+ /* Create a stacking context so the outline isn't obscured by other elements. */
65
+ transform: translateX(0);
39
66
  }
40
67
 
41
- &.last {
42
- border-radius: 0 0.6875rem 0.6875rem 0;
68
+ &.horizontal {
69
+ border-inline-end: 1px solid var(--glide-core-border-base-lighter);
43
70
  }
44
71
 
45
72
  &.vertical {
46
- border: none;
47
73
  border-block-end: 1px solid var(--glide-core-border-base-lighter);
48
74
 
49
- &.first {
50
- border-radius: 0.6875rem 0.6875rem 0 0;
51
- }
52
-
53
- &.last {
54
- border-radius: 0 0 0.6875rem 0.6875rem;
75
+ &.prefix {
76
+ &:not(.icon-only) {
77
+ justify-content: flex-start;
78
+ }
55
79
  }
56
80
  }
57
81
 
58
- &:not(.disabled).selected {
59
- background-color: var(--glide-core-surface-selected);
60
- border-color: var(--glide-core-surface-selected);
61
- color: var(--glide-core-color-white);
62
- }
63
-
64
- &:not(.disabled):active {
65
- background-color: var(--glide-core-surface-selected);
66
- border-color: var(--glide-core-border-focus);
67
- color: var(--glide-core-color-white);
68
- }
69
-
70
- &:not(.disabled):focus-visible {
71
- background-color: var(--glide-core-surface-selected);
72
- border-color: var(--glide-core-border-focus);
73
- color: var(--glide-core-color-white);
74
-
75
- ${focusOutline};
76
- outline-offset: 2px;
77
-
78
- /* create a stacking context so the outline doesn't become obscured behind other elements */
79
- transform: translateX(0);
80
- }
81
-
82
- &:not(.disabled, :active):hover {
83
- background-color: var(--glide-core-surface-hover);
84
- border-color: transparent;
85
- box-shadow: var(--glide-core-glow-sm);
86
- color: var(--glide-core-text-primary);
82
+ &.icon-only {
83
+ min-inline-size: 0;
84
+ padding: var(--glide-core-spacing-xs);
87
85
  }
88
86
 
89
87
  &.disabled {
@@ -92,9 +90,34 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import
92
90
  color: var(--glide-core-text-tertiary-disabled);
93
91
  cursor: not-allowed;
94
92
  }
93
+
94
+ &:not(.disabled) {
95
+ &:active {
96
+ background-color: var(--glide-core-surface-selected);
97
+ border-color: var(--glide-core-border-focus);
98
+ color: var(--glide-core-color-white);
99
+ }
100
+
101
+ &.selected {
102
+ background-color: var(--glide-core-surface-selected);
103
+ border-color: var(--glide-core-surface-selected);
104
+ color: var(--glide-core-color-white);
105
+ }
106
+ }
107
+
108
+ &:not(.disabled, :active) {
109
+ &:hover {
110
+ background-color: var(--glide-core-surface-hover);
111
+ border-color: transparent;
112
+ box-shadow: var(--glide-core-glow-sm);
113
+ color: var(--glide-core-text-primary);
114
+ }
115
+ }
95
116
  }
96
117
 
97
- .visually-hidden {
98
- ${visuallyHidden};
118
+ .label {
119
+ &.visually-hidden {
120
+ ${visuallyHidden};
121
+ }
99
122
  }
100
123
  `];
@@ -1 +1 @@
1
- export {};
1
+ import './button-group.button.js';
@@ -1,3 +1,5 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
2
+ import './button-group.button.js';
1
3
  import { ArgumentError } from 'ow';
2
4
  import { expect, fixture, html } from '@open-wc/testing';
3
5
  import GlideCoreButtonGroupButton from './button-group.button.js';
@@ -6,164 +8,99 @@ GlideCoreButtonGroupButton.shadowRootOptions.mode = 'open';
6
8
  it('registers', async () => {
7
9
  expect(window.customElements.get('glide-core-button-group-button')).to.equal(GlideCoreButtonGroupButton);
8
10
  });
9
- it('is accessible', async () => {
10
- const element = await fixture(html `<glide-core-button-group-button value="value"
11
- >Button</glide-core-button-group-button
12
- >`);
13
- await expect(element).to.be.accessible();
14
- });
15
- it('renders an li with role "radio"', async () => {
16
- const element = await fixture(html `<glide-core-button-group-button value="value"
17
- >Button</glide-core-button-group-button
18
- >`);
19
- const liElement = element.shadowRoot?.querySelector('li');
20
- expect(liElement).to.have.attribute('role', 'radio');
21
- });
22
- it('renders "aria-checked" equal to "false" and "tabindex" equal to "-1" by default', async () => {
23
- const element = await fixture(html `<glide-core-button-group-button value="value"
24
- >Button</glide-core-button-group-button
25
- >`);
26
- const liElement = element.shadowRoot?.querySelector('li');
27
- expect(liElement).to.not.be.null;
28
- expect(liElement).to.have.attribute('aria-checked', 'false');
29
- expect(liElement).to.have.attribute('tabindex', '-1');
30
- });
31
- it('renders "aria-checked" equal to "true" and "tabindex" equal to "0" when attribute "selected" exists', async () => {
32
- const element = await fixture(html `<glide-core-button-group-button value="value" selected
33
- >Button</glide-core-button-group-button
34
- >`);
35
- const liElement = element.shadowRoot?.querySelector('li');
36
- expect(liElement).to.have.attribute('aria-checked', 'true');
37
- expect(liElement).to.have.attribute('tabindex', '0');
38
- });
39
- it('renders two slots, where one has name "prefix", and the other is default with given text', async () => {
40
- const element = await fixture(html `<glide-core-button-group-button value="value" selected
41
- ><span slot="prefix" data-prefix>Prefix</span
42
- >Button</glide-core-button-group-button
43
- >`);
44
- const prefixSlot = element.shadowRoot?.querySelector('slot[name="prefix"]');
45
- const defaultSlot = element.shadowRoot?.querySelector('slot:not([name="prefix"])');
46
- // verify the slots exist
47
- expect(prefixSlot).to.not.be.null;
48
- expect(defaultSlot).to.not.be.null;
49
- const prefixContent = document.querySelector('[data-prefix]');
50
- const content = document.querySelector('glide-core-button-group-button');
51
- // verify the content of the slots exist
52
- expect(prefixContent).to.not.be.null;
53
- expect(prefixContent?.textContent).to.equal('Prefix');
54
- expect(content?.textContent).to.contain('Button');
55
- });
56
- it('is has a disabled presentation when the "disabled" attribute is true', async () => {
57
- const element = await fixture(html `<glide-core-button-group-button value="value" disabled
58
- >Button</glide-core-button-group-button
59
- >`);
60
- const liElement = element.shadowRoot?.querySelector('li');
61
- expect(liElement).to.have.attribute('aria-disabled', 'true');
62
- });
63
- it('does not have a disabled presentation when "disabled" attribute is false', async () => {
64
- const element = await fixture(html `<glide-core-button-group-button value="value"
65
- >Button</glide-core-button-group-button
66
- >`);
67
- const liElement = element.shadowRoot?.querySelector('li');
68
- expect(liElement).to.have.attribute('aria-disabled', 'false');
69
- });
70
- it('has a vertical presention when the "vertical" attribute is set', async () => {
71
- await fixture(html ` <glide-core-button-group-button value="value" vertical
72
- >Button</glide-core-button-group-button
73
- >`);
74
- const buttonElement = document.querySelector('glide-core-button-group-button');
75
- const liElement = buttonElement?.shadowRoot?.querySelector('li');
76
- expect(liElement).to.have.class('vertical');
77
- });
78
- it('does not have a vertical presention when the "vertical" attribute is not set', async () => {
79
- await fixture(html ` <glide-core-button-group-button value="value"
80
- >Button</glide-core-button-group-button
81
- >`);
82
- const buttonElement = document.querySelector('glide-core-button-group-button');
83
- const liElement = buttonElement?.shadowRoot?.querySelector('li');
84
- expect(liElement).to.not.have.class('vertical');
85
- });
86
- it('sets buttons as not tabbable by default', async () => {
87
- const element = await fixture(html `<glide-core-button-group-button value="value"
88
- >Button</glide-core-button-group-button
89
- >`);
90
- const liElement = element.shadowRoot?.querySelector('li');
91
- await expect(liElement).to.have.attribute('tabindex', '-1');
11
+ it('has defaults', async () => {
12
+ const component = await fixture(html `
13
+ <glide-core-button-group-button
14
+ label="Button"
15
+ ></glide-core-button-group-button>
16
+ `);
17
+ expect(component.disabled).to.be.false;
18
+ expect(component.selected).to.be.false;
19
+ expect(component.value).to.equal('');
20
+ expect(component).to.have.attribute('value', '');
21
+ expect(component).to.not.have.attribute('disabled');
22
+ expect(component).to.not.have.attribute('selected');
92
23
  });
93
- it('sets a "selected" button as tabbable', async () => {
94
- const element = await fixture(html `<glide-core-button-group-button value="value" selected
95
- >Button</glide-core-button-group-button
96
- >`);
97
- const liElement = element.shadowRoot?.querySelector('li');
98
- await expect(liElement).to.have.attribute('tabindex', '0');
99
- });
100
- it('has a presentation for variant "icon-only"', async () => {
101
- const element = await fixture(html `<glide-core-button-group-button
102
- value="value"
24
+ it('is accessible', async () => {
25
+ const component = await fixture(html `<glide-core-button-group-button
26
+ label="Button"
27
+ ></glide-core-button-group-button>`);
28
+ await expect(component).to.be.accessible();
29
+ });
30
+ it('can have a label', async () => {
31
+ const component = await fixture(html `<glide-core-button-group-button
32
+ label="Button"
103
33
  selected
104
- variant="icon-only"
105
- ><span slot="prefix">Prefix</span>Button</glide-core-button-group-button
106
- >`);
107
- const liElement = element.shadowRoot?.querySelector('li');
108
- expect(liElement).to.have.class('icon-only');
109
- });
110
- it('does not apply class "icon-only" when variant "icon-only" is absent', async () => {
111
- const element = await fixture(html `<glide-core-button-group-button value="value"
112
- >Button</glide-core-button-group-button
113
- >`);
114
- const liElement = element.shadowRoot?.querySelector('li');
115
- expect(liElement).to.not.have.class('icon-only');
116
- });
117
- it('throws an error when no label is present and variant is `icon-only`', async () => {
118
- const spy = sinon.spy();
119
- try {
120
- await fixture(html `<glide-core-button-group-button
121
- value="value"
122
- selected
123
- variant="icon-only"
124
- ><span slot="prefix">Prefix</span></glide-core-button-group-button
125
- >`);
126
- }
127
- catch (error) {
128
- if (error instanceof ArgumentError) {
129
- spy();
130
- }
131
- }
132
- expect(spy.called).to.be.true;
133
- });
134
- it('throws an error when prefix slot is empty and variant is `icon-only`', async () => {
34
+ ></glide-core-button-group-button>`);
35
+ expect(component.shadowRoot?.textContent?.trim()).to.equal('Button');
36
+ });
37
+ it('can have a "prefix" slot', async () => {
38
+ const component = await fixture(html `<glide-core-button-group-button label="Button">
39
+ <span slot="prefix">Prefix</span>
40
+ </glide-core-button-group-button>`);
41
+ const assignedElements = component.shadowRoot
42
+ ?.querySelector('slot[name="prefix"]')
43
+ ?.assignedElements();
44
+ expect(assignedElements?.at(0)?.textContent).to.equal('Prefix');
45
+ });
46
+ it('sets `aria-checked` when selected', async () => {
47
+ const component = await fixture(html `<glide-core-button-group-button
48
+ label="Button"
49
+ selected
50
+ ></glide-core-button-group-button>`);
51
+ const radio = component.shadowRoot?.querySelector('[role="radio"]');
52
+ expect(radio).to.have.attribute('aria-checked', 'true');
53
+ });
54
+ it('sets `aria-checked` when not selected', async () => {
55
+ const component = await fixture(html `<glide-core-button-group-button
56
+ label="Button"
57
+ ></glide-core-button-group-button>`);
58
+ const radio = component.shadowRoot?.querySelector('[role="radio"]');
59
+ expect(radio).to.have.attribute('aria-checked', 'false');
60
+ });
61
+ it('sets `aria-disabled` when disabled', async () => {
62
+ const component = await fixture(html `<glide-core-button-group-button
63
+ label="Button"
64
+ disabled
65
+ ></glide-core-button-group-button>`);
66
+ const radio = component.shadowRoot?.querySelector('[role="radio"]');
67
+ expect(radio).to.have.attribute('aria-disabled', 'true');
68
+ });
69
+ it('sets `aria-disabled` when not disabled', async () => {
70
+ const component = await fixture(html `<glide-core-button-group-button
71
+ label="Button"
72
+ ></glide-core-button-group-button>`);
73
+ const radio = component.shadowRoot?.querySelector('[role="radio"]');
74
+ expect(radio).to.have.attribute('aria-disabled', 'false');
75
+ });
76
+ it('is tabbable when selected', async () => {
77
+ const component = await fixture(html `<glide-core-button-group-button
78
+ label="Button"
79
+ selected
80
+ ></glide-core-button-group-button>`);
81
+ const radio = component.shadowRoot?.querySelector('[role="radio"]');
82
+ expect(radio).to.have.attribute('tabindex', '0');
83
+ });
84
+ it('is not tabbable when not selected', async () => {
85
+ const component = await fixture(html `<glide-core-button-group-button
86
+ label="Button"
87
+ ></glide-core-button-group-button>`);
88
+ const radio = component.shadowRoot?.querySelector('[role="radio"]');
89
+ expect(radio).to.have.attribute('tabindex', '-1');
90
+ });
91
+ it('throws when icon-only and no "prefix" slot', async () => {
135
92
  const spy = sinon.spy();
136
- // Not sure how to resolve the below, despite the test passing, so disabling console errors for now:
137
- //
138
- // Browser logs:
139
- // An error was thrown in a Promise outside a test. Did you forget to await a function or assertion?
140
- sinon.stub(console, 'error'); // Disable console.error
141
93
  try {
142
94
  await fixture(html `<glide-core-button-group-button
143
95
  value="value"
144
96
  selected
145
- variant="icon-only"
146
- >Button</glide-core-button-group-button
147
- >`);
148
- }
149
- catch (error) {
150
- if (error instanceof ArgumentError) {
151
- spy();
152
- }
153
- }
154
- expect(spy.called).to.be.true;
155
- });
156
- it('does not throw an error when prefix slot is empty and no variant is set', async () => {
157
- const spy = sinon.spy();
158
- try {
159
- await fixture(html `<glide-core-button-group-button value="value" selected
160
- >Button</glide-core-button-group-button
161
- >`);
97
+ privateVariant="icon-only"
98
+ ></glide-core-button-group-button>`);
162
99
  }
163
100
  catch (error) {
164
101
  if (error instanceof ArgumentError) {
165
102
  spy();
166
103
  }
167
104
  }
168
- expect(spy.notCalled).to.be.true;
105
+ expect(spy.callCount).to.equal(1);
169
106
  });