@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
@@ -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,4 +1,5 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-expressions */
2
+ import './button-group.button.js';
2
3
  import { ArgumentError } from 'ow';
3
4
  import { expect, fixture, html } from '@open-wc/testing';
4
5
  import GlideCoreButtonGroupButton from './button-group.button.js';
@@ -7,164 +8,99 @@ GlideCoreButtonGroupButton.shadowRootOptions.mode = 'open';
7
8
  it('registers', async () => {
8
9
  expect(window.customElements.get('glide-core-button-group-button')).to.equal(GlideCoreButtonGroupButton);
9
10
  });
10
- it('is accessible', async () => {
11
- const element = await fixture(html `<glide-core-button-group-button value="value"
12
- >Button</glide-core-button-group-button
13
- >`);
14
- await expect(element).to.be.accessible();
15
- });
16
- it('renders an li with role "radio"', async () => {
17
- const element = await fixture(html `<glide-core-button-group-button value="value"
18
- >Button</glide-core-button-group-button
19
- >`);
20
- const liElement = element.shadowRoot?.querySelector('li');
21
- expect(liElement).to.have.attribute('role', 'radio');
22
- });
23
- it('renders "aria-checked" equal to "false" and "tabindex" equal to "-1" by default', async () => {
24
- const element = await fixture(html `<glide-core-button-group-button value="value"
25
- >Button</glide-core-button-group-button
26
- >`);
27
- const liElement = element.shadowRoot?.querySelector('li');
28
- expect(liElement).to.not.be.null;
29
- expect(liElement).to.have.attribute('aria-checked', 'false');
30
- expect(liElement).to.have.attribute('tabindex', '-1');
31
- });
32
- it('renders "aria-checked" equal to "true" and "tabindex" equal to "0" when attribute "selected" exists', async () => {
33
- const element = await fixture(html `<glide-core-button-group-button value="value" selected
34
- >Button</glide-core-button-group-button
35
- >`);
36
- const liElement = element.shadowRoot?.querySelector('li');
37
- expect(liElement).to.have.attribute('aria-checked', 'true');
38
- expect(liElement).to.have.attribute('tabindex', '0');
39
- });
40
- it('renders two slots, where one has name "prefix", and the other is default with given text', async () => {
41
- const element = await fixture(html `<glide-core-button-group-button value="value" selected
42
- ><span slot="prefix" data-prefix>Prefix</span
43
- >Button</glide-core-button-group-button
44
- >`);
45
- const prefixSlot = element.shadowRoot?.querySelector('slot[name="prefix"]');
46
- const defaultSlot = element.shadowRoot?.querySelector('slot:not([name="prefix"])');
47
- // verify the slots exist
48
- expect(prefixSlot).to.not.be.null;
49
- expect(defaultSlot).to.not.be.null;
50
- const prefixContent = document.querySelector('[data-prefix]');
51
- const content = document.querySelector('glide-core-button-group-button');
52
- // verify the content of the slots exist
53
- expect(prefixContent).to.not.be.null;
54
- expect(prefixContent?.textContent).to.equal('Prefix');
55
- expect(content?.textContent).to.contain('Button');
56
- });
57
- it('is has a disabled presentation when the "disabled" attribute is true', async () => {
58
- const element = await fixture(html `<glide-core-button-group-button value="value" disabled
59
- >Button</glide-core-button-group-button
60
- >`);
61
- const liElement = element.shadowRoot?.querySelector('li');
62
- expect(liElement).to.have.attribute('aria-disabled', 'true');
63
- });
64
- it('does not have a disabled presentation when "disabled" attribute is false', async () => {
65
- const element = await fixture(html `<glide-core-button-group-button value="value"
66
- >Button</glide-core-button-group-button
67
- >`);
68
- const liElement = element.shadowRoot?.querySelector('li');
69
- expect(liElement).to.have.attribute('aria-disabled', 'false');
70
- });
71
- it('has a vertical presention when the "vertical" attribute is set', async () => {
72
- await fixture(html ` <glide-core-button-group-button value="value" vertical
73
- >Button</glide-core-button-group-button
74
- >`);
75
- const buttonElement = document.querySelector('glide-core-button-group-button');
76
- const liElement = buttonElement?.shadowRoot?.querySelector('li');
77
- expect(liElement).to.have.class('vertical');
78
- });
79
- it('does not have a vertical presention when the "vertical" attribute is not set', async () => {
80
- await fixture(html ` <glide-core-button-group-button value="value"
81
- >Button</glide-core-button-group-button
82
- >`);
83
- const buttonElement = document.querySelector('glide-core-button-group-button');
84
- const liElement = buttonElement?.shadowRoot?.querySelector('li');
85
- expect(liElement).to.not.have.class('vertical');
86
- });
87
- it('sets buttons as not tabbable by default', async () => {
88
- const element = await fixture(html `<glide-core-button-group-button value="value"
89
- >Button</glide-core-button-group-button
90
- >`);
91
- const liElement = element.shadowRoot?.querySelector('li');
92
- 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');
93
23
  });
94
- it('sets a "selected" button as tabbable', async () => {
95
- const element = await fixture(html `<glide-core-button-group-button value="value" selected
96
- >Button</glide-core-button-group-button
97
- >`);
98
- const liElement = element.shadowRoot?.querySelector('li');
99
- await expect(liElement).to.have.attribute('tabindex', '0');
100
- });
101
- it('has a presentation for variant "icon-only"', async () => {
102
- const element = await fixture(html `<glide-core-button-group-button
103
- 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"
104
33
  selected
105
- variant="icon-only"
106
- ><span slot="prefix">Prefix</span>Button</glide-core-button-group-button
107
- >`);
108
- const liElement = element.shadowRoot?.querySelector('li');
109
- expect(liElement).to.have.class('icon-only');
110
- });
111
- it('does not apply class "icon-only" when variant "icon-only" is absent', async () => {
112
- const element = await fixture(html `<glide-core-button-group-button value="value"
113
- >Button</glide-core-button-group-button
114
- >`);
115
- const liElement = element.shadowRoot?.querySelector('li');
116
- expect(liElement).to.not.have.class('icon-only');
117
- });
118
- it('throws an error when no label is present and variant is `icon-only`', async () => {
119
- const spy = sinon.spy();
120
- try {
121
- await fixture(html `<glide-core-button-group-button
122
- value="value"
123
- selected
124
- variant="icon-only"
125
- ><span slot="prefix">Prefix</span></glide-core-button-group-button
126
- >`);
127
- }
128
- catch (error) {
129
- if (error instanceof ArgumentError) {
130
- spy();
131
- }
132
- }
133
- expect(spy.called).to.be.true;
134
- });
135
- 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 () => {
136
92
  const spy = sinon.spy();
137
- // Not sure how to resolve the below, despite the test passing, so disabling console errors for now:
138
- //
139
- // Browser logs:
140
- // An error was thrown in a Promise outside a test. Did you forget to await a function or assertion?
141
- sinon.stub(console, 'error'); // Disable console.error
142
93
  try {
143
94
  await fixture(html `<glide-core-button-group-button
144
95
  value="value"
145
96
  selected
146
- variant="icon-only"
147
- >Button</glide-core-button-group-button
148
- >`);
149
- }
150
- catch (error) {
151
- if (error instanceof ArgumentError) {
152
- spy();
153
- }
154
- }
155
- expect(spy.called).to.be.true;
156
- });
157
- it('does not throw an error when prefix slot is empty and no variant is set', async () => {
158
- const spy = sinon.spy();
159
- try {
160
- await fixture(html `<glide-core-button-group-button value="value" selected
161
- >Button</glide-core-button-group-button
162
- >`);
97
+ privateVariant="icon-only"
98
+ ></glide-core-button-group-button>`);
163
99
  }
164
100
  catch (error) {
165
101
  if (error instanceof ArgumentError) {
166
102
  spy();
167
103
  }
168
104
  }
169
- expect(spy.notCalled).to.be.true;
105
+ expect(spy.callCount).to.equal(1);
170
106
  });
@@ -1,74 +1,15 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-expressions */
2
2
  import './button-group.button.js';
3
3
  import { expect, fixture, html, oneEvent } from '@open-wc/testing';
4
- import { sendKeys } from '@web/test-runner-commands';
5
4
  import GlideCoreButtonGroupButton from './button-group.button.js';
6
- import sinon from 'sinon';
7
- GlideCoreButtonGroupButton.shadowRootOptions.mode = 'open';
8
- it('emits a change event when clicked', async () => {
9
- const element = await fixture(html `<glide-core-button-group-button value="value"
10
- >Button</glide-core-button-group-button
11
- >`);
12
- const liElement = element.shadowRoot?.querySelector('li');
13
- // This pattern is adopted from https://open-wc.org/docs/testing/helpers/#testing-events
14
- // Without setTimeout the test fails. The suspicion is this is related to task scheduling,
15
- // 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>`);
16
9
  setTimeout(() => {
17
- liElement?.click();
10
+ component.selected = true;
18
11
  });
19
- const changeEvent = await oneEvent(element, 'change');
20
- expect(changeEvent instanceof Event).to.be.true;
21
- });
22
- it('emits an input event when clicked', 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
- setTimeout(() => {
28
- liElement?.click();
29
- });
30
- const inputEvent = await oneEvent(element, 'input');
31
- expect(inputEvent instanceof Event).to.be.true;
32
- });
33
- it('emits a change event when a space key is pressed and is not already selected', async () => {
34
- const buttonElement = await fixture(html `<glide-core-button-group-button value="value-1"
35
- >Button 1</glide-core-button-group-button
36
- >`);
37
- buttonElement.focus();
38
- setTimeout(async () => {
39
- await sendKeys({ press: ' ' });
40
- });
41
- const changeEvent = await oneEvent(buttonElement, 'change');
42
- expect(changeEvent instanceof Event).to.be.true;
43
- });
44
- it('does not emit change event when a space key is pressed and is selected', async () => {
45
- const buttonElement = await fixture(html `<glide-core-button-group-button value="value-1" selected
46
- >Button 1</glide-core-button-group-button
47
- > `);
48
- const spy = sinon.spy();
49
- buttonElement.addEventListener('change', spy);
50
- buttonElement.focus();
51
- await sendKeys({ press: ' ' });
52
- expect(spy.notCalled).to.be.true;
53
- });
54
- it('emits an input event when a space key is pressed and is not already selected', async () => {
55
- const buttonElement = await fixture(html ` <glide-core-button-group-button value="value-1"
56
- >Button 1</glide-core-button-group-button
57
- >`);
58
- buttonElement.focus();
59
- setTimeout(async () => {
60
- await sendKeys({ press: ' ' });
61
- });
62
- const inputEvent = await oneEvent(buttonElement, 'input');
63
- expect(inputEvent instanceof Event).to.be.true;
64
- });
65
- it('does not emit an input event when a space key is pressed and is selected', async () => {
66
- const buttonElement = await fixture(html `<glide-core-button-group-button value="value-1" selected
67
- >Button 1</glide-core-button-group-button
68
- >`);
69
- buttonElement.focus();
70
- const spy = sinon.spy();
71
- buttonElement.addEventListener('input', spy);
72
- await sendKeys({ press: ' ' });
73
- 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;
74
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
+ });