@crowdstrike/glide-core 0.5.1 → 0.6.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 (242) hide show
  1. package/README.md +11 -1
  2. package/dist/accordion.js +1 -1
  3. package/dist/accordion.styles.js +5 -4
  4. package/dist/accordion.test.basics.js +109 -0
  5. package/dist/accordion.test.events.js +39 -0
  6. package/dist/button-group.button.js +1 -1
  7. package/dist/button-group.button.styles.js +6 -5
  8. package/dist/button-group.button.test.basics.js +169 -0
  9. package/dist/button-group.button.test.events.js +73 -0
  10. package/dist/button-group.js +1 -1
  11. package/dist/button-group.styles.js +3 -3
  12. package/dist/button-group.test.basics.js +268 -0
  13. package/dist/button-group.test.events.js +291 -0
  14. package/dist/button.d.ts +3 -2
  15. package/dist/button.js +1 -1
  16. package/dist/button.styles.js +5 -5
  17. package/dist/button.test.basics.js +202 -0
  18. package/dist/button.test.events.js +25 -0
  19. package/dist/button.test.form.js +49 -0
  20. package/dist/checkbox-group.js +1 -1
  21. package/dist/checkbox-group.styles.js +2 -2
  22. package/dist/checkbox-group.test.basics.js +119 -0
  23. package/dist/checkbox-group.test.events.js +110 -0
  24. package/dist/checkbox-group.test.focus.js +45 -0
  25. package/dist/checkbox-group.test.form.js +130 -0
  26. package/dist/checkbox-group.test.validity.js +75 -0
  27. package/dist/checkbox.js +1 -1
  28. package/dist/checkbox.styles.js +7 -4
  29. package/dist/checkbox.test.basics.js +89 -0
  30. package/dist/checkbox.test.events.js +87 -0
  31. package/dist/checkbox.test.focus.js +38 -0
  32. package/dist/checkbox.test.form.js +115 -0
  33. package/dist/checkbox.test.states.js +62 -0
  34. package/dist/checkbox.test.validity.js +51 -0
  35. package/dist/drawer.d.ts +2 -2
  36. package/dist/drawer.js +1 -15
  37. package/dist/drawer.styles.js +14 -4
  38. package/dist/drawer.test.accessibility.js +22 -0
  39. package/dist/drawer.test.basics.js +43 -0
  40. package/dist/drawer.test.closing.js +37 -0
  41. package/dist/drawer.test.events.js +52 -0
  42. package/dist/drawer.test.floating-components.d.ts +1 -0
  43. package/dist/drawer.test.floating-components.js +51 -0
  44. package/dist/drawer.test.methods.js +34 -0
  45. package/dist/dropdown.d.ts +4 -2
  46. package/dist/dropdown.js +1 -1
  47. package/dist/dropdown.option.d.ts +0 -2
  48. package/dist/dropdown.option.js +1 -1
  49. package/dist/dropdown.option.styles.js +2 -2
  50. package/dist/dropdown.option.test.basics.js +59 -0
  51. package/dist/dropdown.option.test.basics.multiple.js +26 -0
  52. package/dist/dropdown.option.test.basics.single.js +20 -0
  53. package/dist/dropdown.option.test.events.js +27 -0
  54. package/dist/dropdown.option.test.focus.js +11 -0
  55. package/dist/dropdown.option.test.interactions.multiple.js +82 -0
  56. package/dist/dropdown.option.test.interactions.single.js +22 -0
  57. package/dist/dropdown.styles.js +26 -6
  58. package/dist/dropdown.test.basics.filterable.js +84 -0
  59. package/dist/dropdown.test.basics.js +233 -0
  60. package/dist/dropdown.test.basics.multiple.js +270 -0
  61. package/dist/dropdown.test.basics.single.js +79 -0
  62. package/dist/dropdown.test.events.js +268 -0
  63. package/dist/dropdown.test.events.multiple.js +130 -0
  64. package/dist/dropdown.test.focus.d.ts +1 -0
  65. package/dist/dropdown.test.focus.filterable.js +154 -0
  66. package/dist/dropdown.test.focus.js +28 -0
  67. package/dist/dropdown.test.focus.multiple.js +181 -0
  68. package/dist/dropdown.test.focus.single.js +53 -0
  69. package/dist/dropdown.test.form.js +140 -0
  70. package/dist/dropdown.test.form.multiple.js +149 -0
  71. package/dist/dropdown.test.form.single.js +128 -0
  72. package/dist/dropdown.test.interactions.filterable.js +449 -0
  73. package/dist/dropdown.test.interactions.js +446 -0
  74. package/dist/dropdown.test.interactions.multiple.js +908 -0
  75. package/dist/dropdown.test.interactions.single.js +466 -0
  76. package/dist/dropdown.test.validity.js +46 -0
  77. package/dist/icon-button.d.ts +3 -2
  78. package/dist/icon-button.js +1 -1
  79. package/dist/icon-button.styles.js +12 -12
  80. package/dist/icon-button.test.basics.js +110 -0
  81. package/dist/icons/checked.js +1 -1
  82. package/dist/icons/magnifying-glass.js +1 -1
  83. package/dist/input.js +1 -1
  84. package/dist/input.styles.js +4 -3
  85. package/dist/input.test.basics.js +169 -0
  86. package/dist/input.test.events.js +97 -0
  87. package/dist/input.test.focus.js +54 -0
  88. package/dist/input.test.form.js +56 -0
  89. package/dist/input.test.validity.js +50 -0
  90. package/dist/label.js +1 -1
  91. package/dist/label.styles.js +13 -15
  92. package/dist/label.test.basics.js +129 -0
  93. package/dist/library/expect-argument-error.js +1 -1
  94. package/dist/library/localize.d.ts +17 -0
  95. package/dist/library/localize.js +1 -0
  96. package/dist/library/ow.js +1 -1
  97. package/dist/library/ow.test.js +55 -0
  98. package/dist/library/set-containing-block.d.ts +15 -0
  99. package/dist/library/set-containing-block.js +1 -0
  100. package/dist/menu.button.d.ts +1 -2
  101. package/dist/menu.button.js +1 -1
  102. package/dist/menu.button.styles.js +3 -3
  103. package/dist/menu.button.test.basics.js +42 -0
  104. package/dist/menu.d.ts +7 -2
  105. package/dist/menu.js +1 -1
  106. package/dist/menu.link.d.ts +1 -2
  107. package/dist/menu.link.js +1 -1
  108. package/dist/menu.link.styles.js +3 -3
  109. package/dist/menu.link.test.basics.js +46 -0
  110. package/dist/menu.options.d.ts +22 -0
  111. package/dist/menu.options.js +1 -0
  112. package/dist/menu.options.styles.d.ts +2 -0
  113. package/dist/menu.options.styles.js +33 -0
  114. package/dist/menu.options.test.basics.d.ts +2 -0
  115. package/dist/menu.options.test.basics.js +43 -0
  116. package/dist/menu.stories.d.ts +1 -0
  117. package/dist/menu.styles.js +7 -31
  118. package/dist/menu.test.basics.d.ts +1 -0
  119. package/dist/menu.test.basics.js +183 -0
  120. package/dist/menu.test.focus.d.ts +0 -1
  121. package/dist/menu.test.focus.js +84 -0
  122. package/dist/menu.test.interactions.d.ts +1 -1
  123. package/dist/menu.test.interactions.js +664 -0
  124. package/dist/modal.icon-button.js +1 -1
  125. package/dist/modal.icon-button.styles.js +2 -2
  126. package/dist/modal.icon-button.test.basics.js +45 -0
  127. package/dist/modal.js +1 -15
  128. package/dist/modal.styles.js +4 -7
  129. package/dist/modal.tertiary-icon.d.ts +1 -0
  130. package/dist/modal.tertiary-icon.js +1 -1
  131. package/dist/modal.tertiary-icon.test.basics.js +59 -0
  132. package/dist/modal.test.accessibility.js +48 -0
  133. package/dist/modal.test.basics.js +203 -0
  134. package/dist/modal.test.close.js +38 -0
  135. package/dist/modal.test.events.js +110 -0
  136. package/dist/modal.test.floating-components.d.ts +1 -0
  137. package/dist/modal.test.floating-components.js +62 -0
  138. package/dist/modal.test.lock-scroll.js +76 -0
  139. package/dist/modal.test.methods.js +23 -0
  140. package/dist/modal.test.scrollbars.js +19 -0
  141. package/dist/radio-group.js +1 -1
  142. package/dist/radio-group.styles.js +20 -24
  143. package/dist/radio-group.test.basics.js +323 -0
  144. package/dist/radio-group.test.events.js +277 -0
  145. package/dist/radio-group.test.focus.js +75 -0
  146. package/dist/radio-group.test.form.js +104 -0
  147. package/dist/radio-group.test.validity.js +228 -0
  148. package/dist/radio.js +1 -1
  149. package/dist/radio.styles.js +14 -31
  150. package/dist/split-button.d.ts +27 -0
  151. package/dist/split-button.js +1 -0
  152. package/dist/split-button.stories.d.ts +17 -0
  153. package/dist/split-button.styles.d.ts +2 -0
  154. package/dist/split-button.styles.js +102 -0
  155. package/dist/split-button.test.basics.d.ts +1 -0
  156. package/dist/split-button.test.basics.js +99 -0
  157. package/dist/split-container.d.ts +31 -0
  158. package/dist/split-container.js +1 -0
  159. package/dist/split-container.styles.d.ts +2 -0
  160. package/dist/split-container.styles.js +134 -0
  161. package/dist/split-container.test.basics.d.ts +3 -0
  162. package/dist/split-container.test.basics.js +440 -0
  163. package/dist/split-container.test.interactions.d.ts +1 -0
  164. package/dist/split-container.test.interactions.js +20 -0
  165. package/dist/split-link.d.ts +25 -0
  166. package/dist/split-link.js +1 -0
  167. package/dist/split-link.test.basics.d.ts +1 -0
  168. package/dist/split-link.test.basics.js +92 -0
  169. package/dist/split-link.test.interactions.d.ts +1 -0
  170. package/dist/split-link.test.interactions.js +19 -0
  171. package/dist/status-indicator.js +1 -1
  172. package/dist/status-indicator.styles.js +2 -2
  173. package/dist/status-indicator.test.basics.js +102 -0
  174. package/dist/styles/focus-outline.js +1 -4
  175. package/dist/styles/variables.css +1 -1
  176. package/dist/styles/visually-hidden.js +1 -11
  177. package/dist/tab.group.js +1 -1
  178. package/dist/tab.group.styles.js +2 -2
  179. package/dist/tab.group.test.basics.js +185 -0
  180. package/dist/tab.js +1 -1
  181. package/dist/tab.panel.js +1 -1
  182. package/dist/tab.panel.styles.js +3 -3
  183. package/dist/tab.styles.js +80 -55
  184. package/dist/tab.test.basics.js +71 -0
  185. package/dist/tag.js +1 -1
  186. package/dist/tag.styles.js +4 -3
  187. package/dist/tag.test.basics.js +118 -0
  188. package/dist/tag.test.events.js +16 -0
  189. package/dist/tag.test.focus.js +11 -0
  190. package/dist/tag.test.translations.d.ts +1 -0
  191. package/dist/tag.test.translations.js +25 -0
  192. package/dist/textarea.js +2 -2
  193. package/dist/textarea.styles.js +5 -4
  194. package/dist/textarea.test.basics.js +140 -0
  195. package/dist/textarea.test.events.js +204 -0
  196. package/dist/textarea.test.form.js +70 -0
  197. package/dist/textarea.test.validity.js +83 -0
  198. package/dist/toasts.js +1 -1
  199. package/dist/toasts.styles.js +2 -2
  200. package/dist/toasts.test.basics.js +94 -0
  201. package/dist/toasts.toast.js +1 -1
  202. package/dist/toasts.toast.styles.js +5 -2
  203. package/dist/toasts.toast.test.basics.js +139 -0
  204. package/dist/toggle.js +1 -1
  205. package/dist/toggle.styles.js +3 -3
  206. package/dist/toggle.test.basics.js +68 -0
  207. package/dist/toggle.test.events.js +29 -0
  208. package/dist/toggle.test.focus.js +9 -0
  209. package/dist/toggle.test.states.js +43 -0
  210. package/dist/tooltip.d.ts +2 -0
  211. package/dist/tooltip.js +1 -1
  212. package/dist/tooltip.styles.js +5 -3
  213. package/dist/tooltip.test.basics.js +64 -0
  214. package/dist/tooltip.test.interactions.js +78 -0
  215. package/dist/translations/en.d.ts +3 -0
  216. package/dist/translations/en.js +1 -0
  217. package/dist/translations/fr.d.ts +3 -0
  218. package/dist/translations/fr.js +1 -0
  219. package/dist/translations/ja.d.ts +3 -0
  220. package/dist/translations/ja.js +1 -0
  221. package/dist/tree.d.ts +1 -0
  222. package/dist/tree.item.d.ts +3 -1
  223. package/dist/tree.item.icon-button.js +1 -1
  224. package/dist/tree.item.icon-button.styles.js +2 -2
  225. package/dist/tree.item.icon-button.test.basics.js +13 -0
  226. package/dist/tree.item.js +1 -1
  227. package/dist/tree.item.menu.d.ts +2 -0
  228. package/dist/tree.item.menu.js +1 -1
  229. package/dist/tree.item.menu.styles.js +2 -2
  230. package/dist/tree.item.menu.test.basics.js +33 -0
  231. package/dist/tree.item.styles.js +23 -8
  232. package/dist/tree.item.test.basics.js +102 -0
  233. package/dist/tree.js +1 -1
  234. package/dist/tree.stories.d.ts +1 -0
  235. package/dist/tree.styles.js +2 -2
  236. package/dist/tree.test.aria.js +86 -0
  237. package/dist/tree.test.basics.js +123 -0
  238. package/dist/tree.test.events.js +19 -0
  239. package/dist/tree.test.focus.js +261 -0
  240. package/package.json +25 -18
  241. /package/dist/{dropdown.option.test.focus.multiple.d.ts → dropdown.option.test.focus.d.ts} +0 -0
  242. /package/dist/{dropdown.option.test.focus.single.d.ts → dropdown.test.events.multiple.d.ts} +0 -0
@@ -1,8 +1,8 @@
1
- "use strict";import{css as e}from"lit";export default[e`
1
+ import{css}from"lit";export default[css`
2
2
  ::slotted(*) {
3
3
  --size: 1.25rem;
4
4
 
5
5
  block-size: 1.25rem;
6
6
  inline-size: 1.25rem;
7
7
  }
8
- `];
8
+ `];
@@ -0,0 +1,45 @@
1
+ import { ArgumentError } from 'ow';
2
+ import { expect, fixture, html } from '@open-wc/testing';
3
+ import GlideCoreModalIconButton from './modal.icon-button.js';
4
+ import sinon from 'sinon';
5
+ GlideCoreModalIconButton.shadowRootOptions.mode = 'open';
6
+ it('registers', async () => {
7
+ expect(window.customElements.get('glide-core-modal-icon-button')).to.equal(GlideCoreModalIconButton);
8
+ });
9
+ it('is accessible', async () => {
10
+ const element = await fixture(html `<glide-core-modal-icon-button>Test</glide-core-modal-icon-button>`);
11
+ await expect(element).to.be.accessible();
12
+ });
13
+ it('renders and sets default attributes', async () => {
14
+ const element = await fixture(html `
15
+ <glide-core-modal-icon-button>Test</glide-core-modal-icon-button>
16
+ `);
17
+ expect(element).to.be.ok;
18
+ const buttonElement = element.shadowRoot?.querySelector('glide-core-icon-button');
19
+ expect(buttonElement).to.be.not.null;
20
+ expect(buttonElement).to.have.attribute('variant', 'tertiary');
21
+ });
22
+ it('adds an accessible label when given', async () => {
23
+ const element = await fixture(html `<glide-core-modal-icon-button label="test-label"
24
+ >Test</glide-core-modal-icon-button
25
+ >`);
26
+ const buttonElement = element.shadowRoot?.querySelector('glide-core-icon-button');
27
+ expect(buttonElement).to.have.attribute('label', 'test-label');
28
+ });
29
+ it('does not add an acceessible label when not given', async () => {
30
+ const element = await fixture(html `<glide-core-modal-icon-button>Test</glide-core-modal-icon-button>`);
31
+ const buttonElement = element.shadowRoot?.querySelector('glide-core-icon-button');
32
+ expect(buttonElement).to.have.attribute('label', '');
33
+ });
34
+ it('throws if it does not have a default slot', async () => {
35
+ const spy = sinon.spy();
36
+ try {
37
+ await fixture(html `<glide-core-modal-icon-button></glide-core-modal-icon-button>`);
38
+ }
39
+ catch (error) {
40
+ if (error instanceof ArgumentError) {
41
+ spy();
42
+ }
43
+ }
44
+ expect(spy.called).to.be.true;
45
+ });
package/dist/modal.js CHANGED
@@ -1,15 +1 @@
1
- "use strict";var Y=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var b=(l,o,t,n)=>{for(var c=n>1?void 0:n?N(o,t):o,R=l.length-1,B;R>=0;R--)(B=l[R])&&(c=(n?B(o,t,c):B(c))||c);return n&&c&&Y(o,t,c),c};var z=(l,o,t)=>{if(!o.has(l))throw TypeError("Cannot "+t)};var e=(l,o,t)=>(z(l,o,"read from private field"),t?t.call(l):o.get(l)),i=(l,o,t)=>{if(o.has(l))throw TypeError("Cannot add the same private member more than once");o instanceof WeakSet?o.add(l):o.set(l,t)};var r=(l,o,t)=>(z(l,o,"access private method"),t);var s,p,g,v,f,S,w,x,E,j,y,F,C,P,M,K,$,O,L,W,H,X;import"./modal.icon-button.js";import{LitElement as V,html as D}from"lit";import{classMap as U}from"lit/directives/class-map.js";import{createRef as m,ref as u}from"lit/directives/ref.js";import{customElement as q,property as T}from"lit/decorators.js";import{owSlot as I,owSlotType as d}from"./library/ow.js";import{when as J}from"lit/directives/when.js";import h from"./button.js";import Z from"./modal.icon-button.js";import A from"./modal.tertiary-icon.js";import Q from"./modal.styles.js";const k=new CSSStyleSheet;k.insertRule(`
2
- @supports (scrollbar-gutter: stable) {
3
- .private-glide-core-modal-lock-scroll {
4
- scrollbar-gutter: stable !important;
5
- overflow: hidden !important;
6
- }
7
- }
8
- `),k.insertRule(`
9
- @supports not (scrollbar-gutter: stable) {
10
- .private-glide-core-modal-lock-scroll {
11
- padding-right: var(--glide-scroll-size, 0.9375rem) !important;
12
- overflow: hidden !important;
13
- }
14
- }
15
- `);let a=class extends V{constructor(){super(...arguments);i(this,w);i(this,E);i(this,y);i(this,C);i(this,M);i(this,$);i(this,L);i(this,H);this.label="";this.showBackButton=!1;this.size="medium";i(this,s,m());i(this,p,m());i(this,g,m());i(this,v,m());i(this,f,m());i(this,S,m())}close(){e(this,s).value?.open&&(document.documentElement.classList.remove("private-glide-core-modal-lock-scroll"),this.dispatchEvent(new Event("close")),e(this,s).value?.close())}connectedCallback(){super.connectedCallback(),document.adoptedStyleSheets.includes(k)||document.adoptedStyleSheets.push(k)}disconnectedCallback(){super.disconnectedCallback(),document.documentElement.classList.remove("private-glide-core-modal-lock-scroll"),document.adoptedStyleSheets=document.adoptedStyleSheets.filter(t=>t!==k)}firstUpdated(){I(e(this,p).value),d(e(this,S).value,[Z]),d(e(this,g).value,[h]),d(e(this,v).value,[h]),d(e(this,f).value,[A,h])}render(){return D`<dialog class="${U({component:!0,small:this.size==="small",medium:this.size==="medium",large:this.size==="large",xlarge:this.size==="xlarge"})}" tabindex="-1" @keydown="${r(this,L,W)}" @mousedown="${r(this,H,X)}" ${u(e(this,s))}><header class="header"><h2 class="label" data-test="heading" id="heading">${J(this.showBackButton,()=>D`<glide-core-modal-icon-button data-test="back-button" @click="${r(this,w,x)}"><svg width="20" height="20" viewBox="0 0 24 24" fill="none"><title>Dismiss</title><path d="M12 18C11.4477 18 11 18.4477 11 19C11 19.5523 11.4477 20 12 20V18ZM20 14.5C20 16.433 18.433 18 16.5 18V20C19.5376 20 22 17.5376 22 14.5H20ZM16.5 11C18.433 11 20 12.567 20 14.5H22C22 11.4624 19.5376 9 16.5 9V11ZM16.5 18H12V20H16.5V18ZM16.5 9H3V11H16.5V9Z" fill="currentColor"/><path d="M7 6L3 10L7 14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></glide-core-modal-icon-button>`)} ${this.label}</h2><div class="header-actions" role="toolbar"><slot name="header-actions" @slotchange="${r(this,$,O)}" ${u(e(this,S))}></slot><glide-core-modal-icon-button data-test="close-button" @click="${r(this,w,x)}"><svg width="24" height="24" viewBox="0 0 24 24" fill="none"><title>Close</title><path d="M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></glide-core-modal-icon-button></div></header><article aria-labelledby="heading" class="body" role="region" tabindex="0"><slot @slotchange="${r(this,E,j)}" ${u(e(this,p))}></slot></article><footer class="footer"><menu class="menu"><li class="flex align-center"><slot name="tertiary" @slotchange="${r(this,M,K)}" ${u(e(this,f))}></slot></li><li><menu class="actions"><li><slot name="secondary" @slotchange="${r(this,C,P)}" ${u(e(this,v))}></slot></li><li><slot name="primary" @slotchange="${r(this,y,F)}" ${u(e(this,g))}></slot></li></menu></li></menu></footer></dialog>`}showModal(){if(!e(this,s).value?.open){if(document.documentElement.classList.add("private-glide-core-modal-lock-scroll"),!window.CSS.supports("scrollbar-gutter","stable")){const t=Math.abs(window.innerWidth-document.documentElement.clientWidth);document.documentElement.style.setProperty("--glide-scroll-size",`${t}px`)}e(this,s).value?.showModal(),e(this,s).value?.focus()}}};s=new WeakMap,p=new WeakMap,g=new WeakMap,v=new WeakMap,f=new WeakMap,S=new WeakMap,w=new WeakSet,x=function(){document.documentElement.classList.remove("private-glide-core-modal-lock-scroll"),this.dispatchEvent(new Event("close")),e(this,s).value?.close()},E=new WeakSet,j=function(){I(e(this,p).value)},y=new WeakSet,F=function(){d(e(this,g).value,[h])},C=new WeakSet,P=function(){d(e(this,v).value,[h])},M=new WeakSet,K=function(){d(e(this,f).value,[A,h])},$=new WeakSet,O=function(){d(e(this,S).value,[Z])},L=new WeakSet,W=function(t){t.key==="Escape"&&(document.documentElement.classList.remove("private-glide-core-modal-lock-scroll"),this.dispatchEvent(new Event("close")),e(this,s).value?.close())},H=new WeakSet,X=function(t){if(t.target!==e(this,s).value)return;const n=e(this,s).value?.getBoundingClientRect();n&&(n.top<=t.clientY&&t.clientY<=n.top+n.height&&n.left<=t.clientX&&t.clientX<=n.left+n.width||(document.documentElement.classList.remove("private-glide-core-modal-lock-scroll"),this.dispatchEvent(new Event("close")),e(this,s).value?.close()))},a.shadowRootOptions={...V.shadowRootOptions,mode:"closed"},a.styles=Q,b([T({reflect:!0})],a.prototype,"label",2),b([T({attribute:"show-back-button",type:Boolean,reflect:!0})],a.prototype,"showBackButton",2),b([T({reflect:!0})],a.prototype,"size",2),a=b([q("glide-core-modal")],a);export{a as default};
1
+ var __decorate=this&&this.__decorate||function(e,t,o,l){var n,r=arguments.length,i=r<3?t:null===l?l=Object.getOwnPropertyDescriptor(t,o):l;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)i=Reflect.decorate(e,t,o,l);else for(var s=e.length-1;s>=0;s--)(n=e[s])&&(i=(r<3?n(i):r>3?n(t,o,i):n(t,o))||i);return r>3&&i&&Object.defineProperty(t,o,i),i};import"./modal.icon-button.js";import{LitElement,html}from"lit";import{LocalizeController}from"./library/localize.js";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property}from"lit/decorators.js";import{owSlot,owSlotType}from"./library/ow.js";import{setContainingBlock}from"./library/set-containing-block.js";import{when}from"lit/directives/when.js";import GlideCoreButton from"./button.js";import GlideCoreModalIconButton from"./modal.icon-button.js";import GlideCoreModalTertiaryIcon from"./modal.tertiary-icon.js";import styles from"./modal.styles.js";const globalStylesheet=new CSSStyleSheet;globalStylesheet.insertRule("\n @supports (scrollbar-gutter: stable) {\n .private-glide-core-modal-lock-scroll {\n scrollbar-gutter: stable !important;\n overflow: hidden !important;\n }\n }\n"),globalStylesheet.insertRule("\n @supports not (scrollbar-gutter: stable) {\n .private-glide-core-modal-lock-scroll {\n padding-right: var(--glide-scroll-size, 0.9375rem) !important;\n overflow: hidden !important;\n }\n }\n");let GlideCoreModal=class GlideCoreModal extends LitElement{constructor(){super(...arguments),this.label="",this.showBackButton=!1,this.size="medium",this.#e=createRef(),this.#t=createRef(),this.#o=createRef(),this.#l=createRef(),this.#n=createRef(),this.#r=createRef(),this.#i=new LocalizeController(this)}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}close(){this.#e.value?.open&&(document.documentElement.classList.remove("private-glide-core-modal-lock-scroll"),this.dispatchEvent(new Event("close")),this.#e.value?.close())}connectedCallback(){super.connectedCallback();document.adoptedStyleSheets.includes(globalStylesheet)||document.adoptedStyleSheets.push(globalStylesheet)}disconnectedCallback(){super.disconnectedCallback(),document.documentElement.classList.remove("private-glide-core-modal-lock-scroll"),document.adoptedStyleSheets=document.adoptedStyleSheets.filter((e=>e!==globalStylesheet))}firstUpdated(){owSlot(this.#t.value),owSlotType(this.#r.value,[GlideCoreModalIconButton]),owSlotType(this.#o.value,[GlideCoreButton]),owSlotType(this.#l.value,[GlideCoreButton]),owSlotType(this.#n.value,[GlideCoreModalTertiaryIcon,GlideCoreButton])}render(){return html`<dialog class="${classMap({component:!0,small:"small"===this.size,medium:"medium"===this.size,large:"large"===this.size,xlarge:"xlarge"===this.size})}" tabindex="-1" @keydown="${this.#s}" @mousedown="${this.#a}" ${ref(this.#e)}><header class="header"><h2 class="label" data-test="heading" id="heading">${when(this.showBackButton,(()=>html`<glide-core-modal-icon-button data-test="back-button" @click="${this.#c}"><svg width="20" height="20" viewBox="0 0 24 24" fill="none"><title>${this.#i.term("dismiss")}</title><path d="M12 18C11.4477 18 11 18.4477 11 19C11 19.5523 11.4477 20 12 20V18ZM20 14.5C20 16.433 18.433 18 16.5 18V20C19.5376 20 22 17.5376 22 14.5H20ZM16.5 11C18.433 11 20 12.567 20 14.5H22C22 11.4624 19.5376 9 16.5 9V11ZM16.5 18H12V20H16.5V18ZM16.5 9H3V11H16.5V9Z" fill="currentColor"/><path d="M7 6L3 10L7 14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></glide-core-modal-icon-button>`))} ${this.label}</h2><div class="header-actions" role="toolbar"><slot name="header-actions" @slotchange="${this.#d}" ${ref(this.#r)}></slot><glide-core-modal-icon-button data-test="close-button" @click="${this.#c}"><svg width="24" height="24" viewBox="0 0 24 24" fill="none"><title>${this.#i.term("close")}</title><path d="M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></glide-core-modal-icon-button></div></header><article aria-labelledby="heading" class="body" role="region" tabindex="0"><slot @slotchange="${this.#m}" ${ref(this.#t)}></slot></article><footer class="footer"><menu class="menu"><li class="flex align-center"><slot name="tertiary" @slotchange="${this.#h}" ${ref(this.#n)}></slot></li><li><menu class="actions"><li><slot name="secondary" @slotchange="${this.#u}" ${ref(this.#l)}></slot></li><li><slot name="primary" @slotchange="${this.#f}" ${ref(this.#o)}></slot></li></menu></li></menu></footer></dialog>`}showModal(){if(!this.#e.value?.open){if(document.documentElement.classList.add("private-glide-core-modal-lock-scroll"),!window.CSS.supports("scrollbar-gutter","stable")){const e=Math.abs(window.innerWidth-document.documentElement.clientWidth);document.documentElement.style.setProperty("--glide-scroll-size",`${e}px`)}this.#e.value?.showModal(),this.#e.value?.focus()}}#e;#t;#o;#l;#n;#r;#i;#c(){document.documentElement.classList.remove("private-glide-core-modal-lock-scroll"),this.dispatchEvent(new Event("close")),this.#e.value?.close()}#m(){owSlot(this.#t.value);const e=this.#t.value.assignedElements();setContainingBlock({elements:e,containingBlock:this.#e.value})}#f(){owSlotType(this.#o.value,[GlideCoreButton])}#u(){owSlotType(this.#l.value,[GlideCoreButton])}#h(){owSlotType(this.#n.value,[GlideCoreModalTertiaryIcon,GlideCoreButton]);const e=this.#n.value.assignedElements().filter((e=>e instanceof GlideCoreModalTertiaryIcon));for(const t of e)t.setContainingBlock(this.#e.value)}#d(){owSlotType(this.#r.value,[GlideCoreModalIconButton])}#s(e){"Escape"===e.key&&(document.documentElement.classList.remove("private-glide-core-modal-lock-scroll"),this.dispatchEvent(new Event("close")),this.#e.value?.close())}#a(e){if(e.target!==this.#e.value)return;const t=this.#e.value?.getBoundingClientRect();if(t){t.top<=e.clientY&&e.clientY<=t.top+t.height&&t.left<=e.clientX&&e.clientX<=t.left+t.width||(document.documentElement.classList.remove("private-glide-core-modal-lock-scroll"),this.dispatchEvent(new Event("close")),this.#e.value?.close())}}};__decorate([property({reflect:!0})],GlideCoreModal.prototype,"label",void 0),__decorate([property({attribute:"show-back-button",type:Boolean,reflect:!0})],GlideCoreModal.prototype,"showBackButton",void 0),__decorate([property({reflect:!0})],GlideCoreModal.prototype,"size",void 0),GlideCoreModal=__decorate([customElement("glide-core-modal")],GlideCoreModal);export default GlideCoreModal;
@@ -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 {
@@ -23,6 +23,7 @@
23
23
  }
24
24
 
25
25
  .component {
26
+ backdrop-filter: blur(100px);
26
27
  background-color: var(--glide-core-surface-base-lighter);
27
28
  border: none;
28
29
  border-radius: 0.5rem;
@@ -42,10 +43,6 @@
42
43
  outline: none;
43
44
  }
44
45
 
45
- &:focus-visible {
46
- ${e};
47
- }
48
-
49
46
  &::backdrop {
50
47
  animation: backdrop-fade-in 250ms;
51
48
 
@@ -130,7 +127,7 @@
130
127
  }
131
128
 
132
129
  &:focus-visible {
133
- ${e};
130
+ ${focusOutline};
134
131
  }
135
132
  }
136
133
 
@@ -166,4 +163,4 @@
166
163
  .align-center {
167
164
  align-items: center;
168
165
  }
169
- `];
166
+ `];
@@ -18,4 +18,5 @@ export default class GlideCoreModalTertiaryIcon extends LitElement {
18
18
  tooltipPlacement: 'bottom' | 'left' | 'right' | 'top';
19
19
  firstUpdated(): void;
20
20
  render(): import("lit").TemplateResult<1>;
21
+ setContainingBlock(containingBlock: HTMLElement): void;
21
22
  }
@@ -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 n=e.length-1;n>=0;n--)(r=e[n])&&(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";import GlideCoreTooltip from"./tooltip.js";let GlideCoreModalTertiaryIcon=class GlideCoreModalTertiaryIcon extends LitElement{constructor(){super(...arguments),this.tooltipPlacement="bottom",this.#e=createRef(),this.#t=createRef()}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}firstUpdated(){owSlot(this.#e.value)}render(){return html`<glide-core-tooltip placement="${this.tooltipPlacement}" ${ref(this.#t)}>${this.label} <span tabindex="0" aria-label="${ifDefined(this.label)}" slot="target"><slot @slotchange="${this.#o}" ${ref(this.#e)}></slot></span></glide-core-tooltip>`}setContainingBlock(e){this.#t.value.containingBlock=e}#e;#t;#o(){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 @@
1
+ export {};