@crowdstrike/glide-core 0.5.0 → 0.5.2

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 (208) hide show
  1. package/dist/accordion.js +1 -1
  2. package/dist/accordion.styles.js +4 -4
  3. package/dist/accordion.test.basics.js +109 -0
  4. package/dist/accordion.test.events.js +39 -0
  5. package/dist/button-group.button.js +1 -1
  6. package/dist/button-group.button.styles.js +4 -4
  7. package/dist/button-group.button.test.basics.js +169 -0
  8. package/dist/button-group.button.test.events.js +73 -0
  9. package/dist/button-group.js +1 -1
  10. package/dist/button-group.styles.js +3 -3
  11. package/dist/button-group.test.basics.js +268 -0
  12. package/dist/button-group.test.events.js +291 -0
  13. package/dist/button.js +1 -1
  14. package/dist/button.styles.js +4 -4
  15. package/dist/button.test.basics.js +196 -0
  16. package/dist/button.test.events.js +25 -0
  17. package/dist/button.test.form.js +49 -0
  18. package/dist/checkbox-group.js +1 -1
  19. package/dist/checkbox-group.styles.js +2 -2
  20. package/dist/checkbox-group.test.basics.js +119 -0
  21. package/dist/checkbox-group.test.events.js +110 -0
  22. package/dist/checkbox-group.test.focus.js +45 -0
  23. package/dist/checkbox-group.test.form.js +130 -0
  24. package/dist/checkbox-group.test.validity.js +75 -0
  25. package/dist/checkbox.js +1 -1
  26. package/dist/checkbox.styles.js +3 -3
  27. package/dist/checkbox.test.basics.js +89 -0
  28. package/dist/checkbox.test.events.js +87 -0
  29. package/dist/checkbox.test.focus.js +38 -0
  30. package/dist/checkbox.test.form.js +115 -0
  31. package/dist/checkbox.test.states.js +62 -0
  32. package/dist/checkbox.test.validity.js +51 -0
  33. package/dist/drawer.d.ts +2 -2
  34. package/dist/drawer.js +1 -15
  35. package/dist/drawer.styles.js +18 -3
  36. package/dist/drawer.test.accessibility.js +22 -0
  37. package/dist/drawer.test.basics.js +43 -0
  38. package/dist/drawer.test.closing.js +37 -0
  39. package/dist/drawer.test.events.js +52 -0
  40. package/dist/drawer.test.methods.js +34 -0
  41. package/dist/dropdown.d.ts +4 -2
  42. package/dist/dropdown.js +1 -1
  43. package/dist/dropdown.option.d.ts +1 -3
  44. package/dist/dropdown.option.js +1 -1
  45. package/dist/dropdown.option.styles.js +2 -2
  46. package/dist/dropdown.option.test.basics.js +59 -0
  47. package/dist/dropdown.option.test.basics.multiple.js +26 -0
  48. package/dist/dropdown.option.test.basics.single.js +20 -0
  49. package/dist/dropdown.option.test.events.js +27 -0
  50. package/dist/dropdown.option.test.focus.js +11 -0
  51. package/dist/dropdown.option.test.interactions.multiple.js +87 -0
  52. package/dist/dropdown.option.test.interactions.single.js +22 -0
  53. package/dist/dropdown.styles.js +28 -9
  54. package/dist/dropdown.test.basics.filterable.js +84 -0
  55. package/dist/dropdown.test.basics.js +233 -0
  56. package/dist/dropdown.test.basics.multiple.js +270 -0
  57. package/dist/dropdown.test.basics.single.js +79 -0
  58. package/dist/dropdown.test.events.js +268 -0
  59. package/dist/dropdown.test.events.multiple.js +130 -0
  60. package/dist/dropdown.test.focus.d.ts +1 -0
  61. package/dist/dropdown.test.focus.filterable.js +154 -0
  62. package/dist/dropdown.test.focus.js +18 -0
  63. package/dist/dropdown.test.focus.multiple.js +181 -0
  64. package/dist/dropdown.test.focus.single.js +53 -0
  65. package/dist/dropdown.test.form.js +140 -0
  66. package/dist/dropdown.test.form.multiple.js +149 -0
  67. package/dist/dropdown.test.form.single.js +128 -0
  68. package/dist/dropdown.test.interactions.filterable.js +385 -0
  69. package/dist/dropdown.test.interactions.js +446 -0
  70. package/dist/dropdown.test.interactions.multiple.js +908 -0
  71. package/dist/dropdown.test.interactions.single.js +466 -0
  72. package/dist/dropdown.test.validity.js +46 -0
  73. package/dist/icon-button.js +1 -1
  74. package/dist/icon-button.styles.js +3 -3
  75. package/dist/icon-button.test.basics.js +103 -0
  76. package/dist/icons/checked.js +1 -1
  77. package/dist/icons/magnifying-glass.js +1 -1
  78. package/dist/input.js +1 -1
  79. package/dist/input.styles.js +3 -3
  80. package/dist/input.test.basics.js +169 -0
  81. package/dist/input.test.events.js +97 -0
  82. package/dist/input.test.focus.js +54 -0
  83. package/dist/input.test.form.js +56 -0
  84. package/dist/input.test.validity.js +50 -0
  85. package/dist/label.js +1 -1
  86. package/dist/label.styles.js +3 -3
  87. package/dist/label.test.basics.js +129 -0
  88. package/dist/library/expect-argument-error.js +1 -1
  89. package/dist/library/ow.js +1 -1
  90. package/dist/library/ow.test.js +55 -0
  91. package/dist/menu.button.d.ts +1 -2
  92. package/dist/menu.button.js +1 -1
  93. package/dist/menu.button.styles.js +3 -3
  94. package/dist/menu.button.test.basics.js +42 -0
  95. package/dist/menu.d.ts +4 -0
  96. package/dist/menu.js +1 -1
  97. package/dist/menu.link.d.ts +1 -2
  98. package/dist/menu.link.js +1 -1
  99. package/dist/menu.link.styles.js +3 -3
  100. package/dist/menu.link.test.basics.js +46 -0
  101. package/dist/menu.styles.js +13 -6
  102. package/dist/menu.test.basics.js +161 -0
  103. package/dist/menu.test.focus.d.ts +0 -1
  104. package/dist/menu.test.focus.js +66 -0
  105. package/dist/menu.test.interactions.d.ts +0 -1
  106. package/dist/menu.test.interactions.js +522 -0
  107. package/dist/modal.icon-button.js +1 -1
  108. package/dist/modal.icon-button.styles.js +2 -2
  109. package/dist/modal.icon-button.test.basics.js +45 -0
  110. package/dist/modal.js +1 -15
  111. package/dist/modal.styles.js +4 -4
  112. package/dist/modal.tertiary-icon.js +1 -1
  113. package/dist/modal.tertiary-icon.test.basics.js +59 -0
  114. package/dist/modal.test.accessibility.js +48 -0
  115. package/dist/modal.test.basics.js +203 -0
  116. package/dist/modal.test.close.js +38 -0
  117. package/dist/modal.test.events.js +110 -0
  118. package/dist/modal.test.lock-scroll.js +76 -0
  119. package/dist/modal.test.methods.js +23 -0
  120. package/dist/modal.test.scrollbars.js +19 -0
  121. package/dist/radio-group.js +1 -1
  122. package/dist/radio-group.styles.js +2 -2
  123. package/dist/radio-group.test.basics.js +323 -0
  124. package/dist/radio-group.test.events.js +277 -0
  125. package/dist/radio-group.test.focus.js +75 -0
  126. package/dist/radio-group.test.form.js +104 -0
  127. package/dist/radio-group.test.validity.js +228 -0
  128. package/dist/radio.js +1 -1
  129. package/dist/radio.styles.js +4 -4
  130. package/dist/split-button.d.ts +24 -0
  131. package/dist/split-button.js +1 -0
  132. package/dist/split-button.stories.d.ts +17 -0
  133. package/dist/split-button.styles.d.ts +2 -0
  134. package/dist/split-button.styles.js +103 -0
  135. package/dist/split-button.test.basics.d.ts +1 -0
  136. package/dist/split-button.test.basics.js +84 -0
  137. package/dist/split-container.d.ts +30 -0
  138. package/dist/split-container.js +1 -0
  139. package/dist/split-container.styles.d.ts +2 -0
  140. package/dist/split-container.styles.js +132 -0
  141. package/dist/split-container.test.basics.d.ts +3 -0
  142. package/dist/split-container.test.basics.js +445 -0
  143. package/dist/split-container.test.interactions.d.ts +1 -0
  144. package/dist/split-container.test.interactions.js +20 -0
  145. package/dist/split-link.d.ts +25 -0
  146. package/dist/split-link.js +1 -0
  147. package/dist/split-link.test.basics.d.ts +1 -0
  148. package/dist/split-link.test.basics.js +92 -0
  149. package/dist/split-link.test.interactions.d.ts +1 -0
  150. package/dist/split-link.test.interactions.js +19 -0
  151. package/dist/status-indicator.js +1 -1
  152. package/dist/status-indicator.styles.js +2 -2
  153. package/dist/status-indicator.test.basics.js +102 -0
  154. package/dist/styles/focus-outline.js +1 -4
  155. package/dist/styles/visually-hidden.js +1 -11
  156. package/dist/tab.group.js +1 -1
  157. package/dist/tab.group.styles.js +2 -2
  158. package/dist/tab.group.test.basics.js +185 -0
  159. package/dist/tab.js +1 -1
  160. package/dist/tab.panel.js +1 -1
  161. package/dist/tab.panel.styles.js +3 -3
  162. package/dist/tab.styles.js +2 -2
  163. package/dist/tab.test.basics.js +71 -0
  164. package/dist/tag.js +1 -1
  165. package/dist/tag.styles.js +3 -3
  166. package/dist/tag.test.basics.js +118 -0
  167. package/dist/tag.test.events.js +16 -0
  168. package/dist/tag.test.focus.js +11 -0
  169. package/dist/textarea.js +2 -2
  170. package/dist/textarea.styles.js +3 -3
  171. package/dist/textarea.test.basics.js +140 -0
  172. package/dist/textarea.test.events.js +204 -0
  173. package/dist/textarea.test.form.js +70 -0
  174. package/dist/textarea.test.validity.js +83 -0
  175. package/dist/toasts.js +1 -1
  176. package/dist/toasts.styles.js +2 -2
  177. package/dist/toasts.test.basics.js +94 -0
  178. package/dist/toasts.toast.js +1 -1
  179. package/dist/toasts.toast.styles.js +5 -2
  180. package/dist/toasts.toast.test.basics.js +139 -0
  181. package/dist/toggle.js +1 -1
  182. package/dist/toggle.styles.js +3 -3
  183. package/dist/toggle.test.basics.js +64 -0
  184. package/dist/toggle.test.events.js +29 -0
  185. package/dist/toggle.test.focus.js +9 -0
  186. package/dist/toggle.test.states.js +35 -0
  187. package/dist/tooltip.js +1 -1
  188. package/dist/tooltip.styles.js +3 -3
  189. package/dist/tooltip.test.basics.js +64 -0
  190. package/dist/tooltip.test.interactions.js +78 -0
  191. package/dist/tree.item.icon-button.js +1 -1
  192. package/dist/tree.item.icon-button.styles.js +2 -2
  193. package/dist/tree.item.icon-button.test.basics.js +13 -0
  194. package/dist/tree.item.js +1 -1
  195. package/dist/tree.item.menu.js +1 -1
  196. package/dist/tree.item.menu.styles.js +2 -2
  197. package/dist/tree.item.menu.test.basics.js +34 -0
  198. package/dist/tree.item.styles.js +2 -2
  199. package/dist/tree.item.test.basics.js +102 -0
  200. package/dist/tree.js +1 -1
  201. package/dist/tree.styles.js +2 -2
  202. package/dist/tree.test.aria.js +86 -0
  203. package/dist/tree.test.basics.js +123 -0
  204. package/dist/tree.test.events.js +19 -0
  205. package/dist/tree.test.focus.js +261 -0
  206. package/package.json +20 -18
  207. /package/dist/{dropdown.option.test.focus.multiple.d.ts → dropdown.option.test.focus.d.ts} +0 -0
  208. /package/dist/{dropdown.option.test.focus.single.d.ts → dropdown.test.events.multiple.d.ts} +0 -0
@@ -1,4 +1,4 @@
1
- "use strict";import{css as i}from"lit";import e from"./styles/focus-outline.js";export default[i`
1
+ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";export default[css`
2
2
  /* When browser support improves, we can have nicer animations with https://caniuse.com/mdn-css_at-rules_starting-style */
3
3
  @keyframes backdrop-fade-in {
4
4
  from {
@@ -43,7 +43,7 @@
43
43
  }
44
44
 
45
45
  &:focus-visible {
46
- ${e};
46
+ ${focusOutline};
47
47
  }
48
48
 
49
49
  &::backdrop {
@@ -130,7 +130,7 @@
130
130
  }
131
131
 
132
132
  &:focus-visible {
133
- ${e};
133
+ ${focusOutline};
134
134
  }
135
135
  }
136
136
 
@@ -166,4 +166,4 @@
166
166
  .align-center {
167
167
  align-items: center;
168
168
  }
169
- `];
169
+ `];
@@ -1 +1 @@
1
- "use strict";var E=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var r=(e,t,o,m)=>{for(var i=m>1?void 0:m?R(t,o):t,n=e.length-1,f;n>=0;n--)(f=e[n])&&(i=(m?f(t,o,i):f(i))||i);return m&&i&&E(t,o,i),i};var c=(e,t,o)=>{if(!t.has(e))throw TypeError("Cannot "+o)};var s=(e,t,o)=>(c(e,t,"read from private field"),o?o.call(e):t.get(e)),h=(e,t,o)=>{if(t.has(e))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(e):t.set(e,o)};var d=(e,t,o)=>(c(e,t,"access private method"),o);var a,p,v;import"./tooltip.js";import{LitElement as g,html as S}from"lit";import{createRef as $,ref as w}from"lit/directives/ref.js";import{customElement as x,property as b}from"lit/decorators.js";import{ifDefined as L}from"lit/directives/if-defined.js";import{owSlot as u}from"./library/ow.js";let l=class extends g{constructor(){super(...arguments);h(this,p);this.tooltipPlacement="bottom";h(this,a,$())}firstUpdated(){u(s(this,a).value)}render(){return S`<glide-core-tooltip placement="${this.tooltipPlacement}">${this.label} <span tabindex="0" aria-label="${L(this.label)}" slot="target"><slot @slotchange="${d(this,p,v)}" ${w(s(this,a))}></slot></span></glide-core-tooltip>`}};a=new WeakMap,p=new WeakSet,v=function(){u(s(this,a).value)},l.shadowRootOptions={...g.shadowRootOptions,mode:"closed"},r([b()],l.prototype,"label",2),r([b({attribute:"tooltip-placement"})],l.prototype,"tooltipPlacement",2),l=r([x("glide-core-modal-tertiary-icon")],l);export{l as default};
1
+ var __decorate=this&&this.__decorate||function(e,t,o,l){var r,i=arguments.length,a=i<3?t:null===l?l=Object.getOwnPropertyDescriptor(t,o):l;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,o,l);else for(var d=e.length-1;d>=0;d--)(r=e[d])&&(a=(i<3?r(a):i>3?r(t,o,a):r(t,o))||a);return i>3&&a&&Object.defineProperty(t,o,a),a};import"./tooltip.js";import{LitElement,html}from"lit";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property}from"lit/decorators.js";import{ifDefined}from"lit/directives/if-defined.js";import{owSlot}from"./library/ow.js";let GlideCoreModalTertiaryIcon=class GlideCoreModalTertiaryIcon extends LitElement{constructor(){super(...arguments),this.tooltipPlacement="bottom",this.#e=createRef()}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}firstUpdated(){owSlot(this.#e.value)}render(){return html`<glide-core-tooltip placement="${this.tooltipPlacement}">${this.label} <span tabindex="0" aria-label="${ifDefined(this.label)}" slot="target"><slot @slotchange="${this.#t}" ${ref(this.#e)}></slot></span></glide-core-tooltip>`}#e;#t(){owSlot(this.#e.value)}};__decorate([property()],GlideCoreModalTertiaryIcon.prototype,"label",void 0),__decorate([property({attribute:"tooltip-placement"})],GlideCoreModalTertiaryIcon.prototype,"tooltipPlacement",void 0),GlideCoreModalTertiaryIcon=__decorate([customElement("glide-core-modal-tertiary-icon")],GlideCoreModalTertiaryIcon);export default GlideCoreModalTertiaryIcon;
@@ -0,0 +1,59 @@
1
+ import './button.js';
2
+ import { ArgumentError } from 'ow';
3
+ import { expect, fixture, html } from '@open-wc/testing';
4
+ import GlideCoreModalTertiaryIcon from './modal.tertiary-icon.js';
5
+ import sinon from 'sinon';
6
+ GlideCoreModalTertiaryIcon.shadowRootOptions.mode = 'open';
7
+ it('registers', async () => {
8
+ expect(window.customElements.get('glide-core-modal-tertiary-icon')).to.equal(GlideCoreModalTertiaryIcon);
9
+ });
10
+ it('is accessible', async () => {
11
+ const element = await fixture(html `<glide-core-modal-tertiary-icon>Test</glide-core-modal-tertiary-icon>`);
12
+ await expect(element).to.be.accessible();
13
+ });
14
+ it('renders and sets default attributes', async () => {
15
+ const element = await fixture(html `
16
+ <glide-core-modal-tertiary-icon>Test</glide-core-modal-tertiary-icon>
17
+ `);
18
+ expect(element).to.be.ok;
19
+ const spanTag = element.shadowRoot?.querySelector('span');
20
+ expect(spanTag?.getAttribute('tabindex')).to.equal('0');
21
+ const toolip = element.shadowRoot?.querySelector('glide-core-tooltip');
22
+ expect(toolip).to.not.be.null;
23
+ });
24
+ it('adds an accessible label when given', async () => {
25
+ const element = await fixture(html `<glide-core-modal-tertiary-icon label="test-label"
26
+ >Test</glide-core-modal-tertiary-icon
27
+ >`);
28
+ const spanElement = element.shadowRoot?.querySelector('span');
29
+ expect(spanElement).to.have.attribute('aria-label', 'test-label');
30
+ });
31
+ it('does not add an acceessible label when not given', async () => {
32
+ const element = await fixture(html `<glide-core-modal-tertiary-icon>Test</glide-core-modal-tertiary-icon>`);
33
+ const spanElement = element.shadowRoot?.querySelector('span');
34
+ expect(spanElement).to.not.have.attribute('aria-label');
35
+ });
36
+ it('sets the tooltip placement when attribute "tooltip-placement" is given', async () => {
37
+ const element = await fixture(html `<glide-core-modal-tertiary-icon tooltip-placement="right"
38
+ >Test</glide-core-modal-tertiary-icon
39
+ >`);
40
+ const toolTip = element.shadowRoot?.querySelector('glide-core-tooltip');
41
+ expect(toolTip).to.have.attribute('placement', 'right');
42
+ });
43
+ it('sets the tooltip placement to "bottom" when attribute "tooltip-placement" is not given', async () => {
44
+ const element = await fixture(html `<glide-core-modal-tertiary-icon>Test</glide-core-modal-tertiary-icon>`);
45
+ const toolTip = element.shadowRoot?.querySelector('glide-core-tooltip');
46
+ expect(toolTip).to.have.attribute('placement', 'bottom');
47
+ });
48
+ it('throws if it does not have a default slot', async () => {
49
+ const spy = sinon.spy();
50
+ try {
51
+ await fixture(html `<glide-core-modal-tertiary-icon></glide-core-modal-tertiary-icon>`);
52
+ }
53
+ catch (error) {
54
+ if (error instanceof ArgumentError) {
55
+ spy();
56
+ }
57
+ }
58
+ expect(spy.called).to.be.true;
59
+ });
@@ -0,0 +1,48 @@
1
+ import { expect, fixture, html } from '@open-wc/testing';
2
+ import GlideCoreModal from './modal.js';
3
+ GlideCoreModal.shadowRootOptions.mode = 'open';
4
+ it('is accessible', async () => {
5
+ const element = await fixture(html `<glide-core-modal label="Modal title">
6
+ Modal Content
7
+ </glide-core-modal>`);
8
+ await expect(element).to.be.accessible();
9
+ });
10
+ it('focuses the dialog upon opening', async () => {
11
+ const element = await fixture(html `<glide-core-modal label="Modal title">
12
+ Modal Content
13
+ </glide-core-modal>`);
14
+ element.showModal();
15
+ const dialogElement = element.shadowRoot?.querySelector('dialog');
16
+ expect(element.shadowRoot?.activeElement).to.equal(dialogElement);
17
+ });
18
+ it('sets the tabindex on the dialog to "-1"', async () => {
19
+ const element = await fixture(html `<glide-core-modal label="Modal title">
20
+ Modal Content
21
+ </glide-core-modal>`);
22
+ element.showModal();
23
+ const dialogElement = element.shadowRoot?.querySelector('dialog');
24
+ expect(dialogElement?.getAttribute('tabindex')).to.equal('-1');
25
+ });
26
+ it('sets the "toolbar" role on the header-actions section', async () => {
27
+ const element = await fixture(html `<glide-core-modal label="Modal title">
28
+ Modal Content
29
+ </glide-core-modal>`);
30
+ element.showModal();
31
+ const toolbar = element.shadowRoot?.querySelector('[role="toolbar"');
32
+ expect(toolbar).to.be.ok;
33
+ });
34
+ it('sets proper aria attributes and roles on the article', async () => {
35
+ const element = await fixture(html `<glide-core-modal label="Modal title">
36
+ Modal Content
37
+ </glide-core-modal>`);
38
+ element.showModal();
39
+ const content = element.shadowRoot?.querySelector('[role="region"');
40
+ expect(content).to.be.ok;
41
+ expect(content?.tagName).to.equal('ARTICLE');
42
+ expect(content?.getAttribute('aria-labelledby')).to.equal('heading');
43
+ expect(content?.getAttribute('tabindex')).to.equal('0');
44
+ // Verify the aria-labelledby id attribute is found
45
+ const label = element.shadowRoot?.querySelector('#heading');
46
+ expect(label).to.be.ok;
47
+ expect(label?.tagName).to.equal('H2');
48
+ });
@@ -0,0 +1,203 @@
1
+ import './button.js';
2
+ import './modal.icon-button.js';
3
+ import './modal.js';
4
+ import { ArgumentError } from 'ow';
5
+ import { expect, fixture, html } from '@open-wc/testing';
6
+ import GlideCoreModal from './modal.js';
7
+ import expectArgumentError from './library/expect-argument-error.js';
8
+ import sinon from 'sinon';
9
+ GlideCoreModal.shadowRootOptions.mode = 'open';
10
+ it('registers', async () => {
11
+ expect(window.customElements.get('glide-core-modal')).to.equal(GlideCoreModal);
12
+ });
13
+ it('is closed by default', async () => {
14
+ const element = await fixture(html `<glide-core-modal label="Modal title">
15
+ Modal Content
16
+ </glide-core-modal>`);
17
+ const dialog = element.shadowRoot?.querySelector('dialog');
18
+ expect(dialog).to.be.ok;
19
+ expect(dialog?.hasAttribute('open')).to.be.false;
20
+ });
21
+ it('renders the provided "label"', async () => {
22
+ const element = await fixture(html `<glide-core-modal label="Modal title">
23
+ Modal Content
24
+ </glide-core-modal>`);
25
+ element.showModal();
26
+ const label = element.shadowRoot?.querySelector('[data-test="heading"]');
27
+ expect(label).to.be.ok;
28
+ expect(label?.textContent?.trim()).to.equal('Modal title');
29
+ });
30
+ it('does not render the show back button in the label by default', async () => {
31
+ const element = await fixture(html `<glide-core-modal label="Modal title">
32
+ Modal Content
33
+ </glide-core-modal>`);
34
+ element.showModal();
35
+ expect(element.shadowRoot?.querySelector('[data-test="back-button"]')).to.be.null;
36
+ });
37
+ it('renders the show back button in the label when provided with "show-back-button"', async () => {
38
+ const element = await fixture(html `<glide-core-modal label="Modal title" show-back-button>
39
+ Modal Content
40
+ </glide-core-modal>`);
41
+ element.showModal();
42
+ expect(element.shadowRoot?.querySelector('[data-test="back-button"]')).to.be.ok;
43
+ });
44
+ it('renders the provided default slotted content', async () => {
45
+ const element = await fixture(html `<glide-core-modal label="Modal title">
46
+ <p data-body>Inner content</p>
47
+ </glide-core-modal>`);
48
+ element.showModal();
49
+ const body = element.querySelector('[data-body]');
50
+ expect(body).to.be.ok;
51
+ });
52
+ it('renders the provided primary slot content', async () => {
53
+ const element = await fixture(html `<glide-core-modal label="Modal title">
54
+ Modal Content
55
+ <glide-core-button slot="primary" data-primary>Primary</glide-core-button>
56
+ </glide-core-modal>`);
57
+ element.showModal();
58
+ const slotContent = element.querySelector('[data-primary]');
59
+ expect(slotContent).to.be.ok;
60
+ const slotNodes = element.shadowRoot
61
+ ?.querySelector('slot[name="primary"]')
62
+ ?.assignedNodes();
63
+ expect(slotNodes?.length).to.equal(1);
64
+ });
65
+ it('renders the provided secondary slot content', async () => {
66
+ const element = await fixture(html `<glide-core-modal label="Modal title">
67
+ Modal Content
68
+ <glide-core-button slot="secondary" data-secondary
69
+ >Secondary</glide-core-button
70
+ >
71
+ </glide-core-modal>`);
72
+ element.showModal();
73
+ const slotContent = element.querySelector('[data-secondary]');
74
+ expect(slotContent).to.be.ok;
75
+ const slotNodes = element.shadowRoot
76
+ ?.querySelector('slot[name="secondary"]')
77
+ ?.assignedNodes();
78
+ expect(slotNodes?.length).to.equal(1);
79
+ });
80
+ it('renders the provided tertiary slot content', async () => {
81
+ const element = await fixture(html `<glide-core-modal label="Modal title">
82
+ Modal Content
83
+ <glide-core-button slot="tertiary" data-tertiary
84
+ >Tertiary</glide-core-button
85
+ >
86
+ </glide-core-modal>`);
87
+ element.showModal();
88
+ const slotContent = element.querySelector('[data-tertiary]');
89
+ expect(slotContent).to.be.ok;
90
+ const slotNodes = element.shadowRoot
91
+ ?.querySelector('slot[name="tertiary"]')
92
+ ?.assignedNodes();
93
+ expect(slotNodes?.length).to.equal(1);
94
+ });
95
+ it('renders the provided header-actions slot content', async () => {
96
+ const element = await fixture(html `<glide-core-modal label="Modal title">
97
+ Modal Content
98
+
99
+ <glide-core-modal-icon-button slot="header-actions" data-actions="1"
100
+ >action1</glide-core-modal-icon-button
101
+ >
102
+
103
+ <glide-core-modal-icon-button slot="header-actions" data-actions="2"
104
+ >action2</glide-core-modal-icon-button
105
+ >
106
+ </glide-core-modal>`);
107
+ element.showModal();
108
+ const slotContent = element.querySelector('[data-actions]');
109
+ expect(slotContent).to.be.ok;
110
+ const slotNodes = element.shadowRoot
111
+ ?.querySelector('slot[name="header-actions"]')
112
+ ?.assignedNodes();
113
+ expect(slotNodes?.length).to.equal(2);
114
+ });
115
+ it('defaults the size to "medium"', async () => {
116
+ const element = await fixture(html `<glide-core-modal label="Modal title">
117
+ Modal Content
118
+ </glide-core-modal>`);
119
+ expect([
120
+ ...element.shadowRoot.querySelector('dialog')
121
+ .classList,
122
+ ]).to.deep.equal(['component', 'medium']);
123
+ });
124
+ it('sets the size to "small"', async () => {
125
+ const element = await fixture(html `<glide-core-modal label="Modal title" size="small">
126
+ Modal Content
127
+ </glide-core-modal>`);
128
+ expect([
129
+ ...element.shadowRoot.querySelector('dialog')
130
+ .classList,
131
+ ]).to.deep.equal(['component', 'small']);
132
+ });
133
+ it('sets the size to "medium"', async () => {
134
+ const element = await fixture(html `<glide-core-modal label="Modal title" size="medium">
135
+ Modal Content
136
+ </glide-core-modal>`);
137
+ expect([
138
+ ...element.shadowRoot.querySelector('dialog')
139
+ .classList,
140
+ ]).to.deep.equal(['component', 'medium']);
141
+ });
142
+ it('sets the size to "large"', async () => {
143
+ const element = await fixture(html `<glide-core-modal label="Modal title" size="large">
144
+ Modal Content
145
+ </glide-core-modal>`);
146
+ expect([
147
+ ...element.shadowRoot.querySelector('dialog')
148
+ .classList,
149
+ ]).to.deep.equal(['component', 'large']);
150
+ });
151
+ it('sets the size to "xlarge"', async () => {
152
+ const element = await fixture(html `<glide-core-modal label="Modal title" size="xlarge">
153
+ Modal Content
154
+ </glide-core-modal>`);
155
+ expect([
156
+ ...element.shadowRoot.querySelector('dialog')
157
+ .classList,
158
+ ]).to.deep.equal(['component', 'xlarge']);
159
+ });
160
+ it('throws if it does not have a default slot', async () => {
161
+ const spy = sinon.spy();
162
+ try {
163
+ await fixture(html `<glide-core-modal label="Modal title"></glide-core-modal>`);
164
+ }
165
+ catch (error) {
166
+ if (error instanceof ArgumentError) {
167
+ spy();
168
+ }
169
+ }
170
+ expect(spy.called).to.be.true;
171
+ });
172
+ it('throws an error when the "primary" footer slot has the incorrect type', async () => {
173
+ await expectArgumentError(() => {
174
+ return fixture(html `<glide-core-modal label="Modal title">
175
+ Modal Content
176
+ <span slot="primary">Primary</span>
177
+ </glide-core-modal>`);
178
+ });
179
+ });
180
+ it('throws an error when the "secondary" footer slot has the incorrect type', async () => {
181
+ await expectArgumentError(() => {
182
+ return fixture(html `<glide-core-modal label="Modal title">
183
+ Modal Content
184
+ <span slot="secondary">Secondary</span>
185
+ </glide-core-modal>`);
186
+ });
187
+ });
188
+ it('throws an error when the "header actions" slot has the incorrect type', async () => {
189
+ await expectArgumentError(() => {
190
+ return fixture(html `<glide-core-modal label="Modal title">
191
+ Modal Content
192
+ <span slot="header-actions">Header Action</span>
193
+ </glide-core-modal>`);
194
+ });
195
+ });
196
+ it('throws an error when the "tertiary" footer slot has the incorrect type', async () => {
197
+ await expectArgumentError(() => {
198
+ return fixture(html `<glide-core-modal label="Modal title">
199
+ Modal Content
200
+ <span slot="tertiary">Tertiary</span>
201
+ </glide-core-modal>`);
202
+ });
203
+ });
@@ -0,0 +1,38 @@
1
+ import { expect, fixture, html } from '@open-wc/testing';
2
+ import { sendKeys } from '@web/test-runner-commands';
3
+ import GlideCoreModal from './modal.js';
4
+ GlideCoreModal.shadowRootOptions.mode = 'open';
5
+ it('closes the modal when the close button is clicked', async () => {
6
+ const element = await fixture(html `<glide-core-modal label="Modal title">
7
+ Modal Content
8
+ </glide-core-modal>`);
9
+ element.showModal();
10
+ const button = element.shadowRoot.querySelector('[data-test="close-button"]');
11
+ expect(button).to.be.ok;
12
+ button?.click();
13
+ expect(element
14
+ .shadowRoot.querySelector('dialog')
15
+ ?.hasAttribute('open')).to.be.false;
16
+ });
17
+ it('closes the modal when the escape key is pressed', async () => {
18
+ const element = await fixture(html `<glide-core-modal label="Modal title">
19
+ Modal Content
20
+ </glide-core-modal>`);
21
+ element.showModal();
22
+ const dialogElement = element.shadowRoot.querySelector('dialog');
23
+ expect(dialogElement?.hasAttribute('open')).to.be.true;
24
+ await sendKeys({ press: 'Escape' });
25
+ expect(dialogElement?.hasAttribute('open')).to.be.false;
26
+ });
27
+ it('closes the modal via "show-back-button"', async () => {
28
+ const element = await fixture(html `<glide-core-modal label="Modal title" show-back-button>
29
+ Modal Content
30
+ </glide-core-modal>`);
31
+ element.showModal();
32
+ const dialogElement = element.shadowRoot.querySelector('dialog');
33
+ expect(dialogElement?.hasAttribute('open')).to.be.true;
34
+ const button = element.shadowRoot.querySelector('[data-test="back-button"]');
35
+ expect(button).to.be.ok;
36
+ button?.click();
37
+ expect(dialogElement?.hasAttribute('open')).to.be.false;
38
+ });
@@ -0,0 +1,110 @@
1
+ import './modal.js';
2
+ import { expect, fixture, html } from '@open-wc/testing';
3
+ import { sendKeys, sendMouse } from '@web/test-runner-commands';
4
+ import GlideCoreModal from './modal.js';
5
+ import sinon from 'sinon';
6
+ GlideCoreModal.shadowRootOptions.mode = 'open';
7
+ it('dispatches a "close" event when the modal is closed via the "close" method', async () => {
8
+ const spy = sinon.spy();
9
+ const element = await fixture(html `<glide-core-modal label="Modal title"
10
+ >Modal Content</glide-core-modal
11
+ >`);
12
+ element.addEventListener('close', spy);
13
+ element.showModal();
14
+ expect(spy.notCalled).to.be.true;
15
+ element.close();
16
+ expect(spy.called).to.be.true;
17
+ });
18
+ it('dispatches a "close" event when the modal is closed via the close button', async () => {
19
+ const spy = sinon.spy();
20
+ const element = await fixture(html `<glide-core-modal label="Modal title"
21
+ >Modal Content</glide-core-modal
22
+ >`);
23
+ element.addEventListener('close', spy);
24
+ element.showModal();
25
+ const button = element.shadowRoot?.querySelector('[data-test="close-button"]');
26
+ expect(button).to.be.ok;
27
+ button?.click();
28
+ expect(spy.called).to.be.true;
29
+ });
30
+ it('dispatches a "close" event when the modal is closed via the escape key', async () => {
31
+ const spy = sinon.spy();
32
+ const element = await fixture(html `<glide-core-modal label="Modal title"
33
+ >Modal Content</glide-core-modal
34
+ >`);
35
+ element.addEventListener('close', spy);
36
+ element.showModal();
37
+ await sendKeys({ press: 'Escape' });
38
+ expect(spy.called).to.be.true;
39
+ });
40
+ it('does not dispatch a "close" event when the modal is open and non-escape keys are pressed', async () => {
41
+ const spy = sinon.spy();
42
+ const element = await fixture(html `<glide-core-modal label="Modal title"
43
+ >Modal Content</glide-core-modal
44
+ >`);
45
+ element.addEventListener('close', spy);
46
+ element.showModal();
47
+ // Tests only a couple keys.
48
+ await sendKeys({ press: ' ' });
49
+ expect(spy.notCalled).to.be.true;
50
+ await sendKeys({ press: 'Enter' });
51
+ expect(spy.notCalled).to.be.true;
52
+ });
53
+ it('dispatches a "close" event when the modal is closed via "show-back-button"', async () => {
54
+ const spy = sinon.spy();
55
+ const element = await fixture(html `<glide-core-modal label="Modal title" show-back-button>
56
+ Modal Content
57
+ </glide-core-modal>`);
58
+ element.addEventListener('close', spy);
59
+ element.showModal();
60
+ const button = element.shadowRoot?.querySelector('[data-test="back-button"]');
61
+ expect(button).to.be.ok;
62
+ button?.click();
63
+ expect(spy.called).to.be.true;
64
+ });
65
+ it('does not emit a "close" event when clicking inside the dialog and the mouse is not positioned on a "close" button', async () => {
66
+ const spy = sinon.spy();
67
+ const element = await fixture(html `<glide-core-modal label="Modal title"
68
+ >Modal Content</glide-core-modal
69
+ >`);
70
+ element.showModal();
71
+ element.addEventListener('close', spy);
72
+ const dialogElement = element?.shadowRoot?.querySelector('dialog');
73
+ const boundingRectangle = dialogElement?.getBoundingClientRect();
74
+ expect(boundingRectangle).is.not.null;
75
+ const { top, left } = boundingRectangle;
76
+ await sendMouse({
77
+ type: 'click',
78
+ position: [Math.ceil(left + 1), Math.ceil(top + 1)],
79
+ });
80
+ expect(spy.notCalled).to.be.true;
81
+ });
82
+ it(`does not emit a "close" event if a mousedown event's origin does not come from the dialog element`, async () => {
83
+ const spy = sinon.spy();
84
+ const element = await fixture(html `<glide-core-modal label="Modal title">
85
+ <button data-test="target">Inside Body</button>
86
+ </glide-core-modal>`);
87
+ element.showModal();
88
+ element.addEventListener('close', spy);
89
+ const button = element.querySelector('[data-test="target"]');
90
+ expect(button).to.be.ok;
91
+ button?.dispatchEvent(new Event('mousedown', { bubbles: true }));
92
+ expect(spy.notCalled).to.be.true;
93
+ });
94
+ it('emits a "close" event when clicking outside the dialog', async () => {
95
+ const spy = sinon.spy();
96
+ const element = await fixture(html `<glide-core-modal label="Modal title"
97
+ >Modal Content</glide-core-modal
98
+ >`);
99
+ element.showModal();
100
+ element.addEventListener('close', spy);
101
+ const dialogElement = element?.shadowRoot?.querySelector('dialog');
102
+ const boundingRectangle = dialogElement?.getBoundingClientRect();
103
+ expect(boundingRectangle).is.not.null;
104
+ const { top, left } = boundingRectangle;
105
+ await sendMouse({
106
+ type: 'click',
107
+ position: [Math.floor(left - 1), Math.floor(top - 1)],
108
+ });
109
+ expect(spy.called).to.be.true;
110
+ });
@@ -0,0 +1,76 @@
1
+ import * as sinon from 'sinon';
2
+ import { expect, fixture, html } from '@open-wc/testing';
3
+ import { sendKeys, sendMouse } from '@web/test-runner-commands';
4
+ import GlideCoreModal from './modal.js';
5
+ GlideCoreModal.shadowRootOptions.mode = 'open';
6
+ const addSpy = sinon.spy(document.documentElement.classList, 'add');
7
+ const removeSpy = sinon.spy(document.documentElement.classList, 'remove');
8
+ afterEach(() => {
9
+ addSpy.resetHistory();
10
+ removeSpy.resetHistory();
11
+ });
12
+ after(() => {
13
+ addSpy.restore();
14
+ removeSpy.restore();
15
+ });
16
+ it('adds the "private-glide-core-modal-lock-scroll" class to the documentElement when opened and removes it when closed', async () => {
17
+ const element = await fixture(html `<glide-core-modal label="Modal title"
18
+ >Modal Content</glide-core-modal
19
+ >`);
20
+ element.showModal();
21
+ expect(addSpy.callCount).to.equal(1);
22
+ expect(addSpy.calledWith('private-glide-core-modal-lock-scroll')).to.be.ok;
23
+ expect(removeSpy.callCount).to.equal(0);
24
+ element.close();
25
+ expect(removeSpy.callCount).to.equal(1);
26
+ expect(removeSpy.calledWith('private-glide-core-modal-lock-scroll')).to.be.ok;
27
+ // Verify `add` was not called again for some reason
28
+ expect(addSpy.callCount).to.equal(1);
29
+ });
30
+ it('removes the "private-glide-core-modal-lock-scroll" class when the close button is clicked', async () => {
31
+ const element = await fixture(html `<glide-core-modal label="Modal title"
32
+ >Modal Content</glide-core-modal
33
+ >`);
34
+ element.showModal();
35
+ expect(removeSpy.callCount).to.equal(0);
36
+ const button = element.shadowRoot.querySelector('[data-test="close-button"]');
37
+ expect(button).to.be.ok;
38
+ button?.click();
39
+ expect(removeSpy.callCount).to.equal(1);
40
+ });
41
+ it('removes the "private-glide-core-modal-lock-scroll" class when the "close" method is called', async () => {
42
+ const element = await fixture(html `<glide-core-modal label="Modal title"
43
+ >Modal Content</glide-core-modal
44
+ >`);
45
+ element.showModal();
46
+ expect(removeSpy.callCount).to.equal(0);
47
+ const dialogElement = element.shadowRoot.querySelector('dialog');
48
+ expect(dialogElement?.hasAttribute('open')).to.be.true;
49
+ element.close();
50
+ expect(removeSpy.callCount).to.equal(1);
51
+ });
52
+ it('removes the "private-glide-core-modal-lock-scroll" class when the escape key is pressed', async () => {
53
+ const element = await fixture(html `<glide-core-modal label="Modal title"
54
+ >Modal Content</glide-core-modal
55
+ >`);
56
+ element.showModal();
57
+ expect(removeSpy.callCount).to.equal(0);
58
+ await sendKeys({ press: 'Escape' });
59
+ expect(removeSpy.callCount).to.equal(1);
60
+ });
61
+ it('removes class "private-glide-core-modal-lock-scroll" from document when click is outside dialog', async () => {
62
+ const element = await fixture(html `<glide-core-modal label="Modal title"
63
+ >Modal Content</glide-core-modal
64
+ >`);
65
+ element.showModal();
66
+ const dialogElement = element?.shadowRoot?.querySelector('dialog');
67
+ const boundingRectangle = dialogElement?.getBoundingClientRect();
68
+ expect(boundingRectangle).is.not.null;
69
+ expect(document.documentElement.classList.contains('private-glide-core-modal-lock-scroll')).to.be.true;
70
+ const { top, left } = boundingRectangle;
71
+ await sendMouse({
72
+ type: 'click',
73
+ position: [Math.floor(left - 1), Math.floor(top - 1)],
74
+ });
75
+ expect(document.documentElement.classList.contains('private-glide-core-modal-lock-scroll')).to.be.false;
76
+ });
@@ -0,0 +1,23 @@
1
+ import { expect, fixture, html } from '@open-wc/testing';
2
+ import GlideCoreModal from './modal.js';
3
+ GlideCoreModal.shadowRootOptions.mode = 'open';
4
+ it('opens the modal when the "showModal" method is called and closes it when "close" is called', async () => {
5
+ const element = await fixture(html `<glide-core-modal label="Modal title">
6
+ Modal Content
7
+ </glide-core-modal>`);
8
+ element.showModal();
9
+ const dialogElement = element.shadowRoot?.querySelector('dialog');
10
+ expect(dialogElement?.hasAttribute('open')).to.be.true;
11
+ element.close();
12
+ expect(dialogElement?.hasAttribute('open')).to.be.false;
13
+ });
14
+ it('remains open when the "showModal" method is called twice', async () => {
15
+ const element = await fixture(html `<glide-core-modal label="Modal title">
16
+ Modal Content
17
+ </glide-core-modal>`);
18
+ element.showModal();
19
+ const dialogElement = element.shadowRoot?.querySelector('dialog');
20
+ expect(dialogElement?.hasAttribute('open')).to.be.true;
21
+ element.showModal();
22
+ expect(dialogElement?.hasAttribute('open')).to.be.true;
23
+ });
@@ -0,0 +1,19 @@
1
+ import './modal.js';
2
+ import * as sinon from 'sinon';
3
+ import { expect, fixture, html } from '@open-wc/testing';
4
+ import GlideCoreModal from './modal.js';
5
+ GlideCoreModal.shadowRootOptions.mode = 'open';
6
+ const cssSupportsStub = sinon.stub(window.CSS, 'supports').returns(false);
7
+ const setPropertySpy = sinon.spy(document.documentElement.style, 'setProperty');
8
+ afterEach(() => {
9
+ cssSupportsStub.restore();
10
+ setPropertySpy.restore();
11
+ });
12
+ it('sets the "--glide-scroll-size" variable when the browser does not support scrollbar-gutter', async () => {
13
+ const element = await fixture(html `<glide-core-modal label="Modal title">
14
+ Modal Content
15
+ </glide-core-modal>`);
16
+ element.showModal();
17
+ expect(cssSupportsStub.calledWith('scrollbar-gutter')).to.be.ok;
18
+ expect(setPropertySpy.callCount).to.equal(1);
19
+ });