@brandsync/wc 0.0.4 → 0.0.6
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/custom-elements.json +203 -2
- package/dist/brandsync-wc/brandsync-wc.esm.js +1 -1
- package/dist/brandsync-wc/p-1dc6d7f5.entry.js +1 -0
- package/dist/brandsync-wc/{p-ea705742.entry.js → p-4a0da8e9.entry.js} +1 -1
- package/dist/brandsync-wc/p-4d999a8d.entry.js +1 -0
- package/dist/brandsync-wc/p-518b02bf.entry.js +1 -0
- package/dist/brandsync-wc/p-6648d251.entry.js +1 -0
- package/dist/cjs/brandsync-wc.cjs.js +1 -1
- package/dist/cjs/bs-chatbot-response-action.cjs.entry.js +30 -4
- package/dist/cjs/bs-chatbot-suggestion-button.cjs.entry.js +25 -0
- package/dist/cjs/bs-input.cjs.entry.js +2 -2
- package/dist/cjs/bs-menu-item.cjs.entry.js +29 -0
- package/dist/cjs/bs-menu.cjs.entry.js +17 -0
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/collection-manifest.json +4 -1
- package/dist/collection/components/bs-chatbot-response-action/bs-chatbot-response-action.cmp.test.js +43 -2
- package/dist/collection/components/bs-chatbot-response-action/bs-chatbot-response-action.css +13 -0
- package/dist/collection/components/bs-chatbot-response-action/bs-chatbot-response-action.js +70 -6
- package/dist/collection/components/bs-chatbot-response-action/bs-chatbot-response-action.stories.js +96 -0
- package/dist/collection/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.cmp.test.js +24 -0
- package/dist/collection/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.css +57 -0
- package/dist/collection/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.js +104 -0
- package/dist/collection/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.stories.js +30 -0
- package/dist/collection/components/bs-input/bs-input.js +2 -2
- package/dist/collection/components/bs-menu/bs-menu.cmp.test.js +18 -0
- package/dist/collection/components/bs-menu/bs-menu.css +20 -0
- package/dist/collection/components/bs-menu/bs-menu.js +39 -0
- package/dist/collection/components/bs-menu/bs-menu.stories.js +15 -0
- package/dist/collection/components/bs-menu-item/bs-menu-item.cmp.test.js +33 -0
- package/dist/collection/components/bs-menu-item/bs-menu-item.css +68 -0
- package/dist/collection/components/bs-menu-item/bs-menu-item.js +79 -0
- package/dist/collection/components/bs-menu-item/bs-menu-item.stories.js +37 -0
- package/dist/components/bs-chatbot-response-action.js +1 -1
- package/dist/components/bs-chatbot-suggestion-button.d.ts +11 -0
- package/dist/components/bs-chatbot-suggestion-button.js +1 -0
- package/dist/components/bs-input.js +1 -1
- package/dist/components/bs-menu-item.d.ts +11 -0
- package/dist/components/bs-menu-item.js +1 -0
- package/dist/components/bs-menu.d.ts +11 -0
- package/dist/components/bs-menu.js +1 -0
- package/dist/esm/brandsync-wc.js +1 -1
- package/dist/esm/bs-chatbot-response-action.entry.js +31 -5
- package/dist/esm/bs-chatbot-suggestion-button.entry.js +23 -0
- package/dist/esm/bs-input.entry.js +2 -2
- package/dist/esm/bs-menu-item.entry.js +27 -0
- package/dist/esm/bs-menu.entry.js +15 -0
- package/dist/esm/loader.js +1 -1
- package/dist/types/components/bs-chatbot-response-action/bs-chatbot-response-action.d.ts +16 -2
- package/dist/types/components/bs-chatbot-response-action/bs-chatbot-response-action.stories.d.ts +1 -0
- package/dist/types/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.cmp.test.d.ts +1 -0
- package/dist/types/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.d.ts +46 -0
- package/dist/types/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.stories.d.ts +10 -0
- package/dist/types/components/bs-menu/bs-menu.cmp.test.d.ts +1 -0
- package/dist/types/components/bs-menu/bs-menu.d.ts +24 -0
- package/dist/types/components/bs-menu/bs-menu.stories.d.ts +5 -0
- package/dist/types/components/bs-menu-item/bs-menu-item.cmp.test.d.ts +1 -0
- package/dist/types/components/bs-menu-item/bs-menu-item.d.ts +38 -0
- package/dist/types/components/bs-menu-item/bs-menu-item.stories.d.ts +7 -0
- package/dist/types/components.d.ts +364 -3
- package/package.json +2 -1
- package/dist/brandsync-wc/p-4ce458bf.entry.js +0 -1
package/dist/collection/components/bs-chatbot-response-action/bs-chatbot-response-action.cmp.test.js
CHANGED
|
@@ -89,8 +89,8 @@ describe('bs-chatbot-response-action', () => {
|
|
|
89
89
|
expect(menuSpy.length).toBe(0);
|
|
90
90
|
expect(sourcesSpy.length).toBe(0);
|
|
91
91
|
});
|
|
92
|
-
it('emits bsMenuOpen when the more-options button is clicked, and not the other events', async () => {
|
|
93
|
-
const { root, spyOnEvent } = await render(h("bs-chatbot-response-action", { sourcesCount: 3 }));
|
|
92
|
+
it('emits bsMenuOpen with true when the more-options button is clicked, and not the other events', async () => {
|
|
93
|
+
const { root, waitForChanges, spyOnEvent } = await render(h("bs-chatbot-response-action", { sourcesCount: 3 }));
|
|
94
94
|
const likeSpy = spyOnEvent('bsLike');
|
|
95
95
|
const dislikeSpy = spyOnEvent('bsDislike');
|
|
96
96
|
const copySpy = spyOnEvent('bsCopy');
|
|
@@ -98,13 +98,54 @@ describe('bs-chatbot-response-action', () => {
|
|
|
98
98
|
const menuSpy = spyOnEvent('bsMenuOpen');
|
|
99
99
|
const sourcesSpy = spyOnEvent('bsSourcesClick');
|
|
100
100
|
root.shadowRoot.querySelector('[part="menu"]').click();
|
|
101
|
+
await waitForChanges();
|
|
101
102
|
expect(menuSpy).toHaveReceivedEventTimes(1);
|
|
103
|
+
expect(menuSpy).toHaveReceivedEventDetail(true);
|
|
104
|
+
expect(root.menuOpen).toBe(true);
|
|
102
105
|
expect(likeSpy.length).toBe(0);
|
|
103
106
|
expect(dislikeSpy.length).toBe(0);
|
|
104
107
|
expect(copySpy.length).toBe(0);
|
|
105
108
|
expect(regenerateSpy.length).toBe(0);
|
|
106
109
|
expect(sourcesSpy.length).toBe(0);
|
|
107
110
|
});
|
|
111
|
+
it('emits bsMenuOpen with false when the more-options button is clicked again to close it', async () => {
|
|
112
|
+
const { root, waitForChanges, spyOnEvent } = await render(h("bs-chatbot-response-action", { sourcesCount: 3 }));
|
|
113
|
+
const menuSpy = spyOnEvent('bsMenuOpen');
|
|
114
|
+
const menuButton = root.shadowRoot.querySelector('[part="menu"]');
|
|
115
|
+
menuButton.click();
|
|
116
|
+
await waitForChanges();
|
|
117
|
+
menuButton.click();
|
|
118
|
+
await waitForChanges();
|
|
119
|
+
expect(menuSpy).toHaveReceivedEventTimes(2);
|
|
120
|
+
expect(menuSpy).toHaveReceivedEventDetail(false);
|
|
121
|
+
expect(root.menuOpen).toBe(false);
|
|
122
|
+
});
|
|
123
|
+
it('does not render the menu slot wrapper when menuOpen is false', async () => {
|
|
124
|
+
const { root } = await render(h("bs-chatbot-response-action", null));
|
|
125
|
+
expect(root.shadowRoot.querySelector('slot[name="menu"]')).toBeNull();
|
|
126
|
+
});
|
|
127
|
+
it('renders the menu slot wrapper when menuOpen is true', async () => {
|
|
128
|
+
const { root } = await render(h("bs-chatbot-response-action", { menuOpen: true }));
|
|
129
|
+
expect(root.shadowRoot.querySelector('slot[name="menu"]')).not.toBeNull();
|
|
130
|
+
});
|
|
131
|
+
it('closes the menu and emits bsMenuOpen(false) on a click outside the host', async () => {
|
|
132
|
+
const { root, waitForChanges, spyOnEvent } = await render(h("bs-chatbot-response-action", { menuOpen: true }));
|
|
133
|
+
const menuSpy = spyOnEvent('bsMenuOpen');
|
|
134
|
+
document.body.click();
|
|
135
|
+
await waitForChanges();
|
|
136
|
+
expect(root.menuOpen).toBe(false);
|
|
137
|
+
expect(menuSpy).toHaveReceivedEventTimes(1);
|
|
138
|
+
expect(menuSpy).toHaveReceivedEventDetail(false);
|
|
139
|
+
});
|
|
140
|
+
it('closes the menu on Escape', async () => {
|
|
141
|
+
const { root, waitForChanges, spyOnEvent } = await render(h("bs-chatbot-response-action", { menuOpen: true }));
|
|
142
|
+
const menuSpy = spyOnEvent('bsMenuOpen');
|
|
143
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
|
144
|
+
await waitForChanges();
|
|
145
|
+
expect(root.menuOpen).toBe(false);
|
|
146
|
+
expect(menuSpy).toHaveReceivedEventTimes(1);
|
|
147
|
+
expect(menuSpy).toHaveReceivedEventDetail(false);
|
|
148
|
+
});
|
|
108
149
|
it('emits bsSourcesClick when the sources button is clicked, and not the other events', async () => {
|
|
109
150
|
const { root, spyOnEvent } = await render(h("bs-chatbot-response-action", { sourcesCount: 3 }));
|
|
110
151
|
const likeSpy = spyOnEvent('bsLike');
|
package/dist/collection/components/bs-chatbot-response-action/bs-chatbot-response-action.css
CHANGED
|
@@ -63,6 +63,19 @@
|
|
|
63
63
|
background: var(--bs-chatbot-response-action-button-pressed);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
.bs-chatbot-response-action__menu-wrapper {
|
|
67
|
+
position: relative;
|
|
68
|
+
display: inline-flex;
|
|
69
|
+
flex-shrink: 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.bs-chatbot-response-action__menu-popup {
|
|
73
|
+
position: absolute;
|
|
74
|
+
top: calc(100% + var(--bs-spacing-50));
|
|
75
|
+
left: 0;
|
|
76
|
+
z-index: 1;
|
|
77
|
+
}
|
|
78
|
+
|
|
66
79
|
.bs-chatbot-response-action__sources {
|
|
67
80
|
display: inline-flex;
|
|
68
81
|
align-items: center;
|
|
@@ -16,6 +16,8 @@ import { h } from "@stencil/core";
|
|
|
16
16
|
* @slot copy-icon - Overrides the default "Copy" button icon.
|
|
17
17
|
* @slot regenerate-icon - Overrides the default "Regenerate" button icon.
|
|
18
18
|
* @slot menu-icon - Overrides the default "More options" button icon.
|
|
19
|
+
* @slot menu - A `bs-menu` (or other content) rendered in a popup positioned below the "More
|
|
20
|
+
* options" button, shown only while `menuOpen` is `true`.
|
|
19
21
|
* @slot sources-icon - Overrides the default icon in the "N sources" button.
|
|
20
22
|
* @part like - The "Like" icon button.
|
|
21
23
|
* @part dislike - The "Dislike" icon button.
|
|
@@ -39,12 +41,20 @@ import { h } from "@stencil/core";
|
|
|
39
41
|
* @prop --bs-chatbot-response-action-sources-line-height - Aliased to `--bs-line-height-body-sm`.
|
|
40
42
|
*/
|
|
41
43
|
export class BsChatbotResponseAction {
|
|
44
|
+
host;
|
|
42
45
|
/**
|
|
43
46
|
* Number of sources backing the response. When set to a positive number, renders the "N
|
|
44
47
|
* sources" button (singular "1 source" / plural "N sources"). When unset or `0`, the button is
|
|
45
48
|
* not rendered at all.
|
|
46
49
|
*/
|
|
47
50
|
sourcesCount;
|
|
51
|
+
/**
|
|
52
|
+
* Whether the "more options" popup menu (the `menu` slot) is currently open. Mutable + reflected
|
|
53
|
+
* so the component can track/close itself (kebab click, click outside, Escape) the same way
|
|
54
|
+
* `bs-chatbot-header`'s `expanded` prop tracks its own toggle state, while still emitting
|
|
55
|
+
* `bsMenuOpen` for the consumer to react to.
|
|
56
|
+
*/
|
|
57
|
+
menuOpen = false;
|
|
48
58
|
/** Fires when the "Like" button is clicked. */
|
|
49
59
|
bsLike;
|
|
50
60
|
/** Fires when the "Dislike" button is clicked. */
|
|
@@ -53,7 +63,8 @@ export class BsChatbotResponseAction {
|
|
|
53
63
|
bsCopy;
|
|
54
64
|
/** Fires when the "Regenerate" button is clicked. */
|
|
55
65
|
bsRegenerate;
|
|
56
|
-
/** Fires when the "More options" button is clicked
|
|
66
|
+
/** Fires when the "More options" button is clicked or the menu is closed (click outside,
|
|
67
|
+
* Escape), with the new `menuOpen` value. */
|
|
57
68
|
bsMenuOpen;
|
|
58
69
|
/** Fires when the "N sources" button is clicked. */
|
|
59
70
|
bsSourcesClick;
|
|
@@ -70,14 +81,31 @@ export class BsChatbotResponseAction {
|
|
|
70
81
|
this.bsRegenerate.emit();
|
|
71
82
|
};
|
|
72
83
|
onMenuClick = () => {
|
|
73
|
-
this.
|
|
84
|
+
this.menuOpen = !this.menuOpen;
|
|
85
|
+
this.bsMenuOpen.emit(this.menuOpen);
|
|
86
|
+
};
|
|
87
|
+
closeMenu = () => {
|
|
88
|
+
if (!this.menuOpen)
|
|
89
|
+
return;
|
|
90
|
+
this.menuOpen = false;
|
|
91
|
+
this.bsMenuOpen.emit(this.menuOpen);
|
|
74
92
|
};
|
|
93
|
+
onDocumentClick(ev) {
|
|
94
|
+
if (this.menuOpen && !ev.composedPath().includes(this.host)) {
|
|
95
|
+
this.closeMenu();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
onKeydown(ev) {
|
|
99
|
+
if (this.menuOpen && ev.key === 'Escape') {
|
|
100
|
+
this.closeMenu();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
75
103
|
onSourcesClick = () => {
|
|
76
104
|
this.bsSourcesClick.emit();
|
|
77
105
|
};
|
|
78
106
|
render() {
|
|
79
107
|
const hasSources = !!this.sourcesCount && this.sourcesCount > 0;
|
|
80
|
-
return (h("div", { key: '
|
|
108
|
+
return (h("div", { key: '6595c290930eacb48dc9d52db12a29e2942c2d8f', class: "bs-chatbot-response-action" }, h("button", { key: 'e3acdbb150047bce0718a168c048ee9db6c829f2', type: "button", part: "like", class: "bs-chatbot-response-action__button", "aria-label": "Like", onClick: this.onLikeClick }, h("slot", { key: 'bc9339dafb25f7529a097eaa578ef9ff5d61cafe', name: "like-icon" }, h(ThumbsUpIcon, { key: '49bf4ef39238d9763838ee72ca1b1799e2a284ff' }))), h("button", { key: '1a9900dbb7b1892233477cc4140b690de9d87cf3', type: "button", part: "dislike", class: "bs-chatbot-response-action__button", "aria-label": "Dislike", onClick: this.onDislikeClick }, h("slot", { key: 'db4555adfb4a8cc83cb73ff13d537806e5918f25', name: "dislike-icon" }, h(ThumbsDownIcon, { key: 'a741f40e3f0ea3a11e839277ad5659e0689fb13a' }))), h("button", { key: '19fbad2e8ae607368b78dc076188ec2b30d21325', type: "button", part: "copy", class: "bs-chatbot-response-action__button", "aria-label": "Copy", onClick: this.onCopyClick }, h("slot", { key: '8789336bec82b3335be38628d00664246d0f4015', name: "copy-icon" }, h(CopyIcon, { key: 'e65198dff09540ce0d9af3fa2ed1e5ece12396cf' }))), h("button", { key: '09c16cca24ab255299d9f892e3d2c5334fcaf7f8', type: "button", part: "regenerate", class: "bs-chatbot-response-action__button", "aria-label": "Regenerate", onClick: this.onRegenerateClick }, h("slot", { key: '9758f958967908ce1a25805d9d3bea968f3590f4', name: "regenerate-icon" }, h(RegenerateIcon, { key: '608515ede5570b2482cc46c3f2910d56e086f0b3' }))), h("div", { key: '81c609157f7bb8024a2d0b1a3da30ae3d93f46e8', class: "bs-chatbot-response-action__menu-wrapper" }, h("button", { key: '588d3eef27b342c784fd925e7ff4478370ef8b48', type: "button", part: "menu", class: "bs-chatbot-response-action__button", "aria-label": "More options", "aria-haspopup": "menu", "aria-expanded": this.menuOpen ? 'true' : 'false', onClick: this.onMenuClick }, h("slot", { key: 'f14e53e800cc2976306824a6b5131a98cf22ee49', name: "menu-icon" }, h(MenuIcon, { key: '7ce9bc2ee29ab3f7d56b4a5cbead2a014f586841' }))), this.menuOpen && (h("div", { key: '80e0e9688f639c51601f891e46c82059ee1fd921', class: "bs-chatbot-response-action__menu-popup" }, h("slot", { key: '9b1a2652e3460a1d97b4de856269516eaf1273e1', name: "menu" })))), hasSources && (h("button", { key: '15febee771c334afddbbdedc21add700ed44ceb1', type: "button", part: "sources", class: "bs-chatbot-response-action__sources", onClick: this.onSourcesClick }, h("slot", { key: '39d2cf88bda64d2f2b0a6d767e6ba5fcb1acc8bd', name: "sources-icon" }, h(SourcesIcon, { key: '358a3b2c1e8479c7ee92e89401edc5e68ff93e1c' })), h("span", { key: '96b364bdfc9f8ae1ba605f0fd751e19e77959720', class: "bs-chatbot-response-action__sources-label" }, this.sourcesCount, " ", this.sourcesCount === 1 ? 'source' : 'sources')))));
|
|
81
109
|
}
|
|
82
110
|
static get is() { return "bs-chatbot-response-action"; }
|
|
83
111
|
static get encapsulation() { return "shadow"; }
|
|
@@ -111,6 +139,26 @@ export class BsChatbotResponseAction {
|
|
|
111
139
|
"setter": false,
|
|
112
140
|
"reflect": false,
|
|
113
141
|
"attribute": "sources-count"
|
|
142
|
+
},
|
|
143
|
+
"menuOpen": {
|
|
144
|
+
"type": "boolean",
|
|
145
|
+
"mutable": true,
|
|
146
|
+
"complexType": {
|
|
147
|
+
"original": "boolean",
|
|
148
|
+
"resolved": "boolean",
|
|
149
|
+
"references": {}
|
|
150
|
+
},
|
|
151
|
+
"required": false,
|
|
152
|
+
"optional": false,
|
|
153
|
+
"docs": {
|
|
154
|
+
"tags": [],
|
|
155
|
+
"text": "Whether the \"more options\" popup menu (the `menu` slot) is currently open. Mutable + reflected\nso the component can track/close itself (kebab click, click outside, Escape) the same way\n`bs-chatbot-header`'s `expanded` prop tracks its own toggle state, while still emitting\n`bsMenuOpen` for the consumer to react to."
|
|
156
|
+
},
|
|
157
|
+
"getter": false,
|
|
158
|
+
"setter": false,
|
|
159
|
+
"reflect": true,
|
|
160
|
+
"attribute": "menu-open",
|
|
161
|
+
"defaultValue": "false"
|
|
114
162
|
}
|
|
115
163
|
};
|
|
116
164
|
}
|
|
@@ -183,11 +231,11 @@ export class BsChatbotResponseAction {
|
|
|
183
231
|
"composed": true,
|
|
184
232
|
"docs": {
|
|
185
233
|
"tags": [],
|
|
186
|
-
"text": "Fires when the \"More options\" button is clicked."
|
|
234
|
+
"text": "Fires when the \"More options\" button is clicked or the menu is closed (click outside,\nEscape), with the new `menuOpen` value."
|
|
187
235
|
},
|
|
188
236
|
"complexType": {
|
|
189
|
-
"original": "
|
|
190
|
-
"resolved": "
|
|
237
|
+
"original": "boolean",
|
|
238
|
+
"resolved": "boolean",
|
|
191
239
|
"references": {}
|
|
192
240
|
}
|
|
193
241
|
}, {
|
|
@@ -207,6 +255,22 @@ export class BsChatbotResponseAction {
|
|
|
207
255
|
}
|
|
208
256
|
}];
|
|
209
257
|
}
|
|
258
|
+
static get elementRef() { return "host"; }
|
|
259
|
+
static get listeners() {
|
|
260
|
+
return [{
|
|
261
|
+
"name": "click",
|
|
262
|
+
"method": "onDocumentClick",
|
|
263
|
+
"target": "document",
|
|
264
|
+
"capture": false,
|
|
265
|
+
"passive": false
|
|
266
|
+
}, {
|
|
267
|
+
"name": "keydown",
|
|
268
|
+
"method": "onKeydown",
|
|
269
|
+
"target": "document",
|
|
270
|
+
"capture": false,
|
|
271
|
+
"passive": false
|
|
272
|
+
}];
|
|
273
|
+
}
|
|
210
274
|
}
|
|
211
275
|
const ThumbsUpIcon = () => (h("svg", { viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true" }, h("path", { d: "M2.5 8.125H6.25V16.25H2.5C2.33424 16.25 2.17527 16.1842 2.05806 16.0669C1.94085 15.9497 1.875 15.7908 1.875 15.625V8.75C1.875 8.58424 1.94085 8.42527 2.05806 8.30806C2.17527 8.19085 2.33424 8.125 2.5 8.125Z", stroke: "currentColor", "stroke-width": "1.66667", "stroke-linecap": "round", "stroke-linejoin": "round" }), h("path", { d: "M6.25 8.125L9.375 1.875C10.038 1.875 10.6739 2.13839 11.1428 2.60723C11.6116 3.07607 11.875 3.71196 11.875 4.375V6.25H16.875C17.0523 6.25005 17.2276 6.28782 17.3892 6.36081C17.5508 6.4338 17.695 6.54033 17.8123 6.67333C17.9295 6.80634 18.0172 6.96277 18.0693 7.13223C18.1215 7.3017 18.137 7.48033 18.1148 7.65625L17.1773 15.1562C17.1393 15.4583 16.9923 15.736 16.7641 15.9374C16.5358 16.1388 16.2419 16.2499 15.9375 16.25H6.25", stroke: "currentColor", "stroke-width": "1.66667", "stroke-linecap": "round", "stroke-linejoin": "round" })));
|
|
212
276
|
const ThumbsDownIcon = () => (h("svg", { viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true" }, h("path", { d: "M2.5 3.75H6.25V11.875H2.5C2.33424 11.875 2.17527 11.8092 2.05806 11.6919C1.94085 11.5747 1.875 11.4158 1.875 11.25V4.375C1.875 4.20924 1.94085 4.05027 2.05806 3.93306C2.17527 3.81585 2.33424 3.75 2.5 3.75Z", stroke: "currentColor", "stroke-width": "1.66667", "stroke-linecap": "round", "stroke-linejoin": "round" }), h("path", { d: "M6.25 11.875L9.375 18.125C10.038 18.125 10.6739 17.8616 11.1428 17.3928C11.6116 16.9239 11.875 16.288 11.875 15.625V13.75H16.875C17.0523 13.75 17.2276 13.7122 17.3892 13.6392C17.5508 13.5662 17.695 13.4597 17.8123 13.3267C17.9295 13.1937 18.0172 13.0372 18.0693 12.8678C18.1215 12.6983 18.137 12.5197 18.1148 12.3438L17.1773 4.84375C17.1393 4.54174 16.9923 4.26399 16.7641 4.06262C16.5358 3.86124 16.2419 3.75009 15.9375 3.75H6.25", stroke: "currentColor", "stroke-width": "1.66667", "stroke-linecap": "round", "stroke-linejoin": "round" })));
|
package/dist/collection/components/bs-chatbot-response-action/bs-chatbot-response-action.stories.js
CHANGED
|
@@ -31,3 +31,99 @@ export const InResponseMessage = {
|
|
|
31
31
|
</div>
|
|
32
32
|
`,
|
|
33
33
|
};
|
|
34
|
+
const ReadAloudIcon = () => html `
|
|
35
|
+
<svg slot="icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
36
|
+
<path
|
|
37
|
+
d="M7.5 15.75H3C2.80109 15.75 2.61032 15.671 2.46967 15.5303C2.32902 15.3897 2.25 15.1989 2.25 15V9C2.25 8.80109 2.32902 8.61032 2.46967 8.46967C2.61032 8.32902 2.80109 8.25 3 8.25H7.5L14.25 3V21L7.5 15.75Z"
|
|
38
|
+
stroke="currentColor"
|
|
39
|
+
stroke-width="2"
|
|
40
|
+
stroke-linecap="round"
|
|
41
|
+
stroke-linejoin="round"
|
|
42
|
+
/>
|
|
43
|
+
<path d="M7.5 8.25V15.75" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
44
|
+
<path
|
|
45
|
+
d="M18 10.0172C18.4825 10.565 18.7487 11.27 18.7487 12C18.7487 12.73 18.4825 13.435 18 13.9828"
|
|
46
|
+
stroke="currentColor"
|
|
47
|
+
stroke-width="2"
|
|
48
|
+
stroke-linecap="round"
|
|
49
|
+
stroke-linejoin="round"
|
|
50
|
+
/>
|
|
51
|
+
<path
|
|
52
|
+
d="M20.7816 7.5C21.8885 8.73755 22.5004 10.3397 22.5004 12C22.5004 13.6603 21.8885 15.2624 20.7816 16.5"
|
|
53
|
+
stroke="currentColor"
|
|
54
|
+
stroke-width="2"
|
|
55
|
+
stroke-linecap="round"
|
|
56
|
+
stroke-linejoin="round"
|
|
57
|
+
/>
|
|
58
|
+
</svg>
|
|
59
|
+
`;
|
|
60
|
+
const ChatWithHumanIcon = () => html `
|
|
61
|
+
<svg slot="icon" viewBox="0 0 24.5 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
62
|
+
<path
|
|
63
|
+
d="M12.25 15C14.3211 15 16 13.3211 16 11.25C16 9.17893 14.3211 7.5 12.25 7.5C10.1789 7.5 8.5 9.17893 8.5 11.25C8.5 13.3211 10.1789 15 12.25 15Z"
|
|
64
|
+
stroke="currentColor"
|
|
65
|
+
stroke-width="2"
|
|
66
|
+
stroke-linecap="round"
|
|
67
|
+
stroke-linejoin="round"
|
|
68
|
+
/>
|
|
69
|
+
<path
|
|
70
|
+
d="M6.23125 18.6909C6.79554 17.5795 7.65659 16.6459 8.71895 15.9939C9.7813 15.3418 11.0035 14.9966 12.25 14.9966C13.4965 14.9966 14.7187 15.3418 15.7811 15.9939C16.8434 16.6459 17.7045 17.5795 18.2687 18.6909"
|
|
71
|
+
stroke="currentColor"
|
|
72
|
+
stroke-width="2"
|
|
73
|
+
stroke-linecap="round"
|
|
74
|
+
stroke-linejoin="round"
|
|
75
|
+
/>
|
|
76
|
+
<path d="M19 12L21.25 14.25L23.5 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
77
|
+
<path d="M1 12L3.25 9.75L5.5 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
78
|
+
<path
|
|
79
|
+
d="M3.25 9.75V12C3.24929 13.9055 3.85337 15.762 4.97524 17.3022C6.09712 18.8424 7.67888 19.9868 9.49273 20.5706C11.3066 21.1543 13.2589 21.1474 15.0685 20.5506C16.8782 19.9539 18.4517 18.7982 19.5625 17.25"
|
|
80
|
+
stroke="currentColor"
|
|
81
|
+
stroke-width="2"
|
|
82
|
+
stroke-linecap="round"
|
|
83
|
+
stroke-linejoin="round"
|
|
84
|
+
/>
|
|
85
|
+
<path
|
|
86
|
+
d="M21.25 14.25V12C21.2507 10.0945 20.6466 8.238 19.5248 6.69779C18.4029 5.15758 16.8211 4.01319 15.0073 3.42942C13.1934 2.84565 11.2411 2.85264 9.43147 3.44938C7.62185 4.04611 6.04832 5.2018 4.9375 6.75"
|
|
87
|
+
stroke="currentColor"
|
|
88
|
+
stroke-width="2"
|
|
89
|
+
stroke-linecap="round"
|
|
90
|
+
stroke-linejoin="round"
|
|
91
|
+
/>
|
|
92
|
+
</svg>
|
|
93
|
+
`;
|
|
94
|
+
const RaiseTicketIcon = () => html `
|
|
95
|
+
<svg slot="icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
96
|
+
<path d="M9 5.25V18.75" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
97
|
+
<path
|
|
98
|
+
d="M2.25 15C3.04565 15 3.80871 14.6839 4.37132 14.1213C4.93393 13.5587 5.25 12.7956 5.25 12C5.25 11.2044 4.93393 10.4413 4.37132 9.87868C3.80871 9.31607 3.04565 9 2.25 9V6C2.25 5.80109 2.32902 5.61032 2.46967 5.46967C2.61032 5.32902 2.80109 5.25 3 5.25H21C21.1989 5.25 21.3897 5.32902 21.5303 5.46967C21.671 5.61032 21.75 5.80109 21.75 6V9C20.9544 9 20.1913 9.31607 19.6287 9.87868C19.0661 10.4413 18.75 11.2044 18.75 12C18.75 12.7956 19.0661 13.5587 19.6287 14.1213C20.1913 14.6839 20.9544 15 21.75 15V18C21.75 18.1989 21.671 18.3897 21.5303 18.5303C21.3897 18.671 21.1989 18.75 21 18.75H3C2.80109 18.75 2.61032 18.671 2.46967 18.5303C2.32902 18.3897 2.25 18.1989 2.25 18V15Z"
|
|
99
|
+
stroke="currentColor"
|
|
100
|
+
stroke-width="2"
|
|
101
|
+
stroke-linecap="round"
|
|
102
|
+
stroke-linejoin="round"
|
|
103
|
+
/>
|
|
104
|
+
</svg>
|
|
105
|
+
`;
|
|
106
|
+
export const WithMenu = {
|
|
107
|
+
name: 'More options menu',
|
|
108
|
+
render: () => {
|
|
109
|
+
// Demonstrates the intended integration pattern: bs-chatbot-response-action only tracks/emits
|
|
110
|
+
// the menuOpen toggle and provides a `menu` slot positioned below the kebab button -- it has no
|
|
111
|
+
// opinion on what goes in the menu. The consuming app (here, this story) supplies a bs-menu with
|
|
112
|
+
// real bs-menu-items and is responsible for wiring bsMenuOpen to the response-action's menuOpen
|
|
113
|
+
// prop, mirroring how bs-chatbot-header's WithComposer story wires bsExpand.
|
|
114
|
+
const onMenuOpen = (ev) => {
|
|
115
|
+
ev.target.menuOpen = ev.detail;
|
|
116
|
+
};
|
|
117
|
+
return html `
|
|
118
|
+
<div style="max-width: 480px; font-family: sans-serif;">
|
|
119
|
+
<bs-chatbot-response-action sources-count="3" @bsMenuOpen=${onMenuOpen}>
|
|
120
|
+
<bs-menu slot="menu" style="width: 210px;">
|
|
121
|
+
<bs-menu-item>${ReadAloudIcon()}Read aloud</bs-menu-item>
|
|
122
|
+
<bs-menu-item>${ChatWithHumanIcon()}Chat with a human</bs-menu-item>
|
|
123
|
+
<bs-menu-item>${RaiseTicketIcon()}Raise a ticket</bs-menu-item>
|
|
124
|
+
</bs-menu>
|
|
125
|
+
</bs-chatbot-response-action>
|
|
126
|
+
</div>
|
|
127
|
+
`;
|
|
128
|
+
},
|
|
129
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { render, h, describe, it, expect } from "@stencil/vitest";
|
|
2
|
+
describe('bs-chatbot-suggestion-button', () => {
|
|
3
|
+
it('renders the default slot label content and the button part', async () => {
|
|
4
|
+
const { root } = await render(h("bs-chatbot-suggestion-button", null, "How can I help you?"));
|
|
5
|
+
expect(root).toHaveTextContent('How can I help you?');
|
|
6
|
+
const button = root.shadowRoot.querySelector('[part="button"]');
|
|
7
|
+
expect(button).not.toBeNull();
|
|
8
|
+
expect(button.tagName).toBe('BUTTON');
|
|
9
|
+
});
|
|
10
|
+
it('emits bsSelect exactly once on a normal click', async () => {
|
|
11
|
+
const { root, spyOnEvent } = await render(h("bs-chatbot-suggestion-button", null, "Suggestion"));
|
|
12
|
+
const selectSpy = spyOnEvent('bsSelect');
|
|
13
|
+
root.shadowRoot.querySelector('[part="button"]').click();
|
|
14
|
+
expect(selectSpy).toHaveReceivedEventTimes(1);
|
|
15
|
+
});
|
|
16
|
+
it('applies the native disabled attribute and does not fire bsSelect on click when disabled', async () => {
|
|
17
|
+
const { root, spyOnEvent } = await render(h("bs-chatbot-suggestion-button", { disabled: true }, "Suggestion"));
|
|
18
|
+
const selectSpy = spyOnEvent('bsSelect');
|
|
19
|
+
const button = root.shadowRoot.querySelector('[part="button"]');
|
|
20
|
+
expect(button.disabled).toBe(true);
|
|
21
|
+
button.click();
|
|
22
|
+
expect(selectSpy.length).toBe(0);
|
|
23
|
+
});
|
|
24
|
+
});
|
package/dist/collection/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.css
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: inline-block;
|
|
3
|
+
--bs-chatbot-suggestion-button-radius: var(--bs-border-radius-full);
|
|
4
|
+
--bs-chatbot-suggestion-button-border-color: var(--bs-border-primary);
|
|
5
|
+
--bs-chatbot-suggestion-button-bg-default: var(--bs-color-primary-container);
|
|
6
|
+
--bs-chatbot-suggestion-button-bg-hover: var(--bs-color-primary-container-hover);
|
|
7
|
+
/* No pre-built semantic "container-pressed" token matches this tonal button's lighter pressed
|
|
8
|
+
* shade progression (50 -> 100 -> 200) -- alias the primitive directly, same precedent
|
|
9
|
+
* documented in bs-button.css for its subtle/outlined/success/warning/info variants. */
|
|
10
|
+
--bs-chatbot-suggestion-button-bg-pressed: var(--bs-brand-200);
|
|
11
|
+
--bs-chatbot-suggestion-button-bg-focus: var(--bs-color-primary-container-hover);
|
|
12
|
+
--bs-chatbot-suggestion-button-text-default: var(--bs-text-action);
|
|
13
|
+
--bs-chatbot-suggestion-button-text-hover: var(--bs-text-action-hover);
|
|
14
|
+
--bs-chatbot-suggestion-button-focus-ring-color: var(--bs-border-primary-focus);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.bs-chatbot-suggestion-button {
|
|
18
|
+
box-sizing: border-box;
|
|
19
|
+
display: inline-flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: center;
|
|
22
|
+
height: 40px;
|
|
23
|
+
min-width: 80px;
|
|
24
|
+
padding: var(--bs-spacing-100) var(--bs-spacing-200);
|
|
25
|
+
border: var(--bs-border-width-thin) solid var(--bs-chatbot-suggestion-button-border-color);
|
|
26
|
+
border-radius: var(--bs-chatbot-suggestion-button-radius);
|
|
27
|
+
background: var(--bs-chatbot-suggestion-button-bg-default);
|
|
28
|
+
color: var(--bs-chatbot-suggestion-button-text-default);
|
|
29
|
+
font-family: inherit;
|
|
30
|
+
font-size: var(--bs-font-size-md);
|
|
31
|
+
line-height: calc(var(--bs-line-height-body-md) * 1px);
|
|
32
|
+
text-align: center;
|
|
33
|
+
cursor: pointer;
|
|
34
|
+
transition: background-color var(--bs-duration-fast) var(--bs-easing-standard);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.bs-chatbot-suggestion-button:hover:not(:disabled) {
|
|
38
|
+
background: var(--bs-chatbot-suggestion-button-bg-hover);
|
|
39
|
+
color: var(--bs-chatbot-suggestion-button-text-hover);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.bs-chatbot-suggestion-button:active:not(:disabled) {
|
|
43
|
+
background: var(--bs-chatbot-suggestion-button-bg-pressed);
|
|
44
|
+
color: var(--bs-chatbot-suggestion-button-text-hover);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.bs-chatbot-suggestion-button:focus-visible {
|
|
48
|
+
background: var(--bs-chatbot-suggestion-button-bg-focus);
|
|
49
|
+
color: var(--bs-chatbot-suggestion-button-text-hover);
|
|
50
|
+
outline: var(--bs-border-width-medium) solid var(--bs-chatbot-suggestion-button-focus-ring-color);
|
|
51
|
+
outline-offset: 3px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.bs-chatbot-suggestion-button:disabled {
|
|
55
|
+
opacity: var(--bs-opacity-disabled);
|
|
56
|
+
cursor: not-allowed;
|
|
57
|
+
}
|
package/dist/collection/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { h } from "@stencil/core";
|
|
2
|
+
/**
|
|
3
|
+
* A pill-shaped clickable suggestion chip for a Genie AI chat panel (e.g. "How can I help you?").
|
|
4
|
+
*
|
|
5
|
+
* One instance renders one chip -- to show several suggestion prompts side by side, render
|
|
6
|
+
* multiple `bs-chatbot-suggestion-button` elements inside a plain flex-wrap container; this
|
|
7
|
+
* library doesn't ship a separate list/group component for that since a `<div>` with
|
|
8
|
+
* `flex-wrap: wrap` is sufficient.
|
|
9
|
+
*
|
|
10
|
+
* ## When to use
|
|
11
|
+
* - Quick-reply/starter prompts shown above or alongside `bs-composer` in a Genie AI chat panel.
|
|
12
|
+
*
|
|
13
|
+
* ## When not to use
|
|
14
|
+
* - As a general-purpose button -- use `bs-button` instead, this component's pill shape and
|
|
15
|
+
* tonal color treatment are specific to Genie chat suggestion prompts.
|
|
16
|
+
*
|
|
17
|
+
* @slot - Default slot: the suggestion's label text.
|
|
18
|
+
* @part button - The button element.
|
|
19
|
+
* @prop --bs-chatbot-suggestion-button-radius - Corner radius. Aliased to `--bs-border-radius-full`.
|
|
20
|
+
* @prop --bs-chatbot-suggestion-button-border-color - Border color. Aliased to `--bs-border-primary`.
|
|
21
|
+
* @prop --bs-chatbot-suggestion-button-bg-default - Background in the default (enabled) state.
|
|
22
|
+
* Aliased to `--bs-color-primary-container`.
|
|
23
|
+
* @prop --bs-chatbot-suggestion-button-bg-hover - Background on hover. Aliased to
|
|
24
|
+
* `--bs-color-primary-container-hover`.
|
|
25
|
+
* @prop --bs-chatbot-suggestion-button-bg-pressed - Background when pressed. Aliased directly to
|
|
26
|
+
* the primitive `--bs-brand-200` -- there's no pre-built semantic "container-pressed" token
|
|
27
|
+
* matching this tonal button's lighter pressed shade progression (50 -> 100 -> 200), so this
|
|
28
|
+
* component aliases one locally (same situation `bs-button.css` documents for its
|
|
29
|
+
* subtle/outlined/success/warning/info variants).
|
|
30
|
+
* @prop --bs-chatbot-suggestion-button-bg-focus - Background when focused. Aliased to
|
|
31
|
+
* `--bs-color-primary-container-hover` (same as hover).
|
|
32
|
+
* @prop --bs-chatbot-suggestion-button-text-default - Text color in the default state. Aliased to
|
|
33
|
+
* `--bs-text-action`.
|
|
34
|
+
* @prop --bs-chatbot-suggestion-button-text-hover - Text color on hover, pressed, and focus.
|
|
35
|
+
* Aliased to `--bs-text-action-hover`.
|
|
36
|
+
* @prop --bs-chatbot-suggestion-button-focus-ring-color - Focus ring color. Aliased to
|
|
37
|
+
* `--bs-border-primary-focus`.
|
|
38
|
+
*/
|
|
39
|
+
export class BsChatbotSuggestionButton {
|
|
40
|
+
/** Disables the button and applies a reduced-opacity treatment. */
|
|
41
|
+
disabled = false;
|
|
42
|
+
/** Fires when the button is clicked. */
|
|
43
|
+
bsSelect;
|
|
44
|
+
onClick = () => {
|
|
45
|
+
this.bsSelect.emit();
|
|
46
|
+
};
|
|
47
|
+
render() {
|
|
48
|
+
return (h("button", { key: 'e19ad1fd5e95f84f6bff54cbc326e78f70ef3677', type: "button", part: "button", class: "bs-chatbot-suggestion-button", disabled: this.disabled, onClick: this.onClick }, h("slot", { key: '4048fb336d69f0ca78e50ea9d5cba52921906966' })));
|
|
49
|
+
}
|
|
50
|
+
static get is() { return "bs-chatbot-suggestion-button"; }
|
|
51
|
+
static get encapsulation() { return "shadow"; }
|
|
52
|
+
static get originalStyleUrls() {
|
|
53
|
+
return {
|
|
54
|
+
"$": ["bs-chatbot-suggestion-button.css"]
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
static get styleUrls() {
|
|
58
|
+
return {
|
|
59
|
+
"$": ["bs-chatbot-suggestion-button.css"]
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
static get properties() {
|
|
63
|
+
return {
|
|
64
|
+
"disabled": {
|
|
65
|
+
"type": "boolean",
|
|
66
|
+
"mutable": false,
|
|
67
|
+
"complexType": {
|
|
68
|
+
"original": "boolean",
|
|
69
|
+
"resolved": "boolean",
|
|
70
|
+
"references": {}
|
|
71
|
+
},
|
|
72
|
+
"required": false,
|
|
73
|
+
"optional": false,
|
|
74
|
+
"docs": {
|
|
75
|
+
"tags": [],
|
|
76
|
+
"text": "Disables the button and applies a reduced-opacity treatment."
|
|
77
|
+
},
|
|
78
|
+
"getter": false,
|
|
79
|
+
"setter": false,
|
|
80
|
+
"reflect": false,
|
|
81
|
+
"attribute": "disabled",
|
|
82
|
+
"defaultValue": "false"
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
static get events() {
|
|
87
|
+
return [{
|
|
88
|
+
"method": "bsSelect",
|
|
89
|
+
"name": "bsSelect",
|
|
90
|
+
"bubbles": true,
|
|
91
|
+
"cancelable": true,
|
|
92
|
+
"composed": true,
|
|
93
|
+
"docs": {
|
|
94
|
+
"tags": [],
|
|
95
|
+
"text": "Fires when the button is clicked."
|
|
96
|
+
},
|
|
97
|
+
"complexType": {
|
|
98
|
+
"original": "void",
|
|
99
|
+
"resolved": "void",
|
|
100
|
+
"references": {}
|
|
101
|
+
}
|
|
102
|
+
}];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { html } from "lit";
|
|
2
|
+
import { componentDescription, propDescription } from "../../stories-utils";
|
|
3
|
+
const meta = {
|
|
4
|
+
title: 'Genie AI Components/bs-chatbot-suggestion-button',
|
|
5
|
+
parameters: { docs: { description: { component: componentDescription('bs-chatbot-suggestion-button') } } },
|
|
6
|
+
render: args => html `
|
|
7
|
+
<bs-chatbot-suggestion-button ?disabled=${args.disabled}>${args.label}</bs-chatbot-suggestion-button>
|
|
8
|
+
`,
|
|
9
|
+
argTypes: {
|
|
10
|
+
label: { control: 'text', description: 'Default slot: the suggestion label text.' },
|
|
11
|
+
disabled: { control: 'boolean', description: propDescription('bs-chatbot-suggestion-button', 'disabled') },
|
|
12
|
+
},
|
|
13
|
+
args: {
|
|
14
|
+
label: 'How can I help you?',
|
|
15
|
+
disabled: false,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
export default meta;
|
|
19
|
+
export const Default = {};
|
|
20
|
+
export const SuggestionRow = {
|
|
21
|
+
name: 'Multiple suggestions (flex-wrap row)',
|
|
22
|
+
render: () => html `
|
|
23
|
+
<div style="display: flex; flex-wrap: wrap; gap: 8px; max-width: 420px;">
|
|
24
|
+
<bs-chatbot-suggestion-button>How can I help you?</bs-chatbot-suggestion-button>
|
|
25
|
+
<bs-chatbot-suggestion-button>Summarize this document</bs-chatbot-suggestion-button>
|
|
26
|
+
<bs-chatbot-suggestion-button>Draft a follow-up email</bs-chatbot-suggestion-button>
|
|
27
|
+
<bs-chatbot-suggestion-button>Explain this in simple terms</bs-chatbot-suggestion-button>
|
|
28
|
+
</div>
|
|
29
|
+
`,
|
|
30
|
+
};
|
|
@@ -36,10 +36,10 @@ export class BsInput {
|
|
|
36
36
|
this.bsChange.emit(value);
|
|
37
37
|
};
|
|
38
38
|
render() {
|
|
39
|
-
return (h("div", { key: '
|
|
39
|
+
return (h("div", { key: '571583a0d1181a2e307ba86679003c6cc37e4e63', class: `bs-input ${this.error ? 'bs-input--error' : ''}` }, this.label && (
|
|
40
40
|
// "control" is a fixed id, not a generated one -- safe because Shadow DOM scopes ids
|
|
41
41
|
// to this instance's own shadow root, so it can never collide with another bs-input.
|
|
42
|
-
h("label", { key: '
|
|
42
|
+
h("label", { key: '0a6c34ad8de3227e1264af649c1ac227e64c67bd', part: "label", class: "bs-input__label", htmlFor: "control" }, this.label)), h("input", { key: 'de46e2c3272735160ca1773abf6fd270d042cfdd', id: "control", part: "control", class: "bs-input__control", type: this.type, value: this.value, placeholder: this.placeholder, disabled: this.disabled, "aria-describedby": this.error || this.description ? 'description-or-error' : undefined, "aria-invalid": this.error ? 'true' : undefined, onInput: this.onInput, onChange: this.onChange }), this.error ? (h("span", { id: "description-or-error", part: "error", class: "bs-input__error" }, this.error)) : this.description ? (h("span", { id: "description-or-error", part: "description", class: "bs-input__description" }, this.description)) : null));
|
|
43
43
|
}
|
|
44
44
|
static get is() { return "bs-input"; }
|
|
45
45
|
static get encapsulation() { return "shadow"; }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { render, h, describe, it, expect } from "@stencil/vitest";
|
|
2
|
+
describe('bs-menu', () => {
|
|
3
|
+
it('renders with role="menu" on the host', async () => {
|
|
4
|
+
const { root } = await render(h("bs-menu", null));
|
|
5
|
+
expect(root.shadowRoot.querySelector('[part="list"]').getAttribute('role')).toBe('menu');
|
|
6
|
+
});
|
|
7
|
+
it('exposes the list part', async () => {
|
|
8
|
+
const { root } = await render(h("bs-menu", null));
|
|
9
|
+
expect(root.shadowRoot.querySelector('[part="list"]')).not.toBeNull();
|
|
10
|
+
});
|
|
11
|
+
it('renders slotted content in the default slot', async () => {
|
|
12
|
+
const { root } = await render(h("bs-menu", null, h("bs-menu-item", null, "Item one")));
|
|
13
|
+
expect(root).toHaveTextContent('Item one');
|
|
14
|
+
const slot = root.shadowRoot.querySelector('[part="list"] slot:not([name])');
|
|
15
|
+
expect(slot).not.toBeNull();
|
|
16
|
+
expect(slot.assignedElements()).toHaveLength(1);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: block;
|
|
3
|
+
--bs-menu-bg: var(--bs-surface-raised);
|
|
4
|
+
--bs-menu-radius: var(--bs-border-radius-100);
|
|
5
|
+
--bs-menu-padding-y: var(--bs-spacing-50);
|
|
6
|
+
--bs-menu-gap: var(--bs-spacing-50);
|
|
7
|
+
--bs-menu-shadow: var(--bs-shadow-sm);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.bs-menu {
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
gap: var(--bs-menu-gap);
|
|
15
|
+
background: var(--bs-menu-bg);
|
|
16
|
+
border-radius: var(--bs-menu-radius);
|
|
17
|
+
padding: var(--bs-menu-padding-y) 0;
|
|
18
|
+
box-shadow: var(--bs-menu-shadow);
|
|
19
|
+
font-family: inherit;
|
|
20
|
+
}
|