@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.
- package/dist/accordion.js +1 -1
- package/dist/accordion.styles.js +4 -4
- package/dist/accordion.test.basics.js +109 -0
- package/dist/accordion.test.events.js +39 -0
- package/dist/button-group.button.js +1 -1
- package/dist/button-group.button.styles.js +4 -4
- package/dist/button-group.button.test.basics.js +169 -0
- package/dist/button-group.button.test.events.js +73 -0
- package/dist/button-group.js +1 -1
- package/dist/button-group.styles.js +3 -3
- package/dist/button-group.test.basics.js +268 -0
- package/dist/button-group.test.events.js +291 -0
- package/dist/button.js +1 -1
- package/dist/button.styles.js +4 -4
- package/dist/button.test.basics.js +196 -0
- package/dist/button.test.events.js +25 -0
- package/dist/button.test.form.js +49 -0
- package/dist/checkbox-group.js +1 -1
- package/dist/checkbox-group.styles.js +2 -2
- package/dist/checkbox-group.test.basics.js +119 -0
- package/dist/checkbox-group.test.events.js +110 -0
- package/dist/checkbox-group.test.focus.js +45 -0
- package/dist/checkbox-group.test.form.js +130 -0
- package/dist/checkbox-group.test.validity.js +75 -0
- package/dist/checkbox.js +1 -1
- package/dist/checkbox.styles.js +3 -3
- package/dist/checkbox.test.basics.js +89 -0
- package/dist/checkbox.test.events.js +87 -0
- package/dist/checkbox.test.focus.js +38 -0
- package/dist/checkbox.test.form.js +115 -0
- package/dist/checkbox.test.states.js +62 -0
- package/dist/checkbox.test.validity.js +51 -0
- package/dist/drawer.d.ts +2 -2
- package/dist/drawer.js +1 -15
- package/dist/drawer.styles.js +18 -3
- package/dist/drawer.test.accessibility.js +22 -0
- package/dist/drawer.test.basics.js +43 -0
- package/dist/drawer.test.closing.js +37 -0
- package/dist/drawer.test.events.js +52 -0
- package/dist/drawer.test.methods.js +34 -0
- package/dist/dropdown.d.ts +4 -2
- package/dist/dropdown.js +1 -1
- package/dist/dropdown.option.d.ts +1 -3
- package/dist/dropdown.option.js +1 -1
- package/dist/dropdown.option.styles.js +2 -2
- package/dist/dropdown.option.test.basics.js +59 -0
- package/dist/dropdown.option.test.basics.multiple.js +26 -0
- package/dist/dropdown.option.test.basics.single.js +20 -0
- package/dist/dropdown.option.test.events.js +27 -0
- package/dist/dropdown.option.test.focus.js +11 -0
- package/dist/dropdown.option.test.interactions.multiple.js +87 -0
- package/dist/dropdown.option.test.interactions.single.js +22 -0
- package/dist/dropdown.styles.js +28 -9
- package/dist/dropdown.test.basics.filterable.js +84 -0
- package/dist/dropdown.test.basics.js +233 -0
- package/dist/dropdown.test.basics.multiple.js +270 -0
- package/dist/dropdown.test.basics.single.js +79 -0
- package/dist/dropdown.test.events.js +268 -0
- package/dist/dropdown.test.events.multiple.js +130 -0
- package/dist/dropdown.test.focus.d.ts +1 -0
- package/dist/dropdown.test.focus.filterable.js +154 -0
- package/dist/dropdown.test.focus.js +18 -0
- package/dist/dropdown.test.focus.multiple.js +181 -0
- package/dist/dropdown.test.focus.single.js +53 -0
- package/dist/dropdown.test.form.js +140 -0
- package/dist/dropdown.test.form.multiple.js +149 -0
- package/dist/dropdown.test.form.single.js +128 -0
- package/dist/dropdown.test.interactions.filterable.js +385 -0
- package/dist/dropdown.test.interactions.js +446 -0
- package/dist/dropdown.test.interactions.multiple.js +908 -0
- package/dist/dropdown.test.interactions.single.js +466 -0
- package/dist/dropdown.test.validity.js +46 -0
- package/dist/icon-button.js +1 -1
- package/dist/icon-button.styles.js +3 -3
- package/dist/icon-button.test.basics.js +103 -0
- package/dist/icons/checked.js +1 -1
- package/dist/icons/magnifying-glass.js +1 -1
- package/dist/input.js +1 -1
- package/dist/input.styles.js +3 -3
- package/dist/input.test.basics.js +169 -0
- package/dist/input.test.events.js +97 -0
- package/dist/input.test.focus.js +54 -0
- package/dist/input.test.form.js +56 -0
- package/dist/input.test.validity.js +50 -0
- package/dist/label.js +1 -1
- package/dist/label.styles.js +3 -3
- package/dist/label.test.basics.js +129 -0
- package/dist/library/expect-argument-error.js +1 -1
- package/dist/library/ow.js +1 -1
- package/dist/library/ow.test.js +55 -0
- package/dist/menu.button.d.ts +1 -2
- package/dist/menu.button.js +1 -1
- package/dist/menu.button.styles.js +3 -3
- package/dist/menu.button.test.basics.js +42 -0
- package/dist/menu.d.ts +4 -0
- package/dist/menu.js +1 -1
- package/dist/menu.link.d.ts +1 -2
- package/dist/menu.link.js +1 -1
- package/dist/menu.link.styles.js +3 -3
- package/dist/menu.link.test.basics.js +46 -0
- package/dist/menu.styles.js +13 -6
- package/dist/menu.test.basics.js +161 -0
- package/dist/menu.test.focus.d.ts +0 -1
- package/dist/menu.test.focus.js +66 -0
- package/dist/menu.test.interactions.d.ts +0 -1
- package/dist/menu.test.interactions.js +522 -0
- package/dist/modal.icon-button.js +1 -1
- package/dist/modal.icon-button.styles.js +2 -2
- package/dist/modal.icon-button.test.basics.js +45 -0
- package/dist/modal.js +1 -15
- package/dist/modal.styles.js +4 -4
- package/dist/modal.tertiary-icon.js +1 -1
- package/dist/modal.tertiary-icon.test.basics.js +59 -0
- package/dist/modal.test.accessibility.js +48 -0
- package/dist/modal.test.basics.js +203 -0
- package/dist/modal.test.close.js +38 -0
- package/dist/modal.test.events.js +110 -0
- package/dist/modal.test.lock-scroll.js +76 -0
- package/dist/modal.test.methods.js +23 -0
- package/dist/modal.test.scrollbars.js +19 -0
- package/dist/radio-group.js +1 -1
- package/dist/radio-group.styles.js +2 -2
- package/dist/radio-group.test.basics.js +323 -0
- package/dist/radio-group.test.events.js +277 -0
- package/dist/radio-group.test.focus.js +75 -0
- package/dist/radio-group.test.form.js +104 -0
- package/dist/radio-group.test.validity.js +228 -0
- package/dist/radio.js +1 -1
- package/dist/radio.styles.js +4 -4
- package/dist/split-button.d.ts +24 -0
- package/dist/split-button.js +1 -0
- package/dist/split-button.stories.d.ts +17 -0
- package/dist/split-button.styles.d.ts +2 -0
- package/dist/split-button.styles.js +103 -0
- package/dist/split-button.test.basics.d.ts +1 -0
- package/dist/split-button.test.basics.js +84 -0
- package/dist/split-container.d.ts +30 -0
- package/dist/split-container.js +1 -0
- package/dist/split-container.styles.d.ts +2 -0
- package/dist/split-container.styles.js +132 -0
- package/dist/split-container.test.basics.d.ts +3 -0
- package/dist/split-container.test.basics.js +445 -0
- package/dist/split-container.test.interactions.d.ts +1 -0
- package/dist/split-container.test.interactions.js +20 -0
- package/dist/split-link.d.ts +25 -0
- package/dist/split-link.js +1 -0
- package/dist/split-link.test.basics.d.ts +1 -0
- package/dist/split-link.test.basics.js +92 -0
- package/dist/split-link.test.interactions.d.ts +1 -0
- package/dist/split-link.test.interactions.js +19 -0
- package/dist/status-indicator.js +1 -1
- package/dist/status-indicator.styles.js +2 -2
- package/dist/status-indicator.test.basics.js +102 -0
- package/dist/styles/focus-outline.js +1 -4
- package/dist/styles/visually-hidden.js +1 -11
- package/dist/tab.group.js +1 -1
- package/dist/tab.group.styles.js +2 -2
- package/dist/tab.group.test.basics.js +185 -0
- package/dist/tab.js +1 -1
- package/dist/tab.panel.js +1 -1
- package/dist/tab.panel.styles.js +3 -3
- package/dist/tab.styles.js +2 -2
- package/dist/tab.test.basics.js +71 -0
- package/dist/tag.js +1 -1
- package/dist/tag.styles.js +3 -3
- package/dist/tag.test.basics.js +118 -0
- package/dist/tag.test.events.js +16 -0
- package/dist/tag.test.focus.js +11 -0
- package/dist/textarea.js +2 -2
- package/dist/textarea.styles.js +3 -3
- package/dist/textarea.test.basics.js +140 -0
- package/dist/textarea.test.events.js +204 -0
- package/dist/textarea.test.form.js +70 -0
- package/dist/textarea.test.validity.js +83 -0
- package/dist/toasts.js +1 -1
- package/dist/toasts.styles.js +2 -2
- package/dist/toasts.test.basics.js +94 -0
- package/dist/toasts.toast.js +1 -1
- package/dist/toasts.toast.styles.js +5 -2
- package/dist/toasts.toast.test.basics.js +139 -0
- package/dist/toggle.js +1 -1
- package/dist/toggle.styles.js +3 -3
- package/dist/toggle.test.basics.js +64 -0
- package/dist/toggle.test.events.js +29 -0
- package/dist/toggle.test.focus.js +9 -0
- package/dist/toggle.test.states.js +35 -0
- package/dist/tooltip.js +1 -1
- package/dist/tooltip.styles.js +3 -3
- package/dist/tooltip.test.basics.js +64 -0
- package/dist/tooltip.test.interactions.js +78 -0
- package/dist/tree.item.icon-button.js +1 -1
- package/dist/tree.item.icon-button.styles.js +2 -2
- package/dist/tree.item.icon-button.test.basics.js +13 -0
- package/dist/tree.item.js +1 -1
- package/dist/tree.item.menu.js +1 -1
- package/dist/tree.item.menu.styles.js +2 -2
- package/dist/tree.item.menu.test.basics.js +34 -0
- package/dist/tree.item.styles.js +2 -2
- package/dist/tree.item.test.basics.js +102 -0
- package/dist/tree.js +1 -1
- package/dist/tree.styles.js +2 -2
- package/dist/tree.test.aria.js +86 -0
- package/dist/tree.test.basics.js +123 -0
- package/dist/tree.test.events.js +19 -0
- package/dist/tree.test.focus.js +261 -0
- package/package.json +20 -18
- /package/dist/{dropdown.option.test.focus.multiple.d.ts → dropdown.option.test.focus.d.ts} +0 -0
- /package/dist/{dropdown.option.test.focus.single.d.ts → dropdown.test.events.multiple.d.ts} +0 -0
package/dist/accordion.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
var __decorate=this&&this.__decorate||function(e,t,o,s){var i,l=arguments.length,a=l<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,o):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,o,s);else for(var n=e.length-1;n>=0;n--)(i=e[n])&&(a=(l<3?i(a):l>3?i(t,o,a):i(t,o))||a);return l>3&&a&&Object.defineProperty(t,o,a),a};import{LitElement,html}from"lit";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property,state}from"lit/decorators.js";import{owSlot}from"./library/ow.js";import styles from"./accordion.styles.js";let GlideCoreAccordion=class GlideCoreAccordion extends LitElement{constructor(){super(...arguments),this.label="",this.open=!1,this.hasPrefixSlot=!1,this.hasSuffixSlot=!1,this.#e=createRef(),this.#t=createRef(),this.#o=createRef(),this.#s=createRef(),this.#i=createRef()}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}firstUpdated(){owSlot(this.#t.value)}render(){return html`<details class="component" ?open="${this.open}" ${ref(this.#o)}><summary class="summary" @click="${this.#l}" data-test="summary"><svg class="chevron" width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M6 9L12 15L18 9" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg><div class="${classMap({"heading-box":!0,"heading-box-with-prefix":this.hasPrefixSlot})}" data-test="label"><div class="prefix-slot-box"><slot name="prefix" @slotchange="${this.#a}" ${ref(this.#s)}></slot></div><span class="label">${this.label}</span></div><div class="${classMap({"suffix-slot-box":!0,"suffix-slot-box-with-content":this.hasSuffixSlot})}" data-test="suffix"><slot name="suffix" @slotchange="${this.#n}" ${ref(this.#i)}></slot></div></summary><div class="${classMap({content:!0,"content-with-prefix":this.hasPrefixSlot})}" data-test="content" ${ref(this.#e)}><slot @slotchange="${this.#r}" ${ref(this.#t)}></slot></div></details>`}#e;#t;#o;#s;#i;#r(){owSlot(this.#t.value)}#a(){const e=this.#s.value?.assignedNodes();this.hasPrefixSlot=!!(e&&e.length>0)}#n(){const e=this.#i.value?.assignedNodes();this.hasSuffixSlot=!!(e&&e.length>0)}#l(e){const t=this.#o.value,o=this.#e.value,s=!t.open,i=Number.parseFloat(getComputedStyle(o)?.paddingBottom);if(s)requestAnimationFrame((()=>{o.animate({height:["0px",o.offsetHeight-i+"px"],opacity:[0,1]},{duration:150,easing:"ease-in"}),this.dispatchEvent(new CustomEvent("toggle",{detail:{newState:"open",oldState:"closed"}}))}));else{e.preventDefault();o.animate({height:[o.offsetHeight-i+"px","0px"],opacity:[1,0]},{duration:100,easing:"ease-out"}).addEventListener("finish",(()=>{t.open=!1,this.dispatchEvent(new CustomEvent("toggle",{detail:{newState:"closed",oldState:"open"}}))}),{once:!0})}}};__decorate([property({reflect:!0})],GlideCoreAccordion.prototype,"label",void 0),__decorate([property({type:Boolean,reflect:!0})],GlideCoreAccordion.prototype,"open",void 0),__decorate([state()],GlideCoreAccordion.prototype,"hasPrefixSlot",void 0),__decorate([state()],GlideCoreAccordion.prototype,"hasSuffixSlot",void 0),GlideCoreAccordion=__decorate([customElement("glide-core-accordion")],GlideCoreAccordion);export default GlideCoreAccordion;
|
package/dist/accordion.styles.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
import{css}from"lit";import focusOutline from"./styles/focus-outline.js";export default[css`
|
2
2
|
.component {
|
3
3
|
border-radius: 0.625rem;
|
4
4
|
box-shadow: var(--glide-core-shadow-sm);
|
@@ -24,7 +24,7 @@
|
|
24
24
|
}
|
25
25
|
|
26
26
|
&:focus-visible {
|
27
|
-
${
|
27
|
+
${focusOutline};
|
28
28
|
}
|
29
29
|
|
30
30
|
&::marker,
|
@@ -56,7 +56,7 @@
|
|
56
56
|
|
57
57
|
.suffix-slot-box {
|
58
58
|
align-items: center;
|
59
|
-
color:
|
59
|
+
color: var(--glide-core-icon-primary);
|
60
60
|
display: flex;
|
61
61
|
gap: 0.625rem;
|
62
62
|
|
@@ -95,4 +95,4 @@
|
|
95
95
|
.content-with-prefix {
|
96
96
|
padding-inline-start: 3.5rem;
|
97
97
|
}
|
98
|
-
`];
|
98
|
+
`];
|
@@ -0,0 +1,109 @@
|
|
1
|
+
import './accordion.js';
|
2
|
+
import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
|
3
|
+
import GlideCoreAccordion from './accordion.js';
|
4
|
+
GlideCoreAccordion.shadowRootOptions.mode = 'open';
|
5
|
+
it('registers', async () => {
|
6
|
+
expect(window.customElements.get('glide-core-accordion')).to.equal(GlideCoreAccordion);
|
7
|
+
});
|
8
|
+
it('is accessible', async () => {
|
9
|
+
const component = await fixture(html `<glide-core-accordion label="label">
|
10
|
+
Inner content
|
11
|
+
</glide-core-accordion>`);
|
12
|
+
await expect(component).to.be.accessible();
|
13
|
+
});
|
14
|
+
it('is closed by default', async () => {
|
15
|
+
const component = await fixture(html `<glide-core-accordion label="label">
|
16
|
+
Inner content
|
17
|
+
</glide-core-accordion>`);
|
18
|
+
const accordion = component.shadowRoot?.querySelector('details');
|
19
|
+
expect(accordion).to.be.ok;
|
20
|
+
expect(accordion?.hasAttribute('open')).to.be.false;
|
21
|
+
});
|
22
|
+
it('defaults to "open" when provided with the attribute', async () => {
|
23
|
+
const component = await fixture(html `<glide-core-accordion label="label" open>
|
24
|
+
Inner content
|
25
|
+
</glide-core-accordion>`);
|
26
|
+
const accordion = component.shadowRoot?.querySelector('details');
|
27
|
+
expect(accordion).to.be.ok;
|
28
|
+
expect(accordion?.hasAttribute('open')).to.be.true;
|
29
|
+
});
|
30
|
+
it('renders the provided "label"', async () => {
|
31
|
+
const component = await fixture(html `<glide-core-accordion label="Accordion Title">
|
32
|
+
Inner content
|
33
|
+
</glide-core-accordion>`);
|
34
|
+
const label = component.shadowRoot?.querySelector('[data-test="label"]');
|
35
|
+
expect(label).to.be.ok;
|
36
|
+
expect(label?.textContent?.trim()).to.equal('Accordion Title');
|
37
|
+
});
|
38
|
+
it('renders the provided default slotted content', async () => {
|
39
|
+
const component = await fixture(html `<glide-core-accordion label="label"
|
40
|
+
><p data-body>Inner content</p></glide-core-accordion
|
41
|
+
>`);
|
42
|
+
const body = component.querySelector('[data-body]');
|
43
|
+
expect(body).to.be.ok;
|
44
|
+
});
|
45
|
+
it('renders with a prefix slot and applies the appropriate classes', async () => {
|
46
|
+
const component = await fixture(html `<glide-core-accordion label="label">
|
47
|
+
Inner content
|
48
|
+
<span slot="prefix" data-prefix>prefix</span>
|
49
|
+
</glide-core-accordion>`);
|
50
|
+
expect(document.querySelector('[data-prefix]')).to.be.ok;
|
51
|
+
expect([
|
52
|
+
...component.shadowRoot.querySelector('[data-test="label"]').classList,
|
53
|
+
]).to.deep.equal(['heading-box', 'heading-box-with-prefix']);
|
54
|
+
expect([
|
55
|
+
...component.shadowRoot.querySelector('[data-test="content"]').classList,
|
56
|
+
]).to.deep.equal(['content', 'content-with-prefix']);
|
57
|
+
});
|
58
|
+
it('does not apply prefix classes when no prefix slot is provided', async () => {
|
59
|
+
const component = await fixture(html `<glide-core-accordion label="label">
|
60
|
+
Inner content
|
61
|
+
</glide-core-accordion>`);
|
62
|
+
expect([
|
63
|
+
...component.shadowRoot.querySelector('[data-test="label"]').classList,
|
64
|
+
]).to.deep.equal(['heading-box']);
|
65
|
+
expect([
|
66
|
+
...component.shadowRoot.querySelector('[data-test="content"]').classList,
|
67
|
+
]).to.deep.equal(['content']);
|
68
|
+
});
|
69
|
+
it('renders with a suffix slot and applies the appropriate class', async () => {
|
70
|
+
const component = await fixture(html `<glide-core-accordion label="label">
|
71
|
+
Inner content
|
72
|
+
<span slot="suffix" data-suffix>suffix</span>
|
73
|
+
</glide-core-accordion>`);
|
74
|
+
expect(component.querySelector('[data-suffix]')).to.be.ok;
|
75
|
+
expect([
|
76
|
+
...component.shadowRoot.querySelector('[data-test="suffix"]').classList,
|
77
|
+
]).to.deep.equal(['suffix-slot-box', 'suffix-slot-box-with-content']);
|
78
|
+
});
|
79
|
+
it('does not apply the suffix class when no suffix slot is provided', async () => {
|
80
|
+
const component = await fixture(html `<glide-core-accordion label="label">
|
81
|
+
Inner content
|
82
|
+
</glide-core-accordion>`);
|
83
|
+
expect([
|
84
|
+
...component.shadowRoot.querySelector('[data-test="suffix"]').classList,
|
85
|
+
]).to.deep.equal(['suffix-slot-box']);
|
86
|
+
});
|
87
|
+
it('renders without prefix and suffix classes after both are removed', async () => {
|
88
|
+
const component = await fixture(html `
|
89
|
+
<glide-core-accordion label="label">
|
90
|
+
Inner content
|
91
|
+
<span slot="prefix">prefix</span>
|
92
|
+
<span slot="suffix">suffix</span>
|
93
|
+
</glide-core-accordion>
|
94
|
+
`);
|
95
|
+
component.querySelector('[slot="prefix"]')?.remove();
|
96
|
+
component.querySelector('[slot="suffix"]')?.remove();
|
97
|
+
await elementUpdated(component);
|
98
|
+
// prefix
|
99
|
+
expect([
|
100
|
+
...component.shadowRoot.querySelector('[data-test="label"]').classList,
|
101
|
+
]).to.deep.equal(['heading-box']);
|
102
|
+
expect([
|
103
|
+
...component.shadowRoot.querySelector('[data-test="content"]').classList,
|
104
|
+
]).to.deep.equal(['content']);
|
105
|
+
// suffix
|
106
|
+
expect([
|
107
|
+
...component.shadowRoot.querySelector('[data-test="suffix"]').classList,
|
108
|
+
]).to.deep.equal(['suffix-slot-box']);
|
109
|
+
});
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import './accordion.js';
|
2
|
+
import { elementUpdated, expect, fixture, html, oneEvent, } from '@open-wc/testing';
|
3
|
+
import GlideCoreAccordion from './accordion.js';
|
4
|
+
GlideCoreAccordion.shadowRootOptions.mode = 'open';
|
5
|
+
it('dispatches a "toggle" event when the Accordion opens', async () => {
|
6
|
+
let hasToggleBeenCalled = false;
|
7
|
+
const component = await fixture(html `<glide-core-accordion label="label">
|
8
|
+
Inner content
|
9
|
+
</glide-core-accordion>`);
|
10
|
+
component.addEventListener('toggle', () => (hasToggleBeenCalled = true));
|
11
|
+
const summary = component.shadowRoot?.querySelector('[data-test="summary"]');
|
12
|
+
expect(summary).to.be.ok;
|
13
|
+
summary?.click();
|
14
|
+
await oneEvent(component, 'toggle');
|
15
|
+
expect(hasToggleBeenCalled).to.be.true;
|
16
|
+
});
|
17
|
+
it('dispatches a "toggle" event when the Accordion closes', async () => {
|
18
|
+
let hasToggleBeenCalled = false;
|
19
|
+
const component = await fixture(html `<glide-core-accordion label="label" open>
|
20
|
+
Inner content
|
21
|
+
</glide-core-accordion>`);
|
22
|
+
component.addEventListener('toggle', () => (hasToggleBeenCalled = true));
|
23
|
+
const summary = component.shadowRoot?.querySelector('[data-test="summary"]');
|
24
|
+
expect(summary).to.be.ok;
|
25
|
+
summary?.click();
|
26
|
+
// Force the animations to complete with javascript
|
27
|
+
// and by triggering a `finish` event ourselves.
|
28
|
+
component.shadowRoot
|
29
|
+
?.querySelector('[data-test="content"]')
|
30
|
+
?.getAnimations()
|
31
|
+
?.at(0)
|
32
|
+
?.finish();
|
33
|
+
component.shadowRoot
|
34
|
+
?.querySelector('[data-test="content"]')
|
35
|
+
?.dispatchEvent(new AnimationEvent('finish'));
|
36
|
+
await elementUpdated(component);
|
37
|
+
await oneEvent(component, 'toggle');
|
38
|
+
expect(hasToggleBeenCalled).to.be.true;
|
39
|
+
});
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
var _a,GlideCoreButtonGroupButton_1,__decorate=this&&this.__decorate||function(t,e,o,i){var s,l=arguments.length,n=l<3?e:null===i?i=Object.getOwnPropertyDescriptor(e,o):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)n=Reflect.decorate(t,e,o,i);else for(var r=t.length-1;r>=0;r--)(s=t[r])&&(n=(l<3?s(n):l>3?s(e,o,n):s(e,o))||n);return l>3&&n&&Object.defineProperty(e,o,n),n};import{LitElement,html}from"lit";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property,state}from"lit/decorators.js";import{owSlot,owSlotType}from"./library/ow.js";import styles from"./button-group.button.styles.js";let GlideCoreButtonGroupButton=class GlideCoreButtonGroupButton extends LitElement{constructor(){super(...arguments),this.selected=!1,this.disabled=!1,this.value="",this.vertical=!1,this.isSingleButton=!1,this.isTabbable=!1,this.position="inner",this.#t=createRef(),this.#e=createRef(),this.#o=createRef()}static{GlideCoreButtonGroupButton_1=this}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}firstUpdated(){if(owSlot(this.#t.value),owSlotType(this.#t.value,[Text]),this.selected&&(this.isTabbable=!0),"icon-only"===this.variant&&owSlot(this.#o.value),this.#i.length>0){if(this.#i.length>1&&this.#i.at(0)===this?this.position="first":this.#i.length>1&&this.#i.at(-1)===this?this.position="last":1===this.#i.length&&(this.isSingleButton=!0),this.#i.every((t=>t.disabled)))return;const t=this.#i.find((t=>!t.disabled&&t.selected));if(t&&t===this)this.isTabbable=!0;else if(!t){const t=this.#i.find((t=>!t.disabled));t&&t===this&&(this.isTabbable=!0)}}}focus(t){this.#e.value?.focus(t)}render(){return html`<li role="radio" aria-checked="${this.selected}" aria-disabled="${this.disabled}" tabindex="${!this.isTabbable||this.disabled?-1:0}" @click="${this.#s}" @keydown="${this.#l}" ${ref(this.#e)} class="${classMap({component:!0,selected:this.selected,disabled:Boolean(this.disabled),[this.position]:!0,vertical:this.vertical,single:this.isSingleButton,"icon-only":"icon-only"===this.variant})}"><slot name="prefix" ${ref(this.#o)}></slot><span class="${classMap({"visually-hidden":"icon-only"===this.variant})}"><slot @slotchange="${this.#n}" ${ref(this.#t)}></slot></span></li>`}willUpdate(t){if(this.hasUpdated&&t.has("selected")){const e=t.get("selected");if(!0===e)this.isTabbable=!1;else if(!1===e){this.isTabbable=!0,this.focus();for(const t of this.#i)t!==this&&t.selected&&(t.selected=!1)}}}#t;#e;#o;get#i(){return[...this.closest("glide-core-button-group")?.querySelectorAll("glide-core-button-group-button")??[]]}#r(t=this){t.dispatchEvent(new CustomEvent("change",{bubbles:!0,detail:t.value})),t.dispatchEvent(new CustomEvent("input",{bubbles:!0,detail:t.value}))}#s(){this.disabled||this.selected||(this.selected=!0,this.#r())}#n(){owSlot(this.#t.value),owSlotType(this.#t.value,[Text])}#l(t){if(!(this.disabled||this.#i.length<2&&" "!==t.key))switch(t.key){case"ArrowUp":case"ArrowLeft":{t.preventDefault(),this.selected=!1;let e=this.previousElementSibling;for(;!e||e instanceof GlideCoreButtonGroupButton_1&&e.disabled;)if(null===e){const t=this.#i.at(-1);t&&(e=t)}else e=e.previousElementSibling;e&&e instanceof GlideCoreButtonGroupButton_1&&(e.selected=!0,this.#r(e));break}case"ArrowDown":case"ArrowRight":{t.preventDefault(),this.selected=!1;let e=this.nextElementSibling;for(;!e||e instanceof GlideCoreButtonGroupButton_1&&e.disabled;)if(null===e){const t=this.#i.at(0);t&&(e=t)}else e=e.nextElementSibling;e&&e instanceof GlideCoreButtonGroupButton_1&&(e.selected=!0,this.#r(e));break}case" ":t.preventDefault(),this.disabled||this.selected||(this.selected=!0,this.#r())}}};__decorate([property({type:Boolean,reflect:!0})],GlideCoreButtonGroupButton.prototype,"selected",void 0),__decorate([property({type:Boolean,reflect:!0})],GlideCoreButtonGroupButton.prototype,"disabled",void 0),__decorate([property()],GlideCoreButtonGroupButton.prototype,"value",void 0),__decorate([property()],GlideCoreButtonGroupButton.prototype,"variant",void 0),__decorate([property({type:Boolean})],GlideCoreButtonGroupButton.prototype,"vertical",void 0),__decorate([state()],GlideCoreButtonGroupButton.prototype,"isSingleButton",void 0),__decorate([state()],GlideCoreButtonGroupButton.prototype,"isTabbable",void 0),__decorate([state()],GlideCoreButtonGroupButton.prototype,"position",void 0),GlideCoreButtonGroupButton=GlideCoreButtonGroupButton_1=__decorate([customElement("glide-core-button-group-button")],GlideCoreButtonGroupButton);export default GlideCoreButtonGroupButton;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
import{css}from"lit";import focusOutline from"./styles/focus-outline.js";import visuallyHidden from"./styles/visually-hidden.js";export default[css`
|
2
2
|
.component {
|
3
3
|
align-items: center;
|
4
4
|
appearance: none;
|
@@ -72,7 +72,7 @@
|
|
72
72
|
border-color: var(--glide-core-border-focus);
|
73
73
|
color: var(--glide-core-color-white);
|
74
74
|
|
75
|
-
${
|
75
|
+
${focusOutline};
|
76
76
|
|
77
77
|
/* create a stacking context so the outline doesn't become obscured behind other elements */
|
78
78
|
transform: translateX(0);
|
@@ -94,6 +94,6 @@
|
|
94
94
|
}
|
95
95
|
|
96
96
|
.visually-hidden {
|
97
|
-
${
|
97
|
+
${visuallyHidden};
|
98
98
|
}
|
99
|
-
`];
|
99
|
+
`];
|
@@ -0,0 +1,169 @@
|
|
1
|
+
import { ArgumentError } from 'ow';
|
2
|
+
import { expect, fixture, html } from '@open-wc/testing';
|
3
|
+
import GlideCoreButtonGroupButton from './button-group.button.js';
|
4
|
+
import sinon from 'sinon';
|
5
|
+
GlideCoreButtonGroupButton.shadowRootOptions.mode = 'open';
|
6
|
+
it('registers', async () => {
|
7
|
+
expect(window.customElements.get('glide-core-button-group-button')).to.equal(GlideCoreButtonGroupButton);
|
8
|
+
});
|
9
|
+
it('is accessible', async () => {
|
10
|
+
const element = await fixture(html `<glide-core-button-group-button value="value"
|
11
|
+
>Button</glide-core-button-group-button
|
12
|
+
>`);
|
13
|
+
await expect(element).to.be.accessible();
|
14
|
+
});
|
15
|
+
it('renders an li with role "radio"', async () => {
|
16
|
+
const element = await fixture(html `<glide-core-button-group-button value="value"
|
17
|
+
>Button</glide-core-button-group-button
|
18
|
+
>`);
|
19
|
+
const liElement = element.shadowRoot?.querySelector('li');
|
20
|
+
expect(liElement).to.have.attribute('role', 'radio');
|
21
|
+
});
|
22
|
+
it('renders "aria-checked" equal to "false" and "tabindex" equal to "-1" by default', async () => {
|
23
|
+
const element = await fixture(html `<glide-core-button-group-button value="value"
|
24
|
+
>Button</glide-core-button-group-button
|
25
|
+
>`);
|
26
|
+
const liElement = element.shadowRoot?.querySelector('li');
|
27
|
+
expect(liElement).to.not.be.null;
|
28
|
+
expect(liElement).to.have.attribute('aria-checked', 'false');
|
29
|
+
expect(liElement).to.have.attribute('tabindex', '-1');
|
30
|
+
});
|
31
|
+
it('renders "aria-checked" equal to "true" and "tabindex" equal to "0" when attribute "selected" exists', async () => {
|
32
|
+
const element = await fixture(html `<glide-core-button-group-button value="value" selected
|
33
|
+
>Button</glide-core-button-group-button
|
34
|
+
>`);
|
35
|
+
const liElement = element.shadowRoot?.querySelector('li');
|
36
|
+
expect(liElement).to.have.attribute('aria-checked', 'true');
|
37
|
+
expect(liElement).to.have.attribute('tabindex', '0');
|
38
|
+
});
|
39
|
+
it('renders two slots, where one has name "prefix", and the other is default with given text', async () => {
|
40
|
+
const element = await fixture(html `<glide-core-button-group-button value="value" selected
|
41
|
+
><span slot="prefix" data-prefix>Prefix</span
|
42
|
+
>Button</glide-core-button-group-button
|
43
|
+
>`);
|
44
|
+
const prefixSlot = element.shadowRoot?.querySelector('slot[name="prefix"]');
|
45
|
+
const defaultSlot = element.shadowRoot?.querySelector('slot:not([name="prefix"])');
|
46
|
+
// verify the slots exist
|
47
|
+
expect(prefixSlot).to.not.be.null;
|
48
|
+
expect(defaultSlot).to.not.be.null;
|
49
|
+
const prefixContent = document.querySelector('[data-prefix]');
|
50
|
+
const content = document.querySelector('glide-core-button-group-button');
|
51
|
+
// verify the content of the slots exist
|
52
|
+
expect(prefixContent).to.not.be.null;
|
53
|
+
expect(prefixContent?.textContent).to.equal('Prefix');
|
54
|
+
expect(content?.textContent).to.contain('Button');
|
55
|
+
});
|
56
|
+
it('is has a disabled presentation when the "disabled" attribute is true', async () => {
|
57
|
+
const element = await fixture(html `<glide-core-button-group-button value="value" disabled
|
58
|
+
>Button</glide-core-button-group-button
|
59
|
+
>`);
|
60
|
+
const liElement = element.shadowRoot?.querySelector('li');
|
61
|
+
expect(liElement).to.have.attribute('aria-disabled', 'true');
|
62
|
+
});
|
63
|
+
it('does not have a disabled presentation when "disabled" attribute is false', async () => {
|
64
|
+
const element = await fixture(html `<glide-core-button-group-button value="value"
|
65
|
+
>Button</glide-core-button-group-button
|
66
|
+
>`);
|
67
|
+
const liElement = element.shadowRoot?.querySelector('li');
|
68
|
+
expect(liElement).to.have.attribute('aria-disabled', 'false');
|
69
|
+
});
|
70
|
+
it('has a vertical presention when the "vertical" attribute is set', async () => {
|
71
|
+
await fixture(html ` <glide-core-button-group-button value="value" vertical
|
72
|
+
>Button</glide-core-button-group-button
|
73
|
+
>`);
|
74
|
+
const buttonElement = document.querySelector('glide-core-button-group-button');
|
75
|
+
const liElement = buttonElement?.shadowRoot?.querySelector('li');
|
76
|
+
expect(liElement).to.have.class('vertical');
|
77
|
+
});
|
78
|
+
it('does not have a vertical presention when the "vertical" attribute is not set', async () => {
|
79
|
+
await fixture(html ` <glide-core-button-group-button value="value"
|
80
|
+
>Button</glide-core-button-group-button
|
81
|
+
>`);
|
82
|
+
const buttonElement = document.querySelector('glide-core-button-group-button');
|
83
|
+
const liElement = buttonElement?.shadowRoot?.querySelector('li');
|
84
|
+
expect(liElement).to.not.have.class('vertical');
|
85
|
+
});
|
86
|
+
it('sets buttons as not tabbable by default', async () => {
|
87
|
+
const element = await fixture(html `<glide-core-button-group-button value="value"
|
88
|
+
>Button</glide-core-button-group-button
|
89
|
+
>`);
|
90
|
+
const liElement = element.shadowRoot?.querySelector('li');
|
91
|
+
await expect(liElement).to.have.attribute('tabindex', '-1');
|
92
|
+
});
|
93
|
+
it('sets a "selected" button as tabbable', async () => {
|
94
|
+
const element = await fixture(html `<glide-core-button-group-button value="value" selected
|
95
|
+
>Button</glide-core-button-group-button
|
96
|
+
>`);
|
97
|
+
const liElement = element.shadowRoot?.querySelector('li');
|
98
|
+
await expect(liElement).to.have.attribute('tabindex', '0');
|
99
|
+
});
|
100
|
+
it('has a presentation for variant "icon-only"', async () => {
|
101
|
+
const element = await fixture(html `<glide-core-button-group-button
|
102
|
+
value="value"
|
103
|
+
selected
|
104
|
+
variant="icon-only"
|
105
|
+
><span slot="prefix">Prefix</span>Button</glide-core-button-group-button
|
106
|
+
>`);
|
107
|
+
const liElement = element.shadowRoot?.querySelector('li');
|
108
|
+
expect(liElement).to.have.class('icon-only');
|
109
|
+
});
|
110
|
+
it('does not apply class "icon-only" when variant "icon-only" is absent', async () => {
|
111
|
+
const element = await fixture(html `<glide-core-button-group-button value="value"
|
112
|
+
>Button</glide-core-button-group-button
|
113
|
+
>`);
|
114
|
+
const liElement = element.shadowRoot?.querySelector('li');
|
115
|
+
expect(liElement).to.not.have.class('icon-only');
|
116
|
+
});
|
117
|
+
it('throws an error when no label is present and variant is `icon-only`', async () => {
|
118
|
+
const spy = sinon.spy();
|
119
|
+
try {
|
120
|
+
await fixture(html `<glide-core-button-group-button
|
121
|
+
value="value"
|
122
|
+
selected
|
123
|
+
variant="icon-only"
|
124
|
+
><span slot="prefix">Prefix</span></glide-core-button-group-button
|
125
|
+
>`);
|
126
|
+
}
|
127
|
+
catch (error) {
|
128
|
+
if (error instanceof ArgumentError) {
|
129
|
+
spy();
|
130
|
+
}
|
131
|
+
}
|
132
|
+
expect(spy.called).to.be.true;
|
133
|
+
});
|
134
|
+
it('throws an error when prefix slot is empty and variant is `icon-only`', async () => {
|
135
|
+
const spy = sinon.spy();
|
136
|
+
// Not sure how to resolve the below, despite the test passing, so disabling console errors for now:
|
137
|
+
//
|
138
|
+
// Browser logs:
|
139
|
+
// An error was thrown in a Promise outside a test. Did you forget to await a function or assertion?
|
140
|
+
sinon.stub(console, 'error'); // Disable console.error
|
141
|
+
try {
|
142
|
+
await fixture(html `<glide-core-button-group-button
|
143
|
+
value="value"
|
144
|
+
selected
|
145
|
+
variant="icon-only"
|
146
|
+
>Button</glide-core-button-group-button
|
147
|
+
>`);
|
148
|
+
}
|
149
|
+
catch (error) {
|
150
|
+
if (error instanceof ArgumentError) {
|
151
|
+
spy();
|
152
|
+
}
|
153
|
+
}
|
154
|
+
expect(spy.called).to.be.true;
|
155
|
+
});
|
156
|
+
it('does not throw an error when prefix slot is empty and no variant is set', async () => {
|
157
|
+
const spy = sinon.spy();
|
158
|
+
try {
|
159
|
+
await fixture(html `<glide-core-button-group-button value="value" selected
|
160
|
+
>Button</glide-core-button-group-button
|
161
|
+
>`);
|
162
|
+
}
|
163
|
+
catch (error) {
|
164
|
+
if (error instanceof ArgumentError) {
|
165
|
+
spy();
|
166
|
+
}
|
167
|
+
}
|
168
|
+
expect(spy.notCalled).to.be.true;
|
169
|
+
});
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import './button-group.button.js';
|
2
|
+
import { expect, fixture, html, oneEvent } from '@open-wc/testing';
|
3
|
+
import { sendKeys } from '@web/test-runner-commands';
|
4
|
+
import GlideCoreButtonGroupButton from './button-group.button.js';
|
5
|
+
import sinon from 'sinon';
|
6
|
+
GlideCoreButtonGroupButton.shadowRootOptions.mode = 'open';
|
7
|
+
it('emits a change event when clicked', async () => {
|
8
|
+
const element = await fixture(html `<glide-core-button-group-button value="value"
|
9
|
+
>Button</glide-core-button-group-button
|
10
|
+
>`);
|
11
|
+
const liElement = element.shadowRoot?.querySelector('li');
|
12
|
+
// This pattern is adopted from https://open-wc.org/docs/testing/helpers/#testing-events
|
13
|
+
// Without setTimeout the test fails. The suspicion is this is related to task scheduling,
|
14
|
+
// however this can be investigated later.
|
15
|
+
setTimeout(() => {
|
16
|
+
liElement?.click();
|
17
|
+
});
|
18
|
+
const changeEvent = await oneEvent(element, 'change');
|
19
|
+
expect(changeEvent instanceof Event).to.be.true;
|
20
|
+
});
|
21
|
+
it('emits an input event when clicked', async () => {
|
22
|
+
const element = await fixture(html `<glide-core-button-group-button value="value"
|
23
|
+
>Button</glide-core-button-group-button
|
24
|
+
>`);
|
25
|
+
const liElement = element.shadowRoot.querySelector('li');
|
26
|
+
setTimeout(() => {
|
27
|
+
liElement?.click();
|
28
|
+
});
|
29
|
+
const inputEvent = await oneEvent(element, 'input');
|
30
|
+
expect(inputEvent instanceof Event).to.be.true;
|
31
|
+
});
|
32
|
+
it('emits a change event when a space key is pressed and is not already selected', async () => {
|
33
|
+
const buttonElement = await fixture(html `<glide-core-button-group-button value="value-1"
|
34
|
+
>Button 1</glide-core-button-group-button
|
35
|
+
>`);
|
36
|
+
buttonElement.focus();
|
37
|
+
setTimeout(async () => {
|
38
|
+
await sendKeys({ press: ' ' });
|
39
|
+
});
|
40
|
+
const changeEvent = await oneEvent(buttonElement, 'change');
|
41
|
+
expect(changeEvent instanceof Event).to.be.true;
|
42
|
+
});
|
43
|
+
it('does not emit change event when a space key is pressed and is selected', async () => {
|
44
|
+
const buttonElement = await fixture(html `<glide-core-button-group-button value="value-1" selected
|
45
|
+
>Button 1</glide-core-button-group-button
|
46
|
+
> `);
|
47
|
+
const spy = sinon.spy();
|
48
|
+
buttonElement.addEventListener('change', spy);
|
49
|
+
buttonElement.focus();
|
50
|
+
await sendKeys({ press: ' ' });
|
51
|
+
expect(spy.notCalled).to.be.true;
|
52
|
+
});
|
53
|
+
it('emits an input event when a space key is pressed and is not already selected', async () => {
|
54
|
+
const buttonElement = await fixture(html ` <glide-core-button-group-button value="value-1"
|
55
|
+
>Button 1</glide-core-button-group-button
|
56
|
+
>`);
|
57
|
+
buttonElement.focus();
|
58
|
+
setTimeout(async () => {
|
59
|
+
await sendKeys({ press: ' ' });
|
60
|
+
});
|
61
|
+
const inputEvent = await oneEvent(buttonElement, 'input');
|
62
|
+
expect(inputEvent instanceof Event).to.be.true;
|
63
|
+
});
|
64
|
+
it('does not emit an input event when a space key is pressed and is selected', async () => {
|
65
|
+
const buttonElement = await fixture(html `<glide-core-button-group-button value="value-1" selected
|
66
|
+
>Button 1</glide-core-button-group-button
|
67
|
+
>`);
|
68
|
+
buttonElement.focus();
|
69
|
+
const spy = sinon.spy();
|
70
|
+
buttonElement.addEventListener('input', spy);
|
71
|
+
await sendKeys({ press: ' ' });
|
72
|
+
expect(spy.notCalled).to.be.true;
|
73
|
+
});
|
package/dist/button-group.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
var __decorate=this&&this.__decorate||function(t,e,o,r){var i,l=arguments.length,s=l<3?e:null===r?r=Object.getOwnPropertyDescriptor(e,o):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(t,e,o,r);else for(var n=t.length-1;n>=0;n--)(i=t[n])&&(s=(l<3?i(s):l>3?i(e,o,s):i(e,o))||s);return l>3&&s&&Object.defineProperty(e,o,s),s};import{LitElement,html}from"lit";import{classMap}from"lit/directives/class-map.js";import{createRef,ref}from"lit/directives/ref.js";import{customElement,property,queryAssignedElements}from"lit/decorators.js";import{owSlotType}from"./library/ow.js";import{when}from"lit/directives/when.js";import GlideCoreButtonGroupButton from"./button-group.button.js";import styles from"./button-group.styles.js";let GlideCoreButtonGroup=class GlideCoreButtonGroup extends LitElement{constructor(){super(...arguments),this.label="",this.orientation="horizontal",this.#t=createRef()}static{this.shadowRootOptions={...LitElement.shadowRootOptions,mode:"closed"}}static{this.styles=styles}firstUpdated(){if(owSlotType(this.#t.value,[GlideCoreButtonGroupButton]),"vertical"===this.orientation)for(const t of this.listItems)t.toggleAttribute("vertical");if("icon-only"===this.variant)for(const t of this.listItems)t.setAttribute("variant","icon-only")}render(){return html`${when(Boolean(this.label),(()=>html`<div class="label" id="glide-core-button-group">${this.label}</div>`))}<ul aria-labelledby="glide-core-button-group" role="radiogroup" class="${classMap({"radio-group":!0,vertical:"vertical"===this.orientation})}"><slot @slotchange="${this.#e}" ${ref(this.#t)}></slot></ul>`}willUpdate(t){if(this.hasUpdated&&t.has("variant")){if("icon-only"===t.get("variant"))for(const t of this.listItems)t.removeAttribute("variant");else for(const t of this.listItems)t.setAttribute("variant","icon-only")}if(this.hasUpdated&&t.has("orientation")){const e=t.get("orientation");if("vertical"===e)for(const t of this.listItems)t.removeAttribute("vertical");else if("horizontal"===e)for(const t of this.listItems)t.toggleAttribute("vertical")}}#t;#e(){owSlotType(this.#t.value,[GlideCoreButtonGroupButton])}};__decorate([property()],GlideCoreButtonGroup.prototype,"label",void 0),__decorate([queryAssignedElements({selector:"glide-core-button-group-button"})],GlideCoreButtonGroup.prototype,"listItems",void 0),__decorate([property()],GlideCoreButtonGroup.prototype,"variant",void 0),__decorate([property()],GlideCoreButtonGroup.prototype,"orientation",void 0),GlideCoreButtonGroup=__decorate([customElement("glide-core-button-group")],GlideCoreButtonGroup);export default GlideCoreButtonGroup;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
import{css}from"lit";import visuallyHidden from"./styles/visually-hidden.js";export default[css`
|
2
2
|
.radio-group {
|
3
3
|
appearance: none;
|
4
4
|
border: 1px solid var(--glide-core-border-base);
|
@@ -13,6 +13,6 @@
|
|
13
13
|
}
|
14
14
|
|
15
15
|
.label {
|
16
|
-
${
|
16
|
+
${visuallyHidden};
|
17
17
|
}
|
18
|
-
`];
|
18
|
+
`];
|