@brandsync/wc 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/custom-elements.json +207 -1
  2. package/dist/brandsync-wc/brandsync-wc.esm.js +1 -1
  3. package/dist/brandsync-wc/p-5a0fe72e.entry.js +1 -0
  4. package/dist/brandsync-wc/{p-300eea85.entry.js → p-6abdfaf0.entry.js} +1 -1
  5. package/dist/brandsync-wc/{p-27288299.entry.js → p-76eca100.entry.js} +1 -1
  6. package/dist/brandsync-wc/{p-0ded3be8.entry.js → p-a12d0a60.entry.js} +1 -1
  7. package/dist/brandsync-wc/p-bfd09c98.entry.js +1 -0
  8. package/dist/brandsync-wc/p-c63d16e1.entry.js +1 -0
  9. package/dist/brandsync-wc/{p-b24c455f.entry.js → p-dd0fea0e.entry.js} +1 -1
  10. package/dist/brandsync-wc/{p-cdbad27a.entry.js → p-f5f443e2.entry.js} +1 -1
  11. package/dist/brandsync-wc/{p-1dc6d7f5.entry.js → p-f88d9a6e.entry.js} +1 -1
  12. package/dist/cjs/brandsync-wc.cjs.js +1 -1
  13. package/dist/cjs/bs-ai-thinking.cjs.entry.js +34 -0
  14. package/dist/cjs/bs-button-skeleton.cjs.entry.js +1 -1
  15. package/dist/cjs/bs-chatbot-header.cjs.entry.js +2 -2
  16. package/dist/cjs/bs-chatbot-response-action.cjs.entry.js +1 -1
  17. package/dist/cjs/bs-chatbot-suggestion-button.cjs.entry.js +25 -0
  18. package/dist/cjs/bs-input.cjs.entry.js +2 -2
  19. package/dist/cjs/bs-menu-item.cjs.entry.js +1 -1
  20. package/dist/cjs/bs-menu.cjs.entry.js +1 -1
  21. package/dist/cjs/bs-tooltip.cjs.entry.js +24 -0
  22. package/dist/cjs/loader.cjs.js +1 -1
  23. package/dist/collection/collection-manifest.json +4 -1
  24. package/dist/collection/components/bs-ai-thinking/bs-ai-thinking.cmp.test.js +28 -0
  25. package/dist/collection/components/bs-ai-thinking/bs-ai-thinking.css +60 -0
  26. package/dist/collection/components/bs-ai-thinking/bs-ai-thinking.js +72 -0
  27. package/dist/collection/components/bs-ai-thinking/bs-ai-thinking.stories.js +21 -0
  28. package/dist/collection/components/bs-ai-thinking/genie-mark-icon.js +14 -0
  29. package/dist/collection/components/bs-button-skeleton/bs-button-skeleton.js +1 -1
  30. package/dist/collection/components/bs-chatbot-header/bs-chatbot-header.css +33 -0
  31. package/dist/collection/components/bs-chatbot-header/bs-chatbot-header.js +9 -1
  32. package/dist/collection/components/bs-chatbot-response-action/bs-chatbot-response-action.js +1 -1
  33. package/dist/collection/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.cmp.test.js +24 -0
  34. package/dist/collection/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.css +57 -0
  35. package/dist/collection/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.js +104 -0
  36. package/dist/collection/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.stories.js +30 -0
  37. package/dist/collection/components/bs-input/bs-input.js +2 -2
  38. package/dist/collection/components/bs-menu/bs-menu.js +1 -1
  39. package/dist/collection/components/bs-menu-item/bs-menu-item.js +1 -1
  40. package/dist/collection/components/bs-tooltip/bs-tooltip.cmp.test.js +16 -0
  41. package/dist/collection/components/bs-tooltip/bs-tooltip.css +45 -0
  42. package/dist/collection/components/bs-tooltip/bs-tooltip.js +83 -0
  43. package/dist/collection/components/bs-tooltip/bs-tooltip.stories.js +26 -0
  44. package/dist/components/bs-ai-thinking.d.ts +11 -0
  45. package/dist/components/bs-ai-thinking.js +1 -0
  46. package/dist/components/bs-button-skeleton.js +1 -1
  47. package/dist/components/bs-chatbot-header.js +1 -1
  48. package/dist/components/bs-chatbot-response-action.js +1 -1
  49. package/dist/components/bs-chatbot-suggestion-button.d.ts +11 -0
  50. package/dist/components/bs-chatbot-suggestion-button.js +1 -0
  51. package/dist/components/bs-input.js +1 -1
  52. package/dist/components/bs-menu-item.js +1 -1
  53. package/dist/components/bs-menu.js +1 -1
  54. package/dist/components/bs-tooltip.d.ts +11 -0
  55. package/dist/components/bs-tooltip.js +1 -0
  56. package/dist/components/p-CoC4LQCI.js +1 -0
  57. package/dist/esm/brandsync-wc.js +1 -1
  58. package/dist/esm/bs-ai-thinking.entry.js +32 -0
  59. package/dist/esm/bs-button-skeleton.entry.js +1 -1
  60. package/dist/esm/bs-chatbot-header.entry.js +2 -2
  61. package/dist/esm/bs-chatbot-response-action.entry.js +1 -1
  62. package/dist/esm/bs-chatbot-suggestion-button.entry.js +23 -0
  63. package/dist/esm/bs-input.entry.js +2 -2
  64. package/dist/esm/bs-menu-item.entry.js +1 -1
  65. package/dist/esm/bs-menu.entry.js +1 -1
  66. package/dist/esm/bs-tooltip.entry.js +22 -0
  67. package/dist/esm/loader.js +1 -1
  68. package/dist/types/components/bs-ai-thinking/bs-ai-thinking.cmp.test.d.ts +1 -0
  69. package/dist/types/components/bs-ai-thinking/bs-ai-thinking.d.ts +32 -0
  70. package/dist/types/components/bs-ai-thinking/bs-ai-thinking.stories.d.ts +10 -0
  71. package/dist/types/components/bs-ai-thinking/genie-mark-icon.d.ts +1 -0
  72. package/dist/types/components/bs-chatbot-header/bs-chatbot-header.d.ts +8 -0
  73. package/dist/types/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.cmp.test.d.ts +1 -0
  74. package/dist/types/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.d.ts +46 -0
  75. package/dist/types/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.stories.d.ts +10 -0
  76. package/dist/types/components/bs-tooltip/bs-tooltip.cmp.test.d.ts +1 -0
  77. package/dist/types/components/bs-tooltip/bs-tooltip.d.ts +44 -0
  78. package/dist/types/components/bs-tooltip/bs-tooltip.stories.d.ts +9 -0
  79. package/dist/types/components.d.ts +441 -0
  80. package/package.json +2 -1
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ var index = require('./index-CVh-sqA_.js');
4
+
5
+ const bsChatbotSuggestionButtonCss = () => `:host{display:inline-block;--bs-chatbot-suggestion-button-radius:var(--bs-border-radius-full);--bs-chatbot-suggestion-button-border-color:var(--bs-border-primary);--bs-chatbot-suggestion-button-bg-default:var(--bs-color-primary-container);--bs-chatbot-suggestion-button-bg-hover:var(--bs-color-primary-container-hover);--bs-chatbot-suggestion-button-bg-pressed:var(--bs-brand-200);--bs-chatbot-suggestion-button-bg-focus:var(--bs-color-primary-container-hover);--bs-chatbot-suggestion-button-text-default:var(--bs-text-action);--bs-chatbot-suggestion-button-text-hover:var(--bs-text-action-hover);--bs-chatbot-suggestion-button-focus-ring-color:var(--bs-border-primary-focus)}.bs-chatbot-suggestion-button{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;height:40px;min-width:80px;padding:var(--bs-spacing-100) var(--bs-spacing-200);border:var(--bs-border-width-thin) solid var(--bs-chatbot-suggestion-button-border-color);border-radius:var(--bs-chatbot-suggestion-button-radius);background:var(--bs-chatbot-suggestion-button-bg-default);color:var(--bs-chatbot-suggestion-button-text-default);font-family:inherit;font-size:var(--bs-font-size-md);line-height:calc(var(--bs-line-height-body-md) * 1px);text-align:center;cursor:pointer;transition:background-color var(--bs-duration-fast) var(--bs-easing-standard)}.bs-chatbot-suggestion-button:hover:not(:disabled){background:var(--bs-chatbot-suggestion-button-bg-hover);color:var(--bs-chatbot-suggestion-button-text-hover)}.bs-chatbot-suggestion-button:active:not(:disabled){background:var(--bs-chatbot-suggestion-button-bg-pressed);color:var(--bs-chatbot-suggestion-button-text-hover)}.bs-chatbot-suggestion-button:focus-visible{background:var(--bs-chatbot-suggestion-button-bg-focus);color:var(--bs-chatbot-suggestion-button-text-hover);outline:var(--bs-border-width-medium) solid var(--bs-chatbot-suggestion-button-focus-ring-color);outline-offset:3px}.bs-chatbot-suggestion-button:disabled{opacity:var(--bs-opacity-disabled);cursor:not-allowed}`;
6
+
7
+ const BsChatbotSuggestionButton = class {
8
+ constructor(hostRef) {
9
+ index.registerInstance(this, hostRef);
10
+ this.bsSelect = index.createEvent(this, "bsSelect");
11
+ }
12
+ /** Disables the button and applies a reduced-opacity treatment. */
13
+ disabled = false;
14
+ /** Fires when the button is clicked. */
15
+ bsSelect;
16
+ onClick = () => {
17
+ this.bsSelect.emit();
18
+ };
19
+ render() {
20
+ return (index.h("button", { key: 'fc87dab75d560ce5f6612c2bf61875d574eddd1f', type: "button", part: "button", class: "bs-chatbot-suggestion-button", disabled: this.disabled, onClick: this.onClick }, index.h("slot", { key: '27ae5b010dcac9ebbb3924c72299cbb5a067ce6b' })));
21
+ }
22
+ };
23
+ BsChatbotSuggestionButton.style = bsChatbotSuggestionButtonCss();
24
+
25
+ exports.bs_chatbot_suggestion_button = BsChatbotSuggestionButton;
@@ -30,10 +30,10 @@ const BsInput = class {
30
30
  this.bsChange.emit(value);
31
31
  };
32
32
  render() {
33
- return (index.h("div", { key: 'df12d3d7af1bdc0e0e536e6c635ee406e4043b4f', class: `bs-input ${this.error ? 'bs-input--error' : ''}` }, this.label && (
33
+ return (index.h("div", { key: '2c6b38b4594ccde2536515fcd252831bacb6aaeb', class: `bs-input ${this.error ? 'bs-input--error' : ''}` }, this.label && (
34
34
  // "control" is a fixed id, not a generated one -- safe because Shadow DOM scopes ids
35
35
  // to this instance's own shadow root, so it can never collide with another bs-input.
36
- index.h("label", { key: '2e4f867d17ab691a1df1b0b5b2964c0e1e986a91', part: "label", class: "bs-input__label", htmlFor: "control" }, this.label)), index.h("input", { key: 'a078574a5ffad9eafea791cf5866ac123c4d5f56', 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 ? (index.h("span", { id: "description-or-error", part: "error", class: "bs-input__error" }, this.error)) : this.description ? (index.h("span", { id: "description-or-error", part: "description", class: "bs-input__description" }, this.description)) : null));
36
+ index.h("label", { key: '9df0fae013470d8a77b0cc6288c8178b1ac62e0d', part: "label", class: "bs-input__label", htmlFor: "control" }, this.label)), index.h("input", { key: 'a6ddc857d33c4e51a4c64b422a7df5b6261f5180', 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 ? (index.h("span", { id: "description-or-error", part: "error", class: "bs-input__error" }, this.error)) : this.description ? (index.h("span", { id: "description-or-error", part: "description", class: "bs-input__description" }, this.description)) : null));
37
37
  }
38
38
  static get delegatesFocus() { return true; }
39
39
  };
@@ -21,7 +21,7 @@ const BsMenuItem = class {
21
21
  this.bsSelect.emit();
22
22
  };
23
23
  render() {
24
- return (index.h("button", { key: '4d0e94afadd487e2ec44c0c6b416c27eb8f1a5f4', type: "button", part: "item", role: "menuitem", class: "bs-menu-item", onClick: this.onClick }, index.h("span", { key: 'a436154aaddddadc3944049597a87a2b52eb61cb', part: "icon", class: `bs-menu-item__icon ${this.hasIcon ? 'bs-menu-item__icon--has-content' : ''}` }, index.h("slot", { key: '50ad9c1b78bc8ea55219ad7692d7ef7fa7e48189', name: "icon", onSlotchange: this.onIconSlotchange })), index.h("span", { key: 'b3d1ee83556f10de5ad08d6f212ebfd9531dd54e', part: "label", class: "bs-menu-item__label" }, index.h("slot", { key: '5fe02e9b2407c4fd9eaf699caec9ecc9c0e25575' }))));
24
+ return (index.h("button", { key: '6083688b1903b514b0ed6a95a7ee2e64766a94f1', type: "button", part: "item", role: "menuitem", class: "bs-menu-item", onClick: this.onClick }, index.h("span", { key: '65f3df1d22c0e44ed77073de68ce36631b80a31e', part: "icon", class: `bs-menu-item__icon ${this.hasIcon ? 'bs-menu-item__icon--has-content' : ''}` }, index.h("slot", { key: 'a41d500967d857cc5e326194c422992aeab74055', name: "icon", onSlotchange: this.onIconSlotchange })), index.h("span", { key: 'ab17fb6fd7014b7ec74a2eb93de300812a610136', part: "label", class: "bs-menu-item__label" }, index.h("slot", { key: '9b41e86d17fecd71cfe0ff1e5c735aaa143a8d12' }))));
25
25
  }
26
26
  };
27
27
  BsMenuItem.style = bsMenuItemCss();
@@ -9,7 +9,7 @@ const BsMenu = class {
9
9
  index.registerInstance(this, hostRef);
10
10
  }
11
11
  render() {
12
- return (index.h("div", { key: 'd4eee73bfffd1fcfd8f057e394a53fea4688748b', part: "list", class: "bs-menu", role: "menu" }, index.h("slot", { key: '866189bfabc2798ce37b8f0f6141143d4c49081e' })));
12
+ return (index.h("div", { key: 'a48826382a6a13a5047cb893e29fc2f09749b280', part: "list", class: "bs-menu", role: "menu" }, index.h("slot", { key: 'cb4e1eb5988a95f016dc61f698d1c6aadf2d3467' })));
13
13
  }
14
14
  };
15
15
  BsMenu.style = bsMenuCss();
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ var index = require('./index-CVh-sqA_.js');
4
+
5
+ const bsTooltipCss = () => `:host{display:inline-block;--bs-tooltip-bg:var(--bs-surface-inverse);--bs-tooltip-color:var(--bs-text-inverse);--bs-tooltip-radius:var(--bs-border-radius-50);--bs-tooltip-padding-x:var(--bs-spacing-200);--bs-tooltip-padding-y:var(--bs-spacing-100);--bs-tooltip-max-width:200px;--bs-tooltip-shadow:-1px 4px 8px rgba(0, 0, 0, 0.08);--bs-tooltip-font-size:var(--bs-font-size-sm);--bs-tooltip-line-height:var(--bs-line-height-body-sm)}.bs-tooltip{position:relative;display:inline-block}.bs-tooltip__bubble{box-sizing:border-box;max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);background:var(--bs-tooltip-bg);color:var(--bs-tooltip-color);border-radius:var(--bs-tooltip-radius);box-shadow:var(--bs-tooltip-shadow);font-family:inherit;font-size:var(--bs-tooltip-font-size);line-height:calc(var(--bs-tooltip-line-height) * 1px)}.bs-tooltip__arrow{position:absolute;top:-6px;left:50%;transform:translateX(-50%);width:8px;height:8px;background:var(--bs-tooltip-bg);clip-path:polygon(50% 0, 0% 100%, 100% 100%)}`;
6
+
7
+ const BsTooltip = class {
8
+ constructor(hostRef) {
9
+ index.registerInstance(this, hostRef);
10
+ }
11
+ /**
12
+ * Which edge of the bubble the arrow points from, and therefore which side of a trigger the
13
+ * bubble should be placed on. `'top'` is the only value implemented -- it is the only variant
14
+ * confirmed from the Figma "EG Tooltip" component (arrow at the top of the bubble, pointing
15
+ * up). Bottom/left/right variants aren't implemented pending further Figma design confirmation.
16
+ */
17
+ placement = 'top';
18
+ render() {
19
+ return (index.h("div", { key: '784b59f0a817e4756e7954588cd5e42a52508262', class: "bs-tooltip" }, index.h("span", { key: 'b564933fb13a90ab5482c4fde14c99b7431216e5', part: "arrow", class: "bs-tooltip__arrow" }), index.h("div", { key: 'ffb9c4d052d7b8ce618ec825d3989b6311c90c03', part: "bubble", class: "bs-tooltip__bubble" }, index.h("slot", { key: 'cb6cdfe74feb87b23b90da251aa08e76f3bcf0ff' }))));
20
+ }
21
+ };
22
+ BsTooltip.style = bsTooltipCss();
23
+
24
+ exports.bs_tooltip = BsTooltip;
@@ -5,7 +5,7 @@ var index = require('./index-CVh-sqA_.js');
5
5
  const defineCustomElements = async (win, options) => {
6
6
  if (typeof window === 'undefined') return undefined;
7
7
  await index.globalScripts();
8
- return index.bootstrapLazy([["bs-badge.cjs",[[769,"bs-badge",{"variant":[1]}]]],["bs-button.cjs",[[785,"bs-button",{"variant":[1],"size":[1],"disabled":[4],"type":[1],"ariaLabel":[1,"aria-label"],"hasIcon":[32],"hasEndIcon":[32],"hasLabel":[32]}]]],["bs-button-skeleton.cjs",[[513,"bs-button-skeleton",{"size":[1]}]]],["bs-card.cjs",[[769,"bs-card",{"surface":[1]}]]],["bs-chatbot-header.cjs",[[769,"bs-chatbot-header",{"heading":[1],"expanded":[1540]}]]],["bs-chatbot-response-action.cjs",[[769,"bs-chatbot-response-action",{"sourcesCount":[2,"sources-count"],"menuOpen":[1540,"menu-open"]},[[4,"click","onDocumentClick"],[4,"keydown","onKeydown"]]]]],["bs-composer.cjs",[[785,"bs-composer",{"variant":[1],"placeholder":[1],"value":[1],"state":[1],"ariaLabel":[1,"aria-label"]}]]],["bs-data-table.cjs",[[513,"bs-data-table",{"columns":[16],"rows":[16],"sortColumn":[1,"sort-column"],"sortDirection":[1,"sort-direction"],"selectable":[4],"cellRenderer":[16],"selectedIds":[32]}]]],["bs-input.cjs",[[529,"bs-input",{"value":[1],"type":[1],"label":[1],"placeholder":[1],"description":[1],"error":[1],"disabled":[4]}]]],["bs-menu.cjs",[[257,"bs-menu"]]],["bs-menu-item.cjs",[[769,"bs-menu-item",{"hasIcon":[32]}]]],["bs-modal.cjs",[[769,"bs-modal",{"open":[1540],"heading":[1],"size":[1],"hasFooter":[32]},[[4,"keydown","onKeydown"],[4,"focusin","onFocusIn"]]]]]], options);
8
+ return index.bootstrapLazy([["bs-chatbot-header.cjs",[[769,"bs-chatbot-header",{"heading":[1],"expanded":[1540]}]]],["bs-ai-thinking.cjs",[[513,"bs-ai-thinking",{"label":[1]}]]],["bs-badge.cjs",[[769,"bs-badge",{"variant":[1]}]]],["bs-button.cjs",[[785,"bs-button",{"variant":[1],"size":[1],"disabled":[4],"type":[1],"ariaLabel":[1,"aria-label"],"hasIcon":[32],"hasEndIcon":[32],"hasLabel":[32]}]]],["bs-button-skeleton.cjs",[[513,"bs-button-skeleton",{"size":[1]}]]],["bs-card.cjs",[[769,"bs-card",{"surface":[1]}]]],["bs-chatbot-response-action.cjs",[[769,"bs-chatbot-response-action",{"sourcesCount":[2,"sources-count"],"menuOpen":[1540,"menu-open"]},[[4,"click","onDocumentClick"],[4,"keydown","onKeydown"]]]]],["bs-chatbot-suggestion-button.cjs",[[769,"bs-chatbot-suggestion-button",{"disabled":[4]}]]],["bs-composer.cjs",[[785,"bs-composer",{"variant":[1],"placeholder":[1],"value":[1],"state":[1],"ariaLabel":[1,"aria-label"]}]]],["bs-data-table.cjs",[[513,"bs-data-table",{"columns":[16],"rows":[16],"sortColumn":[1,"sort-column"],"sortDirection":[1,"sort-direction"],"selectable":[4],"cellRenderer":[16],"selectedIds":[32]}]]],["bs-input.cjs",[[529,"bs-input",{"value":[1],"type":[1],"label":[1],"placeholder":[1],"description":[1],"error":[1],"disabled":[4]}]]],["bs-menu.cjs",[[257,"bs-menu"]]],["bs-menu-item.cjs",[[769,"bs-menu-item",{"hasIcon":[32]}]]],["bs-modal.cjs",[[769,"bs-modal",{"open":[1540],"heading":[1],"size":[1],"hasFooter":[32]},[[4,"keydown","onKeydown"],[4,"focusin","onFocusIn"]]]]],["bs-tooltip.cjs",[[769,"bs-tooltip",{"placement":[1]}]]]], options);
9
9
  };
10
10
 
11
11
  exports.setNonce = index.setNonce;
@@ -6,12 +6,15 @@
6
6
  "components/bs-composer/bs-composer.js",
7
7
  "components/bs-data-table/bs-data-table.js",
8
8
  "components/bs-modal/bs-modal.js",
9
+ "components/bs-ai-thinking/bs-ai-thinking.js",
9
10
  "components/bs-button-skeleton/bs-button-skeleton.js",
10
11
  "components/bs-chatbot-header/bs-chatbot-header.js",
11
12
  "components/bs-chatbot-response-action/bs-chatbot-response-action.js",
13
+ "components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.js",
12
14
  "components/bs-input/bs-input.js",
13
15
  "components/bs-menu/bs-menu.js",
14
- "components/bs-menu-item/bs-menu-item.js"
16
+ "components/bs-menu-item/bs-menu-item.js",
17
+ "components/bs-tooltip/bs-tooltip.js"
15
18
  ],
16
19
  "mixins": [],
17
20
  "compiler": {
@@ -0,0 +1,28 @@
1
+ import { render, h, describe, it, expect } from "@stencil/vitest";
2
+ describe('bs-ai-thinking', () => {
3
+ it('renders the default label with icon and label parts', async () => {
4
+ const { root } = await render(h("bs-ai-thinking", null));
5
+ expect(root).toHaveTextContent('Thinking');
6
+ const icon = root.shadowRoot.querySelector('[part="icon"]');
7
+ const label = root.shadowRoot.querySelector('[part="label"]');
8
+ expect(icon).not.toBeNull();
9
+ expect(icon.tagName).toBe('IMG');
10
+ expect(label).not.toBeNull();
11
+ });
12
+ it('renders a custom label', async () => {
13
+ const { root } = await render(h("bs-ai-thinking", { label: "Retrieving" }));
14
+ expect(root).toHaveTextContent('Retrieving');
15
+ });
16
+ it('exposes an accessible status region', async () => {
17
+ const { root } = await render(h("bs-ai-thinking", { label: "Searching" }));
18
+ const status = root.shadowRoot.querySelector('[role="status"]');
19
+ expect(status).not.toBeNull();
20
+ expect(status.getAttribute('aria-live')).toBe('polite');
21
+ });
22
+ it('marks the icon as decorative', async () => {
23
+ const { root } = await render(h("bs-ai-thinking", null));
24
+ const icon = root.shadowRoot.querySelector('[part="icon"]');
25
+ expect(icon.getAttribute('alt')).toBe('');
26
+ expect(icon.getAttribute('aria-hidden')).toBe('true');
27
+ });
28
+ });
@@ -0,0 +1,60 @@
1
+ :host {
2
+ display: inline-flex;
3
+ --bs-ai-thinking-gap: var(--bs-spacing-100);
4
+ --bs-ai-thinking-icon-size: 24px;
5
+ --bs-ai-thinking-text: var(--bs-text-muted);
6
+ --bs-ai-thinking-font-size: var(--bs-font-size-md);
7
+ --bs-ai-thinking-line-height: var(--bs-line-height-body-md);
8
+ /* Figma's keyframe track for this label runs over exactly 2s -- no --bs-duration-* token
9
+ * reaches that high (the longest, --bs-duration-slower, is 500ms), so this is aliased directly
10
+ * to the literal value, same precedent as bs-tooltip's --bs-tooltip-shadow. */
11
+ --bs-ai-thinking-cycle-duration: 2s;
12
+ }
13
+
14
+ .bs-ai-thinking {
15
+ display: inline-flex;
16
+ align-items: center;
17
+ gap: var(--bs-ai-thinking-gap);
18
+ font-family: inherit;
19
+ }
20
+
21
+ .bs-ai-thinking__icon {
22
+ display: block;
23
+ width: var(--bs-ai-thinking-icon-size);
24
+ height: var(--bs-ai-thinking-icon-size);
25
+ flex-shrink: 0;
26
+ object-fit: contain;
27
+ }
28
+
29
+ .bs-ai-thinking__label {
30
+ color: var(--bs-ai-thinking-text);
31
+ font-size: var(--bs-ai-thinking-font-size);
32
+ line-height: calc(var(--bs-ai-thinking-line-height) * 1px);
33
+ animation: bs-ai-thinking-pulse var(--bs-ai-thinking-cycle-duration) linear infinite;
34
+ }
35
+
36
+ /* Reproduces Figma's exact keyframe track for this node (hold hidden briefly, ease in with the
37
+ * source's own cubic-bezier, hold visible, then reset) -- a breathing pulse, not an invented
38
+ * shimmer sweep. */
39
+ @keyframes bs-ai-thinking-pulse {
40
+ 0% {
41
+ opacity: 0;
42
+ }
43
+ 8.175% {
44
+ animation-timing-function: cubic-bezier(0.5, 0, 0.5, 1);
45
+ opacity: 0;
46
+ }
47
+ 80.546% {
48
+ opacity: 1;
49
+ }
50
+ 100% {
51
+ opacity: 1;
52
+ }
53
+ }
54
+
55
+ @media (prefers-reduced-motion: reduce) {
56
+ .bs-ai-thinking__label {
57
+ animation: none;
58
+ opacity: 1;
59
+ }
60
+ }
@@ -0,0 +1,72 @@
1
+ import { h } from "@stencil/core";
2
+ import { GENIE_MARK_ICON } from "./genie-mark-icon";
3
+ /**
4
+ * A small inline status indicator for Genie AI surfaces: the colorful Genie mark next to a label
5
+ * (e.g. "Retrieving", "Thinking", "Searching") whose text pulses in a slow breathing loop while
6
+ * an operation is in progress.
7
+ *
8
+ * ## When to use
9
+ * - Inline in a Genie AI chat transcript to show what the assistant is currently doing while a
10
+ * response is being generated (retrieving context, searching, thinking).
11
+ *
12
+ * ## When not to use
13
+ * - A blocking/full-panel loading state -- this is a small inline indicator, not a spinner overlay.
14
+ *
15
+ * The label's pulse animation reproduces Figma's exact keyframe track (a plain opacity
16
+ * fade-in/hold/reset with the source's own cubic-bezier easing) rather than an invented
17
+ * shimmer-sweep -- the source design only specifies this one animation.
18
+ *
19
+ * @part icon - The Genie mark icon.
20
+ * @part label - The status label text.
21
+ * @prop --bs-ai-thinking-gap - Gap between icon and label. Aliased to `--bs-spacing-100`.
22
+ * @prop --bs-ai-thinking-icon-size - Width/height of the icon.
23
+ * @prop --bs-ai-thinking-text - Label color. Aliased to `--bs-text-muted`.
24
+ * @prop --bs-ai-thinking-font-size - Label font size. Aliased to `--bs-font-size-md`.
25
+ * @prop --bs-ai-thinking-line-height - Label line height. Aliased to `--bs-line-height-body-md`.
26
+ * @prop --bs-ai-thinking-cycle-duration - Duration of one pulse loop. Figma specifies exactly 2s,
27
+ * longer than any `--bs-duration-*` token (max `--bs-duration-slower` is 500ms), so this is
28
+ * aliased directly to the literal value.
29
+ */
30
+ export class BsAiThinking {
31
+ /** The status text shown next to the icon, e.g. "Retrieving", "Thinking", "Searching". */
32
+ label = 'Thinking';
33
+ render() {
34
+ return (h("div", { key: '5efb57ce1f0d5bcaed2c0ea963566be5f153e983', class: "bs-ai-thinking", role: "status", "aria-live": "polite" }, h("img", { key: '24e00c0d94ad448cd0f9b08f42baf0208e70baa5', part: "icon", class: "bs-ai-thinking__icon", src: GENIE_MARK_ICON, alt: "", "aria-hidden": "true" }), h("span", { key: '8d207d2c9f7752c853fa85991564d4dd0f547b4c', part: "label", class: "bs-ai-thinking__label" }, this.label)));
35
+ }
36
+ static get is() { return "bs-ai-thinking"; }
37
+ static get encapsulation() { return "shadow"; }
38
+ static get originalStyleUrls() {
39
+ return {
40
+ "$": ["bs-ai-thinking.css"]
41
+ };
42
+ }
43
+ static get styleUrls() {
44
+ return {
45
+ "$": ["bs-ai-thinking.css"]
46
+ };
47
+ }
48
+ static get properties() {
49
+ return {
50
+ "label": {
51
+ "type": "string",
52
+ "mutable": false,
53
+ "complexType": {
54
+ "original": "string",
55
+ "resolved": "string",
56
+ "references": {}
57
+ },
58
+ "required": false,
59
+ "optional": false,
60
+ "docs": {
61
+ "tags": [],
62
+ "text": "The status text shown next to the icon, e.g. \"Retrieving\", \"Thinking\", \"Searching\"."
63
+ },
64
+ "getter": false,
65
+ "setter": false,
66
+ "reflect": false,
67
+ "attribute": "label",
68
+ "defaultValue": "'Thinking'"
69
+ }
70
+ };
71
+ }
72
+ }
@@ -0,0 +1,21 @@
1
+ import { html } from "lit";
2
+ import { componentDescription, propDescription } from "../../stories-utils";
3
+ const meta = {
4
+ title: 'Genie AI Components/bs-ai-thinking',
5
+ parameters: { docs: { description: { component: componentDescription('bs-ai-thinking') } } },
6
+ render: args => html ` <bs-ai-thinking label=${args.label}></bs-ai-thinking> `,
7
+ argTypes: {
8
+ label: { control: 'text', description: propDescription('bs-ai-thinking', 'label') },
9
+ },
10
+ args: {
11
+ label: 'Retrieving',
12
+ },
13
+ };
14
+ export default meta;
15
+ export const Default = {};
16
+ export const Thinking = {
17
+ args: { label: 'Thinking' },
18
+ };
19
+ export const Searching = {
20
+ args: { label: 'Searching' },
21
+ };
@@ -0,0 +1,14 @@
1
+ // Genie brand mark (icon only, no wordmark), exported directly from Figma (node 156:1185,
2
+ // 'Retrieving' -- see bs-ai-thinking.tsx) and inlined as a base64 PNG data URI. This is a
3
+ // raster (not vector) export because the source uses gradient-mesh/image-masked fills that
4
+ // don't reduce to plain SVG paths (same reason bs-chatbot-header's genie-logo.ts embeds its own
5
+ // icon fill as a raster pattern) -- rendering it as a real exported image is required by the
6
+ // design-fidelity rule (never redraw a multi-color gradient mark as flat CSS shapes).
7
+ //
8
+ // Figma's own PNG export came back with an opaque white background baked in (the canvas instance
9
+ // sits on a white container frame), which showed as a visible white square in dark mode. The
10
+ // background was removed by de-matting against that known white backdrop (recovering true alpha
11
+ // + unpremultiplied color per pixel, not a flat white-key which leaves halos on the antialiased
12
+ // edges) before encoding -- see git history for the one-off script if this asset is ever
13
+ // re-exported from a design update.
14
+ export const GENIE_MARK_ICON = 'data:image/png;base64,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';
@@ -25,7 +25,7 @@ export class BsButtonSkeleton {
25
25
  /** Sizing scale, matching the `bs-button` size it stands in for. */
26
26
  size = 'md';
27
27
  render() {
28
- return h("span", { key: '5b51879877adc5abbe6f28d79f27a1f33654a1a4', part: "container", class: `bs-button-skeleton bs-button-skeleton--${this.size}`, role: "status", "aria-label": "Loading" });
28
+ return h("span", { key: '34febfe9aa1e20130b62b7884e766cd5285848d7', part: "container", class: `bs-button-skeleton bs-button-skeleton--${this.size}`, role: "status", "aria-label": "Loading" });
29
29
  }
30
30
  static get is() { return "bs-button-skeleton"; }
31
31
  static get encapsulation() { return "shadow"; }
@@ -6,6 +6,7 @@
6
6
  --bs-chatbot-header-padding-bottom: var(--bs-spacing-100);
7
7
  --bs-chatbot-header-padding-x: var(--bs-margin-fluid);
8
8
  --bs-chatbot-header-gap: var(--bs-navigation-header-gap);
9
+ --bs-chatbot-header-actions-gap: var(--bs-spacing-100);
9
10
  --bs-chatbot-header-button-size: var(--bs-spacing-600);
10
11
  --bs-chatbot-header-button-radius: var(--bs-border-radius-100);
11
12
  --bs-chatbot-header-button-hover: var(--bs-color-neutral-container);
@@ -41,6 +42,7 @@
41
42
  .bs-chatbot-header__actions {
42
43
  display: flex;
43
44
  align-items: center;
45
+ gap: var(--bs-chatbot-header-actions-gap);
44
46
  flex-shrink: 0;
45
47
  }
46
48
 
@@ -79,3 +81,34 @@
79
81
  .bs-chatbot-header__button:active {
80
82
  background: var(--bs-chatbot-header-button-pressed);
81
83
  }
84
+
85
+ .bs-chatbot-header__tooltip-wrapper {
86
+ position: relative;
87
+ display: inline-flex;
88
+ }
89
+
90
+ .bs-chatbot-header__tooltip {
91
+ position: absolute;
92
+ top: 100%;
93
+ left: 50%;
94
+ transform: translateX(-50%);
95
+ margin-top: var(--bs-spacing-100);
96
+ opacity: 0;
97
+ visibility: hidden;
98
+ pointer-events: none;
99
+ /* Hides immediately (no delay) but waits --bs-duration-slow before showing, so the tooltip
100
+ * doesn't flash in on every incidental pointer pass-over -- standard tooltip UX, and this
101
+ * duration token already collapses to 0ms under prefers-reduced-motion (see tokens.css), so
102
+ * no separate reduced-motion handling is needed here. */
103
+ transition: opacity var(--bs-duration-fast) var(--bs-easing-standard);
104
+ transition-delay: 0s;
105
+ white-space: nowrap;
106
+ z-index: 1;
107
+ }
108
+
109
+ .bs-chatbot-header__tooltip-wrapper:hover .bs-chatbot-header__tooltip,
110
+ .bs-chatbot-header__tooltip-wrapper:focus-within .bs-chatbot-header__tooltip {
111
+ opacity: 1;
112
+ visibility: visible;
113
+ transition-delay: var(--bs-duration-slow);
114
+ }
@@ -11,6 +11,13 @@ import { GENIE_LOGO_SVG } from "./genie-logo";
11
11
  * - A generic app/page header — this component's layout and actions are purpose-built for the
12
12
  * Genie chat panel, not a general navigation bar.
13
13
  *
14
+ * Each action button shows a `bs-tooltip` (placement="top", i.e. positioned below the button with
15
+ * its arrow pointing up toward it -- fits naturally since this header sits at the top of a panel)
16
+ * on hover/focus, driven by plain CSS (`:hover`/`:focus-within` on a wrapper, no extra JS state).
17
+ * The tooltip is `aria-hidden` since it's a purely visual reinforcement of each button's existing
18
+ * `aria-label` -- screen readers already get the accessible name from the button itself, so the
19
+ * tooltip doesn't need to be (and shouldn't be) announced a second time.
20
+ *
14
21
  * @slot new-chat-icon - Overrides the default "New chat" button icon.
15
22
  * @slot history-icon - Overrides the default "History" button icon.
16
23
  * @slot expand-icon - Overrides the default "Expand"/"Collapse" button icon (rendered regardless of `expanded` -- there is no separate confirmed "collapsed" glyph in the design, so swap it yourself via this slot if you need one).
@@ -26,6 +33,7 @@ import { GENIE_LOGO_SVG } from "./genie-logo";
26
33
  * @prop --bs-chatbot-header-padding-bottom - Aliased to `--bs-spacing-100`.
27
34
  * @prop --bs-chatbot-header-padding-x - Aliased to `--bs-margin-fluid`.
28
35
  * @prop --bs-chatbot-header-gap - Gap between the logo and the button group. Aliased to `--bs-navigation-header-gap`.
36
+ * @prop --bs-chatbot-header-actions-gap - Gap between the individual action buttons. Aliased to `--bs-spacing-100`.
29
37
  * @prop --bs-chatbot-header-button-size - Width/height of each icon button. Aliased to `--bs-spacing-600`.
30
38
  * @prop --bs-chatbot-header-button-radius - Corner radius of each icon button. Aliased to `--bs-border-radius-100`.
31
39
  * @prop --bs-chatbot-header-button-hover - Icon button background on hover. Aliased to `--bs-color-neutral-container`.
@@ -66,7 +74,7 @@ export class BsChatbotHeader {
66
74
  this.bsClose.emit();
67
75
  };
68
76
  render() {
69
- return (h("div", { key: '80fe762deba2cf1f54c48b4ec1aeca01b561ad4b', class: "bs-chatbot-header", role: "banner", "aria-label": this.heading }, h("div", { key: '167489d1dca22664a6add4450f368748501a13b0', part: "logo", class: "bs-chatbot-header__logo", role: "img", "aria-label": this.heading, innerHTML: GENIE_LOGO_SVG }), h("div", { key: '04f8b156ef9a1cb92b43cc4081220212d5e01ea5', class: "bs-chatbot-header__actions" }, h("button", { key: '513160e9ad7669f33039ad8ee4c376572521567c', type: "button", part: "new-chat", class: "bs-chatbot-header__button", "aria-label": "New chat", onClick: this.onNewChatClick }, h("slot", { key: '0878e1b10f9bce9445e8d0b5c720d80a3c764a26', name: "new-chat-icon" }, h(NewChatIcon, { key: 'fabe10c6f86b8ef2de6e6c2f716dca4d9ad13aa3' }))), h("button", { key: 'fac8aefa4d7d8b2697c4f314894599b49aebdb5a', type: "button", part: "history", class: "bs-chatbot-header__button", "aria-label": "History", onClick: this.onHistoryClick }, h("slot", { key: 'ca04fc061a61f02315f38f0870856003fea1f9d3', name: "history-icon" }, h(HistoryIcon, { key: '543b7814a9a4514a0f035eeb40815df944181f4d' }))), h("button", { key: '0a8974659cdcee7c129df6ab4ea47cafb2a7a165', type: "button", part: "expand", class: "bs-chatbot-header__button", "aria-label": this.expanded ? 'Collapse' : 'Expand', "aria-pressed": this.expanded ? 'true' : 'false', onClick: this.onExpandClick }, h("slot", { key: 'cef1c497dcc189034ff53d6df37685960f58ca34', name: "expand-icon" }, h(ExpandIcon, { key: '2591b845d17b25ece8a59847e8d15bfd8aab552c' }))), h("button", { key: '1a5942671ca8a749419354676b97665f9a95745e', type: "button", part: "close", class: "bs-chatbot-header__button", "aria-label": "Close", onClick: this.onCloseClick }, h("slot", { key: '198b029d9320c163db010295484b15c3577be42e', name: "close-icon" }, h(CloseIcon, { key: 'dae6bcb2dadb666c26ebfe08550ae38c63644443' }))))));
77
+ return (h("div", { key: '9ae798e8d79c992933228383823d3160dc660f90', class: "bs-chatbot-header", role: "banner", "aria-label": this.heading }, h("div", { key: 'c80243df90e7ed74cea0679b94c139fc6bba8cc5', part: "logo", class: "bs-chatbot-header__logo", role: "img", "aria-label": this.heading, innerHTML: GENIE_LOGO_SVG }), h("div", { key: 'ebcfc959d4d7c606866870f1f107c4fc771d25b1', class: "bs-chatbot-header__actions" }, h("span", { key: '153e84f8e63faf67c180338da94e86d2b28da945', class: "bs-chatbot-header__tooltip-wrapper" }, h("button", { key: '76b48c5dcaf482dfbafacf8125a5c2908bbc27c3', type: "button", part: "new-chat", class: "bs-chatbot-header__button", "aria-label": "New chat", onClick: this.onNewChatClick }, h("slot", { key: 'b09177f97df5a1ddb20374d260858f0ae3afea12', name: "new-chat-icon" }, h(NewChatIcon, { key: 'a1907a4fd8730c83af15ad7dbf8ec1d784fac88d' }))), h("bs-tooltip", { key: '77b453884b760bf4687b26adbb06f6220c49dbca', class: "bs-chatbot-header__tooltip", placement: "top", "aria-hidden": "true" }, "New chat")), h("span", { key: '8b775f74bd28efa4b812f959658cae97ffce085f', class: "bs-chatbot-header__tooltip-wrapper" }, h("button", { key: '1a8c279aa94175b2d90654aa4d3c959273147a7c', type: "button", part: "history", class: "bs-chatbot-header__button", "aria-label": "History", onClick: this.onHistoryClick }, h("slot", { key: '8660d04481d3d846731e83b23c865e2bab1cbea7', name: "history-icon" }, h(HistoryIcon, { key: 'a34ed34be60fbc2869b34480342007aa59700177' }))), h("bs-tooltip", { key: '7ba408053f7b0e88330720d3dff4f188f233f46a', class: "bs-chatbot-header__tooltip", placement: "top", "aria-hidden": "true" }, "History")), h("span", { key: '4b4d24319a9b5b2085c8180748f72bfcc532a942', class: "bs-chatbot-header__tooltip-wrapper" }, h("button", { key: 'b9055bd0344465df6afb40aad28ea9565865751e', type: "button", part: "expand", class: "bs-chatbot-header__button", "aria-label": this.expanded ? 'Collapse' : 'Expand', "aria-pressed": this.expanded ? 'true' : 'false', onClick: this.onExpandClick }, h("slot", { key: '460b3c1d20f2a48945df6385e3ebf7f54577881e', name: "expand-icon" }, h(ExpandIcon, { key: '890b772fff27703a7d3da00ff4ad5f2844714075' }))), h("bs-tooltip", { key: 'b43756a31171e14b70bc80574741b28b4dad2c98', class: "bs-chatbot-header__tooltip", placement: "top", "aria-hidden": "true" }, this.expanded ? 'Collapse' : 'Expand')), h("span", { key: '2023c06216607d6205ec3a0a13d2b06a10babae5', class: "bs-chatbot-header__tooltip-wrapper" }, h("button", { key: '1bdc7a342af4b6b37121edb9a93c30ba659d95bf', type: "button", part: "close", class: "bs-chatbot-header__button", "aria-label": "Close", onClick: this.onCloseClick }, h("slot", { key: '1c47b5499cf15ca73aa8a9dab792bf72c441ff38', name: "close-icon" }, h(CloseIcon, { key: '2ce1bad2bca776187e238647c02e3cc4b68018de' }))), h("bs-tooltip", { key: 'd7a3016e6eab564ab0cd9f5ab1d79be2b6408c17', class: "bs-chatbot-header__tooltip", placement: "top", "aria-hidden": "true" }, "Close")))));
70
78
  }
71
79
  static get is() { return "bs-chatbot-header"; }
72
80
  static get encapsulation() { return "shadow"; }
@@ -105,7 +105,7 @@ export class BsChatbotResponseAction {
105
105
  };
106
106
  render() {
107
107
  const hasSources = !!this.sourcesCount && this.sourcesCount > 0;
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')))));
108
+ return (h("div", { key: 'd372000276bedcf0bd64015d955a4ed4ff1c83cc', class: "bs-chatbot-response-action" }, h("button", { key: '6fff591650e4f6114a653ff37bfebe100d4874b7', type: "button", part: "like", class: "bs-chatbot-response-action__button", "aria-label": "Like", onClick: this.onLikeClick }, h("slot", { key: 'e92d29243291141fd63120b4bc6a5024f1908066', name: "like-icon" }, h(ThumbsUpIcon, { key: 'd1a7e8d922f5e712e48e19c6176fdb60ac511240' }))), h("button", { key: '7115ff6c49c5388440f0fe8a352ddd4459a5f8ed', type: "button", part: "dislike", class: "bs-chatbot-response-action__button", "aria-label": "Dislike", onClick: this.onDislikeClick }, h("slot", { key: 'd3642d52b90cbd3f6c60d8b8ead3d14fab29f531', name: "dislike-icon" }, h(ThumbsDownIcon, { key: '8cff196faabd164cf023bfa8bac02f3455885261' }))), h("button", { key: 'd32e2ebce71ab91327d7ad7406ae581b2ff98443', type: "button", part: "copy", class: "bs-chatbot-response-action__button", "aria-label": "Copy", onClick: this.onCopyClick }, h("slot", { key: '1c0459f9fb78202d41e5bf7b3bf5a11ac9c00ecd', name: "copy-icon" }, h(CopyIcon, { key: 'b3d8e9f3cb2125c26967156f046ce3caf44ab8b1' }))), h("button", { key: 'c14f8a2580018edac6b16c82be5b339c7a4033af', type: "button", part: "regenerate", class: "bs-chatbot-response-action__button", "aria-label": "Regenerate", onClick: this.onRegenerateClick }, h("slot", { key: 'cf67332b681da43a7a305909e14a16bf6a3ca962', name: "regenerate-icon" }, h(RegenerateIcon, { key: '76b5b9c39624007532b2c2b7f20554f12cd8eee4' }))), h("div", { key: 'b62694c42bf23484362e188b6b74559810fce693', class: "bs-chatbot-response-action__menu-wrapper" }, h("button", { key: '36715b97f529bfc9c064020d434d0210a3674e52', 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: 'f11d837af777a04cc9031921f92b57e139758f2c', name: "menu-icon" }, h(MenuIcon, { key: '3c0c0a70edcf1e8c817a121fce315b8a747ee8e4' }))), this.menuOpen && (h("div", { key: '5a4848466caecf26ce3797b14609932e76ca653d', class: "bs-chatbot-response-action__menu-popup" }, h("slot", { key: 'f79ac9bcde08eadd0a904223f6f13c9850066827', name: "menu" })))), hasSources && (h("button", { key: 'ef9663bbbb7f74482743177bdfa8053575c3330e', type: "button", part: "sources", class: "bs-chatbot-response-action__sources", onClick: this.onSourcesClick }, h("slot", { key: 'f848fd2b5e545d186a7f337c2d0fe0f300c567f7', name: "sources-icon" }, h(SourcesIcon, { key: '7a8be099bcf97209b30c1b941b1f13691d9c07d3' })), h("span", { key: 'e91be28b3d36b1cc3a91c17218ca5ea77938f887', class: "bs-chatbot-response-action__sources-label" }, this.sourcesCount, " ", this.sourcesCount === 1 ? 'source' : 'sources')))));
109
109
  }
110
110
  static get is() { return "bs-chatbot-response-action"; }
111
111
  static get encapsulation() { return "shadow"; }
@@ -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
+ });
@@ -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
+ }