@crowdstrike/glide-core 0.9.1 → 0.9.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.
- package/dist/accordion.styles.js +2 -4
- package/dist/button-group.button.styles.js +3 -8
- package/dist/button-group.styles.js +2 -2
- package/dist/button.d.ts +1 -0
- package/dist/button.js +1 -1
- package/dist/button.styles.js +2 -4
- package/dist/button.test.events.js +86 -10
- package/dist/checkbox.js +1 -1
- package/dist/checkbox.styles.js +43 -6
- package/dist/checkbox.test.form.js +16 -0
- package/dist/checkbox.test.interactions.js +8 -0
- package/dist/drawer.js +1 -1
- package/dist/dropdown.d.ts +4 -2
- package/dist/dropdown.js +1 -1
- package/dist/dropdown.option.js +1 -1
- package/dist/dropdown.option.styles.js +1 -0
- package/dist/dropdown.styles.js +47 -26
- package/dist/dropdown.test.focus.filterable.js +20 -0
- package/dist/dropdown.test.focus.js +1 -0
- package/dist/dropdown.test.form.js +23 -112
- package/dist/dropdown.test.interactions.filterable.js +121 -17
- package/dist/dropdown.test.interactions.multiple.js +15 -22
- package/dist/dropdown.test.interactions.single.js +44 -22
- package/dist/icon-button.styles.js +2 -4
- package/dist/icons/checked.d.ts +5 -0
- package/dist/icons/checked.js +1 -1
- package/dist/input.d.ts +1 -1
- package/dist/input.js +1 -1
- package/dist/input.stories.d.ts +0 -4
- package/dist/input.styles.d.ts +1 -1
- package/dist/input.styles.js +93 -93
- package/dist/input.test.basics.js +45 -45
- package/dist/input.test.form.js +17 -0
- package/dist/label.styles.js +6 -11
- package/dist/library/localize.test.js +45 -0
- package/dist/menu.button.styles.js +1 -0
- package/dist/menu.js +1 -1
- package/dist/menu.link.styles.js +1 -0
- package/dist/menu.styles.js +3 -1
- package/dist/menu.test.events.js +1 -97
- package/dist/menu.test.focus.js +26 -3
- package/dist/menu.test.interactions.js +3 -0
- package/dist/modal.d.ts +0 -7
- package/dist/modal.icon-button.test.basics.js +9 -9
- package/dist/modal.styles.js +2 -4
- package/dist/modal.tertiary-icon.test.basics.js +14 -14
- package/dist/modal.test.accessibility.js +16 -27
- package/dist/modal.test.basics.js +64 -68
- package/dist/modal.test.close.js +12 -16
- package/dist/modal.test.events.js +32 -44
- package/dist/modal.test.lock-scroll.js +15 -25
- package/dist/modal.test.methods.js +8 -12
- package/dist/modal.test.scrollbars.js +2 -4
- package/dist/radio-group.js +1 -1
- package/dist/radio-group.test.basics.js +3 -3
- package/dist/radio-group.test.events.js +6 -6
- package/dist/radio-group.test.form.js +19 -0
- package/dist/radio.styles.js +2 -6
- package/dist/split-button.styles.js +2 -4
- package/dist/split-container.styles.js +2 -4
- package/dist/styles/focus-outline.d.ts +1 -1
- package/dist/styles/focus-outline.js +7 -1
- package/dist/styles/menu-opening-animation.d.ts +2 -0
- package/dist/styles/menu-opening-animation.js +26 -0
- package/dist/styles/variables.css +1 -1
- package/dist/styles/visually-hidden.d.ts +1 -1
- package/dist/styles/visually-hidden.js +14 -1
- package/dist/tab.group.d.ts +6 -6
- package/dist/tab.group.js +1 -1
- package/dist/tab.group.styles.js +46 -5
- package/dist/tab.group.test.basics.js +9 -2
- package/dist/tab.group.test.interactions.js +70 -93
- package/dist/tab.js +1 -1
- package/dist/tab.panel.styles.js +3 -9
- package/dist/tab.styles.js +6 -13
- package/dist/tab.test.basics.js +15 -17
- package/dist/tag.js +1 -1
- package/dist/tag.styles.js +2 -4
- package/dist/tag.test.basics.js +28 -27
- package/dist/tag.test.events.js +3 -3
- package/dist/tag.test.focus.js +4 -4
- package/dist/textarea.d.ts +1 -1
- package/dist/textarea.stories.d.ts +0 -4
- package/dist/textarea.styles.d.ts +1 -1
- package/dist/textarea.styles.js +63 -67
- package/dist/textarea.test.basics.js +52 -52
- package/dist/toasts.d.ts +5 -0
- package/dist/toasts.styles.js +1 -1
- package/dist/toggle.styles.js +2 -1
- package/dist/toggle.test.interactions.js +11 -0
- package/dist/tooltip.js +1 -1
- package/dist/tooltip.styles.js +22 -18
- package/dist/tooltip.test.interactions.js +6 -6
- package/dist/translations/en.js +1 -1
- package/dist/translations/fr.d.ts +3 -1
- package/dist/translations/fr.js +1 -1
- package/dist/translations/ja.d.ts +3 -1
- package/dist/translations/ja.js +1 -1
- package/dist/tree.item.styles.js +11 -3
- package/dist/tree.item.test.basics.js +0 -30
- package/package.json +1 -1
- package/dist/button.test.form.d.ts +0 -1
- package/dist/button.test.form.js +0 -50
- package/dist/input.test.translations.js +0 -38
- package/dist/tag.test.translations.d.ts +0 -1
- package/dist/tag.test.translations.js +0 -25
- package/dist/textarea.test.translations.d.ts +0 -1
- package/dist/textarea.test.translations.js +0 -34
- /package/dist/{input.test.translations.d.ts → library/localize.test.d.ts} +0 -0
package/dist/input.test.form.js
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
2
2
|
import './input.js';
|
3
3
|
import { expect, fixture, html } from '@open-wc/testing';
|
4
|
+
import { sendKeys } from '@web/test-runner-commands';
|
5
|
+
import GlideCoreInput from './input.js';
|
6
|
+
import sinon from 'sinon';
|
4
7
|
it('can be reset to initial value', async () => {
|
5
8
|
const form = document.createElement('form');
|
6
9
|
const input = await fixture(html `<glide-core-input value="value"></glide-core-input>`, {
|
@@ -55,3 +58,17 @@ it('has no `formData` value when it has a value but without a `name`', async ()
|
|
55
58
|
const formData = new FormData(form);
|
56
59
|
expect(formData.get('name')).to.be.null;
|
57
60
|
});
|
61
|
+
it('submits its form on Enter', async () => {
|
62
|
+
const form = document.createElement('form');
|
63
|
+
const component = await fixture(html `<glide-core-input value="value"></glide-core-input>`, {
|
64
|
+
parentNode: form,
|
65
|
+
});
|
66
|
+
const spy = sinon.spy();
|
67
|
+
form.addEventListener('submit', (event) => {
|
68
|
+
event.preventDefault();
|
69
|
+
spy();
|
70
|
+
});
|
71
|
+
component.focus();
|
72
|
+
await sendKeys({ press: 'Enter' });
|
73
|
+
expect(spy.callCount).to.equal(1);
|
74
|
+
});
|
package/dist/label.styles.js
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import visuallyHidden from"./styles/visually-hidden.js";export default[css`
|
2
|
+
${focusOutline(".optional-tooltip-target:focus-visible ")}
|
3
|
+
${visuallyHidden(".tooltips-and-label.hidden")}
|
4
|
+
`,css`
|
2
5
|
.component {
|
3
6
|
&.horizontal {
|
4
7
|
--column-gap: var(--glide-core-spacing-sm);
|
@@ -34,11 +37,11 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import
|
|
34
37
|
column-gap: var(--glide-core-spacing-xs);
|
35
38
|
display: flex;
|
36
39
|
|
37
|
-
/*
|
38
|
-
Allows for an ellipsis on the label. See the linked comment for why it's "3ch"
|
40
|
+
/*
|
41
|
+
Allows for an ellipsis on the label. See the linked comment for why it's "3ch"
|
39
42
|
instead of "0".
|
40
43
|
|
41
|
-
- https://github.com/CrowdStrike/glide-core/pull/317#issuecomment-2297025365
|
44
|
+
- https://github.com/CrowdStrike/glide-core/pull/317#issuecomment-2297025365
|
42
45
|
- https://css-tricks.com/flexbox-truncated-text/#aa-the-solution-is-min-width-0-on-the-flex-child
|
43
46
|
*/
|
44
47
|
min-inline-size: 3ch;
|
@@ -47,10 +50,6 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import
|
|
47
50
|
&.left {
|
48
51
|
justify-content: flex-end;
|
49
52
|
}
|
50
|
-
|
51
|
-
&.hidden {
|
52
|
-
${visuallyHidden};
|
53
|
-
}
|
54
53
|
}
|
55
54
|
|
56
55
|
.optional-tooltip {
|
@@ -83,10 +82,6 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import
|
|
83
82
|
|
84
83
|
display: flex;
|
85
84
|
padding: 0;
|
86
|
-
|
87
|
-
&:focus-visible {
|
88
|
-
${focusOutline};
|
89
|
-
}
|
90
85
|
}
|
91
86
|
|
92
87
|
.label {
|
@@ -0,0 +1,45 @@
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
7
|
+
};
|
8
|
+
import { LitElement } from 'lit';
|
9
|
+
import { LocalizeController } from './localize.js';
|
10
|
+
import { customElement } from 'lit/decorators.js';
|
11
|
+
import { expect, fixture, html } from '@open-wc/testing';
|
12
|
+
import en from '../translations/en.js';
|
13
|
+
let GlideCoreMockComponent = class GlideCoreMockComponent extends LitElement {
|
14
|
+
constructor() {
|
15
|
+
super(...arguments);
|
16
|
+
this.localize = new LocalizeController(this);
|
17
|
+
}
|
18
|
+
static { this.shadowRootOptions = {
|
19
|
+
...LitElement.shadowRootOptions,
|
20
|
+
mode: 'closed',
|
21
|
+
}; }
|
22
|
+
};
|
23
|
+
GlideCoreMockComponent = __decorate([
|
24
|
+
customElement('mock-component')
|
25
|
+
], GlideCoreMockComponent);
|
26
|
+
it('can call any term from en translation if locale is Japanese', async () => {
|
27
|
+
const component = await fixture(
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
29
|
+
html `<mock-component></mock-component>`);
|
30
|
+
component.lang = 'ja';
|
31
|
+
expect(component.localize.lang()).to.equal('ja');
|
32
|
+
const keys = Object.keys(en);
|
33
|
+
for (const key of keys) {
|
34
|
+
expect(component.localize.term(key)).to.be.ok;
|
35
|
+
}
|
36
|
+
});
|
37
|
+
it('can call any term from en translation if locale is French', async () => {
|
38
|
+
const component = await fixture(html `<mock-component></mock-component>`);
|
39
|
+
component.lang = 'fr';
|
40
|
+
expect(component.localize.lang()).to.equal('fr');
|
41
|
+
const keys = Object.keys(en);
|
42
|
+
for (const key of keys) {
|
43
|
+
expect(component.localize.term(key)).to.be.ok;
|
44
|
+
}
|
45
|
+
});
|
package/dist/menu.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
var __decorate=this&&this.__decorate||function(e,t,i,o){var n,s=arguments.length,l=s<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,i):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)l=Reflect.decorate(e,t,i,o);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(l=(s<3?n(l):s>3?n(t,i,l):n(t,i))||l);return s>3&&l&&Object.defineProperty(t,i,l),l};import{LitElement,html}from"lit";import{autoUpdate,computePosition,flip,offset}from"@floating-ui/dom";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property}from"lit/decorators.js";import{nanoid}from"nanoid";import GlideCoreMenuButton from"./menu.button.js";import GlideCoreMenuLink from"./menu.link.js";import GlideCoreMenuOptions from"./menu.options.js";import ow,{owSlot,owSlotType}from"./library/ow.js";import styles from"./menu.styles.js";let GlideCoreMenu=class GlideCoreMenu extends LitElement{constructor(){super(...arguments),this.placement="bottom-start",this.#e=createRef(),this.#t=createRef(),this.#i=!1,this.#o=!1,this.#n="large",this.#s=createRef(),this.#l=e=>{e.target&&e.target instanceof Node&&this.contains(e.target)||(this.open=!1,this.#a&&(this.#a.ariaActivedescendant=""))}}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}get open(){return this.#o}set open(e){this.#o=e,e&&!this.isTargetDisabled?this.#r():this.#c()}get size(){return this.#n}set size(e){this.#n=e,this.#a&&(this.#a.privateSize=e)}connectedCallback(){super.connectedCallback(),document.addEventListener("click",this.#l,{capture:!0})}createRenderRoot(){return this.#d=super.createRenderRoot(),this.#d}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("click",this.#l,{capture:!0})}firstUpdated(){ow(this.#a,ow.object.instanceOf(GlideCoreMenuOptions)),owSlot(this.#t.value),owSlot(this.#s.value),owSlotType(this.#t.value,[GlideCoreMenuOptions]),this.#t.value.popover="manual";const e=this.#h?.at(0);this.open&&e&&!this.isTargetDisabled&&(e.privateActive=!0,this.#r())}focus(e){this.#p&&"focus"in this.#p&&this.#p?.focus(e)}get isTargetDisabled(){const e=this.#p&&"disabled"in this.#p&&this.#p.disabled,t=this.#p&&"true"===this.#p.ariaDisabled;return Boolean(e)||Boolean(t)}render(){return html`<div class="component" @focusout="${this.#u}" ${ref(this.#e)}><slot class="target-slot" name="target" @click="${this.#m}" @keydown="${this.#f}" @slotchange="${this.#E}" ${ref(this.#s)}></slot><slot class="default-slot" @click="${this.#v}" @focusin="${this.#g}" @keydown="${this.#f}" @mouseover="${this.#w}" @slotchange="${this.#S}" ${ref(this.#t)}></slot></div>`}#C;#e;#t;#i;#o;#d;#n;#s;get#y(){return this.#h?.find((({privateActive:e})=>e))}#l;#c(){this.#C?.(),this.#a&&(this.#a.ariaActivedescendant=""),this.#p&&(this.#p.ariaExpanded="false"),this.#t.value?.hidePopover()}get#a(){const e=this.#t.value?.assignedElements().at(0);return e instanceof GlideCoreMenuOptions?e:null}#S(){ow(this.#a,ow.object.instanceOf(GlideCoreMenuOptions)),owSlot(this.#t.value),owSlotType(this.#t.value,[GlideCoreMenuOptions]);const e=this.#h?.at(0);e&&(e.privateActive=!0),this.#a.privateSize=this.size}#v(){this.open=!1}#g(e){(e.target instanceof GlideCoreMenuButton||e.target instanceof GlideCoreMenuLink)&&this.#y&&this.#a&&(e.target.privateActive=!0,this.#
|
1
|
+
var __decorate=this&&this.__decorate||function(e,t,i,o){var n,s=arguments.length,l=s<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,i):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)l=Reflect.decorate(e,t,i,o);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(l=(s<3?n(l):s>3?n(t,i,l):n(t,i))||l);return s>3&&l&&Object.defineProperty(t,i,l),l};import{LitElement,html}from"lit";import{autoUpdate,computePosition,flip,offset}from"@floating-ui/dom";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property}from"lit/decorators.js";import{nanoid}from"nanoid";import GlideCoreMenuButton from"./menu.button.js";import GlideCoreMenuLink from"./menu.link.js";import GlideCoreMenuOptions from"./menu.options.js";import ow,{owSlot,owSlotType}from"./library/ow.js";import styles from"./menu.styles.js";let GlideCoreMenu=class GlideCoreMenu extends LitElement{constructor(){super(...arguments),this.placement="bottom-start",this.#e=createRef(),this.#t=createRef(),this.#i=!1,this.#o=!1,this.#n="large",this.#s=createRef(),this.#l=e=>{e.target&&e.target instanceof Node&&this.contains(e.target)||(this.open=!1,this.#a&&(this.#a.ariaActivedescendant=""))}}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}get open(){return this.#o}set open(e){this.#o=e,e&&!this.isTargetDisabled?this.#r():this.#c()}get size(){return this.#n}set size(e){this.#n=e,this.#a&&(this.#a.privateSize=e)}connectedCallback(){super.connectedCallback(),document.addEventListener("click",this.#l,{capture:!0})}createRenderRoot(){return this.#d=super.createRenderRoot(),this.#d}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener("click",this.#l,{capture:!0})}firstUpdated(){ow(this.#a,ow.object.instanceOf(GlideCoreMenuOptions)),owSlot(this.#t.value),owSlot(this.#s.value),owSlotType(this.#t.value,[GlideCoreMenuOptions]),this.#t.value.popover="manual";const e=this.#h?.at(0);this.open&&e&&!this.isTargetDisabled&&(e.privateActive=!0,this.#r())}focus(e){this.#p&&"focus"in this.#p&&this.#p?.focus(e)}get isTargetDisabled(){const e=this.#p&&"disabled"in this.#p&&this.#p.disabled,t=this.#p&&"true"===this.#p.ariaDisabled;return Boolean(e)||Boolean(t)}render(){return html`<div class="component" @focusout="${this.#u}" ${ref(this.#e)}><slot class="target-slot" name="target" @click="${this.#m}" @keydown="${this.#f}" @slotchange="${this.#E}" ${ref(this.#s)}></slot><slot class="default-slot" @click="${this.#v}" @focusin="${this.#g}" @keydown="${this.#f}" @mouseover="${this.#w}" @slotchange="${this.#S}" ${ref(this.#t)}></slot></div>`}#C;#e;#t;#i;#o;#d;#n;#s;get#y(){return this.#h?.find((({privateActive:e})=>e))}#l;#c(){this.#C?.(),this.#a&&(this.#a.ariaActivedescendant=""),this.#p&&(this.#p.ariaExpanded="false"),this.#t.value?.hidePopover()}get#a(){const e=this.#t.value?.assignedElements().at(0);return e instanceof GlideCoreMenuOptions?e:null}#S(){ow(this.#a,ow.object.instanceOf(GlideCoreMenuOptions)),owSlot(this.#t.value),owSlotType(this.#t.value,[GlideCoreMenuOptions]);const e=this.#h?.at(0);e&&(e.privateActive=!0),this.#a.privateSize=this.size}#v(){this.open=!1}#g(e){(e.target instanceof GlideCoreMenuButton||e.target instanceof GlideCoreMenuLink)&&this.#y&&this.#a&&(this.#y.privateActive=!1,e.target.privateActive=!0,this.#a.ariaActivedescendant=e.target.id)}#w(e){if(e.target instanceof GlideCoreMenuLink||e.target instanceof GlideCoreMenuButton){if(this.#h)for(const t of this.#h)t.privateActive=t===e.target;this.#a&&(this.#a.ariaActivedescendant=e.target.id)}}#u(e){const t=e.relatedTarget instanceof HTMLElement&&this.#d?.contains(e.relatedTarget),i=e.relatedTarget instanceof GlideCoreMenuOptions,o=e.relatedTarget instanceof GlideCoreMenuButton||e.relatedTarget instanceof GlideCoreMenuLink;t||i||o||(this.open=!1)}#f(e){if(ow(this.#a,ow.object.instanceOf(GlideCoreMenuOptions)),[" ","Enter"].includes(e.key)&&this.open)return this.open=!1,this.focus(),this.#y?.dispatchEvent(new PointerEvent("click",{bubbles:!0})),void(this.#i=!0);if(["Escape"].includes(e.key)&&this.open)return this.open=!1,void this.focus();if(["ArrowUp","ArrowDown"].includes(e.key)&&!this.open&&this.#y)return e.preventDefault(),this.open=!0,void(this.#a.ariaActivedescendant=this.#y.id);if(this.open){ow(this.#h,ow.array),ow(this.#a,ow.object.instanceOf(GlideCoreMenuOptions)),ow(this.#y,ow.object.is((e=>e instanceof GlideCoreMenuButton||e instanceof GlideCoreMenuLink)));const t=this.#h.indexOf(this.#y);if("ArrowUp"===e.key&&!e.metaKey){e.preventDefault();const i=this.#h.at(t-1);return void(i&&0!==t&&(this.#y.privateActive=!1,this.#a.ariaActivedescendant=i.id,i.privateActive=!0))}if("ArrowDown"===e.key&&!e.metaKey){e.preventDefault();const i=this.#h.at(t+1);return void(i&&(this.#y.privateActive=!1,this.#a.ariaActivedescendant=i.id,i.privateActive=!0))}if("ArrowUp"===e.key&&e.metaKey||"Home"===e.key||"PageUp"===e.key){e.preventDefault();const t=this.#h.at(0);return void(t&&(this.#y.privateActive=!1,this.#a.ariaActivedescendant=t.id,t.privateActive=!0))}if("ArrowDown"===e.key&&e.metaKey||"End"===e.key||"PageDown"===e.key){e.preventDefault();const t=this.#h.at(-1);return void(t&&(this.#y.privateActive=!1,this.#a.ariaActivedescendant=t.id,t.privateActive=!0))}}}#E(){owSlot(this.#s.value),ow(this.#p,ow.object.instanceOf(Element)),ow(this.#a,ow.object.instanceOf(GlideCoreMenuOptions));new MutationObserver((e=>{e.some((e=>"disabled"===e.attributeName||"aria-disabled"===e.attributeName))&&(this.open&&!this.isTargetDisabled?this.#r():this.#c())})).observe(this.#p,{attributes:!0}),this.#p.ariaHasPopup="true",this.#p.id=nanoid(),this.#p.setAttribute("aria-controls",this.#a.id),this.#a.ariaLabelledby=this.#p.id,this.open&&!this.isTargetDisabled?this.#r():this.#c()}#m(){this.isTargetDisabled?this.#c():this.#i?this.#i=!1:this.#h&&this.#h.length>0&&(this.open=!this.open)}get#h(){let e=this.#t.value?.assignedElements()?.at(0)?.children;const t=e?.[0];if(t instanceof HTMLSlotElement&&(e=t.assignedElements()),e)return[...e].filter((e=>e instanceof GlideCoreMenuLink||e instanceof GlideCoreMenuButton))}#r(){this.#C?.(),this.#p&&this.#t.value&&(this.#C=autoUpdate(this.#p,this.#t.value,(()=>{(async()=>{if(this.#p&&this.#t.value){const{x:e,y:t,placement:i}=await computePosition(this.#p,this.#t.value,{placement:this.placement,middleware:[offset({mainAxis:Number.parseFloat(window.getComputedStyle(document.body).getPropertyValue("--glide-core-spacing-xxs"))*Number.parseFloat(window.getComputedStyle(document.documentElement).fontSize)}),flip()]});this.#t.value.dataset.placement=i,Object.assign(this.#t.value.style,{left:`${e}px`,top:`${t}px`})}this.#t.value?.showPopover(),this.#a&&this.#y?.id&&(this.#a.ariaActivedescendant=this.#y.id),this.#p&&(this.#p.ariaExpanded="true")})()})))}get#p(){return this.#s.value?.assignedElements().at(0)}};__decorate([property({reflect:!0,type:Boolean})],GlideCoreMenu.prototype,"open",null),__decorate([property({reflect:!0})],GlideCoreMenu.prototype,"placement",void 0),__decorate([property({reflect:!0})],GlideCoreMenu.prototype,"size",null),GlideCoreMenu=__decorate([customElement("glide-core-menu")],GlideCoreMenu);export default GlideCoreMenu;
|
package/dist/menu.link.styles.js
CHANGED
package/dist/menu.styles.js
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
import{css}from"lit";export default[css`
|
1
|
+
import{css}from"lit";import menuOpeningAnimation from"./styles/menu-opening-animation.js";export default[css`
|
2
|
+
${menuOpeningAnimation(".default-slot:popover-open")}
|
3
|
+
`,css`
|
2
4
|
:host {
|
3
5
|
/* Contains elements with "padding", "margin", and "width". Inline by default. */
|
4
6
|
display: inline-block;
|
package/dist/menu.test.events.js
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
import './menu.js';
|
3
3
|
import './menu.link.js';
|
4
4
|
import './menu.options.js';
|
5
|
-
import {
|
5
|
+
import { assert, expect, fixture, html, oneEvent } from '@open-wc/testing';
|
6
6
|
import { sendKeys } from '@web/test-runner-commands';
|
7
7
|
import sinon from 'sinon';
|
8
8
|
it('dispatches one link "click" event when a link is selected via click', async () => {
|
@@ -123,99 +123,3 @@ it('dispatches one button "click" event when a button is selected via Enter', as
|
|
123
123
|
expect(event.target).to.equal(button);
|
124
124
|
expect(spy.callCount).to.equal(1);
|
125
125
|
});
|
126
|
-
it('does not dispatch a link "click" event when opened via click', async () => {
|
127
|
-
const component = await fixture(html `<glide-core-menu>
|
128
|
-
<button slot="target">Target</button>
|
129
|
-
|
130
|
-
<glide-core-menu-options>
|
131
|
-
<glide-core-menu-link label="Link"></glide-core-menu-link>
|
132
|
-
</glide-core-menu-options>
|
133
|
-
</glide-core-menu>`);
|
134
|
-
const spy = sinon.spy();
|
135
|
-
const link = component.querySelector('glide-core-menu-link');
|
136
|
-
const target = component.querySelector('button');
|
137
|
-
link?.addEventListener('click', spy);
|
138
|
-
target?.click();
|
139
|
-
await aTimeout(0);
|
140
|
-
expect(spy.callCount).to.equal(0);
|
141
|
-
});
|
142
|
-
it('does not dispatch a button "click" event when opened via click', async () => {
|
143
|
-
const component = await fixture(html `<glide-core-menu>
|
144
|
-
<button slot="target">Target</button>
|
145
|
-
|
146
|
-
<glide-core-menu-options>
|
147
|
-
<glide-core-menu-button label="Button"></glide-core-menu-button>
|
148
|
-
</glide-core-menu-options>
|
149
|
-
</glide-core-menu>`);
|
150
|
-
const spy = sinon.spy();
|
151
|
-
const button = component.querySelector('glide-core-menu-button');
|
152
|
-
const target = component.querySelector('button');
|
153
|
-
button?.addEventListener('click', spy);
|
154
|
-
target?.click();
|
155
|
-
await aTimeout(0);
|
156
|
-
expect(spy.callCount).to.equal(0);
|
157
|
-
});
|
158
|
-
it('does not dispatch a link "click" event when opened via Space', async () => {
|
159
|
-
const component = await fixture(html `<glide-core-menu>
|
160
|
-
<button slot="target">Target</button>
|
161
|
-
|
162
|
-
<glide-core-menu-options>
|
163
|
-
<glide-core-menu-link label="Link"></glide-core-menu-link>
|
164
|
-
</glide-core-menu-options>
|
165
|
-
</glide-core-menu>`);
|
166
|
-
const spy = sinon.spy();
|
167
|
-
const link = component.querySelector('glide-core-menu-link');
|
168
|
-
link?.addEventListener('click', spy);
|
169
|
-
component.focus();
|
170
|
-
sendKeys({ press: 'Space' });
|
171
|
-
await aTimeout(0);
|
172
|
-
expect(spy.callCount).to.equal(0);
|
173
|
-
});
|
174
|
-
it('does not dispatch a button "click" event when opened via Space', async () => {
|
175
|
-
const component = await fixture(html `<glide-core-menu>
|
176
|
-
<button slot="target">Target</button>
|
177
|
-
|
178
|
-
<glide-core-menu-options>
|
179
|
-
<glide-core-menu-button label="Link"></glide-core-menu-button>
|
180
|
-
</glide-core-menu-options>
|
181
|
-
</glide-core-menu>`);
|
182
|
-
const spy = sinon.spy();
|
183
|
-
const button = component.querySelector('glide-core-menu-button');
|
184
|
-
button?.addEventListener('click', spy);
|
185
|
-
component.focus();
|
186
|
-
sendKeys({ press: 'Space' });
|
187
|
-
await aTimeout(0);
|
188
|
-
expect(spy.callCount).to.equal(0);
|
189
|
-
});
|
190
|
-
it('does not dispatch a link "click" event when opened via Enter', async () => {
|
191
|
-
const component = await fixture(html `<glide-core-menu>
|
192
|
-
<button slot="target">Target</button>
|
193
|
-
|
194
|
-
<glide-core-menu-options>
|
195
|
-
<glide-core-menu-link label="Link"></glide-core-menu-link>
|
196
|
-
</glide-core-menu-options>
|
197
|
-
</glide-core-menu>`);
|
198
|
-
const spy = sinon.spy();
|
199
|
-
const link = component.querySelector('glide-core-menu-link');
|
200
|
-
link?.addEventListener('click', spy);
|
201
|
-
component.focus();
|
202
|
-
sendKeys({ press: 'Enter' });
|
203
|
-
await aTimeout(0);
|
204
|
-
expect(spy.callCount).to.equal(0);
|
205
|
-
});
|
206
|
-
it('does not dispatch a button "click" event when opened via Enter', async () => {
|
207
|
-
const component = await fixture(html `<glide-core-menu>
|
208
|
-
<button slot="target">Target</button>
|
209
|
-
|
210
|
-
<glide-core-menu-options>
|
211
|
-
<glide-core-menu-button label="Link"></glide-core-menu-button>
|
212
|
-
</glide-core-menu-options>
|
213
|
-
</glide-core-menu>`);
|
214
|
-
const spy = sinon.spy();
|
215
|
-
const button = component.querySelector('glide-core-menu-button');
|
216
|
-
button?.addEventListener('click', spy);
|
217
|
-
component.focus();
|
218
|
-
sendKeys({ press: 'Enter' });
|
219
|
-
await aTimeout(0);
|
220
|
-
expect(spy.callCount).to.equal(0);
|
221
|
-
});
|
package/dist/menu.test.focus.js
CHANGED
@@ -69,7 +69,7 @@ it('remains open when an option is focused', async () => {
|
|
69
69
|
expect(component.open).to.be.true;
|
70
70
|
expect(defaultSlot?.checkVisibility()).to.be.true;
|
71
71
|
});
|
72
|
-
it('sets
|
72
|
+
it('sets an inactive option as active when focused', async () => {
|
73
73
|
const component = await fixture(html `<glide-core-menu open>
|
74
74
|
<button slot="target">Target</button>
|
75
75
|
|
@@ -81,12 +81,35 @@ it('sets the focused option as active', async () => {
|
|
81
81
|
// Wait for Floating UI.
|
82
82
|
await aTimeout(0);
|
83
83
|
component.focus();
|
84
|
-
const
|
84
|
+
const button = component.querySelector('glide-core-menu-button');
|
85
85
|
const link = component.querySelector('glide-core-menu-link');
|
86
86
|
const options = component.querySelector('glide-core-menu-options');
|
87
87
|
link?.focus();
|
88
88
|
await elementUpdated(component);
|
89
|
-
expect(target?.privateActive).to.be.false;
|
90
89
|
expect(link?.privateActive).to.be.true;
|
90
|
+
expect(button?.privateActive).to.be.false;
|
91
91
|
expect(options?.getAttribute('aria-activedescendant')).to.equal(link?.id);
|
92
92
|
});
|
93
|
+
// Kind of an odd test. There's a comment in `#onDefaultSlotFocusin` that
|
94
|
+
// explains it.
|
95
|
+
it('sets an already active option as active when focused', async () => {
|
96
|
+
const component = await fixture(html `<glide-core-menu open>
|
97
|
+
<button slot="target">Target</button>
|
98
|
+
|
99
|
+
<glide-core-menu-options>
|
100
|
+
<glide-core-menu-button label="Button"></glide-core-menu-button>
|
101
|
+
<glide-core-menu-link label="Link"></glide-core-menu-link>
|
102
|
+
</glide-core-menu-options>
|
103
|
+
</glide-core-menu>`);
|
104
|
+
// Wait for Floating UI.
|
105
|
+
await aTimeout(0);
|
106
|
+
component.focus();
|
107
|
+
const button = component.querySelector('glide-core-menu-button');
|
108
|
+
const link = component.querySelector('glide-core-menu-link');
|
109
|
+
const options = component.querySelector('glide-core-menu-options');
|
110
|
+
button?.focus();
|
111
|
+
await elementUpdated(component);
|
112
|
+
expect(button?.privateActive).to.be.true;
|
113
|
+
expect(link?.privateActive).to.be.false;
|
114
|
+
expect(options?.getAttribute('aria-activedescendant')).to.equal(button?.id);
|
115
|
+
});
|
@@ -712,6 +712,7 @@ it('activates the first option on Meta + ArrowUp', async () => {
|
|
712
712
|
links[1].dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
|
713
713
|
await sendKeys({ down: 'Meta' });
|
714
714
|
await sendKeys({ press: 'ArrowUp' });
|
715
|
+
await sendKeys({ up: 'Meta' });
|
715
716
|
expect(links[0].privateActive).to.be.true;
|
716
717
|
expect(links[1].privateActive).to.be.false;
|
717
718
|
expect(options?.getAttribute('aria-activedescendant')).to.equal(links[0].id);
|
@@ -872,6 +873,7 @@ it('does not wrap on Meta + ArrowUp', async () => {
|
|
872
873
|
await aTimeout(0);
|
873
874
|
await sendKeys({ down: 'Meta' });
|
874
875
|
await sendKeys({ press: 'ArrowUp' });
|
876
|
+
await sendKeys({ up: 'Meta' });
|
875
877
|
const link = component.querySelector('glide-core-menu-link');
|
876
878
|
expect(link?.privateActive).to.be.true;
|
877
879
|
});
|
@@ -910,5 +912,6 @@ it('does not wrap on ArrowDown', async () => {
|
|
910
912
|
options[1].dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
|
911
913
|
await sendKeys({ down: 'Meta' });
|
912
914
|
await sendKeys({ press: 'ArrowDown' });
|
915
|
+
await sendKeys({ up: 'Meta' });
|
913
916
|
expect(options[1].privateActive).to.be.true;
|
914
917
|
});
|
package/dist/modal.d.ts
CHANGED
@@ -10,10 +10,6 @@ declare global {
|
|
10
10
|
*
|
11
11
|
* @event close - Emitted when the Modal closes.
|
12
12
|
*
|
13
|
-
* @function showModal - A method on the `glide-core-modal` component to open the Modal programmatically.
|
14
|
-
*
|
15
|
-
* @function close - A method on the `glide-core-modal` component to close the Modal programmatically.
|
16
|
-
*
|
17
13
|
* @slot - The content of the modal.
|
18
14
|
*
|
19
15
|
* @slot header-actions - A slot for placing additional header actions. These are co-located with the close button.
|
@@ -36,9 +32,6 @@ export default class GlideCoreModal extends LitElement {
|
|
36
32
|
showBackButton: boolean;
|
37
33
|
/** Fixed-size options for the Modal. */
|
38
34
|
size?: 'small' | 'medium' | 'large' | 'xlarge';
|
39
|
-
/**
|
40
|
-
* Event called by consumers to programmatically close the Modal.
|
41
|
-
*/
|
42
35
|
close(): void;
|
43
36
|
connectedCallback(): void;
|
44
37
|
disconnectedCallback(): void;
|
@@ -8,28 +8,28 @@ it('registers', async () => {
|
|
8
8
|
expect(window.customElements.get('glide-core-modal-icon-button')).to.equal(GlideCoreModalIconButton);
|
9
9
|
});
|
10
10
|
it('is accessible', async () => {
|
11
|
-
const
|
12
|
-
await expect(
|
11
|
+
const component = await fixture(html `<glide-core-modal-icon-button>Test</glide-core-modal-icon-button>`);
|
12
|
+
await expect(component).to.be.accessible();
|
13
13
|
});
|
14
14
|
it('renders and sets default attributes', async () => {
|
15
|
-
const
|
15
|
+
const component = await fixture(html `
|
16
16
|
<glide-core-modal-icon-button>Test</glide-core-modal-icon-button>
|
17
17
|
`);
|
18
|
-
expect(
|
19
|
-
const buttonElement =
|
18
|
+
expect(component).to.be.ok;
|
19
|
+
const buttonElement = component.shadowRoot?.querySelector('glide-core-icon-button');
|
20
20
|
expect(buttonElement).to.be.not.null;
|
21
21
|
expect(buttonElement).to.have.attribute('variant', 'tertiary');
|
22
22
|
});
|
23
23
|
it('adds an accessible label when given', async () => {
|
24
|
-
const
|
24
|
+
const component = await fixture(html `<glide-core-modal-icon-button label="test-label"
|
25
25
|
>Test</glide-core-modal-icon-button
|
26
26
|
>`);
|
27
|
-
const buttonElement =
|
27
|
+
const buttonElement = component.shadowRoot?.querySelector('glide-core-icon-button');
|
28
28
|
expect(buttonElement).to.have.attribute('label', 'test-label');
|
29
29
|
});
|
30
30
|
it('does not add an acceessible label when not given', async () => {
|
31
|
-
const
|
32
|
-
const buttonElement =
|
31
|
+
const component = await fixture(html `<glide-core-modal-icon-button>Test</glide-core-modal-icon-button>`);
|
32
|
+
const buttonElement = component.shadowRoot?.querySelector('glide-core-icon-button');
|
33
33
|
expect(buttonElement).to.have.attribute('label', '');
|
34
34
|
});
|
35
35
|
it('throws if it does not have a default slot', async () => {
|
package/dist/modal.styles.js
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
import{css}from"lit";import focusOutline from"./styles/focus-outline.js";export default[css`
|
2
|
+
${focusOutline(".body:focus-visible")}
|
3
|
+
`,css`
|
2
4
|
/* When browser support improves, we can have nicer animations with https://caniuse.com/mdn-css_at-rules_starting-style */
|
3
5
|
@keyframes backdrop-fade-in {
|
4
6
|
from {
|
@@ -130,10 +132,6 @@ import{css}from"lit";import focusOutline from"./styles/focus-outline.js";export
|
|
130
132
|
&:focus {
|
131
133
|
outline: none;
|
132
134
|
}
|
133
|
-
|
134
|
-
&:focus-visible {
|
135
|
-
${focusOutline};
|
136
|
-
}
|
137
135
|
}
|
138
136
|
|
139
137
|
.footer {
|
@@ -9,41 +9,41 @@ it('registers', async () => {
|
|
9
9
|
expect(window.customElements.get('glide-core-modal-tertiary-icon')).to.equal(GlideCoreModalTertiaryIcon);
|
10
10
|
});
|
11
11
|
it('is accessible', async () => {
|
12
|
-
const
|
13
|
-
await expect(
|
12
|
+
const component = await fixture(html `<glide-core-modal-tertiary-icon>Test</glide-core-modal-tertiary-icon>`);
|
13
|
+
await expect(component).to.be.accessible();
|
14
14
|
});
|
15
15
|
it('renders and sets default attributes', async () => {
|
16
|
-
const
|
16
|
+
const component = await fixture(html `
|
17
17
|
<glide-core-modal-tertiary-icon>Test</glide-core-modal-tertiary-icon>
|
18
18
|
`);
|
19
|
-
expect(
|
20
|
-
const spanTag =
|
19
|
+
expect(component).to.be.ok;
|
20
|
+
const spanTag = component.shadowRoot?.querySelector('span');
|
21
21
|
expect(spanTag?.getAttribute('tabindex')).to.equal('0');
|
22
|
-
const tooltip =
|
22
|
+
const tooltip = component.shadowRoot?.querySelector('glide-core-tooltip');
|
23
23
|
expect(tooltip).to.not.be.null;
|
24
24
|
});
|
25
25
|
it('adds an accessible label when given', async () => {
|
26
|
-
const
|
26
|
+
const component = await fixture(html `<glide-core-modal-tertiary-icon label="test-label"
|
27
27
|
>Test</glide-core-modal-tertiary-icon
|
28
28
|
>`);
|
29
|
-
const spanElement =
|
29
|
+
const spanElement = component.shadowRoot?.querySelector('span');
|
30
30
|
expect(spanElement).to.have.attribute('aria-label', 'test-label');
|
31
31
|
});
|
32
32
|
it('does not add an acceessible label when not given', async () => {
|
33
|
-
const
|
34
|
-
const spanElement =
|
33
|
+
const component = await fixture(html `<glide-core-modal-tertiary-icon>Test</glide-core-modal-tertiary-icon>`);
|
34
|
+
const spanElement = component.shadowRoot?.querySelector('span');
|
35
35
|
expect(spanElement).to.not.have.attribute('aria-label');
|
36
36
|
});
|
37
37
|
it('sets the tooltip placement when attribute "tooltip-placement" is given', async () => {
|
38
|
-
const
|
38
|
+
const component = await fixture(html `<glide-core-modal-tertiary-icon tooltip-placement="right"
|
39
39
|
>Test</glide-core-modal-tertiary-icon
|
40
40
|
>`);
|
41
|
-
const toolTip =
|
41
|
+
const toolTip = component.shadowRoot?.querySelector('glide-core-tooltip');
|
42
42
|
expect(toolTip).to.have.attribute('placement', 'right');
|
43
43
|
});
|
44
44
|
it('sets the tooltip placement to "bottom" when attribute "tooltip-placement" is not given', async () => {
|
45
|
-
const
|
46
|
-
const toolTip =
|
45
|
+
const component = await fixture(html `<glide-core-modal-tertiary-icon>Test</glide-core-modal-tertiary-icon>`);
|
46
|
+
const toolTip = component.shadowRoot?.querySelector('glide-core-tooltip');
|
47
47
|
expect(toolTip).to.have.attribute('placement', 'bottom');
|
48
48
|
});
|
49
49
|
it('throws if it does not have a default slot', async () => {
|
@@ -3,47 +3,36 @@ import { expect, fixture, html } from '@open-wc/testing';
|
|
3
3
|
import GlideCoreModal from './modal.js';
|
4
4
|
GlideCoreModal.shadowRootOptions.mode = 'open';
|
5
5
|
it('is accessible', async () => {
|
6
|
-
const
|
7
|
-
|
8
|
-
</glide-core-modal>`);
|
9
|
-
await expect(element).to.be.accessible();
|
6
|
+
const component = await fixture(html `<glide-core-modal label="Label">Content</glide-core-modal>`);
|
7
|
+
await expect(component).to.be.accessible();
|
10
8
|
});
|
11
9
|
it('focuses the dialog upon opening', async () => {
|
12
|
-
const
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
const dialogElement = element.shadowRoot?.querySelector('dialog');
|
17
|
-
expect(element.shadowRoot?.activeElement).to.equal(dialogElement);
|
10
|
+
const component = await fixture(html `<glide-core-modal label="Label">Content</glide-core-modal>`);
|
11
|
+
component.showModal();
|
12
|
+
const dialogElement = component.shadowRoot?.querySelector('dialog');
|
13
|
+
expect(component.shadowRoot?.activeElement).to.equal(dialogElement);
|
18
14
|
});
|
19
15
|
it('sets the tabindex on the dialog to "-1"', async () => {
|
20
|
-
const
|
21
|
-
|
22
|
-
|
23
|
-
element.showModal();
|
24
|
-
const dialogElement = element.shadowRoot?.querySelector('dialog');
|
16
|
+
const component = await fixture(html `<glide-core-modal label="Label">Content</glide-core-modal>`);
|
17
|
+
component.showModal();
|
18
|
+
const dialogElement = component.shadowRoot?.querySelector('dialog');
|
25
19
|
expect(dialogElement?.getAttribute('tabindex')).to.equal('-1');
|
26
20
|
});
|
27
21
|
it('sets the "toolbar" role on the header-actions section', async () => {
|
28
|
-
const
|
29
|
-
|
30
|
-
|
31
|
-
element.showModal();
|
32
|
-
const toolbar = element.shadowRoot?.querySelector('[role="toolbar"');
|
22
|
+
const component = await fixture(html `<glide-core-modal label="Label">Content</glide-core-modal>`);
|
23
|
+
component.showModal();
|
24
|
+
const toolbar = component.shadowRoot?.querySelector('[role="toolbar"');
|
33
25
|
expect(toolbar).to.be.ok;
|
34
26
|
});
|
35
27
|
it('sets proper aria attributes and roles on the article', async () => {
|
36
|
-
const
|
37
|
-
|
38
|
-
|
39
|
-
element.showModal();
|
40
|
-
const content = element.shadowRoot?.querySelector('[role="region"');
|
28
|
+
const component = await fixture(html `<glide-core-modal label="Label">Content</glide-core-modal>`);
|
29
|
+
component.showModal();
|
30
|
+
const content = component.shadowRoot?.querySelector('[role="region"');
|
41
31
|
expect(content).to.be.ok;
|
42
32
|
expect(content?.tagName).to.equal('ARTICLE');
|
43
33
|
expect(content?.getAttribute('aria-labelledby')).to.equal('heading');
|
44
34
|
expect(content?.getAttribute('tabindex')).to.equal('0');
|
45
|
-
|
46
|
-
const label = element.shadowRoot?.querySelector('#heading');
|
35
|
+
const label = component.shadowRoot?.querySelector('#heading');
|
47
36
|
expect(label).to.be.ok;
|
48
37
|
expect(label?.tagName).to.equal('H2');
|
49
38
|
});
|